search: add playing indicators [#218]

Add playing indicators to the search view too.
This commit is contained in:
Alexander Capehart 2022-09-01 19:51:07 -06:00
parent 87ca4c8ab1
commit 227a258eca
No known key found for this signature in database
GPG key ID: 37DBE3621FE9AD47
8 changed files with 100 additions and 16 deletions

View file

@ -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
}
}

View file

@ -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
}
}

View file

@ -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
}
}

View file

@ -77,6 +77,9 @@ abstract class DetailAdapter<L : DetailAdapter.Listener>(
protected val differ = AsyncListDiffer(this, diffCallback)
val currentList: List<Item>
get() = differ.currentList
fun submitList(list: List<Item>) {
differ.submitList(list)
}

View file

@ -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<RecyclerView.ViewHolder>() {
ActivationAdapter<RecyclerView.ViewHolder>() {
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<Any>
) {
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<Item>
get() = differ.currentList
fun submitList(list: List<Item>, 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<Item>() {

View file

@ -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<Item>) {
private fun handleResults(results: List<Item>) {
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(

View file

@ -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