fix(android): refresh uiState.isPlaying from remoteState during UPnP
android / Build + lint + test (push) Successful in 4m1s

This commit is contained in:
2026-06-03 23:08:32 -04:00
parent 87ad7f4dc2
commit 33285b53c6
@@ -63,6 +63,7 @@ class PlayerController @Inject constructor(
private val radio: RadioController,
private val playerFactory: PlayerFactory,
private val activeUpnpHolder: com.fabledsword.minstrel.player.output.ActiveUpnpHolder,
private val remoteState: RemotePlayerState,
) {
/**
@@ -471,10 +472,31 @@ class PlayerController @Inject constructor(
scope.launch(Dispatchers.Main.immediate) {
while (isActive) {
delay(POSITION_POLL_INTERVAL_MS)
if (!controller.isPlaying) continue
uiStateInternal.value = uiStateInternal.value.copy(
positionMs = controller.currentPosition.coerceAtLeast(0),
bufferedPositionMs = controller.bufferedPosition.coerceAtLeast(0),
// When UPnP is active, MediaController's cached state is stale --
// remoteState.applyTransportPlaying() doesn't fire a Player.Listener
// event, so onEvents never refreshes isPlaying / positionMs from
// the wrapped player. Read directly from remoteState here so the
// play/pause toggle and scrubber reflect Sonos's actual state.
val upnpActive = activeUpnpHolder.active.value != null
val effectiveIsPlaying =
if (upnpActive) remoteState.isPlaying else controller.isPlaying
val effectivePosition =
if (upnpActive) remoteState.positionMs else controller.currentPosition
val effectiveDuration =
if (upnpActive) remoteState.durationMs else controller.duration
val current = uiStateInternal.value
val newPos = effectivePosition.coerceAtLeast(0)
val newDur = effectiveDuration.coerceAtLeast(0)
val newBuf = controller.bufferedPosition.coerceAtLeast(0)
val changed = current.isPlaying != effectiveIsPlaying ||
current.positionMs != newPos ||
current.durationMs != newDur
if (!changed) continue
uiStateInternal.value = current.copy(
isPlaying = effectiveIsPlaying,
positionMs = newPos,
durationMs = newDur,
bufferedPositionMs = newBuf,
)
}
}