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:
parent
0b3a136320
commit
19f3e07c8e
6 changed files with 44 additions and 74 deletions
|
@ -36,6 +36,7 @@ import dagger.hilt.android.AndroidEntryPoint
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import org.oxycblt.auxio.music.service.MusicServiceFragment
|
import org.oxycblt.auxio.music.service.MusicServiceFragment
|
||||||
import org.oxycblt.auxio.playback.service.PlaybackServiceFragment
|
import org.oxycblt.auxio.playback.service.PlaybackServiceFragment
|
||||||
|
import org.oxycblt.auxio.util.logD
|
||||||
|
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
class AuxioService :
|
class AuxioService :
|
||||||
|
@ -149,6 +150,7 @@ class AuxioService :
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun invalidateMusic(mediaId: String) {
|
override fun invalidateMusic(mediaId: String) {
|
||||||
|
logD(mediaId)
|
||||||
notifyChildrenChanged(mediaId)
|
notifyChildrenChanged(mediaId)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -205,7 +205,7 @@ sealed interface DetailSection {
|
||||||
|
|
||||||
data class Artists(override val items: List<Artist>) : PlainSection<Artist>() {
|
data class Artists(override val items: List<Artist>) : PlainSection<Artist>() {
|
||||||
override val order = 0
|
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>) :
|
data class Albums(val category: Category, override val items: List<Album>) :
|
||||||
|
|
|
@ -47,10 +47,6 @@ sealed interface MediaSessionUID {
|
||||||
override fun toString() = "$ID_ITEM:$uid"
|
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 {
|
companion object {
|
||||||
const val ID_CATEGORY = BuildConfig.APPLICATION_ID + ".category"
|
const val ID_CATEGORY = BuildConfig.APPLICATION_ID + ".category"
|
||||||
const val ID_ITEM = BuildConfig.APPLICATION_ID + ".item"
|
const val ID_ITEM = BuildConfig.APPLICATION_ID + ".item"
|
||||||
|
@ -62,16 +58,7 @@ sealed interface MediaSessionUID {
|
||||||
}
|
}
|
||||||
return when (parts[0]) {
|
return when (parts[0]) {
|
||||||
ID_CATEGORY -> Tab(TabNode.fromString(parts[1]) ?: return null)
|
ID_CATEGORY -> Tab(TabNode.fromString(parts[1]) ?: return null)
|
||||||
ID_ITEM -> {
|
ID_ITEM -> SingleItem(Music.UID.fromString(parts[1]) ?: return null)
|
||||||
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) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else -> return null
|
else -> return null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -89,6 +76,10 @@ fun header(name: String): Sugar = {
|
||||||
putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, name)
|
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 = {
|
private fun style(style: Int): Sugar = {
|
||||||
putInt(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_SINGLE_ITEM, style)
|
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)
|
return MediaItem(description.build(), MediaItem.FLAG_BROWSABLE)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Song.toMediaDescription(
|
fun Song.toMediaDescription(context: Context, vararg sugar: Sugar): MediaDescriptionCompat {
|
||||||
context: Context,
|
val mediaSessionUID = MediaSessionUID.SingleItem(uid)
|
||||||
parent: MusicParent? = null,
|
|
||||||
vararg sugar: Sugar
|
|
||||||
): MediaDescriptionCompat {
|
|
||||||
val mediaSessionUID =
|
|
||||||
if (parent == null) {
|
|
||||||
MediaSessionUID.SingleItem(uid)
|
|
||||||
} else {
|
|
||||||
MediaSessionUID.ChildItem(parent.uid, uid)
|
|
||||||
}
|
|
||||||
val extras = makeExtras(context, *sugar)
|
val extras = makeExtras(context, *sugar)
|
||||||
return MediaDescriptionCompat.Builder()
|
return MediaDescriptionCompat.Builder()
|
||||||
.setMediaId(mediaSessionUID.toString())
|
.setMediaId(mediaSessionUID.toString())
|
||||||
|
@ -138,25 +120,12 @@ fun Song.toMediaDescription(
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Song.toMediaItem(
|
fun Song.toMediaItem(context: Context, vararg sugar: Sugar): MediaItem {
|
||||||
context: Context,
|
return MediaItem(toMediaDescription(context, *sugar), MediaItem.FLAG_PLAYABLE)
|
||||||
parent: MusicParent? = null,
|
|
||||||
vararg sugar: Sugar
|
|
||||||
): MediaItem {
|
|
||||||
return MediaItem(toMediaDescription(context, parent, *sugar), MediaItem.FLAG_PLAYABLE)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Album.toMediaItem(
|
fun Album.toMediaItem(context: Context, vararg sugar: Sugar): MediaItem {
|
||||||
context: Context,
|
val mediaSessionUID = MediaSessionUID.SingleItem(uid)
|
||||||
parent: MusicParent? = null,
|
|
||||||
vararg sugar: Sugar
|
|
||||||
): MediaItem {
|
|
||||||
val mediaSessionUID =
|
|
||||||
if (parent == null) {
|
|
||||||
MediaSessionUID.SingleItem(uid)
|
|
||||||
} else {
|
|
||||||
MediaSessionUID.ChildItem(parent.uid, uid)
|
|
||||||
}
|
|
||||||
val extras = makeExtras(context, *sugar)
|
val extras = makeExtras(context, *sugar)
|
||||||
val counts = context.getPlural(R.plurals.fmt_song_count, songs.size)
|
val counts = context.getPlural(R.plurals.fmt_song_count, songs.size)
|
||||||
val description =
|
val description =
|
||||||
|
|
|
@ -21,6 +21,7 @@ package org.oxycblt.auxio.music.service
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.support.v4.media.MediaBrowserCompat.MediaItem
|
import android.support.v4.media.MediaBrowserCompat.MediaItem
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
import org.oxycblt.auxio.BuildConfig
|
||||||
import org.oxycblt.auxio.R
|
import org.oxycblt.auxio.R
|
||||||
import org.oxycblt.auxio.detail.DetailGenerator
|
import org.oxycblt.auxio.detail.DetailGenerator
|
||||||
import org.oxycblt.auxio.detail.DetailSection
|
import org.oxycblt.auxio.detail.DetailSection
|
||||||
|
@ -117,8 +118,6 @@ private constructor(
|
||||||
is MediaSessionUID.Tab -> return uid.node.toMediaItem(context)
|
is MediaSessionUID.Tab -> return uid.node.toMediaItem(context)
|
||||||
is MediaSessionUID.SingleItem ->
|
is MediaSessionUID.SingleItem ->
|
||||||
musicRepository.find(uid.uid)?.let { musicRepository.find(it.uid) }
|
musicRepository.find(uid.uid)?.let { musicRepository.find(it.uid) }
|
||||||
is MediaSessionUID.ChildItem ->
|
|
||||||
musicRepository.find(uid.childUid)?.let { musicRepository.find(it.uid) }
|
|
||||||
null -> null
|
null -> null
|
||||||
}
|
}
|
||||||
?: return null
|
?: return null
|
||||||
|
@ -128,7 +127,7 @@ private constructor(
|
||||||
is Artist -> music.toMediaItem(context)
|
is Artist -> music.toMediaItem(context)
|
||||||
is Genre -> music.toMediaItem(context)
|
is Genre -> music.toMediaItem(context)
|
||||||
is Playlist -> 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> {
|
private fun SearchEngine.Items.toMediaItems(): MutableList<MediaItem> {
|
||||||
val music = mutableListOf<MediaItem>()
|
val music = mutableListOf<MediaItem>()
|
||||||
if (songs != null) {
|
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) {
|
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) {
|
if (artists != null) {
|
||||||
music.addAll(artists.map { it.toMediaItem(context, header(R.string.lbl_artists)) })
|
music.addAll(artists.map { it.toMediaItem(context, header(R.string.lbl_artists)) })
|
||||||
|
@ -185,9 +184,6 @@ private constructor(
|
||||||
is MediaSessionUID.SingleItem -> {
|
is MediaSessionUID.SingleItem -> {
|
||||||
getChildMediaItems(mediaSessionUID.uid)
|
getChildMediaItems(mediaSessionUID.uid)
|
||||||
}
|
}
|
||||||
is MediaSessionUID.ChildItem -> {
|
|
||||||
getChildMediaItems(mediaSessionUID.childUid)
|
|
||||||
}
|
|
||||||
null -> {
|
null -> {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
@ -208,11 +204,11 @@ private constructor(
|
||||||
}
|
}
|
||||||
is TabNode.More -> {
|
is TabNode.More -> {
|
||||||
val tabs = homeGenerator.tabs()
|
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 ->
|
is TabNode.Home ->
|
||||||
when (node.type) {
|
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.ALBUMS -> homeGenerator.albums().map { it.toMediaItem(context) }
|
||||||
MusicType.ARTISTS -> homeGenerator.artists().map { it.toMediaItem(context) }
|
MusicType.ARTISTS -> homeGenerator.artists().map { it.toMediaItem(context) }
|
||||||
MusicType.GENRES -> homeGenerator.genres().map { it.toMediaItem(context) }
|
MusicType.GENRES -> homeGenerator.genres().map { it.toMediaItem(context) }
|
||||||
|
@ -225,18 +221,24 @@ private constructor(
|
||||||
return detail.sections.flatMap { section ->
|
return detail.sections.flatMap { section ->
|
||||||
when (section) {
|
when (section) {
|
||||||
is DetailSection.Songs ->
|
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 ->
|
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 ->
|
is DetailSection.Artists ->
|
||||||
section.items.map { it.toMediaItem(context, header(section.stringRes)) }
|
section.items.map { it.toMediaItem(context, header(section.stringRes)) }
|
||||||
is DetailSection.Discs ->
|
is DetailSection.Discs ->
|
||||||
section.discs.flatMap { (disc, songs) ->
|
section.discs.flatMap { (disc, songs) ->
|
||||||
val discString = disc.resolveNumber(context)
|
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")
|
else -> error("Unknown section type: $section")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val KEY_CHILD_OF = BuildConfig.APPLICATION_ID + ".key.CHILD_OF"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -295,7 +295,7 @@ private constructor(
|
||||||
queue.mapIndexed { i, song ->
|
queue.mapIndexed { i, song ->
|
||||||
val description =
|
val description =
|
||||||
song.toMediaDescription(
|
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
|
// Store the item index so we can then use the analogous index in the
|
||||||
// playback state.
|
// playback state.
|
||||||
MediaSessionCompat.QueueItem(description, i.toLong())
|
MediaSessionCompat.QueueItem(description, i.toLong())
|
||||||
|
|
|
@ -41,11 +41,13 @@ import org.oxycblt.auxio.music.Song
|
||||||
import org.oxycblt.auxio.music.device.DeviceLibrary
|
import org.oxycblt.auxio.music.device.DeviceLibrary
|
||||||
import org.oxycblt.auxio.music.info.Name
|
import org.oxycblt.auxio.music.info.Name
|
||||||
import org.oxycblt.auxio.music.service.MediaSessionUID
|
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.music.user.UserLibrary
|
||||||
import org.oxycblt.auxio.playback.state.PlaybackCommand
|
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
|
||||||
|
|
||||||
class MediaSessionInterface
|
class MediaSessionInterface
|
||||||
@Inject
|
@Inject
|
||||||
|
@ -80,7 +82,10 @@ constructor(
|
||||||
override fun onPlayFromMediaId(mediaId: String?, extras: Bundle?) {
|
override fun onPlayFromMediaId(mediaId: String?, extras: Bundle?) {
|
||||||
super.onPlayFromMediaId(mediaId, extras)
|
super.onPlayFromMediaId(mediaId, extras)
|
||||||
val uid = MediaSessionUID.fromString(mediaId ?: return) ?: return
|
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" })
|
playbackManager.play(requireNotNull(command) { "Invalid playback configuration" })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -105,7 +110,6 @@ constructor(
|
||||||
val songUid =
|
val songUid =
|
||||||
when (uid) {
|
when (uid) {
|
||||||
is MediaSessionUID.SingleItem -> uid.uid
|
is MediaSessionUID.SingleItem -> uid.uid
|
||||||
is MediaSessionUID.ChildItem -> uid.childUid
|
|
||||||
else -> return
|
else -> return
|
||||||
}
|
}
|
||||||
val song = deviceLibrary.songs.find { it.uid == songUid } ?: return
|
val song = deviceLibrary.songs.find { it.uid == songUid } ?: return
|
||||||
|
@ -126,7 +130,6 @@ constructor(
|
||||||
val songUid =
|
val songUid =
|
||||||
when (uid) {
|
when (uid) {
|
||||||
is MediaSessionUID.SingleItem -> uid.uid
|
is MediaSessionUID.SingleItem -> uid.uid
|
||||||
is MediaSessionUID.ChildItem -> uid.childUid
|
|
||||||
else -> return
|
else -> return
|
||||||
}
|
}
|
||||||
val firstAt = playbackManager.queue.indexOfFirst { it.uid == songUid }
|
val firstAt = playbackManager.queue.indexOfFirst { it.uid == songUid }
|
||||||
|
@ -194,20 +197,14 @@ constructor(
|
||||||
context.sendBroadcast(Intent(action))
|
context.sendBroadcast(Intent(action))
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun expandUidIntoCommand(uid: MediaSessionUID): PlaybackCommand? {
|
private fun expandUidIntoCommand(
|
||||||
val music: Music
|
uid: MediaSessionUID,
|
||||||
var parent: MusicParent? = null
|
parentUid: MediaSessionUID?
|
||||||
when (uid) {
|
): PlaybackCommand? {
|
||||||
is MediaSessionUID.SingleItem -> {
|
val unwrappedUid = (uid as? MediaSessionUID.SingleItem)?.uid ?: return null
|
||||||
music = musicRepository.find(uid.uid) ?: return null
|
val unwrappedParentUid = (parentUid as? MediaSessionUID.SingleItem)?.uid
|
||||||
}
|
val music = musicRepository.find(unwrappedUid) ?: return null
|
||||||
is MediaSessionUID.ChildItem -> {
|
val parent = unwrappedParentUid?.let { musicRepository.find(it) as? MusicParent }
|
||||||
music = musicRepository.find(uid.childUid) ?: return null
|
|
||||||
parent = musicRepository.find(uid.parentUid) as? MusicParent ?: return null
|
|
||||||
}
|
|
||||||
else -> return null
|
|
||||||
}
|
|
||||||
|
|
||||||
return expandMusicIntoCommand(music, parent)
|
return expandMusicIntoCommand(music, parent)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue