playback: make sanitization runtime

Do not save the playback state when sanitizing.

After some thought, there is no situation where re-saving the playback
state is desirable. A previously saved state will be consistent with
a sanitized state, and there is no need to save when the service is
active. Thus, save on speed and reduce insane race conditions by just
sanitizing the current runtime state and not saving at all.
This commit is contained in:
OxygenCobalt 2022-07-07 11:58:25 -06:00
parent 83d6c529e2
commit e0a05ef486
No known key found for this signature in database
GPG key ID: 37DBE3621FE9AD47
5 changed files with 41 additions and 30 deletions

View file

@ -32,7 +32,7 @@ If you have the knowledge, you can also implement the feature yourself and creat
Its also recommended that you read about [Auxio's Architecture](../info/ARCHITECTURE.md) as well to make changes better and more efficient.
## Translations
I don't really see the use in weblate, so currently you should see the [Translations Megathread]](https://github.com/OxygenCobalt/Auxio/issues/3) to see how to propose translations.
Go to Auxio's weblate project [here](https://hosted.weblate.org/engage/auxio/).
## Code Contributions
If you have knowledge of Android/Kotlin, feel free to to contribute to the project.

View file

@ -29,6 +29,7 @@ import org.oxycblt.auxio.util.getSystemServiceSafe
import org.oxycblt.auxio.util.logD
import org.oxycblt.auxio.util.newMainPendingIntent
/** The notification responsible for showing the indexer state. */
class IndexerNotification(private val context: Context) :
NotificationCompat.Builder(context, CHANNEL_ID) {
private val notificationManager = context.getSystemServiceSafe(NotificationManager::class)

View file

@ -28,7 +28,6 @@ import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import org.oxycblt.auxio.IntegerTable
import org.oxycblt.auxio.R
import org.oxycblt.auxio.playback.state.PlaybackStateDatabase
import org.oxycblt.auxio.playback.state.PlaybackStateManager
import org.oxycblt.auxio.settings.Settings
import org.oxycblt.auxio.util.logD
@ -118,16 +117,10 @@ class IndexerService : Service(), Indexer.Controller, Settings.Callback {
// Wipe possibly-invalidated album covers
imageLoader.memoryCache?.clear()
// PlaybackStateManager needs to be updated. We would do this in the
// playback module, but this service could be the only component capable
// of doing this at a particular point. Note that while it's certain
// that PlaybackStateManager is initialized by now, it's best to be safe
// and check first.
if (playbackManager.isInitialized) {
playbackManager.sanitize(
PlaybackStateDatabase.getInstance(this@IndexerService),
newLibrary)
}
// Clear invalid models from PlaybackStateManager. Shared objects
// shouldn't be plugged into the callback system of other shared
// objects, so we must update it here.
playbackManager.sanitize(newLibrary)
}
musicStore.updateLibrary(newLibrary)

View file

@ -84,6 +84,7 @@ class MusicStore private constructor() {
}
fun sanitize(song: Song) = songs.find { it.id == song.id }
fun sanitize(songs: List<Song>) = 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 }

View file

@ -384,24 +384,40 @@ class PlaybackStateManager private constructor() {
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.
// TODO: Do we even need to save here? Doesn't seem like it's required for
@Synchronized
fun sanitize(newLibrary: MusicStore.Library) {
if (!isInitialized) {
logD("Not initialized, no need to sanitize")
return
}
logD("Sanitizing state")
val state = synchronized(this) { makeStateImpl() }
val sanitizedState =
withContext(Dispatchers.IO) {
database.write(state)
database.read(newLibrary)
val oldSongId = song?.id
val oldPosition = positionMs
parent =
parent?.let {
when (it) {
is Album -> newLibrary.sanitize(it)
is Artist -> newLibrary.sanitize(it)
is Genre -> newLibrary.sanitize(it)
}
}
synchronized(this) {
if (sanitizedState != null) {
applyStateImpl(sanitizedState)
}
_queue = newLibrary.sanitize(_queue).toMutableList()
while (song?.id != oldSongId && index > -1) {
index--
}
// Continuing playback while also possibly doing drastic state updates is
// a bad idea, so pause.
isPlaying = false
notifyNewPlayback()
// Controller may have reloaded the media item, re-seek to the previous position
seekTo(oldPosition)
}
private fun makeStateImpl() =