diff --git a/android/app/src/main/java/com/fabledsword/minstrel/player/MinstrelForwardingPlayer.kt b/android/app/src/main/java/com/fabledsword/minstrel/player/MinstrelForwardingPlayer.kt index ef7efba0..db857765 100644 --- a/android/app/src/main/java/com/fabledsword/minstrel/player/MinstrelForwardingPlayer.kt +++ b/android/app/src/main/java/com/fabledsword/minstrel/player/MinstrelForwardingPlayer.kt @@ -53,6 +53,12 @@ class MinstrelForwardingPlayer( private val handler = Handler(delegate.applicationLooper) private var pollJob: Job? = null + // Tracks consecutive non-PLAYING poll observations so a single transient + // PAUSED_PLAYBACK / STOPPED tick during a Sonos track transition does not + // flip the play/pause button. Manual pause still feels instant because it + // bypasses the poll entirely via applyTransportPaused(). + @Volatile private var nonPlayingPollStreak = 0 + init { scope.launch { holder.active.collect { active -> onActiveChanged(active) } @@ -227,6 +233,7 @@ class MinstrelForwardingPlayer( private fun onActiveChanged(active: ActiveUpnp?) { pollJob?.cancel() + nonPlayingPollStreak = 0 if (active != null) { Timber.w("UPnP active: %s -- pollLoop starting", active.routeName) // Pause the wrapped ExoPlayer so we are not playing local audio @@ -278,14 +285,28 @@ class MinstrelForwardingPlayer( ) val transport = active.avTransport.getTransportInfo() when (transport.state) { - TransportState.PLAYING -> remoteState.applyTransportPlaying() - TransportState.PAUSED -> remoteState.applyTransportPaused() - TransportState.STOPPED -> remoteState.applyTransportStopped() + TransportState.PLAYING -> { + nonPlayingPollStreak = 0 + remoteState.applyTransportPlaying() + } + TransportState.PAUSED -> { + nonPlayingPollStreak += 1 + if (nonPlayingPollStreak >= NON_PLAYING_CONFIRM) { + remoteState.applyTransportPaused() + } + } + TransportState.STOPPED -> { + nonPlayingPollStreak += 1 + if (nonPlayingPollStreak >= NON_PLAYING_CONFIRM) { + remoteState.applyTransportStopped() + } + } TransportState.TRANSITIONING, TransportState.UNKNOWN -> Unit } } private companion object { const val POLL_INTERVAL_MS = 1_000L + const val NON_PLAYING_CONFIRM = 2 } }