Add sorting to LibraryFragment
Add basic sorting to LibraryFragment.
This commit is contained in:
parent
aad42b5201
commit
37c52d9e5c
17 changed files with 227 additions and 174 deletions
10
AuxioTODO
10
AuxioTODO
|
@ -5,7 +5,6 @@ TODO:
|
||||||
|
|
||||||
/detail/
|
/detail/
|
||||||
|
|
||||||
- Add genre detail
|
|
||||||
- ? Implement Toolbar update functionality ?
|
- ? Implement Toolbar update functionality ?
|
||||||
- ! Implement shared element transitions !
|
- ! Implement shared element transitions !
|
||||||
|
|
||||||
|
@ -24,11 +23,14 @@ TODO:
|
||||||
|
|
||||||
/library/
|
/library/
|
||||||
|
|
||||||
- ? Move into ViewPager ?
|
- Re-add albums/genres into a single adapter
|
||||||
- Sorting
|
- Add highlighting to the current sortmode
|
||||||
- Search
|
- Search
|
||||||
- ? Show Artists, Albums, and Songs in search ?
|
|
||||||
- Exit functionality
|
- Exit functionality
|
||||||
|
- ? Show Artists, Albums, and Songs in search ?
|
||||||
|
- ? Move into ViewPager ?
|
||||||
|
- ! Move Adapter functionality to ListAdapter!
|
||||||
|
|
||||||
|
|
||||||
To be added:
|
To be added:
|
||||||
/prefs/
|
/prefs/
|
||||||
|
|
|
@ -15,7 +15,6 @@ import org.oxycblt.auxio.databinding.FragmentAlbumDetailBinding
|
||||||
import org.oxycblt.auxio.detail.adapters.DetailSongAdapter
|
import org.oxycblt.auxio.detail.adapters.DetailSongAdapter
|
||||||
import org.oxycblt.auxio.music.MusicViewModel
|
import org.oxycblt.auxio.music.MusicViewModel
|
||||||
import org.oxycblt.auxio.recycler.ClickListener
|
import org.oxycblt.auxio.recycler.ClickListener
|
||||||
import org.oxycblt.auxio.recycler.SortMode
|
|
||||||
import org.oxycblt.auxio.theme.applyDivider
|
import org.oxycblt.auxio.theme.applyDivider
|
||||||
import org.oxycblt.auxio.theme.toColor
|
import org.oxycblt.auxio.theme.toColor
|
||||||
|
|
||||||
|
@ -65,7 +64,7 @@ class AlbumDetailFragment : Fragment() {
|
||||||
|
|
||||||
// If the album was shown directly from LibraryFragment, Then enable the ability to
|
// If the album was shown directly from LibraryFragment, Then enable the ability to
|
||||||
// navigate upwards to the parent artist
|
// navigate upwards to the parent artist
|
||||||
if (args.fromLibrary) {
|
if (args.enableParentNav) {
|
||||||
detailModel.navToParent.observe(viewLifecycleOwner) {
|
detailModel.navToParent.observe(viewLifecycleOwner) {
|
||||||
if (it) {
|
if (it) {
|
||||||
findNavController().navigate(
|
findNavController().navigate(
|
||||||
|
@ -89,14 +88,7 @@ class AlbumDetailFragment : Fragment() {
|
||||||
|
|
||||||
// Then update the sort mode of the album adapter.
|
// Then update the sort mode of the album adapter.
|
||||||
songAdapter.submitList(
|
songAdapter.submitList(
|
||||||
detailModel.currentAlbum.value!!.songs.sortedWith(
|
mode.getSortedSongList(detailModel.currentAlbum.value!!.songs)
|
||||||
SortMode.songSortComparators.getOrDefault(
|
|
||||||
mode,
|
|
||||||
|
|
||||||
// If any invalid value is given, just default to the normal sort order.
|
|
||||||
compareByDescending { it.track }
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,6 @@ import org.oxycblt.auxio.detail.adapters.DetailAlbumAdapter
|
||||||
import org.oxycblt.auxio.music.Album
|
import org.oxycblt.auxio.music.Album
|
||||||
import org.oxycblt.auxio.music.MusicViewModel
|
import org.oxycblt.auxio.music.MusicViewModel
|
||||||
import org.oxycblt.auxio.recycler.ClickListener
|
import org.oxycblt.auxio.recycler.ClickListener
|
||||||
import org.oxycblt.auxio.recycler.SortMode
|
|
||||||
import org.oxycblt.auxio.theme.applyDivider
|
import org.oxycblt.auxio.theme.applyDivider
|
||||||
import org.oxycblt.auxio.theme.toColor
|
import org.oxycblt.auxio.theme.toColor
|
||||||
|
|
||||||
|
@ -72,14 +71,7 @@ class ArtistDetailFragment : Fragment() {
|
||||||
|
|
||||||
// Then update the sort mode of the album adapter.
|
// Then update the sort mode of the album adapter.
|
||||||
albumAdapter.submitList(
|
albumAdapter.submitList(
|
||||||
detailModel.currentArtist.value!!.albums.sortedWith(
|
mode.getSortedAlbumList(detailModel.currentArtist.value!!.albums)
|
||||||
SortMode.albumSortComparators.getOrDefault(
|
|
||||||
mode,
|
|
||||||
|
|
||||||
// If any invalid value is given, just default to the normal sort order.
|
|
||||||
compareByDescending { it.year }
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,6 @@ import org.oxycblt.auxio.detail.adapters.DetailArtistAdapter
|
||||||
import org.oxycblt.auxio.music.Artist
|
import org.oxycblt.auxio.music.Artist
|
||||||
import org.oxycblt.auxio.music.MusicViewModel
|
import org.oxycblt.auxio.music.MusicViewModel
|
||||||
import org.oxycblt.auxio.recycler.ClickListener
|
import org.oxycblt.auxio.recycler.ClickListener
|
||||||
import org.oxycblt.auxio.recycler.SortMode
|
|
||||||
import org.oxycblt.auxio.theme.applyDivider
|
import org.oxycblt.auxio.theme.applyDivider
|
||||||
import org.oxycblt.auxio.theme.toColor
|
import org.oxycblt.auxio.theme.toColor
|
||||||
|
|
||||||
|
@ -70,14 +69,7 @@ class GenreDetailFragment : Fragment() {
|
||||||
|
|
||||||
// Then update the sort mode of the artist adapter.
|
// Then update the sort mode of the artist adapter.
|
||||||
albumAdapter.submitList(
|
albumAdapter.submitList(
|
||||||
detailModel.currentGenre.value!!.artists.sortedWith(
|
mode.getSortedArtistList(detailModel.currentGenre.value!!.artists)
|
||||||
SortMode.artistSortComparators.getOrDefault(
|
|
||||||
mode,
|
|
||||||
|
|
||||||
// If any invalid value is given, just default to the normal sort order.
|
|
||||||
compareByDescending { it.name }
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,29 +5,24 @@ import android.util.Log
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.fragment.app.activityViewModels
|
import androidx.fragment.app.activityViewModels
|
||||||
import androidx.navigation.fragment.findNavController
|
import androidx.navigation.fragment.findNavController
|
||||||
import org.oxycblt.auxio.MainFragmentDirections
|
import org.oxycblt.auxio.MainFragmentDirections
|
||||||
|
import org.oxycblt.auxio.R
|
||||||
import org.oxycblt.auxio.databinding.FragmentLibraryBinding
|
import org.oxycblt.auxio.databinding.FragmentLibraryBinding
|
||||||
import org.oxycblt.auxio.library.adapters.AlbumAdapter
|
|
||||||
import org.oxycblt.auxio.library.adapters.ArtistAdapter
|
import org.oxycblt.auxio.library.adapters.ArtistAdapter
|
||||||
import org.oxycblt.auxio.library.adapters.GenreAdapter
|
|
||||||
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.BaseModel
|
||||||
import org.oxycblt.auxio.music.Genre
|
import org.oxycblt.auxio.music.Genre
|
||||||
import org.oxycblt.auxio.music.MusicViewModel
|
import org.oxycblt.auxio.music.MusicViewModel
|
||||||
import org.oxycblt.auxio.recycler.ClickListener
|
import org.oxycblt.auxio.recycler.ClickListener
|
||||||
import org.oxycblt.auxio.theme.SHOW_ALBUMS
|
|
||||||
import org.oxycblt.auxio.theme.SHOW_ARTISTS
|
|
||||||
import org.oxycblt.auxio.theme.SHOW_GENRES
|
|
||||||
import org.oxycblt.auxio.theme.applyDivider
|
import org.oxycblt.auxio.theme.applyDivider
|
||||||
|
|
||||||
class LibraryFragment : Fragment() {
|
class LibraryFragment : Fragment() {
|
||||||
|
|
||||||
// FIXME: Temp value, remove when there are actual preferences
|
|
||||||
private val libraryMode = SHOW_ARTISTS
|
|
||||||
|
|
||||||
private val musicModel: MusicViewModel by activityViewModels()
|
private val musicModel: MusicViewModel by activityViewModels()
|
||||||
private val libraryModel: LibraryViewModel by activityViewModels()
|
private val libraryModel: LibraryViewModel by activityViewModels()
|
||||||
|
|
||||||
|
@ -38,34 +33,34 @@ class LibraryFragment : Fragment() {
|
||||||
): View? {
|
): View? {
|
||||||
val binding = FragmentLibraryBinding.inflate(inflater)
|
val binding = FragmentLibraryBinding.inflate(inflater)
|
||||||
|
|
||||||
binding.libraryRecycler.adapter = when (libraryMode) {
|
val artistAdapter = ArtistAdapter(
|
||||||
SHOW_ARTISTS -> ArtistAdapter(
|
ClickListener { navToItem(it) }
|
||||||
musicModel.artists.value!!,
|
|
||||||
ClickListener {
|
|
||||||
navToArtist(it)
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
SHOW_ALBUMS -> AlbumAdapter(
|
binding.libraryRecycler.adapter = artistAdapter
|
||||||
musicModel.albums.value!!,
|
|
||||||
ClickListener {
|
|
||||||
navToAlbum(it)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
SHOW_GENRES -> GenreAdapter(
|
|
||||||
musicModel.genres.value!!,
|
|
||||||
ClickListener {
|
|
||||||
navToGenre(it)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
else -> null
|
|
||||||
}
|
|
||||||
|
|
||||||
binding.libraryRecycler.applyDivider()
|
binding.libraryRecycler.applyDivider()
|
||||||
binding.libraryRecycler.setHasFixedSize(true)
|
binding.libraryRecycler.setHasFixedSize(true)
|
||||||
|
|
||||||
|
libraryModel.sortMode.observe(viewLifecycleOwner) { mode ->
|
||||||
|
binding.libraryToolbar.overflowIcon = ContextCompat.getDrawable(
|
||||||
|
requireContext(), mode.iconRes
|
||||||
|
)
|
||||||
|
|
||||||
|
artistAdapter.updateData(
|
||||||
|
mode.getSortedArtistList(
|
||||||
|
musicModel.artists.value!!
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.libraryToolbar.setOnMenuItemClickListener {
|
||||||
|
libraryModel.updateSortMode(it)
|
||||||
|
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.libraryToolbar.inflateMenu(R.menu.menu_library)
|
||||||
|
|
||||||
Log.d(this::class.simpleName, "Fragment created.")
|
Log.d(this::class.simpleName, "Fragment created.")
|
||||||
|
|
||||||
return binding.root
|
return binding.root
|
||||||
|
@ -77,39 +72,19 @@ class LibraryFragment : Fragment() {
|
||||||
libraryModel.isAlreadyNavigating = false
|
libraryModel.isAlreadyNavigating = false
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun navToArtist(artist: Artist) {
|
private fun navToItem(baseModel: BaseModel) {
|
||||||
// Dont navigate if an item has already been selected
|
// Don't navigate if an item has already been selected
|
||||||
if (!libraryModel.isAlreadyNavigating) {
|
if (!libraryModel.isAlreadyNavigating) {
|
||||||
libraryModel.isAlreadyNavigating = true
|
libraryModel.isAlreadyNavigating = true
|
||||||
|
|
||||||
findNavController().navigate(
|
findNavController().navigate(
|
||||||
MainFragmentDirections.actionShowArtist(
|
when (baseModel) {
|
||||||
artist.id
|
is Genre -> MainFragmentDirections.actionShowGenre(baseModel.id)
|
||||||
)
|
is Artist -> MainFragmentDirections.actionShowArtist(baseModel.id)
|
||||||
)
|
is Album -> MainFragmentDirections.actionShowAlbum(baseModel.id, true)
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun navToAlbum(album: Album) {
|
else -> return
|
||||||
if (!libraryModel.isAlreadyNavigating) {
|
|
||||||
libraryModel.isAlreadyNavigating = true
|
|
||||||
|
|
||||||
findNavController().navigate(
|
|
||||||
MainFragmentDirections.actionShowAlbum(
|
|
||||||
album.id, true
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private fun navToGenre(genre: Genre) {
|
|
||||||
if (!libraryModel.isAlreadyNavigating) {
|
|
||||||
libraryModel.isAlreadyNavigating = true
|
|
||||||
|
|
||||||
findNavController().navigate(
|
|
||||||
MainFragmentDirections.actionShowGenre(
|
|
||||||
genre.id
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,34 @@
|
||||||
package org.oxycblt.auxio.library
|
package org.oxycblt.auxio.library
|
||||||
|
|
||||||
|
import android.view.MenuItem
|
||||||
|
import androidx.lifecycle.LiveData
|
||||||
|
import androidx.lifecycle.MutableLiveData
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
|
import org.oxycblt.auxio.R
|
||||||
|
import org.oxycblt.auxio.recycler.SortMode
|
||||||
|
import org.oxycblt.auxio.theme.SHOW_ARTISTS
|
||||||
|
|
||||||
class LibraryViewModel : ViewModel() {
|
class LibraryViewModel : ViewModel() {
|
||||||
var isAlreadyNavigating = false
|
var isAlreadyNavigating = false
|
||||||
|
|
||||||
|
// TODO: Move these to pref values when they're added
|
||||||
|
private val mShowMode = MutableLiveData(SHOW_ARTISTS)
|
||||||
|
val showMode: LiveData<Int> get() = mShowMode
|
||||||
|
|
||||||
|
private val mSortMode = MutableLiveData(SortMode.ALPHA_UP)
|
||||||
|
val sortMode: LiveData<SortMode> get() = mSortMode
|
||||||
|
|
||||||
|
fun updateSortMode(item: MenuItem) {
|
||||||
|
val mode = when (item.itemId) {
|
||||||
|
R.id.sort_none -> SortMode.NONE
|
||||||
|
R.id.sort_alpha_down -> SortMode.ALPHA_DOWN
|
||||||
|
R.id.sort_alpha_up -> SortMode.ALPHA_UP
|
||||||
|
|
||||||
|
else -> SortMode.NONE
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mode != mSortMode.value) {
|
||||||
|
mSortMode.value = mode
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,18 +2,16 @@ package org.oxycblt.auxio.library.adapters
|
||||||
|
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.ListAdapter
|
||||||
import org.oxycblt.auxio.databinding.ItemAlbumBinding
|
import org.oxycblt.auxio.databinding.ItemAlbumBinding
|
||||||
import org.oxycblt.auxio.music.Album
|
import org.oxycblt.auxio.music.Album
|
||||||
import org.oxycblt.auxio.recycler.BaseViewHolder
|
import org.oxycblt.auxio.recycler.BaseViewHolder
|
||||||
import org.oxycblt.auxio.recycler.ClickListener
|
import org.oxycblt.auxio.recycler.ClickListener
|
||||||
|
import org.oxycblt.auxio.recycler.DiffCallback
|
||||||
|
|
||||||
class AlbumAdapter(
|
class AlbumAdapter(
|
||||||
private val data: List<Album>,
|
|
||||||
private val listener: ClickListener<Album>
|
private val listener: ClickListener<Album>
|
||||||
) : RecyclerView.Adapter<AlbumAdapter.ViewHolder>() {
|
) : ListAdapter<Album, AlbumAdapter.ViewHolder>(DiffCallback()) {
|
||||||
|
|
||||||
override fun getItemCount(): Int = data.size
|
|
||||||
|
|
||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
||||||
return ViewHolder(
|
return ViewHolder(
|
||||||
|
@ -22,7 +20,7 @@ class AlbumAdapter(
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||||
holder.bind(data[position])
|
holder.bind(getItem(position))
|
||||||
}
|
}
|
||||||
|
|
||||||
inner class ViewHolder(
|
inner class ViewHolder(
|
||||||
|
|
|
@ -9,10 +9,11 @@ import org.oxycblt.auxio.recycler.BaseViewHolder
|
||||||
import org.oxycblt.auxio.recycler.ClickListener
|
import org.oxycblt.auxio.recycler.ClickListener
|
||||||
|
|
||||||
class ArtistAdapter(
|
class ArtistAdapter(
|
||||||
private val data: List<Artist>,
|
|
||||||
private val listener: ClickListener<Artist>
|
private val listener: ClickListener<Artist>
|
||||||
) : RecyclerView.Adapter<ArtistAdapter.ViewHolder>() {
|
) : RecyclerView.Adapter<ArtistAdapter.ViewHolder>() {
|
||||||
|
|
||||||
|
private var data = listOf<Artist>()
|
||||||
|
|
||||||
override fun getItemCount(): Int = data.size
|
override fun getItemCount(): Int = data.size
|
||||||
|
|
||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
||||||
|
@ -25,6 +26,12 @@ class ArtistAdapter(
|
||||||
holder.bind(data[position])
|
holder.bind(data[position])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun updateData(newData: List<Artist>) {
|
||||||
|
data = newData
|
||||||
|
|
||||||
|
notifyDataSetChanged()
|
||||||
|
}
|
||||||
|
|
||||||
inner class ViewHolder(
|
inner class ViewHolder(
|
||||||
private val binding: ItemArtistBinding
|
private val binding: ItemArtistBinding
|
||||||
) : BaseViewHolder<Artist>(binding, listener) {
|
) : BaseViewHolder<Artist>(binding, listener) {
|
||||||
|
|
|
@ -2,18 +2,16 @@ package org.oxycblt.auxio.library.adapters
|
||||||
|
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.ListAdapter
|
||||||
import org.oxycblt.auxio.databinding.ItemGenreBinding
|
import org.oxycblt.auxio.databinding.ItemGenreBinding
|
||||||
import org.oxycblt.auxio.music.Genre
|
import org.oxycblt.auxio.music.Genre
|
||||||
import org.oxycblt.auxio.recycler.BaseViewHolder
|
import org.oxycblt.auxio.recycler.BaseViewHolder
|
||||||
import org.oxycblt.auxio.recycler.ClickListener
|
import org.oxycblt.auxio.recycler.ClickListener
|
||||||
|
import org.oxycblt.auxio.recycler.DiffCallback
|
||||||
|
|
||||||
class GenreAdapter(
|
class GenreAdapter(
|
||||||
private val data: List<Genre>,
|
|
||||||
private val listener: ClickListener<Genre>
|
private val listener: ClickListener<Genre>
|
||||||
) : RecyclerView.Adapter<GenreAdapter.ViewHolder>() {
|
) : ListAdapter<Genre, GenreAdapter.ViewHolder>(DiffCallback()) {
|
||||||
|
|
||||||
override fun getItemCount(): Int = data.size
|
|
||||||
|
|
||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
||||||
return ViewHolder(
|
return ViewHolder(
|
||||||
|
@ -22,7 +20,7 @@ class GenreAdapter(
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||||
holder.bind(data[position])
|
holder.bind(getItem(position))
|
||||||
}
|
}
|
||||||
|
|
||||||
inner class ViewHolder(
|
inner class ViewHolder(
|
||||||
|
|
|
@ -21,7 +21,11 @@ class MusicSorter(
|
||||||
sortAlbumsIntoArtists()
|
sortAlbumsIntoArtists()
|
||||||
sortArtistsIntoGenres()
|
sortArtistsIntoGenres()
|
||||||
|
|
||||||
finalizeMusic()
|
// Remove genre duplicates at the end, as duplicate genres can be added during
|
||||||
|
// the sorting process as well.
|
||||||
|
genres = genres.distinctBy {
|
||||||
|
it.name
|
||||||
|
}.toMutableList()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun sortSongsIntoAlbums() {
|
private fun sortSongsIntoAlbums() {
|
||||||
|
@ -155,25 +159,4 @@ class MusicSorter(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Finalize music
|
|
||||||
private fun finalizeMusic() {
|
|
||||||
// Remove genre duplicates now, as duplicate genres can be added during the sorting process.
|
|
||||||
genres = genres.distinctBy {
|
|
||||||
it.name
|
|
||||||
}.toMutableList()
|
|
||||||
|
|
||||||
// Then finally sort the music
|
|
||||||
genres.sortWith(
|
|
||||||
compareBy(String.CASE_INSENSITIVE_ORDER, { it.name })
|
|
||||||
)
|
|
||||||
|
|
||||||
artists.sortWith(
|
|
||||||
compareBy(String.CASE_INSENSITIVE_ORDER, { it.name })
|
|
||||||
)
|
|
||||||
|
|
||||||
albums.sortWith(
|
|
||||||
compareBy(String.CASE_INSENSITIVE_ORDER, { it.name })
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,11 +3,7 @@ package org.oxycblt.auxio.recycler
|
||||||
import androidx.databinding.ViewDataBinding
|
import androidx.databinding.ViewDataBinding
|
||||||
import androidx.recyclerview.widget.DiffUtil
|
import androidx.recyclerview.widget.DiffUtil
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
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.BaseModel
|
||||||
import org.oxycblt.auxio.music.Song
|
|
||||||
|
|
||||||
// RecyclerView click listener
|
// RecyclerView click listener
|
||||||
class ClickListener<T>(val onClick: (T) -> Unit)
|
class ClickListener<T>(val onClick: (T) -> Unit)
|
||||||
|
@ -44,44 +40,3 @@ abstract class BaseViewHolder<T : BaseModel>(
|
||||||
|
|
||||||
abstract fun onBind(model: T)
|
abstract fun onBind(model: T)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sorting modes
|
|
||||||
enum class SortMode(val iconRes: Int) {
|
|
||||||
// Icons for each mode are assigned to the enums themselves
|
|
||||||
NONE(R.drawable.ic_sort_alpha_down),
|
|
||||||
ALPHA_UP(R.drawable.ic_sort_alpha_up),
|
|
||||||
ALPHA_DOWN(R.drawable.ic_sort_alpha_down),
|
|
||||||
NUMERIC_UP(R.drawable.ic_sort_numeric_up),
|
|
||||||
NUMERIC_DOWN(R.drawable.ic_sort_numeric_down);
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
// Sort comparators are different for each music model, so they are static maps instead.
|
|
||||||
val songSortComparators = mapOf<SortMode, Comparator<Song>>(
|
|
||||||
NUMERIC_DOWN to compareBy { it.track },
|
|
||||||
NUMERIC_UP to compareByDescending { it.track }
|
|
||||||
)
|
|
||||||
|
|
||||||
val albumSortComparators = mapOf<SortMode, Comparator<Album>>(
|
|
||||||
NUMERIC_DOWN to compareByDescending { it.year },
|
|
||||||
NUMERIC_UP to compareBy { it.year },
|
|
||||||
|
|
||||||
// Alphabetic sorting needs to be case-insensitive
|
|
||||||
ALPHA_DOWN to compareByDescending(
|
|
||||||
String.CASE_INSENSITIVE_ORDER
|
|
||||||
) { it.name },
|
|
||||||
ALPHA_UP to compareBy(
|
|
||||||
String.CASE_INSENSITIVE_ORDER
|
|
||||||
) { it.name }
|
|
||||||
)
|
|
||||||
|
|
||||||
val artistSortComparators = mapOf<SortMode, Comparator<Artist>>(
|
|
||||||
// Alphabetic sorting needs to be case-insensitive
|
|
||||||
ALPHA_DOWN to compareBy(
|
|
||||||
String.CASE_INSENSITIVE_ORDER
|
|
||||||
) { it.name },
|
|
||||||
ALPHA_UP to compareByDescending(
|
|
||||||
String.CASE_INSENSITIVE_ORDER
|
|
||||||
) { it.name }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
89
app/src/main/java/org/oxycblt/auxio/recycler/SortMode.kt
Normal file
89
app/src/main/java/org/oxycblt/auxio/recycler/SortMode.kt
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
package org.oxycblt.auxio.recycler
|
||||||
|
|
||||||
|
import org.oxycblt.auxio.R
|
||||||
|
import org.oxycblt.auxio.music.Album
|
||||||
|
import org.oxycblt.auxio.music.Artist
|
||||||
|
import org.oxycblt.auxio.music.Genre
|
||||||
|
import org.oxycblt.auxio.music.Song
|
||||||
|
|
||||||
|
// Sorting modes
|
||||||
|
enum class SortMode(val iconRes: Int) {
|
||||||
|
// Icons for each mode are assigned to the enums themselves
|
||||||
|
NONE(R.drawable.ic_sort_none),
|
||||||
|
ALPHA_UP(R.drawable.ic_sort_alpha_up),
|
||||||
|
ALPHA_DOWN(R.drawable.ic_sort_alpha_down),
|
||||||
|
NUMERIC_UP(R.drawable.ic_sort_numeric_up),
|
||||||
|
NUMERIC_DOWN(R.drawable.ic_sort_numeric_down);
|
||||||
|
|
||||||
|
fun getSortedGenreList(list: List<Genre>): List<Genre> {
|
||||||
|
return when (this) {
|
||||||
|
ALPHA_UP -> list.sortedWith(
|
||||||
|
compareByDescending(String.CASE_INSENSITIVE_ORDER) { it.name }
|
||||||
|
)
|
||||||
|
|
||||||
|
ALPHA_DOWN -> list.sortedWith(
|
||||||
|
compareBy(String.CASE_INSENSITIVE_ORDER) { it.name }
|
||||||
|
)
|
||||||
|
|
||||||
|
else -> list
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getSortedArtistList(list: List<Artist>): List<Artist> {
|
||||||
|
return when (this) {
|
||||||
|
ALPHA_UP -> list.sortedWith(
|
||||||
|
compareByDescending(String.CASE_INSENSITIVE_ORDER) { it.name }
|
||||||
|
)
|
||||||
|
|
||||||
|
ALPHA_DOWN -> list.sortedWith(
|
||||||
|
compareBy(String.CASE_INSENSITIVE_ORDER) { it.name }
|
||||||
|
)
|
||||||
|
|
||||||
|
else -> list
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getSortedAlbumList(list: List<Album>): List<Album> {
|
||||||
|
return when (this) {
|
||||||
|
ALPHA_UP -> list.sortedWith(
|
||||||
|
compareByDescending(String.CASE_INSENSITIVE_ORDER) { it.name }
|
||||||
|
)
|
||||||
|
|
||||||
|
ALPHA_DOWN -> list.sortedWith(
|
||||||
|
compareBy(String.CASE_INSENSITIVE_ORDER) { it.name }
|
||||||
|
)
|
||||||
|
|
||||||
|
NUMERIC_UP -> list.sortedBy { it.year }
|
||||||
|
NUMERIC_DOWN -> list.sortedByDescending { it.year }
|
||||||
|
|
||||||
|
else -> list
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getSortedSongList(list: List<Song>): List<Song> {
|
||||||
|
return when (this) {
|
||||||
|
ALPHA_UP -> list.sortedWith(
|
||||||
|
compareByDescending(String.CASE_INSENSITIVE_ORDER) { it.name }
|
||||||
|
)
|
||||||
|
|
||||||
|
ALPHA_DOWN -> list.sortedWith(
|
||||||
|
compareBy(String.CASE_INSENSITIVE_ORDER) { it.name }
|
||||||
|
)
|
||||||
|
|
||||||
|
NUMERIC_UP -> list.sortedWith(compareByDescending { it.track })
|
||||||
|
NUMERIC_DOWN -> list.sortedWith(compareBy { it.track })
|
||||||
|
|
||||||
|
else -> list
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun toMenuId(): Int {
|
||||||
|
return when (this) {
|
||||||
|
NONE -> R.id.sort_none
|
||||||
|
ALPHA_UP -> R.id.sort_alpha_up
|
||||||
|
ALPHA_DOWN -> R.id.sort_alpha_down
|
||||||
|
|
||||||
|
else -> R.id.sort_none
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
11
app/src/main/res/drawable/ic_sort_none.xml
Normal file
11
app/src/main/res/drawable/ic_sort_none.xml
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24"
|
||||||
|
android:tint="?attr/colorPrimary">
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M3,18h6v-2L3,16v2zM3,6v2h18L21,6L3,6zM3,13h12v-2L3,11v2z"/>
|
||||||
|
</vector>
|
|
@ -7,7 +7,8 @@
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
android:animateLayoutChanges="true">
|
android:animateLayoutChanges="true"
|
||||||
|
android:descendantFocusability="blocksDescendants">
|
||||||
|
|
||||||
<androidx.appcompat.widget.Toolbar
|
<androidx.appcompat.widget.Toolbar
|
||||||
android:id="@+id/library_toolbar"
|
android:id="@+id/library_toolbar"
|
||||||
|
|
18
app/src/main/res/menu/menu_library.xml
Normal file
18
app/src/main/res/menu/menu_library.xml
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item
|
||||||
|
android:id="@+id/sort_none"
|
||||||
|
android:icon="@drawable/ic_sort_none"
|
||||||
|
android:title="@string/label_sort_none"
|
||||||
|
android:contentDescription="@string/description_sort_none"/>
|
||||||
|
<item
|
||||||
|
android:id="@+id/sort_alpha_down"
|
||||||
|
android:icon="@drawable/ic_sort_alpha_down"
|
||||||
|
android:title="@string/label_sort_alpha_down"
|
||||||
|
android:contentDescription="@string/description_sort_alpha_down"/>
|
||||||
|
<item
|
||||||
|
android:id="@+id/sort_alpha_up"
|
||||||
|
android:icon="@drawable/ic_sort_alpha_up"
|
||||||
|
android:title="@string/label_sort_alpha_up"
|
||||||
|
android:contentDescription="@string/description_sort_alpha_up"/>
|
||||||
|
</menu>
|
|
@ -73,7 +73,7 @@
|
||||||
android:name="albumId"
|
android:name="albumId"
|
||||||
app:argType="long" />
|
app:argType="long" />
|
||||||
<argument
|
<argument
|
||||||
android:name="fromLibrary"
|
android:name="enableParentNav"
|
||||||
app:argType="boolean" />
|
app:argType="boolean" />
|
||||||
<action
|
<action
|
||||||
android:id="@+id/action_show_parent_artist"
|
android:id="@+id/action_show_parent_artist"
|
||||||
|
|
|
@ -2,35 +2,48 @@
|
||||||
<resources>
|
<resources>
|
||||||
<string name="app_name">Auxio</string>
|
<string name="app_name">Auxio</string>
|
||||||
|
|
||||||
|
<!-- Title Namespace | Toolbar titles -->
|
||||||
<string name="title_library_fragment">Library</string>
|
<string name="title_library_fragment">Library</string>
|
||||||
<string name="title_all_songs">All Songs</string>
|
<string name="title_all_songs">All Songs</string>
|
||||||
|
|
||||||
|
<!-- Error Namespace | Error Labels -->
|
||||||
<string name="error_no_music">No music found.</string>
|
<string name="error_no_music">No music found.</string>
|
||||||
<string name="error_music_load_failed">Music loading failed.</string>
|
<string name="error_music_load_failed">Music loading failed.</string>
|
||||||
<string name="error_no_perms">Permissions to read storage are needed.</string>
|
<string name="error_no_perms">Permissions to read storage are needed.</string>
|
||||||
|
|
||||||
|
<!-- Label Namespace | Static Labels -->
|
||||||
<string name="label_retry">Retry</string>
|
<string name="label_retry">Retry</string>
|
||||||
<string name="label_grant">Grant</string>
|
<string name="label_grant">Grant</string>
|
||||||
<string name="label_artists">Artists</string>
|
<string name="label_artists">Artists</string>
|
||||||
<string name="label_albums">Albums</string>
|
<string name="label_albums">Albums</string>
|
||||||
<string name="label_songs">Songs</string>
|
<string name="label_songs">Songs</string>
|
||||||
|
<string name="label_sort_none">Default</string>
|
||||||
|
<string name="label_sort_alpha_down">A-Z</string>
|
||||||
|
<string name="label_sort_alpha_up">Z-A</string>
|
||||||
|
|
||||||
|
<!-- Description Namespace | Accessibility Strings -->
|
||||||
<string name="description_album_cover">Album Cover for %s</string>
|
<string name="description_album_cover">Album Cover for %s</string>
|
||||||
<string name="description_artist_image">Artist Image for %s</string>
|
<string name="description_artist_image">Artist Image for %s</string>
|
||||||
<string name="description_genre_image">Genre Image for %s</string>
|
<string name="description_genre_image">Genre Image for %s</string>
|
||||||
<string name="description_track_number">Track %s</string>
|
<string name="description_track_number">Track %s</string>
|
||||||
<string name="description_error">Error</string>
|
<string name="description_error">Error</string>
|
||||||
<string name="description_sort_button">Change Sorting Mode</string>
|
<string name="description_sort_button">Change Sorting Mode</string>
|
||||||
|
<string name="description_sort_none">Default Sort Order</string>
|
||||||
|
<string name="description_sort_alpha_down">Sort from A to Z</string>
|
||||||
|
<string name="description_sort_alpha_up">Sort from Z to A</string>
|
||||||
|
|
||||||
|
<!-- Placeholder Namespace | Placeholder values -->
|
||||||
<string name="placeholder_genre">Unknown Genre</string>
|
<string name="placeholder_genre">Unknown Genre</string>
|
||||||
<string name="placeholder_artist">Unknown Artist</string>
|
<string name="placeholder_artist">Unknown Artist</string>
|
||||||
<string name="placeholder_album">Unknown Album</string>
|
<string name="placeholder_album">Unknown Album</string>
|
||||||
<string name="placeholder_no_date">No Date</string>
|
<string name="placeholder_no_date">No Date</string>
|
||||||
|
|
||||||
|
<!-- Format Namespace | Value formatting -->
|
||||||
<string name="format_info">%1$s / %2$s</string>
|
<string name="format_info">%1$s / %2$s</string>
|
||||||
<string name="format_double_info">%1$s / %2$s / %3$s</string>
|
<string name="format_double_info">%1$s / %2$s / %3$s</string>
|
||||||
<string name="format_double_counts">%1$s, %2$s</string>
|
<string name="format_double_counts">%1$s, %2$s</string>
|
||||||
|
|
||||||
|
<!-- Format Namespace | Value formatting with plurals -->
|
||||||
<plurals name="format_song_count">
|
<plurals name="format_song_count">
|
||||||
<item quantity="one">%s Song</item>
|
<item quantity="one">%s Song</item>
|
||||||
<item quantity="other">%s Songs</item>
|
<item quantity="other">%s Songs</item>
|
||||||
|
|
Loading…
Reference in a new issue