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,
remoteState.durationMs,
player.duration,
desiredIdx = idx,
controllerIdx = idx,
),
bufferedPositionMs = player.bufferedPosition.coerceAtLeast(0),
playbackError = player.playerError?.message,
@@ -525,7 +527,13 @@ class PlayerController @Inject constructor(
val desiredIdx = desiredQueueIndex(controller, upnpActive)
val current = uiStateInternal.value
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)
// Track adjustments are forward-only AND suppressed while a user
// transport press is pending Sonos confirmation. Together those keep
@@ -691,11 +699,22 @@ class PlayerController @Inject constructor(
* until the first SOAP poll lands.
*/
@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 (localMs > 0) return localMs
val idx = mediaController?.currentMediaItemIndex ?: 0
val ref = queueRefs.getOrNull(idx) ?: return 0
// Tier 2 (wrapped player's probed duration) only valid when the
// wrapped player is on the same track we're trying to show. After a
// 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
}