Make QueueAdapter Adapter instead of ListAdapter
Fix a stupid amount of bugs by changing QueueAdapter to a normal adapter with a differ and addition/movement functions instead of a ListAdapter.
This commit is contained in:
parent
2aa630948b
commit
2ebee41ed0
15 changed files with 85 additions and 61 deletions
|
@ -7,7 +7,6 @@
|
||||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:allowBackup="true"
|
|
||||||
android:icon="@mipmap/ic_launcher"
|
android:icon="@mipmap/ic_launcher"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:roundIcon="@mipmap/ic_launcher_round"
|
android:roundIcon="@mipmap/ic_launcher_round"
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package org.oxycblt.auxio.music.processing
|
package org.oxycblt.auxio.music.processing
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
import android.content.ContentResolver
|
import android.content.ContentResolver
|
||||||
import android.provider.MediaStore
|
import android.provider.MediaStore
|
||||||
import android.provider.MediaStore.Audio.Albums
|
import android.provider.MediaStore.Audio.Albums
|
||||||
|
@ -20,6 +21,7 @@ enum class MusicLoaderResponse {
|
||||||
|
|
||||||
// Class that loads music from the FileSystem.
|
// Class that loads music from the FileSystem.
|
||||||
// TODO: Add custom artist images from the filesystem
|
// TODO: Add custom artist images from the filesystem
|
||||||
|
// TODO: Move genre loading of songs [Loads would take longer though]
|
||||||
class MusicLoader(
|
class MusicLoader(
|
||||||
private val resolver: ContentResolver,
|
private val resolver: ContentResolver,
|
||||||
|
|
||||||
|
@ -163,9 +165,9 @@ class MusicLoader(
|
||||||
it.genres.add(genre)
|
it.genres.add(genre)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cursor.close()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
artistGenreCursor?.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
Log.d(
|
Log.d(
|
||||||
|
@ -174,6 +176,7 @@ class MusicLoader(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressLint("InlinedApi")
|
||||||
private fun loadAlbums() {
|
private fun loadAlbums() {
|
||||||
Log.d(this::class.simpleName, "Starting album search...")
|
Log.d(this::class.simpleName, "Starting album search...")
|
||||||
|
|
||||||
|
@ -225,6 +228,7 @@ class MusicLoader(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressLint("InlinedApi")
|
||||||
private fun loadSongs() {
|
private fun loadSongs() {
|
||||||
Log.d(this::class.simpleName, "Starting song search...")
|
Log.d(this::class.simpleName, "Starting song search...")
|
||||||
|
|
||||||
|
|
|
@ -111,7 +111,7 @@ fun NotificationCompat.Builder.updateMode(context: Context) {
|
||||||
if (!NotificationUtils.DO_COMPAT_SUBTEXT) {
|
if (!NotificationUtils.DO_COMPAT_SUBTEXT) {
|
||||||
val playbackManager = PlaybackStateManager.getInstance()
|
val playbackManager = PlaybackStateManager.getInstance()
|
||||||
|
|
||||||
// If the mode is ALL_SONGS, then just put a string, otherwise put the parent model's name.
|
// If playing from all songs, set the subtext as that, otherwise the currently played parent.
|
||||||
if (playbackManager.mode == PlaybackMode.ALL_SONGS) {
|
if (playbackManager.mode == PlaybackMode.ALL_SONGS) {
|
||||||
setSubText(context.getString(R.string.title_all_songs))
|
setSubText(context.getString(R.string.title_all_songs))
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -11,6 +11,7 @@ import org.oxycblt.auxio.music.BaseModel
|
||||||
import org.oxycblt.auxio.music.Genre
|
import org.oxycblt.auxio.music.Genre
|
||||||
import org.oxycblt.auxio.music.Song
|
import org.oxycblt.auxio.music.Song
|
||||||
import org.oxycblt.auxio.music.toDuration
|
import org.oxycblt.auxio.music.toDuration
|
||||||
|
import org.oxycblt.auxio.playback.queue.QueueAdapter
|
||||||
import org.oxycblt.auxio.playback.state.LoopMode
|
import org.oxycblt.auxio.playback.state.LoopMode
|
||||||
import org.oxycblt.auxio.playback.state.PlaybackMode
|
import org.oxycblt.auxio.playback.state.PlaybackMode
|
||||||
import org.oxycblt.auxio.playback.state.PlaybackStateManager
|
import org.oxycblt.auxio.playback.state.PlaybackStateManager
|
||||||
|
@ -156,11 +157,13 @@ class PlaybackViewModel : ViewModel(), PlaybackStateManager.Callback {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove a queue OR user queue item, given a QueueAdapter index.
|
// Remove a queue OR user queue item, given a QueueAdapter index.
|
||||||
fun removeQueueItem(adapterIndex: Int) {
|
fun removeQueueItem(adapterIndex: Int, queueAdapter: QueueAdapter) {
|
||||||
var index = adapterIndex.dec()
|
var index = adapterIndex.dec()
|
||||||
|
|
||||||
// If the item is in the user queue, then remove it from there after accounting for the header.
|
// If the item is in the user queue, then remove it from there after accounting for the header.
|
||||||
if (index < mUserQueue.value!!.size) {
|
if (index < mUserQueue.value!!.size) {
|
||||||
|
queueAdapter.removeItem(adapterIndex)
|
||||||
|
|
||||||
playbackManager.removeUserQueueItem(index)
|
playbackManager.removeUserQueueItem(index)
|
||||||
} else {
|
} else {
|
||||||
// Translate the indices into proper queue indices if removing an item from there.
|
// Translate the indices into proper queue indices if removing an item from there.
|
||||||
|
@ -170,12 +173,14 @@ class PlaybackViewModel : ViewModel(), PlaybackStateManager.Callback {
|
||||||
index -= mUserQueue.value!!.size.inc()
|
index -= mUserQueue.value!!.size.inc()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
queueAdapter.removeItem(adapterIndex)
|
||||||
|
|
||||||
playbackManager.removeQueueItem(index)
|
playbackManager.removeQueueItem(index)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Move queue OR user queue items, given QueueAdapter indices.
|
// Move queue OR user queue items, given QueueAdapter indices.
|
||||||
fun moveQueueItems(adapterFrom: Int, adapterTo: Int): Boolean {
|
fun moveQueueItems(adapterFrom: Int, adapterTo: Int, queueAdapter: QueueAdapter): Boolean {
|
||||||
var from = adapterFrom.dec()
|
var from = adapterFrom.dec()
|
||||||
var to = adapterTo.dec()
|
var to = adapterTo.dec()
|
||||||
|
|
||||||
|
@ -183,6 +188,8 @@ class PlaybackViewModel : ViewModel(), PlaybackStateManager.Callback {
|
||||||
// Ignore invalid movements to out of bounds, header, or queue positions
|
// Ignore invalid movements to out of bounds, header, or queue positions
|
||||||
if (to >= mUserQueue.value!!.size || to < 0) return false
|
if (to >= mUserQueue.value!!.size || to < 0) return false
|
||||||
|
|
||||||
|
queueAdapter.moveItems(adapterFrom, adapterTo)
|
||||||
|
|
||||||
playbackManager.moveUserQueueItems(from, to)
|
playbackManager.moveUserQueueItems(from, to)
|
||||||
} else {
|
} else {
|
||||||
// Ignore invalid movements to out of bounds or header positions
|
// Ignore invalid movements to out of bounds or header positions
|
||||||
|
@ -205,6 +212,8 @@ class PlaybackViewModel : ViewModel(), PlaybackStateManager.Callback {
|
||||||
if (to <= mIndex.value!!) return false
|
if (to <= mIndex.value!!) return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
queueAdapter.moveItems(adapterFrom, adapterTo)
|
||||||
|
|
||||||
playbackManager.moveQueueItems(from, to)
|
playbackManager.moveQueueItems(from, to)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,8 +5,8 @@ import android.util.Log
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.MotionEvent
|
import android.view.MotionEvent
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
|
import androidx.recyclerview.widget.AsyncListDiffer
|
||||||
import androidx.recyclerview.widget.ItemTouchHelper
|
import androidx.recyclerview.widget.ItemTouchHelper
|
||||||
import androidx.recyclerview.widget.ListAdapter
|
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import org.oxycblt.auxio.databinding.ItemQueueSongBinding
|
import org.oxycblt.auxio.databinding.ItemQueueSongBinding
|
||||||
import org.oxycblt.auxio.music.BaseModel
|
import org.oxycblt.auxio.music.BaseModel
|
||||||
|
@ -16,11 +16,24 @@ import org.oxycblt.auxio.recycler.DiffCallback
|
||||||
import org.oxycblt.auxio.recycler.viewholders.BaseViewHolder
|
import org.oxycblt.auxio.recycler.viewholders.BaseViewHolder
|
||||||
import org.oxycblt.auxio.recycler.viewholders.HeaderViewHolder
|
import org.oxycblt.auxio.recycler.viewholders.HeaderViewHolder
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The single adapter for both the Next Queue and the User Queue.
|
||||||
|
* - [submitList] is for the plain async diff calculations, use this if you
|
||||||
|
* have no idea what the differences are between the old data & the new data
|
||||||
|
* - [removeItem] and [moveItems] are used by [org.oxycblt.auxio.playback.PlaybackViewModel]
|
||||||
|
* so that this adapter doesn't flip-out when items are moved (Which happens with [AsyncListDiffer])
|
||||||
|
* @author OxygenCobalt
|
||||||
|
*/
|
||||||
class QueueAdapter(
|
class QueueAdapter(
|
||||||
val touchHelper: ItemTouchHelper
|
private val touchHelper: ItemTouchHelper
|
||||||
) : ListAdapter<BaseModel, RecyclerView.ViewHolder>(DiffCallback<BaseModel>()) {
|
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
||||||
|
private var data = mutableListOf<BaseModel>()
|
||||||
|
private var listDiffer = AsyncListDiffer(this, DiffCallback())
|
||||||
|
|
||||||
|
override fun getItemCount(): Int = data.size
|
||||||
|
|
||||||
override fun getItemViewType(position: Int): Int {
|
override fun getItemViewType(position: Int): Int {
|
||||||
val item = getItem(position)
|
val item = data[position]
|
||||||
|
|
||||||
return if (item is Header)
|
return if (item is Header)
|
||||||
HeaderViewHolder.ITEM_TYPE
|
HeaderViewHolder.ITEM_TYPE
|
||||||
|
@ -39,7 +52,7 @@ class QueueAdapter(
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
||||||
when (val item = getItem(position)) {
|
when (val item = data[position]) {
|
||||||
is Song -> (holder as ViewHolder).bind(item)
|
is Song -> (holder as ViewHolder).bind(item)
|
||||||
is Header -> (holder as HeaderViewHolder).bind(item)
|
is Header -> (holder as HeaderViewHolder).bind(item)
|
||||||
|
|
||||||
|
@ -49,6 +62,27 @@ class QueueAdapter(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun submitList(newData: MutableList<BaseModel>) {
|
||||||
|
if (data != newData) {
|
||||||
|
data = newData
|
||||||
|
|
||||||
|
listDiffer.submitList(newData)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun moveItems(adapterFrom: Int, adapterTo: Int) {
|
||||||
|
val item = data.removeAt(adapterFrom)
|
||||||
|
data.add(adapterTo, item)
|
||||||
|
|
||||||
|
notifyItemMoved(adapterFrom, adapterTo)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun removeItem(adapterIndex: Int) {
|
||||||
|
data.removeAt(adapterIndex)
|
||||||
|
|
||||||
|
notifyItemRemoved(adapterIndex)
|
||||||
|
}
|
||||||
|
|
||||||
// Generic ViewHolder for a queue item
|
// Generic ViewHolder for a queue item
|
||||||
inner class ViewHolder(
|
inner class ViewHolder(
|
||||||
private val binding: ItemQueueSongBinding,
|
private val binding: ItemQueueSongBinding,
|
||||||
|
|
|
@ -10,6 +10,8 @@ import kotlin.math.sign
|
||||||
|
|
||||||
// The drag callback used for the Queue RecyclerView.
|
// The drag callback used for the Queue RecyclerView.
|
||||||
class QueueDragCallback(private val playbackModel: PlaybackViewModel) : ItemTouchHelper.Callback() {
|
class QueueDragCallback(private val playbackModel: PlaybackViewModel) : ItemTouchHelper.Callback() {
|
||||||
|
private lateinit var queueAdapter: QueueAdapter
|
||||||
|
|
||||||
override fun getMovementFlags(
|
override fun getMovementFlags(
|
||||||
recyclerView: RecyclerView,
|
recyclerView: RecyclerView,
|
||||||
viewHolder: RecyclerView.ViewHolder
|
viewHolder: RecyclerView.ViewHolder
|
||||||
|
@ -51,11 +53,15 @@ class QueueDragCallback(private val playbackModel: PlaybackViewModel) : ItemTouc
|
||||||
viewHolder: RecyclerView.ViewHolder,
|
viewHolder: RecyclerView.ViewHolder,
|
||||||
target: RecyclerView.ViewHolder
|
target: RecyclerView.ViewHolder
|
||||||
): Boolean {
|
): Boolean {
|
||||||
return playbackModel.moveQueueItems(viewHolder.adapterPosition, target.adapterPosition)
|
return playbackModel.moveQueueItems(viewHolder.adapterPosition, target.adapterPosition, queueAdapter)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
|
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
|
||||||
playbackModel.removeQueueItem(viewHolder.adapterPosition)
|
playbackModel.removeQueueItem(viewHolder.adapterPosition, queueAdapter)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun addQueueAdapter(adapter: QueueAdapter) {
|
||||||
|
queueAdapter = adapter
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
|
@ -8,7 +8,6 @@ import androidx.fragment.app.Fragment
|
||||||
import androidx.fragment.app.activityViewModels
|
import androidx.fragment.app.activityViewModels
|
||||||
import androidx.navigation.fragment.findNavController
|
import androidx.navigation.fragment.findNavController
|
||||||
import androidx.recyclerview.widget.ItemTouchHelper
|
import androidx.recyclerview.widget.ItemTouchHelper
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
|
||||||
import org.oxycblt.auxio.R
|
import org.oxycblt.auxio.R
|
||||||
import org.oxycblt.auxio.databinding.FragmentQueueBinding
|
import org.oxycblt.auxio.databinding.FragmentQueueBinding
|
||||||
import org.oxycblt.auxio.music.BaseModel
|
import org.oxycblt.auxio.music.BaseModel
|
||||||
|
@ -27,8 +26,10 @@ class QueueFragment : Fragment() {
|
||||||
): View? {
|
): View? {
|
||||||
val binding = FragmentQueueBinding.inflate(inflater)
|
val binding = FragmentQueueBinding.inflate(inflater)
|
||||||
|
|
||||||
val helper = ItemTouchHelper(QueueDragCallback(playbackModel))
|
val callback = QueueDragCallback(playbackModel)
|
||||||
|
val helper = ItemTouchHelper(callback)
|
||||||
val queueAdapter = QueueAdapter(helper)
|
val queueAdapter = QueueAdapter(helper)
|
||||||
|
callback.addQueueAdapter(queueAdapter)
|
||||||
|
|
||||||
// --- UI SETUP ---
|
// --- UI SETUP ---
|
||||||
|
|
||||||
|
@ -50,9 +51,7 @@ class QueueFragment : Fragment() {
|
||||||
findNavController().navigateUp()
|
findNavController().navigateUp()
|
||||||
}
|
}
|
||||||
|
|
||||||
queueAdapter.submitList(createQueueDisplay()) {
|
queueAdapter.submitList(createQueueData())
|
||||||
scrollRecyclerIfNeeded(binding)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
playbackModel.nextItemsInQueue.observe(viewLifecycleOwner) {
|
playbackModel.nextItemsInQueue.observe(viewLifecycleOwner) {
|
||||||
|
@ -60,15 +59,13 @@ class QueueFragment : Fragment() {
|
||||||
findNavController().navigateUp()
|
findNavController().navigateUp()
|
||||||
}
|
}
|
||||||
|
|
||||||
queueAdapter.submitList(createQueueDisplay()) {
|
queueAdapter.submitList(createQueueData())
|
||||||
scrollRecyclerIfNeeded(binding)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return binding.root
|
return binding.root
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createQueueDisplay(): MutableList<BaseModel> {
|
private fun createQueueData(): MutableList<BaseModel> {
|
||||||
val queue = mutableListOf<BaseModel>()
|
val queue = mutableListOf<BaseModel>()
|
||||||
|
|
||||||
if (playbackModel.userQueue.value!!.isNotEmpty()) {
|
if (playbackModel.userQueue.value!!.isNotEmpty()) {
|
||||||
|
@ -93,12 +90,4 @@ class QueueFragment : Fragment() {
|
||||||
|
|
||||||
return queue
|
return queue
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun scrollRecyclerIfNeeded(binding: FragmentQueueBinding) {
|
|
||||||
if ((binding.queueRecycler.layoutManager as LinearLayoutManager)
|
|
||||||
.findFirstVisibleItemPosition() < 1
|
|
||||||
) {
|
|
||||||
binding.queueRecycler.scrollToPosition(0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,8 @@ import kotlin.random.Random
|
||||||
/**
|
/**
|
||||||
* Master class for the playback state. This should ***not*** be used outside of the playback module.
|
* Master class for the playback state. This should ***not*** be used outside of the playback module.
|
||||||
* - If you want to use the playback state in the UI, use [org.oxycblt.auxio.playback.PlaybackViewModel].
|
* - If you want to use the playback state in the UI, use [org.oxycblt.auxio.playback.PlaybackViewModel].
|
||||||
* - If you want to add to the system aspects or the exoplayer instance, use [org.oxycblt.auxio.playback.PlaybackService].
|
* - If you want to use the playback state with the ExoPlayer instance or system-side things,
|
||||||
|
* use [org.oxycblt.auxio.playback.PlaybackService].
|
||||||
*
|
*
|
||||||
* All instantiation should be done with [PlaybackStateManager.from()].
|
* All instantiation should be done with [PlaybackStateManager.from()].
|
||||||
* @author OxygenCobalt
|
* @author OxygenCobalt
|
||||||
|
@ -120,7 +121,7 @@ class PlaybackStateManager private constructor() {
|
||||||
PlaybackMode.ALL_SONGS -> null
|
PlaybackMode.ALL_SONGS -> null
|
||||||
PlaybackMode.IN_ARTIST -> song.album.artist
|
PlaybackMode.IN_ARTIST -> song.album.artist
|
||||||
PlaybackMode.IN_ALBUM -> song.album
|
PlaybackMode.IN_ALBUM -> song.album
|
||||||
PlaybackMode.IN_GENRE -> error("what")
|
PlaybackMode.IN_GENRE -> song.album.artist.genres[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
mMode = mode
|
mMode = mode
|
||||||
|
@ -129,7 +130,7 @@ class PlaybackStateManager private constructor() {
|
||||||
PlaybackMode.ALL_SONGS -> musicStore.songs.toMutableList()
|
PlaybackMode.ALL_SONGS -> musicStore.songs.toMutableList()
|
||||||
PlaybackMode.IN_ARTIST -> song.album.artist.songs
|
PlaybackMode.IN_ARTIST -> song.album.artist.songs
|
||||||
PlaybackMode.IN_ALBUM -> song.album.songs
|
PlaybackMode.IN_ALBUM -> song.album.songs
|
||||||
PlaybackMode.IN_GENRE -> error("what")
|
PlaybackMode.IN_GENRE -> song.album.artist.genres[0].songs
|
||||||
}
|
}
|
||||||
|
|
||||||
resetLoopMode()
|
resetLoopMode()
|
||||||
|
|
|
@ -6,10 +6,10 @@ import org.oxycblt.auxio.music.BaseModel
|
||||||
// Base Diff callback
|
// Base Diff callback
|
||||||
class DiffCallback<T : BaseModel> : DiffUtil.ItemCallback<T>() {
|
class DiffCallback<T : BaseModel> : DiffUtil.ItemCallback<T>() {
|
||||||
override fun areContentsTheSame(oldItem: T, newItem: T): Boolean {
|
override fun areContentsTheSame(oldItem: T, newItem: T): Boolean {
|
||||||
return oldItem.id == newItem.id
|
return oldItem == newItem
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun areItemsTheSame(oldItem: T, newItem: T): Boolean {
|
override fun areItemsTheSame(oldItem: T, newItem: T): Boolean {
|
||||||
return oldItem == newItem
|
return oldItem.id == newItem.id
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,6 +39,10 @@ fun ImageButton.disable(context: Context) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun String.createToast(context: Context) {
|
||||||
|
Toast.makeText(context, this, Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
|
||||||
// Apply a custom vertical divider
|
// Apply a custom vertical divider
|
||||||
fun RecyclerView.applyDivider() {
|
fun RecyclerView.applyDivider() {
|
||||||
val div = DividerItemDecoration(
|
val div = DividerItemDecoration(
|
||||||
|
@ -117,19 +121,7 @@ private fun doUserQueueAdd(context: Context, song: Song, playbackModel: Playback
|
||||||
// This is just to prevent a bug with DiffCallback that creates strange
|
// This is just to prevent a bug with DiffCallback that creates strange
|
||||||
// behavior when duplicate user queue items are added.
|
// behavior when duplicate user queue items are added.
|
||||||
// FIXME: Fix the duplicate item DiffCallback issue
|
// FIXME: Fix the duplicate item DiffCallback issue
|
||||||
if (!playbackModel.userQueue.value!!.contains(song)) {
|
playbackModel.addToUserQueue(song)
|
||||||
playbackModel.addToUserQueue(song)
|
|
||||||
|
|
||||||
Toast.makeText(
|
context.getString(R.string.label_queue_added).createToast(context)
|
||||||
context,
|
|
||||||
context.getString(R.string.label_queue_added),
|
|
||||||
Toast.LENGTH_SHORT
|
|
||||||
).show()
|
|
||||||
} else {
|
|
||||||
Toast.makeText(
|
|
||||||
context,
|
|
||||||
context.getString(R.string.label_queue_already_added),
|
|
||||||
Toast.LENGTH_SHORT
|
|
||||||
).show()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,7 @@ private val ACCENTS = listOf(
|
||||||
Pair(R.color.deep_purple, R.style.Theme_DeepPurple), // 3
|
Pair(R.color.deep_purple, R.style.Theme_DeepPurple), // 3
|
||||||
Pair(R.color.indigo, R.style.Theme_Indigo), // 4
|
Pair(R.color.indigo, R.style.Theme_Indigo), // 4
|
||||||
Pair(R.color.blue, R.style.Theme_Blue), // 5
|
Pair(R.color.blue, R.style.Theme_Blue), // 5
|
||||||
Pair(R.color.light_blue, R.style.Theme_Blue), // 6
|
Pair(R.color.light_blue, R.style.Theme_LightBlue), // 6
|
||||||
Pair(R.color.cyan, R.style.Theme_Cyan), // 7
|
Pair(R.color.cyan, R.style.Theme_Cyan), // 7
|
||||||
Pair(R.color.teal, R.style.Theme_Teal), // 8
|
Pair(R.color.teal, R.style.Theme_Teal), // 8
|
||||||
Pair(R.color.green, R.style.Theme_Green), // 9
|
Pair(R.color.green, R.style.Theme_Green), // 9
|
||||||
|
|
|
@ -1,8 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
|
||||||
<item>
|
|
||||||
<shape android:shape="oval">
|
|
||||||
<solid android:color="?attr/colorPrimary" />
|
|
||||||
</shape>
|
|
||||||
</item>
|
|
||||||
</selector>
|
|
|
@ -1,7 +1,6 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
||||||
tools:context=".recycler.viewholders.HeaderViewHolder">
|
tools:context=".recycler.viewholders.HeaderViewHolder">
|
||||||
|
|
||||||
<data>
|
<data>
|
||||||
|
|
|
@ -61,7 +61,6 @@
|
||||||
<string name="placeholder_genre">Unknown Genre</string>
|
<string name="placeholder_genre">Unknown Genre</string>
|
||||||
<string name="placeholder_artist">Unknown Artist</string>
|
<string name="placeholder_artist">Unknown Artist</string>
|
||||||
<string name="placeholder_album">Unknown Album</string>
|
<string name="placeholder_album">Unknown Album</string>
|
||||||
<string name="placeholder_song">Unknown Song</string>
|
|
||||||
<string name="placeholder_no_date">No Date</string>
|
<string name="placeholder_no_date">No Date</string>
|
||||||
|
|
||||||
<!-- Format Namespace | Value formatting/plurals -->
|
<!-- Format Namespace | Value formatting/plurals -->
|
||||||
|
|
|
@ -7,7 +7,7 @@ buildscript {
|
||||||
jcenter()
|
jcenter()
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath 'com.android.tools.build:gradle:4.1.0'
|
classpath 'com.android.tools.build:gradle:4.1.1'
|
||||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||||
classpath "android.arch.navigation:navigation-safe-args-gradle-plugin:1.0.0"
|
classpath "android.arch.navigation:navigation-safe-args-gradle-plugin:1.0.0"
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue