mirror of
https://github.com/austinried/subtracks.git
synced 2026-11-10 15:11:58 +01:00
fix the queue paging refresh key and drag edge detection
This commit is contained in:
@@ -9,19 +9,27 @@ class QueuePagingSource(
|
||||
private val repository: QueueRepository,
|
||||
private val initialPosition: Long,
|
||||
) : PagingSource<Long, QueueWindowItem>() {
|
||||
override fun getRefreshKey(state: PagingState<Long, QueueWindowItem>): Long? =
|
||||
state.anchorPosition?.let { (it / QUEUE_PAGE_SIZE).toLong() }
|
||||
override fun getRefreshKey(state: PagingState<Long, QueueWindowItem>): Long? {
|
||||
val anchor = state.anchorPosition ?: return null
|
||||
val first =
|
||||
state.pages
|
||||
.firstOrNull()
|
||||
?.data
|
||||
?.firstOrNull()
|
||||
?.position ?: return null
|
||||
return (first + anchor) / QUEUE_PAGE_SIZE
|
||||
}
|
||||
|
||||
override suspend fun load(params: LoadParams<Long>): LoadResult<Long, QueueWindowItem> {
|
||||
val snapshot = repository.snapshot()
|
||||
val page = params.key ?: (initialPosition.coerceAtLeast(0) / QUEUE_PAGE_SIZE)
|
||||
if (snapshot.size == 0L) return LoadResult.Page(emptyList(), null, null)
|
||||
val lastPage = (snapshot.size - 1) / QUEUE_PAGE_SIZE
|
||||
val page = (params.key ?: (initialPosition.coerceAtLeast(0) / QUEUE_PAGE_SIZE)).coerceIn(0, lastPage)
|
||||
val offset = page * QUEUE_PAGE_SIZE
|
||||
val previousKey = if (page > 0) page - 1 else null
|
||||
if (offset >= snapshot.size) return LoadResult.Page(emptyList(), previousKey, null)
|
||||
val end = (offset + params.loadSize).coerceAtMost(snapshot.size)
|
||||
return LoadResult.Page(
|
||||
data = repository.range(snapshot, offset, end - 1),
|
||||
prevKey = previousKey,
|
||||
prevKey = if (page > 0) page - 1 else null,
|
||||
nextKey = if (end < snapshot.size) end / QUEUE_PAGE_SIZE else null,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -49,11 +49,13 @@ data class PlaybackState(
|
||||
private sealed interface QueueUndo {
|
||||
val entries: List<QueueEntry>
|
||||
val cursor: Long
|
||||
val current: Long?
|
||||
}
|
||||
|
||||
private data class RemovedUndo(
|
||||
override val entries: List<QueueEntry>,
|
||||
override val cursor: Long,
|
||||
override val current: Long,
|
||||
val position: Long,
|
||||
val item: QueueItem?,
|
||||
) : QueueUndo
|
||||
@@ -61,6 +63,7 @@ private data class RemovedUndo(
|
||||
private data class MovedUndo(
|
||||
override val entries: List<QueueEntry>,
|
||||
override val cursor: Long,
|
||||
override val current: Long,
|
||||
val from: Long,
|
||||
val to: Long,
|
||||
) : QueueUndo
|
||||
@@ -68,7 +71,9 @@ private data class MovedUndo(
|
||||
private data class ReloadUndo(
|
||||
override val entries: List<QueueEntry>,
|
||||
override val cursor: Long,
|
||||
) : QueueUndo
|
||||
) : QueueUndo {
|
||||
override val current: Long? = null
|
||||
}
|
||||
|
||||
class PlaybackController(
|
||||
private val sourceRepository: SourceRepository,
|
||||
@@ -195,7 +200,7 @@ class PlaybackController(
|
||||
if (removed == null) {
|
||||
ReloadUndo(entries, cursor)
|
||||
} else {
|
||||
RemovedUndo(entries, cursor, position, removed)
|
||||
RemovedUndo(entries, cursor, target, position, removed)
|
||||
}
|
||||
when {
|
||||
position == current -> {
|
||||
@@ -242,7 +247,7 @@ class PlaybackController(
|
||||
val inWindow = from in windowStart..windowEnd && to in windowStart..windowEnd
|
||||
lastEdit =
|
||||
if (inWindow) {
|
||||
MovedUndo(entries, cursor, from, to)
|
||||
MovedUndo(entries, cursor, target, from, to)
|
||||
} else {
|
||||
ReloadUndo(entries, cursor)
|
||||
}
|
||||
@@ -275,7 +280,11 @@ class PlaybackController(
|
||||
val target = undo.cursor.coerceIn(0, restored.size - 1)
|
||||
val restoredId = queueRepository.itemAt(restored, target)?.song?.id
|
||||
windowJob?.cancel()
|
||||
val canMirror = player.currentItem?.id != null && player.currentItem?.id == restoredId
|
||||
val canMirror =
|
||||
undo.current != null &&
|
||||
player.currentItem?.id != null &&
|
||||
player.currentItem?.id == restoredId &&
|
||||
currentPosition() == undo.current
|
||||
val mirrored =
|
||||
when {
|
||||
!canMirror -> {
|
||||
|
||||
@@ -180,10 +180,11 @@ fun QueueScreen(
|
||||
val from = dragging ?: break
|
||||
val info = listState.layoutInfo.visibleItemsInfo.firstOrNull { items.peek(it.index)?.position == from }
|
||||
if (info != null) {
|
||||
val top = info.offset + dragOffset
|
||||
val edge =
|
||||
when {
|
||||
info.offset < listState.layoutInfo.viewportStartOffset + threshold -> -step
|
||||
info.offset + info.size > listState.layoutInfo.viewportEndOffset - threshold -> step
|
||||
top < listState.layoutInfo.viewportStartOffset + threshold -> -step
|
||||
top + info.size > listState.layoutInfo.viewportEndOffset - threshold -> step
|
||||
else -> 0f
|
||||
}
|
||||
if (edge != 0f) {
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
package com.subtracks.data.repo
|
||||
|
||||
import android.content.Context
|
||||
import androidx.paging.PagingConfig
|
||||
import androidx.paging.PagingSource
|
||||
import androidx.paging.PagingState
|
||||
import androidx.room3.Room
|
||||
import androidx.sqlite.driver.bundled.BundledSQLiteDriver
|
||||
import androidx.test.core.app.ApplicationProvider
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import com.subtracks.data.db.SubtracksDatabase
|
||||
import com.subtracks.data.model.Song
|
||||
import com.subtracks.data.model.SongListItem
|
||||
import com.subtracks.data.model.Source
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.After
|
||||
@@ -74,6 +77,21 @@ class QueuePagingSourceTest {
|
||||
assertEquals(0L, first.prevKey)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun theRefreshKeyUsesTheLoadedPositions() =
|
||||
runTest {
|
||||
val source = repository.pagingSource(initialPosition = 100)
|
||||
val state =
|
||||
PagingState(
|
||||
pages = listOf(PagingSource.LoadResult.Page(listOf(itemAt(60)), prevKey = 0L, nextKey = 2L)),
|
||||
anchorPosition = 5,
|
||||
config = PagingConfig(pageSize = QUEUE_PAGE_SIZE, enablePlaceholders = false),
|
||||
leadingPlaceholderCount = 0,
|
||||
)
|
||||
|
||||
assertEquals(1L, source.getRefreshKey(state))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun anEmptyQueueLoadsNothing() =
|
||||
runTest {
|
||||
@@ -84,6 +102,29 @@ class QueuePagingSourceTest {
|
||||
assertEquals(null, result.prevKey)
|
||||
}
|
||||
|
||||
private fun itemAt(position: Long) =
|
||||
QueueWindowItem(
|
||||
position,
|
||||
SongListItem(
|
||||
song =
|
||||
Song(
|
||||
sourceId = 1,
|
||||
id = "s$position",
|
||||
albumId = "al1",
|
||||
artistId = "ar1",
|
||||
title = "Song $position",
|
||||
album = "Album",
|
||||
artist = "Artist",
|
||||
duration = 100,
|
||||
track = position,
|
||||
disc = 1,
|
||||
starred = null,
|
||||
genre = null,
|
||||
),
|
||||
coverArt = null,
|
||||
),
|
||||
)
|
||||
|
||||
private suspend fun load(
|
||||
source: PagingSource<Long, QueueWindowItem>,
|
||||
key: Long?,
|
||||
|
||||
Reference in New Issue
Block a user