From 2aa630948bf7f9b039c1f0922610a0bb6e37b4ec Mon Sep 17 00:00:00 2001 From: OxygenCobalt Date: Thu, 12 Nov 2020 19:25:43 -0700 Subject: [PATCH] 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. --- .../java/org/oxycblt/auxio/music/Models.kt | 1 - .../org/oxycblt/auxio/music/MusicStore.kt | 3 ++ .../org/oxycblt/auxio/music/MusicUtils.kt | 2 +- .../auxio/playback/queue/QueueAdapter.kt | 13 +++--- .../auxio/playback/queue/QueueDragCallback.kt | 2 +- .../auxio/playback/queue/QueueFragment.kt | 2 + .../playback/state/PlaybackStateManager.kt | 4 +- .../org/oxycblt/auxio/recycler/ShowMode.kt | 1 - .../recycler/viewholders/ModelHolders.kt | 10 ++--- .../org/oxycblt/auxio/ui/InterfaceUtils.kt | 43 +++++++++++++------ app/src/main/res/layout/fragment_playback.xml | 2 + app/src/main/res/layout/fragment_queue.xml | 13 +----- .../main/res/menu/menu_album_song_actions.xml | 4 ++ app/src/main/res/values/strings.xml | 2 +- 14 files changed, 59 insertions(+), 43 deletions(-) diff --git a/app/src/main/java/org/oxycblt/auxio/music/Models.kt b/app/src/main/java/org/oxycblt/auxio/music/Models.kt index 082f15ecc..9bb12f588 100644 --- a/app/src/main/java/org/oxycblt/auxio/music/Models.kt +++ b/app/src/main/java/org/oxycblt/auxio/music/Models.kt @@ -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 diff --git a/app/src/main/java/org/oxycblt/auxio/music/MusicStore.kt b/app/src/main/java/org/oxycblt/auxio/music/MusicStore.kt index 1cc16182c..c38129469 100644 --- a/app/src/main/java/org/oxycblt/auxio/music/MusicStore.kt +++ b/app/src/main/java/org/oxycblt/auxio/music/MusicStore.kt @@ -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 diff --git a/app/src/main/java/org/oxycblt/auxio/music/MusicUtils.kt b/app/src/main/java/org/oxycblt/auxio/music/MusicUtils.kt index 49af0e06b..0a3fcde34 100644 --- a/app/src/main/java/org/oxycblt/auxio/music/MusicUtils.kt +++ b/app/src/main/java/org/oxycblt/auxio/music/MusicUtils.kt @@ -35,7 +35,7 @@ private val ID3_GENRES = arrayOf( "Anime", "JPop", "Synthpop" ) -const val PAREN_FILTER = "()" +private const val PAREN_FILTER = "()" // --- EXTENSION FUNCTIONS --- diff --git a/app/src/main/java/org/oxycblt/auxio/playback/queue/QueueAdapter.kt b/app/src/main/java/org/oxycblt/auxio/playback/queue/QueueAdapter.kt index dd7daafc7..1ad4caf98 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/queue/QueueAdapter.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/queue/QueueAdapter.kt @@ -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 } } diff --git a/app/src/main/java/org/oxycblt/auxio/playback/queue/QueueDragCallback.kt b/app/src/main/java/org/oxycblt/auxio/playback/queue/QueueDragCallback.kt index 04c1e2b88..97d81f20d 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/queue/QueueDragCallback.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/queue/QueueDragCallback.kt @@ -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 diff --git a/app/src/main/java/org/oxycblt/auxio/playback/queue/QueueFragment.kt b/app/src/main/java/org/oxycblt/auxio/playback/queue/QueueFragment.kt index d92c8d3a8..be511894c 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/queue/QueueFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/queue/QueueFragment.kt @@ -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() diff --git a/app/src/main/java/org/oxycblt/auxio/playback/state/PlaybackStateManager.kt b/app/src/main/java/org/oxycblt/auxio/playback/state/PlaybackStateManager.kt index 961bb94bf..7c94209fc 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/state/PlaybackStateManager.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/state/PlaybackStateManager.kt @@ -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() } diff --git a/app/src/main/java/org/oxycblt/auxio/recycler/ShowMode.kt b/app/src/main/java/org/oxycblt/auxio/recycler/ShowMode.kt index 0d04a75f4..2971d61de 100644 --- a/app/src/main/java/org/oxycblt/auxio/recycler/ShowMode.kt +++ b/app/src/main/java/org/oxycblt/auxio/recycler/ShowMode.kt @@ -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; diff --git a/app/src/main/java/org/oxycblt/auxio/recycler/viewholders/ModelHolders.kt b/app/src/main/java/org/oxycblt/auxio/recycler/viewholders/ModelHolders.kt index 22b7853a4..56e106e64 100644 --- a/app/src/main/java/org/oxycblt/auxio/recycler/viewholders/ModelHolders.kt +++ b/app/src/main/java/org/oxycblt/auxio/recycler/viewholders/ModelHolders.kt @@ -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( diff --git a/app/src/main/java/org/oxycblt/auxio/ui/InterfaceUtils.kt b/app/src/main/java/org/oxycblt/auxio/ui/InterfaceUtils.kt index 487d40d18..e50408be5 100644 --- a/app/src/main/java/org/oxycblt/auxio/ui/InterfaceUtils.kt +++ b/app/src/main/java/org/oxycblt/auxio/ui/InterfaceUtils.kt @@ -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() + } +} diff --git a/app/src/main/res/layout/fragment_playback.xml b/app/src/main/res/layout/fragment_playback.xml index 2e4c9becf..50c9264b9 100644 --- a/app/src/main/res/layout/fragment_playback.xml +++ b/app/src/main/res/layout/fragment_playback.xml @@ -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" diff --git a/app/src/main/res/layout/fragment_queue.xml b/app/src/main/res/layout/fragment_queue.xml index 46b213325..ef6d336d8 100644 --- a/app/src/main/res/layout/fragment_queue.xml +++ b/app/src/main/res/layout/fragment_queue.xml @@ -1,7 +1,8 @@ + xmlns:app="http://schemas.android.com/apk/res-auto" + tools:context=".playback.queue.QueueFragment"> - - \ No newline at end of file diff --git a/app/src/main/res/menu/menu_album_song_actions.xml b/app/src/main/res/menu/menu_album_song_actions.xml index e84e868bd..1b4bb6df0 100644 --- a/app/src/main/res/menu/menu_album_song_actions.xml +++ b/app/src/main/res/menu/menu_album_song_actions.xml @@ -8,4 +8,8 @@ android:id="@+id/action_go_artist" android:title="@string/label_go_artist" android:icon="@drawable/ic_artist" /> + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 05175bef3..5c521aad6 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -32,7 +32,7 @@ Add to queue Added to queue Next in Queue - Nothing here + Already in queue! Music Playback The music playback service for Auxio.