fix(player): return volume control to the local speaker when a remote session stops

Stopping a remote session left Android stuck on the remote volume slider
with no way back to the device speaker.

Two causes:

1. `player_stop`'s remote branch sent "Stop" to the session and returned
   without touching the playback mode, so the manager stayed in Remote.
   It now drops to Idle, mirroring what the local branch already does.

2. Volume routing was torn down at a single call site
   (`transfer_to_local_inner`), so every *other* exit from remote mode
   leaked the Android VolumeProviderCompat. Routing is now derived from
   the transition inside `set_mode`: entering remote attaches control,
   any exit from remote hands it back to the local media stream. This
   also covers the frontend `disconnect()` path (Remote -> Idle) and the
   local-playback-start paths (Remote -> Local).

Adds a `RemoteVolumeControl` trait so the routing rule is unit-testable
off-device — the real implementation is Android JNI. Tests cover
remote->idle, remote->local, remote->remote (re-arms, never releases),
and that local/idle transitions leave routing untouched.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-12 20:09:17 +02:00
co-authored by Claude Opus 5
parent 0ca2857c3a
commit 2ca2174cea
2 changed files with 220 additions and 23 deletions
+10
View File
@@ -1003,6 +1003,16 @@ pub async fn player_stop(
.clone()
};
client.send_session_command(session_id, "Stop").await?;
// Stopping the remote session ends the cast, so the manager returns to
// Idle — same as a local stop. This is also what hands OS volume control
// back to this device: set_mode releases the Android remote volume
// provider on any exit from remote mode. Without it the mode stayed
// Remote and the system volume slider remained stuck on the remote
// session with no way back to the local speaker.
playback_mode
.0
.set_mode(crate::playback_mode::PlaybackMode::Idle);
} else {
// Local playback
let controller = player.0.lock().await;