diff --git a/app/src/main/java/org/oxycblt/auxio/coil/MosaicFetcher.kt b/app/src/main/java/org/oxycblt/auxio/coil/MosaicFetcher.kt index 28ae8460e..561660f01 100644 --- a/app/src/main/java/org/oxycblt/auxio/coil/MosaicFetcher.kt +++ b/app/src/main/java/org/oxycblt/auxio/coil/MosaicFetcher.kt @@ -101,7 +101,7 @@ class MosaicFetcher(private val context: Context) : Fetcher> { * Iterate through a list of [Closeable]s, running [use] on each. * @param action What to do for each [Closeable] */ - private fun List.useForEach(action: (T) -> R) { + private fun List.useForEach(action: (T) -> Unit) { forEach { it.use(action) } diff --git a/app/src/main/java/org/oxycblt/auxio/playback/PlaybackService.kt b/app/src/main/java/org/oxycblt/auxio/playback/PlaybackService.kt index 5d845a932..25b4bb5e4 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/PlaybackService.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/PlaybackService.kt @@ -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() } } diff --git a/app/src/main/java/org/oxycblt/auxio/playback/PlaybackViewModel.kt b/app/src/main/java/org/oxycblt/auxio/playback/PlaybackViewModel.kt index 86d8ccfa4..bb3bfead5 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/PlaybackViewModel.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/PlaybackViewModel.kt @@ -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 */ diff --git a/app/src/main/java/org/oxycblt/auxio/playback/state/PlaybackStateManager.kt b/app/src/main/java/org/oxycblt/auxio/playback/state/PlaybackStateManager.kt index 8f721ea3b..df2faa9f9 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/state/PlaybackStateManager.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/state/PlaybackStateManager.kt @@ -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() } - mIndex = mQueue.indexOf(lastSong) + 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 --- /** diff --git a/app/src/main/java/org/oxycblt/auxio/recycler/SortMode.kt b/app/src/main/java/org/oxycblt/auxio/recycler/SortMode.kt index 058df649a..0ae47cfd7 100644 --- a/app/src/main/java/org/oxycblt/auxio/recycler/SortMode.kt +++ b/app/src/main/java/org/oxycblt/auxio/recycler/SortMode.kt @@ -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() + + songs.groupBy { it.album }.toSortedMap(compareBy { it.year }).values.forEach { + list.addAll(it.sortedWith(compareBy { it.track })) + } + + list + } + NUMERIC_DOWN -> { + val list = mutableListOf() + + songs.groupBy { it.album }.toSortedMap(compareByDescending { it.year }).values.forEach { + list.addAll(it.sortedWith(compareBy { it.track })) + } + + list + } else -> songs } diff --git a/app/src/main/res/layout/fragment_main.xml b/app/src/main/res/layout/fragment_main.xml index f07256641..112e9bffe 100644 --- a/app/src/main/res/layout/fragment_main.xml +++ b/app/src/main/res/layout/fragment_main.xml @@ -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" diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 421f0276e..cbccd8ff2 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -237,4 +237,14 @@ fitCenter @dimen/padding_play_pause + + \ No newline at end of file