music: make compat more menu
This way we can make sure that external providers never truncate our MediaItem count.
This commit is contained in:
parent
e23ac33b85
commit
f1e1152e21
5 changed files with 139 additions and 54 deletions
|
@ -30,6 +30,7 @@ import androidx.core.app.NotificationCompat
|
||||||
import androidx.core.app.NotificationManagerCompat
|
import androidx.core.app.NotificationManagerCompat
|
||||||
import androidx.core.app.ServiceCompat
|
import androidx.core.app.ServiceCompat
|
||||||
import androidx.media.MediaBrowserServiceCompat
|
import androidx.media.MediaBrowserServiceCompat
|
||||||
|
import androidx.media.utils.MediaConstants
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
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
|
||||||
|
@ -83,7 +84,12 @@ class AuxioService :
|
||||||
clientPackageName: String,
|
clientPackageName: String,
|
||||||
clientUid: Int,
|
clientUid: Int,
|
||||||
rootHints: Bundle?
|
rootHints: Bundle?
|
||||||
): BrowserRoot = musicFragment.getRoot()
|
): BrowserRoot {
|
||||||
|
val maximumRootChildLimit =
|
||||||
|
rootHints?.getInt(
|
||||||
|
MediaConstants.BROWSER_ROOT_HINTS_KEY_ROOT_CHILDREN_LIMIT, 4) ?: 4
|
||||||
|
return musicFragment.getRoot(maximumRootChildLimit)
|
||||||
|
}
|
||||||
|
|
||||||
override fun onLoadItem(itemId: String, result: Result<MediaItem>) {
|
override fun onLoadItem(itemId: String, result: Result<MediaItem>) {
|
||||||
musicFragment.getItem(itemId, result)
|
musicFragment.getItem(itemId, result)
|
||||||
|
|
|
@ -0,0 +1,94 @@
|
||||||
|
package org.oxycblt.auxio.music.service
|
||||||
|
|
||||||
|
import org.oxycblt.auxio.R
|
||||||
|
|
||||||
|
sealed interface Category {
|
||||||
|
val id: String
|
||||||
|
val nameRes: Int
|
||||||
|
val bitmapRes: Int?
|
||||||
|
|
||||||
|
data class Root(val amount: Int) : Category {
|
||||||
|
override val id = "root/$amount"
|
||||||
|
override val nameRes = R.string.info_app_name
|
||||||
|
override val bitmapRes = null
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val ID_PREFIX = "root"
|
||||||
|
|
||||||
|
fun fromString(str: String): Root? {
|
||||||
|
val split = str.split("/", limit = 2)
|
||||||
|
if (split.size != 2) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
val limit = split[1].toIntOrNull() ?: return null
|
||||||
|
return Root(limit)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data class More(val remainder: Int) : Category {
|
||||||
|
override val id = "more/$remainder"
|
||||||
|
override val nameRes = R.string.lbl_more
|
||||||
|
override val bitmapRes = null
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val ID_PREFIX = "more"
|
||||||
|
|
||||||
|
fun fromString(str: String): More? {
|
||||||
|
val split = str.split("/", limit = 2)
|
||||||
|
if (split.size != 2) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
val remainder = split[1].toIntOrNull() ?: return null
|
||||||
|
return More(remainder)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data object Songs : Category {
|
||||||
|
override val id = "songs"
|
||||||
|
override val nameRes = R.string.lbl_songs
|
||||||
|
override val bitmapRes = R.drawable.ic_song_bitmap_24
|
||||||
|
}
|
||||||
|
|
||||||
|
data object Albums : Category {
|
||||||
|
override val id = "albums"
|
||||||
|
override val nameRes = R.string.lbl_albums
|
||||||
|
override val bitmapRes = R.drawable.ic_album_bitmap_24
|
||||||
|
}
|
||||||
|
|
||||||
|
data object Artists : Category {
|
||||||
|
override val id = "artists"
|
||||||
|
override val nameRes = R.string.lbl_artists
|
||||||
|
override val bitmapRes = R.drawable.ic_artist_bitmap_24
|
||||||
|
}
|
||||||
|
|
||||||
|
data object Genres : Category {
|
||||||
|
override val id = "genres"
|
||||||
|
override val nameRes = R.string.lbl_genres
|
||||||
|
override val bitmapRes = R.drawable.ic_genre_bitmap_24
|
||||||
|
}
|
||||||
|
|
||||||
|
data object Playlists : Category {
|
||||||
|
override val id = "playlists"
|
||||||
|
override val nameRes = R.string.lbl_playlists
|
||||||
|
override val bitmapRes = R.drawable.ic_playlist_bitmap_24
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
val MUSIC = arrayOf(Songs, Albums, Artists, Genres, Playlists)
|
||||||
|
val DEVICE_MUSIC = arrayOf(Songs, Albums, Artists, Genres)
|
||||||
|
val USER_MUSIC = arrayOf(Playlists)
|
||||||
|
fun fromString(str: String): Category? =
|
||||||
|
when {
|
||||||
|
str.startsWith(Root.ID_PREFIX) -> Root.fromString(str)
|
||||||
|
str.startsWith(More.ID_PREFIX) -> More.fromString(str)
|
||||||
|
str == Songs.id -> Songs
|
||||||
|
str == Albums.id -> Albums
|
||||||
|
str == Artists.id -> Artists
|
||||||
|
str == Genres.id -> Genres
|
||||||
|
str == Playlists.id -> Playlists
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -23,7 +23,6 @@ import android.graphics.BitmapFactory
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.support.v4.media.MediaBrowserCompat.MediaItem
|
import android.support.v4.media.MediaBrowserCompat.MediaItem
|
||||||
import android.support.v4.media.MediaDescriptionCompat
|
import android.support.v4.media.MediaDescriptionCompat
|
||||||
import androidx.annotation.DrawableRes
|
|
||||||
import androidx.annotation.StringRes
|
import androidx.annotation.StringRes
|
||||||
import androidx.media.utils.MediaConstants
|
import androidx.media.utils.MediaConstants
|
||||||
import org.oxycblt.auxio.BuildConfig
|
import org.oxycblt.auxio.BuildConfig
|
||||||
|
@ -38,22 +37,6 @@ import org.oxycblt.auxio.music.Song
|
||||||
import org.oxycblt.auxio.music.resolveNames
|
import org.oxycblt.auxio.music.resolveNames
|
||||||
import org.oxycblt.auxio.util.getPlural
|
import org.oxycblt.auxio.util.getPlural
|
||||||
|
|
||||||
enum class Category(val id: String, @StringRes val nameRes: Int, @DrawableRes val bitmapRes: Int?) {
|
|
||||||
ROOT("root", R.string.info_app_name, null),
|
|
||||||
MORE("more", R.string.lbl_more, R.drawable.ic_more_24),
|
|
||||||
SONGS("songs", R.string.lbl_songs, R.drawable.ic_song_bitmap_24),
|
|
||||||
ALBUMS("albums", R.string.lbl_albums, R.drawable.ic_album_bitmap_24),
|
|
||||||
ARTISTS("artists", R.string.lbl_artists, R.drawable.ic_artist_bitmap_24),
|
|
||||||
GENRES("genres", R.string.lbl_genres, R.drawable.ic_genre_bitmap_24),
|
|
||||||
PLAYLISTS("playlists", R.string.lbl_playlists, R.drawable.ic_playlist_bitmap_24);
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
val DEVICE_MUSIC = listOf(ROOT, SONGS, ALBUMS, ARTISTS, GENRES)
|
|
||||||
val USER_MUSIC = listOf(ROOT, PLAYLISTS)
|
|
||||||
val IMPORTANT = listOf(SONGS, ALBUMS, ARTISTS, GENRES, PLAYLISTS)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sealed interface MediaSessionUID {
|
sealed interface MediaSessionUID {
|
||||||
data class CategoryItem(val category: Category) : MediaSessionUID {
|
data class CategoryItem(val category: Category) : MediaSessionUID {
|
||||||
override fun toString() = "$ID_CATEGORY:$category"
|
override fun toString() = "$ID_CATEGORY:$category"
|
||||||
|
@ -78,17 +61,7 @@ sealed interface MediaSessionUID {
|
||||||
}
|
}
|
||||||
return when (parts[0]) {
|
return when (parts[0]) {
|
||||||
ID_CATEGORY ->
|
ID_CATEGORY ->
|
||||||
CategoryItem(
|
CategoryItem(Category.fromString(parts[1]) ?: return null)
|
||||||
when (parts[1]) {
|
|
||||||
Category.ROOT.id -> Category.ROOT
|
|
||||||
Category.MORE.id -> Category.MORE
|
|
||||||
Category.SONGS.id -> Category.SONGS
|
|
||||||
Category.ALBUMS.id -> Category.ALBUMS
|
|
||||||
Category.ARTISTS.id -> Category.ARTISTS
|
|
||||||
Category.GENRES.id -> Category.GENRES
|
|
||||||
Category.PLAYLISTS.id -> Category.PLAYLISTS
|
|
||||||
else -> return null
|
|
||||||
})
|
|
||||||
ID_ITEM -> {
|
ID_ITEM -> {
|
||||||
val uids = parts[1].split(">", limit = 2)
|
val uids = parts[1].split(">", limit = 2)
|
||||||
if (uids.size == 1) {
|
if (uids.size == 1) {
|
||||||
|
@ -113,7 +86,6 @@ fun header(@StringRes nameRes: Int): Sugar = {
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Category.toMediaItem(context: Context): MediaItem {
|
fun Category.toMediaItem(context: Context): MediaItem {
|
||||||
// TODO: Make custom overflow menu for compat
|
|
||||||
val extras =
|
val extras =
|
||||||
Bundle().apply {
|
Bundle().apply {
|
||||||
putInt(
|
putInt(
|
||||||
|
@ -126,8 +98,8 @@ fun Category.toMediaItem(context: Context): MediaItem {
|
||||||
.setMediaId(mediaSessionUID.toString())
|
.setMediaId(mediaSessionUID.toString())
|
||||||
.setTitle(context.getString(nameRes))
|
.setTitle(context.getString(nameRes))
|
||||||
.setExtras(extras)
|
.setExtras(extras)
|
||||||
if (bitmapRes != null) {
|
bitmapRes?.let { res ->
|
||||||
val bitmap = BitmapFactory.decodeResource(context.resources, bitmapRes)
|
val bitmap = BitmapFactory.decodeResource(context.resources, res)
|
||||||
description.setIconBitmap(bitmap)
|
description.setIconBitmap(bitmap)
|
||||||
}
|
}
|
||||||
return MediaItem(description.build(), MediaItem.FLAG_BROWSABLE)
|
return MediaItem(description.build(), MediaItem.FLAG_BROWSABLE)
|
||||||
|
|
|
@ -172,27 +172,7 @@ constructor(
|
||||||
): List<MediaItem>? {
|
): List<MediaItem>? {
|
||||||
return when (val mediaSessionUID = MediaSessionUID.fromString(id)) {
|
return when (val mediaSessionUID = MediaSessionUID.fromString(id)) {
|
||||||
is MediaSessionUID.CategoryItem -> {
|
is MediaSessionUID.CategoryItem -> {
|
||||||
when (mediaSessionUID.category) {
|
getCategoryMediaItems(mediaSessionUID.category, deviceLibrary, userLibrary)
|
||||||
Category.ROOT -> Category.IMPORTANT.map { it.toMediaItem(context) }
|
|
||||||
Category.MORE -> TODO()
|
|
||||||
Category.SONGS ->
|
|
||||||
listSettings.songSort.songs(deviceLibrary.songs).map {
|
|
||||||
it.toMediaItem(context, null)
|
|
||||||
}
|
|
||||||
Category.ALBUMS ->
|
|
||||||
listSettings.albumSort.albums(deviceLibrary.albums).map {
|
|
||||||
it.toMediaItem(context)
|
|
||||||
}
|
|
||||||
Category.ARTISTS ->
|
|
||||||
listSettings.artistSort.artists(deviceLibrary.artists).map {
|
|
||||||
it.toMediaItem(context)
|
|
||||||
}
|
|
||||||
Category.GENRES ->
|
|
||||||
listSettings.genreSort.genres(deviceLibrary.genres).map {
|
|
||||||
it.toMediaItem(context)
|
|
||||||
}
|
|
||||||
Category.PLAYLISTS -> userLibrary.playlists.map { it.toMediaItem(context) }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
is MediaSessionUID.SingleItem -> {
|
is MediaSessionUID.SingleItem -> {
|
||||||
getChildMediaItems(mediaSessionUID.uid)
|
getChildMediaItems(mediaSessionUID.uid)
|
||||||
|
@ -206,6 +186,38 @@ constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun getCategoryMediaItems(category: Category, deviceLibrary: DeviceLibrary, userLibrary: UserLibrary) =
|
||||||
|
when (category) {
|
||||||
|
is Category.Root -> {
|
||||||
|
val base = Category.MUSIC.take(category.amount)
|
||||||
|
if (base.size < Category.MUSIC.size) {
|
||||||
|
base + Category.More(Category.MUSIC.size - base.size)
|
||||||
|
} else {
|
||||||
|
base
|
||||||
|
}.map { it.toMediaItem(context) }
|
||||||
|
}
|
||||||
|
is Category.More -> Category.MUSIC.takeLast(category.remainder).map {
|
||||||
|
it.toMediaItem(context)
|
||||||
|
}
|
||||||
|
is Category.Songs ->
|
||||||
|
listSettings.songSort.songs(deviceLibrary.songs).map {
|
||||||
|
it.toMediaItem(context, null)
|
||||||
|
}
|
||||||
|
is Category.Albums ->
|
||||||
|
listSettings.albumSort.albums(deviceLibrary.albums).map {
|
||||||
|
it.toMediaItem(context)
|
||||||
|
}
|
||||||
|
is Category.Artists ->
|
||||||
|
listSettings.artistSort.artists(deviceLibrary.artists).map {
|
||||||
|
it.toMediaItem(context)
|
||||||
|
}
|
||||||
|
is Category.Genres ->
|
||||||
|
listSettings.genreSort.genres(deviceLibrary.genres).map {
|
||||||
|
it.toMediaItem(context)
|
||||||
|
}
|
||||||
|
is Category.Playlists -> userLibrary.playlists.map { it.toMediaItem(context) }
|
||||||
|
}
|
||||||
|
|
||||||
private fun getChildMediaItems(uid: Music.UID): List<MediaItem>? {
|
private fun getChildMediaItems(uid: Music.UID): List<MediaItem>? {
|
||||||
return when (val item = musicRepository.find(uid)) {
|
return when (val item = musicRepository.find(uid)) {
|
||||||
is Album -> {
|
is Album -> {
|
||||||
|
|
|
@ -76,7 +76,8 @@ constructor(
|
||||||
indexer.createNotification(post)
|
indexer.createNotification(post)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getRoot() = BrowserRoot(Category.ROOT.id, null)
|
fun getRoot(maxItems: Int) =
|
||||||
|
BrowserRoot(MediaSessionUID.CategoryItem(Category.Root(maxItems)).toString(), null)
|
||||||
|
|
||||||
fun getItem(mediaId: String, result: Result<MediaItem>) =
|
fun getItem(mediaId: String, result: Result<MediaItem>) =
|
||||||
result.dispatch { musicBrowser.getItem(mediaId) }
|
result.dispatch { musicBrowser.getItem(mediaId) }
|
||||||
|
|
Loading…
Reference in a new issue