From be8623ad2db779b06fe2b76a8e6edadd8095378d Mon Sep 17 00:00:00 2001 From: Alexander Capehart Date: Fri, 2 Sep 2022 13:35:59 -0600 Subject: [PATCH] recycler: add span size abstraction to adapter Make adapter instances handle span sizes. This supercedes the hackier solution where the fragments would have to reference adapter data in order to determine span size. Not anymore. --- .../auxio/detail/AlbumDetailFragment.kt | 9 +------- .../auxio/detail/ArtistDetailFragment.kt | 9 +------- .../auxio/detail/GenreDetailFragment.kt | 9 +------- .../detail/recycler/AlbumDetailAdapter.kt | 5 ++++ .../detail/recycler/ArtistDetailAdapter.kt | 5 ++++ .../auxio/detail/recycler/DetailAdapter.kt | 8 ++++++- .../detail/recycler/GenreDetailAdapter.kt | 5 ++++ .../org/oxycblt/auxio/image/IndicatorView.kt | 9 ++++---- .../org/oxycblt/auxio/search/SearchAdapter.kt | 5 +++- .../oxycblt/auxio/search/SearchFragment.kt | 6 +---- .../auxio/ui/recycler/AuxioRecyclerView.kt | 23 ++++++++++++------- 11 files changed, 50 insertions(+), 43 deletions(-) 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 185702206..a1ddf6b68 100644 --- a/app/src/main/java/org/oxycblt/auxio/detail/AlbumDetailFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/detail/AlbumDetailFragment.kt @@ -41,7 +41,6 @@ import org.oxycblt.auxio.playback.state.PlaybackMode import org.oxycblt.auxio.settings.Settings import org.oxycblt.auxio.ui.Sort import org.oxycblt.auxio.ui.fragment.MenuFragment -import org.oxycblt.auxio.ui.recycler.Header import org.oxycblt.auxio.ui.recycler.Item import org.oxycblt.auxio.util.canScroll import org.oxycblt.auxio.util.collect @@ -84,13 +83,7 @@ class AlbumDetailFragment : setOnMenuItemClickListener(this@AlbumDetailFragment) } - binding.detailRecycler.apply { - adapter = detailAdapter - setSpanSizeLookup { pos -> - val item = detailAdapter.currentList[pos] - item is Album || item is Header || item is SortHeader - } - } + binding.detailRecycler.adapter = detailAdapter // -- VIEWMODEL SETUP --- 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 6e90e0526..501d218ff 100644 --- a/app/src/main/java/org/oxycblt/auxio/detail/ArtistDetailFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/detail/ArtistDetailFragment.kt @@ -39,7 +39,6 @@ import org.oxycblt.auxio.playback.state.PlaybackMode import org.oxycblt.auxio.settings.Settings import org.oxycblt.auxio.ui.Sort import org.oxycblt.auxio.ui.fragment.MenuFragment -import org.oxycblt.auxio.ui.recycler.Header import org.oxycblt.auxio.ui.recycler.Item import org.oxycblt.auxio.util.collect import org.oxycblt.auxio.util.collectImmediately @@ -79,13 +78,7 @@ class ArtistDetailFragment : setOnMenuItemClickListener(this@ArtistDetailFragment) } - binding.detailRecycler.apply { - adapter = detailAdapter - setSpanSizeLookup { pos -> - val item = detailAdapter.currentList[pos] - item is Artist || item is Header || item is SortHeader - } - } + binding.detailRecycler.adapter = detailAdapter // --- VIEWMODEL SETUP --- 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 74227d047..541186601 100644 --- a/app/src/main/java/org/oxycblt/auxio/detail/GenreDetailFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/detail/GenreDetailFragment.kt @@ -40,7 +40,6 @@ import org.oxycblt.auxio.playback.state.PlaybackMode import org.oxycblt.auxio.settings.Settings import org.oxycblt.auxio.ui.Sort import org.oxycblt.auxio.ui.fragment.MenuFragment -import org.oxycblt.auxio.ui.recycler.Header import org.oxycblt.auxio.ui.recycler.Item import org.oxycblt.auxio.util.collect import org.oxycblt.auxio.util.collectImmediately @@ -80,13 +79,7 @@ class GenreDetailFragment : setOnMenuItemClickListener(this@GenreDetailFragment) } - binding.detailRecycler.apply { - adapter = detailAdapter - setSpanSizeLookup { pos -> - val item = detailAdapter.currentList[pos] - item is Genre || item is Header || item is SortHeader - } - } + binding.detailRecycler.adapter = detailAdapter // --- VIEWMODEL SETUP --- diff --git a/app/src/main/java/org/oxycblt/auxio/detail/recycler/AlbumDetailAdapter.kt b/app/src/main/java/org/oxycblt/auxio/detail/recycler/AlbumDetailAdapter.kt index 48af998cb..ceeb3ca0b 100644 --- a/app/src/main/java/org/oxycblt/auxio/detail/recycler/AlbumDetailAdapter.kt +++ b/app/src/main/java/org/oxycblt/auxio/detail/recycler/AlbumDetailAdapter.kt @@ -77,6 +77,11 @@ class AlbumDetailAdapter(private val listener: Listener) : } } + override fun isItemFullWidth(position: Int): Boolean { + val item = differ.currentList[position] + return super.isItemFullWidth(position) || item is Album || item is DiscHeader + } + companion object { private val DIFFER = object : SimpleItemCallback() { diff --git a/app/src/main/java/org/oxycblt/auxio/detail/recycler/ArtistDetailAdapter.kt b/app/src/main/java/org/oxycblt/auxio/detail/recycler/ArtistDetailAdapter.kt index 8f1868664..08ae90468 100644 --- a/app/src/main/java/org/oxycblt/auxio/detail/recycler/ArtistDetailAdapter.kt +++ b/app/src/main/java/org/oxycblt/auxio/detail/recycler/ArtistDetailAdapter.kt @@ -78,6 +78,11 @@ class ArtistDetailAdapter(private val listener: Listener) : } } + override fun isItemFullWidth(position: Int): Boolean { + val item = differ.currentList[position] + return super.isItemFullWidth(position) || item is Artist + } + companion object { private val DIFFER = object : SimpleItemCallback() { diff --git a/app/src/main/java/org/oxycblt/auxio/detail/recycler/DetailAdapter.kt b/app/src/main/java/org/oxycblt/auxio/detail/recycler/DetailAdapter.kt index 54e27f118..5fd7c8f82 100644 --- a/app/src/main/java/org/oxycblt/auxio/detail/recycler/DetailAdapter.kt +++ b/app/src/main/java/org/oxycblt/auxio/detail/recycler/DetailAdapter.kt @@ -26,6 +26,7 @@ import androidx.recyclerview.widget.RecyclerView import org.oxycblt.auxio.IntegerTable import org.oxycblt.auxio.databinding.ItemSortHeaderBinding import org.oxycblt.auxio.detail.SortHeader +import org.oxycblt.auxio.ui.recycler.AuxioRecyclerView import org.oxycblt.auxio.ui.recycler.Header import org.oxycblt.auxio.ui.recycler.HeaderViewHolder import org.oxycblt.auxio.ui.recycler.IndicatorAdapter @@ -38,7 +39,7 @@ import org.oxycblt.auxio.util.inflater abstract class DetailAdapter( private val listener: L, diffCallback: DiffUtil.ItemCallback -) : IndicatorAdapter() { +) : IndicatorAdapter(), AuxioRecyclerView.SpanSizeLookup { private var isPlaying = false @Suppress("LeakingThis") override fun getItemCount() = differ.currentList.size @@ -77,6 +78,11 @@ abstract class DetailAdapter( super.onBindViewHolder(holder, position, payloads) } + override fun isItemFullWidth(position: Int): Boolean { + val item = differ.currentList[position] + return item is Header || item is SortHeader + } + protected val differ = AsyncListDiffer(this, diffCallback) override val currentList: List diff --git a/app/src/main/java/org/oxycblt/auxio/detail/recycler/GenreDetailAdapter.kt b/app/src/main/java/org/oxycblt/auxio/detail/recycler/GenreDetailAdapter.kt index 9802f71c6..1121874a6 100644 --- a/app/src/main/java/org/oxycblt/auxio/detail/recycler/GenreDetailAdapter.kt +++ b/app/src/main/java/org/oxycblt/auxio/detail/recycler/GenreDetailAdapter.kt @@ -71,6 +71,11 @@ class GenreDetailAdapter(private val listener: Listener) : } } + override fun isItemFullWidth(position: Int): Boolean { + val item = differ.currentList[position] + return super.isItemFullWidth(position) || item is Genre + } + companion object { val DIFFER = object : SimpleItemCallback() { diff --git a/app/src/main/java/org/oxycblt/auxio/image/IndicatorView.kt b/app/src/main/java/org/oxycblt/auxio/image/IndicatorView.kt index 2827afbe6..f2cdceada 100644 --- a/app/src/main/java/org/oxycblt/auxio/image/IndicatorView.kt +++ b/app/src/main/java/org/oxycblt/auxio/image/IndicatorView.kt @@ -33,9 +33,9 @@ import org.oxycblt.auxio.util.getColorCompat import org.oxycblt.auxio.util.getDrawableCompat /** - * View that displays the playback indicator. Nominally emulates [StyledImageView], but is - * much different internally as an animated icon can't be wrapped within StyledDrawable without - * causing insane issues. + * View that displays the playback indicator. Nominally emulates [StyledImageView], but is much + * different internally as an animated icon can't be wrapped within StyledDrawable without causing + * insane issues. * @author OxygenCobalt */ class IndicatorView @@ -45,7 +45,8 @@ constructor(context: Context, attrs: AttributeSet? = null, @AttrRes defStyleAttr private val playingIndicatorDrawable = context.getDrawableCompat(R.drawable.ic_playing_indicator_24) as AnimationDrawable - private val pausedIndicatorDrawable = context.getDrawableCompat(R.drawable.ic_paused_indicator_24) + private val pausedIndicatorDrawable = + context.getDrawableCompat(R.drawable.ic_paused_indicator_24) private val indicatorMatrix = Matrix() private val indicatorMatrixSrc = RectF() 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 facb7fdfc..a12bd0b8f 100644 --- a/app/src/main/java/org/oxycblt/auxio/search/SearchAdapter.kt +++ b/app/src/main/java/org/oxycblt/auxio/search/SearchAdapter.kt @@ -26,6 +26,7 @@ import org.oxycblt.auxio.music.Genre import org.oxycblt.auxio.music.Song import org.oxycblt.auxio.ui.recycler.AlbumViewHolder import org.oxycblt.auxio.ui.recycler.ArtistViewHolder +import org.oxycblt.auxio.ui.recycler.AuxioRecyclerView import org.oxycblt.auxio.ui.recycler.GenreViewHolder import org.oxycblt.auxio.ui.recycler.Header import org.oxycblt.auxio.ui.recycler.HeaderViewHolder @@ -36,7 +37,7 @@ import org.oxycblt.auxio.ui.recycler.SimpleItemCallback import org.oxycblt.auxio.ui.recycler.SongViewHolder class SearchAdapter(private val listener: MenuItemListener) : - IndicatorAdapter() { + IndicatorAdapter(), AuxioRecyclerView.SpanSizeLookup { private val differ = AsyncListDiffer(this, DIFFER) override fun getItemCount() = differ.currentList.size @@ -79,6 +80,8 @@ class SearchAdapter(private val listener: MenuItemListener) : } } + override fun isItemFullWidth(position: Int) = differ.currentList[position] is Header + override val currentList: List get() = differ.currentList 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 0990b4a5e..724d17fcf 100644 --- a/app/src/main/java/org/oxycblt/auxio/search/SearchFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/search/SearchFragment.kt @@ -39,7 +39,6 @@ import org.oxycblt.auxio.music.MusicParent import org.oxycblt.auxio.music.Song import org.oxycblt.auxio.settings.Settings import org.oxycblt.auxio.ui.fragment.MenuFragment -import org.oxycblt.auxio.ui.recycler.Header import org.oxycblt.auxio.ui.recycler.Item import org.oxycblt.auxio.ui.recycler.MenuItemListener import org.oxycblt.auxio.util.androidViewModels @@ -104,10 +103,7 @@ class SearchFragment : } } - binding.searchRecycler.apply { - adapter = searchAdapter - setSpanSizeLookup { pos -> searchAdapter.currentList[pos] is Header } - } + binding.searchRecycler.adapter = searchAdapter // --- VIEWMODEL SETUP --- diff --git a/app/src/main/java/org/oxycblt/auxio/ui/recycler/AuxioRecyclerView.kt b/app/src/main/java/org/oxycblt/auxio/ui/recycler/AuxioRecyclerView.kt index 6c7b93169..3edd521d5 100644 --- a/app/src/main/java/org/oxycblt/auxio/ui/recycler/AuxioRecyclerView.kt +++ b/app/src/main/java/org/oxycblt/auxio/ui/recycler/AuxioRecyclerView.kt @@ -54,13 +54,20 @@ constructor(context: Context, attrs: AttributeSet? = null, @AttrRes defStyleAttr return insets } - // TODO: Move abstraction to adapters since using external data will not work well - inline fun setSpanSizeLookup(crossinline fullWidth: (Int) -> Boolean) { - val glm = layoutManager as GridLayoutManager - val spanCount = glm.spanCount - glm.spanSizeLookup = - object : GridLayoutManager.SpanSizeLookup() { - override fun getSpanSize(position: Int) = if (fullWidth(position)) spanCount else 1 - } + override fun setAdapter(adapter: Adapter<*>?) { + super.setAdapter(adapter) + + if (adapter is SpanSizeLookup) { + val glm = (layoutManager as GridLayoutManager) + glm.spanSizeLookup = + object : GridLayoutManager.SpanSizeLookup() { + override fun getSpanSize(position: Int) = + if (adapter.isItemFullWidth(position)) glm.spanCount else 1 + } + } + } + + interface SpanSizeLookup { + fun isItemFullWidth(position: Int): Boolean } }