From b2d71f8903ce7f87f3b58118cf13801865e68300 Mon Sep 17 00:00:00 2001 From: Alexander Capehart Date: Sat, 13 Jan 2024 20:26:02 -0700 Subject: [PATCH] playback: reattach settings to player It's mostly identical to prior, albiet pausing on repeat now leaves the player position at the end of the song rather than the beginning. That's the only regression I couldn't figure out how to resolve. --- .../auxio/playback/PlaybackSettings.kt | 6 +++ .../playback/state/PlaybackStateHolder.kt | 2 - .../playback/state/PlaybackStateManager.kt | 18 ++++---- .../auxio/playback/system/ExoPlayerExt.kt | 11 ----- .../auxio/playback/system/PlaybackService.kt | 46 +++++++++++++++---- 5 files changed, 54 insertions(+), 29 deletions(-) diff --git a/app/src/main/java/org/oxycblt/auxio/playback/PlaybackSettings.kt b/app/src/main/java/org/oxycblt/auxio/playback/PlaybackSettings.kt index a270c5c07..846a6ae68 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/PlaybackSettings.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/PlaybackSettings.kt @@ -66,6 +66,8 @@ interface PlaybackSettings : Settings { fun onNotificationActionChanged() {} /** Called when [barAction] has changed. */ fun onBarActionChanged() {} + /** Called when [pauseOnRepeat] has changed. */ + fun onPauseOnRepeatChanged() {} } } @@ -187,6 +189,10 @@ class PlaybackSettingsImpl @Inject constructor(@ApplicationContext context: Cont logD("Dispatching bar action change") listener.onBarActionChanged() } + getString(R.string.set_key_repeat_pause) -> { + logD("Dispatching pause on repeat change") + listener.onPauseOnRepeatChanged() + } } } diff --git a/app/src/main/java/org/oxycblt/auxio/playback/state/PlaybackStateHolder.kt b/app/src/main/java/org/oxycblt/auxio/playback/state/PlaybackStateHolder.kt index ef8175755..373e9ff2d 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/state/PlaybackStateHolder.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/state/PlaybackStateHolder.kt @@ -34,8 +34,6 @@ interface PlaybackStateHolder { fun resolveQueue(): RawQueue - val isShuffled: Boolean - val audioSessionId: Int fun newPlayback(queue: List, start: Song?, parent: MusicParent?, shuffled: Boolean) 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 b38c63c0b..50a5155bb 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 @@ -187,9 +187,9 @@ interface PlaybackStateManager { * [PlaybackStateHolder]. * * @param stateHolder The [PlaybackStateHolder] to synchronize with. Must be the current - * [PlaybackStateHolder]. Does nothing if invoked by another [PlaybackStateHolder] - * implementation. - * @param ack The [StateAck] to acknowledge. + * [PlaybackStateHolder]. Does nothing if invoked by another [PlaybackStateHolder] + * implementation. + * @param ack The [StateAck] to acknowledge. */ fun ack(stateHolder: PlaybackStateHolder, ack: StateAck) @@ -269,8 +269,8 @@ interface PlaybackStateManager { fun onQueueChanged(queue: List, index: Int, change: QueueChange) {} /** - * Called when the queue has changed in a non-trivial manner (such as re-shuffling), but - * the currently playing [Song] has not. + * Called when the queue has changed in a non-trivial manner (such as re-shuffling), but the + * currently playing [Song] has not. * * @param queue The songs of the new queue. * @param index The new index of the currently playing [Song]. @@ -407,6 +407,8 @@ class PlaybackStateManagerImpl @Inject constructor() : PlaybackStateManager { } this.stateHolder = stateHolder + + // TODO: Re-init player } @Synchronized @@ -627,7 +629,7 @@ class PlaybackStateManagerImpl @Inject constructor() : PlaybackStateManager { ) if (change.type == QueueChange.Type.SONG) { - playing(true) + stateHolder.playing(true) } listeners.forEach { @@ -640,7 +642,7 @@ class PlaybackStateManagerImpl @Inject constructor() : PlaybackStateManager { stateMirror.copy( queue = rawQueue.resolveSongs(), index = rawQueue.resolveIndex(), - isShuffled = stateHolder.isShuffled, + isShuffled = rawQueue.isShuffled, rawQueue = rawQueue) listeners.forEach { it.onQueueReordered( @@ -654,7 +656,7 @@ class PlaybackStateManagerImpl @Inject constructor() : PlaybackStateManager { parent = stateHolder.parent, queue = rawQueue.resolveSongs(), index = rawQueue.resolveIndex(), - isShuffled = stateHolder.isShuffled, + isShuffled = rawQueue.isShuffled, rawQueue = rawQueue) listeners.forEach { it.onNewPlayback( diff --git a/app/src/main/java/org/oxycblt/auxio/playback/system/ExoPlayerExt.kt b/app/src/main/java/org/oxycblt/auxio/playback/system/ExoPlayerExt.kt index 0ae7ce70f..b5fba9d74 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/system/ExoPlayerExt.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/system/ExoPlayerExt.kt @@ -24,7 +24,6 @@ import androidx.media3.common.Player import androidx.media3.exoplayer.ExoPlayer import org.oxycblt.auxio.music.Song import org.oxycblt.auxio.playback.state.RawQueue -import org.oxycblt.auxio.playback.state.RepeatMode import org.oxycblt.auxio.util.logD val ExoPlayer.song @@ -33,19 +32,9 @@ val ExoPlayer.song fun ExoPlayer.resolveQueue(): RawQueue { val heap = (0 until mediaItemCount).map { getMediaItemAt(it).song } val shuffledMapping = if (shuffleModeEnabled) unscrambleQueueIndices() else emptyList() - logD(shuffledMapping) return RawQueue(heap, shuffledMapping, currentMediaItemIndex) } -val ExoPlayer.repeat: RepeatMode - get() = - when (repeatMode) { - Player.REPEAT_MODE_OFF -> RepeatMode.NONE - Player.REPEAT_MODE_ONE -> RepeatMode.TRACK - Player.REPEAT_MODE_ALL -> RepeatMode.ALL - else -> throw IllegalStateException("Unknown repeat mode: $repeatMode") - } - fun ExoPlayer.orderedQueue(queue: Collection, start: Song?) { clearMediaItems() shuffleModeEnabled = false diff --git a/app/src/main/java/org/oxycblt/auxio/playback/system/PlaybackService.kt b/app/src/main/java/org/oxycblt/auxio/playback/system/PlaybackService.kt index 64c5d2ffd..6c9a2dfab 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/system/PlaybackService.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/system/PlaybackService.kt @@ -88,6 +88,7 @@ class PlaybackService : Service(), Player.Listener, PlaybackStateHolder, + PlaybackSettings.Listener, MediaSessionComponent.Listener, MusicRepository.UpdateListener { // Player components @@ -156,6 +157,7 @@ class PlaybackService : playbackManager.registerStateHolder(this) musicRepository.addUpdateListener(this) mediaSessionComponent.registerListener(this) + playbackSettings.registerListener(this) val intentFilter = IntentFilter().apply { @@ -197,6 +199,7 @@ class PlaybackService : playbackManager.playing(false) playbackManager.unregisterStateHolder(this) musicRepository.removeUpdateListener(this) + playbackSettings.unregisterListener(this) unregisterReceiver(systemReceiver) serviceJob.cancel() @@ -217,9 +220,6 @@ class PlaybackService : // --- PLAYBACKSTATEHOLDER OVERRIDES --- - override val repeatMode - get() = player.repeat - override val progression: Progression get() = player.song?.let { @@ -232,10 +232,16 @@ class PlaybackService : } ?: Progression.nil() - override var parent: MusicParent? = null + override val repeatMode + get() = + when (val repeatMode = player.repeatMode) { + Player.REPEAT_MODE_OFF -> RepeatMode.NONE + Player.REPEAT_MODE_ONE -> RepeatMode.TRACK + Player.REPEAT_MODE_ALL -> RepeatMode.ALL + else -> throw IllegalStateException("Unknown repeat mode: $repeatMode") + } - override val isShuffled - get() = player.shuffleModeEnabled + override var parent: MusicParent? = null override fun resolveQueue() = player.resolveQueue() @@ -272,6 +278,7 @@ class PlaybackService : RepeatMode.TRACK -> Player.REPEAT_MODE_ONE } playbackManager.ack(this, StateAck.RepeatModeChanged) + updatePauseOnRepeat() } override fun seekTo(positionMs: Long) { @@ -284,7 +291,11 @@ class PlaybackService : } override fun prev() { - player.seekToPrevious() + if (playbackSettings.rewindWithPrev) { + player.seekToPrevious() + } else { + player.seekToPreviousMediaItem() + } playbackManager.ack(this, StateAck.IndexMoved) } @@ -398,6 +409,15 @@ class PlaybackService : } } + override fun onPlaybackStateChanged(playbackState: Int) { + super.onPlaybackStateChanged(playbackState) + + if (playbackState == Player.STATE_ENDED && player.repeatMode == Player.REPEAT_MODE_OFF) { + goto(0) + player.pause() + } + } + override fun onEvents(player: Player, events: Player.Events) { super.onEvents(player, events) @@ -418,6 +438,12 @@ class PlaybackService : playbackManager.next() } + // --- OTHER OVERRIDES --- + + override fun onPauseOnRepeatChanged() { + updatePauseOnRepeat() + } + override fun onMusicChanges(changes: MusicRepository.Changes) { if (changes.deviceLibrary && musicRepository.deviceLibrary != null) { // We now have a library, see if we have anything we need to do. @@ -428,6 +454,11 @@ class PlaybackService : // --- OTHER FUNCTIONS --- + private fun updatePauseOnRepeat() { + player.pauseAtEndOfMediaItems = + playbackManager.repeatMode == RepeatMode.TRACK && playbackSettings.pauseOnRepeat + } + private fun broadcastAudioEffectAction(event: String) { logD("Broadcasting AudioEffect event: $event") sendBroadcast( @@ -556,6 +587,5 @@ class PlaybackService : const val ACTION_PLAY_PAUSE = BuildConfig.APPLICATION_ID + ".action.PLAY_PAUSE" const val ACTION_SKIP_NEXT = BuildConfig.APPLICATION_ID + ".action.NEXT" const val ACTION_EXIT = BuildConfig.APPLICATION_ID + ".action.EXIT" - private const val REWIND_THRESHOLD = 3000L } }