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 kotlinx.coroutines.launch
|
||||||
import org.oxycblt.auxio.IntegerTable
|
import org.oxycblt.auxio.IntegerTable
|
||||||
import org.oxycblt.auxio.R
|
import org.oxycblt.auxio.R
|
||||||
|
import org.oxycblt.auxio.playback.state.PlaybackStateDatabase
|
||||||
import org.oxycblt.auxio.playback.state.PlaybackStateManager
|
import org.oxycblt.auxio.playback.state.PlaybackStateManager
|
||||||
import org.oxycblt.auxio.settings.Settings
|
import org.oxycblt.auxio.settings.Settings
|
||||||
import org.oxycblt.auxio.util.logD
|
import org.oxycblt.auxio.util.logD
|
||||||
|
|
@ -117,10 +118,9 @@ class IndexerService : Service(), Indexer.Controller, Settings.Callback {
|
||||||
// Wipe possibly-invalidated album covers
|
// Wipe possibly-invalidated album covers
|
||||||
imageLoader.memoryCache?.clear()
|
imageLoader.memoryCache?.clear()
|
||||||
|
|
||||||
// Clear invalid models from PlaybackStateManager. Shared objects
|
// Clear invalid models from PlaybackStateManager.
|
||||||
// shouldn't be plugged into the callback system of other shared
|
playbackManager.sanitize(
|
||||||
// objects, so we must update it here.
|
PlaybackStateDatabase.getInstance(this@IndexerService), newLibrary)
|
||||||
playbackManager.sanitize(newLibrary)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
musicStore.updateLibrary(newLibrary)
|
musicStore.updateLibrary(newLibrary)
|
||||||
|
|
|
||||||
|
|
@ -83,10 +83,15 @@ class MusicStore private constructor() {
|
||||||
songs.find { it.path.name == displayName }
|
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 }
|
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) }
|
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 }
|
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 }
|
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 }
|
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 rawState = readRawState() ?: return null
|
||||||
val queue = readQueue(library)
|
val queue = readQueue(library)
|
||||||
|
|
||||||
|
// Correct the index to match up with a possibly shortened queue (file removals/changes)
|
||||||
var actualIndex = rawState.index
|
var actualIndex = rawState.index
|
||||||
while (queue.getOrNull(actualIndex)?.id != rawState.songId && actualIndex > -1) {
|
while (queue.getOrNull(actualIndex)?.id != rawState.songId && actualIndex > -1) {
|
||||||
actualIndex--
|
actualIndex--
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,6 @@ import org.oxycblt.auxio.music.Song
|
||||||
import org.oxycblt.auxio.settings.Settings
|
import org.oxycblt.auxio.settings.Settings
|
||||||
import org.oxycblt.auxio.util.logD
|
import org.oxycblt.auxio.util.logD
|
||||||
import org.oxycblt.auxio.util.logW
|
import org.oxycblt.auxio.util.logW
|
||||||
import org.oxycblt.auxio.util.unlikelyToBeNull
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Master class (and possible god object) for the playback state.
|
* 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) }
|
val state = withContext(Dispatchers.IO) { database.read(library) }
|
||||||
|
|
||||||
synchronized(this) {
|
synchronized(this) {
|
||||||
val exists = state != null
|
val exists =
|
||||||
if (exists) {
|
if (state != null) {
|
||||||
applyStateImpl(unlikelyToBeNull(state))
|
// 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
|
isInitialized = true
|
||||||
|
|
||||||
|
|
@ -384,40 +400,47 @@ class PlaybackStateManager private constructor() {
|
||||||
withContext(Dispatchers.IO) { database.write(state) }
|
withContext(Dispatchers.IO) { database.write(state) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@Synchronized
|
/** Sanitize the state with [newLibrary]. Writes the state to [database] */
|
||||||
fun sanitize(newLibrary: MusicStore.Library) {
|
suspend fun sanitize(database: PlaybackStateDatabase, newLibrary: MusicStore.Library) {
|
||||||
if (!isInitialized) {
|
val state =
|
||||||
logD("Not initialized, no need to sanitize")
|
synchronized(this) {
|
||||||
return
|
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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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()
|
withContext(Dispatchers.IO) { database.write(state) }
|
||||||
|
|
||||||
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() =
|
private fun makeStateImpl() =
|
||||||
|
|
@ -429,23 +452,6 @@ class PlaybackStateManager private constructor() {
|
||||||
isShuffled = isShuffled,
|
isShuffled = isShuffled,
|
||||||
repeatMode = repeatMode)
|
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 ---
|
// --- CALLBACKS ---
|
||||||
|
|
||||||
private fun notifyIndexMoved() {
|
private fun notifyIndexMoved() {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue