mirror of
https://github.com/austinried/subtracks.git
synced 2026-11-11 15:29:15 +01:00
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:
@@ -870,12 +870,12 @@ class PlaybackController(
|
|||||||
endedHandled = false
|
endedHandled = false
|
||||||
return@withLock
|
return@withLock
|
||||||
}
|
}
|
||||||
|
lastPosition = dest
|
||||||
shiftWindowLocked(dest)
|
shiftWindowLocked(dest)
|
||||||
player.ensurePrepared()
|
player.ensurePrepared()
|
||||||
player.seekToIndex((dest - windowStart).toInt())
|
player.seekToIndex((dest - windowStart).toInt())
|
||||||
if (wasEnded) player.play()
|
if (wasEnded) player.play()
|
||||||
endedHandled = false
|
endedHandled = false
|
||||||
lastPosition = dest
|
|
||||||
refresh(dest)
|
refresh(dest)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -317,7 +317,11 @@ fun QueueScreen(
|
|||||||
var dragId by remember { mutableStateOf<Long?>(null) }
|
var dragId by remember { mutableStateOf<Long?>(null) }
|
||||||
var dragFrom by remember { mutableStateOf<Long?>(null) }
|
var dragFrom by remember { mutableStateOf<Long?>(null) }
|
||||||
|
|
||||||
val reorderState =
|
val upNextReorder =
|
||||||
|
rememberReorderableLazyListState(listState) { from, to ->
|
||||||
|
onReorder(from.index, to.index)
|
||||||
|
}
|
||||||
|
val contextReorder =
|
||||||
rememberReorderableLazyListState(listState) { from, to ->
|
rememberReorderableLazyListState(listState) { from, to ->
|
||||||
onReorder(from.index, to.index)
|
onReorder(from.index, to.index)
|
||||||
}
|
}
|
||||||
@@ -378,10 +382,11 @@ fun QueueScreen(
|
|||||||
if (index == 0 || rows[index - 1].upNext != row.upNext) {
|
if (index == 0 || rows[index - 1].upNext != row.upNext) {
|
||||||
QueueSectionHeader(if (row.upNext) "Up next" else contextTitle ?: "Next up")
|
QueueSectionHeader(if (row.upNext) "Up next" else contextTitle ?: "Next up")
|
||||||
}
|
}
|
||||||
val draggable = row.upNext || !shuffle
|
val enabled = row.upNext || !shuffle
|
||||||
ReorderableItem(
|
ReorderableItem(
|
||||||
state = reorderState,
|
state = if (row.upNext) upNextReorder else contextReorder,
|
||||||
key = row.id,
|
key = row.id,
|
||||||
|
enabled = enabled,
|
||||||
animateItemModifier =
|
animateItemModifier =
|
||||||
Modifier.animateItem(
|
Modifier.animateItem(
|
||||||
fadeInSpec = tween(150),
|
fadeInSpec = tween(150),
|
||||||
@@ -395,7 +400,7 @@ fun QueueScreen(
|
|||||||
floating = isDragging,
|
floating = isDragging,
|
||||||
coverArt = coverArt,
|
coverArt = coverArt,
|
||||||
dragHandle =
|
dragHandle =
|
||||||
if (!draggable) {
|
if (!enabled) {
|
||||||
null
|
null
|
||||||
} else {
|
} else {
|
||||||
Modifier.draggableHandle(
|
Modifier.draggableHandle(
|
||||||
|
|||||||
@@ -612,6 +612,119 @@ class PlaybackControllerTest {
|
|||||||
assertTrue(controller.state.value.layout > before)
|
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
|
@Test
|
||||||
fun theDurationUsesTheNextTracksMetadataWhileItLoads() {
|
fun theDurationUsesTheNextTracksMetadataWhileItLoads() {
|
||||||
seedAlbum(3, sourceId = 1)
|
seedAlbum(3, sourceId = 1)
|
||||||
|
|||||||
Reference in New Issue
Block a user