service: bundle parent info into extras

Instead of using mediaId.

This makes it so that there is only really one mediaId to work
with, with an optional extra for playback that I desperately
hope is preserved on all instances of Android Auto.
This commit is contained in:
Alexander Capehart 2024-10-14 12:46:06 -06:00
parent 0b3a136320
commit 19f3e07c8e
No known key found for this signature in database
GPG key ID: 37DBE3621FE9AD47
6 changed files with 44 additions and 74 deletions

View file

@ -36,6 +36,7 @@ import dagger.hilt.android.AndroidEntryPoint
import javax.inject.Inject
import org.oxycblt.auxio.music.service.MusicServiceFragment
import org.oxycblt.auxio.playback.service.PlaybackServiceFragment
import org.oxycblt.auxio.util.logD
@AndroidEntryPoint
class AuxioService :
@ -149,6 +150,7 @@ class AuxioService :
}
override fun invalidateMusic(mediaId: String) {
logD(mediaId)
notifyChildrenChanged(mediaId)
}

View file

@ -205,7 +205,7 @@ sealed interface DetailSection {
data class Artists(override val items: List<Artist>) : PlainSection<Artist>() {
override val order = 0
override val stringRes = R.string.lbl_songs
override val stringRes = R.string.lbl_artists
}
data class Albums(val category: Category, override val items: List<Album>) :

View file

@ -47,10 +47,6 @@ sealed interface MediaSessionUID {
override fun toString() = "$ID_ITEM:$uid"
}
data class ChildItem(val parentUid: Music.UID, val childUid: Music.UID) : MediaSessionUID {
override fun toString() = "$ID_ITEM:$parentUid>$childUid"
}
companion object {
const val ID_CATEGORY = BuildConfig.APPLICATION_ID + ".category"
const val ID_ITEM = BuildConfig.APPLICATION_ID + ".item"
@ -62,16 +58,7 @@ sealed interface MediaSessionUID {
}
return when (parts[0]) {
ID_CATEGORY -> Tab(TabNode.fromString(parts[1]) ?: return null)
ID_ITEM -> {
val uids = parts[1].split(">", limit = 2)
if (uids.size == 1) {
Music.UID.fromString(uids[0])?.let { SingleItem(it) }
} else {
Music.UID.fromString(uids[0])?.let { parent ->
Music.UID.fromString(uids[1])?.let { child -> ChildItem(parent, child) }
}
}
}
ID_ITEM -> SingleItem(Music.UID.fromString(parts[1]) ?: return null)
else -> return null
}
}
@ -89,6 +76,10 @@ fun header(name: String): Sugar = {
putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, name)
}
fun child(of: MusicParent): Sugar = {
putString(MusicBrowser.KEY_CHILD_OF, MediaSessionUID.SingleItem(of.uid).toString())
}
private fun style(style: Int): Sugar = {
putInt(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_SINGLE_ITEM, style)
}
@ -115,17 +106,8 @@ fun TabNode.toMediaItem(context: Context): MediaItem {
return MediaItem(description.build(), MediaItem.FLAG_BROWSABLE)
}
fun Song.toMediaDescription(
context: Context,
parent: MusicParent? = null,
vararg sugar: Sugar
): MediaDescriptionCompat {
val mediaSessionUID =
if (parent == null) {
MediaSessionUID.SingleItem(uid)
} else {
MediaSessionUID.ChildItem(parent.uid, uid)
}
fun Song.toMediaDescription(context: Context, vararg sugar: Sugar): MediaDescriptionCompat {
val mediaSessionUID = MediaSessionUID.SingleItem(uid)
val extras = makeExtras(context, *sugar)
return MediaDescriptionCompat.Builder()
.setMediaId(mediaSessionUID.toString())
@ -138,25 +120,12 @@ fun Song.toMediaDescription(
.build()
}
fun Song.toMediaItem(
context: Context,
parent: MusicParent? = null,
vararg sugar: Sugar
): MediaItem {
return MediaItem(toMediaDescription(context, parent, *sugar), MediaItem.FLAG_PLAYABLE)
fun Song.toMediaItem(context: Context, vararg sugar: Sugar): MediaItem {
return MediaItem(toMediaDescription(context, *sugar), MediaItem.FLAG_PLAYABLE)
}
fun Album.toMediaItem(
context: Context,
parent: MusicParent? = null,
vararg sugar: Sugar
): MediaItem {
val mediaSessionUID =
if (parent == null) {
MediaSessionUID.SingleItem(uid)
} else {
MediaSessionUID.ChildItem(parent.uid, uid)
}
fun Album.toMediaItem(context: Context, vararg sugar: Sugar): MediaItem {
val mediaSessionUID = MediaSessionUID.SingleItem(uid)
val extras = makeExtras(context, *sugar)
val counts = context.getPlural(R.plurals.fmt_song_count, songs.size)
val description =

View file

@ -21,6 +21,7 @@ package org.oxycblt.auxio.music.service
import android.content.Context
import android.support.v4.media.MediaBrowserCompat.MediaItem
import javax.inject.Inject
import org.oxycblt.auxio.BuildConfig
import org.oxycblt.auxio.R
import org.oxycblt.auxio.detail.DetailGenerator
import org.oxycblt.auxio.detail.DetailSection
@ -117,8 +118,6 @@ private constructor(
is MediaSessionUID.Tab -> return uid.node.toMediaItem(context)
is MediaSessionUID.SingleItem ->
musicRepository.find(uid.uid)?.let { musicRepository.find(it.uid) }
is MediaSessionUID.ChildItem ->
musicRepository.find(uid.childUid)?.let { musicRepository.find(it.uid) }
null -> null
}
?: return null
@ -128,7 +127,7 @@ private constructor(
is Artist -> music.toMediaItem(context)
is Genre -> music.toMediaItem(context)
is Playlist -> music.toMediaItem(context)
is Song -> music.toMediaItem(context, null)
is Song -> music.toMediaItem(context)
}
}
@ -160,10 +159,10 @@ private constructor(
private fun SearchEngine.Items.toMediaItems(): MutableList<MediaItem> {
val music = mutableListOf<MediaItem>()
if (songs != null) {
music.addAll(songs.map { it.toMediaItem(context, null, header(R.string.lbl_songs)) })
music.addAll(songs.map { it.toMediaItem(context, header(R.string.lbl_songs)) })
}
if (albums != null) {
music.addAll(albums.map { it.toMediaItem(context, null, header(R.string.lbl_albums)) })
music.addAll(albums.map { it.toMediaItem(context, header(R.string.lbl_albums)) })
}
if (artists != null) {
music.addAll(artists.map { it.toMediaItem(context, header(R.string.lbl_artists)) })
@ -185,9 +184,6 @@ private constructor(
is MediaSessionUID.SingleItem -> {
getChildMediaItems(mediaSessionUID.uid)
}
is MediaSessionUID.ChildItem -> {
getChildMediaItems(mediaSessionUID.childUid)
}
null -> {
return null
}
@ -208,11 +204,11 @@ private constructor(
}
is TabNode.More -> {
val tabs = homeGenerator.tabs()
tabs.takeLast(tabs.size - maxTabs).map { TabNode.Home(it).toMediaItem(context) }
tabs.takeLast(tabs.size - maxTabs + 1).map { TabNode.Home(it).toMediaItem(context) }
}
is TabNode.Home ->
when (node.type) {
MusicType.SONGS -> homeGenerator.songs().map { it.toMediaItem(context, null) }
MusicType.SONGS -> homeGenerator.songs().map { it.toMediaItem(context) }
MusicType.ALBUMS -> homeGenerator.albums().map { it.toMediaItem(context) }
MusicType.ARTISTS -> homeGenerator.artists().map { it.toMediaItem(context) }
MusicType.GENRES -> homeGenerator.genres().map { it.toMediaItem(context) }
@ -225,18 +221,24 @@ private constructor(
return detail.sections.flatMap { section ->
when (section) {
is DetailSection.Songs ->
section.items.map { it.toMediaItem(context, null, header(section.stringRes)) }
section.items.map {
it.toMediaItem(context, header(section.stringRes), child(detail.parent))
}
is DetailSection.Albums ->
section.items.map { it.toMediaItem(context, null, header(section.stringRes)) }
section.items.map { it.toMediaItem(context, header(section.stringRes)) }
is DetailSection.Artists ->
section.items.map { it.toMediaItem(context, header(section.stringRes)) }
is DetailSection.Discs ->
section.discs.flatMap { (disc, songs) ->
val discString = disc.resolveNumber(context)
songs.map { it.toMediaItem(context, null, header(discString)) }
songs.map { it.toMediaItem(context, header(discString)) }
}
else -> error("Unknown section type: $section")
}
}
}
companion object {
const val KEY_CHILD_OF = BuildConfig.APPLICATION_ID + ".key.CHILD_OF"
}
}

View file

@ -295,7 +295,7 @@ private constructor(
queue.mapIndexed { i, song ->
val description =
song.toMediaDescription(
context, null, { putInt(MediaSessionInterface.KEY_QUEUE_POS, i) })
context, { 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

@ -41,11 +41,13 @@ import org.oxycblt.auxio.music.Song
import org.oxycblt.auxio.music.device.DeviceLibrary
import org.oxycblt.auxio.music.info.Name
import org.oxycblt.auxio.music.service.MediaSessionUID
import org.oxycblt.auxio.music.service.MusicBrowser
import org.oxycblt.auxio.music.user.UserLibrary
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
class MediaSessionInterface
@Inject
@ -80,7 +82,10 @@ constructor(
override fun onPlayFromMediaId(mediaId: String?, extras: Bundle?) {
super.onPlayFromMediaId(mediaId, extras)
val uid = MediaSessionUID.fromString(mediaId ?: return) ?: return
val command = expandUidIntoCommand(uid)
val parentUid =
extras?.getString(MusicBrowser.KEY_CHILD_OF)?.let { MediaSessionUID.fromString(it) }
val command = expandUidIntoCommand(uid, parentUid)
logD(extras?.getString(MusicBrowser.KEY_CHILD_OF))
playbackManager.play(requireNotNull(command) { "Invalid playback configuration" })
}
@ -105,7 +110,6 @@ constructor(
val songUid =
when (uid) {
is MediaSessionUID.SingleItem -> uid.uid
is MediaSessionUID.ChildItem -> uid.childUid
else -> return
}
val song = deviceLibrary.songs.find { it.uid == songUid } ?: return
@ -126,7 +130,6 @@ constructor(
val songUid =
when (uid) {
is MediaSessionUID.SingleItem -> uid.uid
is MediaSessionUID.ChildItem -> uid.childUid
else -> return
}
val firstAt = playbackManager.queue.indexOfFirst { it.uid == songUid }
@ -194,20 +197,14 @@ constructor(
context.sendBroadcast(Intent(action))
}
private fun expandUidIntoCommand(uid: MediaSessionUID): PlaybackCommand? {
val music: Music
var parent: MusicParent? = null
when (uid) {
is MediaSessionUID.SingleItem -> {
music = musicRepository.find(uid.uid) ?: return null
}
is MediaSessionUID.ChildItem -> {
music = musicRepository.find(uid.childUid) ?: return null
parent = musicRepository.find(uid.parentUid) as? MusicParent ?: return null
}
else -> return null
}
private fun expandUidIntoCommand(
uid: MediaSessionUID,
parentUid: MediaSessionUID?
): PlaybackCommand? {
val unwrappedUid = (uid as? MediaSessionUID.SingleItem)?.uid ?: return null
val unwrappedParentUid = (parentUid as? MediaSessionUID.SingleItem)?.uid
val music = musicRepository.find(unwrappedUid) ?: return null
val parent = unwrappedParentUid?.let { musicRepository.find(it) as? MusicParent }
return expandMusicIntoCommand(music, parent)
}