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 5d7a0d5de..6a09ec491 100644 --- a/app/src/main/java/org/oxycblt/auxio/detail/AlbumDetailFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/detail/AlbumDetailFragment.kt @@ -87,7 +87,7 @@ class AlbumDetailFragment : binding.detailRecycler.apply { adapter = detailAdapter setSpanSizeLookup { pos -> - val item = detailModel.albumData.value[pos] + val item = detailAdapter.currentList[pos] item is Album || item is Header || item is SortHeader } } 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 953e9c537..5038b7974 100644 --- a/app/src/main/java/org/oxycblt/auxio/detail/ArtistDetailFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/detail/ArtistDetailFragment.kt @@ -82,7 +82,7 @@ class ArtistDetailFragment : binding.detailRecycler.apply { adapter = detailAdapter setSpanSizeLookup { pos -> - val item = detailModel.artistData.value[pos] + val item = detailAdapter.currentList[pos] item is Artist || item is Header || item is SortHeader } } 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 b3174070c..8f8554346 100644 --- a/app/src/main/java/org/oxycblt/auxio/detail/GenreDetailFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/detail/GenreDetailFragment.kt @@ -83,7 +83,7 @@ class GenreDetailFragment : binding.detailRecycler.apply { adapter = detailAdapter setSpanSizeLookup { pos -> - val item = detailModel.genreData.value[pos] + val item = detailAdapter.currentList[pos] item is Genre || item is Header || item is SortHeader } } 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 d793a020a..1d656e4aa 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 @@ -77,6 +77,9 @@ abstract class DetailAdapter( protected val differ = AsyncListDiffer(this, diffCallback) + val currentList: List + get() = differ.currentList + fun submitList(list: List) { differ.submitList(list) } 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 095375abc..65f104448 100644 --- a/app/src/main/java/org/oxycblt/auxio/search/SearchAdapter.kt +++ b/app/src/main/java/org/oxycblt/auxio/search/SearchAdapter.kt @@ -24,6 +24,7 @@ import org.oxycblt.auxio.music.Album import org.oxycblt.auxio.music.Artist import org.oxycblt.auxio.music.Genre import org.oxycblt.auxio.music.Song +import org.oxycblt.auxio.ui.recycler.ActivationAdapter import org.oxycblt.auxio.ui.recycler.AlbumViewHolder import org.oxycblt.auxio.ui.recycler.ArtistViewHolder import org.oxycblt.auxio.ui.recycler.GenreViewHolder @@ -35,8 +36,12 @@ import org.oxycblt.auxio.ui.recycler.SimpleItemCallback import org.oxycblt.auxio.ui.recycler.SongViewHolder class SearchAdapter(private val listener: MenuItemListener) : - RecyclerView.Adapter() { + ActivationAdapter() { private val differ = AsyncListDiffer(this, DIFFER) + private var currentSong: Song? = null + private var currentAlbum: Album? = null + private var currentArtist: Artist? = null + private var currentGenre: Genre? = null override fun getItemCount() = differ.currentList.size @@ -45,7 +50,7 @@ class SearchAdapter(private val listener: MenuItemListener) : is Song -> SongViewHolder.VIEW_TYPE is Album -> AlbumViewHolder.VIEW_TYPE is Artist -> ArtistViewHolder.VIEW_TYPE - is Genre -> HeaderViewHolder.VIEW_TYPE + is Genre -> GenreViewHolder.VIEW_TYPE is Header -> HeaderViewHolder.VIEW_TYPE else -> super.getItemViewType(position) } @@ -60,18 +65,62 @@ class SearchAdapter(private val listener: MenuItemListener) : else -> error("Invalid item type $viewType") } - override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { - when (val item = differ.currentList[position]) { - is Song -> (holder as SongViewHolder).bind(item, listener) - is Album -> (holder as AlbumViewHolder).bind(item, listener) - is Artist -> (holder as ArtistViewHolder).bind(item, listener) - is Genre -> (holder as GenreViewHolder).bind(item, listener) - is Header -> (holder as HeaderViewHolder).bind(item) + override fun onBindViewHolder( + holder: RecyclerView.ViewHolder, + position: Int, + payloads: List + ) { + super.onBindViewHolder(holder, position, payloads) + + if (payloads.isEmpty()) { + when (val item = differ.currentList[position]) { + is Song -> (holder as SongViewHolder).bind(item, listener) + is Album -> (holder as AlbumViewHolder).bind(item, listener) + is Artist -> (holder as ArtistViewHolder).bind(item, listener) + is Genre -> (holder as GenreViewHolder).bind(item, listener) + is Header -> (holder as HeaderViewHolder).bind(item) + } } } + override fun shouldActivateViewHolder(position: Int): Boolean { + val item = differ.currentList[position] + + return (item is Song && item.id == currentSong?.id) || + (item is Album && item.id == currentAlbum?.id) || + (item is Artist && item.id == currentArtist?.id) || + (item is Genre && item.id == currentGenre?.id) + } + + val currentList: List + get() = differ.currentList + fun submitList(list: List, callback: () -> Unit) = differ.submitList(list, callback) + fun activateSong(song: Song?) { + if (song == currentSong) return + activateImpl(differ.currentList, currentSong, song) + currentSong = song + } + + fun activateAlbum(album: Album?) { + if (album == currentAlbum) return + activateImpl(differ.currentList, currentAlbum, album) + currentAlbum = album + } + + fun activateArtist(artist: Artist?) { + if (artist == currentArtist) return + activateImpl(differ.currentList, currentArtist, artist) + currentArtist = artist + } + + fun activateGenre(genre: Genre?) { + if (genre == currentGenre) return + activateImpl(differ.currentList, currentGenre, genre) + currentGenre = genre + } + companion object { private val DIFFER = object : SimpleItemCallback() { 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 d4834cc34..2861d3af9 100644 --- a/app/src/main/java/org/oxycblt/auxio/search/SearchFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/search/SearchFragment.kt @@ -106,12 +106,13 @@ class SearchFragment : binding.searchRecycler.apply { adapter = searchAdapter - setSpanSizeLookup { pos -> searchModel.searchResults.value[pos] is Header } + setSpanSizeLookup { pos -> searchAdapter.currentList[pos] is Header } } // --- VIEWMODEL SETUP --- - collectImmediately(searchModel.searchResults, ::updateResults) + collectImmediately(searchModel.searchResults, ::handleResults) + collectImmediately(playbackModel.song, playbackModel.parent, ::handlePlayback) collect(navModel.exploreNavigationItem, ::handleNavigation) } @@ -151,7 +152,7 @@ class SearchFragment : } } - private fun updateResults(results: List) { + private fun handleResults(results: List) { val binding = requireBinding() searchAdapter.submitList(results.toMutableList()) { @@ -164,6 +165,36 @@ class SearchFragment : binding.searchRecycler.isInvisible = results.isEmpty() } + private fun handlePlayback(song: Song?, parent: MusicParent?) { + if (parent == null) { + searchAdapter.activateSong(song) + } else { + // Ignore playback not from all songs + searchAdapter.activateSong(null) + } + + if (parent is Album) { + searchAdapter.activateAlbum(parent) + } else { + // Ignore playback not from albums + searchAdapter.activateAlbum(null) + } + + if (parent is Artist) { + searchAdapter.activateArtist(parent) + } else { + // Ignore playback not from artists + searchAdapter.activateArtist(null) + } + + if (parent is Genre) { + searchAdapter.activateGenre(parent) + } else { + // Ignore playback not from artists + searchAdapter.activateGenre(null) + } + } + private fun handleNavigation(item: Music?) { findNavController() .navigate( 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 c97c30518..6c7b93169 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,6 +54,7 @@ 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 diff --git a/app/src/main/java/org/oxycblt/auxio/ui/recycler/RecyclerFramework.kt b/app/src/main/java/org/oxycblt/auxio/ui/recycler/RecyclerFramework.kt index 274e33bd2..81067b726 100644 --- a/app/src/main/java/org/oxycblt/auxio/ui/recycler/RecyclerFramework.kt +++ b/app/src/main/java/org/oxycblt/auxio/ui/recycler/RecyclerFramework.kt @@ -207,7 +207,7 @@ abstract class ActivationAdapter : RecyclerView.Ad } } } - + companion object { val PAYLOAD_ACTIVATION_CHANGED = Any() }