Create parent abstraction
Create a BaseModel variant for albums, artists, and genres to simplify on alot of code that should only run on those.
This commit is contained in:
parent
ec310a5b93
commit
67c177ccf3
8 changed files with 79 additions and 104 deletions
|
@ -5,22 +5,22 @@ import android.view.ViewGroup
|
|||
import androidx.recyclerview.widget.RecyclerView
|
||||
import org.oxycblt.auxio.music.Album
|
||||
import org.oxycblt.auxio.music.Artist
|
||||
import org.oxycblt.auxio.music.BaseModel
|
||||
import org.oxycblt.auxio.music.Genre
|
||||
import org.oxycblt.auxio.music.Parent
|
||||
import org.oxycblt.auxio.recycler.viewholders.AlbumViewHolder
|
||||
import org.oxycblt.auxio.recycler.viewholders.ArtistViewHolder
|
||||
import org.oxycblt.auxio.recycler.viewholders.GenreViewHolder
|
||||
|
||||
/**
|
||||
* An adapter for displaying library items.
|
||||
* An adapter for displaying library items. Supports [Parent]s only.
|
||||
* @author OxygenCobalt
|
||||
*/
|
||||
class LibraryAdapter(
|
||||
private val doOnClick: (data: BaseModel) -> Unit,
|
||||
private val doOnLongClick: (view: View, data: BaseModel) -> Unit
|
||||
private val doOnClick: (data: Parent) -> Unit,
|
||||
private val doOnLongClick: (view: View, data: Parent) -> Unit
|
||||
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
||||
|
||||
private var data = listOf<BaseModel>()
|
||||
private var data = listOf<Parent>()
|
||||
|
||||
override fun getItemCount(): Int = data.size
|
||||
|
||||
|
@ -29,8 +29,6 @@ class LibraryAdapter(
|
|||
is Genre -> GenreViewHolder.ITEM_TYPE
|
||||
is Artist -> ArtistViewHolder.ITEM_TYPE
|
||||
is Album -> AlbumViewHolder.ITEM_TYPE
|
||||
|
||||
else -> -1
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -64,7 +62,7 @@ class LibraryAdapter(
|
|||
* Update the data directly. [notifyDataSetChanged] will be called
|
||||
* @param newData The new data to be used
|
||||
*/
|
||||
fun updateData(newData: List<BaseModel>) {
|
||||
fun updateData(newData: List<Parent>) {
|
||||
data = newData
|
||||
|
||||
notifyDataSetChanged()
|
||||
|
|
|
@ -12,11 +12,10 @@ import org.oxycblt.auxio.R
|
|||
import org.oxycblt.auxio.databinding.FragmentLibraryBinding
|
||||
import org.oxycblt.auxio.detail.DetailViewModel
|
||||
import org.oxycblt.auxio.logD
|
||||
import org.oxycblt.auxio.logE
|
||||
import org.oxycblt.auxio.music.Album
|
||||
import org.oxycblt.auxio.music.Artist
|
||||
import org.oxycblt.auxio.music.BaseModel
|
||||
import org.oxycblt.auxio.music.Genre
|
||||
import org.oxycblt.auxio.music.Parent
|
||||
import org.oxycblt.auxio.music.Song
|
||||
import org.oxycblt.auxio.ui.ActionMenu
|
||||
import org.oxycblt.auxio.ui.fixAnimInfoLeak
|
||||
|
@ -81,10 +80,10 @@ class LibraryFragment : Fragment() {
|
|||
if (it != null) {
|
||||
libraryModel.updateNavigationStatus(false)
|
||||
|
||||
if (it is Song) {
|
||||
onItemSelection(it.album)
|
||||
} else {
|
||||
if (it is Parent) {
|
||||
onItemSelection(it)
|
||||
} else if (it is Song) {
|
||||
onItemSelection(it.album)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -107,34 +106,22 @@ class LibraryFragment : Fragment() {
|
|||
}
|
||||
|
||||
/**
|
||||
* Navigate to an item
|
||||
* @param baseModel The item that should be navigated to.
|
||||
* Navigate to a parent UI
|
||||
* @param parent The parent that should be navigated with
|
||||
*/
|
||||
private fun onItemSelection(baseModel: BaseModel) {
|
||||
if (baseModel is Song) {
|
||||
logE("onItemSelection does not support songs")
|
||||
return
|
||||
}
|
||||
|
||||
private fun onItemSelection(parent: Parent) {
|
||||
requireView().rootView.clearFocus()
|
||||
|
||||
if (!libraryModel.isNavigating) {
|
||||
libraryModel.updateNavigationStatus(true)
|
||||
|
||||
logD("Navigating to the detail fragment for ${baseModel.name}")
|
||||
logD("Navigating to the detail fragment for ${parent.name}")
|
||||
|
||||
findNavController().navigate(
|
||||
when (baseModel) {
|
||||
is Genre -> LibraryFragmentDirections.actionShowGenre(baseModel.id)
|
||||
is Artist -> LibraryFragmentDirections.actionShowArtist(baseModel.id)
|
||||
is Album -> LibraryFragmentDirections.actionShowAlbum(baseModel.id)
|
||||
|
||||
// If given model wasn't valid, then reset the navigation status
|
||||
// and abort the navigation.
|
||||
else -> {
|
||||
libraryModel.updateNavigationStatus(false)
|
||||
return
|
||||
}
|
||||
when (parent) {
|
||||
is Genre -> LibraryFragmentDirections.actionShowGenre(parent.id)
|
||||
is Artist -> LibraryFragmentDirections.actionShowArtist(parent.id)
|
||||
is Album -> LibraryFragmentDirections.actionShowAlbum(parent.id)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
|
@ -5,8 +5,8 @@ import androidx.lifecycle.LiveData
|
|||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import org.oxycblt.auxio.R
|
||||
import org.oxycblt.auxio.music.BaseModel
|
||||
import org.oxycblt.auxio.music.MusicStore
|
||||
import org.oxycblt.auxio.music.Parent
|
||||
import org.oxycblt.auxio.recycler.DisplayMode
|
||||
import org.oxycblt.auxio.recycler.SortMode
|
||||
import org.oxycblt.auxio.settings.SettingsManager
|
||||
|
@ -17,8 +17,8 @@ import org.oxycblt.auxio.settings.SettingsManager
|
|||
* @author OxygenCobalt
|
||||
*/
|
||||
class LibraryViewModel : ViewModel(), SettingsManager.Callback {
|
||||
private val mLibraryData = MutableLiveData(listOf<BaseModel>())
|
||||
val libraryData: LiveData<List<BaseModel>> get() = mLibraryData
|
||||
private val mLibraryData = MutableLiveData(listOf<Parent>())
|
||||
val libraryData: LiveData<List<Parent>> get() = mLibraryData
|
||||
|
||||
private var mIsNavigating = false
|
||||
val isNavigating: Boolean get() = mIsNavigating
|
||||
|
@ -91,17 +91,11 @@ class LibraryViewModel : ViewModel(), SettingsManager.Callback {
|
|||
*/
|
||||
private fun updateLibraryData() {
|
||||
mLibraryData.value = when (mDisplayMode) {
|
||||
DisplayMode.SHOW_GENRES -> {
|
||||
mSortMode.getSortedGenreList(musicStore.genres)
|
||||
}
|
||||
DisplayMode.SHOW_GENRES -> mSortMode.getSortedGenreList(musicStore.genres)
|
||||
|
||||
DisplayMode.SHOW_ARTISTS -> {
|
||||
mSortMode.getSortedBaseModelList(musicStore.artists)
|
||||
}
|
||||
DisplayMode.SHOW_ARTISTS -> mSortMode.getSortedArtistList(musicStore.artists)
|
||||
|
||||
DisplayMode.SHOW_ALBUMS -> {
|
||||
mSortMode.getSortedAlbumList(musicStore.albums)
|
||||
}
|
||||
DisplayMode.SHOW_ALBUMS -> mSortMode.getSortedAlbumList(musicStore.albums)
|
||||
|
||||
else -> error("DisplayMode $mDisplayMode is unsupported.")
|
||||
}
|
||||
|
|
|
@ -15,6 +15,12 @@ sealed class BaseModel {
|
|||
abstract val name: String
|
||||
}
|
||||
|
||||
/**
|
||||
* [BaseModel] variant that denotes that this object is a parent of other data objects, such
|
||||
* as an [Album] or [Artist]
|
||||
*/
|
||||
sealed class Parent : BaseModel()
|
||||
|
||||
/**
|
||||
* The data object for a song. Inherits [BaseModel].
|
||||
* @property albumId The Song's Album ID. Never use this outside of when attaching a song to its album.
|
||||
|
@ -72,7 +78,7 @@ data class Song(
|
|||
}
|
||||
|
||||
/**
|
||||
* The data object for an album. Inherits [BaseModel].
|
||||
* The data object for an album. Inherits [Parent].
|
||||
* @property artistName The name of the parent artist. Do not use this outside of creating the artist from albums
|
||||
* @property coverUri The [Uri] for the album's cover. **Load this using Coil.**
|
||||
* @property year The year this album was released. 0 if there is none in the metadata.
|
||||
|
@ -87,7 +93,7 @@ data class Album(
|
|||
val artistName: String,
|
||||
val coverUri: Uri = Uri.EMPTY,
|
||||
val year: Int = 0
|
||||
) : BaseModel() {
|
||||
) : Parent() {
|
||||
private var mArtist: Artist? = null
|
||||
val artist: Artist get() {
|
||||
val artist = mArtist
|
||||
|
@ -123,7 +129,7 @@ data class Album(
|
|||
}
|
||||
|
||||
/**
|
||||
* The data object for an artist. Inherits [BaseModel]
|
||||
* The data object for an artist. Inherits [Parent]
|
||||
* @property albums The list of all [Album]s in this artist
|
||||
* @property genre The most prominent genre for this artist
|
||||
* @property songs The list of all [Song]s in this artist
|
||||
|
@ -133,7 +139,7 @@ data class Artist(
|
|||
override val id: Long = -1,
|
||||
override val name: String,
|
||||
val albums: List<Album>
|
||||
) : BaseModel() {
|
||||
) : Parent() {
|
||||
init {
|
||||
albums.forEach {
|
||||
it.applyArtist(this)
|
||||
|
@ -158,7 +164,7 @@ data class Artist(
|
|||
}
|
||||
|
||||
/**
|
||||
* The data object for a genre. Inherits [BaseModel]
|
||||
* The data object for a genre. Inherits [Parent]
|
||||
* @property songs The list of all [Song]s in this genre.
|
||||
* @property displayName A name that can be displayed without it showing up as an integer. ***USE THIS INSTEAD OF [name]!!!!***
|
||||
* @author OxygenCobalt
|
||||
|
@ -166,7 +172,7 @@ data class Artist(
|
|||
data class Genre(
|
||||
override val id: Long = -1,
|
||||
override val name: String,
|
||||
) : BaseModel() {
|
||||
) : Parent() {
|
||||
private val mSongs = mutableListOf<Song>()
|
||||
val songs: List<Song> get() = mSongs
|
||||
|
||||
|
|
|
@ -25,8 +25,8 @@ class MusicStore private constructor() {
|
|||
val songs: List<Song> get() = mSongs
|
||||
|
||||
/** All parent models (ex Albums, Artists) loaded by Auxio */
|
||||
val parents: MutableList<BaseModel> by lazy {
|
||||
mutableListOf<BaseModel>().apply {
|
||||
val parents: MutableList<Parent> by lazy {
|
||||
mutableListOf<Parent>().apply {
|
||||
addAll(mGenres)
|
||||
addAll(mArtists)
|
||||
addAll(mAlbums)
|
||||
|
|
|
@ -12,8 +12,8 @@ import org.oxycblt.auxio.logD
|
|||
import org.oxycblt.auxio.logE
|
||||
import org.oxycblt.auxio.music.Album
|
||||
import org.oxycblt.auxio.music.Artist
|
||||
import org.oxycblt.auxio.music.BaseModel
|
||||
import org.oxycblt.auxio.music.Genre
|
||||
import org.oxycblt.auxio.music.Parent
|
||||
import org.oxycblt.auxio.music.Song
|
||||
import org.oxycblt.auxio.music.toDuration
|
||||
import org.oxycblt.auxio.playback.queue.QueueAdapter
|
||||
|
@ -34,7 +34,7 @@ import org.oxycblt.auxio.ui.createToast
|
|||
class PlaybackViewModel : ViewModel(), PlaybackStateManager.Callback {
|
||||
// Playback
|
||||
private val mSong = MutableLiveData<Song?>()
|
||||
private val mParent = MutableLiveData<BaseModel?>()
|
||||
private val mParent = MutableLiveData<Parent?>()
|
||||
private val mPosition = MutableLiveData(0L)
|
||||
|
||||
// Queue
|
||||
|
@ -56,7 +56,7 @@ class PlaybackViewModel : ViewModel(), PlaybackStateManager.Callback {
|
|||
/** The current song. */
|
||||
val song: LiveData<Song?> get() = mSong
|
||||
/** The current model that is being played from, such as an [Album] or [Artist] */
|
||||
val parent: LiveData<BaseModel?> get() = mParent
|
||||
val parent: LiveData<Parent?> get() = mParent
|
||||
/** The current playback position, in seconds */
|
||||
val position: LiveData<Long> get() = mPosition
|
||||
|
||||
|
@ -125,7 +125,7 @@ class PlaybackViewModel : ViewModel(), PlaybackStateManager.Callback {
|
|||
return
|
||||
}
|
||||
|
||||
playbackManager.playParentModel(album, shuffled)
|
||||
playbackManager.playParent(album, shuffled)
|
||||
}
|
||||
|
||||
/** Play an Artist */
|
||||
|
@ -136,7 +136,7 @@ class PlaybackViewModel : ViewModel(), PlaybackStateManager.Callback {
|
|||
return
|
||||
}
|
||||
|
||||
playbackManager.playParentModel(artist, shuffled)
|
||||
playbackManager.playParent(artist, shuffled)
|
||||
}
|
||||
|
||||
/** Play a genre. */
|
||||
|
@ -147,7 +147,7 @@ class PlaybackViewModel : ViewModel(), PlaybackStateManager.Callback {
|
|||
return
|
||||
}
|
||||
|
||||
playbackManager.playParentModel(genre, shuffled)
|
||||
playbackManager.playParent(genre, shuffled)
|
||||
}
|
||||
|
||||
/** Shuffle all songs */
|
||||
|
@ -365,7 +365,7 @@ class PlaybackViewModel : ViewModel(), PlaybackStateManager.Callback {
|
|||
mSong.value = song
|
||||
}
|
||||
|
||||
override fun onParentUpdate(parent: BaseModel?) {
|
||||
override fun onParentUpdate(parent: Parent?) {
|
||||
mParent.value = parent
|
||||
}
|
||||
|
||||
|
|
|
@ -10,10 +10,9 @@ import org.oxycblt.auxio.logD
|
|||
import org.oxycblt.auxio.logE
|
||||
import org.oxycblt.auxio.music.Album
|
||||
import org.oxycblt.auxio.music.Artist
|
||||
import org.oxycblt.auxio.music.BaseModel
|
||||
import org.oxycblt.auxio.music.Genre
|
||||
import org.oxycblt.auxio.music.Header
|
||||
import org.oxycblt.auxio.music.MusicStore
|
||||
import org.oxycblt.auxio.music.Parent
|
||||
import org.oxycblt.auxio.music.Song
|
||||
import org.oxycblt.auxio.recycler.SortMode
|
||||
import org.oxycblt.auxio.settings.SettingsManager
|
||||
|
@ -42,7 +41,7 @@ class PlaybackStateManager private constructor() {
|
|||
field = value
|
||||
callbacks.forEach { it.onPositionUpdate(value) }
|
||||
}
|
||||
private var mParent: BaseModel? = null
|
||||
private var mParent: Parent? = null
|
||||
set(value) {
|
||||
field = value
|
||||
callbacks.forEach { it.onParentUpdate(value) }
|
||||
|
@ -98,7 +97,7 @@ class PlaybackStateManager private constructor() {
|
|||
/** The currently playing song. Null if there isn't one */
|
||||
val song: Song? get() = mSong
|
||||
/** The parent the queue is based on, null if all_songs */
|
||||
val parent: BaseModel? get() = mParent
|
||||
val parent: Parent? get() = mParent
|
||||
/** The current playback progress */
|
||||
val position: Long get() = mPosition
|
||||
/** The current queue determined by [parent] and [mode] */
|
||||
|
@ -192,33 +191,26 @@ class PlaybackStateManager private constructor() {
|
|||
|
||||
/**
|
||||
* Play a parent model, e.g an artist or an album.
|
||||
* @param baseModel The model to use
|
||||
* @param parent The model to use
|
||||
* @param shuffled Whether to shuffle the queue or not
|
||||
*/
|
||||
fun playParentModel(baseModel: BaseModel, shuffled: Boolean) {
|
||||
if (baseModel is Song || baseModel is Header) {
|
||||
// This should never occur.
|
||||
logE("playParentModel is not meant to play ${baseModel::class.simpleName}.")
|
||||
fun playParent(parent: Parent, shuffled: Boolean) {
|
||||
logD("Playing ${parent.name}")
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
logD("Playing ${baseModel.name}")
|
||||
|
||||
mParent = baseModel
|
||||
mParent = parent
|
||||
mIndex = 0
|
||||
|
||||
when (baseModel) {
|
||||
when (parent) {
|
||||
is Album -> {
|
||||
mQueue = baseModel.songs.toMutableList()
|
||||
mQueue = parent.songs.toMutableList()
|
||||
mMode = PlaybackMode.IN_ALBUM
|
||||
}
|
||||
is Artist -> {
|
||||
mQueue = baseModel.songs.toMutableList()
|
||||
mQueue = parent.songs.toMutableList()
|
||||
mMode = PlaybackMode.IN_ARTIST
|
||||
}
|
||||
is Genre -> {
|
||||
mQueue = baseModel.songs.toMutableList()
|
||||
mQueue = parent.songs.toMutableList()
|
||||
mMode = PlaybackMode.IN_GENRE
|
||||
}
|
||||
|
||||
|
@ -817,7 +809,7 @@ class PlaybackStateManager private constructor() {
|
|||
*/
|
||||
interface Callback {
|
||||
fun onSongUpdate(song: Song?) {}
|
||||
fun onParentUpdate(parent: BaseModel?) {}
|
||||
fun onParentUpdate(parent: Parent?) {}
|
||||
fun onPositionUpdate(position: Long) {}
|
||||
fun onQueueUpdate(queue: MutableList<Song>) {}
|
||||
fun onUserQueueUpdate(userQueue: MutableList<Song>) {}
|
||||
|
|
|
@ -4,6 +4,7 @@ import androidx.annotation.DrawableRes
|
|||
import androidx.annotation.IdRes
|
||||
import org.oxycblt.auxio.R
|
||||
import org.oxycblt.auxio.music.Album
|
||||
import org.oxycblt.auxio.music.Artist
|
||||
import org.oxycblt.auxio.music.BaseModel
|
||||
import org.oxycblt.auxio.music.Genre
|
||||
import org.oxycblt.auxio.music.Song
|
||||
|
@ -25,8 +26,8 @@ enum class SortMode(@DrawableRes val iconRes: Int) {
|
|||
|
||||
/**
|
||||
* Get a sorted list of genres for a SortMode. Only supports alphabetic sorting.
|
||||
* @param genres An unsorted list of artists.
|
||||
* @return The sorted list of artists.
|
||||
* @param genres An unsorted list of genres.
|
||||
* @return The sorted list of genres.
|
||||
*/
|
||||
fun getSortedGenreList(genres: List<Genre>): List<Genre> {
|
||||
return when (this) {
|
||||
|
@ -42,6 +43,25 @@ enum class SortMode(@DrawableRes val iconRes: Int) {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a sorted list of artists for a SortMode. Only supports alphabetic sorting.
|
||||
* @param artists An unsorted list of artists.
|
||||
* @return The sorted list of artists.
|
||||
*/
|
||||
fun getSortedArtistList(artists: List<Artist>): List<Artist> {
|
||||
return when (this) {
|
||||
ALPHA_UP -> artists.sortedWith(
|
||||
compareByDescending(String.CASE_INSENSITIVE_ORDER) { it.name }
|
||||
)
|
||||
|
||||
ALPHA_DOWN -> artists.sortedWith(
|
||||
compareBy(String.CASE_INSENSITIVE_ORDER) { it.name }
|
||||
)
|
||||
|
||||
else -> artists
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a sorted list of albums for a SortMode. Supports alpha + numeric sorting.
|
||||
* @param albums An unsorted list of albums.
|
||||
|
@ -124,28 +144,6 @@ enum class SortMode(@DrawableRes val iconRes: Int) {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a sorted list of BaseModels. Supports alpha + numeric sorting.
|
||||
* @param baseModels An unsorted list of BaseModels.
|
||||
* @return The sorted list of BaseModels.
|
||||
*/
|
||||
fun getSortedBaseModelList(baseModels: List<BaseModel>): List<BaseModel> {
|
||||
return when (this) {
|
||||
ALPHA_UP -> baseModels.sortedWith(
|
||||
compareByDescending(String.CASE_INSENSITIVE_ORDER) { it.name }
|
||||
)
|
||||
|
||||
ALPHA_DOWN -> baseModels.sortedWith(
|
||||
compareBy(String.CASE_INSENSITIVE_ORDER) { it.name }
|
||||
)
|
||||
|
||||
NUMERIC_UP -> baseModels.sortedWith(compareByDescending { it.id })
|
||||
NUMERIC_DOWN -> baseModels.sortedWith(compareBy { it.id })
|
||||
|
||||
else -> baseModels
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a sorting menu ID for this mode. Alphabetic only.
|
||||
* @return The action id for this mode.
|
||||
|
|
Loading…
Reference in a new issue