Files
Android-247-Radio/chat-summaries/2026-03-09_player-state-machine-refactor.md
cottongin 5dd7a411ed fix: refactor player state machine to eliminate race conditions
Introduce a Connecting state so the UI reflects user intent immediately,
centralize navigation in MainActivity via state transitions, and replace
the pauseRequested volatile flag with controller state as single source
of truth.

Made-with: Cursor
2026-03-10 05:15:31 -04:00

2.5 KiB

Player State Machine Refactor

Task

Refactored the player state management to eliminate race conditions during rapid station tapping, stop/play transitions, and the NowPlayingScreen bounce-back bug.

Changes Made

PlaybackState.kt

  • Added Connecting state variant to represent the period between user tap and service processing the intent.

RadioController.kt

  • play() now sets state to Connecting synchronously before sending the intent, eliminating the async gap.
  • stop() now sets state to Idle synchronously before sending the intent.
  • pause() now sets state to Paused synchronously before sending the intent.
  • Added redundancy guard: double-tapping a station that's already Connecting is a no-op.

RadioPlaybackService.kt

  • Removed the pauseRequested volatile flag entirely.
  • The handlePlay() finally block now checks controller.state.value (the single source of truth) instead of the volatile flag to determine cleanup behavior.
  • Renamed cleanup() to cleanupResources() since it no longer sets Idle state (that's now the controller's responsibility).
  • handlePause() and handleStop() no longer set pauseRequested.

MainActivity.kt

  • Centralized navigation logic via a LaunchedEffect that observes RadioController.state.
  • Navigates to NowPlaying on Idle -> Active transitions.
  • Navigates back to StationList on Active -> Idle transitions while on NowPlaying.
  • Station switching (Playing(A) -> Connecting(B)) stays on NowPlaying.

NowPlayingScreen.kt

  • Removed the LaunchedEffect(playbackState) that called onBack() on Idle (was the source of the bounce-back bug).
  • Added Connecting to the when branches, showing a loading spinner overlay.
  • Disabled pause/skip-ahead buttons while connecting.

NowPlayingViewModel.kt

  • Added Connecting handling in session timer (uses sessionStartedAt), connection timer (shows 0), and artwork resolver (uses station default artwork).

StationListScreen.kt

  • Removed onNavigateToNowPlaying() from onPlay callbacks (navigation is now centralized in MainActivity).
  • Added Connecting to mini player visibility and "now playing" station highlight.

MiniPlayer.kt

  • Added Connecting to the destructuring when block.
  • Shows "Connecting..." subtitle for the Connecting state.

Follow-up Items

  • The stayConnected and retryImmediatelyOnNetwork volatile flags in RadioPlaybackService could be further consolidated into the state machine in a future pass.