mirror of
https://github.com/austinried/subtracks.git
synced 2026-11-10 15:11:58 +01:00
library: stop download writes from invalidating the browse lists
The album/artist/playlist paging SQL named song_downloads, so Room invalidated the pages on every download progress tick, janking the list and resetting scroll when a download started. Route the default (filter off) path through a raw query that only observes its own table; the downloaded/offline path keeps the download-aware queries.
This commit is contained in:
@@ -4,6 +4,8 @@ import androidx.paging.PagingSource
|
||||
import androidx.room3.Dao
|
||||
import androidx.room3.DaoReturnTypeConverters
|
||||
import androidx.room3.Query
|
||||
import androidx.room3.RawQuery
|
||||
import androidx.room3.RoomRawQuery
|
||||
import androidx.room3.Transaction
|
||||
import androidx.room3.Upsert
|
||||
import androidx.room3.paging.PagingSourceDaoReturnTypeConverter
|
||||
@@ -432,6 +434,15 @@ interface LibraryDao {
|
||||
)
|
||||
suspend fun deleteOrphanPlaylistSongs(sourceId: Long)
|
||||
|
||||
@RawQuery(observedEntities = [Album::class])
|
||||
fun albumsPaging(query: RoomRawQuery): PagingSource<Int, Album>
|
||||
|
||||
@RawQuery(observedEntities = [Artist::class])
|
||||
fun artistsPaging(query: RoomRawQuery): PagingSource<Int, Artist>
|
||||
|
||||
@RawQuery(observedEntities = [Playlist::class])
|
||||
fun playlistsPaging(query: RoomRawQuery): PagingSource<Int, Playlist>
|
||||
|
||||
@Query("SELECT * $ALBUMS_FILTER ORDER BY $ALBUM_ORDER_BY_NAME")
|
||||
fun albumsByName(
|
||||
sourceId: Long,
|
||||
|
||||
@@ -0,0 +1,142 @@
|
||||
package com.subtracks.data.db
|
||||
|
||||
import androidx.room3.RoomRawQuery
|
||||
import com.subtracks.data.prefs.AlbumSort
|
||||
import com.subtracks.data.prefs.ArtistSort
|
||||
import com.subtracks.data.prefs.PlaylistSort
|
||||
|
||||
// These filters mirror the download-aware ones in LibraryDao but never mention song_downloads, so
|
||||
// the resulting PagingSource is not invalidated by download writes (including progress updates).
|
||||
internal const val ALBUMS_FILTER_BASE =
|
||||
"FROM albums WHERE sourceId = ? " +
|
||||
"AND (? = 0 OR (? = 1 AND starred IS NOT NULL) OR (? = 2 AND starred IS NULL)) " +
|
||||
"AND (? = '' " +
|
||||
"OR (length(?) >= 3 AND rowid IN (SELECT rowid FROM album_search " +
|
||||
"WHERE album_search MATCH '\"' || replace(?, '\"', '\"\"') || '\"')) " +
|
||||
"OR (length(?) < 3 AND (instr(lower(name), lower(?)) > 0 " +
|
||||
"OR instr(lower(albumArtist), lower(?)) > 0))) "
|
||||
|
||||
internal const val ARTISTS_FILTER_BASE =
|
||||
"FROM artists WHERE sourceId = ? " +
|
||||
"AND (? = 0 OR (? = 1 AND starred IS NOT NULL) OR (? = 2 AND starred IS NULL)) " +
|
||||
"AND (? = '' " +
|
||||
"OR (length(?) >= 3 AND rowid IN (SELECT rowid FROM artist_search " +
|
||||
"WHERE artist_search MATCH '\"' || replace(?, '\"', '\"\"') || '\"')) " +
|
||||
"OR (length(?) < 3 AND instr(lower(name), lower(?)) > 0)) "
|
||||
|
||||
internal const val PLAYLISTS_FILTER_BASE =
|
||||
"FROM playlists WHERE sourceId = ? " +
|
||||
"AND (? = '' " +
|
||||
"OR (length(?) >= 3 AND rowid IN (SELECT rowid FROM playlist_search " +
|
||||
"WHERE playlist_search MATCH '\"' || replace(?, '\"', '\"\"') || '\"')) " +
|
||||
"OR (length(?) < 3 AND instr(lower(name), lower(?)) > 0)) "
|
||||
|
||||
internal fun albumsDefaultQuery(
|
||||
sourceId: Long,
|
||||
starredFilter: Int,
|
||||
search: String,
|
||||
sort: AlbumSort,
|
||||
descending: Boolean,
|
||||
): RoomRawQuery {
|
||||
val order = albumOrder(sort, descending)
|
||||
return RoomRawQuery("SELECT * $ALBUMS_FILTER_BASE ORDER BY $order") { stmt ->
|
||||
stmt.bindLong(1, sourceId)
|
||||
stmt.bindInt(2, starredFilter)
|
||||
stmt.bindInt(3, starredFilter)
|
||||
stmt.bindInt(4, starredFilter)
|
||||
stmt.bindText(5, search)
|
||||
stmt.bindText(6, search)
|
||||
stmt.bindText(7, search)
|
||||
stmt.bindText(8, search)
|
||||
stmt.bindText(9, search)
|
||||
stmt.bindText(10, search)
|
||||
}
|
||||
}
|
||||
|
||||
internal fun artistsDefaultQuery(
|
||||
sourceId: Long,
|
||||
starredFilter: Int,
|
||||
search: String,
|
||||
sort: ArtistSort,
|
||||
descending: Boolean,
|
||||
): RoomRawQuery {
|
||||
val order = artistOrder(sort, descending)
|
||||
return RoomRawQuery("SELECT * $ARTISTS_FILTER_BASE ORDER BY $order") { stmt ->
|
||||
stmt.bindLong(1, sourceId)
|
||||
stmt.bindInt(2, starredFilter)
|
||||
stmt.bindInt(3, starredFilter)
|
||||
stmt.bindInt(4, starredFilter)
|
||||
stmt.bindText(5, search)
|
||||
stmt.bindText(6, search)
|
||||
stmt.bindText(7, search)
|
||||
stmt.bindText(8, search)
|
||||
stmt.bindText(9, search)
|
||||
}
|
||||
}
|
||||
|
||||
internal fun playlistsDefaultQuery(
|
||||
sourceId: Long,
|
||||
search: String,
|
||||
sort: PlaylistSort,
|
||||
descending: Boolean,
|
||||
): RoomRawQuery {
|
||||
val order = playlistOrder(sort, descending)
|
||||
return RoomRawQuery("SELECT * $PLAYLISTS_FILTER_BASE ORDER BY $order") { stmt ->
|
||||
stmt.bindLong(1, sourceId)
|
||||
stmt.bindText(2, search)
|
||||
stmt.bindText(3, search)
|
||||
stmt.bindText(4, search)
|
||||
stmt.bindText(5, search)
|
||||
stmt.bindText(6, search)
|
||||
}
|
||||
}
|
||||
|
||||
private fun albumOrder(
|
||||
sort: AlbumSort,
|
||||
descending: Boolean,
|
||||
): String =
|
||||
when (sort) {
|
||||
AlbumSort.Name -> if (descending) ALBUM_ORDER_BY_NAME_REVERSED else ALBUM_ORDER_BY_NAME
|
||||
AlbumSort.Artist -> if (descending) ALBUM_ORDER_BY_ARTIST_REVERSED else ALBUM_ORDER_BY_ARTIST
|
||||
AlbumSort.Year -> if (descending) ALBUM_ORDER_BY_YEAR_REVERSED else ALBUM_ORDER_BY_YEAR
|
||||
AlbumSort.Added -> if (descending) ALBUM_ORDER_BY_ADDED_REVERSED else ALBUM_ORDER_BY_ADDED
|
||||
AlbumSort.Starred -> if (descending) ALBUM_ORDER_BY_STARRED_REVERSED else ALBUM_ORDER_BY_STARRED
|
||||
AlbumSort.Frequent -> if (descending) ALBUM_ORDER_BY_FREQUENT_REVERSED else ALBUM_ORDER_BY_FREQUENT
|
||||
AlbumSort.Recent -> if (descending) ALBUM_ORDER_BY_RECENT_REVERSED else ALBUM_ORDER_BY_RECENT
|
||||
}
|
||||
|
||||
private fun artistOrder(
|
||||
sort: ArtistSort,
|
||||
descending: Boolean,
|
||||
): String =
|
||||
when (sort) {
|
||||
ArtistSort.Name -> {
|
||||
if (descending) ARTIST_ORDER_BY_NAME_REVERSED else ARTIST_ORDER_BY_NAME
|
||||
}
|
||||
|
||||
ArtistSort.AlbumCount -> {
|
||||
if (descending) ARTIST_ORDER_BY_ALBUM_COUNT_REVERSED else ARTIST_ORDER_BY_ALBUM_COUNT
|
||||
}
|
||||
|
||||
ArtistSort.Starred -> {
|
||||
if (descending) ARTIST_ORDER_BY_STARRED_REVERSED else ARTIST_ORDER_BY_STARRED
|
||||
}
|
||||
|
||||
ArtistSort.Frequent -> {
|
||||
if (descending) ARTIST_ORDER_BY_FREQUENT_REVERSED else ARTIST_ORDER_BY_FREQUENT
|
||||
}
|
||||
|
||||
ArtistSort.Recent -> {
|
||||
if (descending) ARTIST_ORDER_BY_RECENT_REVERSED else ARTIST_ORDER_BY_RECENT
|
||||
}
|
||||
}
|
||||
|
||||
private fun playlistOrder(
|
||||
sort: PlaylistSort,
|
||||
descending: Boolean,
|
||||
): String =
|
||||
when (sort) {
|
||||
PlaylistSort.Name -> if (descending) PLAYLIST_ORDER_BY_NAME_REVERSED else PLAYLIST_ORDER_BY_NAME
|
||||
PlaylistSort.Added -> if (descending) PLAYLIST_ORDER_BY_ADDED_REVERSED else PLAYLIST_ORDER_BY_ADDED
|
||||
PlaylistSort.Updated -> if (descending) PLAYLIST_ORDER_BY_UPDATED_REVERSED else PLAYLIST_ORDER_BY_UPDATED
|
||||
}
|
||||
@@ -5,6 +5,9 @@ import androidx.paging.PagingConfig
|
||||
import androidx.paging.PagingData
|
||||
import androidx.paging.PagingSource
|
||||
import com.subtracks.data.db.SubtracksDatabase
|
||||
import com.subtracks.data.db.albumsDefaultQuery
|
||||
import com.subtracks.data.db.artistsDefaultQuery
|
||||
import com.subtracks.data.db.playlistsDefaultQuery
|
||||
import com.subtracks.data.model.Album
|
||||
import com.subtracks.data.model.AlbumSongItem
|
||||
import com.subtracks.data.model.Artist
|
||||
@@ -148,60 +151,64 @@ class LibraryRepository(
|
||||
pager(40) {
|
||||
val dao = db.libraryDao()
|
||||
val downloadedFilter = if (downloaded) 1 else 0
|
||||
when (sort) {
|
||||
AlbumSort.Name -> {
|
||||
if (descending) {
|
||||
dao.albumsByNameReversed(sourceId, starred.ordinal, search, downloadedFilter)
|
||||
} else {
|
||||
dao.albumsByName(sourceId, starred.ordinal, search, downloadedFilter)
|
||||
if (!downloaded) {
|
||||
dao.albumsPaging(albumsDefaultQuery(sourceId, starred.ordinal, search, sort, descending))
|
||||
} else {
|
||||
when (sort) {
|
||||
AlbumSort.Name -> {
|
||||
if (descending) {
|
||||
dao.albumsByNameReversed(sourceId, starred.ordinal, search, downloadedFilter)
|
||||
} else {
|
||||
dao.albumsByName(sourceId, starred.ordinal, search, downloadedFilter)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AlbumSort.Artist -> {
|
||||
if (descending) {
|
||||
dao.albumsByArtistReversed(sourceId, starred.ordinal, search, downloadedFilter)
|
||||
} else {
|
||||
dao.albumsByArtist(sourceId, starred.ordinal, search, downloadedFilter)
|
||||
AlbumSort.Artist -> {
|
||||
if (descending) {
|
||||
dao.albumsByArtistReversed(sourceId, starred.ordinal, search, downloadedFilter)
|
||||
} else {
|
||||
dao.albumsByArtist(sourceId, starred.ordinal, search, downloadedFilter)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AlbumSort.Year -> {
|
||||
if (descending) {
|
||||
dao.albumsByYearReversed(sourceId, starred.ordinal, search, downloadedFilter)
|
||||
} else {
|
||||
dao.albumsByYear(sourceId, starred.ordinal, search, downloadedFilter)
|
||||
AlbumSort.Year -> {
|
||||
if (descending) {
|
||||
dao.albumsByYearReversed(sourceId, starred.ordinal, search, downloadedFilter)
|
||||
} else {
|
||||
dao.albumsByYear(sourceId, starred.ordinal, search, downloadedFilter)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AlbumSort.Added -> {
|
||||
if (descending) {
|
||||
dao.albumsByRecentlyAddedReversed(sourceId, starred.ordinal, search, downloadedFilter)
|
||||
} else {
|
||||
dao.albumsByRecentlyAdded(sourceId, starred.ordinal, search, downloadedFilter)
|
||||
AlbumSort.Added -> {
|
||||
if (descending) {
|
||||
dao.albumsByRecentlyAddedReversed(sourceId, starred.ordinal, search, downloadedFilter)
|
||||
} else {
|
||||
dao.albumsByRecentlyAdded(sourceId, starred.ordinal, search, downloadedFilter)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AlbumSort.Starred -> {
|
||||
if (descending) {
|
||||
dao.albumsByStarredReversed(sourceId, starred.ordinal, search, downloadedFilter)
|
||||
} else {
|
||||
dao.albumsByStarred(sourceId, starred.ordinal, search, downloadedFilter)
|
||||
AlbumSort.Starred -> {
|
||||
if (descending) {
|
||||
dao.albumsByStarredReversed(sourceId, starred.ordinal, search, downloadedFilter)
|
||||
} else {
|
||||
dao.albumsByStarred(sourceId, starred.ordinal, search, downloadedFilter)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AlbumSort.Frequent -> {
|
||||
if (descending) {
|
||||
dao.albumsByFrequentReversed(sourceId, starred.ordinal, search, downloadedFilter)
|
||||
} else {
|
||||
dao.albumsByFrequent(sourceId, starred.ordinal, search, downloadedFilter)
|
||||
AlbumSort.Frequent -> {
|
||||
if (descending) {
|
||||
dao.albumsByFrequentReversed(sourceId, starred.ordinal, search, downloadedFilter)
|
||||
} else {
|
||||
dao.albumsByFrequent(sourceId, starred.ordinal, search, downloadedFilter)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AlbumSort.Recent -> {
|
||||
if (descending) {
|
||||
dao.albumsByRecentReversed(sourceId, starred.ordinal, search, downloadedFilter)
|
||||
} else {
|
||||
dao.albumsByRecent(sourceId, starred.ordinal, search, downloadedFilter)
|
||||
AlbumSort.Recent -> {
|
||||
if (descending) {
|
||||
dao.albumsByRecentReversed(sourceId, starred.ordinal, search, downloadedFilter)
|
||||
} else {
|
||||
dao.albumsByRecent(sourceId, starred.ordinal, search, downloadedFilter)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -218,44 +225,48 @@ class LibraryRepository(
|
||||
pager(60) {
|
||||
val dao = db.libraryDao()
|
||||
val downloadedFilter = if (downloaded) 1 else 0
|
||||
when (sort) {
|
||||
ArtistSort.Name -> {
|
||||
if (descending) {
|
||||
dao.artistsByNameReversed(sourceId, starred.ordinal, search, downloadedFilter)
|
||||
} else {
|
||||
dao.artistsByName(sourceId, starred.ordinal, search, downloadedFilter)
|
||||
if (!downloaded) {
|
||||
dao.artistsPaging(artistsDefaultQuery(sourceId, starred.ordinal, search, sort, descending))
|
||||
} else {
|
||||
when (sort) {
|
||||
ArtistSort.Name -> {
|
||||
if (descending) {
|
||||
dao.artistsByNameReversed(sourceId, starred.ordinal, search, downloadedFilter)
|
||||
} else {
|
||||
dao.artistsByName(sourceId, starred.ordinal, search, downloadedFilter)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ArtistSort.AlbumCount -> {
|
||||
if (descending) {
|
||||
dao.artistsByAlbumCountReversed(sourceId, starred.ordinal, search, downloadedFilter)
|
||||
} else {
|
||||
dao.artistsByAlbumCount(sourceId, starred.ordinal, search, downloadedFilter)
|
||||
ArtistSort.AlbumCount -> {
|
||||
if (descending) {
|
||||
dao.artistsByAlbumCountReversed(sourceId, starred.ordinal, search, downloadedFilter)
|
||||
} else {
|
||||
dao.artistsByAlbumCount(sourceId, starred.ordinal, search, downloadedFilter)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ArtistSort.Starred -> {
|
||||
if (descending) {
|
||||
dao.artistsByStarredReversed(sourceId, starred.ordinal, search, downloadedFilter)
|
||||
} else {
|
||||
dao.artistsByStarred(sourceId, starred.ordinal, search, downloadedFilter)
|
||||
ArtistSort.Starred -> {
|
||||
if (descending) {
|
||||
dao.artistsByStarredReversed(sourceId, starred.ordinal, search, downloadedFilter)
|
||||
} else {
|
||||
dao.artistsByStarred(sourceId, starred.ordinal, search, downloadedFilter)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ArtistSort.Frequent -> {
|
||||
if (descending) {
|
||||
dao.artistsByFrequentReversed(sourceId, starred.ordinal, search, downloadedFilter)
|
||||
} else {
|
||||
dao.artistsByFrequent(sourceId, starred.ordinal, search, downloadedFilter)
|
||||
ArtistSort.Frequent -> {
|
||||
if (descending) {
|
||||
dao.artistsByFrequentReversed(sourceId, starred.ordinal, search, downloadedFilter)
|
||||
} else {
|
||||
dao.artistsByFrequent(sourceId, starred.ordinal, search, downloadedFilter)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ArtistSort.Recent -> {
|
||||
if (descending) {
|
||||
dao.artistsByRecentReversed(sourceId, starred.ordinal, search, downloadedFilter)
|
||||
} else {
|
||||
dao.artistsByRecent(sourceId, starred.ordinal, search, downloadedFilter)
|
||||
ArtistSort.Recent -> {
|
||||
if (descending) {
|
||||
dao.artistsByRecentReversed(sourceId, starred.ordinal, search, downloadedFilter)
|
||||
} else {
|
||||
dao.artistsByRecent(sourceId, starred.ordinal, search, downloadedFilter)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -271,28 +282,32 @@ class LibraryRepository(
|
||||
pager(40) {
|
||||
val dao = db.libraryDao()
|
||||
val downloadedFilter = if (downloaded) 1 else 0
|
||||
when (sort) {
|
||||
PlaylistSort.Name -> {
|
||||
if (descending) {
|
||||
dao.playlistsByNameReversed(sourceId, search, downloadedFilter)
|
||||
} else {
|
||||
dao.playlistsByName(sourceId, search, downloadedFilter)
|
||||
if (!downloaded) {
|
||||
dao.playlistsPaging(playlistsDefaultQuery(sourceId, search, sort, descending))
|
||||
} else {
|
||||
when (sort) {
|
||||
PlaylistSort.Name -> {
|
||||
if (descending) {
|
||||
dao.playlistsByNameReversed(sourceId, search, downloadedFilter)
|
||||
} else {
|
||||
dao.playlistsByName(sourceId, search, downloadedFilter)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PlaylistSort.Added -> {
|
||||
if (descending) {
|
||||
dao.playlistsByAddedReversed(sourceId, search, downloadedFilter)
|
||||
} else {
|
||||
dao.playlistsByAdded(sourceId, search, downloadedFilter)
|
||||
PlaylistSort.Added -> {
|
||||
if (descending) {
|
||||
dao.playlistsByAddedReversed(sourceId, search, downloadedFilter)
|
||||
} else {
|
||||
dao.playlistsByAdded(sourceId, search, downloadedFilter)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PlaylistSort.Updated -> {
|
||||
if (descending) {
|
||||
dao.playlistsByUpdatedReversed(sourceId, search, downloadedFilter)
|
||||
} else {
|
||||
dao.playlistsByUpdated(sourceId, search, downloadedFilter)
|
||||
PlaylistSort.Updated -> {
|
||||
if (descending) {
|
||||
dao.playlistsByUpdatedReversed(sourceId, search, downloadedFilter)
|
||||
} else {
|
||||
dao.playlistsByUpdated(sourceId, search, downloadedFilter)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import androidx.room3.useReaderConnection
|
||||
import androidx.sqlite.driver.bundled.BundledSQLiteDriver
|
||||
import androidx.test.core.app.ApplicationProvider
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import com.subtracks.awaitUntil
|
||||
import com.subtracks.data.model.Album
|
||||
import com.subtracks.data.model.Artist
|
||||
import com.subtracks.data.model.DownloadStatus
|
||||
@@ -15,6 +16,9 @@ import com.subtracks.data.model.PlaylistSong
|
||||
import com.subtracks.data.model.Song
|
||||
import com.subtracks.data.model.SongDownload
|
||||
import com.subtracks.data.model.Source
|
||||
import com.subtracks.data.prefs.AlbumSort
|
||||
import com.subtracks.data.prefs.ArtistSort
|
||||
import com.subtracks.data.prefs.PlaylistSort
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.After
|
||||
@@ -24,6 +28,7 @@ import org.junit.Assert.assertTrue
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class LibraryDaoTest {
|
||||
@@ -703,6 +708,117 @@ class LibraryDaoTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun defaultPagingQueriesMatchTheDownloadFreeFilters() =
|
||||
runTest {
|
||||
val sourceId = source()
|
||||
val dao = db.libraryDao()
|
||||
dao.upsertAlbums(
|
||||
listOf(
|
||||
album(sourceId, "a", "Beta", year = 2000, starred = null).copy(albumArtist = "Zed"),
|
||||
album(sourceId, "b", "Alpha", year = 2010, starred = 5).copy(albumArtist = "Zed"),
|
||||
album(sourceId, "c", "Gamma", year = 1990, starred = null).copy(albumArtist = "Ann"),
|
||||
),
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
listOf("Alpha", "Beta", "Gamma"),
|
||||
dao.albumsPaging(albumsDefaultQuery(sourceId, 0, "", AlbumSort.Name, false)).page().map { it.name },
|
||||
)
|
||||
assertEquals(
|
||||
listOf("Gamma", "Beta", "Alpha"),
|
||||
dao.albumsPaging(albumsDefaultQuery(sourceId, 0, "", AlbumSort.Name, true)).page().map { it.name },
|
||||
)
|
||||
assertEquals(
|
||||
listOf("Alpha", "Beta", "Gamma"),
|
||||
dao.albumsPaging(albumsDefaultQuery(sourceId, 0, "", AlbumSort.Year, false)).page().map { it.name },
|
||||
)
|
||||
assertEquals(
|
||||
listOf("Gamma", "Beta", "Alpha"),
|
||||
dao.albumsPaging(albumsDefaultQuery(sourceId, 0, "", AlbumSort.Artist, false)).page().map { it.name },
|
||||
)
|
||||
assertEquals(
|
||||
listOf("Alpha"),
|
||||
dao.albumsPaging(albumsDefaultQuery(sourceId, 0, "alp", AlbumSort.Name, false)).page().map { it.name },
|
||||
)
|
||||
assertEquals(
|
||||
listOf("Gamma"),
|
||||
dao.albumsPaging(albumsDefaultQuery(sourceId, 0, "am", AlbumSort.Name, false)).page().map { it.name },
|
||||
)
|
||||
assertEquals(
|
||||
listOf("Alpha"),
|
||||
dao.albumsPaging(albumsDefaultQuery(sourceId, 1, "", AlbumSort.Name, false)).page().map { it.name },
|
||||
)
|
||||
assertEquals(
|
||||
listOf("Beta", "Gamma"),
|
||||
dao.albumsPaging(albumsDefaultQuery(sourceId, 2, "", AlbumSort.Name, false)).page().map { it.name },
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun defaultArtistAndPlaylistPagingQueriesMatchTheDownloadFreeFilters() =
|
||||
runTest {
|
||||
val sourceId = source()
|
||||
val dao = db.libraryDao()
|
||||
dao.upsertArtists(
|
||||
listOf(
|
||||
artist(sourceId, "ar-1", "Beta", albumCount = 3),
|
||||
artist(sourceId, "ar-2", "Alpha", albumCount = 1),
|
||||
),
|
||||
)
|
||||
dao.upsertPlaylists(
|
||||
listOf(
|
||||
playlist(sourceId, "p1", "Beta", created = 0, changed = 5),
|
||||
playlist(sourceId, "p2", "Alpha", created = 1, changed = 0),
|
||||
),
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
listOf("Alpha", "Beta"),
|
||||
dao.artistsPaging(artistsDefaultQuery(sourceId, 0, "a", ArtistSort.Name, false)).page().map { it.name },
|
||||
)
|
||||
assertEquals(
|
||||
listOf("Beta", "Alpha"),
|
||||
dao.artistsPaging(artistsDefaultQuery(sourceId, 0, "", ArtistSort.AlbumCount, false)).page().map { it.name },
|
||||
)
|
||||
assertEquals(
|
||||
listOf("Alpha", "Beta"),
|
||||
dao.playlistsPaging(playlistsDefaultQuery(sourceId, "a", PlaylistSort.Name, false)).page().map { it.name },
|
||||
)
|
||||
assertEquals(
|
||||
listOf("Alpha", "Beta"),
|
||||
dao.playlistsPaging(playlistsDefaultQuery(sourceId, "", PlaylistSort.Added, false)).page().map { it.name },
|
||||
)
|
||||
assertEquals(
|
||||
listOf("Beta", "Alpha"),
|
||||
dao.playlistsPaging(playlistsDefaultQuery(sourceId, "", PlaylistSort.Updated, false)).page().map { it.name },
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun downloadWritesDoNotInvalidateTheDefaultPagingSource() =
|
||||
runTest {
|
||||
val sourceId = source()
|
||||
val dao = db.libraryDao()
|
||||
dao.upsertAlbums(listOf(album(sourceId, "a", "Alpha", year = null, starred = null)))
|
||||
dao.upsertSongs(listOf(song(sourceId, "s1", "One", starred = null).copy(albumId = "a")))
|
||||
|
||||
val defaultInvalidated = AtomicBoolean(false)
|
||||
val default = dao.albumsPaging(albumsDefaultQuery(sourceId, 0, "", AlbumSort.Name, false))
|
||||
default.registerInvalidatedCallback { defaultInvalidated.set(true) }
|
||||
default.page()
|
||||
|
||||
val downloadedInvalidated = AtomicBoolean(false)
|
||||
val downloaded = dao.albumsByName(sourceId, 0, "", downloadedFilter = 1)
|
||||
downloaded.registerInvalidatedCallback { downloadedInvalidated.set(true) }
|
||||
downloaded.page()
|
||||
|
||||
db.downloadDao().upsert(SongDownload(sourceId, "s1", DownloadStatus.Completed))
|
||||
|
||||
awaitUntil("the downloaded list observes download writes") { downloadedInvalidated.get() }
|
||||
assertFalse("the default list must not observe download writes", defaultInvalidated.get())
|
||||
}
|
||||
|
||||
private suspend fun source(): Long =
|
||||
db.sourcesDao().insertSource(Source(name = "Navidrome", address = "http://localhost", isActive = true, createdAt = 0))
|
||||
|
||||
|
||||
Reference in New Issue
Block a user