Update shuffling system

Finally collapse all usage of shuffle into setShuffling, instead of the mix of checks that used to be throughout PlaybackStateManager.
This commit is contained in:
OxygenCobalt 2021-01-18 16:53:10 -07:00
parent 44fa178c34
commit 627553344c
No known key found for this signature in database
GPG key ID: 37DBE3621FE9AD47
7 changed files with 88 additions and 66 deletions

View file

@ -101,7 +101,7 @@ class MosaicFetcher(private val context: Context) : Fetcher<List<Uri>> {
* Iterate through a list of [Closeable]s, running [use] on each.
* @param action What to do for each [Closeable]
*/
private fun <T : Closeable, R> List<T>.useForEach(action: (T) -> R) {
private fun <T : Closeable> List<T>.useForEach(action: (T) -> Unit) {
forEach {
it.use(action)
}

View file

@ -38,9 +38,9 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.conflate
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.takeWhile
import kotlinx.coroutines.launch
import org.oxycblt.auxio.coil.getBitmap
@ -484,7 +484,7 @@ class PlaybackService : Service(), Player.EventListener, PlaybackStateManager.Ca
// Play/Pause if any of the keys are play/pause
KeyEvent.KEYCODE_MEDIA_PAUSE, KeyEvent.KEYCODE_MEDIA_PLAY,
KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE, KeyEvent.KEYCODE_HEADSETHOOK -> {
playbackManager.setPlayingStatus(!playbackManager.isPlaying)
playbackManager.setPlaying(!playbackManager.isPlaying)
true
}
@ -557,7 +557,7 @@ class PlaybackService : Service(), Player.EventListener, PlaybackStateManager.Ca
player.volume = VOLUME_DUCK
animateVolume(VOLUME_DUCK, VOLUME_FULL)
} else if (pauseWasFromAudioFocus) {
playbackManager.setPlayingStatus(true)
playbackManager.setPlaying(true)
}
pauseWasFromAudioFocus = false
@ -567,7 +567,7 @@ class PlaybackService : Service(), Player.EventListener, PlaybackStateManager.Ca
private fun onLoss() {
if (settingsManager.doAudioFocus && playbackManager.isPlaying) {
pauseWasFromAudioFocus = true
playbackManager.setPlayingStatus(false)
playbackManager.setPlaying(false)
}
}
@ -606,10 +606,10 @@ class PlaybackService : Service(), Player.EventListener, PlaybackStateManager.Ca
NotificationUtils.ACTION_LOOP ->
playbackManager.setLoopMode(playbackManager.loopMode.increment())
NotificationUtils.ACTION_SHUFFLE ->
playbackManager.setShuffleStatus(!playbackManager.isShuffling)
playbackManager.setShuffling(!playbackManager.isShuffling, keepSong = true)
NotificationUtils.ACTION_SKIP_PREV -> playbackManager.prev()
NotificationUtils.ACTION_PLAY_PAUSE -> {
playbackManager.setPlayingStatus(!playbackManager.isPlaying)
playbackManager.setPlaying(!playbackManager.isPlaying)
}
NotificationUtils.ACTION_SKIP_NEXT -> playbackManager.next()
NotificationUtils.ACTION_EXIT -> stop()
@ -643,7 +643,7 @@ class PlaybackService : Service(), Player.EventListener, PlaybackStateManager.Ca
if (playbackManager.song != null && settingsManager.doPlugMgt) {
logD("Device connected, resuming...")
playbackManager.setPlayingStatus(true)
playbackManager.setPlaying(true)
}
}
@ -654,7 +654,7 @@ class PlaybackService : Service(), Player.EventListener, PlaybackStateManager.Ca
if (playbackManager.song != null && settingsManager.doPlugMgt) {
logD("Device disconnected, pausing...")
playbackManager.setPlayingStatus(false)
playbackManager.setPlaying(false)
}
}
@ -662,7 +662,7 @@ class PlaybackService : Service(), Player.EventListener, PlaybackStateManager.Ca
* Stop if the X button was clicked from the notification
*/
private fun stop() {
playbackManager.setPlayingStatus(false)
playbackManager.setPlaying(false)
stopForegroundAndNotification()
}
}

View file

@ -279,12 +279,12 @@ class PlaybackViewModel : ViewModel(), PlaybackStateManager.Callback {
fun invertPlayingStatus() {
enableAnimation()
playbackManager.setPlayingStatus(!playbackManager.isPlaying)
playbackManager.setPlaying(!playbackManager.isPlaying)
}
/** Flip the shuffle status, e.g from on to off */
/** Flip the shuffle status, e.g from on to off. Will keep song by default. */
fun invertShuffleStatus() {
playbackManager.setShuffleStatus(!playbackManager.isShuffling)
playbackManager.setShuffling(!playbackManager.isShuffling, keepSong = true)
}
/** Increment the loop status, e.g from off to loop once */

View file

@ -153,8 +153,6 @@ class PlaybackStateManager private constructor() {
fun playSong(song: Song, mode: PlaybackMode) {
logD("Updating song to ${song.name} and mode to $mode")
val shouldShuffle = settingsManager.keepShuffle && mIsShuffling
when (mode) {
PlaybackMode.ALL_SONGS -> {
mParent = null
@ -190,7 +188,9 @@ class PlaybackStateManager private constructor() {
resetLoopMode()
updatePlayback(song)
setShuffleStatus(shouldShuffle)
// Depending on the configuration, keep the shuffle mode on.
setShuffling(settingsManager.keepShuffle && mIsShuffling, keepSong = true)
mIndex = mQueue.indexOf(song)
}
@ -212,19 +212,18 @@ class PlaybackStateManager private constructor() {
mParent = baseModel
mIndex = 0
mIsShuffling = shuffled
when (baseModel) {
is Album -> {
mQueue = orderSongsInAlbum(baseModel)
mQueue = baseModel.songs.toMutableList()
mMode = PlaybackMode.IN_ALBUM
}
is Artist -> {
mQueue = orderSongsInArtist(baseModel)
mQueue = baseModel.songs.toMutableList()
mMode = PlaybackMode.IN_ARTIST
}
is Genre -> {
mQueue = orderSongsInGenre(baseModel)
mQueue = baseModel.songs.toMutableList()
mMode = PlaybackMode.IN_GENRE
}
@ -233,14 +232,8 @@ class PlaybackStateManager private constructor() {
}
resetLoopMode()
setShuffling(shuffled, keepSong = false)
updatePlayback(mQueue[0])
if (mIsShuffling) {
genShuffle(false)
} else {
resetShuffle()
}
}
/**
@ -254,7 +247,7 @@ class PlaybackStateManager private constructor() {
mPosition = 0
if (!mIsPlaying) {
setPlayingStatus(true)
setPlaying(true)
}
}
@ -352,7 +345,7 @@ class PlaybackStateManager private constructor() {
mSong = mQueue[0]
mPosition = 0
setPlayingStatus(false)
setPlaying(false)
mIsInUserQueue = false
}
@ -503,25 +496,37 @@ class PlaybackStateManager private constructor() {
* Shuffle all songs.
*/
fun shuffleAll() {
mIsShuffling = true
mMode = PlaybackMode.ALL_SONGS
mIndex = 0
mQueue = musicStore.songs.toMutableList()
genShuffle(false)
setShuffling(true, keepSong = false)
updatePlayback(mQueue[0])
}
/**
* Set the shuffle status. Updates the queue accordingly
* @param value Whether the queue should be shuffled or not.
* @param keepSong Whether the current song should be kept as the queue is shuffled/unshuffled
*/
fun setShuffling(value: Boolean, keepSong: Boolean) {
mIsShuffling = value
if (mIsShuffling) {
genShuffle(keepSong, mIsInUserQueue)
} else {
resetShuffle(keepSong, mIsInUserQueue)
}
}
/**
* Generate a new shuffled queue.
* @param keepSong Whether to keep the currently playing song or to dispose of it
* @param useLastSong (Optional, defaults to false) Whether to use the last song in the queue instead of the current one
* @param keepSong Whether the current song should be kept as the queue is shuffled
* @param useLastSong 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
useLastSong: Boolean
) {
val lastSong = if (useLastSong) mQueue[0] else mSong
@ -543,9 +548,13 @@ class PlaybackStateManager private constructor() {
/**
* Reset the queue to its normal, ordered state.
* @param useLastSong (Optional, defaults to false) Whether to use the previous song for the index calculations.
* @param keepSong Whether the current song should be kept as the queue is unshuffled
* @param useLastSong Whether to use the previous song for the index calculations.
*/
private fun resetShuffle(useLastSong: Boolean = false) {
private fun resetShuffle(
keepSong: Boolean,
useLastSong: Boolean
) {
val lastSong = if (useLastSong) mQueue[mIndex] else mSong
mQueue = when (mMode) {
@ -555,7 +564,9 @@ class PlaybackStateManager private constructor() {
PlaybackMode.ALL_SONGS -> musicStore.songs.toMutableList()
}
if (keepSong) {
mIndex = mQueue.indexOf(lastSong)
}
forceQueueUpdate()
}
@ -566,7 +577,7 @@ class PlaybackStateManager private constructor() {
* Set the current playing status
* @param value Whether the playback should be playing or paused.
*/
fun setPlayingStatus(value: Boolean) {
fun setPlaying(value: Boolean) {
if (mIsPlaying != value) {
if (value) {
mHasPlayed = true
@ -577,25 +588,11 @@ class PlaybackStateManager private constructor() {
}
/**
* Set the shuffle status. Updates the queue accordingly
* @param value Whether the queue should be shuffled or not.
* Rewind to the beginning of a song.
*/
fun setShuffleStatus(value: Boolean) {
mIsShuffling = value
if (mIsShuffling) {
genShuffle(
keepSong = true,
useLastSong = mIsInUserQueue
)
} else {
resetShuffle(mIsInUserQueue)
}
}
fun rewind() {
seekTo(0)
setPlayingStatus(true)
setPlaying(true)
}
/**
@ -606,13 +603,6 @@ class PlaybackStateManager private constructor() {
mLoopMode = mode
}
/**
* Reset the has played status as if this instance is fresh.
*/
fun resetHasPlayedStatus() {
mHasPlayed = false
}
/**
* Reset the current [LoopMode], if needed.
* Use this instead of duplicating the code manually.
@ -624,6 +614,13 @@ class PlaybackStateManager private constructor() {
}
}
/**
* Reset the has played status as if this instance is fresh.
*/
fun resetHasPlayedStatus() {
mHasPlayed = false
}
// --- PERSISTENCE FUNCTIONS ---
/**

View file

@ -101,8 +101,24 @@ enum class SortMode(@DrawableRes val iconRes: Int) {
compareBy(String.CASE_INSENSITIVE_ORDER) { it.name }
)
NUMERIC_UP -> songs.sortedWith(compareBy { it.album.year })
NUMERIC_DOWN -> songs.sortedWith(compareByDescending { it.album.year })
NUMERIC_UP -> {
val list = mutableListOf<Song>()
songs.groupBy { it.album }.toSortedMap(compareBy { it.year }).values.forEach {
list.addAll(it.sortedWith(compareBy { it.track }))
}
list
}
NUMERIC_DOWN -> {
val list = mutableListOf<Song>()
songs.groupBy { it.album }.toSortedMap(compareByDescending { it.year }).values.forEach {
list.addAll(it.sortedWith(compareBy { it.track }))
}
list
}
else -> songs
}

View file

@ -40,7 +40,6 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/background"
android:elevation="@dimen/elevation_normal"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"

View file

@ -236,5 +236,15 @@
<item name="android:layout_marginEnd">@dimen/margin_large</item>
<item name="android:scaleType">fitCenter</item>
<item name="android:padding">@dimen/padding_play_pause</item>
</style>
<style name="Widget.Design.BottomNavigationView" parent="">
<item name="elevation">@dimen/design_bottom_navigation_elevation</item>
<item name="enforceTextAppearance">false</item>
<item name="enforceMaterialTheme">false</item>
<item name="itemBackground">?attr/selectableItemBackgroundBorderless</item>
<item name="itemHorizontalTranslationEnabled">true</item>
<item name="itemIconSize">@dimen/design_bottom_navigation_icon_size</item>
<item name="labelVisibilityMode">auto</item>
</style>
</resources>