Commit Graph
1 Commits
Author SHA1 Message Date
dtourolle edff6eedc9 fix(player): let the background-audio toggle govern backgrounding again
🏗️ Build and Test JellyTau / Run Tests (push) Successful in 18m44s
🏗️ Build and Test JellyTau / Supply Chain (push) Failing after 49s
Publish Documentation / Build & publish docs to gitea-pages (push) Successful in 5m27s
Traceability Validation / Check Requirement Traces (push) Successful in 10s
Build & Release / Run Tests (push) Successful in 14m48s
🏗️ Build and Test JellyTau / Android Compile Check (push) Successful in 4m19s
Build & Release / Build Linux (push) Successful in 20m20s
Build & Release / Build Windows (push) Successful in 15m36s
Build & Release / Build Android (push) Successful in 30m46s
Build & Release / Create Release (push) Successful in 38s
Locking the screen kept a video's audio playing whether or not the
background-audio button was on. Reported as "audio only mode is always
active even if not selected".

The button (UR-040) was built for the WebView <video> path, where losing
visibility kills the decode: it chose between handing off to a native
audio stream and letting playback stop. Native video then became the
default renderer (DR-188), and on that path playback runs through
ExoPlayer inside a MediaSessionService -- a foreground media service
whose entire purpose is to keep playing while the app is hidden. Nothing
stopped it, and nothing in the codebase paused on background.

So the button governed a handoff that no longer had a gap to bridge.
There was no interruption to paper over, and a user who never touched it
got background playback anyway.

The gating made it self-concealing: MainActivity.onStop only dispatched
'jellytau-background' when backgroundAudioEnabled was already true. The
one notification that the app had gone away was itself conditional on the
setting, so with the button OFF nothing could react even in principle.
onStop and onStart now fire unconditionally and carry the two facts only
the activity knows -- whether the toggle is armed, and whether Android
put the window into picture-in-picture.

What to do about it is decided in Rust (player/background_policy.rs),
because it depends on whether the item has a picture to lose:

  video + toggle off  -> Pause
  video + toggle on   -> HandOffToAudio
  music, either       -> KeepPlaying   (no picture to give up)
  picture-in-picture  -> KeepPlaying   (the window is still on screen)

It takes no renderer parameter on purpose. Two renderers with two
behaviours and one toggle reaching only one of them is what produced the
defect; a rule that cannot see the renderer cannot reproduce it.

Two failure modes are deliberate. A decision call that fails leaves
playback alone rather than risking silence mid-listen. An event with no
detail -- older Kotlin against newer JS -- reads as "armed, not PiP",
degrading to the previous behaviour instead of pausing unexpectedly.

Foregrounding resumes only what backgrounding paused: a video the user
paused themselves before locking stays paused.

Written test-first per CLAUDE.md. The stub encoded today's behaviour
(nothing ever pauses) and failed exactly as reported --
`left: KeepPlaying, right: Pause` -- before the rule was implemented.

Verified on a device, R8-minified, both directions:

  [player_background_action] video=true armed=false pip=false -> Pause
  [player_background_action] video=true armed=true  pip=false -> HandOffToAudio

UR-040 / DR-224 / UT-211.
2026-08-22 10:09:46 +02:00