Fix StickyHeaderContainer header transitions (scroll-driven push)#4851
Merged
Conversation
Replaces the time-based after-the-fact transition with scroll-driven push behavior so the pinned header reacts in lockstep with the user's finger: - TRANSITION_SLIDE: as the next section's header rises into the slot it pushes the pinned header up by the overlap amount, replacing it seamlessly when the rising header reaches the top. - TRANSITION_NONE: the pinned host is hidden during the overlap so the rising header (in the scroller) covers the slot naturally. - TRANSITION_FADE: the pinned header's opacity drops from 255 to 0 as the next section closes the gap, revealing it underneath. Removes the time-based machinery (snapshot, animator, paintGlass overlay) that caused the boundary jitter and the "B slides under A then suddenly reappears below A" jump. transitionDurationMillis is retained for API compatibility but no longer affects visuals. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Contributor
✅ Continuous Quality ReportTest & Coverage
Static Analysis
Generated automatically by the PR CI workflow. |
Collaborator
Author
|
Compared 86 screenshots: 86 matched. Native Android coverage
✅ Native Android screenshot tests passed. Native Android coverage
Benchmark ResultsDetailed Performance Metrics
|
Collaborator
Author
|
Compared 86 screenshots: 86 matched. Benchmark Results
Build and Run Timing
Detailed Performance Metrics
|
iOS and Android emulator captures of StickyHeaderScreenshotTest, StickyHeaderSlideTransitionScreenshotTest and (Android only) StickyHeaderFadeTransitionScreenshotTest now reflect the new scroll-driven push behavior. The iOS Fade test PNG was not present in the artifact this run (only the smaller preview chunks were emitted via the device log, not the full-size PNG chunks) so its golden is left untouched and will be refreshed once the next iOS run captures it cleanly. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The previous CI run dropped the full PNG chunks for this screenshot, emitting only the preview, so it was skipped in the prior golden update. The latest run captured the full PNG cleanly. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The previous retry path silently fell back to `monkey ... >/dev/null 2>&1 || true` whenever `cmd package resolve-activity --brief` returned output that didn't fit the expected `pkg/.Activity` shape — which is common on newer Android (the brief output sometimes prepends a `priority=...` line and adds leading whitespace). When that happened the retry waited the full 600s with zero diagnostic signal and failed the build. Replaces the resolve+monkey dance with `am start -W -a MAIN -c LAUNCHER -p $PACKAGE_NAME`, which launches the app by intent filter and prints a `Status=…` line we can parse. The output is logged verbatim, the process is verified with `pidof` before committing to the 10-minute wait, and the retry skips the wait entirely if no PID is detected so CI fails fast instead of burning ten minutes on a no-op. Also uploads `connectedAndroidTest*.log` as an artifact so the next decode flake — if there is one — is reproducible from CI. Comments in both files spell out the right mechanics and explicitly warn against re-introducing output redirects, which were the root cause of the silent failure. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The new diagnostics surfaced the actual root cause that was hidden behind the previous silent-monkey fallback: Gradle's connectedAndroidTest task uninstalls BOTH APKs at teardown (`DeviceConnector ... uninstalling com.codenameone.examples.hellocodenameone` appears in the gradle log next to the .test uninstall). By the time the retry block ran, the package was gone and `am start` returned "Activity not started, unable to resolve Intent ... pkg=...". Fix: before relaunching, check `pm list packages` (with `grep -x` to avoid `<pkg>.test` substring matches) and `adb install -r` the main APK from `$GRADLE_PROJECT_DIR/app/build/outputs/apk/debug/app-debug.apk` when it's missing. We don't need the .test APK — the main APK contains Cn1ssDeviceRunner, which re-runs the entire suite when the launcher activity starts. Both branches of the install logic log their action and any failure output so the next debugger can tell at a glance whether the install succeeded, the APK was missing, or something further down (like `am start`) is the real issue. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #4849.
Summary
StickyHeaderContainerwith a scroll-driven push so the pinned header reacts in lockstep with the user's finger instead of running a 250ms animation after the swap.Implementation notes
pushOffsetderived every scroll event fromactiveHeader.height - next.relYdrives all three styles.transitionStartMs,animate()registration, and thepaintGlassoverlay draw. These were the source of the jitter (per-paintsetYperturbations to unwind a timeline that was disconnected from scroll).setTransitionDurationMillis/getTransitionDurationMillisretained as no-ops for API compatibility;isTransitionInProgressandgetTransitionProgressnow reflect scroll-driven progress within the push window.Test plan
StickyHeaderContainerTestrewritten for the new model — 17 tests covering activation, deactivation, slide push offset, NONE host hiding, FADE alpha ramp, and progress-tracks-scroll. All pass againstmvn -f maven/core-unittests test -Dtest=StickyHeaderContainerTest.StickyHeaderSlideTransitionScreenshotTest,StickyHeaderFadeTransitionScreenshotTest) updated to step the scroll position through the push window across the 6 grid frames instead of jumping the scroll once and steppingAnimationTime.🤖 Generated with Claude Code