diff --git a/app/src/main/kotlin/com/subtracks/playback/PlaybackController.kt b/app/src/main/kotlin/com/subtracks/playback/PlaybackController.kt index 329be82d..336b718f 100644 --- a/app/src/main/kotlin/com/subtracks/playback/PlaybackController.kt +++ b/app/src/main/kotlin/com/subtracks/playback/PlaybackController.kt @@ -870,12 +870,12 @@ class PlaybackController( endedHandled = false return@withLock } + lastPosition = dest shiftWindowLocked(dest) player.ensurePrepared() player.seekToIndex((dest - windowStart).toInt()) if (wasEnded) player.play() endedHandled = false - lastPosition = dest refresh(dest) } } diff --git a/app/src/main/kotlin/com/subtracks/ui/playback/QueueScreen.kt b/app/src/main/kotlin/com/subtracks/ui/playback/QueueScreen.kt index d2eb6b53..8f03b908 100644 --- a/app/src/main/kotlin/com/subtracks/ui/playback/QueueScreen.kt +++ b/app/src/main/kotlin/com/subtracks/ui/playback/QueueScreen.kt @@ -317,7 +317,11 @@ fun QueueScreen( var dragId by remember { mutableStateOf(null) } var dragFrom by remember { mutableStateOf(null) } - val reorderState = + val upNextReorder = + rememberReorderableLazyListState(listState) { from, to -> + onReorder(from.index, to.index) + } + val contextReorder = rememberReorderableLazyListState(listState) { from, to -> onReorder(from.index, to.index) } @@ -378,10 +382,11 @@ fun QueueScreen( if (index == 0 || rows[index - 1].upNext != row.upNext) { QueueSectionHeader(if (row.upNext) "Up next" else contextTitle ?: "Next up") } - val draggable = row.upNext || !shuffle + val enabled = row.upNext || !shuffle ReorderableItem( - state = reorderState, + state = if (row.upNext) upNextReorder else contextReorder, key = row.id, + enabled = enabled, animateItemModifier = Modifier.animateItem( fadeInSpec = tween(150), @@ -395,7 +400,7 @@ fun QueueScreen( floating = isDragging, coverArt = coverArt, dragHandle = - if (!draggable) { + if (!enabled) { null } else { Modifier.draggableHandle( diff --git a/app/src/test/kotlin/com/subtracks/playback/PlaybackControllerTest.kt b/app/src/test/kotlin/com/subtracks/playback/PlaybackControllerTest.kt index 6e4d38b1..21fab716 100644 --- a/app/src/test/kotlin/com/subtracks/playback/PlaybackControllerTest.kt +++ b/app/src/test/kotlin/com/subtracks/playback/PlaybackControllerTest.kt @@ -612,6 +612,119 @@ class PlaybackControllerTest { assertTrue(controller.state.value.layout > before) } + @Test + fun previousFromADeepQueuedTrackGoesBackWithoutConsuming() { + seedAlbum(3, sourceId = 1) + seedSong("q1", "al2") + seedSong("q2", "al2") + seedSong("q3", "al2") + + controller.playAlbum(1, "al1", 0) + await { + controller.state.value.item + ?.id == "s1" + } + controller.addToQueue(1, QueueKind.Song, "q1") + controller.addToQueue(1, QueueKind.Song, "q2") + controller.addToQueue(1, QueueKind.Song, "q3") + await { runBlocking { controller.upcomingItem()?.id } == "q1" } + + controller.playAt(2) + await { + controller.state.value.item + ?.id == "q2" + } + + controller.previous() + await { + controller.state.value.item + ?.id == "q1" + } + await { runBlocking { controller.upcomingItem()?.id } == "q2" } + } + + @Test + fun previousFromAQueuedTrackDoesNotConsumeItWithAnEagerDispatcher() { + seedAlbum(3, sourceId = 1) + seedSong("q1", "al2") + seedSong("q2", "al2") + seedSong("q3", "al2") + val eagerHandle = FakePlayerHandle() + val inline = + object : kotlinx.coroutines.CoroutineDispatcher() { + override fun dispatch( + context: kotlin.coroutines.CoroutineContext, + block: Runnable, + ) = block.run() + } + val eager = PlaybackController(sources, queues, FakePlayerConnection(eagerHandle), dispatcher = inline) + + eager.playAlbum(1, "al1", 0) + await { + eager.state.value.item + ?.id == "s1" + } + eager.addToQueue(1, QueueKind.Song, "q1") + eager.addToQueue(1, QueueKind.Song, "q2") + eager.addToQueue(1, QueueKind.Song, "q3") + await { runBlocking { eager.upcomingItem()?.id } == "q1" } + + eager.playAt(2) + await { + eager.state.value.item + ?.id == "q2" + } + + eager.previous() + await { + eager.state.value.item + ?.id == "q1" + } + Thread.sleep(300) + assertEquals( + "q1", + eager.state.value.item + ?.id, + ) + assertEquals("q2", runBlocking { eager.upcomingItem()?.id }) + eager.close() + } + + @Test + fun nextFollowsTheReorderedQueue() { + seedAlbum(3, sourceId = 1) + seedSong("q1", "al2") + seedSong("q2", "al2") + + controller.playAlbum(1, "al1", 0) + await { + controller.state.value.item + ?.id == "s1" + } + controller.addToQueue(1, QueueKind.Song, "q1") + controller.addToQueue(1, QueueKind.Song, "q2") + await { runBlocking { controller.upcomingItem()?.id } == "q1" } + + runBlocking { controller.move(2, 1) } + await { runBlocking { controller.upcomingItem()?.id } == "q2" } + + controller.next() + await { + controller.state.value.item + ?.id == "q2" + } + controller.next() + await { + controller.state.value.item + ?.id == "q1" + } + controller.next() + await { + controller.state.value.item + ?.id == "s2" + } + } + @Test fun theDurationUsesTheNextTracksMetadataWhileItLoads() { seedAlbum(3, sourceId = 1)