playback: save state during sanitize
Save, but do not read the playback state when sanitizing. Turns out there is an edge-case where we want to save the state. Still keep the runtime sanitization, as that greatly reduces the time it takes to rescan the library.
This commit is contained in:
parent
41ff92a587
commit
a217bde713
4 changed files with 69 additions and 57 deletions
|
|
@ -28,6 +28,7 @@ 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
|
||||
|
|
@ -117,10 +118,9 @@ class IndexerService : Service(), Indexer.Controller, Settings.Callback {
|
|||
// Wipe possibly-invalidated album covers
|
||||
imageLoader.memoryCache?.clear()
|
||||
|
||||
// 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)
|
||||
// Clear invalid models from PlaybackStateManager.
|
||||
playbackManager.sanitize(
|
||||
PlaybackStateDatabase.getInstance(this@IndexerService), newLibrary)
|
||||
}
|
||||
|
||||
musicStore.updateLibrary(newLibrary)
|
||||
|
|
|
|||
|
|
@ -83,10 +83,15 @@ class MusicStore private constructor() {
|
|||
songs.find { it.path.name == displayName }
|
||||
}
|
||||
|
||||
/** Sanitize an old item to find the corresponding item in a new library. */
|
||||
fun sanitize(song: Song) = songs.find { it.id == song.id }
|
||||
/** Sanitize an old item to find the corresponding item in a new library. */
|
||||
fun sanitize(songs: List<Song>) = songs.mapNotNull { sanitize(it) }
|
||||
/** Sanitize an old item to find the corresponding item in a new library. */
|
||||
fun sanitize(album: Album) = albums.find { it.id == album.id }
|
||||
/** Sanitize an old item to find the corresponding item in a new library. */
|
||||
fun sanitize(artist: Artist) = artists.find { it.id == artist.id }
|
||||
/** Sanitize an old item to find the corresponding item in a new library. */
|
||||
fun sanitize(genre: Genre) = genres.find { it.id == genre.id }
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -107,6 +107,7 @@ class PlaybackStateDatabase private constructor(context: Context) :
|
|||
val rawState = readRawState() ?: return null
|
||||
val queue = readQueue(library)
|
||||
|
||||
// Correct the index to match up with a possibly shortened queue (file removals/changes)
|
||||
var actualIndex = rawState.index
|
||||
while (queue.getOrNull(actualIndex)?.id != rawState.songId && actualIndex > -1) {
|
||||
actualIndex--
|
||||
|
|
|
|||
|
|
@ -30,7 +30,6 @@ import org.oxycblt.auxio.music.Song
|
|||
import org.oxycblt.auxio.settings.Settings
|
||||
import org.oxycblt.auxio.util.logD
|
||||
import org.oxycblt.auxio.util.logW
|
||||
import org.oxycblt.auxio.util.unlikelyToBeNull
|
||||
|
||||
/**
|
||||
* Master class (and possible god object) for the playback state.
|
||||
|
|
@ -364,10 +363,27 @@ class PlaybackStateManager private constructor() {
|
|||
val state = withContext(Dispatchers.IO) { database.read(library) }
|
||||
|
||||
synchronized(this) {
|
||||
val exists = state != null
|
||||
if (exists) {
|
||||
applyStateImpl(unlikelyToBeNull(state))
|
||||
}
|
||||
val exists =
|
||||
if (state != null) {
|
||||
// Continuing playback while also possibly doing drastic state updates is
|
||||
// a bad idea, so pause.
|
||||
isPlaying = false
|
||||
|
||||
index = state.index
|
||||
parent = state.parent
|
||||
_queue = state.queue.toMutableList()
|
||||
repeatMode = state.repeatMode
|
||||
isShuffled = state.isShuffled
|
||||
|
||||
notifyNewPlayback()
|
||||
seekTo(state.positionMs)
|
||||
notifyRepeatModeChanged()
|
||||
notifyShuffledChanged()
|
||||
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
|
||||
isInitialized = true
|
||||
|
||||
|
|
@ -384,40 +400,47 @@ class PlaybackStateManager private constructor() {
|
|||
withContext(Dispatchers.IO) { database.write(state) }
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
fun sanitize(newLibrary: MusicStore.Library) {
|
||||
if (!isInitialized) {
|
||||
logD("Not initialized, no need to sanitize")
|
||||
return
|
||||
}
|
||||
|
||||
logD("Sanitizing state")
|
||||
|
||||
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)
|
||||
/** Sanitize the state with [newLibrary]. Writes the state to [database] */
|
||||
suspend fun sanitize(database: PlaybackStateDatabase, newLibrary: MusicStore.Library) {
|
||||
val state =
|
||||
synchronized(this) {
|
||||
if (!isInitialized) {
|
||||
logD("Not initialized, no need to sanitize")
|
||||
return
|
||||
}
|
||||
|
||||
logD("Sanitizing state")
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
_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)
|
||||
|
||||
makeStateImpl()
|
||||
}
|
||||
|
||||
_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)
|
||||
withContext(Dispatchers.IO) { database.write(state) }
|
||||
}
|
||||
|
||||
private fun makeStateImpl() =
|
||||
|
|
@ -429,23 +452,6 @@ class PlaybackStateManager private constructor() {
|
|||
isShuffled = isShuffled,
|
||||
repeatMode = repeatMode)
|
||||
|
||||
private fun applyStateImpl(state: PlaybackStateDatabase.SavedState) {
|
||||
// Continuing playback while also possibly doing drastic state updates is
|
||||
// a bad idea, so pause.
|
||||
isPlaying = false
|
||||
|
||||
index = state.index
|
||||
parent = state.parent
|
||||
_queue = state.queue.toMutableList()
|
||||
repeatMode = state.repeatMode
|
||||
isShuffled = state.isShuffled
|
||||
|
||||
notifyNewPlayback()
|
||||
seekTo(state.positionMs)
|
||||
notifyRepeatModeChanged()
|
||||
notifyShuffledChanged()
|
||||
}
|
||||
|
||||
// --- CALLBACKS ---
|
||||
|
||||
private fun notifyIndexMoved() {
|
||||
|
|
|
|||
Loading…
Reference in a new issue