From 7ef4eb5fded9d7f1511331eb45b55c8dc021cedf Mon Sep 17 00:00:00 2001 From: OxygenCobalt Date: Mon, 28 Dec 2020 13:55:20 -0700 Subject: [PATCH] Update artist detail layout Update the artist detail layout so that it also relies on a recyclerview entirely instead of a nestedscrollview. --- .../auxio/detail/AlbumDetailFragment.kt | 2 +- .../auxio/detail/ArtistDetailFragment.kt | 31 ++-- .../auxio/detail/GenreDetailFragment.kt | 2 +- .../detail/adapters/ArtistAlbumAdapter.kt | 58 +++++++- .../auxio/detail/adapters/GenreSongAdapter.kt | 7 +- .../auxio/recycler/LinearCenterScroller.kt | 6 +- .../org/oxycblt/auxio/ui/InterfaceUtils.kt | 5 +- .../layout-land/fragment_artist_detail.xml | 133 ------------------ .../res/layout-land/item_artist_header.xml | 95 +++++++++++++ .../res/layout/fragment_artist_detail.xml | 130 ----------------- app/src/main/res/layout/fragment_detail.xml | 2 +- .../main/res/layout/item_artist_header.xml | 93 ++++++++++++ ...enre_actions.xml => menu_genre_detail.xml} | 0 app/src/main/res/navigation/nav_explore.xml | 2 +- 14 files changed, 266 insertions(+), 300 deletions(-) delete mode 100644 app/src/main/res/layout-land/fragment_artist_detail.xml create mode 100644 app/src/main/res/layout-land/item_artist_header.xml delete mode 100644 app/src/main/res/layout/fragment_artist_detail.xml create mode 100644 app/src/main/res/layout/item_artist_header.xml rename app/src/main/res/menu/{menu_genre_actions.xml => menu_genre_detail.xml} (100%) 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 1f7f4d8c5..4931f8e28 100644 --- a/app/src/main/java/org/oxycblt/auxio/detail/AlbumDetailFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/detail/AlbumDetailFragment.kt @@ -108,7 +108,7 @@ class AlbumDetailFragment : DetailFragment() { // Don't enable the sort button if there's only one song [or less] if (detailModel.currentAlbum.value!!.songs.size < 2) { - binding.albumSortButton.disable(requireContext()) + binding.albumSortButton.disable() } // If this fragment was created in order to nav to an item, then snap scroll to that item. 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 f2df44d13..1411f4e0e 100644 --- a/app/src/main/java/org/oxycblt/auxio/detail/ArtistDetailFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/detail/ArtistDetailFragment.kt @@ -9,13 +9,13 @@ import androidx.fragment.app.activityViewModels import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.navArgs import org.oxycblt.auxio.R -import org.oxycblt.auxio.databinding.FragmentArtistDetailBinding +import org.oxycblt.auxio.databinding.FragmentDetailBinding import org.oxycblt.auxio.detail.adapters.ArtistAlbumAdapter import org.oxycblt.auxio.logD import org.oxycblt.auxio.music.Artist +import org.oxycblt.auxio.music.BaseModel import org.oxycblt.auxio.music.MusicStore import org.oxycblt.auxio.playback.PlaybackViewModel -import org.oxycblt.auxio.ui.disable import org.oxycblt.auxio.ui.setupAlbumActions /** @@ -31,7 +31,7 @@ class ArtistDetailFragment : DetailFragment() { container: ViewGroup?, savedInstanceState: Bundle? ): View { - val binding = FragmentArtistDetailBinding.inflate(inflater) + val binding = FragmentDetailBinding.inflate(inflater) // If DetailViewModel isn't already storing the artist, get it from MusicStore // using the ID given by the navigation arguments @@ -46,6 +46,7 @@ class ArtistDetailFragment : DetailFragment() { } val albumAdapter = ArtistAlbumAdapter( + detailModel, viewLifecycleOwner, doOnClick = { if (!detailModel.isNavigating) { detailModel.updateNavigationStatus(true) @@ -65,11 +66,10 @@ class ArtistDetailFragment : DetailFragment() { // --- UI SETUP --- binding.lifecycleOwner = this - binding.detailModel = detailModel - binding.playbackModel = playbackModel - binding.artist = detailModel.currentArtist.value!! - binding.artistToolbar.apply { + binding.detailToolbar.apply { + inflateMenu(R.menu.menu_artist_detail) + setNavigationOnClickListener { findNavController().navigateUp() } @@ -98,12 +98,7 @@ class ArtistDetailFragment : DetailFragment() { } } - // Disable the sort button if there is only one album [Or less] - if (detailModel.currentArtist.value!!.albums.size < 2) { - binding.artistSortButton.disable(requireContext()) - } - - binding.artistAlbumRecycler.apply { + binding.detailRecycler.apply { adapter = albumAdapter setHasFixedSize(true) } @@ -113,13 +108,11 @@ class ArtistDetailFragment : DetailFragment() { detailModel.artistSortMode.observe(viewLifecycleOwner) { mode -> logD("Updating sort mode to $mode") - // Update the current sort icon - binding.artistSortButton.setImageResource(mode.iconRes) + val data = mutableListOf(detailModel.currentArtist.value!!).also { + it.addAll(mode.getSortedAlbumList(detailModel.currentArtist.value!!.albums)) + } - // Then update the sort mode of the album adapter. - albumAdapter.submitList( - mode.getSortedAlbumList(detailModel.currentArtist.value!!.albums) - ) + albumAdapter.submitList(data) } playbackModel.navToItem.observe(viewLifecycleOwner) { diff --git a/app/src/main/java/org/oxycblt/auxio/detail/GenreDetailFragment.kt b/app/src/main/java/org/oxycblt/auxio/detail/GenreDetailFragment.kt index 7918127b6..1c53bb3d9 100644 --- a/app/src/main/java/org/oxycblt/auxio/detail/GenreDetailFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/detail/GenreDetailFragment.kt @@ -49,7 +49,7 @@ class GenreDetailFragment : DetailFragment() { } val songAdapter = GenreSongAdapter( - viewLifecycleOwner, detailModel, + detailModel, viewLifecycleOwner, doOnClick = { playbackModel.playSong(it, PlaybackMode.IN_GENRE) }, diff --git a/app/src/main/java/org/oxycblt/auxio/detail/adapters/ArtistAlbumAdapter.kt b/app/src/main/java/org/oxycblt/auxio/detail/adapters/ArtistAlbumAdapter.kt index 3311411cf..f86d27478 100644 --- a/app/src/main/java/org/oxycblt/auxio/detail/adapters/ArtistAlbumAdapter.kt +++ b/app/src/main/java/org/oxycblt/auxio/detail/adapters/ArtistAlbumAdapter.kt @@ -3,35 +3,76 @@ package org.oxycblt.auxio.detail.adapters import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import androidx.lifecycle.LifecycleOwner import androidx.recyclerview.widget.ListAdapter import androidx.recyclerview.widget.RecyclerView import org.oxycblt.auxio.databinding.ItemArtistAlbumBinding +import org.oxycblt.auxio.databinding.ItemArtistHeaderBinding +import org.oxycblt.auxio.detail.DetailViewModel import org.oxycblt.auxio.music.Album +import org.oxycblt.auxio.music.Artist +import org.oxycblt.auxio.music.BaseModel import org.oxycblt.auxio.recycler.DiffCallback import org.oxycblt.auxio.recycler.viewholders.BaseViewHolder +import org.oxycblt.auxio.ui.disable /** * An adapter for displaying the [Album]s of an artist. */ class ArtistAlbumAdapter( + private val detailModel: DetailViewModel, + private val lifecycleOwner: LifecycleOwner, private val doOnClick: (data: Album) -> Unit, private val doOnLongClick: (data: Album, view: View) -> Unit, -) : ListAdapter(DiffCallback()) { +) : ListAdapter(DiffCallback()) { + + override fun getItemViewType(position: Int): Int { + return when (getItem(position)) { + is Artist -> ARTIST_HEADER_ITEM_TYPE + is Album -> ARTIST_ALBUM_ITEM_TYPE + + else -> -1 + } + } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { - return AlbumViewHolder( - ItemArtistAlbumBinding.inflate(LayoutInflater.from(parent.context)) - ) + return when (viewType) { + ARTIST_HEADER_ITEM_TYPE -> ArtistHeaderViewHolder( + ItemArtistHeaderBinding.inflate(LayoutInflater.from(parent.context)) + ) + + ARTIST_ALBUM_ITEM_TYPE -> ArtistAlbumViewHolder( + ItemArtistAlbumBinding.inflate(LayoutInflater.from(parent.context)) + ) + + else -> error("Invalid ViewHolder item type $viewType") + } } override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { when (val item = getItem(position)) { - is Album -> (holder as AlbumViewHolder).bind(item) + is Artist -> (holder as ArtistHeaderViewHolder).bind(item) + is Album -> (holder as ArtistAlbumViewHolder).bind(item) + } + } + + inner class ArtistHeaderViewHolder( + private val binding: ItemArtistHeaderBinding + ) : BaseViewHolder(binding, null, null) { + + override fun onBind(data: Artist) { + binding.artist = data + binding.detailModel = detailModel + binding.lifecycleOwner = lifecycleOwner + + if (data.albums.size < 2) { + binding.artistSortButton.disable() + } } } // Generic ViewHolder for a detail album - inner class AlbumViewHolder( + inner class ArtistAlbumViewHolder( private val binding: ItemArtistAlbumBinding, ) : BaseViewHolder(binding, doOnClick, doOnLongClick) { @@ -41,4 +82,9 @@ class ArtistAlbumAdapter( binding.albumName.requestLayout() } } + + companion object { + const val ARTIST_HEADER_ITEM_TYPE = 0xA022 + const val ARTIST_ALBUM_ITEM_TYPE = 0xA023 + } } diff --git a/app/src/main/java/org/oxycblt/auxio/detail/adapters/GenreSongAdapter.kt b/app/src/main/java/org/oxycblt/auxio/detail/adapters/GenreSongAdapter.kt index dc9cfdbbd..9f57b5f95 100644 --- a/app/src/main/java/org/oxycblt/auxio/detail/adapters/GenreSongAdapter.kt +++ b/app/src/main/java/org/oxycblt/auxio/detail/adapters/GenreSongAdapter.kt @@ -14,13 +14,14 @@ import org.oxycblt.auxio.music.Genre import org.oxycblt.auxio.music.Song import org.oxycblt.auxio.recycler.DiffCallback import org.oxycblt.auxio.recycler.viewholders.BaseViewHolder +import org.oxycblt.auxio.ui.disable /** * An adapter for displaying the [Song]s of a genre. */ class GenreSongAdapter( - private val lifecycleOwner: LifecycleOwner, private val detailModel: DetailViewModel, + private val lifecycleOwner: LifecycleOwner, private val doOnClick: (data: Song) -> Unit, private val doOnLongClick: (data: Song, view: View) -> Unit ) : ListAdapter(DiffCallback()) { @@ -62,6 +63,10 @@ class GenreSongAdapter( binding.genre = data binding.detailModel = detailModel binding.lifecycleOwner = lifecycleOwner + + if (data.songs.size < 2) { + binding.genreSortButton.disable() + } } } diff --git a/app/src/main/java/org/oxycblt/auxio/recycler/LinearCenterScroller.kt b/app/src/main/java/org/oxycblt/auxio/recycler/LinearCenterScroller.kt index 6ca0a01fe..e2870d9d6 100644 --- a/app/src/main/java/org/oxycblt/auxio/recycler/LinearCenterScroller.kt +++ b/app/src/main/java/org/oxycblt/auxio/recycler/LinearCenterScroller.kt @@ -74,7 +74,7 @@ class LinearCenterScroller(target: Int) : RecyclerView.SmoothScroller() { return calculateDeltaToFit(top, bottom, start, end) } - private fun calcDyToMakeVisible(view: View): Int { + fun calcDyToMakeVisible(view: View): Int { val manager = layoutManager ?: return 0 if (!manager.canScrollVertically()) return 0 @@ -116,9 +116,7 @@ class LinearCenterScroller(target: Int) : RecyclerView.SmoothScroller() { interimTargetDx = (TARGET_SEEK_SCROLL_DIST * scrollVector.x).toInt() interimTargetDy = (TARGET_SEEK_SCROLL_DIST * scrollVector.y).toInt() - // To avoid UI hiccups, trigger a smooth scroll to a distance little further than the - // interim target. Since we track the distance travelled in onSeekTargetStep callback, it - // won't actually scroll more than what we need. + action.update( (interimTargetDx * TARGET_SEEK_EXTRA_SCROLL_RATIO).toInt(), (interimTargetDy * TARGET_SEEK_EXTRA_SCROLL_RATIO).toInt(), diff --git a/app/src/main/java/org/oxycblt/auxio/ui/InterfaceUtils.kt b/app/src/main/java/org/oxycblt/auxio/ui/InterfaceUtils.kt index 0a674d3fd..9402b59ca 100644 --- a/app/src/main/java/org/oxycblt/auxio/ui/InterfaceUtils.kt +++ b/app/src/main/java/org/oxycblt/auxio/ui/InterfaceUtils.kt @@ -37,9 +37,8 @@ fun MenuItem.applyColor(@ColorInt color: Int) { /** * Disable an image button. - * @param context [Context] required to change the [ImageButton]s color. */ -fun ImageButton.disable(context: Context) { +fun ImageButton.disable() { if (isEnabled) { imageTintList = ColorStateList.valueOf( R.color.inactive_color.toColor(context) @@ -240,7 +239,7 @@ fun PopupMenu.setupGenreActions(genre: Genre, playbackModel: PlaybackViewModel) else -> false } } - inflateAndShow(R.menu.menu_genre_actions) + inflateAndShow(R.menu.menu_genre_detail) } /** diff --git a/app/src/main/res/layout-land/fragment_artist_detail.xml b/app/src/main/res/layout-land/fragment_artist_detail.xml deleted file mode 100644 index aef8cf65d..000000000 --- a/app/src/main/res/layout-land/fragment_artist_detail.xml +++ /dev/null @@ -1,133 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout-land/item_artist_header.xml b/app/src/main/res/layout-land/item_artist_header.xml new file mode 100644 index 000000000..85552589d --- /dev/null +++ b/app/src/main/res/layout-land/item_artist_header.xml @@ -0,0 +1,95 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_artist_detail.xml b/app/src/main/res/layout/fragment_artist_detail.xml deleted file mode 100644 index 289179111..000000000 --- a/app/src/main/res/layout/fragment_artist_detail.xml +++ /dev/null @@ -1,130 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_detail.xml b/app/src/main/res/layout/fragment_detail.xml index a5680c967..b871cb253 100644 --- a/app/src/main/res/layout/fragment_detail.xml +++ b/app/src/main/res/layout/fragment_detail.xml @@ -27,6 +27,6 @@ app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/genre_song_header" - tools:listitem="@layout/item_genre_header" /> + tools:listitem="@layout/item_artist_header" /> \ No newline at end of file diff --git a/app/src/main/res/layout/item_artist_header.xml b/app/src/main/res/layout/item_artist_header.xml new file mode 100644 index 000000000..0d9dbb0af --- /dev/null +++ b/app/src/main/res/layout/item_artist_header.xml @@ -0,0 +1,93 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/menu/menu_genre_actions.xml b/app/src/main/res/menu/menu_genre_detail.xml similarity index 100% rename from app/src/main/res/menu/menu_genre_actions.xml rename to app/src/main/res/menu/menu_genre_detail.xml diff --git a/app/src/main/res/navigation/nav_explore.xml b/app/src/main/res/navigation/nav_explore.xml index d733bfebd..a2b982ec5 100644 --- a/app/src/main/res/navigation/nav_explore.xml +++ b/app/src/main/res/navigation/nav_explore.xml @@ -35,7 +35,7 @@ android:id="@+id/artist_detail_fragment" android:name="org.oxycblt.auxio.detail.ArtistDetailFragment" android:label="ArtistDetailFragment" - tools:layout="@layout/fragment_artist_detail"> + tools:layout="@layout/fragment_detail">