diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index f422c3a12..67ef8206c 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -7,7 +7,6 @@ = mUserQueue.value!!.size || to < 0) return false + queueAdapter.moveItems(adapterFrom, adapterTo) + playbackManager.moveUserQueueItems(from, to) } else { // 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 } + queueAdapter.moveItems(adapterFrom, adapterTo) + playbackManager.moveQueueItems(from, to) } 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 1ad4caf98..e5c3cce94 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 @@ -5,8 +5,8 @@ import android.util.Log import android.view.LayoutInflater import android.view.MotionEvent import android.view.ViewGroup +import androidx.recyclerview.widget.AsyncListDiffer import androidx.recyclerview.widget.ItemTouchHelper -import androidx.recyclerview.widget.ListAdapter import androidx.recyclerview.widget.RecyclerView import org.oxycblt.auxio.databinding.ItemQueueSongBinding 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.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( - val touchHelper: ItemTouchHelper -) : ListAdapter(DiffCallback()) { + private val touchHelper: ItemTouchHelper +) : RecyclerView.Adapter() { + private var data = mutableListOf() + private var listDiffer = AsyncListDiffer(this, DiffCallback()) + + override fun getItemCount(): Int = data.size + override fun getItemViewType(position: Int): Int { - val item = getItem(position) + val item = data[position] return if (item is Header) HeaderViewHolder.ITEM_TYPE @@ -39,7 +52,7 @@ class QueueAdapter( } 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 Header -> (holder as HeaderViewHolder).bind(item) @@ -49,6 +62,27 @@ class QueueAdapter( } } + fun submitList(newData: MutableList) { + 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 inner class ViewHolder( 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 97d81f20d..4cbb0984b 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 @@ -10,6 +10,8 @@ import kotlin.math.sign // The drag callback used for the Queue RecyclerView. class QueueDragCallback(private val playbackModel: PlaybackViewModel) : ItemTouchHelper.Callback() { + private lateinit var queueAdapter: QueueAdapter + override fun getMovementFlags( recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder @@ -51,11 +53,15 @@ class QueueDragCallback(private val playbackModel: PlaybackViewModel) : ItemTouc viewHolder: RecyclerView.ViewHolder, target: RecyclerView.ViewHolder ): Boolean { - return playbackModel.moveQueueItems(viewHolder.adapterPosition, target.adapterPosition) + return playbackModel.moveQueueItems(viewHolder.adapterPosition, target.adapterPosition, queueAdapter) } 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 { 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 be511894c..4681d1145 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 @@ -8,7 +8,6 @@ import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels import androidx.navigation.fragment.findNavController import androidx.recyclerview.widget.ItemTouchHelper -import androidx.recyclerview.widget.LinearLayoutManager import org.oxycblt.auxio.R import org.oxycblt.auxio.databinding.FragmentQueueBinding import org.oxycblt.auxio.music.BaseModel @@ -27,8 +26,10 @@ class QueueFragment : Fragment() { ): View? { val binding = FragmentQueueBinding.inflate(inflater) - val helper = ItemTouchHelper(QueueDragCallback(playbackModel)) + val callback = QueueDragCallback(playbackModel) + val helper = ItemTouchHelper(callback) val queueAdapter = QueueAdapter(helper) + callback.addQueueAdapter(queueAdapter) // --- UI SETUP --- @@ -50,9 +51,7 @@ class QueueFragment : Fragment() { findNavController().navigateUp() } - queueAdapter.submitList(createQueueDisplay()) { - scrollRecyclerIfNeeded(binding) - } + queueAdapter.submitList(createQueueData()) } playbackModel.nextItemsInQueue.observe(viewLifecycleOwner) { @@ -60,15 +59,13 @@ class QueueFragment : Fragment() { findNavController().navigateUp() } - queueAdapter.submitList(createQueueDisplay()) { - scrollRecyclerIfNeeded(binding) - } + queueAdapter.submitList(createQueueData()) } return binding.root } - private fun createQueueDisplay(): MutableList { + private fun createQueueData(): MutableList { val queue = mutableListOf() if (playbackModel.userQueue.value!!.isNotEmpty()) { @@ -93,12 +90,4 @@ class QueueFragment : Fragment() { return queue } - - private fun scrollRecyclerIfNeeded(binding: FragmentQueueBinding) { - if ((binding.queueRecycler.layoutManager as LinearLayoutManager) - .findFirstVisibleItemPosition() < 1 - ) { - binding.queueRecycler.scrollToPosition(0) - } - } } 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 7c94209fc..208503330 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 @@ -13,7 +13,8 @@ import kotlin.random.Random /** * 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 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()]. * @author OxygenCobalt @@ -120,7 +121,7 @@ class PlaybackStateManager private constructor() { PlaybackMode.ALL_SONGS -> null PlaybackMode.IN_ARTIST -> song.album.artist PlaybackMode.IN_ALBUM -> song.album - PlaybackMode.IN_GENRE -> error("what") + PlaybackMode.IN_GENRE -> song.album.artist.genres[0] } mMode = mode @@ -129,7 +130,7 @@ class PlaybackStateManager private constructor() { PlaybackMode.ALL_SONGS -> musicStore.songs.toMutableList() PlaybackMode.IN_ARTIST -> song.album.artist.songs PlaybackMode.IN_ALBUM -> song.album.songs - PlaybackMode.IN_GENRE -> error("what") + PlaybackMode.IN_GENRE -> song.album.artist.genres[0].songs } resetLoopMode() diff --git a/app/src/main/java/org/oxycblt/auxio/recycler/DiffCallback.kt b/app/src/main/java/org/oxycblt/auxio/recycler/DiffCallback.kt index db2a586df..f04767d61 100644 --- a/app/src/main/java/org/oxycblt/auxio/recycler/DiffCallback.kt +++ b/app/src/main/java/org/oxycblt/auxio/recycler/DiffCallback.kt @@ -6,10 +6,10 @@ import org.oxycblt.auxio.music.BaseModel // Base Diff callback class DiffCallback : DiffUtil.ItemCallback() { override fun areContentsTheSame(oldItem: T, newItem: T): Boolean { - return oldItem.id == newItem.id + return oldItem == newItem } override fun areItemsTheSame(oldItem: T, newItem: T): Boolean { - return oldItem == newItem + return oldItem.id == newItem.id } } 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 e50408be5..597616ae3 100644 --- a/app/src/main/java/org/oxycblt/auxio/ui/InterfaceUtils.kt +++ b/app/src/main/java/org/oxycblt/auxio/ui/InterfaceUtils.kt @@ -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 fun RecyclerView.applyDivider() { 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 // behavior when duplicate user queue items are added. // FIXME: Fix the duplicate item DiffCallback issue - if (!playbackModel.userQueue.value!!.contains(song)) { - playbackModel.addToUserQueue(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() - } + context.getString(R.string.label_queue_added).createToast(context) } diff --git a/app/src/main/java/org/oxycblt/auxio/ui/ThemeUtils.kt b/app/src/main/java/org/oxycblt/auxio/ui/ThemeUtils.kt index 2a9095709..0afa0c283 100644 --- a/app/src/main/java/org/oxycblt/auxio/ui/ThemeUtils.kt +++ b/app/src/main/java/org/oxycblt/auxio/ui/ThemeUtils.kt @@ -19,7 +19,7 @@ private val ACCENTS = listOf( Pair(R.color.deep_purple, R.style.Theme_DeepPurple), // 3 Pair(R.color.indigo, R.style.Theme_Indigo), // 4 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.teal, R.style.Theme_Teal), // 8 Pair(R.color.green, R.style.Theme_Green), // 9 diff --git a/app/src/main/res/drawable/ui_seekbar_indicator.xml b/app/src/main/res/drawable/ui_seekbar_indicator.xml deleted file mode 100644 index a6a040324..000000000 --- a/app/src/main/res/drawable/ui_seekbar_indicator.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/item_header.xml b/app/src/main/res/layout/item_header.xml index 56b5df336..a40341f26 100644 --- a/app/src/main/res/layout/item_header.xml +++ b/app/src/main/res/layout/item_header.xml @@ -1,7 +1,6 @@ diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 5c521aad6..e8dcdd985 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -61,7 +61,6 @@ Unknown Genre Unknown Artist Unknown Album - Unknown Song No Date diff --git a/build.gradle b/build.gradle index 3c34f805b..b6795aad6 100644 --- a/build.gradle +++ b/build.gradle @@ -7,7 +7,7 @@ buildscript { jcenter() } 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 "android.arch.navigation:navigation-safe-args-gradle-plugin:1.0.0"