fix up-next drag and previous behaviour

- set lastPosition before seeking in jumpTo. With Dispatchers.Main
  .immediate the media transition runs synchronously inside the seek, so
  onPositionChanged saw the old position as an auto-advance and consumed
  the track you were leaving; previous from a queued track removed it and
  played the next one.
- give the up-next and context sections their own reorderable states. The
  library only targets items whose key is in that state's reorderableKeys,
  so a drag can no longer target a row in the other section: no more
  fighting the section boundary when dragging to the top and no no-op drop
  below the block.
- disable context reordering while shuffled via ReorderableItem(enabled).
This commit is contained in:
austinried
2026-10-04 00:30:00 +00:00
parent e7a1927ccd
commit 5462b0f4ba
3 changed files with 123 additions and 5 deletions
@@ -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)