Update action header structure
Create a seperate ActionHeader object for headers with actions attached alongside a new viewholder to accompany it. This allows action headers to be created easier without creating your own viewholder.
This commit is contained in:
parent
029a4b1ff6
commit
cc72ebc251
8 changed files with 76 additions and 62 deletions
|
@ -1,6 +1,8 @@
|
|||
package org.oxycblt.auxio.music
|
||||
|
||||
import android.net.Uri
|
||||
import android.widget.ImageButton
|
||||
import androidx.annotation.DrawableRes
|
||||
|
||||
// --- MUSIC MODELS ---
|
||||
|
||||
|
@ -189,10 +191,20 @@ data class Genre(
|
|||
|
||||
/**
|
||||
* A data object used solely for the "Header" UI element. Inherits [BaseModel].
|
||||
* @property isAction Value that marks whether this header should have an action attached to it.
|
||||
*/
|
||||
data class Header(
|
||||
override val id: Long = -1,
|
||||
override val name: String = "",
|
||||
val isAction: Boolean = false
|
||||
) : BaseModel()
|
||||
|
||||
/**
|
||||
* A data object for a header with an action button. Inherits [BaseModel].
|
||||
* @property icon The icon ot apply for this header
|
||||
* @property action The callback that will be called when the action button is clicked.
|
||||
*/
|
||||
data class ActionHeader(
|
||||
override val id: Long = -1,
|
||||
override val name: String = "",
|
||||
@DrawableRes val icon: Int,
|
||||
val action: (button: ImageButton) -> Unit,
|
||||
) : BaseModel()
|
||||
|
|
|
@ -16,9 +16,10 @@ import org.oxycblt.auxio.logD
|
|||
* @author OxygenCobalt
|
||||
*
|
||||
* FIXME: Here's a catalog of problems that I already know about with this abomination
|
||||
* - Does not support the album artist tag
|
||||
* - All loading is done at startup [Not efficent for large libraries, would require massive arch retooling to fix]
|
||||
* - Genre system is a bottleneck [Nothing I can do about it, MediaStore is garbage]
|
||||
* - Does not support the album artist tag [Nothing I can do that doesn't involve rolling my own loader]
|
||||
* - Genre system is a bottleneck [See Above]
|
||||
* Blame MediaStore, loading anything on this platform is a nightmare.
|
||||
*/
|
||||
class MusicLoader(private val context: Context) {
|
||||
var genres = mutableListOf<Genre>()
|
||||
|
@ -100,7 +101,7 @@ class MusicLoader(private val context: Context) {
|
|||
Albums.ARTIST, // 2
|
||||
Albums.FIRST_YEAR, // 4
|
||||
),
|
||||
"", null,
|
||||
null, null,
|
||||
Albums.DEFAULT_SORT_ORDER
|
||||
)
|
||||
|
||||
|
|
|
@ -3,19 +3,18 @@ package org.oxycblt.auxio.playback.queue
|
|||
import android.annotation.SuppressLint
|
||||
import android.view.MotionEvent
|
||||
import android.view.ViewGroup
|
||||
import androidx.appcompat.widget.TooltipCompat
|
||||
import androidx.recyclerview.widget.AsyncListDiffer
|
||||
import androidx.recyclerview.widget.ItemTouchHelper
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import org.oxycblt.auxio.R
|
||||
import org.oxycblt.auxio.databinding.ItemActionHeaderBinding
|
||||
import org.oxycblt.auxio.databinding.ItemQueueSongBinding
|
||||
import org.oxycblt.auxio.logE
|
||||
import org.oxycblt.auxio.music.ActionHeader
|
||||
import org.oxycblt.auxio.music.BaseModel
|
||||
import org.oxycblt.auxio.music.Header
|
||||
import org.oxycblt.auxio.music.Song
|
||||
import org.oxycblt.auxio.playback.PlaybackViewModel
|
||||
import org.oxycblt.auxio.recycler.DiffCallback
|
||||
import org.oxycblt.auxio.recycler.viewholders.ActionHeaderViewHolder
|
||||
import org.oxycblt.auxio.recycler.viewholders.BaseViewHolder
|
||||
import org.oxycblt.auxio.recycler.viewholders.HeaderViewHolder
|
||||
import org.oxycblt.auxio.ui.inflater
|
||||
|
@ -23,12 +22,10 @@ import org.oxycblt.auxio.ui.inflater
|
|||
/**
|
||||
* The single adapter for both the Next Queue and the User Queue.
|
||||
* @param touchHelper The [ItemTouchHelper] ***containing*** [QueueDragCallback] to be used
|
||||
* @param playbackModel The [PlaybackViewModel] to dispatch updates to.
|
||||
* @author OxygenCobalt
|
||||
*/
|
||||
class QueueAdapter(
|
||||
private val touchHelper: ItemTouchHelper,
|
||||
private val playbackModel: PlaybackViewModel,
|
||||
private val touchHelper: ItemTouchHelper
|
||||
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
||||
private var data = mutableListOf<BaseModel>()
|
||||
private var listDiffer = AsyncListDiffer(this, DiffCallback())
|
||||
|
@ -38,14 +35,11 @@ class QueueAdapter(
|
|||
override fun getItemViewType(position: Int): Int {
|
||||
val item = data[position]
|
||||
|
||||
return if (item is Header) {
|
||||
if (item.isAction) {
|
||||
USER_QUEUE_HEADER_ITEM_TYPE
|
||||
} else {
|
||||
HeaderViewHolder.ITEM_TYPE
|
||||
}
|
||||
} else {
|
||||
QUEUE_SONG_ITEM_TYPE
|
||||
return when (item) {
|
||||
is Header -> HeaderViewHolder.ITEM_TYPE
|
||||
is ActionHeader -> ActionHeaderViewHolder.ITEM_TYPE
|
||||
is Song -> QUEUE_SONG_ITEM_TYPE
|
||||
else -> -1
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -53,7 +47,7 @@ class QueueAdapter(
|
|||
return when (viewType) {
|
||||
HeaderViewHolder.ITEM_TYPE -> HeaderViewHolder.from(parent.context)
|
||||
|
||||
USER_QUEUE_HEADER_ITEM_TYPE -> UserQueueHeaderViewHolder(
|
||||
ActionHeaderViewHolder.ITEM_TYPE -> ActionHeaderViewHolder(
|
||||
ItemActionHeaderBinding.inflate(parent.context.inflater)
|
||||
)
|
||||
|
||||
|
@ -68,12 +62,8 @@ class QueueAdapter(
|
|||
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
||||
when (val item = data[position]) {
|
||||
is Song -> (holder as QueueSongViewHolder).bind(item)
|
||||
|
||||
is Header -> if (item.isAction) {
|
||||
(holder as UserQueueHeaderViewHolder).bind(item)
|
||||
} else {
|
||||
(holder as HeaderViewHolder).bind(item)
|
||||
}
|
||||
is Header -> (holder as HeaderViewHolder).bind(item)
|
||||
is ActionHeader -> (holder as ActionHeaderViewHolder).bind(item)
|
||||
|
||||
else -> logE("Bad data given to QueueAdapter.")
|
||||
}
|
||||
|
@ -158,34 +148,7 @@ class QueueAdapter(
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ViewHolder for the **user queue header**. Has the clear queue button.
|
||||
*/
|
||||
inner class UserQueueHeaderViewHolder(
|
||||
private val binding: ItemActionHeaderBinding
|
||||
) : BaseViewHolder<Header>(binding) {
|
||||
|
||||
init {
|
||||
binding.headerButton.apply {
|
||||
contentDescription = context.getString(R.string.description_clear_user_queue)
|
||||
TooltipCompat.setTooltipText(this, contentDescription)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onBind(data: Header) {
|
||||
binding.header = data
|
||||
binding.headerButton.apply {
|
||||
setImageResource(R.drawable.ic_clear)
|
||||
|
||||
setOnClickListener {
|
||||
playbackModel.clearUserQueue()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val QUEUE_SONG_ITEM_TYPE = 0xA005
|
||||
const val USER_QUEUE_HEADER_ITEM_TYPE = 0xA006
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ import androidx.navigation.fragment.findNavController
|
|||
import androidx.recyclerview.widget.ItemTouchHelper
|
||||
import org.oxycblt.auxio.R
|
||||
import org.oxycblt.auxio.databinding.FragmentQueueBinding
|
||||
import org.oxycblt.auxio.music.ActionHeader
|
||||
import org.oxycblt.auxio.music.BaseModel
|
||||
import org.oxycblt.auxio.music.Header
|
||||
import org.oxycblt.auxio.playback.PlaybackViewModel
|
||||
|
@ -36,7 +37,7 @@ class QueueFragment : Fragment() {
|
|||
|
||||
val callback = QueueDragCallback(playbackModel)
|
||||
val helper = ItemTouchHelper(callback)
|
||||
val queueAdapter = QueueAdapter(helper, playbackModel)
|
||||
val queueAdapter = QueueAdapter(helper)
|
||||
var lastShuffle = playbackModel.isShuffling.value
|
||||
|
||||
callback.addQueueAdapter(queueAdapter)
|
||||
|
@ -136,10 +137,13 @@ class QueueFragment : Fragment() {
|
|||
val nextQueue = playbackModel.nextItemsInQueue.value!!
|
||||
|
||||
if (userQueue.isNotEmpty()) {
|
||||
queue += Header(
|
||||
queue += ActionHeader(
|
||||
id = -2,
|
||||
name = getString(R.string.label_next_user_queue),
|
||||
isAction = true
|
||||
icon = R.drawable.ic_clear,
|
||||
action = {
|
||||
playbackModel.clearUserQueue()
|
||||
}
|
||||
)
|
||||
|
||||
queue += userQueue
|
||||
|
@ -147,9 +151,7 @@ class QueueFragment : Fragment() {
|
|||
|
||||
if (nextQueue.isNotEmpty()) {
|
||||
queue += Header(
|
||||
id = -3,
|
||||
name = getString(R.string.format_next_from, getParentName()),
|
||||
isAction = false
|
||||
id = -3, name = getString(R.string.format_next_from, getParentName()),
|
||||
)
|
||||
|
||||
queue += nextQueue
|
||||
|
|
|
@ -2,11 +2,13 @@ package org.oxycblt.auxio.recycler.viewholders
|
|||
|
||||
import android.content.Context
|
||||
import android.view.View
|
||||
import org.oxycblt.auxio.databinding.ItemActionHeaderBinding
|
||||
import org.oxycblt.auxio.databinding.ItemAlbumBinding
|
||||
import org.oxycblt.auxio.databinding.ItemArtistBinding
|
||||
import org.oxycblt.auxio.databinding.ItemGenreBinding
|
||||
import org.oxycblt.auxio.databinding.ItemHeaderBinding
|
||||
import org.oxycblt.auxio.databinding.ItemSongBinding
|
||||
import org.oxycblt.auxio.music.ActionHeader
|
||||
import org.oxycblt.auxio.music.Album
|
||||
import org.oxycblt.auxio.music.Artist
|
||||
import org.oxycblt.auxio.music.Genre
|
||||
|
@ -170,3 +172,35 @@ class HeaderViewHolder(private val binding: ItemHeaderBinding) : BaseViewHolder<
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The Shared ViewHolder for a [ActionHeader]. Instantiation should be done with [from]
|
||||
*/
|
||||
class ActionHeaderViewHolder(
|
||||
private val binding: ItemActionHeaderBinding
|
||||
) : BaseViewHolder<ActionHeader>(binding) {
|
||||
|
||||
override fun onBind(data: ActionHeader) {
|
||||
binding.header = data
|
||||
binding.headerButton.apply {
|
||||
setImageResource(data.icon)
|
||||
|
||||
setOnClickListener {
|
||||
data.action(binding.headerButton)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val ITEM_TYPE = 0xA006
|
||||
|
||||
/**
|
||||
* Create an instance of [HeaderViewHolder]
|
||||
*/
|
||||
fun from(context: Context): HeaderViewHolder {
|
||||
return HeaderViewHolder(
|
||||
ItemHeaderBinding.inflate(context.inflater)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,6 +33,8 @@ class SearchAdapter(
|
|||
is Album -> AlbumViewHolder.ITEM_TYPE
|
||||
is Song -> SongViewHolder.ITEM_TYPE
|
||||
is Header -> HeaderViewHolder.ITEM_TYPE
|
||||
|
||||
else -> -1
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
<variable
|
||||
name="header"
|
||||
type="org.oxycblt.auxio.music.Header" />
|
||||
type="org.oxycblt.auxio.music.ActionHeader" />
|
||||
</data>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
|
|
|
@ -44,7 +44,7 @@ To prevent any strange bugs, all integer representations must be unique. A table
|
|||
0xA004 | HeaderViewHolder
|
||||
|
||||
0xA005 | QueueSongViewHolder
|
||||
0xA006 | UserQueueHeaderViewHolder
|
||||
0xA006 | ActionHeaderViewHolder
|
||||
|
||||
0xA007 | AlbumHeaderViewHolder
|
||||
0xA008 | AlbumSongViewHolder
|
||||
|
@ -89,7 +89,7 @@ org.oxycblt.auxio # Main UI's and logging utilities
|
|||
├──.coil # Fetchers and utilities for Coil, contains binding adapters than be used in the user interface.
|
||||
├──.database # Databases and their items for Auxio
|
||||
├──.detail # UIs for more album/artist/genre details
|
||||
└──.adapters # RecyclerView adapters for the detail UIs, which display the header information and items
|
||||
│ └──.adapters # RecyclerView adapters for the detail UIs, which display the header information and items
|
||||
├──.library # Library UI
|
||||
├──.loading # Loading UI
|
||||
├──.music # Music storage and loading
|
||||
|
|
Loading…
Reference in a new issue