queue: rework internal queue system
Rework the queue internally to decouple the queue from playback and better respond to reshuffling. This is being implemented under the assumption that I will be implementing the sliding queue eventually.
This commit is contained in:
parent
496b72ca78
commit
6c59a03042
13 changed files with 178 additions and 91 deletions
|
@ -28,10 +28,10 @@ import org.oxycblt.auxio.databinding.ItemSortHeaderBinding
|
|||
import org.oxycblt.auxio.ui.recycler.AsyncBackingData
|
||||
import org.oxycblt.auxio.ui.recycler.BindingViewHolder
|
||||
import org.oxycblt.auxio.ui.recycler.Header
|
||||
import org.oxycblt.auxio.ui.recycler.HeaderViewHolder
|
||||
import org.oxycblt.auxio.ui.recycler.Item
|
||||
import org.oxycblt.auxio.ui.recycler.MenuItemListener
|
||||
import org.oxycblt.auxio.ui.recycler.MultiAdapter
|
||||
import org.oxycblt.auxio.ui.recycler.NewHeaderViewHolder
|
||||
import org.oxycblt.auxio.ui.recycler.SimpleItemCallback
|
||||
import org.oxycblt.auxio.util.context
|
||||
import org.oxycblt.auxio.util.inflater
|
||||
|
@ -70,14 +70,14 @@ abstract class DetailAdapter<L : DetailAdapter.Listener>(
|
|||
|
||||
override fun getCreatorFromItem(item: Item) =
|
||||
when (item) {
|
||||
is Header -> NewHeaderViewHolder.CREATOR
|
||||
is Header -> HeaderViewHolder.CREATOR
|
||||
is SortHeader -> SortHeaderViewHolder.CREATOR
|
||||
else -> null
|
||||
}
|
||||
|
||||
override fun getCreatorFromViewType(viewType: Int) =
|
||||
when (viewType) {
|
||||
NewHeaderViewHolder.CREATOR.viewType -> NewHeaderViewHolder.CREATOR
|
||||
HeaderViewHolder.CREATOR.viewType -> HeaderViewHolder.CREATOR
|
||||
SortHeaderViewHolder.CREATOR.viewType -> SortHeaderViewHolder.CREATOR
|
||||
else -> null
|
||||
}
|
||||
|
@ -90,7 +90,7 @@ abstract class DetailAdapter<L : DetailAdapter.Listener>(
|
|||
) {
|
||||
if (payload.isEmpty()) {
|
||||
when (item) {
|
||||
is Header -> (viewHolder as NewHeaderViewHolder).bind(item, Unit)
|
||||
is Header -> (viewHolder as HeaderViewHolder).bind(item, Unit)
|
||||
is SortHeader -> (viewHolder as SortHeaderViewHolder).bind(item, listener)
|
||||
}
|
||||
}
|
||||
|
@ -111,7 +111,7 @@ abstract class DetailAdapter<L : DetailAdapter.Listener>(
|
|||
override fun areItemsTheSame(oldItem: Item, newItem: Item): Boolean {
|
||||
return when {
|
||||
oldItem is Header && newItem is Header ->
|
||||
NewHeaderViewHolder.DIFFER.areItemsTheSame(oldItem, newItem)
|
||||
HeaderViewHolder.DIFFER.areItemsTheSame(oldItem, newItem)
|
||||
oldItem is SortHeader && newItem is SortHeader ->
|
||||
SortHeaderViewHolder.DIFFER.areItemsTheSame(oldItem, newItem)
|
||||
else -> false
|
||||
|
|
|
@ -114,8 +114,6 @@ class PlaybackPanelFragment :
|
|||
collectImmediately(playbackModel.repeatMode, ::updateRepeat)
|
||||
collectImmediately(playbackModel.isPlaying, ::updatePlaying)
|
||||
collectImmediately(playbackModel.isShuffled, ::updateShuffled)
|
||||
collectImmediately(playbackModel.nextUp, ::updateNextUp)
|
||||
|
||||
logD("Fragment Created")
|
||||
}
|
||||
|
||||
|
@ -195,8 +193,4 @@ class PlaybackPanelFragment :
|
|||
private fun updateShuffled(isShuffled: Boolean) {
|
||||
requireBinding().playbackShuffle.isActivated = isShuffled
|
||||
}
|
||||
|
||||
private fun updateNextUp(nextUp: List<Song>) {
|
||||
queueItem.isEnabled = nextUp.isNotEmpty()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,7 +36,6 @@ import org.oxycblt.auxio.playback.state.PlaybackStateManager
|
|||
import org.oxycblt.auxio.playback.state.RepeatMode
|
||||
import org.oxycblt.auxio.settings.Settings
|
||||
import org.oxycblt.auxio.util.application
|
||||
import org.oxycblt.auxio.util.logD
|
||||
import org.oxycblt.auxio.util.logE
|
||||
|
||||
/**
|
||||
|
@ -77,11 +76,6 @@ class PlaybackViewModel(application: Application) :
|
|||
val isShuffled: StateFlow<Boolean>
|
||||
get() = _isShuffled
|
||||
|
||||
private val _nextUp = MutableStateFlow(listOf<Song>())
|
||||
/** The queue, without the previous items. */
|
||||
val nextUp: StateFlow<List<Song>>
|
||||
get() = _nextUp
|
||||
|
||||
init {
|
||||
musicStore.addCallback(this)
|
||||
playbackManager.addCallback(this)
|
||||
|
@ -196,39 +190,6 @@ class PlaybackViewModel(application: Application) :
|
|||
playbackManager.prev()
|
||||
}
|
||||
|
||||
/**
|
||||
* Go to an item in the queue using it's recyclerview adapter index. No-ops if out of bounds.
|
||||
*/
|
||||
fun goto(adapterIndex: Int) {
|
||||
val index = adapterIndex + (playbackManager.queue.size - _nextUp.value.size)
|
||||
logD(adapterIndex)
|
||||
logD(playbackManager.queue.size - _nextUp.value.size)
|
||||
|
||||
if (index in playbackManager.queue.indices) {
|
||||
playbackManager.goto(index)
|
||||
}
|
||||
}
|
||||
|
||||
/** Remove a queue item using it's recyclerview adapter index. */
|
||||
fun removeQueueDataItem(adapterIndex: Int) {
|
||||
val index = adapterIndex + (playbackManager.queue.size - _nextUp.value.size)
|
||||
if (index in playbackManager.queue.indices) {
|
||||
playbackManager.removeQueueItem(index)
|
||||
}
|
||||
}
|
||||
/** Move queue items using their recyclerview adapter indices. */
|
||||
fun moveQueueDataItems(adapterFrom: Int, adapterTo: Int): Boolean {
|
||||
val delta = (playbackManager.queue.size - _nextUp.value.size)
|
||||
val from = adapterFrom + delta
|
||||
val to = adapterTo + delta
|
||||
if (from in playbackManager.queue.indices && to in playbackManager.queue.indices) {
|
||||
playbackManager.moveQueueItem(from, to)
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
/** Add a [Song] to the top of the queue. */
|
||||
fun playNext(song: Song) {
|
||||
playbackManager.playNext(song)
|
||||
|
@ -334,17 +295,11 @@ class PlaybackViewModel(application: Application) :
|
|||
|
||||
override fun onIndexMoved(index: Int) {
|
||||
_song.value = playbackManager.song
|
||||
_nextUp.value = playbackManager.queue.slice(index + 1 until playbackManager.queue.size)
|
||||
}
|
||||
|
||||
override fun onQueueChanged(index: Int, queue: List<Song>) {
|
||||
_nextUp.value = queue.slice(index + 1 until queue.size)
|
||||
}
|
||||
|
||||
override fun onNewPlayback(index: Int, queue: List<Song>, parent: MusicParent?) {
|
||||
_song.value = playbackManager.song
|
||||
_parent.value = playbackManager.parent
|
||||
_nextUp.value = queue.slice(index + 1 until queue.size)
|
||||
}
|
||||
|
||||
override fun onPositionChanged(positionMs: Long) {
|
||||
|
|
|
@ -31,7 +31,7 @@ import org.oxycblt.auxio.music.Song
|
|||
import org.oxycblt.auxio.ui.recycler.*
|
||||
import org.oxycblt.auxio.util.*
|
||||
|
||||
class QueueAdapter(private val listener: QueueItemListener) :
|
||||
class QueueAdapter(listener: QueueItemListener) :
|
||||
MonoAdapter<Song, QueueItemListener, QueueSongViewHolder>(listener) {
|
||||
override val data = SyncBackingData(this, QueueSongViewHolder.DIFFER)
|
||||
override val creator = QueueSongViewHolder.CREATOR
|
||||
|
|
|
@ -27,7 +27,6 @@ import kotlin.math.max
|
|||
import kotlin.math.min
|
||||
import kotlin.math.sign
|
||||
import org.oxycblt.auxio.R
|
||||
import org.oxycblt.auxio.playback.PlaybackViewModel
|
||||
import org.oxycblt.auxio.util.getDimenSafe
|
||||
import org.oxycblt.auxio.util.logD
|
||||
|
||||
|
@ -37,7 +36,7 @@ import org.oxycblt.auxio.util.logD
|
|||
* hot garbage. This shouldn't have *too many* UI bugs. I hope.
|
||||
* @author OxygenCobalt
|
||||
*/
|
||||
class QueueDragCallback(private val playbackModel: PlaybackViewModel) : ItemTouchHelper.Callback() {
|
||||
class QueueDragCallback(private val playbackModel: QueueViewModel) : ItemTouchHelper.Callback() {
|
||||
private var shouldLift = true
|
||||
|
||||
override fun getMovementFlags(
|
||||
|
|
|
@ -20,14 +20,12 @@ package org.oxycblt.auxio.playback.queue
|
|||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.activityViewModels
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import androidx.recyclerview.widget.ItemTouchHelper
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import org.oxycblt.auxio.databinding.FragmentQueueBinding
|
||||
import org.oxycblt.auxio.music.Song
|
||||
import org.oxycblt.auxio.playback.PlaybackViewModel
|
||||
import org.oxycblt.auxio.ui.fragment.ViewBindingFragment
|
||||
import org.oxycblt.auxio.util.androidActivityViewModels
|
||||
import org.oxycblt.auxio.util.collectImmediately
|
||||
|
||||
/**
|
||||
|
@ -35,10 +33,10 @@ import org.oxycblt.auxio.util.collectImmediately
|
|||
* @author OxygenCobalt
|
||||
*/
|
||||
class QueueFragment : ViewBindingFragment<FragmentQueueBinding>(), QueueItemListener {
|
||||
private val playbackModel: PlaybackViewModel by androidActivityViewModels()
|
||||
private val queueModel: QueueViewModel by activityViewModels()
|
||||
private val queueAdapter = QueueAdapter(this)
|
||||
private val touchHelper: ItemTouchHelper by lifecycleObject {
|
||||
ItemTouchHelper(QueueDragCallback(playbackModel))
|
||||
ItemTouchHelper(QueueDragCallback(queueModel))
|
||||
}
|
||||
|
||||
override fun onCreateBinding(inflater: LayoutInflater) = FragmentQueueBinding.inflate(inflater)
|
||||
|
@ -53,7 +51,7 @@ class QueueFragment : ViewBindingFragment<FragmentQueueBinding>(), QueueItemList
|
|||
|
||||
// --- VIEWMODEL SETUP ----
|
||||
|
||||
collectImmediately(playbackModel.nextUp, ::updateQueue)
|
||||
collectImmediately(queueModel.queue, ::updateQueue)
|
||||
}
|
||||
|
||||
override fun onDestroyBinding(binding: FragmentQueueBinding) {
|
||||
|
@ -62,19 +60,20 @@ class QueueFragment : ViewBindingFragment<FragmentQueueBinding>(), QueueItemList
|
|||
}
|
||||
|
||||
override fun onClick(viewHolder: RecyclerView.ViewHolder) {
|
||||
playbackModel.goto(viewHolder.bindingAdapterPosition)
|
||||
queueModel.goto(viewHolder.bindingAdapterPosition)
|
||||
}
|
||||
|
||||
override fun onPickUp(viewHolder: RecyclerView.ViewHolder) {
|
||||
touchHelper.startDrag(viewHolder)
|
||||
}
|
||||
|
||||
private fun updateQueue(queue: List<Song>) {
|
||||
if (queue.isEmpty()) {
|
||||
findNavController().navigateUp()
|
||||
return
|
||||
private fun updateQueue(queue: QueueViewModel.QueueData) {
|
||||
if (queue.nonTrivial) {
|
||||
// nonTrivial implies that using a synced submitList would be slow, replace the list
|
||||
// instead.
|
||||
queueAdapter.data.replaceList(queue.queue)
|
||||
} else {
|
||||
queueAdapter.data.submitList(queue.queue)
|
||||
}
|
||||
|
||||
queueAdapter.data.submitList(queue.toMutableList())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,96 @@
|
|||
/*
|
||||
* Copyright (c) 2022 Auxio Project
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.oxycblt.auxio.playback.queue
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import org.oxycblt.auxio.music.MusicParent
|
||||
import org.oxycblt.auxio.music.Song
|
||||
import org.oxycblt.auxio.playback.state.PlaybackStateManager
|
||||
import org.oxycblt.auxio.util.logD
|
||||
|
||||
class QueueViewModel : ViewModel(), PlaybackStateManager.Callback {
|
||||
private val playbackManager = PlaybackStateManager.getInstance()
|
||||
|
||||
data class QueueData(val queue: List<Song>, val nonTrivial: Boolean)
|
||||
|
||||
private val _queue = MutableStateFlow(QueueData(listOf(), false))
|
||||
val queue: StateFlow<QueueData> = _queue
|
||||
|
||||
init {
|
||||
playbackManager.addCallback(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* Go to an item in the queue using it's recyclerview adapter index. No-ops if out of bounds.
|
||||
*/
|
||||
fun goto(adapterIndex: Int) {
|
||||
val index = adapterIndex + (playbackManager.queue.size - _queue.value.queue.size)
|
||||
logD(adapterIndex)
|
||||
logD(playbackManager.queue.size - _queue.value.queue.size)
|
||||
|
||||
if (index in playbackManager.queue.indices) {
|
||||
playbackManager.goto(index)
|
||||
}
|
||||
}
|
||||
|
||||
/** Remove a queue item using it's recyclerview adapter index. */
|
||||
fun removeQueueDataItem(adapterIndex: Int) {
|
||||
val index = adapterIndex + (playbackManager.queue.size - _queue.value.queue.size)
|
||||
if (index in playbackManager.queue.indices) {
|
||||
playbackManager.removeQueueItem(index)
|
||||
}
|
||||
}
|
||||
/** Move queue items using their recyclerview adapter indices. */
|
||||
fun moveQueueDataItems(adapterFrom: Int, adapterTo: Int): Boolean {
|
||||
val delta = (playbackManager.queue.size - _queue.value.queue.size)
|
||||
val from = adapterFrom + delta
|
||||
val to = adapterTo + delta
|
||||
if (from in playbackManager.queue.indices && to in playbackManager.queue.indices) {
|
||||
playbackManager.moveQueueItem(from, to)
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
override fun onIndexMoved(index: Int) {
|
||||
_queue.value = QueueData(generateQueue(index, playbackManager.queue), false)
|
||||
}
|
||||
|
||||
override fun onQueueChanged(queue: List<Song>) {
|
||||
_queue.value = QueueData(generateQueue(playbackManager.index, queue), false)
|
||||
}
|
||||
|
||||
override fun onQueueReworked(index: Int, queue: List<Song>) {
|
||||
_queue.value = QueueData(generateQueue(index, queue), true)
|
||||
}
|
||||
|
||||
override fun onNewPlayback(index: Int, queue: List<Song>, parent: MusicParent?) {
|
||||
_queue.value = QueueData(generateQueue(index, queue), true)
|
||||
}
|
||||
|
||||
private fun generateQueue(index: Int, queue: List<Song>) =
|
||||
queue.slice(index + 1..playbackManager.queue.lastIndex)
|
||||
|
||||
override fun onCleared() {
|
||||
super.onCleared()
|
||||
playbackManager.removeCallback(this)
|
||||
}
|
||||
}
|
|
@ -280,7 +280,7 @@ class PlaybackStateManager private constructor() {
|
|||
val library = musicStore.library ?: return
|
||||
val song = song ?: return
|
||||
applyNewQueue(library, settings, shuffled, song)
|
||||
notifyQueueChanged()
|
||||
notifyQueueReworked()
|
||||
notifyShuffledChanged()
|
||||
}
|
||||
|
||||
|
@ -464,7 +464,13 @@ class PlaybackStateManager private constructor() {
|
|||
|
||||
private fun notifyQueueChanged() {
|
||||
for (callback in callbacks) {
|
||||
callback.onQueueChanged(index, queue)
|
||||
callback.onQueueChanged(queue)
|
||||
}
|
||||
}
|
||||
|
||||
private fun notifyQueueReworked() {
|
||||
for (callback in callbacks) {
|
||||
callback.onQueueReworked(index, queue)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -529,8 +535,11 @@ class PlaybackStateManager private constructor() {
|
|||
/** Called when the index is moved, but the queue does not change. This changes the song. */
|
||||
fun onIndexMoved(index: Int) {}
|
||||
|
||||
/** Called when the queue and/or index changed, but the song has not. */
|
||||
fun onQueueChanged(index: Int, queue: List<Song>) {}
|
||||
/** Called when the queue has changed in a way that does not change the index or song. */
|
||||
fun onQueueChanged(queue: List<Song>) {}
|
||||
|
||||
/** Called when the queue and index has changed, but the song has not changed.. */
|
||||
fun onQueueReworked(index: Int, queue: List<Song>) {}
|
||||
|
||||
/** Called when playback is changed completely, with a new index, queue, and parent. */
|
||||
fun onNewPlayback(index: Int, queue: List<Song>, parent: MusicParent?) {}
|
||||
|
|
|
@ -20,6 +20,8 @@ package org.oxycblt.auxio.playback.system
|
|||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.graphics.Bitmap
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.os.SystemClock
|
||||
import android.support.v4.media.MediaDescriptionCompat
|
||||
import android.support.v4.media.MediaMetadataCompat
|
||||
|
@ -101,10 +103,15 @@ class MediaSessionComponent(
|
|||
invalidateSessionState()
|
||||
}
|
||||
|
||||
override fun onQueueChanged(index: Int, queue: List<Song>) {
|
||||
override fun onQueueChanged(queue: List<Song>) {
|
||||
updateQueue(queue)
|
||||
}
|
||||
|
||||
override fun onQueueReworked(index: Int, queue: List<Song>) {
|
||||
updateQueue(queue)
|
||||
invalidateSessionState()
|
||||
}
|
||||
|
||||
override fun onNewPlayback(index: Int, queue: List<Song>, parent: MusicParent?) {
|
||||
updateMediaMetadata(playbackManager.song, parent)
|
||||
updateQueue(queue)
|
||||
|
@ -118,8 +125,8 @@ class MediaSessionComponent(
|
|||
return
|
||||
}
|
||||
|
||||
// We would leave the artist field null if it didn't exist and let downstream consumers
|
||||
// handle it, but that would break the notification display.
|
||||
// Note: We would leave the artist field null if it didn't exist and let downstream
|
||||
// consumers handle it, but that would break the notification display.
|
||||
val title = song.resolveName(context)
|
||||
val artist = song.resolveIndividualArtistName(context)
|
||||
val builder =
|
||||
|
@ -180,11 +187,11 @@ class MediaSessionComponent(
|
|||
private fun updateQueue(queue: List<Song>) {
|
||||
val queueItems =
|
||||
queue.mapIndexed { i, song ->
|
||||
// Since we usually have to load many songs into the queue, use the Cover URI
|
||||
// Since we usually have to load many songs into the queue, use the MediaStore URI
|
||||
// instead of loading a bitmap.
|
||||
val description =
|
||||
MediaDescriptionCompat.Builder()
|
||||
.setMediaId(song.id.toString())
|
||||
.setMediaId("Song:${song.id}")
|
||||
.setTitle(song.resolveName(context))
|
||||
.setSubtitle(song.resolveIndividualArtistName(context))
|
||||
.setIconUri(song.album.coverUri)
|
||||
|
@ -245,8 +252,8 @@ class MediaSessionComponent(
|
|||
invalidateSessionState()
|
||||
|
||||
if (!playbackManager.isPlaying) {
|
||||
// Hack around issue where the position won't update after a seek (but only when it's
|
||||
// paused). Apparently this can be fixed by re-posting the notification, but not always
|
||||
// Hack around issue where the position won't update after a seek when paused.
|
||||
// Apparently this can be fixed by re-posting the notification, but not always
|
||||
// when we invalidate the state (that will cause us to be rate-limited), and also not
|
||||
// always when we seek (that will also cause us to be rate-limited). Someone looked at
|
||||
// this system and said it was well-designed.
|
||||
|
@ -256,6 +263,31 @@ class MediaSessionComponent(
|
|||
|
||||
// --- MEDIASESSION CALLBACKS ---
|
||||
|
||||
override fun onPlayFromMediaId(mediaId: String?, extras: Bundle?) {
|
||||
super.onPlayFromMediaId(mediaId, extras)
|
||||
// STUB: Unimplemented
|
||||
}
|
||||
|
||||
override fun onPlayFromUri(uri: Uri?, extras: Bundle?) {
|
||||
super.onPlayFromUri(uri, extras)
|
||||
// STUB: Unimplemented
|
||||
}
|
||||
|
||||
override fun onPlayFromSearch(query: String?, extras: Bundle?) {
|
||||
super.onPlayFromSearch(query, extras)
|
||||
// STUB: Unimplemented
|
||||
}
|
||||
|
||||
override fun onAddQueueItem(description: MediaDescriptionCompat?) {
|
||||
super.onAddQueueItem(description)
|
||||
// STUB: Unimplemented
|
||||
}
|
||||
|
||||
override fun onRemoveQueueItem(description: MediaDescriptionCompat?) {
|
||||
super.onRemoveQueueItem(description)
|
||||
// STUB: Unimplemented
|
||||
}
|
||||
|
||||
override fun onPlay() {
|
||||
playbackManager.isPlaying = true
|
||||
}
|
||||
|
|
|
@ -27,10 +27,10 @@ import org.oxycblt.auxio.ui.recycler.ArtistViewHolder
|
|||
import org.oxycblt.auxio.ui.recycler.AsyncBackingData
|
||||
import org.oxycblt.auxio.ui.recycler.GenreViewHolder
|
||||
import org.oxycblt.auxio.ui.recycler.Header
|
||||
import org.oxycblt.auxio.ui.recycler.HeaderViewHolder
|
||||
import org.oxycblt.auxio.ui.recycler.Item
|
||||
import org.oxycblt.auxio.ui.recycler.MenuItemListener
|
||||
import org.oxycblt.auxio.ui.recycler.MultiAdapter
|
||||
import org.oxycblt.auxio.ui.recycler.NewHeaderViewHolder
|
||||
import org.oxycblt.auxio.ui.recycler.SimpleItemCallback
|
||||
import org.oxycblt.auxio.ui.recycler.SongViewHolder
|
||||
|
||||
|
@ -43,7 +43,7 @@ class SearchAdapter(listener: MenuItemListener) : MultiAdapter<MenuItemListener>
|
|||
is Album -> AlbumViewHolder.CREATOR
|
||||
is Artist -> ArtistViewHolder.CREATOR
|
||||
is Genre -> GenreViewHolder.CREATOR
|
||||
is Header -> NewHeaderViewHolder.CREATOR
|
||||
is Header -> HeaderViewHolder.CREATOR
|
||||
else -> null
|
||||
}
|
||||
|
||||
|
@ -53,7 +53,7 @@ class SearchAdapter(listener: MenuItemListener) : MultiAdapter<MenuItemListener>
|
|||
AlbumViewHolder.CREATOR.viewType -> AlbumViewHolder.CREATOR
|
||||
ArtistViewHolder.CREATOR.viewType -> ArtistViewHolder.CREATOR
|
||||
GenreViewHolder.CREATOR.viewType -> GenreViewHolder.CREATOR
|
||||
NewHeaderViewHolder.CREATOR.viewType -> NewHeaderViewHolder.CREATOR
|
||||
HeaderViewHolder.CREATOR.viewType -> HeaderViewHolder.CREATOR
|
||||
else -> null
|
||||
}
|
||||
|
||||
|
@ -68,7 +68,7 @@ class SearchAdapter(listener: MenuItemListener) : MultiAdapter<MenuItemListener>
|
|||
is Album -> (viewHolder as AlbumViewHolder).bind(item, listener)
|
||||
is Artist -> (viewHolder as ArtistViewHolder).bind(item, listener)
|
||||
is Genre -> (viewHolder as GenreViewHolder).bind(item, listener)
|
||||
is Header -> (viewHolder as NewHeaderViewHolder).bind(item, Unit)
|
||||
is Header -> (viewHolder as HeaderViewHolder).bind(item, Unit)
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
|
@ -87,7 +87,7 @@ class SearchAdapter(listener: MenuItemListener) : MultiAdapter<MenuItemListener>
|
|||
oldItem is Genre && newItem is Genre ->
|
||||
GenreViewHolder.DIFFER.areItemsTheSame(oldItem, newItem)
|
||||
oldItem is Header && newItem is Header ->
|
||||
NewHeaderViewHolder.DIFFER.areItemsTheSame(oldItem, newItem)
|
||||
HeaderViewHolder.DIFFER.areItemsTheSame(oldItem, newItem)
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
|
|
@ -201,7 +201,7 @@ private constructor(
|
|||
* The Shared ViewHolder for a [Header].
|
||||
* @author OxygenCobalt
|
||||
*/
|
||||
class NewHeaderViewHolder private constructor(private val binding: ItemHeaderBinding) :
|
||||
class HeaderViewHolder private constructor(private val binding: ItemHeaderBinding) :
|
||||
BindingViewHolder<Header, Unit>(binding.root) {
|
||||
|
||||
override fun bind(item: Header, listener: Unit) {
|
||||
|
@ -210,12 +210,12 @@ class NewHeaderViewHolder private constructor(private val binding: ItemHeaderBin
|
|||
|
||||
companion object {
|
||||
val CREATOR =
|
||||
object : Creator<NewHeaderViewHolder> {
|
||||
object : Creator<HeaderViewHolder> {
|
||||
override val viewType: Int
|
||||
get() = IntegerTable.ITEM_TYPE_HEADER
|
||||
|
||||
override fun create(context: Context) =
|
||||
NewHeaderViewHolder(ItemHeaderBinding.inflate(context.inflater))
|
||||
HeaderViewHolder(ItemHeaderBinding.inflate(context.inflater))
|
||||
}
|
||||
|
||||
val DIFFER =
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
android:layout_height="wrap_content">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/interact_body"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?attr/selectableItemBackground">
|
||||
|
|
|
@ -81,6 +81,8 @@
|
|||
<string name="lbl_shuffle">Shuffle</string>
|
||||
|
||||
<string name="lbl_queue">Queue</string>
|
||||
<string name="lbl_next_up">Next up</string>
|
||||
<string name="lbl_later">Later</string>
|
||||
<string name="lbl_play_next">Play next</string>
|
||||
<string name="lbl_queue_add">Add to queue</string>
|
||||
<string name="lbl_queue_added">Added to queue</string>
|
||||
|
|
Loading…
Reference in a new issue