playback: wrap adjacent artwork with repeat

The now playing strip took its neighbours from previousItem() and upcomingItem(), which returned null at the queue edges online even when repeat would wrap. Swiping there resisted and only a hard fling advanced the track, leaving no artwork to slide in. Mirror next() and previousTrack() so the online path wraps with repeat All, and with repeat One for the next slot.
This commit is contained in:
austinried
2026-10-18 07:33:58 +00:00
parent 562ca1607b
commit f1628439be
2 changed files with 75 additions and 2 deletions
@@ -934,7 +934,17 @@ class PlaybackController(
}
val snapshot = current ?: return null
val at = position ?: return null
if (!isOffline) return queueRepository.itemAt(snapshot, at + 1)?.toQueueItem()
if (!isOffline) {
val next =
if (at + 1 < snapshot.size) {
at + 1
} else if (repeatMode != RepeatMode.Off) {
0L
} else {
null
}
return next?.let { queueRepository.itemAt(snapshot, it)?.toQueueItem() }
}
downloads.awaitLoaded()
val target =
downloadedAtOrAfter(snapshot, at + 1)
@@ -953,7 +963,15 @@ class PlaybackController(
}
val snapshot = current ?: return null
val at = position ?: return null
if (!isOffline) return if (at <= 0) null else queueRepository.itemAt(snapshot, at - 1)?.toQueueItem()
if (!isOffline) {
val previous =
when {
at > 0 -> at - 1
repeatMode == RepeatMode.All -> snapshot.size - 1
else -> return null
}
return queueRepository.itemAt(snapshot, previous)?.toQueueItem()
}
downloads.awaitLoaded()
val target =
downloadedAtOrBefore(snapshot, at - 1)
@@ -218,6 +218,61 @@ class PlaybackControllerTest {
assertNull(runBlocking { controller.previousItem() })
}
@Test
fun previousItemWrapsToTheEndWithRepeatAll() {
seedAlbum(3, sourceId = 1)
controller.playAlbum(1, "al1", 0)
await {
controller.state.value.item
?.id == "s1"
}
controller.cycleRepeat()
assertEquals(RepeatMode.All, controller.state.value.repeat)
assertEquals("s3", runBlocking { controller.previousItem()?.id })
}
@Test
fun upcomingItemIsNullAtTheEndOfTheQueue() {
seedAlbum(3, sourceId = 1)
controller.playAlbum(1, "al1", 2)
await {
controller.state.value.item
?.id == "s3"
}
assertNull(runBlocking { controller.upcomingItem() })
}
@Test
fun upcomingItemWrapsToTheStartWithRepeatAll() {
seedAlbum(3, sourceId = 1)
controller.playAlbum(1, "al1", 2)
await {
controller.state.value.item
?.id == "s3"
}
controller.cycleRepeat()
assertEquals(RepeatMode.All, controller.state.value.repeat)
assertEquals("s1", runBlocking { controller.upcomingItem()?.id })
}
@Test
fun upcomingItemWrapsToTheStartWithRepeatOne() {
seedAlbum(3, sourceId = 1)
controller.playAlbum(1, "al1", 2)
await {
controller.state.value.item
?.id == "s3"
}
controller.cycleRepeat()
controller.cycleRepeat()
assertEquals(RepeatMode.One, controller.state.value.repeat)
assertEquals("s1", runBlocking { controller.upcomingItem()?.id })
}
@Test
fun previousTrackMovesBackEvenOncePreviousWouldRestart() {
seedAlbum(3, sourceId = 1)