service: share home list logic between service/ui
This commit is contained in:
parent
e4310cfe17
commit
29d663f500
5 changed files with 187 additions and 78 deletions
|
@ -42,9 +42,9 @@ interface HomeSettings : Settings<HomeSettings.Listener> {
|
|||
|
||||
interface Listener {
|
||||
/** Called when the [homeTabs] configuration changes. */
|
||||
fun onTabsChanged()
|
||||
fun onTabsChanged() {}
|
||||
/** Called when the [shouldHideCollaborators] configuration changes. */
|
||||
fun onHideCollaboratorsChanged()
|
||||
fun onHideCollaboratorsChanged() {}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -23,6 +23,7 @@ import dagger.hilt.android.lifecycle.HiltViewModel
|
|||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import org.oxycblt.auxio.home.list.HomeListGenerator
|
||||
import org.oxycblt.auxio.home.tabs.Tab
|
||||
import org.oxycblt.auxio.list.ListSettings
|
||||
import org.oxycblt.auxio.list.adapter.UpdateInstructions
|
||||
|
@ -52,8 +53,9 @@ constructor(
|
|||
private val homeSettings: HomeSettings,
|
||||
private val listSettings: ListSettings,
|
||||
private val playbackSettings: PlaybackSettings,
|
||||
private val musicRepository: MusicRepository,
|
||||
) : ViewModel(), MusicRepository.UpdateListener, HomeSettings.Listener {
|
||||
homeGeneratorFactory: HomeListGenerator.Factory
|
||||
) : ViewModel(), HomeSettings.Listener, HomeListGenerator.Invalidator {
|
||||
private val generator = homeGeneratorFactory.create(this)
|
||||
|
||||
private val _songList = MutableStateFlow(listOf<Song>())
|
||||
/** A list of [Song]s, sorted by the preferred [Sort], to be shown in the home view. */
|
||||
|
@ -165,46 +167,37 @@ constructor(
|
|||
get() = _showOuter
|
||||
|
||||
init {
|
||||
musicRepository.addUpdateListener(this)
|
||||
homeSettings.registerListener(this)
|
||||
}
|
||||
|
||||
override fun onCleared() {
|
||||
super.onCleared()
|
||||
musicRepository.removeUpdateListener(this)
|
||||
homeSettings.unregisterListener(this)
|
||||
generator.release()
|
||||
}
|
||||
|
||||
override fun onMusicChanges(changes: MusicRepository.Changes) {
|
||||
val deviceLibrary = musicRepository.deviceLibrary
|
||||
if (changes.deviceLibrary && deviceLibrary != null) {
|
||||
logD("Refreshing library")
|
||||
// Get the each list of items in the library to use as our list data.
|
||||
// Applying the preferred sorting to them.
|
||||
_songInstructions.put(UpdateInstructions.Diff)
|
||||
_songList.value = listSettings.songSort.songs(deviceLibrary.songs)
|
||||
_albumInstructions.put(UpdateInstructions.Diff)
|
||||
_albumList.value = listSettings.albumSort.albums(deviceLibrary.albums)
|
||||
_artistInstructions.put(UpdateInstructions.Diff)
|
||||
_artistList.value =
|
||||
listSettings.artistSort.artists(
|
||||
if (homeSettings.shouldHideCollaborators) {
|
||||
logD("Filtering collaborator artists")
|
||||
// Hide Collaborators is enabled, filter out collaborators.
|
||||
deviceLibrary.artists.filter { it.explicitAlbums.isNotEmpty() }
|
||||
} else {
|
||||
logD("Using all artists")
|
||||
deviceLibrary.artists
|
||||
})
|
||||
_genreInstructions.put(UpdateInstructions.Diff)
|
||||
_genreList.value = listSettings.genreSort.genres(deviceLibrary.genres)
|
||||
}
|
||||
|
||||
val userLibrary = musicRepository.userLibrary
|
||||
if (changes.userLibrary && userLibrary != null) {
|
||||
logD("Refreshing playlists")
|
||||
_playlistInstructions.put(UpdateInstructions.Diff)
|
||||
_playlistList.value = listSettings.playlistSort.playlists(userLibrary.playlists)
|
||||
override fun invalidate(type: MusicType, instructions: UpdateInstructions) {
|
||||
when (type) {
|
||||
MusicType.SONGS -> {
|
||||
_songList.value = generator.songs()
|
||||
_songInstructions.put(instructions)
|
||||
}
|
||||
MusicType.ALBUMS -> {
|
||||
_albumList.value = generator.albums()
|
||||
_albumInstructions.put(instructions)
|
||||
}
|
||||
MusicType.ARTISTS -> {
|
||||
_artistList.value = generator.artists()
|
||||
_artistInstructions.put(instructions)
|
||||
}
|
||||
MusicType.GENRES -> {
|
||||
_genreList.value = generator.genres()
|
||||
_genreInstructions.put(instructions)
|
||||
}
|
||||
MusicType.PLAYLISTS -> {
|
||||
_playlistList.value = generator.playlists()
|
||||
_playlistInstructions.put(instructions)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -215,13 +208,6 @@ constructor(
|
|||
_shouldRecreate.put(Unit)
|
||||
}
|
||||
|
||||
override fun onHideCollaboratorsChanged() {
|
||||
// Changes in the hide collaborator setting will change the artist contents
|
||||
// of the library, consider it a library update.
|
||||
logD("Collaborator setting changed, forwarding update")
|
||||
onMusicChanges(MusicRepository.Changes(deviceLibrary = true, userLibrary = false))
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply a new [Sort] to [songList].
|
||||
*
|
||||
|
@ -229,8 +215,6 @@ constructor(
|
|||
*/
|
||||
fun applySongSort(sort: Sort) {
|
||||
listSettings.songSort = sort
|
||||
_songInstructions.put(UpdateInstructions.Replace(0))
|
||||
_songList.value = listSettings.songSort.songs(_songList.value)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -240,8 +224,6 @@ constructor(
|
|||
*/
|
||||
fun applyAlbumSort(sort: Sort) {
|
||||
listSettings.albumSort = sort
|
||||
_albumInstructions.put(UpdateInstructions.Replace(0))
|
||||
_albumList.value = listSettings.albumSort.albums(_albumList.value)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -251,8 +233,6 @@ constructor(
|
|||
*/
|
||||
fun applyArtistSort(sort: Sort) {
|
||||
listSettings.artistSort = sort
|
||||
_artistInstructions.put(UpdateInstructions.Replace(0))
|
||||
_artistList.value = listSettings.artistSort.artists(_artistList.value)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -262,8 +242,6 @@ constructor(
|
|||
*/
|
||||
fun applyGenreSort(sort: Sort) {
|
||||
listSettings.genreSort = sort
|
||||
_genreInstructions.put(UpdateInstructions.Replace(0))
|
||||
_genreList.value = listSettings.genreSort.genres(_genreList.value)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -273,8 +251,6 @@ constructor(
|
|||
*/
|
||||
fun applyPlaylistSort(sort: Sort) {
|
||||
listSettings.playlistSort = sort
|
||||
_playlistInstructions.put(UpdateInstructions.Replace(0))
|
||||
_playlistList.value = listSettings.playlistSort.playlists(_playlistList.value)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -0,0 +1,110 @@
|
|||
package org.oxycblt.auxio.home.list
|
||||
|
||||
import org.oxycblt.auxio.home.HomeSettings
|
||||
import org.oxycblt.auxio.list.ListSettings
|
||||
import org.oxycblt.auxio.list.adapter.UpdateInstructions
|
||||
import org.oxycblt.auxio.music.Album
|
||||
import org.oxycblt.auxio.music.Artist
|
||||
import org.oxycblt.auxio.music.Genre
|
||||
import org.oxycblt.auxio.music.MusicRepository
|
||||
import org.oxycblt.auxio.music.MusicType
|
||||
import org.oxycblt.auxio.music.Playlist
|
||||
import org.oxycblt.auxio.music.Song
|
||||
import org.oxycblt.auxio.playback.PlaybackSettings
|
||||
import org.oxycblt.auxio.util.logD
|
||||
import javax.inject.Inject
|
||||
|
||||
interface HomeListGenerator {
|
||||
fun songs(): List<Song>
|
||||
fun albums(): List<Album>
|
||||
fun artists(): List<Artist>
|
||||
fun genres(): List<Genre>
|
||||
fun playlists(): List<Playlist>
|
||||
fun release()
|
||||
|
||||
interface Invalidator {
|
||||
fun invalidate(type: MusicType, instructions: UpdateInstructions)
|
||||
}
|
||||
|
||||
interface Factory {
|
||||
fun create(invalidator: Invalidator): HomeListGenerator
|
||||
}
|
||||
}
|
||||
|
||||
private class HomeListGeneratorImpl(
|
||||
private val invalidator: HomeListGenerator.Invalidator,
|
||||
private val homeSettings: HomeSettings,
|
||||
private val listSettings: ListSettings,
|
||||
private val musicRepository: MusicRepository,
|
||||
) : HomeListGenerator, HomeSettings.Listener, ListSettings.Listener, MusicRepository.UpdateListener {
|
||||
override fun songs() =
|
||||
musicRepository.deviceLibrary?.let { listSettings.songSort.songs(it.songs) } ?: 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 genres() = musicRepository.deviceLibrary?.let { listSettings.genreSort.genres(it.genres) } ?: emptyList()
|
||||
override fun playlists() = musicRepository.userLibrary?.let { listSettings.playlistSort.playlists(it.playlists) } ?: emptyList()
|
||||
|
||||
init {
|
||||
homeSettings.registerListener(this)
|
||||
listSettings.registerListener(this)
|
||||
musicRepository.addUpdateListener(this)
|
||||
}
|
||||
|
||||
override fun release() {
|
||||
homeSettings.unregisterListener(this)
|
||||
listSettings.unregisterListener(this)
|
||||
musicRepository.removeUpdateListener(this)
|
||||
}
|
||||
|
||||
override fun onHideCollaboratorsChanged() {
|
||||
// Changes in the hide collaborator setting will change the artist contents
|
||||
// of the library, consider it a library update.
|
||||
logD("Collaborator setting changed, forwarding update")
|
||||
onMusicChanges(MusicRepository.Changes(deviceLibrary = true, userLibrary = false))
|
||||
}
|
||||
|
||||
override fun onSongSortChanged() {
|
||||
super.onSongSortChanged()
|
||||
invalidator.invalidate(MusicType.SONGS, UpdateInstructions.Replace(0))
|
||||
}
|
||||
|
||||
override fun onAlbumSortChanged() {
|
||||
super.onAlbumSortChanged()
|
||||
invalidator.invalidate(MusicType.ALBUMS, UpdateInstructions.Replace(0))
|
||||
}
|
||||
|
||||
override fun onArtistSortChanged() {
|
||||
super.onArtistSortChanged()
|
||||
invalidator.invalidate(MusicType.ARTISTS, UpdateInstructions.Replace(0))
|
||||
}
|
||||
|
||||
override fun onGenreSortChanged() {
|
||||
super.onGenreSortChanged()
|
||||
invalidator.invalidate(MusicType.GENRES, UpdateInstructions.Replace(0))
|
||||
}
|
||||
|
||||
override fun onPlaylistSortChanged() {
|
||||
super.onPlaylistSortChanged()
|
||||
invalidator.invalidate(MusicType.PLAYLISTS, UpdateInstructions.Replace(0))
|
||||
}
|
||||
|
||||
override fun onMusicChanges(changes: MusicRepository.Changes) {
|
||||
val deviceLibrary = musicRepository.deviceLibrary
|
||||
if (changes.deviceLibrary && deviceLibrary != null) {
|
||||
logD("Refreshing library")
|
||||
// Get the each list of items in the library to use as our list data.
|
||||
// Applying the preferred sorting to them.
|
||||
invalidator.invalidate(MusicType.SONGS, UpdateInstructions.Diff)
|
||||
invalidator.invalidate(MusicType.ALBUMS, UpdateInstructions.Diff)
|
||||
invalidator.invalidate(MusicType.ARTISTS, UpdateInstructions.Diff)
|
||||
invalidator.invalidate(MusicType.GENRES, UpdateInstructions.Diff)
|
||||
}
|
||||
|
||||
val userLibrary = musicRepository.userLibrary
|
||||
if (changes.userLibrary && userLibrary != null) {
|
||||
logD("Refreshing playlists")
|
||||
invalidator.invalidate(MusicType.PLAYLISTS, UpdateInstructions.Diff)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -26,7 +26,7 @@ import org.oxycblt.auxio.R
|
|||
import org.oxycblt.auxio.list.sort.Sort
|
||||
import org.oxycblt.auxio.settings.Settings
|
||||
|
||||
interface ListSettings : Settings<Unit> {
|
||||
interface ListSettings : Settings<ListSettings.Listener> {
|
||||
/** The [Sort] mode used in Song lists. */
|
||||
var songSort: Sort
|
||||
/** The [Sort] mode used in Album lists. */
|
||||
|
@ -43,10 +43,18 @@ interface ListSettings : Settings<Unit> {
|
|||
var artistSongSort: Sort
|
||||
/** The [Sort] mode used in a Genre's Song list. */
|
||||
var genreSongSort: Sort
|
||||
|
||||
interface Listener {
|
||||
fun onSongSortChanged() {}
|
||||
fun onAlbumSortChanged() {}
|
||||
fun onArtistSortChanged() {}
|
||||
fun onGenreSortChanged() {}
|
||||
fun onPlaylistSortChanged() {}
|
||||
}
|
||||
}
|
||||
|
||||
class ListSettingsImpl @Inject constructor(@ApplicationContext val context: Context) :
|
||||
Settings.Impl<Unit>(context), ListSettings {
|
||||
Settings.Impl<ListSettings.Listener>(context), ListSettings {
|
||||
override var songSort: Sort
|
||||
get() =
|
||||
Sort.fromIntCode(
|
||||
|
@ -145,4 +153,14 @@ class ListSettingsImpl @Inject constructor(@ApplicationContext val context: Cont
|
|||
apply()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onSettingChanged(key: String, listener: ListSettings.Listener) {
|
||||
when (key) {
|
||||
getString(R.string.set_key_songs_sort) -> listener.onSongSortChanged()
|
||||
getString(R.string.set_key_albums_sort) -> listener.onAlbumSortChanged()
|
||||
getString(R.string.set_key_artists_sort) -> listener.onArtistSortChanged()
|
||||
getString(R.string.set_key_genres_sort) -> listener.onGenreSortChanged()
|
||||
getString(R.string.set_key_playlists_sort) -> listener.onPlaylistSortChanged()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,13 +23,16 @@ import android.support.v4.media.MediaBrowserCompat.MediaItem
|
|||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import javax.inject.Inject
|
||||
import org.oxycblt.auxio.R
|
||||
import org.oxycblt.auxio.home.list.HomeListGenerator
|
||||
import org.oxycblt.auxio.list.ListSettings
|
||||
import org.oxycblt.auxio.list.adapter.UpdateInstructions
|
||||
import org.oxycblt.auxio.list.sort.Sort
|
||||
import org.oxycblt.auxio.music.Album
|
||||
import org.oxycblt.auxio.music.Artist
|
||||
import org.oxycblt.auxio.music.Genre
|
||||
import org.oxycblt.auxio.music.Music
|
||||
import org.oxycblt.auxio.music.MusicRepository
|
||||
import org.oxycblt.auxio.music.MusicType
|
||||
import org.oxycblt.auxio.music.Playlist
|
||||
import org.oxycblt.auxio.music.Song
|
||||
import org.oxycblt.auxio.music.device.DeviceLibrary
|
||||
|
@ -42,12 +45,14 @@ constructor(
|
|||
@ApplicationContext private val context: Context,
|
||||
private val musicRepository: MusicRepository,
|
||||
private val searchEngine: SearchEngine,
|
||||
private val listSettings: ListSettings
|
||||
) : MusicRepository.UpdateListener {
|
||||
private val listSettings: ListSettings,
|
||||
homeGeneratorFactory: HomeListGenerator.Factory
|
||||
) : MusicRepository.UpdateListener, HomeListGenerator.Invalidator {
|
||||
interface Invalidator {
|
||||
fun invalidateMusic(ids: Set<String>)
|
||||
}
|
||||
|
||||
private val generator = homeGeneratorFactory.create(this)
|
||||
private var invalidator: Invalidator? = null
|
||||
|
||||
fun attach(invalidator: Invalidator) {
|
||||
|
@ -59,6 +64,18 @@ constructor(
|
|||
musicRepository.removeUpdateListener(this)
|
||||
}
|
||||
|
||||
override fun invalidate(type: MusicType, instructions: UpdateInstructions) {
|
||||
val category = when (type) {
|
||||
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))
|
||||
}
|
||||
|
||||
override fun onMusicChanges(changes: MusicRepository.Changes) {
|
||||
val deviceLibrary = musicRepository.deviceLibrary
|
||||
val invalidate = mutableSetOf<String>()
|
||||
|
@ -126,7 +143,7 @@ constructor(
|
|||
return listOf()
|
||||
}
|
||||
|
||||
return getMediaItemList(parentId, deviceLibrary, userLibrary)
|
||||
return getMediaItemList(parentId)
|
||||
}
|
||||
|
||||
suspend fun search(query: String): MutableList<MediaItem> {
|
||||
|
@ -166,13 +183,11 @@ constructor(
|
|||
}
|
||||
|
||||
private fun getMediaItemList(
|
||||
id: String,
|
||||
deviceLibrary: DeviceLibrary,
|
||||
userLibrary: UserLibrary
|
||||
id: String
|
||||
): List<MediaItem>? {
|
||||
return when (val mediaSessionUID = MediaSessionUID.fromString(id)) {
|
||||
is MediaSessionUID.CategoryItem -> {
|
||||
getCategoryMediaItems(mediaSessionUID.category, deviceLibrary, userLibrary)
|
||||
getCategoryMediaItems(mediaSessionUID.category)
|
||||
}
|
||||
is MediaSessionUID.SingleItem -> {
|
||||
getChildMediaItems(mediaSessionUID.uid)
|
||||
|
@ -187,9 +202,7 @@ constructor(
|
|||
}
|
||||
|
||||
private fun getCategoryMediaItems(
|
||||
category: Category,
|
||||
deviceLibrary: DeviceLibrary,
|
||||
userLibrary: UserLibrary
|
||||
category: Category
|
||||
) =
|
||||
when (category) {
|
||||
is Category.Root -> {
|
||||
|
@ -203,19 +216,11 @@ constructor(
|
|||
}
|
||||
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) }
|
||||
is Category.Songs -> generator.songs().map { it.toMediaItem(context) }
|
||||
is Category.Albums -> generator.albums().map { it.toMediaItem(context) }
|
||||
is Category.Artists -> generator.artists().map { it.toMediaItem(context) }
|
||||
is Category.Genres -> generator.genres().map { it.toMediaItem(context) }
|
||||
is Category.Playlists -> generator.playlists().map { it.toMediaItem(context) }
|
||||
}
|
||||
|
||||
private fun getChildMediaItems(uid: Music.UID): List<MediaItem>? {
|
||||
|
|
Loading…
Reference in a new issue