From 9883cf1c918c24bf27ead7f7c5d3df88947e9f48 Mon Sep 17 00:00:00 2001 From: Alexander Capehart Date: Thu, 17 Oct 2024 09:57:47 -0600 Subject: [PATCH] list: tweak header/divider object hierarchy Make a new generic Header/Divider superclass that all headers derive. This allows disc headers to be recognized generically in places like the grid layout manager. --- .../org/oxycblt/auxio/detail/DetailFragment.kt | 6 +++--- .../org/oxycblt/auxio/detail/DetailViewModel.kt | 12 ++++++------ .../auxio/detail/list/AlbumDetailListAdapter.kt | 6 ++++-- .../auxio/detail/list/DetailListAdapter.kt | 10 +++++----- .../detail/list/PlaylistDetailListAdapter.kt | 6 +++--- app/src/main/java/org/oxycblt/auxio/list/Data.kt | 16 +++++++++++----- .../oxycblt/auxio/list/recycler/ViewHolders.kt | 8 ++++---- .../org/oxycblt/auxio/search/SearchAdapter.kt | 6 +++--- .../org/oxycblt/auxio/search/SearchFragment.kt | 6 +++--- .../org/oxycblt/auxio/search/SearchViewModel.kt | 10 +++++----- 10 files changed, 47 insertions(+), 39 deletions(-) diff --git a/app/src/main/java/org/oxycblt/auxio/detail/DetailFragment.kt b/app/src/main/java/org/oxycblt/auxio/detail/DetailFragment.kt index 5213f08b0..675f5c198 100644 --- a/app/src/main/java/org/oxycblt/auxio/detail/DetailFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/detail/DetailFragment.kt @@ -31,10 +31,10 @@ import kotlin.math.min import org.oxycblt.auxio.R import org.oxycblt.auxio.databinding.FragmentDetailBinding import org.oxycblt.auxio.detail.list.DetailListAdapter -import org.oxycblt.auxio.list.Divider -import org.oxycblt.auxio.list.Header import org.oxycblt.auxio.list.ListFragment import org.oxycblt.auxio.list.ListViewModel +import org.oxycblt.auxio.list.PlainDivider +import org.oxycblt.auxio.list.PlainHeader import org.oxycblt.auxio.music.Music import org.oxycblt.auxio.music.MusicParent import org.oxycblt.auxio.music.MusicViewModel @@ -91,7 +91,7 @@ abstract class DetailFragment

: detailModel.artistSongList.value.getOrElse(it - 1) { return@setFullWidthLookup false } - item is Divider || item is Header + item is PlainDivider || item is PlainHeader } else { true } 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 1d16d7992..5675b89f5 100644 --- a/app/src/main/java/org/oxycblt/auxio/detail/DetailViewModel.kt +++ b/app/src/main/java/org/oxycblt/auxio/detail/DetailViewModel.kt @@ -34,10 +34,10 @@ import org.oxycblt.auxio.detail.list.DiscHeader import org.oxycblt.auxio.detail.list.EditHeader import org.oxycblt.auxio.detail.list.SortHeader import org.oxycblt.auxio.list.BasicHeader -import org.oxycblt.auxio.list.Divider -import org.oxycblt.auxio.list.Header import org.oxycblt.auxio.list.Item import org.oxycblt.auxio.list.ListSettings +import org.oxycblt.auxio.list.PlainDivider +import org.oxycblt.auxio.list.PlainHeader import org.oxycblt.auxio.list.adapter.UpdateInstructions import org.oxycblt.auxio.list.sort.Sort import org.oxycblt.auxio.music.Album @@ -531,7 +531,7 @@ constructor( list: MutableStateFlow>, instructions: MutableEvent, replace: Int?, - songHeader: (Int) -> Header = { SortHeader(it) } + songHeader: (Int) -> PlainHeader = { SortHeader(it) } ) { if (detail == null) { parent.value = null @@ -547,7 +547,7 @@ constructor( if (section is DetailSection.Songs) songHeader(section.stringRes) else BasicHeader(section.stringRes) if (newList.isNotEmpty()) { - newList.add(Divider(header)) + newList.add(PlainDivider(header)) } newList.add(header) section.items @@ -555,7 +555,7 @@ constructor( is DetailSection.Discs -> { val header = SortHeader(section.stringRes) if (newList.isNotEmpty()) { - newList.add(Divider(header)) + newList.add(PlainDivider(header)) } newList.add(header) buildList { @@ -600,7 +600,7 @@ constructor( val list = mutableListOf() if (edited.isNotEmpty()) { val header = EditHeader(R.string.lbl_songs) - list.add(Divider(header)) + list.add(PlainDivider(header)) list.add(header) list.addAll(edited) } diff --git a/app/src/main/java/org/oxycblt/auxio/detail/list/AlbumDetailListAdapter.kt b/app/src/main/java/org/oxycblt/auxio/detail/list/AlbumDetailListAdapter.kt index 2d09a1e4d..1af1a595c 100644 --- a/app/src/main/java/org/oxycblt/auxio/detail/list/AlbumDetailListAdapter.kt +++ b/app/src/main/java/org/oxycblt/auxio/detail/list/AlbumDetailListAdapter.kt @@ -29,6 +29,8 @@ import org.oxycblt.auxio.IntegerTable import org.oxycblt.auxio.R import org.oxycblt.auxio.databinding.ItemAlbumSongBinding import org.oxycblt.auxio.databinding.ItemDiscHeaderBinding +import org.oxycblt.auxio.list.Divider +import org.oxycblt.auxio.list.Header import org.oxycblt.auxio.list.Item import org.oxycblt.auxio.list.SelectableListListener import org.oxycblt.auxio.list.adapter.SelectionIndicatorAdapter @@ -100,9 +102,9 @@ class AlbumDetailListAdapter(private val listener: Listener) : * * @author Alexander Capehart (OxygenCobalt) */ -data class DiscHeader(val inner: Disc?) : Item +data class DiscHeader(val inner: Disc?) : Header -data class DiscDivider(val anchor: DiscHeader?) : Item +data class DiscDivider(override val anchor: DiscHeader?) : Divider /** * A [RecyclerView.ViewHolder] that displays a [DiscHeader] to delimit different disc groups. Use diff --git a/app/src/main/java/org/oxycblt/auxio/detail/list/DetailListAdapter.kt b/app/src/main/java/org/oxycblt/auxio/detail/list/DetailListAdapter.kt index 08293199f..87aebf5df 100644 --- a/app/src/main/java/org/oxycblt/auxio/detail/list/DetailListAdapter.kt +++ b/app/src/main/java/org/oxycblt/auxio/detail/list/DetailListAdapter.kt @@ -27,9 +27,9 @@ import androidx.recyclerview.widget.RecyclerView import org.oxycblt.auxio.IntegerTable import org.oxycblt.auxio.databinding.ItemSortHeaderBinding import org.oxycblt.auxio.list.BasicHeader -import org.oxycblt.auxio.list.Divider -import org.oxycblt.auxio.list.Header import org.oxycblt.auxio.list.Item +import org.oxycblt.auxio.list.PlainDivider +import org.oxycblt.auxio.list.PlainHeader import org.oxycblt.auxio.list.SelectableListListener import org.oxycblt.auxio.list.adapter.SelectionIndicatorAdapter import org.oxycblt.auxio.list.adapter.SimpleDiffCallback @@ -55,7 +55,7 @@ abstract class DetailListAdapter( override fun getItemViewType(position: Int) = when (getItem(position)) { // Implement support for headers and sort headers - is Divider -> DividerViewHolder.VIEW_TYPE + is PlainDivider -> DividerViewHolder.VIEW_TYPE is BasicHeader -> BasicHeaderViewHolder.VIEW_TYPE is SortHeader -> SortHeaderViewHolder.VIEW_TYPE else -> super.getItemViewType(position) @@ -91,7 +91,7 @@ abstract class DetailListAdapter( object : SimpleDiffCallback() { override fun areContentsTheSame(oldItem: Item, newItem: Item): Boolean { return when { - oldItem is Divider && newItem is Divider -> + oldItem is PlainDivider && newItem is PlainDivider -> DividerViewHolder.DIFF_CALLBACK.areContentsTheSame(oldItem, newItem) oldItem is BasicHeader && newItem is BasicHeader -> BasicHeaderViewHolder.DIFF_CALLBACK.areContentsTheSame(oldItem, newItem) @@ -110,7 +110,7 @@ abstract class DetailListAdapter( * @param titleRes The string resource to use as the header title * @author Alexander Capehart (OxygenCobalt) */ -data class SortHeader(@StringRes override val titleRes: Int) : Header +data class SortHeader(@StringRes override val titleRes: Int) : PlainHeader /** * A [RecyclerView.ViewHolder] that displays a [SortHeader] and it's actions. Use [from] to create diff --git a/app/src/main/java/org/oxycblt/auxio/detail/list/PlaylistDetailListAdapter.kt b/app/src/main/java/org/oxycblt/auxio/detail/list/PlaylistDetailListAdapter.kt index 6bd2ed44d..213c65b78 100644 --- a/app/src/main/java/org/oxycblt/auxio/detail/list/PlaylistDetailListAdapter.kt +++ b/app/src/main/java/org/oxycblt/auxio/detail/list/PlaylistDetailListAdapter.kt @@ -33,8 +33,8 @@ import org.oxycblt.auxio.IntegerTable import org.oxycblt.auxio.databinding.ItemEditHeaderBinding import org.oxycblt.auxio.databinding.ItemEditableSongBinding import org.oxycblt.auxio.list.EditableListListener -import org.oxycblt.auxio.list.Header import org.oxycblt.auxio.list.Item +import org.oxycblt.auxio.list.PlainHeader import org.oxycblt.auxio.list.adapter.PlayingIndicatorAdapter import org.oxycblt.auxio.list.adapter.SelectionIndicatorAdapter import org.oxycblt.auxio.list.adapter.SimpleDiffCallback @@ -140,12 +140,12 @@ class PlaylistDetailListAdapter(private val listener: Listener) : } /** - * A [Header] variant that displays an edit button. + * A [PlainHeader] variant that displays an edit button. * * @param titleRes The string resource to use as the header title * @author Alexander Capehart (OxygenCobalt) */ -data class EditHeader(@StringRes override val titleRes: Int) : Header +data class EditHeader(@StringRes override val titleRes: Int) : PlainHeader /** * Displays an [EditHeader] and it's actions. Use [from] to create an instance. diff --git a/app/src/main/java/org/oxycblt/auxio/list/Data.kt b/app/src/main/java/org/oxycblt/auxio/list/Data.kt index 8636e1579..5507bb9fc 100644 --- a/app/src/main/java/org/oxycblt/auxio/list/Data.kt +++ b/app/src/main/java/org/oxycblt/auxio/list/Data.kt @@ -24,12 +24,14 @@ import androidx.annotation.StringRes /** A marker for something that is a RecyclerView item. Has no functionality on it's own. */ interface Item +interface Header : Item + /** * A "header" used for delimiting groups of data. * * @author Alexander Capehart (OxygenCobalt) */ -interface Header : Item { +interface PlainHeader : Header { /** The string resource used for the header's title. */ val titleRes: Int } @@ -40,12 +42,16 @@ interface Header : Item { * @param titleRes The string resource used for the header's title. * @author Alexander Capehart (OxygenCobalt) */ -data class BasicHeader(@StringRes override val titleRes: Int) : Header +data class BasicHeader(@StringRes override val titleRes: Int) : PlainHeader + +interface Divider : Item { + val anchor: T? +} /** * A divider decoration used to delimit groups of data. * - * @param anchor The [Header] this divider should be next to in a list. Used as a way to preserve - * divider continuity during list updates. + * @param anchor The [PlainHeader] this divider should be next to in a list. Used as a way to + * preserve divider continuity during list updates. */ -data class Divider(val anchor: Header?) : Item +data class PlainDivider(override val anchor: PlainHeader?) : Divider diff --git a/app/src/main/java/org/oxycblt/auxio/list/recycler/ViewHolders.kt b/app/src/main/java/org/oxycblt/auxio/list/recycler/ViewHolders.kt index 36565b63f..d7f5e1d23 100644 --- a/app/src/main/java/org/oxycblt/auxio/list/recycler/ViewHolders.kt +++ b/app/src/main/java/org/oxycblt/auxio/list/recycler/ViewHolders.kt @@ -27,7 +27,7 @@ import org.oxycblt.auxio.databinding.ItemHeaderBinding import org.oxycblt.auxio.databinding.ItemParentBinding import org.oxycblt.auxio.databinding.ItemSongBinding import org.oxycblt.auxio.list.BasicHeader -import org.oxycblt.auxio.list.Divider +import org.oxycblt.auxio.list.PlainDivider import org.oxycblt.auxio.list.SelectableListListener import org.oxycblt.auxio.list.adapter.SelectionIndicatorAdapter import org.oxycblt.auxio.list.adapter.SimpleDiffCallback @@ -360,7 +360,7 @@ class BasicHeaderViewHolder private constructor(private val binding: ItemHeaderB } /** - * A [RecyclerView.ViewHolder] that displays a [Divider]. Use [from] to create an instance. + * A [RecyclerView.ViewHolder] that displays a [PlainDivider]. Use [from] to create an instance. * * @author Alexander Capehart (OxygenCobalt) */ @@ -381,8 +381,8 @@ class DividerViewHolder private constructor(divider: MaterialDivider) : /** A comparator that can be used with DiffUtil. */ val DIFF_CALLBACK = - object : SimpleDiffCallback() { - override fun areContentsTheSame(oldItem: Divider, newItem: Divider) = + object : SimpleDiffCallback() { + override fun areContentsTheSame(oldItem: PlainDivider, newItem: PlainDivider) = oldItem.anchor == newItem.anchor } } diff --git a/app/src/main/java/org/oxycblt/auxio/search/SearchAdapter.kt b/app/src/main/java/org/oxycblt/auxio/search/SearchAdapter.kt index a800221fb..7b9befdef 100644 --- a/app/src/main/java/org/oxycblt/auxio/search/SearchAdapter.kt +++ b/app/src/main/java/org/oxycblt/auxio/search/SearchAdapter.kt @@ -21,8 +21,8 @@ package org.oxycblt.auxio.search import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView import org.oxycblt.auxio.list.BasicHeader -import org.oxycblt.auxio.list.Divider import org.oxycblt.auxio.list.Item +import org.oxycblt.auxio.list.PlainDivider import org.oxycblt.auxio.list.SelectableListListener import org.oxycblt.auxio.list.adapter.SelectionIndicatorAdapter import org.oxycblt.auxio.list.adapter.SimpleDiffCallback @@ -57,7 +57,7 @@ class SearchAdapter(private val listener: SelectableListListener) : is Artist -> ArtistViewHolder.VIEW_TYPE is Genre -> GenreViewHolder.VIEW_TYPE is Playlist -> PlaylistViewHolder.VIEW_TYPE - is Divider -> DividerViewHolder.VIEW_TYPE + is PlainDivider -> DividerViewHolder.VIEW_TYPE is BasicHeader -> BasicHeaderViewHolder.VIEW_TYPE else -> super.getItemViewType(position) } @@ -102,7 +102,7 @@ class SearchAdapter(private val listener: SelectableListListener) : GenreViewHolder.DIFF_CALLBACK.areContentsTheSame(oldItem, newItem) oldItem is Playlist && newItem is Playlist -> PlaylistViewHolder.DIFF_CALLBACK.areContentsTheSame(oldItem, newItem) - oldItem is Divider && newItem is Divider -> + oldItem is PlainDivider && newItem is PlainDivider -> DividerViewHolder.DIFF_CALLBACK.areContentsTheSame(oldItem, newItem) oldItem is BasicHeader && newItem is BasicHeader -> BasicHeaderViewHolder.DIFF_CALLBACK.areContentsTheSame(oldItem, newItem) diff --git a/app/src/main/java/org/oxycblt/auxio/search/SearchFragment.kt b/app/src/main/java/org/oxycblt/auxio/search/SearchFragment.kt index af6556c83..ddc3c754b 100644 --- a/app/src/main/java/org/oxycblt/auxio/search/SearchFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/search/SearchFragment.kt @@ -38,11 +38,11 @@ import org.oxycblt.auxio.R import org.oxycblt.auxio.databinding.FragmentSearchBinding import org.oxycblt.auxio.detail.DetailViewModel import org.oxycblt.auxio.detail.Show -import org.oxycblt.auxio.list.Divider -import org.oxycblt.auxio.list.Header import org.oxycblt.auxio.list.Item import org.oxycblt.auxio.list.ListFragment import org.oxycblt.auxio.list.ListViewModel +import org.oxycblt.auxio.list.PlainDivider +import org.oxycblt.auxio.list.PlainHeader import org.oxycblt.auxio.list.menu.Menu import org.oxycblt.auxio.music.Album import org.oxycblt.auxio.music.Artist @@ -153,7 +153,7 @@ class SearchFragment : ListFragment() { searchModel.searchResults.value.getOrElse(it) { return@setFullWidthLookup false } - item is Divider || item is Header + item is PlainDivider || item is PlainHeader } } diff --git a/app/src/main/java/org/oxycblt/auxio/search/SearchViewModel.kt b/app/src/main/java/org/oxycblt/auxio/search/SearchViewModel.kt index fb60d7ff9..6eff6450e 100644 --- a/app/src/main/java/org/oxycblt/auxio/search/SearchViewModel.kt +++ b/app/src/main/java/org/oxycblt/auxio/search/SearchViewModel.kt @@ -30,8 +30,8 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.yield import org.oxycblt.auxio.R import org.oxycblt.auxio.list.BasicHeader -import org.oxycblt.auxio.list.Divider import org.oxycblt.auxio.list.Item +import org.oxycblt.auxio.list.PlainDivider import org.oxycblt.auxio.list.sort.Sort import org.oxycblt.auxio.music.MusicRepository import org.oxycblt.auxio.music.MusicType @@ -152,7 +152,7 @@ constructor( logD("Adding ${it.size} albums to search results") val header = BasicHeader(R.string.lbl_albums) if (isNotEmpty()) { - add(Divider(header)) + add(PlainDivider(header)) } add(header) @@ -162,7 +162,7 @@ constructor( logD("Adding ${it.size} playlists to search results") val header = BasicHeader(R.string.lbl_playlists) if (isNotEmpty()) { - add(Divider(header)) + add(PlainDivider(header)) } add(header) @@ -172,7 +172,7 @@ constructor( logD("Adding ${it.size} genres to search results") val header = BasicHeader(R.string.lbl_genres) if (isNotEmpty()) { - add(Divider(header)) + add(PlainDivider(header)) } add(header) @@ -182,7 +182,7 @@ constructor( logD("Adding ${it.size} songs to search results") val header = BasicHeader(R.string.lbl_songs) if (isNotEmpty()) { - add(Divider(header)) + add(PlainDivider(header)) } add(header)