diff --git a/app/src/main/java/org/oxycblt/auxio/database/PlaybackStateDatabase.kt b/app/src/main/java/org/oxycblt/auxio/database/PlaybackStateDatabase.kt index 372a72eb2..3f6a8fd3a 100644 --- a/app/src/main/java/org/oxycblt/auxio/database/PlaybackStateDatabase.kt +++ b/app/src/main/java/org/oxycblt/auxio/database/PlaybackStateDatabase.kt @@ -8,7 +8,10 @@ import android.database.sqlite.SQLiteOpenHelper import android.util.Log /** - * A bootstrapped SQLite database for managing the persistent playback state and queue. + * A SQLite database for managing the persistent playback state and queue. + * Yes, I know androidx has Room which supposedly makes database creation easier, but it also + * has a crippling bug where it will endlessly allocate rows even if you clear the entire db, so... + * @author OxygenCobalt */ class PlaybackStateDatabase(context: Context) : SQLiteOpenHelper(context, DB_NAME, null, DB_VERSION) { override fun onCreate(db: SQLiteDatabase) { @@ -64,6 +67,9 @@ class PlaybackStateDatabase(context: Context) : SQLiteOpenHelper(context, DB_NAM // --- INTERFACE FUNCTIONS --- + /** + * Clear the previously written [PlaybackState] and write a new one. + */ fun writeState(state: PlaybackState) { val database = writableDatabase database.beginTransaction() @@ -102,6 +108,11 @@ class PlaybackStateDatabase(context: Context) : SQLiteOpenHelper(context, DB_NAM } } + /** + * Read the stored [PlaybackState] from the database, if there is one. + * @return The stored [PlaybackState], null if there isnt one,. + * @author OxygenCobalt + */ fun readState(): PlaybackState? { val database = writableDatabase @@ -112,6 +123,7 @@ class PlaybackStateDatabase(context: Context) : SQLiteOpenHelper(context, DB_NAM stateCursor = database.query(TABLE_NAME_STATE, null, null, null, null, null, null) stateCursor?.use { cursor -> + // Don't bother if the cursor [and therefore database] has nothing in it. if (cursor.count == 0) return@use val songIndex = cursor.getColumnIndexOrThrow(PlaybackState.COLUMN_SONG_ID) @@ -127,6 +139,7 @@ class PlaybackStateDatabase(context: Context) : SQLiteOpenHelper(context, DB_NAM val inUserQueueIndex = cursor.getColumnIndexOrThrow(PlaybackState.COLUMN_IN_USER_QUEUE) + // If there is something in it, get the first item from it, ignoring anything else. cursor.moveToFirst() state = PlaybackState( @@ -147,6 +160,11 @@ class PlaybackStateDatabase(context: Context) : SQLiteOpenHelper(context, DB_NAM } } + /** + * Write a list of [QueueItem]s to the database, clearing the previous queue present. + * @param queue The list of [QueueItem]s to be written. + * @author OxygenCobalt + */ fun writeQueue(queue: List) { val database = readableDatabase database.beginTransaction() @@ -196,6 +214,11 @@ class PlaybackStateDatabase(context: Context) : SQLiteOpenHelper(context, DB_NAM } } + /** + * Read the database for any [QueueItem]s. + * @return A list of any stored [QueueItem]s. + * @author OxygenCobalt + */ fun readQueue(): List { val database = readableDatabase 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 0926bbb95..5e70f52cf 100644 --- a/app/src/main/java/org/oxycblt/auxio/music/Models.kt +++ b/app/src/main/java/org/oxycblt/auxio/music/Models.kt @@ -120,6 +120,5 @@ data class Genre( */ data class Header( override val id: Long = -1, - override var name: String = "", - val isAction: Boolean = false + override var name: String = "" ) : BaseModel() diff --git a/app/src/main/java/org/oxycblt/auxio/playback/PlaybackViewModel.kt b/app/src/main/java/org/oxycblt/auxio/playback/PlaybackViewModel.kt index 53aa9d48c..b3d10dc34 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/PlaybackViewModel.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/PlaybackViewModel.kt @@ -159,7 +159,7 @@ class PlaybackViewModel : ViewModel(), PlaybackStateManager.Callback { } // Remove a queue OR user queue item, given a QueueAdapter index. - fun removeQueueItem(adapterIndex: Int, queueAdapter: QueueAdapter) { + fun removeQueueAdapterItem(adapterIndex: Int, queueAdapter: QueueAdapter) { var index = adapterIndex.dec() // If the item is in the user queue, then remove it from there after accounting for the header. @@ -182,7 +182,7 @@ class PlaybackViewModel : ViewModel(), PlaybackStateManager.Callback { } // Move queue OR user queue items, given QueueAdapter indices. - fun moveQueueItems(adapterFrom: Int, adapterTo: Int, queueAdapter: QueueAdapter): Boolean { + fun moveQueueAdapterItems(adapterFrom: Int, adapterTo: Int, queueAdapter: QueueAdapter): Boolean { var from = adapterFrom.dec() var to = adapterTo.dec() @@ -230,6 +230,10 @@ class PlaybackViewModel : ViewModel(), PlaybackStateManager.Callback { playbackManager.addToUserQueue(songs) } + fun clearUserQueue() { + playbackManager.clearUserQueue() + } + // --- STATUS FUNCTIONS --- // Flip the playing status. 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 005b29c32..0afc4eba6 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 @@ -98,6 +98,14 @@ class QueueAdapter( } } + fun clearUserQueue() { + val nextQueueHeaderIndex = data.indexOfLast { it is Header } + val slice = data.slice(0 until nextQueueHeaderIndex) + + data.removeAll(slice) + notifyItemRangeRemoved(0, slice.size) + } + // Generic ViewHolder for a queue item inner class QueueSongViewHolder( private val binding: ItemQueueSongBinding, 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 5383e4cbb..7fe567bd0 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 @@ -53,7 +53,7 @@ class QueueDragCallback(private val playbackModel: PlaybackViewModel) : ItemTouc viewHolder: RecyclerView.ViewHolder, target: RecyclerView.ViewHolder ): Boolean { - return playbackModel.moveQueueItems( + return playbackModel.moveQueueAdapterItems( viewHolder.adapterPosition, target.adapterPosition, queueAdapter @@ -61,7 +61,7 @@ class QueueDragCallback(private val playbackModel: PlaybackViewModel) : ItemTouc } override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) { - playbackModel.removeQueueItem(viewHolder.adapterPosition, queueAdapter) + playbackModel.removeQueueAdapterItem(viewHolder.adapterPosition, queueAdapter) } fun addQueueAdapter(adapter: QueueAdapter) { 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 571b44622..b913d9511 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 @@ -37,10 +37,22 @@ class QueueFragment : Fragment() { val queueAdapter = QueueAdapter(helper) callback.addQueueAdapter(queueAdapter) + val queueClearItem = binding.queueToolbar.menu.findItem(R.id.action_clear_user_queue) + // --- UI SETUP --- - binding.queueToolbar.setNavigationOnClickListener { - findNavController().navigateUp() + binding.queueToolbar.apply { + setNavigationOnClickListener { + findNavController().navigateUp() + } + + setOnMenuItemClickListener { + if (it.itemId == R.id.action_clear_user_queue) { + queueAdapter.clearUserQueue() + playbackModel.clearUserQueue() + true + } else false + } } binding.queueRecycler.apply { @@ -53,8 +65,14 @@ class QueueFragment : Fragment() { // --- VIEWMODEL SETUP --- playbackModel.userQueue.observe(viewLifecycleOwner) { - if (it.isEmpty() && playbackModel.nextItemsInQueue.value!!.isEmpty()) { - findNavController().navigateUp() + if (it.isEmpty()) { + queueClearItem.isEnabled = false + + if (playbackModel.nextItemsInQueue.value!!.isEmpty()) { + findNavController().navigateUp() + + return@observe + } } queueAdapter.submitList(createQueueData()) @@ -76,10 +94,7 @@ class QueueFragment : Fragment() { if (playbackModel.userQueue.value!!.isNotEmpty()) { queue.add( - Header( - name = getString(R.string.label_next_user_queue), - isAction = true - ) + Header(name = getString(R.string.label_next_user_queue)) ) queue.addAll(playbackModel.userQueue.value!!) } @@ -93,8 +108,7 @@ class QueueFragment : Fragment() { getString(R.string.label_all_songs) else playbackModel.parent.value!!.name - ), - isAction = false + ) ) ) queue.addAll(playbackModel.nextItemsInQueue.value!!) 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 d207291d6..6233b6930 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 @@ -337,6 +337,12 @@ class PlaybackStateManager private constructor() { forceUserQueueUpdate() } + fun clearUserQueue() { + mUserQueue.clear() + + forceUserQueueUpdate() + } + // Force any callbacks to update when the queue is changed. private fun forceQueueUpdate() { mQueue = mQueue diff --git a/app/src/main/res/layout/fragment_queue.xml b/app/src/main/res/layout/fragment_queue.xml index ef6d336d8..5ed4fbf39 100644 --- a/app/src/main/res/layout/fragment_queue.xml +++ b/app/src/main/res/layout/fragment_queue.xml @@ -20,6 +20,8 @@ android:clickable="true" android:focusable="true" android:elevation="@dimen/elevation_normal" + app:popupTheme="@style/Widget.CustomPopup" + app:menu="@menu/menu_queue" app:navigationIcon="@drawable/ic_down" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" diff --git a/app/src/main/res/layout/item_action_header.xml b/app/src/main/res/layout/item_action_header.xml deleted file mode 100644 index 1ab419529..000000000 --- a/app/src/main/res/layout/item_action_header.xml +++ /dev/null @@ -1,52 +0,0 @@ - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/menu/menu_queue.xml b/app/src/main/res/menu/menu_queue.xml new file mode 100644 index 000000000..db57952ae --- /dev/null +++ b/app/src/main/res/menu/menu_queue.xml @@ -0,0 +1,8 @@ + + + + \ 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 9dcc6550c..71f2dc7ba 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -25,6 +25,7 @@ Add to queue Added to queue Next in Queue + Clear queue Music Playback The music playback service for Auxio.