diff --git a/app/src/main/java/org/oxycblt/auxio/GenericUtils.kt b/app/src/main/java/org/oxycblt/auxio/GenericUtils.kt deleted file mode 100644 index 69cda0ed5..000000000 --- a/app/src/main/java/org/oxycblt/auxio/GenericUtils.kt +++ /dev/null @@ -1,4 +0,0 @@ -package org.oxycblt.auxio - -// RecyclerView click listener -class ClickListener(val onClick: (T) -> Unit) \ No newline at end of file diff --git a/app/src/main/java/org/oxycblt/auxio/detail/AlbumDetailFragment.kt b/app/src/main/java/org/oxycblt/auxio/detail/AlbumDetailFragment.kt index 702947891..b5381066e 100644 --- a/app/src/main/java/org/oxycblt/auxio/detail/AlbumDetailFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/detail/AlbumDetailFragment.kt @@ -9,7 +9,7 @@ import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.navArgs -import org.oxycblt.auxio.ClickListener +import org.oxycblt.auxio.reycler.ClickListener import org.oxycblt.auxio.R import org.oxycblt.auxio.databinding.FragmentAlbumDetailBinding import org.oxycblt.auxio.detail.adapters.DetailSongAdapter @@ -75,4 +75,10 @@ class AlbumDetailFragment : Fragment() { return binding.root } + + override fun onDestroy() { + super.onDestroy() + + detailModel.currentAlbum = null + } } \ No newline at end of file diff --git a/app/src/main/java/org/oxycblt/auxio/detail/ArtistDetailFragment.kt b/app/src/main/java/org/oxycblt/auxio/detail/ArtistDetailFragment.kt index 69eb8e2e3..87a3a0447 100644 --- a/app/src/main/java/org/oxycblt/auxio/detail/ArtistDetailFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/detail/ArtistDetailFragment.kt @@ -9,12 +9,14 @@ import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.navArgs -import org.oxycblt.auxio.ClickListener +import org.oxycblt.auxio.reycler.ClickListener import org.oxycblt.auxio.databinding.FragmentArtistDetailBinding import org.oxycblt.auxio.detail.adapters.DetailAlbumAdapter import org.oxycblt.auxio.music.MusicViewModel import org.oxycblt.auxio.music.models.Album +import org.oxycblt.auxio.reycler.SortMode import org.oxycblt.auxio.theme.applyDivider +import java.util.Comparator class ArtistDetailFragment : Fragment() { @@ -37,20 +39,37 @@ class ArtistDetailFragment : Fragment() { }!! } - val artistAdapter = DetailAlbumAdapter( - detailModel.currentArtist!!.albums, + val albumAdapter = DetailAlbumAdapter( ClickListener { navToAlbum(it) } ) binding.lifecycleOwner = this + binding.detailModel = detailModel binding.artist = detailModel.currentArtist!! - binding.albumRecycler.adapter = artistAdapter + 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 diff --git a/app/src/main/java/org/oxycblt/auxio/detail/DetailViewModel.kt b/app/src/main/java/org/oxycblt/auxio/detail/DetailViewModel.kt index 4667e1162..4b0edf3c8 100644 --- a/app/src/main/java/org/oxycblt/auxio/detail/DetailViewModel.kt +++ b/app/src/main/java/org/oxycblt/auxio/detail/DetailViewModel.kt @@ -1,10 +1,12 @@ package org.oxycblt.auxio.detail +import android.util.Log import androidx.lifecycle.LiveData 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.reycler.SortMode class DetailViewModel : ViewModel() { var isAlreadyNavigating = false @@ -12,6 +14,9 @@ class DetailViewModel : ViewModel() { private val mNavToParentArtist = MutableLiveData() val navToParentArtist: LiveData get() = mNavToParentArtist + private val mArtistSortMode = MutableLiveData(SortMode.NUMERIC_DOWN) + val artistSortMode: LiveData get() = mArtistSortMode + var currentArtist: Artist? = null var currentAlbum: Album? = null @@ -22,4 +27,15 @@ class DetailViewModel : ViewModel() { fun doneWithNavToParent() { mNavToParentArtist.value = false } -} \ No newline at end of file + + 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 + } + } +} diff --git a/app/src/main/java/org/oxycblt/auxio/detail/adapters/DetailAlbumAdapter.kt b/app/src/main/java/org/oxycblt/auxio/detail/adapters/DetailAlbumAdapter.kt index 0e12fd70b..f195afcf4 100644 --- a/app/src/main/java/org/oxycblt/auxio/detail/adapters/DetailAlbumAdapter.kt +++ b/app/src/main/java/org/oxycblt/auxio/detail/adapters/DetailAlbumAdapter.kt @@ -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.ClickListener +import org.oxycblt.auxio.reycler.ClickListener import org.oxycblt.auxio.databinding.ItemAlbumBinding import org.oxycblt.auxio.music.models.Album +import org.oxycblt.auxio.reycler.DiffCallback class DetailAlbumAdapter( - private val data: List, private val listener: ClickListener -) : RecyclerView.Adapter() { - - override fun getItemCount(): Int = data.size +) : ListAdapter(DiffCallback()){ 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 diff --git a/app/src/main/java/org/oxycblt/auxio/detail/adapters/DetailSongAdapter.kt b/app/src/main/java/org/oxycblt/auxio/detail/adapters/DetailSongAdapter.kt index 4bcef03bf..83c51ca52 100644 --- a/app/src/main/java/org/oxycblt/auxio/detail/adapters/DetailSongAdapter.kt +++ b/app/src/main/java/org/oxycblt/auxio/detail/adapters/DetailSongAdapter.kt @@ -3,7 +3,7 @@ package org.oxycblt.auxio.detail.adapters import android.view.LayoutInflater import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView -import org.oxycblt.auxio.ClickListener +import org.oxycblt.auxio.reycler.ClickListener import org.oxycblt.auxio.databinding.ItemAlbumSongBinding import org.oxycblt.auxio.music.models.Song diff --git a/app/src/main/java/org/oxycblt/auxio/library/LibraryFragment.kt b/app/src/main/java/org/oxycblt/auxio/library/LibraryFragment.kt index ec474b08c..acad70e26 100644 --- a/app/src/main/java/org/oxycblt/auxio/library/LibraryFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/library/LibraryFragment.kt @@ -8,7 +8,7 @@ import android.view.ViewGroup import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels import androidx.navigation.fragment.findNavController -import org.oxycblt.auxio.ClickListener +import org.oxycblt.auxio.reycler.ClickListener import org.oxycblt.auxio.MainFragmentDirections import org.oxycblt.auxio.databinding.FragmentLibraryBinding import org.oxycblt.auxio.library.adapters.ArtistAdapter diff --git a/app/src/main/java/org/oxycblt/auxio/library/adapters/AlbumAdapter.kt b/app/src/main/java/org/oxycblt/auxio/library/adapters/AlbumAdapter.kt index 5ff069180..0e0cf5026 100644 --- a/app/src/main/java/org/oxycblt/auxio/library/adapters/AlbumAdapter.kt +++ b/app/src/main/java/org/oxycblt/auxio/library/adapters/AlbumAdapter.kt @@ -3,7 +3,7 @@ package org.oxycblt.auxio.library.adapters import android.view.LayoutInflater import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView -import org.oxycblt.auxio.ClickListener +import org.oxycblt.auxio.reycler.ClickListener import org.oxycblt.auxio.databinding.ItemAlbumBinding import org.oxycblt.auxio.music.models.Album diff --git a/app/src/main/java/org/oxycblt/auxio/library/adapters/ArtistAdapter.kt b/app/src/main/java/org/oxycblt/auxio/library/adapters/ArtistAdapter.kt index 00a41091c..a71f6172d 100644 --- a/app/src/main/java/org/oxycblt/auxio/library/adapters/ArtistAdapter.kt +++ b/app/src/main/java/org/oxycblt/auxio/library/adapters/ArtistAdapter.kt @@ -3,7 +3,7 @@ package org.oxycblt.auxio.library.adapters import android.view.LayoutInflater import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView -import org.oxycblt.auxio.ClickListener +import org.oxycblt.auxio.reycler.ClickListener import org.oxycblt.auxio.databinding.ItemArtistBinding import org.oxycblt.auxio.music.models.Artist diff --git a/app/src/main/java/org/oxycblt/auxio/music/MusicUtils.kt b/app/src/main/java/org/oxycblt/auxio/music/MusicUtils.kt index cb59da50d..98fa419e4 100644 --- a/app/src/main/java/org/oxycblt/auxio/music/MusicUtils.kt +++ b/app/src/main/java/org/oxycblt/auxio/music/MusicUtils.kt @@ -86,6 +86,7 @@ fun TextView.bindArtistCounts(artist: Artist) { text = context.getString(R.string.format_double_counts, albums, songs) } + // Get the artist genre. // TODO: Stub, add option to list all genres instead of just the most prominent @BindingAdapter("artistGenre") diff --git a/app/src/main/java/org/oxycblt/auxio/music/MusicViewModel.kt b/app/src/main/java/org/oxycblt/auxio/music/MusicViewModel.kt index c7d322d2e..0e915baae 100644 --- a/app/src/main/java/org/oxycblt/auxio/music/MusicViewModel.kt +++ b/app/src/main/java/org/oxycblt/auxio/music/MusicViewModel.kt @@ -21,6 +21,8 @@ import org.oxycblt.auxio.music.processing.MusicLoaderResponse import org.oxycblt.auxio.music.processing.MusicSorter // ViewModel for music storage. May also be a god object. +// TODO: Move the lists from the music models to a map, would +// make some systems a lot less hacky and maybe decrease memory usage. class MusicViewModel(private val app: Application) : ViewModel() { // Coroutine diff --git a/app/src/main/java/org/oxycblt/auxio/music/coil/ArtistImageFetcher.kt b/app/src/main/java/org/oxycblt/auxio/music/coil/ArtistImageFetcher.kt index 6e0750348..6b101e58e 100644 --- a/app/src/main/java/org/oxycblt/auxio/music/coil/ArtistImageFetcher.kt +++ b/app/src/main/java/org/oxycblt/auxio/music/coil/ArtistImageFetcher.kt @@ -61,6 +61,8 @@ class ArtistImageFetcher(private val context: Context) : Fetcher> { var y = 0 val increment = MOSAIC_BITMAP_SIZE / 2 + // For each stream, create a bitmap scaled to 1/4th of the mosaics combined size + // and place it on a corner of the canvas. for (stream in streams) { val bitmap = Bitmap.createScaledBitmap( BitmapFactory.decodeStream(stream), diff --git a/app/src/main/java/org/oxycblt/auxio/music/processing/MusicLoader.kt b/app/src/main/java/org/oxycblt/auxio/music/processing/MusicLoader.kt index 4e6b4f78f..34c2e8bf0 100644 --- a/app/src/main/java/org/oxycblt/auxio/music/processing/MusicLoader.kt +++ b/app/src/main/java/org/oxycblt/auxio/music/processing/MusicLoader.kt @@ -26,7 +26,6 @@ class MusicLoader( private val artistPlaceholder: String, private val albumPlaceholder: String, ) { - var genres = mutableListOf() var artists = mutableListOf() var albums = mutableListOf() @@ -111,14 +110,15 @@ class MusicLoader( private fun loadArtists() { Log.d(this::class.simpleName, "Starting artist search...") - // To associate artists with their genres, a new cursor is - // created with all the artists of that type. + // Iterate through the artists for each loaded genre, and then add the genre + // with the artist. + // This is only done because using GENRE_NAME for songs is broken and has been for years. for (genre in genres) { artistCursor = resolver.query( Genres.Members.getContentUri("external", genre.id), arrayOf( Artists._ID, // 0 - Artists.ARTIST // 1 + Artists.ARTIST, // 1 ), null, null, Artists.DEFAULT_SORT_ORDER @@ -227,7 +227,7 @@ class MusicLoader( Media.TITLE, // 2 Media.ALBUM_ID, // 3 Media.TRACK, // 4 - Media.DURATION // 5 + Media.DURATION, // 5 ), Media.IS_MUSIC + "=1", null, Media.DEFAULT_SORT_ORDER diff --git a/app/src/main/java/org/oxycblt/auxio/reycler/RecyclerUtils.kt b/app/src/main/java/org/oxycblt/auxio/reycler/RecyclerUtils.kt new file mode 100644 index 000000000..e3fc626c0 --- /dev/null +++ b/app/src/main/java/org/oxycblt/auxio/reycler/RecyclerUtils.kt @@ -0,0 +1,47 @@ +package org.oxycblt.auxio.reycler + +import androidx.recyclerview.widget.DiffUtil +import org.oxycblt.auxio.R +import org.oxycblt.auxio.music.models.Album +import org.oxycblt.auxio.music.models.Artist + +// RecyclerView click listener +class ClickListener(val onClick: (T) -> Unit) + +// Diff callback +class DiffCallback : DiffUtil.ItemCallback() { + 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 + 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 albumSortComparators = mapOf>( + NUMERIC_DOWN to compareBy { it.year }, + NUMERIC_UP to compareByDescending { 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 }, + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/org/oxycblt/auxio/songs/SongAdapter.kt b/app/src/main/java/org/oxycblt/auxio/songs/SongAdapter.kt index 04868196b..31f3214a6 100644 --- a/app/src/main/java/org/oxycblt/auxio/songs/SongAdapter.kt +++ b/app/src/main/java/org/oxycblt/auxio/songs/SongAdapter.kt @@ -3,7 +3,7 @@ package org.oxycblt.auxio.songs import android.view.LayoutInflater import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView -import org.oxycblt.auxio.ClickListener +import org.oxycblt.auxio.reycler.ClickListener import org.oxycblt.auxio.databinding.ItemSongBinding import org.oxycblt.auxio.music.models.Song diff --git a/app/src/main/java/org/oxycblt/auxio/songs/SongsFragment.kt b/app/src/main/java/org/oxycblt/auxio/songs/SongsFragment.kt index 601b2ea72..4cac2cbb1 100644 --- a/app/src/main/java/org/oxycblt/auxio/songs/SongsFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/songs/SongsFragment.kt @@ -7,7 +7,7 @@ import android.view.View import android.view.ViewGroup import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels -import org.oxycblt.auxio.ClickListener +import org.oxycblt.auxio.reycler.ClickListener import org.oxycblt.auxio.databinding.FragmentSongsBinding import org.oxycblt.auxio.music.MusicViewModel import org.oxycblt.auxio.theme.applyDivider diff --git a/app/src/main/res/drawable/ic_sort_alpha_down.xml b/app/src/main/res/drawable/ic_sort_alpha_down.xml new file mode 100644 index 000000000..bdf62d8c9 --- /dev/null +++ b/app/src/main/res/drawable/ic_sort_alpha_down.xml @@ -0,0 +1,19 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_sort_alpha_up.xml b/app/src/main/res/drawable/ic_sort_alpha_up.xml new file mode 100644 index 000000000..fa124ad70 --- /dev/null +++ b/app/src/main/res/drawable/ic_sort_alpha_up.xml @@ -0,0 +1,19 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_sort_numeric_down.xml b/app/src/main/res/drawable/ic_sort_numeric_down.xml new file mode 100644 index 000000000..ecd376513 --- /dev/null +++ b/app/src/main/res/drawable/ic_sort_numeric_down.xml @@ -0,0 +1,19 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_sort_numeric_up.xml b/app/src/main/res/drawable/ic_sort_numeric_up.xml new file mode 100644 index 000000000..95a5d6fac --- /dev/null +++ b/app/src/main/res/drawable/ic_sort_numeric_up.xml @@ -0,0 +1,19 @@ + + + + + diff --git a/app/src/main/res/layout/fragment_artist_detail.xml b/app/src/main/res/layout/fragment_artist_detail.xml index bad8fbb16..924dbd4de 100644 --- a/app/src/main/res/layout/fragment_artist_detail.xml +++ b/app/src/main/res/layout/fragment_artist_detail.xml @@ -4,10 +4,13 @@ xmlns:tools="http://schemas.android.com/tools"> - + + -