playback: improve queue item setup

- Use same media description code
- Make queue removal more reliable
This commit is contained in:
Alexander Capehart 2024-08-29 09:29:46 -06:00
parent 130d30c70d
commit 889713d5e0
No known key found for this signature in database
GPG key ID: 37DBE3621FE9AD47
3 changed files with 59 additions and 59 deletions

View file

@ -105,11 +105,8 @@ fun Category.toMediaItem(context: Context): MediaItem {
return MediaItem(description.build(), MediaItem.FLAG_BROWSABLE)
}
fun Song.toMediaItem(
context: Context,
parent: MusicParent? = null,
vararg sugar: Sugar
): MediaItem {
fun Song.toMediaDescription(context: Context, parent: MusicParent? = null,
vararg sugar: Sugar): MediaDescriptionCompat {
val mediaSessionUID =
if (parent == null) {
MediaSessionUID.SingleItem(uid)
@ -117,17 +114,23 @@ fun Song.toMediaItem(
MediaSessionUID.ChildItem(parent.uid, uid)
}
val extras = Bundle().apply { sugar.forEach { this.it(context) } }
val description =
MediaDescriptionCompat.Builder()
.setMediaId(mediaSessionUID.toString())
.setTitle(name.resolve(context))
.setSubtitle(artists.resolveNames(context))
.setDescription(album.name.resolve(context))
.setIconUri(album.cover.single.mediaStoreCoverUri)
.setMediaUri(uri)
.setExtras(extras)
.build()
return MediaItem(description, MediaItem.FLAG_PLAYABLE)
return MediaDescriptionCompat.Builder()
.setMediaId(mediaSessionUID.toString())
.setTitle(name.resolve(context))
.setSubtitle(artists.resolveNames(context))
.setDescription(album.name.resolve(context))
.setIconUri(cover.mediaStoreCoverUri)
.setMediaUri(uri)
.setExtras(extras)
.build()
}
fun Song.toMediaItem(
context: Context,
parent: MusicParent? = null,
vararg sugar: Sugar
): MediaItem {
return MediaItem(toMediaDescription(context, parent, *sugar), MediaItem.FLAG_PLAYABLE)
}
fun Album.toMediaItem(

View file

@ -42,6 +42,8 @@ import org.oxycblt.auxio.music.MusicParent
import org.oxycblt.auxio.music.Song
import org.oxycblt.auxio.music.resolveNames
import org.oxycblt.auxio.music.service.MediaSessionUID
import org.oxycblt.auxio.music.service.toMediaDescription
import org.oxycblt.auxio.music.service.toMediaItem
import org.oxycblt.auxio.playback.ActionMode
import org.oxycblt.auxio.playback.PlaybackSettings
import org.oxycblt.auxio.playback.service.MediaSessionInterface
@ -304,20 +306,7 @@ private constructor(
private fun updateQueue(queue: List<Song>) {
val queueItems =
queue.mapIndexed { i, song ->
val description =
MediaDescriptionCompat.Builder()
// Media ID should not be the item index but rather the UID,
// as it's used to request a song to be played from the queue.
.setMediaId(song.uid.toString())
.setTitle(song.name.resolve(context))
.setSubtitle(song.artists.resolveNames(context))
// Since we usually have to load many songs into the queue, use the
// MediaStore URI instead of loading a bitmap.
.setIconUri(song.album.cover.single.mediaStoreCoverUri)
.setMediaUri(song.uri)
.setExtras(
Bundle().apply { putInt(MediaSessionInterface.KEY_QUEUE_POS, i) })
.build()
val description = song.toMediaDescription(context, null, { putInt(MediaSessionInterface.KEY_QUEUE_POS, i) })
// Store the item index so we can then use the analogous index in the
// playback state.
MediaSessionCompat.QueueItem(description, i.toLong())

View file

@ -93,22 +93,11 @@ constructor(
super.onPlayFromSearch(query, extras)
val deviceLibrary = musicRepository.deviceLibrary ?: return
val userLibrary = musicRepository.userLibrary ?: return
val command = expandSearchInfoCommand(
query.ifBlank { null },
extras,
deviceLibrary,
userLibrary)
val command =
expandSearchInfoCommand(query.ifBlank { null }, extras, deviceLibrary, userLibrary)
playbackManager.play(requireNotNull(command) { "Invalid playback configuration" })
}
data class QueryBundle(
val title: String?,
val album: String?,
val artist: String?,
val genre: String?,
val playlist: String?
)
override fun onAddQueueItem(description: MediaDescriptionCompat) {
super.onAddQueueItem(description)
val deviceLibrary = musicRepository.deviceLibrary ?: return
@ -125,8 +114,22 @@ constructor(
override fun onRemoveQueueItem(description: MediaDescriptionCompat) {
super.onRemoveQueueItem(description)
val at = description.extras?.getInt(KEY_QUEUE_POS) ?: return
playbackManager.removeQueueItem(at)
val at = description.extras?.getInt(KEY_QUEUE_POS)
if (at != null) {
// Direct queue item removal w/preserved extras, we can explicitly remove
// the correct item rather than a duplicate elsewhere.
playbackManager.removeQueueItem(at)
return
}
// Non-queue item or queue item lost it's extras in transit, remove the first item
val uid = MediaSessionUID.fromString(description.mediaId ?: return) ?: return
val songUid = when (uid) {
is MediaSessionUID.SingleItem -> uid.uid
is MediaSessionUID.ChildItem -> uid.childUid
else -> return
}
val firstAt = playbackManager.queue.indexOfFirst { it.uid == songUid }
playbackManager.removeQueueItem(firstAt)
}
override fun onPlay() {
@ -224,26 +227,28 @@ constructor(
val songQuery = extras.getString(MediaStore.EXTRA_MEDIA_TITLE)
val albumQuery = extras.getString(MediaStore.EXTRA_MEDIA_ALBUM)
val artistQuery = extras.getString(MediaStore.EXTRA_MEDIA_ARTIST)
val best = deviceLibrary.songs.maxByOrNull {
fuzzy(it.name, songQuery) + fuzzy(it.album.name, albumQuery) +
val best =
deviceLibrary.songs.maxByOrNull {
fuzzy(it.name, songQuery) +
fuzzy(it.album.name, albumQuery) +
it.artists.maxOf { artist -> fuzzy(artist.name, artistQuery) }
}
}
if (best != null) {
return expandSongIntoCommand(best, null)
}
}
MediaStore.Audio.Albums.ENTRY_CONTENT_TYPE -> {
val albumQuery = extras.getString(MediaStore.EXTRA_MEDIA_ALBUM)
val artistQuery = extras.getString(MediaStore.EXTRA_MEDIA_ARTIST)
val best = deviceLibrary.albums.maxByOrNull {
fuzzy(it.name, albumQuery) + it.artists.maxOf { artist -> fuzzy(artist.name, artistQuery) }
}
val best =
deviceLibrary.albums.maxByOrNull {
fuzzy(it.name, albumQuery) +
it.artists.maxOf { artist -> fuzzy(artist.name, artistQuery) }
}
if (best != null) {
return commandFactory.album(best, ShuffleMode.OFF)
}
}
MediaStore.Audio.Artists.ENTRY_CONTENT_TYPE -> {
val artistQuery = extras.getString(MediaStore.EXTRA_MEDIA_ARTIST)
val best = deviceLibrary.artists.maxByOrNull { fuzzy(it.name, artistQuery) }
@ -251,7 +256,6 @@ constructor(
return commandFactory.artist(best, ShuffleMode.OFF)
}
}
MediaStore.Audio.Genres.ENTRY_CONTENT_TYPE -> {
val genreQuery = extras.getString(MediaStore.EXTRA_MEDIA_GENRE)
val best = deviceLibrary.genres.maxByOrNull { fuzzy(it.name, genreQuery) }
@ -259,7 +263,6 @@ constructor(
return commandFactory.genre(best, ShuffleMode.OFF)
}
}
MediaStore.Audio.Playlists.ENTRY_CONTENT_TYPE -> {
val playlistQuery = extras.getString(MediaStore.EXTRA_MEDIA_PLAYLIST)
val best = userLibrary.playlists.maxByOrNull { fuzzy(it.name, playlistQuery) }
@ -267,14 +270,19 @@ constructor(
return commandFactory.playlist(best, ShuffleMode.OFF)
}
}
else -> {}
}
val bestMusic = (deviceLibrary.songs + deviceLibrary.albums + deviceLibrary.artists + deviceLibrary.genres + userLibrary.playlists)
.maxByOrNull { fuzzy(it.name, query) }
val bestMusic =
(deviceLibrary.songs +
deviceLibrary.albums +
deviceLibrary.artists +
deviceLibrary.genres +
userLibrary.playlists)
.maxByOrNull { fuzzy(it.name, query) }
// TODO: Error out when we can't correctly resolve the query
return bestMusic?.let { expandMusicIntoCommand(it, null) } ?: commandFactory.all(ShuffleMode.ON)
return bestMusic?.let { expandMusicIntoCommand(it, null) }
?: commandFactory.all(ShuffleMode.ON)
}
private fun fuzzy(name: Name, query: String?): Double =