diff --git a/app/src/main/java/org/oxycblt/auxio/music/IndexerService.kt b/app/src/main/java/org/oxycblt/auxio/music/IndexerService.kt index d6fe5fc8b..8641f4b59 100644 --- a/app/src/main/java/org/oxycblt/auxio/music/IndexerService.kt +++ b/app/src/main/java/org/oxycblt/auxio/music/IndexerService.kt @@ -47,8 +47,6 @@ import org.oxycblt.auxio.util.logD * * TODO: Add file observing * - * TODO: Audit usages of synchronized - * * TODO: Rework UI flow once again */ class IndexerService : Service(), Indexer.Controller, Settings.Callback { 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 abf28922d..f9832d59d 100644 --- a/app/src/main/java/org/oxycblt/auxio/music/MusicStore.kt +++ b/app/src/main/java/org/oxycblt/auxio/music/MusicStore.kt @@ -35,12 +35,14 @@ class MusicStore private constructor() { private set /** Add a callback to this instance. Make sure to remove it when done. */ + @Synchronized fun addCallback(callback: Callback) { callback.onLibraryChanged(library) callbacks.add(callback) } /** Remove a callback from this instance. */ + @Synchronized fun removeCallback(callback: Callback) { callbacks.remove(callback) } @@ -82,7 +84,6 @@ class MusicStore private constructor() { } fun sanitize(song: Song) = songs.find { it.id == song.id } - fun sanitize(songs: List) = songs.mapNotNull { sanitize(it) } fun sanitize(album: Album) = albums.find { it.id == album.id } fun sanitize(artist: Artist) = artists.find { it.id == artist.id } fun sanitize(genre: Genre) = genres.find { it.id == genre.id } diff --git a/app/src/main/java/org/oxycblt/auxio/playback/state/PlaybackStateDatabase.kt b/app/src/main/java/org/oxycblt/auxio/playback/state/PlaybackStateDatabase.kt index cd934fe4f..3749812ae 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/state/PlaybackStateDatabase.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/state/PlaybackStateDatabase.kt @@ -101,7 +101,6 @@ class PlaybackStateDatabase(context: Context) : // --- INTERFACE FUNCTIONS --- - @Synchronized fun read(library: MusicStore.Library): SavedState? { requireBackgroundThread() @@ -186,7 +185,6 @@ class PlaybackStateDatabase(context: Context) : } /** Clear the previously written [SavedState] and write a new one. */ - @Synchronized fun write(state: SavedState) { requireBackgroundThread() 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 451ae8d2a..0ea615937 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 @@ -360,48 +360,59 @@ class PlaybackStateManager private constructor() { /** Restore the state from the [database] */ suspend fun restoreState(database: PlaybackStateDatabase) { val library = musicStore.library ?: return - withContext(Dispatchers.IO) { readImpl(database, library) }?.let(::restoreImpl) - isInitialized = true + val state = withContext(Dispatchers.IO) { database.read(library) } + + synchronized(this) { + if (state != null) { + applyStateImpl(state) + } + + isInitialized = true + } } /** Save the current state to the [database]. */ suspend fun saveState(database: PlaybackStateDatabase) { logD("Saving state to DB") - // Pack the entire state and save it to the database. - withContext(Dispatchers.IO) { saveImpl(database) } + val state = synchronized(this) { makeStateImpl() } + + withContext(Dispatchers.IO) { database.write(state) } } suspend fun sanitize(database: PlaybackStateDatabase, newLibrary: MusicStore.Library) { // Since we need to sanitize the state and re-save it for consistency, take the // easy way out and just write a new state and restore from it. Don't really care. logD("Sanitizing state") - isPlaying = false val state = - withContext(Dispatchers.IO) { - saveImpl(database) - readImpl(database, newLibrary) + synchronized(this) { + isPlaying = false + makeStateImpl() } - state?.let(::restoreImpl) + val sanitizedState = + withContext(Dispatchers.IO) { + database.write(state) + database.read(newLibrary) + } + + synchronized(this) { + if (sanitizedState != null) { + applyStateImpl(state) + } + } } - private fun readImpl( - database: PlaybackStateDatabase, - library: MusicStore.Library - ): PlaybackStateDatabase.SavedState? { - logD("Getting state from DB") + private fun makeStateImpl() = + PlaybackStateDatabase.SavedState( + index = index, + parent = parent, + queue = _queue, + positionMs = positionMs, + isShuffled = isShuffled, + repeatMode = repeatMode) - val start = System.currentTimeMillis() - val state = database.read(library) - - logD("State read completed successfully in ${System.currentTimeMillis() - start}ms") - - return state - } - - @Synchronized - private fun restoreImpl(state: PlaybackStateDatabase.SavedState) { + private fun applyStateImpl(state: PlaybackStateDatabase.SavedState) { index = state.index parent = state.parent _queue = state.queue.toMutableList() @@ -414,23 +425,6 @@ class PlaybackStateManager private constructor() { notifyShuffledChanged() } - @Synchronized - private fun saveImpl(database: PlaybackStateDatabase) { - val start = System.currentTimeMillis() - - database.write( - PlaybackStateDatabase.SavedState( - index = index, - parent = parent, - queue = _queue, - positionMs = positionMs, - isShuffled = isShuffled, - repeatMode = repeatMode)) - - this@PlaybackStateManager.logD( - "State save completed successfully in ${System.currentTimeMillis() - start}ms") - } - // --- CALLBACKS --- private fun notifyIndexMoved() { diff --git a/app/src/main/java/org/oxycblt/auxio/util/FrameworkUtil.kt b/app/src/main/java/org/oxycblt/auxio/util/FrameworkUtil.kt index 142334743..9eaa9b2ed 100644 --- a/app/src/main/java/org/oxycblt/auxio/util/FrameworkUtil.kt +++ b/app/src/main/java/org/oxycblt/auxio/util/FrameworkUtil.kt @@ -236,8 +236,8 @@ val AndroidViewModel.application: Application fun SQLiteDatabase.queryAll(tableName: String, block: (Cursor) -> R) = query(tableName, null, null, null, null, null, null)?.use(block) -// Note: ViewCompat.setOnApplyWindowInsets is a horrible buggy mess, so we use the native method -// and convert the insets as needed to their compat forms. +// Note: WindowInsetsCompat and it's related methods are a non-functional mess that does not +// work for Auxio's use-case. Use our own methods instead. /** * Resolve system bar insets in a version-aware manner. This can be used to apply padding to a view