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.RepeatMode
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
* and routes them to PlaybackStateManager so I know that they will work the way I want it to.
* A thin wrapper around the player instance that drastically reduces the command surface and
* 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
*/
@ -70,45 +77,34 @@ class MediaSessionPlayer(
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(
mediaItems: MutableList<MediaItem>,
startIndex: Int,
startPositionMs: Long
) {
// 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
// of this, we expand the given MediaItems into the command that should be sent to the
// player.
val command =
if (mediaItems.size > 1) {
this.playMediaItemSelection(mediaItems, startIndex)
} else {
this.playSingleMediaItem(mediaItems.first())
// We assume the only people calling this method are going to be the MediaSession callbacks.
// As part of this, we expand the given MediaItems into the command that should be sent to
// the player.
if (startIndex != C.INDEX_UNSET || startPositionMs != C.TIME_UNSET) {
error("Playing MediaItems with custom position parameters is not supported")
}
if (mediaItems.size != 1) {
error("Playing multiple MediaItems is not supported")
}
val command = expandMediaItemIntoCommand(mediaItems.first())
requireNotNull(command) { "Invalid playback configuration" }
playbackManager.play(command)
if (startPositionMs != C.TIME_UNSET) {
playbackManager.seekTo(startPositionMs)
}
}
private fun playMediaItemSelection(
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? {
private fun expandMediaItemIntoCommand(mediaItem: MediaItem): PlaybackCommand? {
val uid = MediaSessionUID.fromString(mediaItem.mediaId) ?: return null
val music: Music
var parent: MusicParent? = null
@ -143,9 +139,9 @@ class MediaSessionPlayer(
null -> commandFactory.songFromAll(music, ShuffleMode.IMPLICIT)
}
override fun setPlayWhenReady(playWhenReady: Boolean) {
playbackManager.playing(playWhenReady)
}
override fun play() = playbackManager.playing(true)
override fun pause() = playbackManager.playing(false)
override fun setRepeatMode(repeatMode: Int) {
val appRepeatMode =
@ -243,9 +239,6 @@ class MediaSessionPlayer(
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(index: Int, mediaItem: MediaItem) = notAllowed()
@ -280,14 +273,12 @@ class MediaSessionPlayer(
@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 release() = notAllowed()
override fun setPlayWhenReady(playWhenReady: Boolean) = notAllowed()
override fun stop() = notAllowed()
override fun hasNextMediaItem() = notAllowed()
@ -337,7 +328,11 @@ class MediaSessionPlayer(
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> {