fix(android): effectiveDuration uses desiredIdx so duration tracks the displayed title
android / Build + lint + test (push) Successful in 4m2s

This commit is contained in:
2026-06-04 07:17:17 -04:00
parent 4021938046
commit aa23a72693
@@ -462,6 +462,8 @@ class PlayerController @Inject constructor(
upnpActive, upnpActive,
remoteState.durationMs, remoteState.durationMs,
player.duration, player.duration,
desiredIdx = idx,
controllerIdx = idx,
), ),
bufferedPositionMs = player.bufferedPosition.coerceAtLeast(0), bufferedPositionMs = player.bufferedPosition.coerceAtLeast(0),
playbackError = player.playerError?.message, playbackError = player.playerError?.message,
@@ -525,7 +527,13 @@ class PlayerController @Inject constructor(
val desiredIdx = desiredQueueIndex(controller, upnpActive) val desiredIdx = desiredQueueIndex(controller, upnpActive)
val current = uiStateInternal.value val current = uiStateInternal.value
val newPos = effectivePosition.coerceAtLeast(0) val newPos = effectivePosition.coerceAtLeast(0)
val newDur = effectiveDuration(upnpActive, remoteState.durationMs, controller.duration) val newDur = effectiveDuration(
upnpActive,
remoteState.durationMs,
controller.duration,
desiredIdx = desiredIdx,
controllerIdx = controller.currentMediaItemIndex,
)
val newBuf = controller.bufferedPosition.coerceAtLeast(0) val newBuf = controller.bufferedPosition.coerceAtLeast(0)
// Track adjustments are forward-only AND suppressed while a user // Track adjustments are forward-only AND suppressed while a user
// transport press is pending Sonos confirmation. Together those keep // transport press is pending Sonos confirmation. Together those keep
@@ -691,11 +699,22 @@ class PlayerController @Inject constructor(
* until the first SOAP poll lands. * until the first SOAP poll lands.
*/ */
@Suppress("ReturnCount") // 3-tier fallback reads cleanest as a ladder of early returns @Suppress("ReturnCount") // 3-tier fallback reads cleanest as a ladder of early returns
private fun effectiveDuration(upnpActive: Boolean, remoteMs: Long, localMs: Long): Long { private fun effectiveDuration(
upnpActive: Boolean,
remoteMs: Long,
localMs: Long,
desiredIdx: Int,
controllerIdx: Int,
): Long {
if (upnpActive && remoteMs > 0) return remoteMs if (upnpActive && remoteMs > 0) return remoteMs
if (localMs > 0) return localMs // Tier 2 (wrapped player's probed duration) only valid when the
val idx = mediaController?.currentMediaItemIndex ?: 0 // wrapped player is on the same track we're trying to show. After a
val ref = queueRefs.getOrNull(idx) ?: return 0 // Sonos natural advance the polling tick updates desiredIdx from
// Sonos's truth while controllerIdx is briefly stale -- using the
// wrapped player's duration here would surface the old track's
// length under the new track's title.
if (localMs > 0 && controllerIdx == desiredIdx) return localMs
val ref = queueRefs.getOrNull(desiredIdx) ?: return 0
return ref.durationSec.toLong() * MS_PER_SECOND return ref.durationSec.toLong() * MS_PER_SECOND
} }