From 018fcdef420768d6110f5b84cc5facb93d6261ee Mon Sep 17 00:00:00 2001 From: chr56 Date: Tue, 14 Jan 2025 16:10:27 +0800 Subject: [PATCH] [DynamicDatabaseLoader] redesign to fix orderless results --- .../mediastore/loaders/DatabaseLoaders.kt | 109 ++++++++---------- 1 file changed, 46 insertions(+), 63 deletions(-) diff --git a/app/src/main/java/player/phonograph/repo/mediastore/loaders/DatabaseLoaders.kt b/app/src/main/java/player/phonograph/repo/mediastore/loaders/DatabaseLoaders.kt index 1ee96f774..cb8c22fa5 100644 --- a/app/src/main/java/player/phonograph/repo/mediastore/loaders/DatabaseLoaders.kt +++ b/app/src/main/java/player/phonograph/repo/mediastore/loaders/DatabaseLoaders.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022~2023 chr_56 + * Copyright (c) 2022~2025 chr_56 */ package player.phonograph.repo.mediastore.loaders @@ -11,15 +11,23 @@ import player.phonograph.repo.database.SongPlayCountStore import player.phonograph.repo.mediastore.internal.intoSongs import player.phonograph.repo.mediastore.internal.querySongs import android.content.Context -import android.database.Cursor import android.provider.BaseColumns class TopTracksLoader(private val songPlayCountStore: SongPlayCountStore) : - DynamicDatabaseLoader(songPlayCountStore) { + DynamicDatabaseLoader(songPlayCountStore, NUMBER_OF_TOP_TRACKS) { - override fun queryCursorImpl(context: Context): Cursor? = - songPlayCountStore.getTopPlayedResults(NUMBER_OF_TOP_TRACKS) - .intoSongCursor(context, SongPlayCountStore.SongPlayCountColumns.ID) + override fun fetch(context: Context): LongArray? { + val cursor = songPlayCountStore.getTopPlayedResults(0) + val results = mutableListOf() + if (cursor.moveToFirst()) { + val idColumnIndex = cursor.getColumnIndex(SongPlayCountStore.SongPlayCountColumns.ID) + do { + val id = cursor.getLong(idColumnIndex) + results.add(id) + } while (cursor.moveToNext()) + } + return results.toLongArray() + } companion object { private const val NUMBER_OF_TOP_TRACKS = 150 @@ -30,71 +38,46 @@ class TopTracksLoader(private val songPlayCountStore: SongPlayCountStore) : class RecentlyPlayedTracksLoader(private val historyStore: HistoryStore) : DynamicDatabaseLoader(historyStore) { - override fun queryCursorImpl(context: Context): Cursor? = - historyStore.queryRecentIds() - .intoSongCursor(context, HistoryStore.RecentStoreColumns.ID) + override fun fetch(context: Context): LongArray? { + val cursor = historyStore.queryRecentIds() + val results = mutableListOf() + if (cursor.moveToFirst()) { + val idColumnIndex = cursor.getColumnIndex(HistoryStore.RecentStoreColumns.ID) + do { + val id = cursor.getLong(idColumnIndex) + results.add(id) + } while (cursor.moveToNext()) + } + return results.toLongArray() + } companion object { fun get() = GlobalContext.get().get() } } -abstract class DynamicDatabaseLoader(private val db: Any) { - - fun tracks(context: Context): List = queryCursorAndClear(context).intoSongs() - - protected abstract fun queryCursorImpl(context: Context): Cursor? - - protected fun queryCursorAndClear(context: Context): Cursor? { - - val songCursor = queryCursorImpl(context) ?: return null - +abstract class DynamicDatabaseLoader(private val db: Any, private val limit: Int = -1) { + + protected abstract fun fetch(context: Context): LongArray? + + fun tracks(context: Context): List { + val ids: LongArray = fetch(context) ?: return emptyList() + val songs = ids.map { id -> + val songs = querySongs( + context, + selection = "${BaseColumns._ID} = ?", + selectionValues = arrayOf(id.toString()) + ).intoSongs() + if (songs.isNotEmpty()) { + songs.first() + } else { + null + } + }.filterNotNull() if (db is ShallowDatabase) { - // clean up the databases with any ids not found - val exists = songIds(songCursor) - db.gc(exists) + db.gc(songs.map { it.id }) } - - return songCursor - } - - private fun songIds(songCursor: Cursor): List { - val exists = mutableListOf() - if (songCursor.moveToFirst()) { - val index = songCursor.getColumnIndex(BaseColumns._ID) - do { - val id = songCursor.getLong(index) - if (id > 0) exists.add(id) - } while (songCursor.moveToNext()) - } - return exists - } - - protected fun Cursor.intoSongCursor(context: Context, idColumnName: String): Cursor? = - use { cursor -> generateSongCursor(context, cursor, idColumnName) } - - protected fun generateSongCursor(context: Context, cursor: Cursor, idColumnName: String): Cursor? { - val count = cursor.count - val idColumnIndex = cursor.getColumnIndex(idColumnName) - - if (count < 0 || idColumnIndex < 0) return null - - cursor.moveToFirst() - val selectionPlaceHolder = when { - count > 1 -> "?" + ",?".repeat(count - 1) - count == 1 -> "?" - else -> return null // empty cursor - } - - val ids = Array(count) { - cursor.getLong(idColumnIndex).toString().also { cursor.moveToNext() } - } - - return querySongs( - context, - selection = "${BaseColumns._ID} IN ( $selectionPlaceHolder )", - selectionValues = ids - ) + return if (limit > 0) songs.take(limit) else songs } } \ No newline at end of file