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.
This commit is contained in:
Alexander Capehart 2024-01-13 20:26:02 -07:00
parent bd240f967e
commit b2d71f8903
No known key found for this signature in database
GPG key ID: 37DBE3621FE9AD47
5 changed files with 54 additions and 29 deletions

View file

@ -66,6 +66,8 @@ interface PlaybackSettings : Settings<PlaybackSettings.Listener> {
fun onNotificationActionChanged() {} fun onNotificationActionChanged() {}
/** Called when [barAction] has changed. */ /** Called when [barAction] has changed. */
fun onBarActionChanged() {} 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") logD("Dispatching bar action change")
listener.onBarActionChanged() listener.onBarActionChanged()
} }
getString(R.string.set_key_repeat_pause) -> {
logD("Dispatching pause on repeat change")
listener.onPauseOnRepeatChanged()
}
} }
} }

View file

@ -34,8 +34,6 @@ interface PlaybackStateHolder {
fun resolveQueue(): RawQueue fun resolveQueue(): RawQueue
val isShuffled: Boolean
val audioSessionId: Int val audioSessionId: Int
fun newPlayback(queue: List<Song>, start: Song?, parent: MusicParent?, shuffled: Boolean) fun newPlayback(queue: List<Song>, start: Song?, parent: MusicParent?, shuffled: Boolean)

View file

@ -187,9 +187,9 @@ interface PlaybackStateManager {
* [PlaybackStateHolder]. * [PlaybackStateHolder].
* *
* @param stateHolder The [PlaybackStateHolder] to synchronize with. Must be the current * @param stateHolder The [PlaybackStateHolder] to synchronize with. Must be the current
* [PlaybackStateHolder]. Does nothing if invoked by another [PlaybackStateHolder] * [PlaybackStateHolder]. Does nothing if invoked by another [PlaybackStateHolder]
* implementation. * implementation.
* @param ack The [StateAck] to acknowledge. * @param ack The [StateAck] to acknowledge.
*/ */
fun ack(stateHolder: PlaybackStateHolder, ack: StateAck) fun ack(stateHolder: PlaybackStateHolder, ack: StateAck)
@ -269,8 +269,8 @@ interface PlaybackStateManager {
fun onQueueChanged(queue: List<Song>, index: Int, change: QueueChange) {} fun onQueueChanged(queue: List<Song>, index: Int, change: QueueChange) {}
/** /**
* Called when the queue has changed in a non-trivial manner (such as re-shuffling), but * Called when the queue has changed in a non-trivial manner (such as re-shuffling), but the
* the currently playing [Song] has not. * currently playing [Song] has not.
* *
* @param queue The songs of the new queue. * @param queue The songs of the new queue.
* @param index The new index of the currently playing [Song]. * @param index The new index of the currently playing [Song].
@ -407,6 +407,8 @@ class PlaybackStateManagerImpl @Inject constructor() : PlaybackStateManager {
} }
this.stateHolder = stateHolder this.stateHolder = stateHolder
// TODO: Re-init player
} }
@Synchronized @Synchronized
@ -627,7 +629,7 @@ class PlaybackStateManagerImpl @Inject constructor() : PlaybackStateManager {
) )
if (change.type == QueueChange.Type.SONG) { if (change.type == QueueChange.Type.SONG) {
playing(true) stateHolder.playing(true)
} }
listeners.forEach { listeners.forEach {
@ -640,7 +642,7 @@ class PlaybackStateManagerImpl @Inject constructor() : PlaybackStateManager {
stateMirror.copy( stateMirror.copy(
queue = rawQueue.resolveSongs(), queue = rawQueue.resolveSongs(),
index = rawQueue.resolveIndex(), index = rawQueue.resolveIndex(),
isShuffled = stateHolder.isShuffled, isShuffled = rawQueue.isShuffled,
rawQueue = rawQueue) rawQueue = rawQueue)
listeners.forEach { listeners.forEach {
it.onQueueReordered( it.onQueueReordered(
@ -654,7 +656,7 @@ class PlaybackStateManagerImpl @Inject constructor() : PlaybackStateManager {
parent = stateHolder.parent, parent = stateHolder.parent,
queue = rawQueue.resolveSongs(), queue = rawQueue.resolveSongs(),
index = rawQueue.resolveIndex(), index = rawQueue.resolveIndex(),
isShuffled = stateHolder.isShuffled, isShuffled = rawQueue.isShuffled,
rawQueue = rawQueue) rawQueue = rawQueue)
listeners.forEach { listeners.forEach {
it.onNewPlayback( it.onNewPlayback(

View file

@ -24,7 +24,6 @@ import androidx.media3.common.Player
import androidx.media3.exoplayer.ExoPlayer import androidx.media3.exoplayer.ExoPlayer
import org.oxycblt.auxio.music.Song import org.oxycblt.auxio.music.Song
import org.oxycblt.auxio.playback.state.RawQueue import org.oxycblt.auxio.playback.state.RawQueue
import org.oxycblt.auxio.playback.state.RepeatMode
import org.oxycblt.auxio.util.logD import org.oxycblt.auxio.util.logD
val ExoPlayer.song val ExoPlayer.song
@ -33,19 +32,9 @@ val ExoPlayer.song
fun ExoPlayer.resolveQueue(): RawQueue { fun ExoPlayer.resolveQueue(): RawQueue {
val heap = (0 until mediaItemCount).map { getMediaItemAt(it).song } val heap = (0 until mediaItemCount).map { getMediaItemAt(it).song }
val shuffledMapping = if (shuffleModeEnabled) unscrambleQueueIndices() else emptyList() val shuffledMapping = if (shuffleModeEnabled) unscrambleQueueIndices() else emptyList()
logD(shuffledMapping)
return RawQueue(heap, shuffledMapping, currentMediaItemIndex) 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<Song>, start: Song?) { fun ExoPlayer.orderedQueue(queue: Collection<Song>, start: Song?) {
clearMediaItems() clearMediaItems()
shuffleModeEnabled = false shuffleModeEnabled = false

View file

@ -88,6 +88,7 @@ class PlaybackService :
Service(), Service(),
Player.Listener, Player.Listener,
PlaybackStateHolder, PlaybackStateHolder,
PlaybackSettings.Listener,
MediaSessionComponent.Listener, MediaSessionComponent.Listener,
MusicRepository.UpdateListener { MusicRepository.UpdateListener {
// Player components // Player components
@ -156,6 +157,7 @@ class PlaybackService :
playbackManager.registerStateHolder(this) playbackManager.registerStateHolder(this)
musicRepository.addUpdateListener(this) musicRepository.addUpdateListener(this)
mediaSessionComponent.registerListener(this) mediaSessionComponent.registerListener(this)
playbackSettings.registerListener(this)
val intentFilter = val intentFilter =
IntentFilter().apply { IntentFilter().apply {
@ -197,6 +199,7 @@ class PlaybackService :
playbackManager.playing(false) playbackManager.playing(false)
playbackManager.unregisterStateHolder(this) playbackManager.unregisterStateHolder(this)
musicRepository.removeUpdateListener(this) musicRepository.removeUpdateListener(this)
playbackSettings.unregisterListener(this)
unregisterReceiver(systemReceiver) unregisterReceiver(systemReceiver)
serviceJob.cancel() serviceJob.cancel()
@ -217,9 +220,6 @@ class PlaybackService :
// --- PLAYBACKSTATEHOLDER OVERRIDES --- // --- PLAYBACKSTATEHOLDER OVERRIDES ---
override val repeatMode
get() = player.repeat
override val progression: Progression override val progression: Progression
get() = get() =
player.song?.let { player.song?.let {
@ -232,10 +232,16 @@ class PlaybackService :
} }
?: Progression.nil() ?: 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 override var parent: MusicParent? = null
get() = player.shuffleModeEnabled
override fun resolveQueue() = player.resolveQueue() override fun resolveQueue() = player.resolveQueue()
@ -272,6 +278,7 @@ class PlaybackService :
RepeatMode.TRACK -> Player.REPEAT_MODE_ONE RepeatMode.TRACK -> Player.REPEAT_MODE_ONE
} }
playbackManager.ack(this, StateAck.RepeatModeChanged) playbackManager.ack(this, StateAck.RepeatModeChanged)
updatePauseOnRepeat()
} }
override fun seekTo(positionMs: Long) { override fun seekTo(positionMs: Long) {
@ -284,7 +291,11 @@ class PlaybackService :
} }
override fun prev() { override fun prev() {
player.seekToPrevious() if (playbackSettings.rewindWithPrev) {
player.seekToPrevious()
} else {
player.seekToPreviousMediaItem()
}
playbackManager.ack(this, StateAck.IndexMoved) 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) { override fun onEvents(player: Player, events: Player.Events) {
super.onEvents(player, events) super.onEvents(player, events)
@ -418,6 +438,12 @@ class PlaybackService :
playbackManager.next() playbackManager.next()
} }
// --- OTHER OVERRIDES ---
override fun onPauseOnRepeatChanged() {
updatePauseOnRepeat()
}
override fun onMusicChanges(changes: MusicRepository.Changes) { override fun onMusicChanges(changes: MusicRepository.Changes) {
if (changes.deviceLibrary && musicRepository.deviceLibrary != null) { if (changes.deviceLibrary && musicRepository.deviceLibrary != null) {
// We now have a library, see if we have anything we need to do. // We now have a library, see if we have anything we need to do.
@ -428,6 +454,11 @@ class PlaybackService :
// --- OTHER FUNCTIONS --- // --- OTHER FUNCTIONS ---
private fun updatePauseOnRepeat() {
player.pauseAtEndOfMediaItems =
playbackManager.repeatMode == RepeatMode.TRACK && playbackSettings.pauseOnRepeat
}
private fun broadcastAudioEffectAction(event: String) { private fun broadcastAudioEffectAction(event: String) {
logD("Broadcasting AudioEffect event: $event") logD("Broadcasting AudioEffect event: $event")
sendBroadcast( sendBroadcast(
@ -556,6 +587,5 @@ class PlaybackService :
const val ACTION_PLAY_PAUSE = BuildConfig.APPLICATION_ID + ".action.PLAY_PAUSE" const val ACTION_PLAY_PAUSE = BuildConfig.APPLICATION_ID + ".action.PLAY_PAUSE"
const val ACTION_SKIP_NEXT = BuildConfig.APPLICATION_ID + ".action.NEXT" const val ACTION_SKIP_NEXT = BuildConfig.APPLICATION_ID + ".action.NEXT"
const val ACTION_EXIT = BuildConfig.APPLICATION_ID + ".action.EXIT" const val ACTION_EXIT = BuildConfig.APPLICATION_ID + ".action.EXIT"
private const val REWIND_THRESHOLD = 3000L
} }
} }