Add sorting to AlbumDetailFragment
Add sorting to AlbumDetailFragment. Ignore the other commit.
This commit is contained in:
parent
35e186d8a9
commit
35814c5b40
10 changed files with 137 additions and 55 deletions
|
@ -21,8 +21,8 @@ import org.oxycblt.auxio.theme.toColor
|
|||
class MainFragment : Fragment() {
|
||||
private val shownFragments = listOf(0, 1)
|
||||
|
||||
private lateinit var libraryFragment: LibraryFragment
|
||||
private lateinit var songsFragment: SongsFragment
|
||||
private val libraryFragment: LibraryFragment by lazy { LibraryFragment() }
|
||||
private val songsFragment: SongsFragment by lazy { SongsFragment() }
|
||||
|
||||
private val tabIcons = listOf(
|
||||
R.drawable.ic_library,
|
||||
|
@ -81,21 +81,10 @@ class MainFragment : Fragment() {
|
|||
|
||||
private fun fragmentAt(position: Int): Fragment {
|
||||
return when (position) {
|
||||
0 -> {
|
||||
if (!::libraryFragment.isInitialized) {
|
||||
libraryFragment = LibraryFragment()
|
||||
}
|
||||
0 -> libraryFragment
|
||||
1 -> songsFragment
|
||||
|
||||
libraryFragment
|
||||
}
|
||||
|
||||
else -> {
|
||||
if (!::songsFragment.isInitialized) {
|
||||
songsFragment = SongsFragment()
|
||||
}
|
||||
|
||||
songsFragment
|
||||
}
|
||||
else -> libraryFragment
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@ import org.oxycblt.auxio.databinding.FragmentAlbumDetailBinding
|
|||
import org.oxycblt.auxio.detail.adapters.DetailSongAdapter
|
||||
import org.oxycblt.auxio.music.MusicViewModel
|
||||
import org.oxycblt.auxio.recycler.ClickListener
|
||||
import org.oxycblt.auxio.recycler.SortMode
|
||||
import org.oxycblt.auxio.theme.applyDivider
|
||||
|
||||
class AlbumDetailFragment : Fragment() {
|
||||
|
@ -39,7 +40,6 @@ class AlbumDetailFragment : Fragment() {
|
|||
}
|
||||
|
||||
val songAdapter = DetailSongAdapter(
|
||||
detailModel.currentAlbum!!.songs,
|
||||
ClickListener {
|
||||
Log.d(this::class.simpleName, it.name)
|
||||
}
|
||||
|
@ -73,6 +73,23 @@ class AlbumDetailFragment : Fragment() {
|
|||
binding.artistName.setBackgroundResource(R.drawable.ripple)
|
||||
}
|
||||
|
||||
detailModel.albumSortMode.observe(viewLifecycleOwner) { mode ->
|
||||
// Update the current sort icon
|
||||
binding.sortButton.setImageResource(mode.iconRes)
|
||||
|
||||
// Then update the sort mode of the album adapter.
|
||||
songAdapter.submitList(
|
||||
detailModel.currentAlbum!!.songs.sortedWith(
|
||||
SortMode.songSortComparators.getOrDefault(
|
||||
mode,
|
||||
|
||||
// If any invalid value is given, just default to the normal sort order.
|
||||
compareByDescending { it.track }
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
Log.d(this::class.simpleName, "Fragment created.")
|
||||
|
||||
return binding.root
|
||||
|
|
|
@ -14,6 +14,7 @@ import org.oxycblt.auxio.detail.adapters.DetailAlbumAdapter
|
|||
import org.oxycblt.auxio.music.MusicViewModel
|
||||
import org.oxycblt.auxio.music.models.Album
|
||||
import org.oxycblt.auxio.recycler.ClickListener
|
||||
import org.oxycblt.auxio.recycler.SortMode
|
||||
import org.oxycblt.auxio.theme.applyDivider
|
||||
|
||||
class ArtistDetailFragment : Fragment() {
|
||||
|
@ -38,19 +39,36 @@ class ArtistDetailFragment : Fragment() {
|
|||
}
|
||||
|
||||
val albumAdapter = DetailAlbumAdapter(
|
||||
detailModel.currentArtist!!.albums,
|
||||
ClickListener {
|
||||
navToAlbum(it)
|
||||
}
|
||||
)
|
||||
|
||||
binding.lifecycleOwner = this
|
||||
binding.detailModel = detailModel
|
||||
binding.artist = detailModel.currentArtist!!
|
||||
|
||||
binding.albumRecycler.adapter = albumAdapter
|
||||
binding.albumRecycler.applyDivider()
|
||||
binding.albumRecycler.setHasFixedSize(true)
|
||||
|
||||
detailModel.artistSortMode.observe(viewLifecycleOwner) { mode ->
|
||||
// Update the current sort icon
|
||||
binding.sortButton.setImageResource(mode.iconRes)
|
||||
|
||||
// Then update the sort mode of the album adapter.
|
||||
albumAdapter.submitList(
|
||||
detailModel.currentArtist!!.albums.sortedWith(
|
||||
SortMode.albumSortComparators.getOrDefault(
|
||||
mode,
|
||||
|
||||
// If any invalid value is given, just default to the normal sort order.
|
||||
compareByDescending { it.year }
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
Log.d(this::class.simpleName, "Fragment created.")
|
||||
|
||||
return binding.root
|
||||
|
|
|
@ -5,6 +5,7 @@ import androidx.lifecycle.MutableLiveData
|
|||
import androidx.lifecycle.ViewModel
|
||||
import org.oxycblt.auxio.music.models.Album
|
||||
import org.oxycblt.auxio.music.models.Artist
|
||||
import org.oxycblt.auxio.recycler.SortMode
|
||||
|
||||
class DetailViewModel : ViewModel() {
|
||||
var isAlreadyNavigating = false
|
||||
|
@ -12,6 +13,12 @@ class DetailViewModel : ViewModel() {
|
|||
private val mNavToParentArtist = MutableLiveData<Boolean>()
|
||||
val navToParentArtist: LiveData<Boolean> get() = mNavToParentArtist
|
||||
|
||||
private val mArtistSortMode = MutableLiveData(SortMode.NUMERIC_DOWN)
|
||||
val artistSortMode: LiveData<SortMode> get() = mArtistSortMode
|
||||
|
||||
private val mAlbumSortMode = MutableLiveData(SortMode.NUMERIC_DOWN)
|
||||
val albumSortMode: LiveData<SortMode> get() = mAlbumSortMode
|
||||
|
||||
var currentArtist: Artist? = null
|
||||
var currentAlbum: Album? = null
|
||||
|
||||
|
@ -22,4 +29,24 @@ class DetailViewModel : ViewModel() {
|
|||
fun doneWithNavToParent() {
|
||||
mNavToParentArtist.value = false
|
||||
}
|
||||
|
||||
fun incrementArtistSortMode() {
|
||||
mArtistSortMode.value = when (mArtistSortMode.value) {
|
||||
SortMode.NUMERIC_DOWN -> SortMode.NUMERIC_UP
|
||||
SortMode.NUMERIC_UP -> SortMode.ALPHA_DOWN
|
||||
SortMode.ALPHA_DOWN -> SortMode.ALPHA_UP
|
||||
SortMode.ALPHA_UP -> SortMode.NUMERIC_DOWN
|
||||
|
||||
else -> SortMode.NUMERIC_DOWN
|
||||
}
|
||||
}
|
||||
|
||||
fun incrementAlbumSortMode() {
|
||||
mAlbumSortMode.value = when (mAlbumSortMode.value) {
|
||||
SortMode.NUMERIC_DOWN -> SortMode.NUMERIC_UP
|
||||
SortMode.NUMERIC_UP -> SortMode.NUMERIC_DOWN
|
||||
|
||||
else -> SortMode.NUMERIC_DOWN
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,17 +2,16 @@ package org.oxycblt.auxio.detail.adapters
|
|||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.ListAdapter
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import org.oxycblt.auxio.databinding.ItemAlbumBinding
|
||||
import org.oxycblt.auxio.music.models.Album
|
||||
import org.oxycblt.auxio.recycler.AlbumDiffCallback
|
||||
import org.oxycblt.auxio.recycler.ClickListener
|
||||
|
||||
class DetailAlbumAdapter(
|
||||
private val data: List<Album>,
|
||||
private val listener: ClickListener<Album>
|
||||
) : RecyclerView.Adapter<DetailAlbumAdapter.ViewHolder>() {
|
||||
|
||||
override fun getItemCount(): Int = data.size
|
||||
) : ListAdapter<Album, DetailAlbumAdapter.ViewHolder>(AlbumDiffCallback()) {
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
||||
return ViewHolder(
|
||||
|
@ -21,7 +20,7 @@ class DetailAlbumAdapter(
|
|||
}
|
||||
|
||||
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||
holder.bind(data[position])
|
||||
holder.bind(getItem(position))
|
||||
}
|
||||
|
||||
// Generic ViewHolder for an album
|
||||
|
|
|
@ -2,17 +2,16 @@ package org.oxycblt.auxio.detail.adapters
|
|||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.ListAdapter
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import org.oxycblt.auxio.databinding.ItemAlbumSongBinding
|
||||
import org.oxycblt.auxio.music.models.Song
|
||||
import org.oxycblt.auxio.recycler.ClickListener
|
||||
import org.oxycblt.auxio.recycler.SongDiffCallback
|
||||
|
||||
class DetailSongAdapter(
|
||||
private val data: List<Song>,
|
||||
private val listener: ClickListener<Song>
|
||||
) : RecyclerView.Adapter<DetailSongAdapter.ViewHolder>() {
|
||||
|
||||
override fun getItemCount(): Int = data.size
|
||||
) : ListAdapter<Song, DetailSongAdapter.ViewHolder>(SongDiffCallback()) {
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
||||
return ViewHolder(
|
||||
|
@ -21,7 +20,7 @@ class DetailSongAdapter(
|
|||
}
|
||||
|
||||
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||
holder.bind(data[position])
|
||||
holder.bind(getItem(position))
|
||||
}
|
||||
|
||||
// Generic ViewHolder for a song
|
||||
|
|
|
@ -31,10 +31,8 @@ class MusicSorter(
|
|||
|
||||
for (album in albums) {
|
||||
// Find all songs that match the current album ID to prevent any bugs w/comparing names.
|
||||
// Also sort them by their track number.
|
||||
val albumSongs = songs.filter {
|
||||
it.albumId == album.id
|
||||
}.sortedBy { it.track }
|
||||
// This cant be done with artists/genres sadly.
|
||||
val albumSongs = songs.filter { it.albumId == album.id }
|
||||
|
||||
// Then add them to the album
|
||||
for (song in albumSongs) {
|
||||
|
@ -74,10 +72,8 @@ class MusicSorter(
|
|||
val unknownAlbums = albums.toMutableList()
|
||||
|
||||
for (artist in artists) {
|
||||
// Find all albums that match the current artist name, and then sort them by year
|
||||
val artistAlbums = albums.filter {
|
||||
it.artistName == artist.name
|
||||
}.sortedByDescending { it.year }
|
||||
// Find all albums that match the current artist name
|
||||
val artistAlbums = albums.filter { it.artistName == artist.name }
|
||||
|
||||
// Then add them to the artist, along with refreshing the amount of albums
|
||||
for (album in artistAlbums) {
|
||||
|
|
|
@ -8,18 +8,7 @@ import org.oxycblt.auxio.music.models.Song
|
|||
// RecyclerView click listener
|
||||
class ClickListener<T>(val onClick: (T) -> Unit)
|
||||
|
||||
// Diff callback for albums
|
||||
class AlbumDiffCallback : DiffUtil.ItemCallback<Album>() {
|
||||
override fun areContentsTheSame(oldItem: Album, newItem: Album): Boolean {
|
||||
return oldItem.id == newItem.id
|
||||
}
|
||||
|
||||
override fun areItemsTheSame(oldItem: Album, newItem: Album): Boolean {
|
||||
return oldItem == newItem
|
||||
}
|
||||
}
|
||||
|
||||
// Diff callback for songs
|
||||
// Song Diff callback
|
||||
class SongDiffCallback : DiffUtil.ItemCallback<Song>() {
|
||||
override fun areContentsTheSame(oldItem: Song, newItem: Song): Boolean {
|
||||
return oldItem.id == newItem.id
|
||||
|
@ -30,6 +19,17 @@ class SongDiffCallback : DiffUtil.ItemCallback<Song>() {
|
|||
}
|
||||
}
|
||||
|
||||
// Album Diff callback
|
||||
class AlbumDiffCallback : DiffUtil.ItemCallback<Album>() {
|
||||
override fun areContentsTheSame(oldItem: Album, newItem: Album): Boolean {
|
||||
return oldItem.id == newItem.id
|
||||
}
|
||||
|
||||
override fun areItemsTheSame(oldItem: Album, newItem: Album): Boolean {
|
||||
return oldItem == newItem
|
||||
}
|
||||
}
|
||||
|
||||
// Sorting modes
|
||||
enum class SortMode(val iconRes: Int) {
|
||||
// Icons for each mode are assigned to the enums themselves
|
||||
|
@ -40,8 +40,12 @@ enum class SortMode(val iconRes: Int) {
|
|||
NUMERIC_DOWN(R.drawable.ic_sort_numeric_down);
|
||||
|
||||
companion object {
|
||||
// Sort comparators are different for each music model, so they are
|
||||
// static maps instead.
|
||||
// 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 },
|
||||
|
@ -54,10 +58,5 @@ enum class SortMode(val iconRes: Int) {
|
|||
String.CASE_INSENSITIVE_ORDER
|
||||
) { it.name },
|
||||
)
|
||||
|
||||
val songSortComparators = mapOf<SortMode, Comparator<Song>>(
|
||||
NUMERIC_DOWN to compareBy { it.track },
|
||||
NUMERIC_UP to compareByDescending { it.track }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -112,6 +112,23 @@
|
|||
android:textSize="16sp"
|
||||
app:layout_constraintTop_toBottomOf="@+id/album_year" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/sort_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="0dp"
|
||||
android:layout_marginTop="@dimen/margin_medium"
|
||||
android:background="@drawable/header_dividers"
|
||||
android:contentDescription="@string/description_sort_button"
|
||||
android:paddingStart="@dimen/padding_medium"
|
||||
android:paddingTop="@dimen/padding_small"
|
||||
android:paddingEnd="@dimen/margin_medium"
|
||||
android:paddingBottom="@dimen/padding_small"
|
||||
android:onClick="@{() -> detailModel.incrementAlbumSortMode()}"
|
||||
tools:src="@drawable/ic_sort_numeric_down"
|
||||
app:layout_constraintBottom_toTopOf="@+id/song_recycler"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/album_year" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/song_recycler"
|
||||
android:layout_width="match_parent"
|
||||
|
|
|
@ -8,6 +8,10 @@
|
|||
<variable
|
||||
name="artist"
|
||||
type="org.oxycblt.auxio.music.models.Artist" />
|
||||
|
||||
<variable
|
||||
name="detailModel"
|
||||
type="org.oxycblt.auxio.detail.DetailViewModel" />
|
||||
</data>
|
||||
|
||||
<LinearLayout
|
||||
|
@ -106,6 +110,23 @@
|
|||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/artist_counts" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/sort_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="0dp"
|
||||
android:layout_marginTop="@dimen/margin_medium"
|
||||
android:background="@drawable/header_dividers"
|
||||
android:contentDescription="@string/description_sort_button"
|
||||
android:paddingStart="@dimen/padding_medium"
|
||||
android:paddingTop="@dimen/padding_small"
|
||||
android:paddingEnd="@dimen/margin_medium"
|
||||
android:paddingBottom="@dimen/padding_small"
|
||||
android:onClick="@{() -> detailModel.incrementArtistSortMode()}"
|
||||
tools:src="@drawable/ic_sort_numeric_down"
|
||||
app:layout_constraintBottom_toTopOf="@+id/album_recycler"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/artist_counts" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/album_recycler"
|
||||
android:layout_width="match_parent"
|
||||
|
|
Loading…
Reference in a new issue