home: indicate playback on items [#218]
Indicate playback in the home view as well. This is mostly a QoL change. Might also add this to the search view.
This commit is contained in:
parent
5f6cdad507
commit
87ca4c8ab1
13 changed files with 227 additions and 93 deletions
|
@ -257,10 +257,10 @@ class AlbumDetailFragment :
|
|||
}
|
||||
|
||||
if (parent is Album && parent.id == unlikelyToBeNull(detailModel.currentAlbum.value).id) {
|
||||
detailAdapter.highlightSong(song)
|
||||
detailAdapter.activateSong(song)
|
||||
} else {
|
||||
// Clear the ViewHolders if the mode isn't ALL_SONGS
|
||||
detailAdapter.highlightSong(null)
|
||||
detailAdapter.activateSong(null)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -202,18 +202,18 @@ class ArtistDetailFragment :
|
|||
|
||||
private fun updatePlayback(song: Song?, parent: MusicParent?) {
|
||||
if (parent is Artist && parent.id == unlikelyToBeNull(detailModel.currentArtist.value).id) {
|
||||
detailAdapter.highlightSong(song)
|
||||
detailAdapter.activateSong(song)
|
||||
} else {
|
||||
// Clear the ViewHolders if the given song is not part of this artist.
|
||||
detailAdapter.highlightSong(null)
|
||||
// Ignore song playback not from the artist
|
||||
detailAdapter.activateSong(null)
|
||||
}
|
||||
|
||||
if (parent is Album &&
|
||||
parent.artist.id == unlikelyToBeNull(detailModel.currentArtist.value).id) {
|
||||
detailAdapter.highlightAlbum(parent)
|
||||
detailAdapter.activateAlbum(parent)
|
||||
} else {
|
||||
// Clear out the album viewholder if the parent is not an album.
|
||||
detailAdapter.highlightAlbum(null)
|
||||
// Ignore album playback not from the artist
|
||||
detailAdapter.activateAlbum(null)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -83,7 +83,7 @@ class GenreDetailFragment :
|
|||
binding.detailRecycler.apply {
|
||||
adapter = detailAdapter
|
||||
setSpanSizeLookup { pos ->
|
||||
val item = detailModel.albumData.value[pos]
|
||||
val item = detailModel.genreData.value[pos]
|
||||
item is Genre || item is Header || item is SortHeader
|
||||
}
|
||||
}
|
||||
|
@ -195,9 +195,10 @@ class GenreDetailFragment :
|
|||
|
||||
private fun updatePlayback(song: Song?, parent: MusicParent?) {
|
||||
if (parent is Genre && parent.id == unlikelyToBeNull(detailModel.currentGenre.value).id) {
|
||||
detailAdapter.highlightSong(song)
|
||||
detailAdapter.activateSong(song)
|
||||
} else {
|
||||
detailAdapter.highlightSong(null)
|
||||
// Ignore song playback not from the genre
|
||||
detailAdapter.activateSong(null)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -64,25 +64,28 @@ class AlbumDetailAdapter(private val listener: Listener) :
|
|||
override fun onBindViewHolder(
|
||||
holder: RecyclerView.ViewHolder,
|
||||
position: Int,
|
||||
payload: List<Any>
|
||||
payloads: List<Any>
|
||||
) {
|
||||
if (payload.isEmpty()) {
|
||||
super.onBindViewHolder(holder, position, payloads)
|
||||
|
||||
if (payloads.isEmpty()) {
|
||||
when (val item = differ.currentList[position]) {
|
||||
is Album -> (holder as AlbumDetailViewHolder).bind(item, listener)
|
||||
is DiscHeader -> (holder as DiscHeaderViewHolder).bind(item)
|
||||
is Song -> (holder as AlbumSongViewHolder).bind(item, listener)
|
||||
}
|
||||
}
|
||||
|
||||
super.onBindViewHolder(holder, position, payload)
|
||||
}
|
||||
|
||||
override fun shouldHighlightViewHolder(item: Item) = item is Song && item.id == currentSong?.id
|
||||
override fun shouldActivateViewHolder(position: Int): Boolean {
|
||||
val item = differ.currentList[position]
|
||||
return item is Song && item.id == currentSong?.id
|
||||
}
|
||||
|
||||
/** Update the [song] that this adapter should highlight */
|
||||
fun highlightSong(song: Song?) {
|
||||
/** Update the [song] that this adapter should indicate playback */
|
||||
fun activateSong(song: Song?) {
|
||||
if (song == currentSong) return
|
||||
highlightImpl(currentSong, song)
|
||||
activateImpl(differ.currentList, currentSong, song)
|
||||
currentSong = song
|
||||
}
|
||||
|
||||
|
|
|
@ -66,34 +66,36 @@ class ArtistDetailAdapter(private val listener: Listener) :
|
|||
override fun onBindViewHolder(
|
||||
holder: RecyclerView.ViewHolder,
|
||||
position: Int,
|
||||
payload: List<Any>
|
||||
payloads: List<Any>
|
||||
) {
|
||||
if (payload.isEmpty()) {
|
||||
super.onBindViewHolder(holder, position, payloads)
|
||||
|
||||
if (payloads.isEmpty()) {
|
||||
when (val item = differ.currentList[position]) {
|
||||
is Artist -> (holder as ArtistDetailViewHolder).bind(item, listener)
|
||||
is Album -> (holder as ArtistAlbumViewHolder).bind(item, listener)
|
||||
is Song -> (holder as ArtistSongViewHolder).bind(item, listener)
|
||||
}
|
||||
}
|
||||
|
||||
super.onBindViewHolder(holder, position, payload)
|
||||
}
|
||||
|
||||
override fun shouldHighlightViewHolder(item: Item) =
|
||||
(item is Album && item.id == currentAlbum?.id) ||
|
||||
override fun shouldActivateViewHolder(position: Int): Boolean {
|
||||
val item = differ.currentList[position]
|
||||
return (item is Album && item.id == currentAlbum?.id) ||
|
||||
(item is Song && item.id == currentSong?.id)
|
||||
}
|
||||
|
||||
/** Update the current [album] that this adapter should highlight */
|
||||
fun highlightAlbum(album: Album?) {
|
||||
/** Update the [album] that this adapter should indicate playback */
|
||||
fun activateAlbum(album: Album?) {
|
||||
if (album == currentAlbum) return
|
||||
highlightImpl(currentAlbum, album)
|
||||
activateImpl(differ.currentList, currentAlbum, album)
|
||||
currentAlbum = album
|
||||
}
|
||||
|
||||
/** Update the [song] that this adapter should highlight */
|
||||
fun highlightSong(song: Song?) {
|
||||
/** Update the [song] that this adapter should indicate playback */
|
||||
fun activateSong(song: Song?) {
|
||||
if (song == currentSong) return
|
||||
highlightImpl(currentSong, song)
|
||||
activateImpl(differ.currentList, currentSong, song)
|
||||
currentSong = song
|
||||
}
|
||||
|
||||
|
|
|
@ -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.ActivationAdapter
|
||||
import org.oxycblt.auxio.ui.recycler.Header
|
||||
import org.oxycblt.auxio.ui.recycler.HeaderViewHolder
|
||||
import org.oxycblt.auxio.ui.recycler.Item
|
||||
|
@ -33,12 +34,11 @@ import org.oxycblt.auxio.ui.recycler.MenuItemListener
|
|||
import org.oxycblt.auxio.ui.recycler.SimpleItemCallback
|
||||
import org.oxycblt.auxio.util.context
|
||||
import org.oxycblt.auxio.util.inflater
|
||||
import org.oxycblt.auxio.util.logW
|
||||
|
||||
abstract class DetailAdapter<L : DetailAdapter.Listener>(
|
||||
private val listener: L,
|
||||
diffCallback: DiffUtil.ItemCallback<Item>
|
||||
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
||||
) : ActivationAdapter<RecyclerView.ViewHolder>() {
|
||||
@Suppress("LeakingThis") override fun getItemCount() = differ.currentList.size
|
||||
|
||||
override fun getItemViewType(position: Int) =
|
||||
|
@ -61,58 +61,27 @@ abstract class DetailAdapter<L : DetailAdapter.Listener>(
|
|||
override fun onBindViewHolder(
|
||||
holder: RecyclerView.ViewHolder,
|
||||
position: Int,
|
||||
payload: List<Any>
|
||||
payloads: List<Any>
|
||||
) {
|
||||
val item = differ.currentList[position]
|
||||
|
||||
if (payload.isEmpty()) {
|
||||
if (payloads.isEmpty()) {
|
||||
when (item) {
|
||||
is Header -> (holder as HeaderViewHolder).bind(item)
|
||||
is SortHeader -> (holder as SortHeaderViewHolder).bind(item, listener)
|
||||
}
|
||||
}
|
||||
|
||||
holder.itemView.isActivated = shouldHighlightViewHolder(item)
|
||||
super.onBindViewHolder(holder, position, payloads)
|
||||
}
|
||||
|
||||
protected val differ = AsyncListDiffer(this, diffCallback)
|
||||
|
||||
protected abstract fun shouldHighlightViewHolder(item: Item): Boolean
|
||||
|
||||
protected inline fun <reified T : Item> highlightImpl(oldItem: T?, newItem: T?) {
|
||||
if (oldItem != null) {
|
||||
val pos = differ.currentList.indexOfFirst { item -> item.id == oldItem.id && item is T }
|
||||
|
||||
if (pos > -1) {
|
||||
notifyItemChanged(pos, PAYLOAD_HIGHLIGHT_CHANGED)
|
||||
} else {
|
||||
logW("oldItem was not in adapter data")
|
||||
}
|
||||
}
|
||||
|
||||
if (newItem != null) {
|
||||
val pos = differ.currentList.indexOfFirst { item -> item is T && item.id == newItem.id }
|
||||
|
||||
if (pos > -1) {
|
||||
notifyItemChanged(pos, PAYLOAD_HIGHLIGHT_CHANGED)
|
||||
} else {
|
||||
logW("newItem was not in adapter data")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun submitList(list: List<Item>) {
|
||||
differ.submitList(list)
|
||||
}
|
||||
|
||||
companion object {
|
||||
// This payload value serves two purposes:
|
||||
// 1. It disables animations from notifyItemChanged, which looks bad when highlighting
|
||||
// ViewHolders.
|
||||
// 2. It instructs adapters to avoid re-binding information, and instead simply
|
||||
// change the highlight state.
|
||||
val PAYLOAD_HIGHLIGHT_CHANGED = Any()
|
||||
|
||||
val DIFFER =
|
||||
object : SimpleItemCallback<Item>() {
|
||||
override fun areItemsTheSame(oldItem: Item, newItem: Item): Boolean {
|
||||
|
|
|
@ -58,24 +58,27 @@ class GenreDetailAdapter(private val listener: Listener) :
|
|||
override fun onBindViewHolder(
|
||||
holder: RecyclerView.ViewHolder,
|
||||
position: Int,
|
||||
payload: List<Any>
|
||||
payloads: List<Any>
|
||||
) {
|
||||
if (payload.isEmpty()) {
|
||||
super.onBindViewHolder(holder, position, payloads)
|
||||
|
||||
if (payloads.isEmpty()) {
|
||||
when (val item = differ.currentList[position]) {
|
||||
is Genre -> (holder as GenreDetailViewHolder).bind(item, listener)
|
||||
is Song -> (holder as SongViewHolder).bind(item, listener)
|
||||
}
|
||||
}
|
||||
|
||||
super.onBindViewHolder(holder, position, payload)
|
||||
}
|
||||
|
||||
override fun shouldHighlightViewHolder(item: Item) = item is Song && item.id == currentSong?.id
|
||||
override fun shouldActivateViewHolder(position: Int): Boolean {
|
||||
val item = differ.currentList[position]
|
||||
return item is Song && item.id == currentSong?.id
|
||||
}
|
||||
|
||||
/** Update the [song] that this adapter should highlight */
|
||||
fun highlightSong(song: Song?) {
|
||||
/** Update the [song] that this adapter should indicate playback */
|
||||
fun activateSong(song: Song?) {
|
||||
if (song == currentSong) return
|
||||
highlightImpl(currentSong, song)
|
||||
activateImpl(differ.currentList, currentSong, song)
|
||||
currentSong = song
|
||||
}
|
||||
|
||||
|
|
|
@ -21,14 +21,15 @@ import android.os.Bundle
|
|||
import android.text.format.DateUtils
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import java.util.*
|
||||
import org.oxycblt.auxio.R
|
||||
import org.oxycblt.auxio.databinding.FragmentHomeListBinding
|
||||
import org.oxycblt.auxio.music.Album
|
||||
import org.oxycblt.auxio.music.Music
|
||||
import org.oxycblt.auxio.music.MusicParent
|
||||
import org.oxycblt.auxio.ui.DisplayMode
|
||||
import org.oxycblt.auxio.ui.Sort
|
||||
import org.oxycblt.auxio.ui.recycler.ActivationAdapter
|
||||
import org.oxycblt.auxio.ui.recycler.AlbumViewHolder
|
||||
import org.oxycblt.auxio.ui.recycler.Item
|
||||
import org.oxycblt.auxio.ui.recycler.MenuItemListener
|
||||
|
@ -55,6 +56,7 @@ class AlbumListFragment : HomeListFragment<Album>() {
|
|||
}
|
||||
|
||||
collectImmediately(homeModel.albums, homeAdapter::replaceList)
|
||||
collectImmediately(playbackModel.parent, ::handleParent)
|
||||
}
|
||||
|
||||
override fun getPopup(pos: Int): String? {
|
||||
|
@ -107,21 +109,47 @@ class AlbumListFragment : HomeListFragment<Album>() {
|
|||
}
|
||||
}
|
||||
|
||||
private fun handleParent(parent: MusicParent?) {
|
||||
if (parent is Album) {
|
||||
homeAdapter.activateAlbum(parent)
|
||||
} else {
|
||||
// Ignore playback not from albums
|
||||
homeAdapter.activateAlbum(null)
|
||||
}
|
||||
}
|
||||
|
||||
private class AlbumAdapter(private val listener: MenuItemListener) :
|
||||
RecyclerView.Adapter<AlbumViewHolder>() {
|
||||
ActivationAdapter<AlbumViewHolder>() {
|
||||
private val differ = SyncListDiffer(this, AlbumViewHolder.DIFFER)
|
||||
private var currentAlbum: Album? = null
|
||||
|
||||
override fun getItemCount() = differ.currentList.size
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) =
|
||||
AlbumViewHolder.new(parent)
|
||||
|
||||
override fun onBindViewHolder(holder: AlbumViewHolder, position: Int) {
|
||||
holder.bind(differ.currentList[position], listener)
|
||||
override fun onBindViewHolder(holder: AlbumViewHolder, position: Int, payloads: List<Any>) {
|
||||
super.onBindViewHolder(holder, position, payloads)
|
||||
|
||||
if (payloads.isEmpty()) {
|
||||
holder.bind(differ.currentList[position], listener)
|
||||
}
|
||||
}
|
||||
|
||||
override fun shouldActivateViewHolder(position: Int): Boolean {
|
||||
val item = differ.currentList[position]
|
||||
return item.id == currentAlbum?.id
|
||||
}
|
||||
|
||||
fun replaceList(newList: List<Album>) {
|
||||
differ.replaceList(newList)
|
||||
}
|
||||
|
||||
/** Update the [album] that this adapter should indicate playback */
|
||||
fun activateAlbum(album: Album?) {
|
||||
if (album == currentAlbum) return
|
||||
activateImpl(differ.currentList, currentAlbum, album)
|
||||
currentAlbum = album
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,13 +20,14 @@ package org.oxycblt.auxio.home.list
|
|||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import org.oxycblt.auxio.R
|
||||
import org.oxycblt.auxio.databinding.FragmentHomeListBinding
|
||||
import org.oxycblt.auxio.music.Artist
|
||||
import org.oxycblt.auxio.music.Music
|
||||
import org.oxycblt.auxio.music.MusicParent
|
||||
import org.oxycblt.auxio.ui.DisplayMode
|
||||
import org.oxycblt.auxio.ui.Sort
|
||||
import org.oxycblt.auxio.ui.recycler.ActivationAdapter
|
||||
import org.oxycblt.auxio.ui.recycler.ArtistViewHolder
|
||||
import org.oxycblt.auxio.ui.recycler.Item
|
||||
import org.oxycblt.auxio.ui.recycler.MenuItemListener
|
||||
|
@ -50,6 +51,7 @@ class ArtistListFragment : HomeListFragment<Artist>() {
|
|||
}
|
||||
|
||||
collectImmediately(homeModel.artists, homeAdapter::replaceList)
|
||||
collectImmediately(playbackModel.parent, ::handleParent)
|
||||
}
|
||||
|
||||
override fun getPopup(pos: Int): String? {
|
||||
|
@ -83,21 +85,51 @@ class ArtistListFragment : HomeListFragment<Artist>() {
|
|||
}
|
||||
}
|
||||
|
||||
private fun handleParent(parent: MusicParent?) {
|
||||
if (parent is Artist) {
|
||||
homeAdapter.activateArtist(parent)
|
||||
} else {
|
||||
// Ignore playback not from artists
|
||||
homeAdapter.activateArtist(null)
|
||||
}
|
||||
}
|
||||
|
||||
private class ArtistAdapter(private val listener: MenuItemListener) :
|
||||
RecyclerView.Adapter<ArtistViewHolder>() {
|
||||
ActivationAdapter<ArtistViewHolder>() {
|
||||
private val differ = SyncListDiffer(this, ArtistViewHolder.DIFFER)
|
||||
private var currentArtist: Artist? = null
|
||||
|
||||
override fun getItemCount() = differ.currentList.size
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) =
|
||||
ArtistViewHolder.new(parent)
|
||||
|
||||
override fun onBindViewHolder(holder: ArtistViewHolder, position: Int) {
|
||||
holder.bind(differ.currentList[position], listener)
|
||||
override fun onBindViewHolder(
|
||||
holder: ArtistViewHolder,
|
||||
position: Int,
|
||||
payloads: List<Any>
|
||||
) {
|
||||
super.onBindViewHolder(holder, position, payloads)
|
||||
|
||||
if (payloads.isEmpty()) {
|
||||
holder.bind(differ.currentList[position], listener)
|
||||
}
|
||||
}
|
||||
|
||||
override fun shouldActivateViewHolder(position: Int): Boolean {
|
||||
val item = differ.currentList[position]
|
||||
return item.id == currentArtist?.id
|
||||
}
|
||||
|
||||
fun replaceList(newList: List<Artist>) {
|
||||
differ.replaceList(newList)
|
||||
}
|
||||
|
||||
/** Update the [artist] that this adapter should indicate playback */
|
||||
fun activateArtist(artist: Artist?) {
|
||||
if (artist == currentArtist) return
|
||||
activateImpl(differ.currentList, currentArtist, artist)
|
||||
currentArtist = artist
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,13 +20,14 @@ package org.oxycblt.auxio.home.list
|
|||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import org.oxycblt.auxio.R
|
||||
import org.oxycblt.auxio.databinding.FragmentHomeListBinding
|
||||
import org.oxycblt.auxio.music.Genre
|
||||
import org.oxycblt.auxio.music.Music
|
||||
import org.oxycblt.auxio.music.MusicParent
|
||||
import org.oxycblt.auxio.ui.DisplayMode
|
||||
import org.oxycblt.auxio.ui.Sort
|
||||
import org.oxycblt.auxio.ui.recycler.ActivationAdapter
|
||||
import org.oxycblt.auxio.ui.recycler.GenreViewHolder
|
||||
import org.oxycblt.auxio.ui.recycler.Item
|
||||
import org.oxycblt.auxio.ui.recycler.MenuItemListener
|
||||
|
@ -50,6 +51,7 @@ class GenreListFragment : HomeListFragment<Genre>() {
|
|||
}
|
||||
|
||||
collectImmediately(homeModel.genres, homeAdapter::replaceList)
|
||||
collectImmediately(playbackModel.parent, ::handlePlayback)
|
||||
}
|
||||
|
||||
override fun getPopup(pos: Int): String? {
|
||||
|
@ -83,21 +85,47 @@ class GenreListFragment : HomeListFragment<Genre>() {
|
|||
}
|
||||
}
|
||||
|
||||
private fun handlePlayback(parent: MusicParent?) {
|
||||
if (parent is Genre) {
|
||||
homeAdapter.activateGenre(parent)
|
||||
} else {
|
||||
// Ignore playback not from genres
|
||||
homeAdapter.activateGenre(null)
|
||||
}
|
||||
}
|
||||
|
||||
private class GenreAdapter(private val listener: MenuItemListener) :
|
||||
RecyclerView.Adapter<GenreViewHolder>() {
|
||||
ActivationAdapter<GenreViewHolder>() {
|
||||
private val differ = SyncListDiffer(this, GenreViewHolder.DIFFER)
|
||||
private var currentGenre: Genre? = null
|
||||
|
||||
override fun getItemCount() = differ.currentList.size
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) =
|
||||
GenreViewHolder.new(parent)
|
||||
|
||||
override fun onBindViewHolder(holder: GenreViewHolder, position: Int) {
|
||||
holder.bind(differ.currentList[position], listener)
|
||||
override fun onBindViewHolder(holder: GenreViewHolder, position: Int, payloads: List<Any>) {
|
||||
super.onBindViewHolder(holder, position, payloads)
|
||||
|
||||
if (payloads.isEmpty()) {
|
||||
holder.bind(differ.currentList[position], listener)
|
||||
}
|
||||
}
|
||||
|
||||
override fun shouldActivateViewHolder(position: Int): Boolean {
|
||||
val item = differ.currentList[position]
|
||||
return item.id == currentGenre?.id
|
||||
}
|
||||
|
||||
fun replaceList(newList: List<Genre>) {
|
||||
differ.replaceList(newList)
|
||||
}
|
||||
|
||||
/** Update the [genre] that this adapter should indicate playback */
|
||||
fun activateGenre(genre: Genre?) {
|
||||
if (genre == currentGenre) return
|
||||
activateImpl(differ.currentList, currentGenre, genre)
|
||||
currentGenre = genre
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,14 +21,15 @@ import android.os.Bundle
|
|||
import android.text.format.DateUtils
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import java.util.Formatter
|
||||
import org.oxycblt.auxio.R
|
||||
import org.oxycblt.auxio.databinding.FragmentHomeListBinding
|
||||
import org.oxycblt.auxio.music.MusicParent
|
||||
import org.oxycblt.auxio.music.Song
|
||||
import org.oxycblt.auxio.settings.Settings
|
||||
import org.oxycblt.auxio.ui.DisplayMode
|
||||
import org.oxycblt.auxio.ui.Sort
|
||||
import org.oxycblt.auxio.ui.recycler.ActivationAdapter
|
||||
import org.oxycblt.auxio.ui.recycler.Item
|
||||
import org.oxycblt.auxio.ui.recycler.MenuItemListener
|
||||
import org.oxycblt.auxio.ui.recycler.SongViewHolder
|
||||
|
@ -57,6 +58,7 @@ class SongListFragment : HomeListFragment<Song>() {
|
|||
}
|
||||
|
||||
collectImmediately(homeModel.songs, homeAdapter::replaceList)
|
||||
collectImmediately(playbackModel.song, playbackModel.parent, ::handlePlayback)
|
||||
}
|
||||
|
||||
override fun getPopup(pos: Int): String? {
|
||||
|
@ -111,21 +113,47 @@ class SongListFragment : HomeListFragment<Song>() {
|
|||
}
|
||||
}
|
||||
|
||||
private fun handlePlayback(song: Song?, parent: MusicParent?) {
|
||||
if (parent == null) {
|
||||
homeAdapter.activateSong(song)
|
||||
} else {
|
||||
// Ignore playback that is not from all songs
|
||||
homeAdapter.activateSong(null)
|
||||
}
|
||||
}
|
||||
|
||||
private class SongAdapter(private val listener: MenuItemListener) :
|
||||
RecyclerView.Adapter<SongViewHolder>() {
|
||||
ActivationAdapter<SongViewHolder>() {
|
||||
private val differ = SyncListDiffer(this, SongViewHolder.DIFFER)
|
||||
private var currentSong: Song? = null
|
||||
|
||||
override fun getItemCount() = differ.currentList.size
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) =
|
||||
SongViewHolder.new(parent)
|
||||
|
||||
override fun onBindViewHolder(holder: SongViewHolder, position: Int) {
|
||||
holder.bind(differ.currentList[position], listener)
|
||||
override fun onBindViewHolder(holder: SongViewHolder, position: Int, payloads: List<Any>) {
|
||||
super.onBindViewHolder(holder, position, payloads)
|
||||
|
||||
if (payloads.isEmpty()) {
|
||||
holder.bind(differ.currentList[position], listener)
|
||||
}
|
||||
}
|
||||
|
||||
override fun shouldActivateViewHolder(position: Int): Boolean {
|
||||
val item = differ.currentList[position]
|
||||
return item.id == currentSong?.id
|
||||
}
|
||||
|
||||
fun replaceList(newList: List<Song>) {
|
||||
differ.replaceList(newList)
|
||||
}
|
||||
|
||||
/** Update the [song] that this adapter should indicate playback */
|
||||
fun activateSong(song: Song?) {
|
||||
if (song == currentSong) return
|
||||
activateImpl(differ.currentList, currentSong, song)
|
||||
currentSong = song
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ import androidx.recyclerview.widget.AdapterListUpdateCallback
|
|||
import androidx.recyclerview.widget.AsyncListDiffer
|
||||
import androidx.recyclerview.widget.DiffUtil
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import org.oxycblt.auxio.util.logW
|
||||
|
||||
/**
|
||||
* The base for all items in Auxio. Any datatype can derive this type and gain some behavior not
|
||||
|
@ -171,3 +172,43 @@ abstract class SimpleItemCallback<T : Item> : DiffUtil.ItemCallback<T>() {
|
|||
return oldItem.id == newItem.id
|
||||
}
|
||||
}
|
||||
|
||||
abstract class ActivationAdapter<VH : RecyclerView.ViewHolder> : RecyclerView.Adapter<VH>() {
|
||||
override fun onBindViewHolder(holder: VH, position: Int) = throw UnsupportedOperationException()
|
||||
|
||||
override fun onBindViewHolder(holder: VH, position: Int, payloads: List<Any>) {
|
||||
holder.itemView.isActivated = shouldActivateViewHolder(position)
|
||||
}
|
||||
|
||||
protected abstract fun shouldActivateViewHolder(position: Int): Boolean
|
||||
|
||||
protected inline fun <reified T : Item> activateImpl(
|
||||
currentList: List<Item>,
|
||||
oldItem: T?,
|
||||
newItem: T?
|
||||
) {
|
||||
if (oldItem != null) {
|
||||
val pos = currentList.indexOfFirst { item -> item.id == oldItem.id && item is T }
|
||||
|
||||
if (pos > -1) {
|
||||
notifyItemChanged(pos, PAYLOAD_ACTIVATION_CHANGED)
|
||||
} else {
|
||||
logW("oldItem was not in adapter data")
|
||||
}
|
||||
}
|
||||
|
||||
if (newItem != null) {
|
||||
val pos = currentList.indexOfFirst { item -> item is T && item.id == newItem.id }
|
||||
|
||||
if (pos > -1) {
|
||||
notifyItemChanged(pos, PAYLOAD_ACTIVATION_CHANGED)
|
||||
} else {
|
||||
logW("newItem was not in adapter data")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
val PAYLOAD_ACTIVATION_CHANGED = Any()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -213,8 +213,7 @@ list similar to this:
|
|||
`Item being displayed | Header Item | Child Item | Child Item | Child Item...`
|
||||
|
||||
Note that the actual dataset used is more complex once sorting and disc numbers is taken into
|
||||
account. Item highlighting and certain shared ViewHolders are already managed by the `DetailAdapter`
|
||||
super-class, which should be implemented by all adapters in the detail UI.
|
||||
account.
|
||||
|
||||
#### `.home`
|
||||
This package contains the components for the "home" UI in Auxio, or the UI that the user first sees
|
||||
|
|
Loading…
Reference in a new issue