search: add playing indicators [#218]
Add playing indicators to the search view too.
This commit is contained in:
parent
87ca4c8ab1
commit
227a258eca
8 changed files with 100 additions and 16 deletions
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,7 +65,14 @@ class SearchAdapter(private val listener: MenuItemListener) :
|
|||
else -> error("Invalid item type $viewType")
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
||||
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)
|
||||
|
|
@ -69,9 +81,46 @@ class SearchAdapter(private val listener: MenuItemListener) :
|
|||
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>() {
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in a new issue