playback: tweak media3 command surface

This commit is contained in:
Alexander Capehart 2024-04-11 15:09:25 -06:00
parent fb15791c2f
commit 0ca928a477
No known key found for this signature in database
GPG key ID: 37DBE3621FE9AD47

View file

@ -44,10 +44,17 @@ import org.oxycblt.auxio.playback.state.PlaybackCommand
import org.oxycblt.auxio.playback.state.PlaybackStateManager import org.oxycblt.auxio.playback.state.PlaybackStateManager
import org.oxycblt.auxio.playback.state.RepeatMode import org.oxycblt.auxio.playback.state.RepeatMode
import org.oxycblt.auxio.playback.state.ShuffleMode import org.oxycblt.auxio.playback.state.ShuffleMode
import org.oxycblt.auxio.util.logD
import org.oxycblt.auxio.util.logE
/** /**
* A thin wrapper around the player instance that takes all the events I know MediaSession will send * A thin wrapper around the player instance that drastically reduces the command surface and
* and routes them to PlaybackStateManager so I know that they will work the way I want it to. * forwards all commands to PlaybackStateManager so I can ensure that all the unhinged commands
* that Media3 will throw at me will be handled in a predictable way, rather than just clobbering
* the playback state. Largely limited to the legacy media APIs.
*
* I'll add more support as I go along when I can confirm that apps will use the Media3 API and
* send more advanced commands.
* *
* @author Alexander Capehart * @author Alexander Capehart
*/ */
@ -70,45 +77,34 @@ class MediaSessionPlayer(
command in setOf(Player.COMMAND_SEEK_TO_NEXT, Player.COMMAND_SEEK_TO_PREVIOUS) command in setOf(Player.COMMAND_SEEK_TO_NEXT, Player.COMMAND_SEEK_TO_PREVIOUS)
} }
override fun setMediaItems(mediaItems: MutableList<MediaItem>, resetPosition: Boolean) {
if (!resetPosition) {
error("Playing MediaItems with custom position parameters is not supported")
}
setMediaItems(mediaItems, C.INDEX_UNSET, C.TIME_UNSET)
}
override fun setMediaItems( override fun setMediaItems(
mediaItems: MutableList<MediaItem>, mediaItems: MutableList<MediaItem>,
startIndex: Int, startIndex: Int,
startPositionMs: Long startPositionMs: Long
) { ) {
// We assume the only people calling this method are going to be the MediaSession callbacks, // We assume the only people calling this method are going to be the MediaSession callbacks.
// since anything else (like newPlayback) will be calling directly on the player. As part // As part of this, we expand the given MediaItems into the command that should be sent to
// of this, we expand the given MediaItems into the command that should be sent to the // the player.
// player. if (startIndex != C.INDEX_UNSET || startPositionMs != C.TIME_UNSET) {
val command = error("Playing MediaItems with custom position parameters is not supported")
if (mediaItems.size > 1) {
this.playMediaItemSelection(mediaItems, startIndex)
} else {
this.playSingleMediaItem(mediaItems.first())
} }
if (mediaItems.size != 1) {
error("Playing multiple MediaItems is not supported")
}
val command = expandMediaItemIntoCommand(mediaItems.first())
requireNotNull(command) { "Invalid playback configuration" } requireNotNull(command) { "Invalid playback configuration" }
playbackManager.play(command) playbackManager.play(command)
if (startPositionMs != C.TIME_UNSET) {
playbackManager.seekTo(startPositionMs)
}
} }
private fun playMediaItemSelection( private fun expandMediaItemIntoCommand(mediaItem: MediaItem): PlaybackCommand? {
mediaItems: List<MediaItem>,
startIndex: Int
): PlaybackCommand? {
val deviceLibrary = musicRepository.deviceLibrary ?: return null
val targetSong = mediaItems.getOrNull(startIndex)?.toSong(deviceLibrary)
val songs = mediaItems.mapNotNull { it.toSong(deviceLibrary) }
var index = startIndex
if (targetSong != null) {
while (songs.getOrNull(index)?.uid != targetSong.uid) {
index--
}
}
return commandFactory.songs(songs, ShuffleMode.OFF)
}
private fun playSingleMediaItem(mediaItem: MediaItem): PlaybackCommand? {
val uid = MediaSessionUID.fromString(mediaItem.mediaId) ?: return null val uid = MediaSessionUID.fromString(mediaItem.mediaId) ?: return null
val music: Music val music: Music
var parent: MusicParent? = null var parent: MusicParent? = null
@ -143,9 +139,9 @@ class MediaSessionPlayer(
null -> commandFactory.songFromAll(music, ShuffleMode.IMPLICIT) null -> commandFactory.songFromAll(music, ShuffleMode.IMPLICIT)
} }
override fun setPlayWhenReady(playWhenReady: Boolean) { override fun play() = playbackManager.playing(true)
playbackManager.playing(playWhenReady)
} override fun pause() = playbackManager.playing(false)
override fun setRepeatMode(repeatMode: Int) { override fun setRepeatMode(repeatMode: Int) {
val appRepeatMode = val appRepeatMode =
@ -243,9 +239,6 @@ class MediaSessionPlayer(
override fun setMediaItems(mediaItems: MutableList<MediaItem>) = notAllowed() override fun setMediaItems(mediaItems: MutableList<MediaItem>) = notAllowed()
override fun setMediaItems(mediaItems: MutableList<MediaItem>, resetPosition: Boolean) =
notAllowed()
override fun addMediaItem(mediaItem: MediaItem) = notAllowed() override fun addMediaItem(mediaItem: MediaItem) = notAllowed()
override fun addMediaItem(index: Int, mediaItem: MediaItem) = notAllowed() override fun addMediaItem(index: Int, mediaItem: MediaItem) = notAllowed()
@ -280,14 +273,12 @@ class MediaSessionPlayer(
@Deprecated("Deprecated in Java") override fun seekToNextWindow() = notAllowed() @Deprecated("Deprecated in Java") override fun seekToNextWindow() = notAllowed()
override fun play() = playbackManager.playing(true)
override fun pause() = playbackManager.playing(false)
override fun prepare() = notAllowed() override fun prepare() = notAllowed()
override fun release() = notAllowed() override fun release() = notAllowed()
override fun setPlayWhenReady(playWhenReady: Boolean) = notAllowed()
override fun stop() = notAllowed() override fun stop() = notAllowed()
override fun hasNextMediaItem() = notAllowed() override fun hasNextMediaItem() = notAllowed()
@ -337,7 +328,11 @@ class MediaSessionPlayer(
override fun clearVideoTextureView(textureView: TextureView?) = notAllowed() override fun clearVideoTextureView(textureView: TextureView?) = notAllowed()
private fun notAllowed(): Nothing = error("MediaSession unexpectedly called this method") private fun notAllowed(): Nothing {
logD("MediaSession unexpectedly called this method")
logE(Exception().stackTraceToString())
error("MediaSession unexpectedly called this method")
}
} }
fun Player.unscrambleQueueIndices(): List<Int> { fun Player.unscrambleQueueIndices(): List<Int> {