From 17e5aed131441ca92bccdefa83f266a9b1bf8255 Mon Sep 17 00:00:00 2001 From: OxygenCobalt Date: Wed, 14 Apr 2021 11:14:17 -0600 Subject: [PATCH] Move playback state system to hashes Use unique-ish hashes in the playback state system instead of the less efficent and less reliable string system. This cuts save times in ~half and improves restore times by ~1/3. Yeah, this is like the 4th time I've changed this system but unless I have some major loader refactor I think this wont change again. --- .../auxio/database/BlacklistDatabase.kt | 1 + .../oxycblt/auxio/database/DatabaseUtils.kt | 10 ---- .../oxycblt/auxio/database/PlaybackState.kt | 16 +++--- .../auxio/database/PlaybackStateDatabase.kt | 38 ++++++-------- .../org/oxycblt/auxio/database/QueueItem.kt | 12 ++--- .../java/org/oxycblt/auxio/music/Models.kt | 52 ++++++++++++++----- .../org/oxycblt/auxio/music/MusicLoader.kt | 16 +++--- .../org/oxycblt/auxio/music/MusicStore.kt | 14 ++--- .../playback/state/PlaybackStateManager.kt | 31 +++++------ .../auxio/settings/SettingsListFragment.kt | 3 +- .../org/oxycblt/auxio/ui/InterfaceUtils.kt | 19 +++++++ .../java/org/oxycblt/auxio/ui/MemberBinder.kt | 5 +- 12 files changed, 121 insertions(+), 96 deletions(-) diff --git a/app/src/main/java/org/oxycblt/auxio/database/BlacklistDatabase.kt b/app/src/main/java/org/oxycblt/auxio/database/BlacklistDatabase.kt index ac20d0c13..a5be2a4f0 100644 --- a/app/src/main/java/org/oxycblt/auxio/database/BlacklistDatabase.kt +++ b/app/src/main/java/org/oxycblt/auxio/database/BlacklistDatabase.kt @@ -6,6 +6,7 @@ import android.database.sqlite.SQLiteDatabase import android.database.sqlite.SQLiteOpenHelper import androidx.core.database.sqlite.transaction import org.oxycblt.auxio.logD +import org.oxycblt.auxio.ui.assertBackgroundThread /** * Database for storing blacklisted paths. diff --git a/app/src/main/java/org/oxycblt/auxio/database/DatabaseUtils.kt b/app/src/main/java/org/oxycblt/auxio/database/DatabaseUtils.kt index 2d7b5d1ee..9589ebca0 100644 --- a/app/src/main/java/org/oxycblt/auxio/database/DatabaseUtils.kt +++ b/app/src/main/java/org/oxycblt/auxio/database/DatabaseUtils.kt @@ -2,7 +2,6 @@ package org.oxycblt.auxio.database import android.database.Cursor import android.database.sqlite.SQLiteDatabase -import android.os.Looper /** * Shortcut for querying all items in a database and running [block] with the cursor returned. @@ -10,12 +9,3 @@ import android.os.Looper */ fun SQLiteDatabase.queryAll(tableName: String, block: (Cursor) -> R) = query(tableName, null, null, null, null, null, null)?.use(block) - -/** - * Assert that we are on a background thread. - */ -fun assertBackgroundThread() { - check(Looper.myLooper() != Looper.getMainLooper()) { - "Database operations must be ran on a background thread." - } -} diff --git a/app/src/main/java/org/oxycblt/auxio/database/PlaybackState.kt b/app/src/main/java/org/oxycblt/auxio/database/PlaybackState.kt index fa5a4cc22..d8e845375 100644 --- a/app/src/main/java/org/oxycblt/auxio/database/PlaybackState.kt +++ b/app/src/main/java/org/oxycblt/auxio/database/PlaybackState.kt @@ -3,8 +3,8 @@ package org.oxycblt.auxio.database /** * A database entity that stores a compressed variant of the current playback state. * @property id - The database key for this state - * @property songName - The song that is currently playing - * @property parentName - The parent that is being played from [-1 if none] + * @property songHash - The hash for the currently playing song + * @property parentHash - The hash for the currently playing parent * @property index - The current index in the queue. * @property mode - The integer form of the current [org.oxycblt.auxio.playback.state.PlaybackMode] * @property isShuffling - A bool for if the queue was shuffled @@ -14,10 +14,9 @@ package org.oxycblt.auxio.database */ data class PlaybackState( val id: Long = 0L, - val songName: String = "", - val songAlbumName: String = "", + val songHash: Int, val position: Long, - val parentName: String = "", + val parentHash: Int, val index: Int, val mode: Int, val isShuffling: Boolean, @@ -26,11 +25,10 @@ data class PlaybackState( ) { companion object { const val COLUMN_ID = "state_id" - const val COLUMN_SONG_NAME = "cur_song_name" - const val COLUMN_SONG_ALBUM_NAME = "cur_song_album" + const val COLUMN_SONG_HASH = "song" const val COLUMN_POSITION = "position" - const val COLUMN_PARENT_NAME = "parent_name" - const val COLUMN_INDEX = "state_index" + const val COLUMN_PARENT_HASH = "parent" + const val COLUMN_INDEX = "_index" const val COLUMN_MODE = "mode" const val COLUMN_IS_SHUFFLING = "is_shuffling" const val COLUMN_LOOP_MODE = "loop_mode" diff --git a/app/src/main/java/org/oxycblt/auxio/database/PlaybackStateDatabase.kt b/app/src/main/java/org/oxycblt/auxio/database/PlaybackStateDatabase.kt index 8e854680b..33940fd6e 100644 --- a/app/src/main/java/org/oxycblt/auxio/database/PlaybackStateDatabase.kt +++ b/app/src/main/java/org/oxycblt/auxio/database/PlaybackStateDatabase.kt @@ -4,9 +4,9 @@ import android.content.ContentValues import android.content.Context import android.database.sqlite.SQLiteDatabase import android.database.sqlite.SQLiteOpenHelper -import androidx.core.database.getStringOrNull import androidx.core.database.sqlite.transaction import org.oxycblt.auxio.logD +import org.oxycblt.auxio.ui.assertBackgroundThread /** * A SQLite database for managing the persistent playback state and queue. @@ -57,10 +57,9 @@ class PlaybackStateDatabase(context: Context) : */ private fun constructStateTable(command: StringBuilder): StringBuilder { command.append("${PlaybackState.COLUMN_ID} LONG PRIMARY KEY,") - .append("${PlaybackState.COLUMN_SONG_NAME} TEXT NOT NULL,") - .append("${PlaybackState.COLUMN_SONG_ALBUM_NAME} TEXT NOT NULL,") + .append("${PlaybackState.COLUMN_SONG_HASH} INTEGER NOT NULL,") .append("${PlaybackState.COLUMN_POSITION} LONG NOT NULL,") - .append("${PlaybackState.COLUMN_PARENT_NAME} TEXT NOT NULL,") + .append("${PlaybackState.COLUMN_PARENT_HASH} INTEGER NOT NULL,") .append("${PlaybackState.COLUMN_INDEX} INTEGER NOT NULL,") .append("${PlaybackState.COLUMN_MODE} INTEGER NOT NULL,") .append("${PlaybackState.COLUMN_IS_SHUFFLING} BOOLEAN NOT NULL,") @@ -75,8 +74,8 @@ class PlaybackStateDatabase(context: Context) : */ private fun constructQueueTable(command: StringBuilder): StringBuilder { command.append("${QueueItem.COLUMN_ID} LONG PRIMARY KEY,") - .append("${QueueItem.COLUMN_SONG_NAME} TEXT NOT NULL,") - .append("${QueueItem.COLUMN_ALBUM_NAME} TEXT NOT NULL,") + .append("${QueueItem.COLUMN_SONG_HASH} INTEGER NOT NULL,") + .append("${QueueItem.COLUMN_ALBUM_HASH} INTEGER NOT NULL,") .append("${QueueItem.COLUMN_IS_USER_QUEUE} BOOLEAN NOT NULL)") return command @@ -97,10 +96,9 @@ class PlaybackStateDatabase(context: Context) : val stateData = ContentValues(10).apply { put(PlaybackState.COLUMN_ID, state.id) - put(PlaybackState.COLUMN_SONG_NAME, state.songName) - put(PlaybackState.COLUMN_SONG_ALBUM_NAME, state.songAlbumName) + put(PlaybackState.COLUMN_SONG_HASH, state.songHash) put(PlaybackState.COLUMN_POSITION, state.position) - put(PlaybackState.COLUMN_PARENT_NAME, state.parentName) + put(PlaybackState.COLUMN_PARENT_HASH, state.parentHash) put(PlaybackState.COLUMN_INDEX, state.index) put(PlaybackState.COLUMN_MODE, state.mode) put(PlaybackState.COLUMN_IS_SHUFFLING, state.isShuffling) @@ -126,10 +124,9 @@ class PlaybackStateDatabase(context: Context) : readableDatabase.queryAll(TABLE_NAME_STATE) { cursor -> if (cursor.count == 0) return@queryAll - val songIndex = cursor.getColumnIndexOrThrow(PlaybackState.COLUMN_SONG_NAME) - val albumIndex = cursor.getColumnIndexOrThrow(PlaybackState.COLUMN_SONG_ALBUM_NAME) + val songIndex = cursor.getColumnIndexOrThrow(PlaybackState.COLUMN_SONG_HASH) val posIndex = cursor.getColumnIndexOrThrow(PlaybackState.COLUMN_POSITION) - val parentIndex = cursor.getColumnIndexOrThrow(PlaybackState.COLUMN_PARENT_NAME) + val parentIndex = cursor.getColumnIndexOrThrow(PlaybackState.COLUMN_PARENT_HASH) val indexIndex = cursor.getColumnIndexOrThrow(PlaybackState.COLUMN_INDEX) val modeIndex = cursor.getColumnIndexOrThrow(PlaybackState.COLUMN_MODE) val shuffleIndex = cursor.getColumnIndexOrThrow(PlaybackState.COLUMN_IS_SHUFFLING) @@ -141,10 +138,9 @@ class PlaybackStateDatabase(context: Context) : cursor.moveToFirst() state = PlaybackState( - songName = cursor.getStringOrNull(songIndex) ?: "", - songAlbumName = cursor.getStringOrNull(albumIndex) ?: "", + songHash = cursor.getInt(songIndex), position = cursor.getLong(posIndex), - parentName = cursor.getStringOrNull(parentIndex) ?: "", + parentHash = cursor.getInt(parentIndex), index = cursor.getInt(indexIndex), mode = cursor.getInt(modeIndex), isShuffling = cursor.getInt(shuffleIndex) == 1, @@ -183,8 +179,8 @@ class PlaybackStateDatabase(context: Context) : val itemData = ContentValues(4).apply { put(QueueItem.COLUMN_ID, item.id) - put(QueueItem.COLUMN_SONG_NAME, item.songName) - put(QueueItem.COLUMN_ALBUM_NAME, item.albumName) + put(QueueItem.COLUMN_SONG_HASH, item.songHash) + put(QueueItem.COLUMN_ALBUM_HASH, item.albumHash) put(QueueItem.COLUMN_IS_USER_QUEUE, item.isUserQueue) } @@ -213,15 +209,15 @@ class PlaybackStateDatabase(context: Context) : if (cursor.count == 0) return@queryAll val idIndex = cursor.getColumnIndexOrThrow(QueueItem.COLUMN_ID) - val songIdIndex = cursor.getColumnIndexOrThrow(QueueItem.COLUMN_SONG_NAME) - val albumIdIndex = cursor.getColumnIndexOrThrow(QueueItem.COLUMN_ALBUM_NAME) + val songIdIndex = cursor.getColumnIndexOrThrow(QueueItem.COLUMN_SONG_HASH) + val albumIdIndex = cursor.getColumnIndexOrThrow(QueueItem.COLUMN_ALBUM_HASH) val isUserQueueIndex = cursor.getColumnIndexOrThrow(QueueItem.COLUMN_IS_USER_QUEUE) while (cursor.moveToNext()) { queueItems += QueueItem( id = cursor.getLong(idIndex), - songName = cursor.getStringOrNull(songIdIndex) ?: "", - albumName = cursor.getStringOrNull(albumIdIndex) ?: "", + songHash = cursor.getInt(songIdIndex), + albumHash = cursor.getInt(albumIdIndex), isUserQueue = cursor.getInt(isUserQueueIndex) == 1 ) } diff --git a/app/src/main/java/org/oxycblt/auxio/database/QueueItem.kt b/app/src/main/java/org/oxycblt/auxio/database/QueueItem.kt index 53d9d6a84..cfe97e190 100644 --- a/app/src/main/java/org/oxycblt/auxio/database/QueueItem.kt +++ b/app/src/main/java/org/oxycblt/auxio/database/QueueItem.kt @@ -3,21 +3,21 @@ package org.oxycblt.auxio.database /** * A database entity that stores a simplified representation of a song in a queue. * @property id The database entity's id - * @property songName The song name for this queue item - * @property albumName The album name for this queue item, used to make searching quicker + * @property songHash The hash for the song represented + * @property albumHash The hash for the album represented * @property isUserQueue A bool for if this queue item is a user queue item or not * @author OxygenCobalt */ data class QueueItem( var id: Long = 0L, - val songName: String = "", - val albumName: String = "", + val songHash: Int, + val albumHash: Int, val isUserQueue: Boolean = false ) { companion object { const val COLUMN_ID = "id" - const val COLUMN_SONG_NAME = "song_name" - const val COLUMN_ALBUM_NAME = "album_name" + const val COLUMN_SONG_HASH = "song" + const val COLUMN_ALBUM_HASH = "album" const val COLUMN_IS_USER_QUEUE = "is_user_queue" } } diff --git a/app/src/main/java/org/oxycblt/auxio/music/Models.kt b/app/src/main/java/org/oxycblt/auxio/music/Models.kt index 14eebde04..a2ad9e633 100644 --- a/app/src/main/java/org/oxycblt/auxio/music/Models.kt +++ b/app/src/main/java/org/oxycblt/auxio/music/Models.kt @@ -4,9 +4,6 @@ import android.net.Uri // --- MUSIC MODELS --- -// TODO: Implement some kind of hash system, removing the need to redundant names but also without the volatility of id -// They need to be completely unique, however, and from whatever information I have about them on creation - /** * The base data object for all music. * @property id The ID that is assigned to this object @@ -20,10 +17,18 @@ sealed class BaseModel { /** * [BaseModel] variant that denotes that this object is a parent of other data objects, such * as an [Album] or [Artist] - * @property displayName Name that handles the usage of [Genre.resolvedName] and the normal [BaseModel.name] + * @property hash A versatile, unique(ish) hash used for databases + * @property displayName Name that handles the usage of [Genre.resolvedName] + * and the normal [BaseModel.name] */ sealed class Parent : BaseModel() { - val displayName: String get() = if (this is Genre) resolvedName else name + abstract val hash: Int + + val displayName: String get() = if (this is Genre) { + resolvedName + } else { + name + } } /** @@ -38,6 +43,7 @@ sealed class Parent : BaseModel() { * These are not ensured to be linked due to possible quirks in the genre loading system. * @property seconds The Song's duration in seconds * @property formattedDuration The Song's duration as a duration string. + * @property hash A versatile, unique(ish) hash used for databases */ data class Song( override val id: Long = -1, @@ -45,7 +51,7 @@ data class Song( val fileName: String, val albumId: Long = -1, val track: Int = -1, - val duration: Long = 0, + val duration: Long = 0 ) : BaseModel() { private var mAlbum: Album? = null private var mGenre: Genre? = null @@ -56,6 +62,8 @@ data class Song( val seconds = duration / 1000 val formattedDuration = seconds.toDuration() + val hash = songHash() + fun linkAlbum(album: Album) { if (mAlbum == null) { mAlbum = album @@ -67,6 +75,13 @@ data class Song( mGenre = genre } } + + private fun songHash(): Int { + var result = name.hashCode() + result = 31 * result + track + result = 31 * result + duration.hashCode() + return result + } } /** @@ -94,6 +109,8 @@ data class Album( val totalDuration: String get() = songs.sumOf { it.seconds }.toDuration() + override val hash = albumHash() + fun linkArtist(artist: Artist) { mArtist = artist } @@ -104,6 +121,13 @@ data class Album( mSongs.add(song) } } + + private fun albumHash(): Int { + var result = name.hashCode() + result = 31 * result + artistName.hashCode() + result = 31 * result + year + return result + } } /** @@ -117,12 +141,6 @@ data class Artist( override val name: String, val albums: List ) : Parent() { - init { - albums.forEach { album -> - album.linkArtist(this) - } - } - val genre: Genre? by lazy { // Get the genre that corresponds to the most songs in this artist, which would be // the most "Prominent" genre. @@ -132,6 +150,14 @@ data class Artist( val songs: List by lazy { albums.flatMap { it.songs } } + + override val hash = name.hashCode() + + init { + albums.forEach { album -> + album.linkArtist(this) + } + } } /** @@ -153,6 +179,8 @@ data class Genre( val totalDuration: String get() = songs.sumOf { it.seconds }.toDuration() + override val hash = name.hashCode() + fun linkSong(song: Song) { mSongs.add(song) song.linkGenre(this) diff --git a/app/src/main/java/org/oxycblt/auxio/music/MusicLoader.kt b/app/src/main/java/org/oxycblt/auxio/music/MusicLoader.kt index 475abd0f0..7e79e2047 100644 --- a/app/src/main/java/org/oxycblt/auxio/music/MusicLoader.kt +++ b/app/src/main/java/org/oxycblt/auxio/music/MusicLoader.kt @@ -13,8 +13,12 @@ import org.oxycblt.auxio.logD /** * Class that loads/constructs [Genre]s, [Artist]s, [Album]s, and [Song] objects from the filesystem - * TODO: Use album artist instead of artist tag. * @author OxygenCobalt + * + * FIXME: Here's a catalog of problems that I already know about with this abomination + * - Does not support the album artist tag + * - All loading is done at startup [Not efficent for large libraries, would require massive arch retooling to fix] + * - Genre system is a bottleneck [Nothing I can do about it, MediaStore is garbage] */ class MusicLoader(private val context: Context) { var genres = mutableListOf() @@ -94,9 +98,9 @@ class MusicLoader(private val context: Context) { Albums._ID, // 0 Albums.ALBUM, // 1 Albums.ARTIST, // 2 - Albums.FIRST_YEAR, // 3 + Albums.FIRST_YEAR, // 4 ), - null, null, + "", null, Albums.DEFAULT_SORT_ORDER ) @@ -106,13 +110,13 @@ class MusicLoader(private val context: Context) { albumCursor?.use { cursor -> val idIndex = cursor.getColumnIndexOrThrow(Albums._ID) val nameIndex = cursor.getColumnIndexOrThrow(Albums.ALBUM) - val artistIdIndex = cursor.getColumnIndexOrThrow(Albums.ARTIST) + val artistNameIndex = cursor.getColumnIndexOrThrow(Albums.ARTIST) val yearIndex = cursor.getColumnIndexOrThrow(Albums.FIRST_YEAR) while (cursor.moveToNext()) { val id = cursor.getLong(idIndex) val name = cursor.getString(nameIndex) ?: albumPlaceholder - var artistName = cursor.getString(artistIdIndex) ?: artistPlaceholder + var artistName = cursor.getString(artistNameIndex) ?: artistPlaceholder val year = cursor.getInt(yearIndex) val coverUri = id.toAlbumArtURI() @@ -144,7 +148,7 @@ class MusicLoader(private val context: Context) { Media.TITLE, // 2 Media.ALBUM_ID, // 3 Media.TRACK, // 4 - Media.DURATION // 5 + Media.DURATION, // 5 ), selector, args, Media.DEFAULT_SORT_ORDER diff --git a/app/src/main/java/org/oxycblt/auxio/music/MusicStore.kt b/app/src/main/java/org/oxycblt/auxio/music/MusicStore.kt index e3a688836..574a4d36a 100644 --- a/app/src/main/java/org/oxycblt/auxio/music/MusicStore.kt +++ b/app/src/main/java/org/oxycblt/auxio/music/MusicStore.kt @@ -75,20 +75,14 @@ class MusicStore private constructor() { } /** - * Find a song from this instance in a safe manner. - * Using a normal search of the songs list runs the risk of getting the *wrong* song with - * the same name, so the album name is also used to fix the above problem. - * FIXME: Artist names are more unique than album names, use those - * @param name The name of the song - * @param albumName The name of the song's album. - * @return The song requested, null if there isnt one. + * Find a song in a faster manner using a hash for its album as well. */ - fun findSong(name: String, albumName: String): Song? { - return albums.find { it.name == albumName }?.songs?.find { it.name == name } + fun findSongFast(songHash: Int, albumHash: Int): Song? { + return albums.find { it.hash == albumHash }?.songs?.find { it.hash == songHash } } /** - * Find a song for a [uri], this is similar to [findSong], but with some kind of content uri. + * Find a song for a [uri], this is similar to [findSongFast], but with some kind of content uri. * @return The corresponding [Song] for this [uri], null if there isnt one. */ fun findSongForUri(uri: Uri, resolver: ContentResolver): Song? { diff --git a/app/src/main/java/org/oxycblt/auxio/playback/state/PlaybackStateManager.kt b/app/src/main/java/org/oxycblt/auxio/playback/state/PlaybackStateManager.kt index 801d5842f..8f36fa6f1 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/state/PlaybackStateManager.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/state/PlaybackStateManager.kt @@ -27,7 +27,6 @@ import org.oxycblt.auxio.settings.SettingsManager * All access should be done with [PlaybackStateManager.getInstance]. * * TODO: Queues should reflect sort mode - * TODO: Update loop mode to actually make sense [#13] * @author OxygenCobalt */ class PlaybackStateManager private constructor() { @@ -605,10 +604,9 @@ class PlaybackStateManager private constructor() { */ private fun packToPlaybackState(): PlaybackState { return PlaybackState( - songName = mSong?.name ?: "", - songAlbumName = mSong?.album?.name ?: "", + songHash = mSong?.hash ?: Int.MIN_VALUE, position = mPosition, - parentName = mParent?.name ?: "", + parentHash = mParent?.hash ?: Int.MIN_VALUE, index = mIndex, mode = mMode.toInt(), isShuffling = mIsShuffling, @@ -625,11 +623,11 @@ class PlaybackStateManager private constructor() { // Do queue setup first mMode = PlaybackMode.fromInt(playbackState.mode) ?: PlaybackMode.ALL_SONGS - mParent = findParent(playbackState.parentName, mMode) + mParent = findParent(playbackState.parentHash, mMode) mIndex = playbackState.index // Then set up the current state - mSong = musicStore.findSong(playbackState.songName, playbackState.songAlbumName) + mSong = musicStore.songs.find { it.hash == playbackState.songHash } mLoopMode = LoopMode.fromInt(playbackState.loopMode) ?: LoopMode.NONE mIsShuffling = playbackState.isShuffling mIsInUserQueue = playbackState.inUserQueue @@ -647,12 +645,12 @@ class PlaybackStateManager private constructor() { var queueItemId = 0L mUserQueue.forEach { song -> - unified.add(QueueItem(queueItemId, song.name, song.album.name, true)) + unified.add(QueueItem(queueItemId, song.hash, song.album.hash, true)) queueItemId++ } mQueue.forEach { song -> - unified.add(QueueItem(queueItemId, song.name, song.album.name, false)) + unified.add(QueueItem(queueItemId, song.hash, song.album.hash, false)) queueItemId++ } @@ -664,8 +662,8 @@ class PlaybackStateManager private constructor() { * @param queueItems The list of [QueueItem]s to unpack. */ private fun unpackQueue(queueItems: List) { - queueItems.forEach { item -> - musicStore.findSong(item.songName, item.albumName)?.let { song -> + for (item in queueItems) { + musicStore.findSongFast(item.songHash, item.albumHash)?.let { song -> if (item.isUserQueue) { mUserQueue.add(song) } else { @@ -689,15 +687,14 @@ class PlaybackStateManager private constructor() { } /** - * Get a [Parent] from music store given a [name] and playback [mode]. + * Get a [Parent] from music store given a [hash] and PlaybackMode [mode]. */ - private fun findParent(name: String, mode: PlaybackMode): Parent? { + private fun findParent(hash: Int, mode: PlaybackMode): Parent? { return when (mode) { - PlaybackMode.IN_GENRE -> musicStore.genres.find { it.name == name } - PlaybackMode.IN_ARTIST -> musicStore.artists.find { it.name == name } - PlaybackMode.IN_ALBUM -> musicStore.albums.find { it.name == name } - - else -> null + PlaybackMode.IN_GENRE -> musicStore.genres.find { it.hash == hash } + PlaybackMode.IN_ARTIST -> musicStore.artists.find { it.hash == hash } + PlaybackMode.IN_ALBUM -> musicStore.albums.find { it.hash == hash } + PlaybackMode.ALL_SONGS -> null } } diff --git a/app/src/main/java/org/oxycblt/auxio/settings/SettingsListFragment.kt b/app/src/main/java/org/oxycblt/auxio/settings/SettingsListFragment.kt index cbb072f22..520923a76 100644 --- a/app/src/main/java/org/oxycblt/auxio/settings/SettingsListFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/settings/SettingsListFragment.kt @@ -18,6 +18,7 @@ import org.oxycblt.auxio.settings.blacklist.BlacklistDialog import org.oxycblt.auxio.settings.ui.IntListPrefDialog import org.oxycblt.auxio.settings.ui.IntListPreference import org.oxycblt.auxio.ui.Accent +import org.oxycblt.auxio.ui.showToast /** * The actual fragment containing the settings menu. Inherits [PreferenceFragmentCompat]. @@ -129,7 +130,7 @@ class SettingsListFragment : PreferenceFragmentCompat() { SettingsManager.KEY_SAVE_STATE -> { onPreferenceClickListener = Preference.OnPreferenceClickListener { playbackModel.savePlaybackState(requireContext()) { - requireContext().getString(R.string.label_state_saved) + requireContext().showToast(R.string.label_state_saved) } true diff --git a/app/src/main/java/org/oxycblt/auxio/ui/InterfaceUtils.kt b/app/src/main/java/org/oxycblt/auxio/ui/InterfaceUtils.kt index 6c1912e88..5c1b33007 100644 --- a/app/src/main/java/org/oxycblt/auxio/ui/InterfaceUtils.kt +++ b/app/src/main/java/org/oxycblt/auxio/ui/InterfaceUtils.kt @@ -9,6 +9,7 @@ import android.graphics.Point import android.graphics.drawable.AnimatedVectorDrawable import android.graphics.drawable.Drawable import android.os.Build +import android.os.Looper import android.util.DisplayMetrics import android.util.TypedValue import android.view.LayoutInflater @@ -138,6 +139,24 @@ fun Context.showToast(@StringRes str: Int) { Toast.makeText(applicationContext, getString(str), Toast.LENGTH_SHORT).show() } +/** + * Assert that we are on a background thread. + */ +fun assertBackgroundThread() { + check(Looper.myLooper() != Looper.getMainLooper()) { + "This operation must be ran on a background thread." + } +} + +/** + * Assert that we are on a foreground thread. + */ +fun assertMainThread() { + check(Looper.myLooper() == Looper.getMainLooper()) { + "This operation must be ran on the main thread" + } +} + // --- CONFIGURATION --- /** diff --git a/app/src/main/java/org/oxycblt/auxio/ui/MemberBinder.kt b/app/src/main/java/org/oxycblt/auxio/ui/MemberBinder.kt index d0ddd6ea2..2faabc79f 100644 --- a/app/src/main/java/org/oxycblt/auxio/ui/MemberBinder.kt +++ b/app/src/main/java/org/oxycblt/auxio/ui/MemberBinder.kt @@ -1,6 +1,5 @@ package org.oxycblt.auxio.ui -import android.os.Looper import android.view.LayoutInflater import androidx.databinding.ViewDataBinding import androidx.fragment.app.Fragment @@ -42,9 +41,7 @@ class MemberBinder( } override fun getValue(thisRef: Fragment, property: KProperty<*>): T { - check(Looper.myLooper() == Looper.getMainLooper()) { - "View can only be accessed on the main thread." - } + assertMainThread() val binding = fragmentBinding