mirror of
https://github.com/austinried/subtracks.git
synced 2026-11-11 15:29:15 +01:00
address third review: rebuild the window when the shuffle is dropped, atomic queue version
This commit is contained in:
@@ -10,6 +10,7 @@ import com.subtracks.data.model.QueueEntry
|
|||||||
import com.subtracks.data.model.QueueKind
|
import com.subtracks.data.model.QueueKind
|
||||||
import com.subtracks.data.model.ShuffleOrder
|
import com.subtracks.data.model.ShuffleOrder
|
||||||
import com.subtracks.data.model.SongListItem
|
import com.subtracks.data.model.SongListItem
|
||||||
|
import java.util.concurrent.atomic.AtomicLong
|
||||||
|
|
||||||
const val QUEUE_CHUNK = 60
|
const val QUEUE_CHUNK = 60
|
||||||
|
|
||||||
@@ -62,8 +63,7 @@ class QueueRepository(
|
|||||||
@Volatile
|
@Volatile
|
||||||
private var cachedShuffleOrder: LongArray? = null
|
private var cachedShuffleOrder: LongArray? = null
|
||||||
|
|
||||||
@Volatile
|
private val queueVersion = AtomicLong()
|
||||||
private var queueVersion = 0L
|
|
||||||
|
|
||||||
@Volatile
|
@Volatile
|
||||||
private var cachedFlatIds: List<String>? = null
|
private var cachedFlatIds: List<String>? = null
|
||||||
@@ -98,6 +98,7 @@ class QueueRepository(
|
|||||||
}
|
}
|
||||||
|
|
||||||
suspend fun snapshot(): QueueSnapshot {
|
suspend fun snapshot(): QueueSnapshot {
|
||||||
|
val version = queueVersion.get()
|
||||||
val loaded =
|
val loaded =
|
||||||
db.useReaderConnection { transactor ->
|
db.useReaderConnection { transactor ->
|
||||||
transactor.deferredTransaction {
|
transactor.deferredTransaction {
|
||||||
@@ -110,7 +111,7 @@ class QueueRepository(
|
|||||||
}
|
}
|
||||||
val (entries, order, shuffled) = loaded
|
val (entries, order, shuffled) = loaded
|
||||||
val valid = if (shuffled && order == null) null else order
|
val valid = if (shuffled && order == null) null else order
|
||||||
return QueueSnapshot(entries, valid, queueVersion)
|
return QueueSnapshot(entries, valid, version)
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun modes(): QueueModes {
|
suspend fun modes(): QueueModes {
|
||||||
@@ -138,7 +139,7 @@ class QueueRepository(
|
|||||||
|
|
||||||
fun invalidateLibraryCache() {
|
fun invalidateLibraryCache() {
|
||||||
cachedFlatIds = null
|
cachedFlatIds = null
|
||||||
queueVersion++
|
queueVersion.incrementAndGet()
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun setRepeat(mode: Int) = dao.setCursor(cursorRow().copy(repeatMode = mode))
|
suspend fun setRepeat(mode: Int) = dao.setCursor(cursorRow().copy(repeatMode = mode))
|
||||||
@@ -173,6 +174,15 @@ class QueueRepository(
|
|||||||
return rows(entry, entry.offset + offset, 1).firstOrNull()
|
return rows(entry, entry.offset + offset, 1).firstOrNull()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun flatIndexOf(
|
||||||
|
snapshot: QueueSnapshot,
|
||||||
|
songId: String,
|
||||||
|
): Long? {
|
||||||
|
if (!snapshot.shuffled) return null
|
||||||
|
val index = flatIds(snapshot).indexOf(songId)
|
||||||
|
return if (index >= 0) index.toLong() else null
|
||||||
|
}
|
||||||
|
|
||||||
suspend fun range(
|
suspend fun range(
|
||||||
snapshot: QueueSnapshot,
|
snapshot: QueueSnapshot,
|
||||||
first: Long,
|
first: Long,
|
||||||
@@ -271,7 +281,7 @@ class QueueRepository(
|
|||||||
dao.insert(entries.mapIndexed { index, entry -> entry.copy(id = 0, position = index.toLong()) })
|
dao.insert(entries.mapIndexed { index, entry -> entry.copy(id = 0, position = index.toLong()) })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
queueVersion++
|
queueVersion.incrementAndGet()
|
||||||
cachedFlatIds = null
|
cachedFlatIds = null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -398,8 +398,10 @@ class PlaybackController(
|
|||||||
fun previous() {
|
fun previous() {
|
||||||
val player = player ?: return
|
val player = player ?: return
|
||||||
if (player.currentPositionMs > RESTART_THRESHOLD_MS) {
|
if (player.currentPositionMs > RESTART_THRESHOLD_MS) {
|
||||||
player.seekTo(0)
|
scope.launch {
|
||||||
refresh()
|
startLock.withLock { player.seekTo(0) }
|
||||||
|
refresh()
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
val snapshot = snapshot ?: return
|
val snapshot = snapshot ?: return
|
||||||
@@ -657,9 +659,20 @@ class PlaybackController(
|
|||||||
|
|
||||||
private suspend fun shiftWindowLocked(center: Long) {
|
private suspend fun shiftWindowLocked(center: Long) {
|
||||||
val player = player ?: return
|
val player = player ?: return
|
||||||
|
val previous = snapshot
|
||||||
val snapshot = readSnapshot()
|
val snapshot = readSnapshot()
|
||||||
this.snapshot = snapshot
|
this.snapshot = snapshot
|
||||||
if (snapshot.size == 0L) return
|
if (snapshot.size == 0L) return
|
||||||
|
if (previous != null && previous.shuffled != snapshot.shuffled) {
|
||||||
|
val currentId = player.currentItem?.id
|
||||||
|
val position =
|
||||||
|
(currentId?.let { queueRepository.flatIndexOf(snapshot, it) } ?: center)
|
||||||
|
.coerceIn(0, snapshot.size - 1)
|
||||||
|
queueRepository.setCursor(position)
|
||||||
|
rebuildWindow(queueRepository.window(snapshot, position, QUEUE_WINDOW_RADIUS), position)
|
||||||
|
refresh(position)
|
||||||
|
return
|
||||||
|
}
|
||||||
val target = center.coerceIn(0, snapshot.size - 1)
|
val target = center.coerceIn(0, snapshot.size - 1)
|
||||||
val desiredStart = (target - QUEUE_WINDOW_RADIUS).coerceAtLeast(0)
|
val desiredStart = (target - QUEUE_WINDOW_RADIUS).coerceAtLeast(0)
|
||||||
val desiredEnd = (target + QUEUE_WINDOW_RADIUS).coerceAtMost(snapshot.size - 1)
|
val desiredEnd = (target + QUEUE_WINDOW_RADIUS).coerceAtMost(snapshot.size - 1)
|
||||||
|
|||||||
@@ -680,7 +680,7 @@ class PlaybackControllerTest {
|
|||||||
handle.positionMs = 5_000L
|
handle.positionMs = 5_000L
|
||||||
controller.previous()
|
controller.previous()
|
||||||
|
|
||||||
assertTrue(handle.operations.contains("seekTo(0)"))
|
await { handle.operations.contains("seekTo(0)") }
|
||||||
assertEquals(
|
assertEquals(
|
||||||
"s2",
|
"s2",
|
||||||
controller.state.value.item
|
controller.state.value.item
|
||||||
|
|||||||
Reference in New Issue
Block a user