mirror of
https://github.com/austinried/subtracks.git
synced 2026-11-11 07:29:14 +01:00
playback: share edge wrapping with the art predictions
Lift the wrap arithmetic out of next(), previous() and previousTrack() into nextPosition()/previousPosition() so upcomingItem()/previousItem() cannot drift from the actions again. A one-track queue no longer predicts itself as its own neighbour, online or offline: the wrap would land on the current position, and the actions are a no-op there, so the strip offered a dead swipe.
This commit is contained in:
@@ -693,10 +693,27 @@ class PlaybackController(
|
|||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
when {
|
nextPosition(snapshot, current)?.let { jumpTo(it) }
|
||||||
current < snapshot.size - 1 -> jumpTo(current + 1)
|
|
||||||
repeatMode != RepeatMode.Off -> jumpTo(0)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun nextPosition(
|
||||||
|
snapshot: QueueSnapshot,
|
||||||
|
current: Long,
|
||||||
|
): Long? =
|
||||||
|
when {
|
||||||
|
current < snapshot.size - 1 -> current + 1
|
||||||
|
repeatMode != RepeatMode.Off -> 0L
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun previousPosition(
|
||||||
|
snapshot: QueueSnapshot,
|
||||||
|
current: Long,
|
||||||
|
): Long? =
|
||||||
|
when {
|
||||||
|
current > 0 -> current - 1
|
||||||
|
repeatMode == RepeatMode.All -> snapshot.size - 1
|
||||||
|
else -> null
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun consumeUpNext(position: Long) =
|
private suspend fun consumeUpNext(position: Long) =
|
||||||
@@ -784,21 +801,14 @@ class PlaybackController(
|
|||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
when {
|
val target = previousPosition(snapshot, current)
|
||||||
current > 0 -> {
|
if (target != null) {
|
||||||
jumpTo(current - 1)
|
jumpTo(target)
|
||||||
}
|
} else {
|
||||||
|
|
||||||
repeatMode == RepeatMode.All -> {
|
|
||||||
jumpTo(snapshot.size - 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
else -> {
|
|
||||||
player.seekTo(0)
|
player.seekTo(0)
|
||||||
refresh()
|
refresh()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
fun previousTrack() {
|
fun previousTrack() {
|
||||||
val snapshot = snapshot ?: return
|
val snapshot = snapshot ?: return
|
||||||
@@ -814,10 +824,7 @@ class PlaybackController(
|
|||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
when {
|
previousPosition(snapshot, current)?.let { jumpTo(it) }
|
||||||
current > 0 -> jumpTo(current - 1)
|
|
||||||
repeatMode == RepeatMode.All -> jumpTo(snapshot.size - 1)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun addToQueue(
|
fun addToQueue(
|
||||||
@@ -935,21 +942,15 @@ class PlaybackController(
|
|||||||
val snapshot = current ?: return null
|
val snapshot = current ?: return null
|
||||||
val at = position ?: return null
|
val at = position ?: return null
|
||||||
if (!isOffline) {
|
if (!isOffline) {
|
||||||
val next =
|
return nextPosition(snapshot, at)
|
||||||
if (at + 1 < snapshot.size) {
|
?.takeIf { it != at }
|
||||||
at + 1
|
?.let { queueRepository.itemAt(snapshot, it)?.toQueueItem() }
|
||||||
} else if (repeatMode != RepeatMode.Off) {
|
|
||||||
0L
|
|
||||||
} else {
|
|
||||||
null
|
|
||||||
}
|
|
||||||
return next?.let { queueRepository.itemAt(snapshot, it)?.toQueueItem() }
|
|
||||||
}
|
}
|
||||||
downloads.awaitLoaded()
|
downloads.awaitLoaded()
|
||||||
val target =
|
val target =
|
||||||
downloadedAtOrAfter(snapshot, at + 1)
|
downloadedAtOrAfter(snapshot, at + 1)
|
||||||
?: if (repeatMode != RepeatMode.Off) downloadedAtOrAfter(snapshot, 0L) else null
|
?: if (repeatMode != RepeatMode.Off) downloadedAtOrAfter(snapshot, 0L) else null
|
||||||
return target?.let { queueRepository.itemAt(snapshot, it)?.toQueueItem() }
|
return target?.takeIf { it != at }?.let { queueRepository.itemAt(snapshot, it)?.toQueueItem() }
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun previousItem(): QueueItem? {
|
suspend fun previousItem(): QueueItem? {
|
||||||
@@ -964,19 +965,15 @@ class PlaybackController(
|
|||||||
val snapshot = current ?: return null
|
val snapshot = current ?: return null
|
||||||
val at = position ?: return null
|
val at = position ?: return null
|
||||||
if (!isOffline) {
|
if (!isOffline) {
|
||||||
val previous =
|
return previousPosition(snapshot, at)
|
||||||
when {
|
?.takeIf { it != at }
|
||||||
at > 0 -> at - 1
|
?.let { queueRepository.itemAt(snapshot, it)?.toQueueItem() }
|
||||||
repeatMode == RepeatMode.All -> snapshot.size - 1
|
|
||||||
else -> return null
|
|
||||||
}
|
|
||||||
return queueRepository.itemAt(snapshot, previous)?.toQueueItem()
|
|
||||||
}
|
}
|
||||||
downloads.awaitLoaded()
|
downloads.awaitLoaded()
|
||||||
val target =
|
val target =
|
||||||
downloadedAtOrBefore(snapshot, at - 1)
|
downloadedAtOrBefore(snapshot, at - 1)
|
||||||
?: if (repeatMode == RepeatMode.All) downloadedAtOrBefore(snapshot, snapshot.size - 1) else null
|
?: if (repeatMode == RepeatMode.All) downloadedAtOrBefore(snapshot, snapshot.size - 1) else null
|
||||||
return target?.let { queueRepository.itemAt(snapshot, it)?.toQueueItem() }
|
return target?.takeIf { it != at }?.let { queueRepository.itemAt(snapshot, it)?.toQueueItem() }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun play(
|
private fun play(
|
||||||
|
|||||||
@@ -2262,6 +2262,38 @@ class PlaybackControllerTest {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun aSingleTrackQueueHasNoAdjacentArtwork() {
|
||||||
|
seedAlbum(1, sourceId = 1)
|
||||||
|
controller.playAlbum(1, "al1", 0)
|
||||||
|
await {
|
||||||
|
controller.state.value.item
|
||||||
|
?.id == "s1"
|
||||||
|
}
|
||||||
|
controller.cycleRepeat()
|
||||||
|
assertEquals(RepeatMode.All, controller.state.value.repeat)
|
||||||
|
|
||||||
|
assertNull(runBlocking { controller.upcomingItem() })
|
||||||
|
assertNull(runBlocking { controller.previousItem() })
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun aSingleDownloadedTrackHasNoAdjacentArtworkWhenOffline() {
|
||||||
|
seedAlbum(1, sourceId = 1)
|
||||||
|
markDownloaded(1, "s1")
|
||||||
|
setOffline()
|
||||||
|
controller.playAlbum(1, "al1", 0)
|
||||||
|
await {
|
||||||
|
controller.state.value.item
|
||||||
|
?.id == "s1"
|
||||||
|
}
|
||||||
|
controller.cycleRepeat()
|
||||||
|
assertEquals(RepeatMode.All, controller.state.value.repeat)
|
||||||
|
|
||||||
|
assertNull(runBlocking { controller.upcomingItem() })
|
||||||
|
assertNull(runBlocking { controller.previousItem() })
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun repeatAllLoopsAfterEndingWithRepeatOff() {
|
fun repeatAllLoopsAfterEndingWithRepeatOff() {
|
||||||
seedAlbum(2, sourceId = 1)
|
seedAlbum(2, sourceId = 1)
|
||||||
|
|||||||
Reference in New Issue
Block a user