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 } }