Prevent the addition of duplicate queue items

Band-aid over a bug with DiffCallback that causes weird behavior with duplicate queue items by disallowing the addition of queue items if theyre already allowed.
This commit is contained in:
OxygenCobalt 2020-11-12 19:25:43 -07:00
parent 604145fb69
commit 2aa630948b
14 changed files with 59 additions and 43 deletions

View file

@ -3,7 +3,6 @@ package org.oxycblt.auxio.music
import android.net.Uri
// --- MUSIC MODELS ---
// FIXME: Remove parent/child references so that they can be parcelable?
// The base model for all music
// This is used in a lot of general functions in order to have generic utilities

View file

@ -92,6 +92,9 @@ class MusicStore private constructor() {
@Volatile
private var INSTANCE: MusicStore? = null
/**
* Get/Instantiate the single instance of [MusicStore].
*/
fun getInstance(): MusicStore {
val currentInstance = INSTANCE

View file

@ -35,7 +35,7 @@ private val ID3_GENRES = arrayOf(
"Anime", "JPop", "Synthpop"
)
const val PAREN_FILTER = "()"
private const val PAREN_FILTER = "()"
// --- EXTENSION FUNCTIONS ---

View file

@ -22,17 +22,16 @@ class QueueAdapter(
override fun getItemViewType(position: Int): Int {
val item = getItem(position)
if (item is Header) {
return HeaderViewHolder.ITEM_TYPE
} else {
return QUEUE_ITEM_VIEW_TYPE
}
return if (item is Header)
HeaderViewHolder.ITEM_TYPE
else
QUEUE_ITEM_TYPE
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return when (viewType) {
HeaderViewHolder.ITEM_TYPE -> HeaderViewHolder.from(parent.context)
QUEUE_ITEM_VIEW_TYPE -> ViewHolder(
QUEUE_ITEM_TYPE -> ViewHolder(
ItemQueueSongBinding.inflate(LayoutInflater.from(parent.context))
)
else -> error("Someone messed with the ViewHolder item types. Tell OxygenCobalt.")
@ -76,6 +75,6 @@ class QueueAdapter(
}
companion object {
const val QUEUE_ITEM_VIEW_TYPE = 0xA030
const val QUEUE_ITEM_TYPE = 0xA015
}
}

View file

@ -14,7 +14,7 @@ class QueueDragCallback(private val playbackModel: PlaybackViewModel) : ItemTouc
recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder
): Int {
// Only allow dragging/swiping with the queue item ViewHolder, not the header items.
// Only allow dragging/swiping with the queue item ViewHolder, not the headers.
return if (viewHolder is QueueAdapter.ViewHolder) {
makeFlag(
ItemTouchHelper.ACTION_STATE_DRAG, ItemTouchHelper.UP or ItemTouchHelper.DOWN

View file

@ -43,6 +43,8 @@ class QueueFragment : Fragment() {
helper.attachToRecyclerView(this)
}
// --- VIEWMODEL SETUP ---
playbackModel.userQueue.observe(viewLifecycleOwner) {
if (it.isEmpty() && playbackModel.nextItemsInQueue.value!!.isEmpty()) {
findNavController().navigateUp()

View file

@ -12,7 +12,7 @@ import kotlin.random.Random
/**
* Master class for the playback state. This should ***not*** be used outside of the playback module.
* - If you want to show 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].
*
* All instantiation should be done with [PlaybackStateManager.from()].
@ -301,6 +301,8 @@ class PlaybackStateManager private constructor() {
mUserQueue.removeAt(index)
Log.d(this::class.simpleName, mUserQueue.toString())
forceUserQueueUpdate()
}

View file

@ -1,6 +1,5 @@
package org.oxycblt.auxio.recycler
// TODO: Swap these temp values for actual constants
enum class ShowMode {
SHOW_GENRES, SHOW_ARTISTS, SHOW_ALBUMS, SHOW_SONGS;

View file

@ -28,7 +28,7 @@ class GenreViewHolder private constructor(
}
companion object {
const val ITEM_TYPE = 10
const val ITEM_TYPE = 0xA010
fun from(context: Context, doOnClick: (Genre) -> Unit): GenreViewHolder {
return GenreViewHolder(
@ -50,7 +50,7 @@ class ArtistViewHolder private constructor(
}
companion object {
const val ITEM_TYPE = 11
const val ITEM_TYPE = 0xA011
fun from(context: Context, doOnClick: (Artist) -> Unit): ArtistViewHolder {
return ArtistViewHolder(
@ -72,7 +72,7 @@ class AlbumViewHolder private constructor(
}
companion object {
const val ITEM_TYPE = 12
const val ITEM_TYPE = 0xA012
fun from(context: Context, doOnClick: (data: Album) -> Unit): AlbumViewHolder {
return AlbumViewHolder(
@ -99,7 +99,7 @@ class SongViewHolder private constructor(
}
companion object {
const val ITEM_TYPE = 13
const val ITEM_TYPE = 0xA013
fun from(
context: Context,
@ -123,7 +123,7 @@ class HeaderViewHolder(
}
companion object {
const val ITEM_TYPE = 14
const val ITEM_TYPE = 0xA014
fun from(context: Context): HeaderViewHolder {
return HeaderViewHolder(

View file

@ -60,13 +60,7 @@ fun PopupMenu.setupSongActions(song: Song, context: Context, playbackModel: Play
setOnMenuItemClickListener {
when (it.itemId) {
R.id.action_queue_add -> {
playbackModel.addToUserQueue(song)
Toast.makeText(
context,
context.getString(R.string.label_queue_added),
Toast.LENGTH_SHORT
).show()
doUserQueueAdd(context, song, playbackModel)
true
}
@ -97,13 +91,7 @@ fun PopupMenu.setupAlbumSongActions(
setOnMenuItemClickListener {
when (it.itemId) {
R.id.action_queue_add -> {
playbackModel.addToUserQueue(song)
Toast.makeText(
context,
context.getString(R.string.label_queue_added),
Toast.LENGTH_SHORT
).show()
doUserQueueAdd(context, song, playbackModel)
true
}
@ -113,8 +101,35 @@ fun PopupMenu.setupAlbumSongActions(
true
}
R.id.action_play_artist -> {
playbackModel.playSong(song, PlaybackMode.IN_ARTIST)
true
}
else -> false
}
}
show()
}
private fun doUserQueueAdd(context: Context, song: Song, playbackModel: PlaybackViewModel) {
// If the song was already added to the user queue, then don't add it again.
// This is just to prevent a bug with DiffCallback that creates strange
// behavior when duplicate user queue items are added.
// FIXME: Fix the duplicate item DiffCallback issue
if (!playbackModel.userQueue.value!!.contains(song)) {
playbackModel.addToUserQueue(song)
Toast.makeText(
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()
}
}

View file

@ -45,6 +45,8 @@
android:layout_height="0dp"
android:layout_margin="@dimen/margin_mid_large"
android:contentDescription="@{@string/description_album_cover(song.name)}"
android:outlineProvider="bounds"
android:elevation="4dp"
app:coverArt="@{song}"
app:layout_constraintBottom_toTopOf="@+id/playback_song"
app:layout_constraintDimensionRatio="1:1"

View file

@ -1,7 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto">
xmlns:app="http://schemas.android.com/apk/res-auto"
tools:context=".playback.queue.QueueFragment">
<LinearLayout
android:layout_width="match_parent"
@ -34,15 +35,5 @@
tools:layout_editor_absoluteX="0dp"
tools:listitem="@layout/item_song" />
<TextView
android:id="@+id/queue_nothing_indicator"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="@dimen/margin_medium"
android:textSize="15sp"
android:text="@string/label_empty_queue"
android:textAlignment="center"
android:visibility="gone" />
</LinearLayout>
</layout>

View file

@ -8,4 +8,8 @@
android:id="@+id/action_go_artist"
android:title="@string/label_go_artist"
android:icon="@drawable/ic_artist" />
<item
android:id="@+id/action_play_artist"
android:title="@string/label_play_artist"
android:icon="@drawable/ic_artist" />
</menu>

View file

@ -32,7 +32,7 @@
<string name="label_queue_add">Add to queue</string>
<string name="label_queue_added">Added to queue</string>
<string name="label_next_user_queue">Next in Queue</string>
<string name="label_empty_queue">Nothing here</string>
<string name="label_queue_already_added">Already in queue!</string>
<string name="label_notification_playback">Music Playback</string>
<string name="label_service_playback">The music playback service for Auxio.</string>