Move user queue clear action into header
Move the button for clearing the user queue into the header for the user queue, so that its more consistent.
This commit is contained in:
parent
1d50d24c4f
commit
844870f4d4
17 changed files with 165 additions and 65 deletions
|
@ -1,6 +1,5 @@
|
||||||
apply plugin: 'com.android.application'
|
apply plugin: 'com.android.application'
|
||||||
apply plugin: 'kotlin-android'
|
apply plugin: 'kotlin-android'
|
||||||
apply plugin: 'kotlin-android-extensions'
|
|
||||||
apply plugin: 'kotlin-kapt'
|
apply plugin: 'kotlin-kapt'
|
||||||
apply plugin: "androidx.navigation.safeargs"
|
apply plugin: "androidx.navigation.safeargs"
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,7 @@ import androidx.navigation.fragment.findNavController
|
||||||
* A Base [Fragment] implementing a [OnBackPressedCallback] so that Auxio will navigate upwards
|
* A Base [Fragment] implementing a [OnBackPressedCallback] so that Auxio will navigate upwards
|
||||||
* instead of out of the app if a Detail Fragment is currently open. Also carries the
|
* instead of out of the app if a Detail Fragment is currently open. Also carries the
|
||||||
* multi-navigation fix.
|
* multi-navigation fix.
|
||||||
|
* // TODO: Merge headers with recyclerview [if possible]
|
||||||
* @author OxygenCobalt
|
* @author OxygenCobalt
|
||||||
*/
|
*/
|
||||||
abstract class DetailFragment : Fragment() {
|
abstract class DetailFragment : Fragment() {
|
||||||
|
|
|
@ -12,20 +12,20 @@ import org.oxycblt.auxio.recycler.viewholders.BaseViewHolder
|
||||||
class DetailAlbumAdapter(
|
class DetailAlbumAdapter(
|
||||||
private val doOnClick: (data: Album) -> Unit,
|
private val doOnClick: (data: Album) -> Unit,
|
||||||
private val doOnLongClick: (data: Album, view: View) -> Unit
|
private val doOnLongClick: (data: Album, view: View) -> Unit
|
||||||
) : ListAdapter<Album, DetailAlbumAdapter.ViewHolder>(DiffCallback()) {
|
) : ListAdapter<Album, DetailAlbumAdapter.AlbumViewHolder>(DiffCallback()) {
|
||||||
|
|
||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AlbumViewHolder {
|
||||||
return ViewHolder(
|
return AlbumViewHolder(
|
||||||
ItemArtistAlbumBinding.inflate(LayoutInflater.from(parent.context))
|
ItemArtistAlbumBinding.inflate(LayoutInflater.from(parent.context))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
override fun onBindViewHolder(holder: AlbumViewHolder, position: Int) {
|
||||||
holder.bind(getItem(position))
|
holder.bind(getItem(position))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generic ViewHolder for a detail album
|
// Generic ViewHolder for a detail album
|
||||||
inner class ViewHolder(
|
inner class AlbumViewHolder(
|
||||||
private val binding: ItemArtistAlbumBinding,
|
private val binding: ItemArtistAlbumBinding,
|
||||||
) : BaseViewHolder<Album>(binding, doOnClick, doOnLongClick) {
|
) : BaseViewHolder<Album>(binding, doOnClick, doOnLongClick) {
|
||||||
|
|
||||||
|
|
|
@ -120,5 +120,6 @@ data class Genre(
|
||||||
*/
|
*/
|
||||||
data class Header(
|
data class Header(
|
||||||
override val id: Long = -1,
|
override val id: Long = -1,
|
||||||
override var name: String = ""
|
override var name: String = "",
|
||||||
|
val isAction: Boolean = false
|
||||||
) : BaseModel()
|
) : BaseModel()
|
||||||
|
|
|
@ -14,6 +14,7 @@ import org.oxycblt.auxio.MainFragmentDirections
|
||||||
import org.oxycblt.auxio.R
|
import org.oxycblt.auxio.R
|
||||||
import org.oxycblt.auxio.databinding.FragmentCompactPlaybackBinding
|
import org.oxycblt.auxio.databinding.FragmentCompactPlaybackBinding
|
||||||
import org.oxycblt.auxio.music.MusicStore
|
import org.oxycblt.auxio.music.MusicStore
|
||||||
|
import org.oxycblt.auxio.ui.createToast
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A [Fragment] that displays the currently played song at a glance, with some basic controls.
|
* A [Fragment] that displays the currently played song at a glance, with some basic controls.
|
||||||
|
@ -57,6 +58,7 @@ class CompactPlaybackFragment : Fragment() {
|
||||||
|
|
||||||
binding.root.setOnLongClickListener {
|
binding.root.setOnLongClickListener {
|
||||||
playbackModel.save(requireContext())
|
playbackModel.save(requireContext())
|
||||||
|
getString(R.string.debug_state_saved).createToast(requireContext())
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -105,17 +105,28 @@ fun NotificationCompat.Builder.setMetadata(song: Song, context: Context, onDone:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// I have no idea how to update a specific action on the fly so I have to use these restricted APIs
|
/**
|
||||||
|
* Update the playing button on the media notification.
|
||||||
|
* @param context The context required to refresh the action
|
||||||
|
*/
|
||||||
@SuppressLint("RestrictedApi")
|
@SuppressLint("RestrictedApi")
|
||||||
fun NotificationCompat.Builder.updatePlaying(context: Context) {
|
fun NotificationCompat.Builder.updatePlaying(context: Context) {
|
||||||
mActions[2] = newAction(NotificationUtils.ACTION_PLAY_PAUSE, context)
|
mActions[2] = newAction(NotificationUtils.ACTION_PLAY_PAUSE, context)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the loop button on the media notification
|
||||||
|
* @param context The context required to refresh the action
|
||||||
|
*/
|
||||||
@SuppressLint("RestrictedApi")
|
@SuppressLint("RestrictedApi")
|
||||||
fun NotificationCompat.Builder.updateLoop(context: Context) {
|
fun NotificationCompat.Builder.updateLoop(context: Context) {
|
||||||
mActions[0] = newAction(NotificationUtils.ACTION_LOOP, context)
|
mActions[0] = newAction(NotificationUtils.ACTION_LOOP, context)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the subtext of the media notification to reflect the current mode.
|
||||||
|
* @param context The context required to get the strings required to show certain modes
|
||||||
|
*/
|
||||||
fun NotificationCompat.Builder.updateMode(context: Context) {
|
fun NotificationCompat.Builder.updateMode(context: Context) {
|
||||||
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.O) {
|
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.O) {
|
||||||
val playbackManager = PlaybackStateManager.getInstance()
|
val playbackManager = PlaybackStateManager.getInstance()
|
||||||
|
@ -164,12 +175,10 @@ private fun newAction(action: String, context: Context): NotificationCompat.Acti
|
||||||
}
|
}
|
||||||
|
|
||||||
return NotificationCompat.Action.Builder(
|
return NotificationCompat.Action.Builder(
|
||||||
drawable, action, newPlaybackIntent(action, context)
|
drawable, action,
|
||||||
|
PendingIntent.getBroadcast(
|
||||||
|
context, NotificationUtils.REQUEST_CODE,
|
||||||
|
Intent(action), PendingIntent.FLAG_UPDATE_CURRENT
|
||||||
|
)
|
||||||
).build()
|
).build()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun newPlaybackIntent(action: String, context: Context): PendingIntent {
|
|
||||||
return PendingIntent.getBroadcast(
|
|
||||||
context, NotificationUtils.REQUEST_CODE, Intent(action), PendingIntent.FLAG_UPDATE_CURRENT
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
|
@ -263,8 +263,6 @@ class PlaybackService : Service(), Player.EventListener, PlaybackStateManager.Ca
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Do testing where service is destroyed after restore [Possible edge case]
|
|
||||||
|
|
||||||
override fun onLoopUpdate(mode: LoopMode) {
|
override fun onLoopUpdate(mode: LoopMode) {
|
||||||
changeIsFromAudioFocus = false
|
changeIsFromAudioFocus = false
|
||||||
|
|
||||||
|
@ -296,6 +294,15 @@ class PlaybackService : Service(), Player.EventListener, PlaybackStateManager.Ca
|
||||||
player.prepare()
|
player.prepare()
|
||||||
player.seekTo(playbackManager.position)
|
player.seekTo(playbackManager.position)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
when (playbackManager.loopMode) {
|
||||||
|
LoopMode.NONE -> {
|
||||||
|
player.repeatMode = Player.REPEAT_MODE_OFF
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
player.repeatMode = Player.REPEAT_MODE_ONE
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun restoreNotification() {
|
private fun restoreNotification() {
|
||||||
|
|
|
@ -8,6 +8,8 @@ import android.view.ViewGroup
|
||||||
import androidx.recyclerview.widget.AsyncListDiffer
|
import androidx.recyclerview.widget.AsyncListDiffer
|
||||||
import androidx.recyclerview.widget.ItemTouchHelper
|
import androidx.recyclerview.widget.ItemTouchHelper
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
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.databinding.ItemQueueSongBinding
|
||||||
import org.oxycblt.auxio.music.BaseModel
|
import org.oxycblt.auxio.music.BaseModel
|
||||||
import org.oxycblt.auxio.music.Header
|
import org.oxycblt.auxio.music.Header
|
||||||
|
@ -25,7 +27,8 @@ import org.oxycblt.auxio.recycler.viewholders.HeaderViewHolder
|
||||||
* @author OxygenCobalt
|
* @author OxygenCobalt
|
||||||
*/
|
*/
|
||||||
class QueueAdapter(
|
class QueueAdapter(
|
||||||
private val touchHelper: ItemTouchHelper
|
private val touchHelper: ItemTouchHelper,
|
||||||
|
private val onHeaderAction: () -> Unit
|
||||||
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
||||||
private var data = mutableListOf<BaseModel>()
|
private var data = mutableListOf<BaseModel>()
|
||||||
private var listDiffer = AsyncListDiffer(this, DiffCallback())
|
private var listDiffer = AsyncListDiffer(this, DiffCallback())
|
||||||
|
@ -36,7 +39,10 @@ class QueueAdapter(
|
||||||
val item = data[position]
|
val item = data[position]
|
||||||
|
|
||||||
return if (item is Header)
|
return if (item is Header)
|
||||||
HeaderViewHolder.ITEM_TYPE
|
if (item.isAction)
|
||||||
|
USER_QUEUE_HEADER_ITEM_tYPE
|
||||||
|
else
|
||||||
|
HeaderViewHolder.ITEM_TYPE
|
||||||
else
|
else
|
||||||
QUEUE_ITEM_TYPE
|
QUEUE_ITEM_TYPE
|
||||||
}
|
}
|
||||||
|
@ -47,6 +53,9 @@ class QueueAdapter(
|
||||||
QUEUE_ITEM_TYPE -> QueueSongViewHolder(
|
QUEUE_ITEM_TYPE -> QueueSongViewHolder(
|
||||||
ItemQueueSongBinding.inflate(LayoutInflater.from(parent.context))
|
ItemQueueSongBinding.inflate(LayoutInflater.from(parent.context))
|
||||||
)
|
)
|
||||||
|
USER_QUEUE_HEADER_ITEM_tYPE -> UserQueueHeaderViewHolder(
|
||||||
|
ItemActionHeaderBinding.inflate(LayoutInflater.from(parent.context))
|
||||||
|
)
|
||||||
else -> error("Someone messed with the ViewHolder item types.")
|
else -> error("Someone messed with the ViewHolder item types.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -54,7 +63,12 @@ class QueueAdapter(
|
||||||
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
||||||
when (val item = data[position]) {
|
when (val item = data[position]) {
|
||||||
is Song -> (holder as QueueSongViewHolder).bind(item)
|
is Song -> (holder as QueueSongViewHolder).bind(item)
|
||||||
is Header -> (holder as HeaderViewHolder).bind(item)
|
is Header ->
|
||||||
|
if (item.isAction) {
|
||||||
|
(holder as UserQueueHeaderViewHolder).bind(item)
|
||||||
|
} else {
|
||||||
|
(holder as HeaderViewHolder).bind(item)
|
||||||
|
}
|
||||||
|
|
||||||
else -> {
|
else -> {
|
||||||
Log.e(this::class.simpleName, "Bad data fed to QueueAdapter.")
|
Log.e(this::class.simpleName, "Bad data fed to QueueAdapter.")
|
||||||
|
@ -131,7 +145,24 @@ class QueueAdapter(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inner class UserQueueHeaderViewHolder(
|
||||||
|
private val binding: ItemActionHeaderBinding
|
||||||
|
) : BaseViewHolder<Header>(binding, null, null) {
|
||||||
|
override fun onBind(data: Header) {
|
||||||
|
binding.header = data
|
||||||
|
binding.headerButton.apply {
|
||||||
|
setImageResource(R.drawable.ic_clear)
|
||||||
|
|
||||||
|
setOnClickListener {
|
||||||
|
clearUserQueue()
|
||||||
|
onHeaderAction()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val QUEUE_ITEM_TYPE = 0xA015
|
const val QUEUE_ITEM_TYPE = 0xA015
|
||||||
|
const val USER_QUEUE_HEADER_ITEM_tYPE = 0xA016
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,25 +34,16 @@ class QueueFragment : Fragment() {
|
||||||
|
|
||||||
val callback = QueueDragCallback(playbackModel)
|
val callback = QueueDragCallback(playbackModel)
|
||||||
val helper = ItemTouchHelper(callback)
|
val helper = ItemTouchHelper(callback)
|
||||||
val queueAdapter = QueueAdapter(helper)
|
val queueAdapter = QueueAdapter(helper) {
|
||||||
callback.addQueueAdapter(queueAdapter)
|
playbackModel.clearUserQueue()
|
||||||
|
}
|
||||||
|
|
||||||
val queueClearItem = binding.queueToolbar.menu.findItem(R.id.action_clear_user_queue)
|
callback.addQueueAdapter(queueAdapter)
|
||||||
|
|
||||||
// --- UI SETUP ---
|
// --- UI SETUP ---
|
||||||
|
|
||||||
binding.queueToolbar.apply {
|
binding.queueToolbar.setNavigationOnClickListener {
|
||||||
setNavigationOnClickListener {
|
findNavController().navigateUp()
|
||||||
findNavController().navigateUp()
|
|
||||||
}
|
|
||||||
|
|
||||||
setOnMenuItemClickListener {
|
|
||||||
if (it.itemId == R.id.action_clear_user_queue) {
|
|
||||||
queueAdapter.clearUserQueue()
|
|
||||||
playbackModel.clearUserQueue()
|
|
||||||
true
|
|
||||||
} else false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
binding.queueRecycler.apply {
|
binding.queueRecycler.apply {
|
||||||
|
@ -65,14 +56,10 @@ class QueueFragment : Fragment() {
|
||||||
// --- VIEWMODEL SETUP ---
|
// --- VIEWMODEL SETUP ---
|
||||||
|
|
||||||
playbackModel.userQueue.observe(viewLifecycleOwner) {
|
playbackModel.userQueue.observe(viewLifecycleOwner) {
|
||||||
if (it.isEmpty()) {
|
if (it.isEmpty() && playbackModel.nextItemsInQueue.value!!.isEmpty()) {
|
||||||
queueClearItem.isEnabled = false
|
findNavController().navigateUp()
|
||||||
|
|
||||||
if (playbackModel.nextItemsInQueue.value!!.isEmpty()) {
|
return@observe
|
||||||
findNavController().navigateUp()
|
|
||||||
|
|
||||||
return@observe
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
queueAdapter.submitList(createQueueData())
|
queueAdapter.submitList(createQueueData())
|
||||||
|
@ -94,7 +81,7 @@ class QueueFragment : Fragment() {
|
||||||
|
|
||||||
if (playbackModel.userQueue.value!!.isNotEmpty()) {
|
if (playbackModel.userQueue.value!!.isNotEmpty()) {
|
||||||
queue.add(
|
queue.add(
|
||||||
Header(name = getString(R.string.label_next_user_queue))
|
Header(name = getString(R.string.label_next_user_queue), isAction = true)
|
||||||
)
|
)
|
||||||
queue.addAll(playbackModel.userQueue.value!!)
|
queue.addAll(playbackModel.userQueue.value!!)
|
||||||
}
|
}
|
||||||
|
@ -108,7 +95,8 @@ class QueueFragment : Fragment() {
|
||||||
getString(R.string.label_all_songs)
|
getString(R.string.label_all_songs)
|
||||||
else
|
else
|
||||||
playbackModel.parent.value!!.name
|
playbackModel.parent.value!!.name
|
||||||
)
|
),
|
||||||
|
isAction = false
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
queue.addAll(playbackModel.nextItemsInQueue.value!!)
|
queue.addAll(playbackModel.nextItemsInQueue.value!!)
|
||||||
|
|
|
@ -3,6 +3,8 @@ package org.oxycblt.auxio.recycler.viewholders
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
|
import androidx.annotation.DrawableRes
|
||||||
|
import org.oxycblt.auxio.databinding.ItemActionHeaderBinding
|
||||||
import org.oxycblt.auxio.databinding.ItemAlbumBinding
|
import org.oxycblt.auxio.databinding.ItemAlbumBinding
|
||||||
import org.oxycblt.auxio.databinding.ItemArtistBinding
|
import org.oxycblt.auxio.databinding.ItemArtistBinding
|
||||||
import org.oxycblt.auxio.databinding.ItemGenreBinding
|
import org.oxycblt.auxio.databinding.ItemGenreBinding
|
||||||
|
@ -129,7 +131,7 @@ class SongViewHolder private constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
open class HeaderViewHolder(
|
class HeaderViewHolder(
|
||||||
private val binding: ItemHeaderBinding
|
private val binding: ItemHeaderBinding
|
||||||
) : BaseViewHolder<Header>(binding, null, null) {
|
) : BaseViewHolder<Header>(binding, null, null) {
|
||||||
|
|
||||||
|
@ -147,3 +149,20 @@ open class HeaderViewHolder(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
abstract class ActionHeaderViewHolder(
|
||||||
|
protected val binding: ItemActionHeaderBinding,
|
||||||
|
@DrawableRes private val iconRes: Int
|
||||||
|
) : BaseViewHolder<Header>(binding, null, null) {
|
||||||
|
override fun onBind(data: Header) {
|
||||||
|
binding.header = data
|
||||||
|
binding.headerButton.apply {
|
||||||
|
setImageResource(iconRes)
|
||||||
|
setOnClickListener {
|
||||||
|
onActionClick()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract fun onActionClick()
|
||||||
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ import android.view.MenuItem
|
||||||
import android.widget.ImageButton
|
import android.widget.ImageButton
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.annotation.ColorInt
|
import androidx.annotation.ColorInt
|
||||||
|
import androidx.annotation.MenuRes
|
||||||
import androidx.appcompat.widget.PopupMenu
|
import androidx.appcompat.widget.PopupMenu
|
||||||
import androidx.recyclerview.widget.DividerItemDecoration
|
import androidx.recyclerview.widget.DividerItemDecoration
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
@ -63,7 +64,6 @@ fun RecyclerView.applyDivider() {
|
||||||
}
|
}
|
||||||
|
|
||||||
fun PopupMenu.setupSongActions(song: Song, context: Context, playbackModel: PlaybackViewModel) {
|
fun PopupMenu.setupSongActions(song: Song, context: Context, playbackModel: PlaybackViewModel) {
|
||||||
inflate(R.menu.menu_song_actions)
|
|
||||||
setOnMenuItemClickListener {
|
setOnMenuItemClickListener {
|
||||||
when (it.itemId) {
|
when (it.itemId) {
|
||||||
R.id.action_queue_add -> {
|
R.id.action_queue_add -> {
|
||||||
|
@ -85,7 +85,7 @@ fun PopupMenu.setupSongActions(song: Song, context: Context, playbackModel: Play
|
||||||
else -> false
|
else -> false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
show()
|
inflateAndShow(R.menu.menu_song_actions)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun PopupMenu.setupAlbumSongActions(
|
fun PopupMenu.setupAlbumSongActions(
|
||||||
|
@ -94,7 +94,6 @@ fun PopupMenu.setupAlbumSongActions(
|
||||||
detailViewModel: DetailViewModel,
|
detailViewModel: DetailViewModel,
|
||||||
playbackModel: PlaybackViewModel
|
playbackModel: PlaybackViewModel
|
||||||
) {
|
) {
|
||||||
inflate(R.menu.menu_album_song_actions)
|
|
||||||
setOnMenuItemClickListener {
|
setOnMenuItemClickListener {
|
||||||
when (it.itemId) {
|
when (it.itemId) {
|
||||||
R.id.action_queue_add -> {
|
R.id.action_queue_add -> {
|
||||||
|
@ -117,7 +116,7 @@ fun PopupMenu.setupAlbumSongActions(
|
||||||
else -> false
|
else -> false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
show()
|
inflateAndShow(R.menu.menu_album_song_actions)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun PopupMenu.setupAlbumActions(
|
fun PopupMenu.setupAlbumActions(
|
||||||
|
@ -125,7 +124,6 @@ fun PopupMenu.setupAlbumActions(
|
||||||
context: Context,
|
context: Context,
|
||||||
playbackModel: PlaybackViewModel
|
playbackModel: PlaybackViewModel
|
||||||
) {
|
) {
|
||||||
inflate(R.menu.menu_album_actions)
|
|
||||||
setOnMenuItemClickListener {
|
setOnMenuItemClickListener {
|
||||||
when (it.itemId) {
|
when (it.itemId) {
|
||||||
R.id.action_queue_add -> {
|
R.id.action_queue_add -> {
|
||||||
|
@ -148,7 +146,7 @@ fun PopupMenu.setupAlbumActions(
|
||||||
else -> false
|
else -> false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
show()
|
inflateAndShow(R.menu.menu_album_actions)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun PopupMenu.setupArtistActions(
|
fun PopupMenu.setupArtistActions(
|
||||||
|
@ -156,7 +154,6 @@ fun PopupMenu.setupArtistActions(
|
||||||
context: Context,
|
context: Context,
|
||||||
playbackModel: PlaybackViewModel
|
playbackModel: PlaybackViewModel
|
||||||
) {
|
) {
|
||||||
inflate(R.menu.menu_detail)
|
|
||||||
setOnMenuItemClickListener {
|
setOnMenuItemClickListener {
|
||||||
when (it.itemId) {
|
when (it.itemId) {
|
||||||
R.id.action_queue_add -> {
|
R.id.action_queue_add -> {
|
||||||
|
@ -179,7 +176,7 @@ fun PopupMenu.setupArtistActions(
|
||||||
else -> false
|
else -> false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
show()
|
inflateAndShow(R.menu.menu_detail)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun PopupMenu.setupGenreActions(
|
fun PopupMenu.setupGenreActions(
|
||||||
|
@ -187,7 +184,6 @@ fun PopupMenu.setupGenreActions(
|
||||||
context: Context,
|
context: Context,
|
||||||
playbackModel: PlaybackViewModel
|
playbackModel: PlaybackViewModel
|
||||||
) {
|
) {
|
||||||
inflate(R.menu.menu_detail)
|
|
||||||
setOnMenuItemClickListener {
|
setOnMenuItemClickListener {
|
||||||
when (it.itemId) {
|
when (it.itemId) {
|
||||||
R.id.action_queue_add -> {
|
R.id.action_queue_add -> {
|
||||||
|
@ -210,5 +206,10 @@ fun PopupMenu.setupGenreActions(
|
||||||
else -> false
|
else -> false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
inflateAndShow(R.menu.menu_detail)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun PopupMenu.inflateAndShow(@MenuRes menuRes: Int) {
|
||||||
|
inflate(menuRes)
|
||||||
show()
|
show()
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,6 @@
|
||||||
android:focusable="true"
|
android:focusable="true"
|
||||||
android:elevation="@dimen/elevation_normal"
|
android:elevation="@dimen/elevation_normal"
|
||||||
app:popupTheme="@style/Widget.CustomPopup"
|
app:popupTheme="@style/Widget.CustomPopup"
|
||||||
app:menu="@menu/menu_queue"
|
|
||||||
app:navigationIcon="@drawable/ic_down"
|
app:navigationIcon="@drawable/ic_down"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
|
48
app/src/main/res/layout/item_action_header.xml
Normal file
48
app/src/main/res/layout/item_action_header.xml
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
tools:context=".recycler.viewholders.HeaderViewHolder">
|
||||||
|
|
||||||
|
<data>
|
||||||
|
|
||||||
|
<variable
|
||||||
|
name="header"
|
||||||
|
type="org.oxycblt.auxio.music.Header" />
|
||||||
|
</data>
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/header_text"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:fontFamily="@font/inter_semibold"
|
||||||
|
android:paddingStart="@dimen/padding_medium"
|
||||||
|
android:paddingTop="@dimen/padding_small"
|
||||||
|
android:paddingEnd="@dimen/padding_small"
|
||||||
|
android:paddingBottom="@dimen/padding_small"
|
||||||
|
android:text="@{header.name}"
|
||||||
|
android:textColor="?android:attr/textColorPrimary"
|
||||||
|
android:textSize="19sp"
|
||||||
|
app:layout_constraintEnd_toStartOf="@+id/header_button"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
tools:text="Songs" />
|
||||||
|
|
||||||
|
<ImageButton
|
||||||
|
android:id="@+id/header_button"
|
||||||
|
style="@style/Widget.AppCompat.Button.Borderless"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:background="@drawable/ui_header_dividers"
|
||||||
|
android:tint="?android:attr/colorPrimary"
|
||||||
|
app:layout_constraintDimensionRatio="1:1"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
tools:src="@drawable/ic_clear"
|
||||||
|
tools:ignore="ContentDescription" />
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
</layout>
|
|
@ -2,7 +2,7 @@
|
||||||
<layout xmlns:app="http://schemas.android.com/apk/res-auto"
|
<layout xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
tools:context=".detail.adapters.DetailAlbumAdapter.ViewHolder">
|
tools:context=".detail.adapters.DetailAlbumAdapter.AlbumViewHolder">
|
||||||
|
|
||||||
<data>
|
<data>
|
||||||
|
|
||||||
|
|
|
@ -1,8 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
|
||||||
<item
|
|
||||||
android:id="@+id/action_clear_user_queue"
|
|
||||||
android:title="@string/label_clear_user_queue"
|
|
||||||
app:showAsAction="never" />
|
|
||||||
</menu>
|
|
|
@ -29,6 +29,9 @@
|
||||||
<string name="label_channel">Music Playback</string>
|
<string name="label_channel">Music Playback</string>
|
||||||
<string name="label_service_playback">The music playback service for Auxio.</string>
|
<string name="label_service_playback">The music playback service for Auxio.</string>
|
||||||
|
|
||||||
|
<!-- Debug Namespace | Debug labels -->
|
||||||
|
<string name="debug_state_saved">State saved</string>
|
||||||
|
|
||||||
<!-- Error Namespace | Error Labels -->
|
<!-- Error Namespace | Error Labels -->
|
||||||
<string name="error_no_music">No music found.</string>
|
<string name="error_no_music">No music found.</string>
|
||||||
<string name="error_music_load_failed">Music loading failed.</string>
|
<string name="error_music_load_failed">Music loading failed.</string>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||||
buildscript {
|
buildscript {
|
||||||
ext.kotlin_version = "1.4.10"
|
ext.kotlin_version = "1.4.20"
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
google()
|
google()
|
||||||
|
|
Loading…
Reference in a new issue