home: mirror tabs to mediasession browser

This commit is contained in:
Alexander Capehart 2024-09-13 13:35:37 -06:00
parent 29d663f500
commit 3832c4e525
No known key found for this signature in database
GPG key ID: 37DBE3621FE9AD47
7 changed files with 175 additions and 124 deletions

View file

@ -1,6 +1,6 @@
package org.oxycblt.auxio.home.list package org.oxycblt.auxio.home
import org.oxycblt.auxio.home.HomeSettings import org.oxycblt.auxio.home.tabs.Tab
import org.oxycblt.auxio.list.ListSettings import org.oxycblt.auxio.list.ListSettings
import org.oxycblt.auxio.list.adapter.UpdateInstructions import org.oxycblt.auxio.list.adapter.UpdateInstructions
import org.oxycblt.auxio.music.Album import org.oxycblt.auxio.music.Album
@ -10,39 +10,46 @@ import org.oxycblt.auxio.music.MusicRepository
import org.oxycblt.auxio.music.MusicType import org.oxycblt.auxio.music.MusicType
import org.oxycblt.auxio.music.Playlist import org.oxycblt.auxio.music.Playlist
import org.oxycblt.auxio.music.Song import org.oxycblt.auxio.music.Song
import org.oxycblt.auxio.playback.PlaybackSettings
import org.oxycblt.auxio.util.logD import org.oxycblt.auxio.util.logD
import javax.inject.Inject
interface HomeListGenerator { interface HomeGenerator {
fun songs(): List<Song> fun songs(): List<Song>
fun albums(): List<Album> fun albums(): List<Album>
fun artists(): List<Artist> fun artists(): List<Artist>
fun genres(): List<Genre> fun genres(): List<Genre>
fun playlists(): List<Playlist> fun playlists(): List<Playlist>
fun tabs(): List<MusicType>
fun release() fun release()
interface Invalidator { interface Invalidator {
fun invalidate(type: MusicType, instructions: UpdateInstructions) fun invalidateMusic(type: MusicType, instructions: UpdateInstructions)
fun invalidateTabs()
} }
interface Factory { interface Factory {
fun create(invalidator: Invalidator): HomeListGenerator fun create(invalidator: Invalidator): HomeGenerator
} }
} }
private class HomeListGeneratorImpl( private class HomeGeneratorImpl(
private val invalidator: HomeListGenerator.Invalidator, private val invalidator: HomeGenerator.Invalidator,
private val homeSettings: HomeSettings, private val homeSettings: HomeSettings,
private val listSettings: ListSettings, private val listSettings: ListSettings,
private val musicRepository: MusicRepository, private val musicRepository: MusicRepository,
) : HomeListGenerator, HomeSettings.Listener, ListSettings.Listener, MusicRepository.UpdateListener { ) : HomeGenerator, HomeSettings.Listener, ListSettings.Listener, MusicRepository.UpdateListener {
override fun songs() = override fun songs() =
musicRepository.deviceLibrary?.let { listSettings.songSort.songs(it.songs) } ?: emptyList() musicRepository.deviceLibrary?.let { listSettings.songSort.songs(it.songs) } ?: emptyList()
override fun albums() = musicRepository.deviceLibrary?.let { listSettings.albumSort.albums(it.albums) } ?: emptyList() override fun albums() = musicRepository.deviceLibrary?.let { listSettings.albumSort.albums(it.albums) } ?: emptyList()
override fun artists() = musicRepository.deviceLibrary?.let { listSettings.artistSort.artists(it.artists) } ?: emptyList() override fun artists() = musicRepository.deviceLibrary?.let { listSettings.artistSort.artists(it.artists) } ?: emptyList()
override fun genres() = musicRepository.deviceLibrary?.let { listSettings.genreSort.genres(it.genres) } ?: emptyList() override fun genres() = musicRepository.deviceLibrary?.let { listSettings.genreSort.genres(it.genres) } ?: emptyList()
override fun playlists() = musicRepository.userLibrary?.let { listSettings.playlistSort.playlists(it.playlists) } ?: emptyList() override fun playlists() = musicRepository.userLibrary?.let { listSettings.playlistSort.playlists(it.playlists) } ?: emptyList()
override fun tabs() =
homeSettings.homeTabs.filterIsInstance<Tab.Visible>().map { it.type }
override fun onTabsChanged() {
invalidator.invalidateTabs()
}
init { init {
homeSettings.registerListener(this) homeSettings.registerListener(this)
@ -51,9 +58,9 @@ private class HomeListGeneratorImpl(
} }
override fun release() { override fun release() {
homeSettings.unregisterListener(this)
listSettings.unregisterListener(this)
musicRepository.removeUpdateListener(this) musicRepository.removeUpdateListener(this)
listSettings.unregisterListener(this)
homeSettings.unregisterListener(this)
} }
override fun onHideCollaboratorsChanged() { override fun onHideCollaboratorsChanged() {
@ -65,27 +72,27 @@ private class HomeListGeneratorImpl(
override fun onSongSortChanged() { override fun onSongSortChanged() {
super.onSongSortChanged() super.onSongSortChanged()
invalidator.invalidate(MusicType.SONGS, UpdateInstructions.Replace(0)) invalidator.invalidateMusic(MusicType.SONGS, UpdateInstructions.Replace(0))
} }
override fun onAlbumSortChanged() { override fun onAlbumSortChanged() {
super.onAlbumSortChanged() super.onAlbumSortChanged()
invalidator.invalidate(MusicType.ALBUMS, UpdateInstructions.Replace(0)) invalidator.invalidateMusic(MusicType.ALBUMS, UpdateInstructions.Replace(0))
} }
override fun onArtistSortChanged() { override fun onArtistSortChanged() {
super.onArtistSortChanged() super.onArtistSortChanged()
invalidator.invalidate(MusicType.ARTISTS, UpdateInstructions.Replace(0)) invalidator.invalidateMusic(MusicType.ARTISTS, UpdateInstructions.Replace(0))
} }
override fun onGenreSortChanged() { override fun onGenreSortChanged() {
super.onGenreSortChanged() super.onGenreSortChanged()
invalidator.invalidate(MusicType.GENRES, UpdateInstructions.Replace(0)) invalidator.invalidateMusic(MusicType.GENRES, UpdateInstructions.Replace(0))
} }
override fun onPlaylistSortChanged() { override fun onPlaylistSortChanged() {
super.onPlaylistSortChanged() super.onPlaylistSortChanged()
invalidator.invalidate(MusicType.PLAYLISTS, UpdateInstructions.Replace(0)) invalidator.invalidateMusic(MusicType.PLAYLISTS, UpdateInstructions.Replace(0))
} }
override fun onMusicChanges(changes: MusicRepository.Changes) { override fun onMusicChanges(changes: MusicRepository.Changes) {
@ -94,16 +101,16 @@ private class HomeListGeneratorImpl(
logD("Refreshing library") logD("Refreshing library")
// Get the each list of items in the library to use as our list data. // Get the each list of items in the library to use as our list data.
// Applying the preferred sorting to them. // Applying the preferred sorting to them.
invalidator.invalidate(MusicType.SONGS, UpdateInstructions.Diff) invalidator.invalidateMusic(MusicType.SONGS, UpdateInstructions.Diff)
invalidator.invalidate(MusicType.ALBUMS, UpdateInstructions.Diff) invalidator.invalidateMusic(MusicType.ALBUMS, UpdateInstructions.Diff)
invalidator.invalidate(MusicType.ARTISTS, UpdateInstructions.Diff) invalidator.invalidateMusic(MusicType.ARTISTS, UpdateInstructions.Diff)
invalidator.invalidate(MusicType.GENRES, UpdateInstructions.Diff) invalidator.invalidateMusic(MusicType.GENRES, UpdateInstructions.Diff)
} }
val userLibrary = musicRepository.userLibrary val userLibrary = musicRepository.userLibrary
if (changes.userLibrary && userLibrary != null) { if (changes.userLibrary && userLibrary != null) {
logD("Refreshing playlists") logD("Refreshing playlists")
invalidator.invalidate(MusicType.PLAYLISTS, UpdateInstructions.Diff) invalidator.invalidateMusic(MusicType.PLAYLISTS, UpdateInstructions.Diff)
} }
} }

View file

@ -23,15 +23,14 @@ import dagger.hilt.android.lifecycle.HiltViewModel
import javax.inject.Inject import javax.inject.Inject
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
import org.oxycblt.auxio.home.list.HomeListGenerator
import org.oxycblt.auxio.home.tabs.Tab import org.oxycblt.auxio.home.tabs.Tab
import org.oxycblt.auxio.home.tabs.TabListGenerator
import org.oxycblt.auxio.list.ListSettings import org.oxycblt.auxio.list.ListSettings
import org.oxycblt.auxio.list.adapter.UpdateInstructions import org.oxycblt.auxio.list.adapter.UpdateInstructions
import org.oxycblt.auxio.list.sort.Sort import org.oxycblt.auxio.list.sort.Sort
import org.oxycblt.auxio.music.Album import org.oxycblt.auxio.music.Album
import org.oxycblt.auxio.music.Artist import org.oxycblt.auxio.music.Artist
import org.oxycblt.auxio.music.Genre import org.oxycblt.auxio.music.Genre
import org.oxycblt.auxio.music.MusicRepository
import org.oxycblt.auxio.music.MusicType import org.oxycblt.auxio.music.MusicType
import org.oxycblt.auxio.music.Playlist import org.oxycblt.auxio.music.Playlist
import org.oxycblt.auxio.music.Song import org.oxycblt.auxio.music.Song
@ -50,12 +49,11 @@ import org.oxycblt.auxio.util.logD
class HomeViewModel class HomeViewModel
@Inject @Inject
constructor( constructor(
private val homeSettings: HomeSettings,
private val listSettings: ListSettings, private val listSettings: ListSettings,
private val playbackSettings: PlaybackSettings, private val playbackSettings: PlaybackSettings,
homeGeneratorFactory: HomeListGenerator.Factory homeGeneratorFactory: HomeGenerator.Factory
) : ViewModel(), HomeSettings.Listener, HomeListGenerator.Invalidator { ) : ViewModel(), HomeGenerator.Invalidator {
private val generator = homeGeneratorFactory.create(this) private val homeGenerator = homeGeneratorFactory.create(this)
private val _songList = MutableStateFlow(listOf<Song>()) private val _songList = MutableStateFlow(listOf<Song>())
/** A list of [Song]s, sorted by the preferred [Sort], to be shown in the home view. */ /** A list of [Song]s, sorted by the preferred [Sort], to be shown in the home view. */
@ -138,7 +136,7 @@ constructor(
* A list of [MusicType] corresponding to the current [Tab] configuration, excluding invisible * A list of [MusicType] corresponding to the current [Tab] configuration, excluding invisible
* [Tab]s. * [Tab]s.
*/ */
var currentTabTypes = makeTabTypes() var currentTabTypes = homeGenerator.tabs()
private set private set
private val _currentTabType = MutableStateFlow(currentTabTypes[0]) private val _currentTabType = MutableStateFlow(currentTabTypes[0])
@ -166,45 +164,38 @@ constructor(
val showOuter: Event<Outer> val showOuter: Event<Outer>
get() = _showOuter get() = _showOuter
init {
homeSettings.registerListener(this)
}
override fun onCleared() { override fun onCleared() {
super.onCleared() super.onCleared()
homeSettings.unregisterListener(this) homeGenerator.release()
generator.release()
} }
override fun invalidate(type: MusicType, instructions: UpdateInstructions) { override fun invalidateMusic(type: MusicType, instructions: UpdateInstructions) {
when (type) { when (type) {
MusicType.SONGS -> { MusicType.SONGS -> {
_songList.value = generator.songs() _songList.value = homeGenerator.songs()
_songInstructions.put(instructions) _songInstructions.put(instructions)
} }
MusicType.ALBUMS -> { MusicType.ALBUMS -> {
_albumList.value = generator.albums() _albumList.value = homeGenerator.albums()
_albumInstructions.put(instructions) _albumInstructions.put(instructions)
} }
MusicType.ARTISTS -> { MusicType.ARTISTS -> {
_artistList.value = generator.artists() _artistList.value = homeGenerator.artists()
_artistInstructions.put(instructions) _artistInstructions.put(instructions)
} }
MusicType.GENRES -> { MusicType.GENRES -> {
_genreList.value = generator.genres() _genreList.value = homeGenerator.genres()
_genreInstructions.put(instructions) _genreInstructions.put(instructions)
} }
MusicType.PLAYLISTS -> { MusicType.PLAYLISTS -> {
_playlistList.value = generator.playlists() _playlistList.value = homeGenerator.playlists()
_playlistInstructions.put(instructions) _playlistInstructions.put(instructions)
} }
} }
} }
override fun onTabsChanged() { override fun invalidateTabs() {
// Tabs changed, update the current tabs and set up a re-create event. currentTabTypes = homeGenerator.tabs()
currentTabTypes = makeTabTypes()
logD("Updating tabs: ${currentTabType.value}")
_shouldRecreate.put(Unit) _shouldRecreate.put(Unit)
} }
@ -290,15 +281,6 @@ constructor(
fun showAbout() { fun showAbout() {
_showOuter.put(Outer.About) _showOuter.put(Outer.About)
} }
/**
* Create a list of [MusicType]s representing a simpler version of the [Tab] configuration.
*
* @return A list of the [MusicType]s for each visible [Tab] in the configuration, ordered in
* the same way as the configuration.
*/
private fun makeTabTypes() =
homeSettings.homeTabs.filterIsInstance<Tab.Visible>().map { it.type }
} }
sealed interface Outer { sealed interface Outer {

View file

@ -37,40 +37,23 @@ class AdaptiveTabStrategy(context: Context, private val tabs: List<MusicType>) :
private val width = context.resources.configuration.smallestScreenWidthDp private val width = context.resources.configuration.smallestScreenWidthDp
override fun onConfigureTab(tab: TabLayout.Tab, position: Int) { override fun onConfigureTab(tab: TabLayout.Tab, position: Int) {
val icon: Int val homeTab = tabs[position]
val string: Int val icon = when (homeTab) {
MusicType.SONGS -> R.drawable.ic_song_24
when (tabs[position]) { MusicType.ALBUMS -> R.drawable.ic_album_24
MusicType.SONGS -> { MusicType.ARTISTS -> R.drawable.ic_artist_24
icon = R.drawable.ic_song_24 MusicType.GENRES -> R.drawable.ic_genre_24
string = R.string.lbl_songs MusicType.PLAYLISTS -> R.drawable.ic_playlist_24
}
MusicType.ALBUMS -> {
icon = R.drawable.ic_album_24
string = R.string.lbl_albums
}
MusicType.ARTISTS -> {
icon = R.drawable.ic_artist_24
string = R.string.lbl_artists
}
MusicType.GENRES -> {
icon = R.drawable.ic_genre_24
string = R.string.lbl_genres
}
MusicType.PLAYLISTS -> {
icon = R.drawable.ic_playlist_24
string = R.string.lbl_playlists
}
} }
// Use expected sw* size thresholds when choosing a configuration. // Use expected sw* size thresholds when choosing a configuration.
when { when {
// On small screens, only display an icon. // On small screens, only display an icon.
width < 370 -> tab.setIcon(icon).setContentDescription(string) width < 370 -> tab.setIcon(icon).setContentDescription(homeTab.nameRes)
// On large screens, display an icon and text. // On large screens, display an icon and text.
width < 600 -> tab.setText(string) width < 600 -> tab.setText(homeTab.nameRes).setIcon(icon)
// On medium-size screens, display text. // On medium-size screens, display text.
else -> tab.setIcon(icon).setText(string) else -> tab.setIcon(icon).setText(homeTab.nameRes)
} }
} }
} }

View file

@ -19,6 +19,7 @@
package org.oxycblt.auxio.music package org.oxycblt.auxio.music
import org.oxycblt.auxio.IntegerTable import org.oxycblt.auxio.IntegerTable
import org.oxycblt.auxio.R
/** /**
* General configuration enum to control what kind of music is being worked with. * General configuration enum to control what kind of music is being worked with.
@ -52,6 +53,16 @@ enum class MusicType {
PLAYLISTS -> IntegerTable.MUSIC_MODE_PLAYLISTS PLAYLISTS -> IntegerTable.MUSIC_MODE_PLAYLISTS
} }
val nameRes: Int
get() =
when (this) {
SONGS -> R.string.lbl_songs
ALBUMS -> R.string.lbl_albums
ARTISTS -> R.string.lbl_artists
GENRES -> R.string.lbl_genres
PLAYLISTS -> R.string.lbl_playlists
}
companion object { companion object {
/** /**
* Convert a [MusicType] integer representation into an instance. * Convert a [MusicType] integer representation into an instance.

View file

@ -44,8 +44,8 @@ import org.oxycblt.auxio.playback.formatDurationDs
import org.oxycblt.auxio.util.getPlural import org.oxycblt.auxio.util.getPlural
sealed interface MediaSessionUID { sealed interface MediaSessionUID {
data class CategoryItem(val category: Category) : MediaSessionUID { data class Tab(val node: TabNode) : MediaSessionUID {
override fun toString() = "$ID_CATEGORY:${category.id}" override fun toString() = "$ID_CATEGORY:${node.id}"
} }
data class SingleItem(val uid: Music.UID) : MediaSessionUID { data class SingleItem(val uid: Music.UID) : MediaSessionUID {
@ -66,7 +66,7 @@ sealed interface MediaSessionUID {
return null return null
} }
return when (parts[0]) { return when (parts[0]) {
ID_CATEGORY -> CategoryItem(Category.fromString(parts[1]) ?: return null) ID_CATEGORY -> Tab(TabNode.fromString(parts[1]) ?: 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) {
@ -148,12 +148,12 @@ private fun makeExtras(context: Context, vararg sugars: Sugar): Bundle {
return Bundle().apply { sugars.forEach { this.it(context) } } return Bundle().apply { sugars.forEach { this.it(context) } }
} }
fun Category.toMediaItem(context: Context): MediaItem { fun TabNode.toMediaItem(context: Context): MediaItem {
val extras = val extras =
makeExtras( makeExtras(
context, context,
style(MediaConstants.DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_CATEGORY_LIST_ITEM)) style(MediaConstants.DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_CATEGORY_LIST_ITEM))
val mediaSessionUID = MediaSessionUID.CategoryItem(this) val mediaSessionUID = MediaSessionUID.Tab(this)
val description = val description =
MediaDescriptionCompat.Builder() MediaDescriptionCompat.Builder()
.setMediaId(mediaSessionUID.toString()) .setMediaId(mediaSessionUID.toString())

View file

@ -23,7 +23,7 @@ import android.support.v4.media.MediaBrowserCompat.MediaItem
import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.android.qualifiers.ApplicationContext
import javax.inject.Inject import javax.inject.Inject
import org.oxycblt.auxio.R import org.oxycblt.auxio.R
import org.oxycblt.auxio.home.list.HomeListGenerator import org.oxycblt.auxio.home.HomeGenerator
import org.oxycblt.auxio.list.ListSettings import org.oxycblt.auxio.list.ListSettings
import org.oxycblt.auxio.list.adapter.UpdateInstructions import org.oxycblt.auxio.list.adapter.UpdateInstructions
import org.oxycblt.auxio.list.sort.Sort import org.oxycblt.auxio.list.sort.Sort
@ -35,8 +35,6 @@ import org.oxycblt.auxio.music.MusicRepository
import org.oxycblt.auxio.music.MusicType import org.oxycblt.auxio.music.MusicType
import org.oxycblt.auxio.music.Playlist import org.oxycblt.auxio.music.Playlist
import org.oxycblt.auxio.music.Song import org.oxycblt.auxio.music.Song
import org.oxycblt.auxio.music.device.DeviceLibrary
import org.oxycblt.auxio.music.user.UserLibrary
import org.oxycblt.auxio.search.SearchEngine import org.oxycblt.auxio.search.SearchEngine
class MusicBrowser class MusicBrowser
@ -46,13 +44,13 @@ constructor(
private val musicRepository: MusicRepository, private val musicRepository: MusicRepository,
private val searchEngine: SearchEngine, private val searchEngine: SearchEngine,
private val listSettings: ListSettings, private val listSettings: ListSettings,
homeGeneratorFactory: HomeListGenerator.Factory homeGeneratorFactory: HomeGenerator.Factory
) : MusicRepository.UpdateListener, HomeListGenerator.Invalidator { ) : MusicRepository.UpdateListener, HomeGenerator.Invalidator {
interface Invalidator { interface Invalidator {
fun invalidateMusic(ids: Set<String>) fun invalidateMusic(ids: Set<String>)
} }
private val generator = homeGeneratorFactory.create(this) private val homeGenerator = homeGeneratorFactory.create(this)
private var invalidator: Invalidator? = null private var invalidator: Invalidator? = null
fun attach(invalidator: Invalidator) { fun attach(invalidator: Invalidator) {
@ -64,26 +62,24 @@ constructor(
musicRepository.removeUpdateListener(this) musicRepository.removeUpdateListener(this)
} }
override fun invalidate(type: MusicType, instructions: UpdateInstructions) { override fun invalidateMusic(type: MusicType, instructions: UpdateInstructions) {
val category = when (type) { val id = MediaSessionUID.Tab(TabNode.Home(type)).toString()
MusicType.SONGS -> Category.Songs
MusicType.ALBUMS -> Category.Albums
MusicType.ARTISTS -> Category.Artists
MusicType.GENRES -> Category.Genres
MusicType.PLAYLISTS -> Category.Playlists
}
val id = MediaSessionUID.CategoryItem(category).toString()
invalidator?.invalidateMusic(setOf(id)) invalidator?.invalidateMusic(setOf(id))
} }
override fun invalidateTabs() {
for (i in 0..10) {
// TODO: Temporary bodge, move the amount parameter to a bundle extra
val rootId = MediaSessionUID.Tab(TabNode.Root(i)).toString()
val moreId = MediaSessionUID.Tab(TabNode.More(i)).toString()
invalidator?.invalidateMusic(setOf(rootId, moreId))
}
}
override fun onMusicChanges(changes: MusicRepository.Changes) { override fun onMusicChanges(changes: MusicRepository.Changes) {
val deviceLibrary = musicRepository.deviceLibrary val deviceLibrary = musicRepository.deviceLibrary
val invalidate = mutableSetOf<String>() val invalidate = mutableSetOf<String>()
if (changes.deviceLibrary && deviceLibrary != null) { if (changes.deviceLibrary && deviceLibrary != null) {
Category.DEVICE_MUSIC.forEach {
invalidate.add(MediaSessionUID.CategoryItem(it).toString())
}
deviceLibrary.albums.forEach { deviceLibrary.albums.forEach {
val id = MediaSessionUID.SingleItem(it.uid).toString() val id = MediaSessionUID.SingleItem(it.uid).toString()
invalidate.add(id) invalidate.add(id)
@ -101,9 +97,6 @@ constructor(
} }
val userLibrary = musicRepository.userLibrary val userLibrary = musicRepository.userLibrary
if (changes.userLibrary && userLibrary != null) { if (changes.userLibrary && userLibrary != null) {
Category.USER_MUSIC.forEach {
invalidate.add(MediaSessionUID.CategoryItem(it).toString())
}
userLibrary.playlists.forEach { userLibrary.playlists.forEach {
val id = MediaSessionUID.SingleItem(it.uid).toString() val id = MediaSessionUID.SingleItem(it.uid).toString()
invalidate.add(id) invalidate.add(id)
@ -118,7 +111,7 @@ constructor(
fun getItem(mediaId: String): MediaItem? { fun getItem(mediaId: String): MediaItem? {
val music = val music =
when (val uid = MediaSessionUID.fromString(mediaId)) { when (val uid = MediaSessionUID.fromString(mediaId)) {
is MediaSessionUID.CategoryItem -> return uid.category.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 -> is MediaSessionUID.ChildItem ->
@ -186,8 +179,8 @@ constructor(
id: String id: String
): List<MediaItem>? { ): List<MediaItem>? {
return when (val mediaSessionUID = MediaSessionUID.fromString(id)) { return when (val mediaSessionUID = MediaSessionUID.fromString(id)) {
is MediaSessionUID.CategoryItem -> { is MediaSessionUID.Tab -> {
getCategoryMediaItems(mediaSessionUID.category) getCategoryMediaItems(mediaSessionUID.node)
} }
is MediaSessionUID.SingleItem -> { is MediaSessionUID.SingleItem -> {
getChildMediaItems(mediaSessionUID.uid) getChildMediaItems(mediaSessionUID.uid)
@ -202,25 +195,30 @@ constructor(
} }
private fun getCategoryMediaItems( private fun getCategoryMediaItems(
category: Category node: TabNode
) = ) =
when (category) { when (node) {
is Category.Root -> { is TabNode.Root -> {
val base = Category.MUSIC.take(category.amount - 1) val tabs = homeGenerator.tabs()
if (base.size < Category.MUSIC.size) { val base = tabs.take(node.amount - 1).map { TabNode.Home(it) }
base + Category.More(Category.MUSIC.size - base.size) if (base.size < tabs.size) {
base + TabNode.More(Category.MUSIC.size - base.size)
} else { } else {
base base
} }
.map { it.toMediaItem(context) } .map { it.toMediaItem(context) }
} }
is Category.More -> is TabNode.More ->
Category.MUSIC.takeLast(category.remainder).map { it.toMediaItem(context) } homeGenerator.tabs().takeLast(node.remainder).map {
is Category.Songs -> generator.songs().map { it.toMediaItem(context) } TabNode.Home(it).toMediaItem(context) }
is Category.Albums -> generator.albums().map { it.toMediaItem(context) } is TabNode.Home ->
is Category.Artists -> generator.artists().map { it.toMediaItem(context) } when (node.type) {
is Category.Genres -> generator.genres().map { it.toMediaItem(context) } MusicType.SONGS -> homeGenerator.songs().map { it.toMediaItem(context, null) }
is Category.Playlists -> generator.playlists().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) }
MusicType.PLAYLISTS -> homeGenerator.playlists().map { it.toMediaItem(context) }
}
} }
private fun getChildMediaItems(uid: Music.UID): List<MediaItem>? { private fun getChildMediaItems(uid: Music.UID): List<MediaItem>? {

View file

@ -0,0 +1,70 @@
package org.oxycblt.auxio.music.service
import org.oxycblt.auxio.R
import org.oxycblt.auxio.home.tabs.Tab
import org.oxycblt.auxio.music.MusicType
sealed class TabNode {
abstract val id: String
abstract val data: Int
abstract val nameRes: Int
abstract val bitmapRes: Int?
override fun toString() = "${id}/${data}"
data class Root(val amount: Int) : TabNode() {
override val id = ID
override val data = amount
override val nameRes = R.string.info_app_name
override val bitmapRes = null
companion object {
const val ID = "root"
}
}
data class More(val remainder: Int) : TabNode() {
override val id = ID
override val data = remainder
override val nameRes = R.string.lbl_more
override val bitmapRes = null
companion object {
const val ID = "more"
}
}
data class Home(val type: MusicType) : TabNode() {
override val id = ID
override val data = type.intCode
override val bitmapRes: Int
get() = when (type) {
MusicType.SONGS -> R.drawable.ic_song_bitmap_24
MusicType.ALBUMS -> R.drawable.ic_album_bitmap_24
MusicType.ARTISTS -> R.drawable.ic_artist_bitmap_24
MusicType.GENRES -> R.drawable.ic_genre_bitmap_24
MusicType.PLAYLISTS -> R.drawable.ic_playlist_bitmap_24
}
override val nameRes = type.nameRes
companion object {
const val ID = "home"
}
}
companion object {
fun fromString(str: String): TabNode? {
val split = str.split("/", limit = 2)
if (split.size != 2) {
return null
}
val data = split[1].toIntOrNull() ?: return null
return when (split[0]) {
Root.ID -> Root(data)
More.ID -> More(data)
Home.ID -> Home(MusicType.fromIntCode(data) ?: return null)
else -> null
}
}
}
}