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:
OxygenCobalt 2021-01-30 11:18:29 -07:00
parent ec310a5b93
commit 67c177ccf3
No known key found for this signature in database
GPG key ID: 37DBE3621FE9AD47
8 changed files with 79 additions and 104 deletions

View file

@ -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()

View file

@ -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)
}
)
}

View file

@ -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.")
}

View file

@ -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

View file

@ -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)

View file

@ -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
}

View file

@ -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>) {}

View file

@ -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.