From 4965d5971c1443ee69af42b9f86920fd6121fea5 Mon Sep 17 00:00:00 2001 From: Peter Stone Date: Fri, 25 Sep 2026 19:07:04 +0000 Subject: Advance the queue from the service, not from the activity Marking an episode played and moving the queue on lived in PlayerViewModel, which is scoped to the activity and releases its MediaController in onCleared. So with the activity gone -- swiped off recents, or reclaimed under memory pressure -- the audio carried on from the foreground service while nothing marked anything played and nothing advanced the queue. Background listening with the screen off is the normal case for a podcast app, so this was broken in the case that matters most. The outro offset and ad-chapter skipping added last commit rode the same tick and had the same hole. Completion now belongs to PlaybackService, which is the component that survives backgrounding. Two new tested classes carry the thinking: PlayheadPolicy decides what a tick means (finish early on an outro offset, seek past a marked break, or nothing -- with finishing beating skipping, or a show with both would seek into the credits it was told to skip and sit there), and PlaybackAdvancer owns finishing, advancing, and working out where the next episode starts. Neither knows about Media3, so both are testable off-device; the service turns their answers into player calls. Where an episode starts is now decided in exactly one place. PlaybackAdvancer .start is used by the service when the queue moves on and by the view model when you tap Play, so an episode begins in the same position however it got there. PlayerConnection no longer carries its own copy of the resume rules. PlayerConnection deliberately stops handling STATE_ENDED and PlayerViewModel loses onFinished entirely -- with both in place the queue would advance twice. The view model now follows whatever is playing rather than only what it started, so the player screen's show, speed and chapter list stay right when the service moves on by itself. Verified on a device with "don't keep activities" enabled, which destroys the activity the moment it is backgrounded: an episode was resumed, the app sent to home with no activity records left, and the service took the episode to its outro line, marked it played, dropped it from the queue and started the next one, which went on playing. Also isolated the data loss seen twice this session: connectedDebugAndroidTest uninstalls the app when it finishes, taking the database with it. A plain adb install -r left four episodes in place and the next instrumented run left "run-as: unknown package". Not a migration fault, and now written down as such. No permission changes, no schema change. 385 unit tests green, detekt and lint clean. Coverage 66.23 -> 65.33 with an exception recorded: the logic moved out of the coverage-excluded ui/** tree into the counted service, and the ExoPlayer glue that remains cannot run off-device. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_018AwnALZL6bD4VBtATDVv3B --- .coverage-baseline | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to '.coverage-baseline') diff --git a/.coverage-baseline b/.coverage-baseline index e8aa377..5d8929f 100644 --- a/.coverage-baseline +++ b/.coverage-baseline @@ -1 +1 @@ -66.23 +65.33 -- cgit v1.2.3