Skip to content

Harden launch animation (PR #36 review follow-up)#43

Merged
Xitee1 merged 1 commit into
mainfrom
fix/launch-animation-review
Jul 12, 2026
Merged

Harden launch animation (PR #36 review follow-up)#43
Xitee1 merged 1 commit into
mainfrom
fix/launch-animation-review

Conversation

@Xitee1

@Xitee1 Xitee1 commented Jul 12, 2026

Copy link
Copy Markdown
Owner

Follow-up to the launch animation merged in #36. This applies the fixes from the post-merge code review — they were prepared but never made it into #36 before it merged. ./gradlew assembleDebug lint is green.

Correctness

  1. Idle play icon no longer rotates in landscape / blank first frame / no crossfade when animation is off. The play glyph is back inside PlayButton (with its orientation iconRotation and the Play↔Stop crossfade). LaunchOverlay now renders only during Crouch/Launch; the button alpha-hides its play icon during flight via a new hidePlayIcon param. launch() also takes a startIconRotationDeg so the handoff is seamless in landscape.
  2. Seed could crash startup. The one-time seed in SettingsRepositoryImpl now catches IOException (best-effort; falls back to the UserSettings default), matching the read path.
  3. Phase clock desynced from animations at non-1× animator duration scale. Phases now advance by awaiting their animations (coroutineScope { launch { animateTo(...) } }) instead of wall-clock delay(). Durations live in one place.
  4. TalkBack announced the opposite action for ~1.16 s. PlayButton gained describesRunning, fed with the real running state, so it announces "Stop timer" while a tap actually stops the timer.
  5. Tap races. launch() sets phase synchronously (Idle guard now blocks same-frame double-launches), onToggle reads launchController.phase live, and reset()/launch() serialize via cancelAndJoin so a pending reset snap can't cut into a new run.

Performance / cleanup

  1. Per-frame full-screen recomposition. impactPulse and buttonScale are now () -> Float read inside draw/graphicsLayer lambdas; the overlay reads its animatables the same way. Frames are layer updates + redraws, not recomposition. The reduce-motion Settings.Global read moved into onToggle (once per tap, and picks up runtime changes).
  2. Seed design. Dropped the LAUNCH_ANIMATION_SEEDED key — the seed gates on key absence, removing both the overwrite-a-user-toggle race and the per-start write transaction.
  3. Nits. FastOutSlowInEasing instead of hand-rolled beziers, geometry.lerp for offset interpolation, appTheme() read inside LaunchOverlay (two color params dropped), and the design docs moved into docs/plans/ per CLAUDE.md.

Note

The choreography still deserves one manual pass on a device — especially landscape launch, tap-during-animation, and a 2× animator-scale run — since the repo has no UI tests.

🤖 Generated with Claude Code

Follow-up fixes for the launch animation merged in #36. Build + lint green.

- Keep the play icon in PlayButton (restores landscape rotation, the
  Play->Stop crossfade on the animation-off/reduce-motion path, and the
  first-frame icon); LaunchOverlay renders only during Crouch/Launch and
  the button alpha-hides its play icon during flight.
- Sequence controller phases by awaiting animations instead of wall-clock
  delay(), so the state machine no longer desyncs at non-1x animator
  duration scale; durations now live in one place.
- Set phase synchronously and serialize reset()/launch() via cancelAndJoin
  so same-frame double taps and a pending reset snap can't glitch the run.
- Derive the button's a11y contentDescription from the real timer state so
  TalkBack announces "Stop" while a tap actually stops the running timer.
- Read impactPulse/buttonScale/overlay animatables in draw/graphicsLayer
  lambdas (() -> Float), avoiding full-screen recomposition per frame;
  evaluate the reduce-motion gate at tap time instead of every recomposition.
- Seed launchAnimationEnabled on key-absence (drop the SEEDED key + its
  overwrite race and per-start write), and guard the seed with try/catch so
  an IOException can't crash startup.
- Nits: use FastOutSlowInEasing and geometry.lerp, read appTheme() inside
  LaunchOverlay, and move the design docs into docs/plans/ per CLAUDE.md.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@Xitee1
Xitee1 merged commit 5697850 into main Jul 12, 2026
1 check passed
@Xitee1
Xitee1 deleted the fix/launch-animation-review branch July 12, 2026 07:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant