Update shuffling system

Optimize the shuffling system in PlaybackStateManager.
This commit is contained in:
OxygenCobalt 2021-01-05 09:42:36 -07:00
parent 35db7792bb
commit 332d1d0170
No known key found for this signature in database
GPG key ID: 37DBE3621FE9AD47
3 changed files with 77 additions and 55 deletions

View file

@ -12,7 +12,6 @@ import org.oxycblt.auxio.recycler.SortMode
/** /**
* ViewModel that stores data for the [DetailFragment]s, such as what they're showing & what * ViewModel that stores data for the [DetailFragment]s, such as what they're showing & what
* [SortMode] they are currently on. * [SortMode] they are currently on.
* TODO: Preserve previous backstack when doing playing item navigation
*/ */
class DetailViewModel : ViewModel() { class DetailViewModel : ViewModel() {
private var mIsNavigating = false private var mIsNavigating = false

View file

@ -93,7 +93,6 @@ class PlaybackStateManager private constructor() {
} }
private var mIsRestored = false private var mIsRestored = false
private var mHasPlayed = false private var mHasPlayed = false
private var mShuffleSeed = -1L
/** The currently playing song. Null if there isn't one */ /** The currently playing song. Null if there isn't one */
val song: Song? get() = mSong val song: Song? get() = mSong
@ -121,6 +120,7 @@ class PlaybackStateManager private constructor() {
val hasPlayed: Boolean get() = mHasPlayed val hasPlayed: Boolean get() = mHasPlayed
private val settingsManager = SettingsManager.getInstance() private val settingsManager = SettingsManager.getInstance()
private val musicStore = MusicStore.getInstance()
// --- CALLBACKS --- // --- CALLBACKS ---
@ -151,45 +151,60 @@ class PlaybackStateManager private constructor() {
fun playSong(song: Song, mode: PlaybackMode) { fun playSong(song: Song, mode: PlaybackMode) {
logD("Updating song to ${song.name} and mode to $mode") logD("Updating song to ${song.name} and mode to $mode")
val musicStore = MusicStore.getInstance() val shouldShuffle = settingsManager.keepShuffle && mIsShuffling
when (mode) { when (mode) {
PlaybackMode.ALL_SONGS -> { PlaybackMode.ALL_SONGS -> {
mParent = null mParent = null
mQueue = musicStore.songs.toMutableList()
mQueue = if (shouldShuffle) {
musicStore.songs.toMutableList()
} else {
genShuffle(musicStore.songs.toMutableList(), false)
}
} }
PlaybackMode.IN_GENRE -> { PlaybackMode.IN_GENRE -> {
if (song.genre != null) {
mParent = song.genre mParent = song.genre
mQueue = orderSongsInGenre(song.genre!!) mQueue = if (shouldShuffle) {
genShuffle(song.genre!!.songs.toMutableList(), false)
} else {
orderSongsInGenre(song.genre!!)
}
} else {
// I really don't know what to do if the song doesn't have a genre, so just
// ignore it.
logE("Song doesn't have a genre. Not playing.")
return
}
} }
PlaybackMode.IN_ARTIST -> { PlaybackMode.IN_ARTIST -> {
mParent = song.album.artist mParent = song.album.artist
mQueue = orderSongsInArtist(song.album.artist) mQueue = if (shouldShuffle) {
genShuffle(song.album.artist.songs.toMutableList(), false)
} else {
orderSongsInArtist(song.album.artist)
}
} }
PlaybackMode.IN_ALBUM -> { PlaybackMode.IN_ALBUM -> {
mParent = song.album mParent = song.album
mQueue = orderSongsInAlbum(song.album) mQueue = if (shouldShuffle) {
genShuffle(song.album.songs.toMutableList(), false)
} else {
orderSongsInAlbum(song.album)
}
} }
} }
mMode = mode mMode = mode
mIsShuffling = shouldShuffle
resetLoopMode() resetLoopMode()
updatePlayback(song) updatePlayback(song)
if (settingsManager.keepShuffle) {
if (mIsShuffling) {
genShuffle(true)
} else {
resetShuffle()
}
} else {
setShuffleStatus(false)
}
mIndex = mQueue.indexOf(song) mIndex = mQueue.indexOf(song)
} }
@ -214,15 +229,30 @@ class PlaybackStateManager private constructor() {
when (baseModel) { when (baseModel) {
is Album -> { is Album -> {
mQueue = orderSongsInAlbum(baseModel) mQueue = if (mIsShuffling) {
genShuffle(baseModel.songs.toMutableList(), false)
} else {
orderSongsInAlbum(baseModel)
}
mMode = PlaybackMode.IN_ALBUM mMode = PlaybackMode.IN_ALBUM
} }
is Artist -> { is Artist -> {
mQueue = orderSongsInArtist(baseModel) mQueue = if (mIsShuffling) {
genShuffle(baseModel.songs.toMutableList(), false)
} else {
orderSongsInArtist(baseModel)
}
mMode = PlaybackMode.IN_ARTIST mMode = PlaybackMode.IN_ARTIST
} }
is Genre -> { is Genre -> {
mQueue = orderSongsInGenre(baseModel) mQueue = if (mIsShuffling) {
genShuffle(baseModel.songs.toMutableList(), false)
} else {
orderSongsInGenre(baseModel)
}
mMode = PlaybackMode.IN_GENRE mMode = PlaybackMode.IN_GENRE
} }
@ -233,12 +263,6 @@ class PlaybackStateManager private constructor() {
resetLoopMode() resetLoopMode()
updatePlayback(mQueue[0]) updatePlayback(mQueue[0])
if (mIsShuffling) {
genShuffle(false)
} else {
resetShuffle()
}
} }
/** /**
@ -504,38 +528,44 @@ class PlaybackStateManager private constructor() {
val musicStore = MusicStore.getInstance() val musicStore = MusicStore.getInstance()
mIsShuffling = true mIsShuffling = true
mQueue = musicStore.songs.toMutableList() mQueue = genShuffle(musicStore.songs.toMutableList(), false)
mMode = PlaybackMode.ALL_SONGS mMode = PlaybackMode.ALL_SONGS
mIndex = 0 mIndex = 0
genShuffle(false)
updatePlayback(mQueue[0]) updatePlayback(mQueue[0])
} }
/** /**
* Generate a new shuffled queue. * Generate a new shuffled queue.
* @param queueToShuffle The queue to shuffle
* @param keepSong Whether to keep the currently playing song or to dispose of it * @param keepSong Whether to keep the currently playing song or to dispose of it
* @param useLastSong (Optional, defaults to false) Whether to use the previous song for the index calculations caused by the above parameter. * @param useLastSong (Optional, defaults to false) Whether to use the last song in the queue instead of the current one
* @return A new shuffled queue
*/ */
private fun genShuffle(keepSong: Boolean, useLastSong: Boolean = false) { private fun genShuffle(
mShuffleSeed = Random.Default.nextLong() queueToShuffle: MutableList<Song>,
keepSong: Boolean,
useLastSong: Boolean = false
): MutableList<Song> {
val newSeed = Random.Default.nextLong()
logD("Shuffling queue with seed $mShuffleSeed") val lastSong =
val lastSong = if (useLastSong) mQueue[mIndex] else mSong logD("Shuffling queue with seed $newSeed")
mQueue.shuffle(Random(mShuffleSeed)) queueToShuffle.shuffle(Random(newSeed))
mIndex = 0 mIndex = 0
// If specified, make the current song the first member of the queue. // If specified, make the current song the first member of the queue.
if (keepSong) { if (keepSong) {
moveQueueItems(mQueue.indexOf(lastSong), 0) val song = queueToShuffle.removeAt(queueToShuffle.indexOf(mSong))
queueToShuffle.add(0, song)
} else { } else {
// Otherwise, just start from the zeroth position in the queue. // Otherwise, just start from the zeroth position in the queue.
mSong = mQueue[0] mSong = mQueue[0]
} }
forceQueueUpdate() return queueToShuffle
} }
/** /**
@ -543,11 +573,14 @@ class PlaybackStateManager private constructor() {
* @param useLastSong (Optional, defaults to false) Whether to use the previous song for the index calculations. * @param useLastSong (Optional, defaults to false) Whether to use the previous song for the index calculations.
*/ */
private fun resetShuffle(useLastSong: Boolean = false) { private fun resetShuffle(useLastSong: Boolean = false) {
mShuffleSeed = -1L
val lastSong = if (useLastSong) mQueue[mIndex] else mSong val lastSong = if (useLastSong) mQueue[mIndex] else mSong
setupOrderedQueue() mQueue = when (mMode) {
PlaybackMode.IN_ARTIST -> orderSongsInArtist(mParent as Artist)
PlaybackMode.IN_ALBUM -> orderSongsInAlbum(mParent as Album)
PlaybackMode.IN_GENRE -> orderSongsInGenre(mParent as Genre)
PlaybackMode.ALL_SONGS -> MusicStore.getInstance().songs.toMutableList()
}
mIndex = mQueue.indexOf(lastSong) mIndex = mQueue.indexOf(lastSong)
@ -578,7 +611,11 @@ class PlaybackStateManager private constructor() {
mIsShuffling = value mIsShuffling = value
if (mIsShuffling) { if (mIsShuffling) {
genShuffle(true, mIsInUserQueue) mQueue = genShuffle(
mQueue,
keepSong = true,
useLastSong = mIsInUserQueue
)
} else { } else {
resetShuffle(mIsInUserQueue) resetShuffle(mIsInUserQueue)
} }
@ -795,18 +832,6 @@ class PlaybackStateManager private constructor() {
// --- ORDERING FUNCTIONS --- // --- ORDERING FUNCTIONS ---
/**
* Set up an ordered queue.
*/
private fun setupOrderedQueue() {
mQueue = when (mMode) {
PlaybackMode.IN_ARTIST -> orderSongsInArtist(mParent as Artist)
PlaybackMode.IN_ALBUM -> orderSongsInAlbum(mParent as Album)
PlaybackMode.IN_GENRE -> orderSongsInGenre(mParent as Genre)
PlaybackMode.ALL_SONGS -> MusicStore.getInstance().songs.toMutableList()
}
}
/** /**
* Create an ordered queue based on an [Album]. * Create an ordered queue based on an [Album].
*/ */

View file

@ -35,8 +35,6 @@
android:id="@+id/song_fast_scroll" android:id="@+id/song_fast_scroll"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="0dp" android:layout_height="0dp"
android:layout_marginTop="@dimen/margin_small"
android:layout_marginBottom="@dimen/margin_small"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@+id/song_toolbar" /> app:layout_constraintTop_toBottomOf="@+id/song_toolbar" />