diff --git a/CHANGELOG.md b/CHANGELOG.md index 5df056172..99de2d1a2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ at the cost of longer loading times - Added Last Added sorting - Search now takes sort tags and file names in account [#184] - Added option to clear playback state in settings +- Added ability to play songs from queue #### What's Improved - App now exposes an (immutable) queue. diff --git a/app/src/main/java/org/oxycblt/auxio/music/system/IndexerService.kt b/app/src/main/java/org/oxycblt/auxio/music/system/IndexerService.kt index 154d9db73..7fc587821 100644 --- a/app/src/main/java/org/oxycblt/auxio/music/system/IndexerService.kt +++ b/app/src/main/java/org/oxycblt/auxio/music/system/IndexerService.kt @@ -72,6 +72,11 @@ class IndexerService : Service(), Indexer.Controller, Settings.Callback { indexingNotification = IndexingNotification(this) observingNotification = ObservingNotification(this) + wakeLock = + getSystemServiceSafe(PowerManager::class) + .newWakeLock( + PowerManager.PARTIAL_WAKE_LOCK, BuildConfig.APPLICATION_ID + ".IndexerService") + settings = Settings(this, this) indexerContentObserver = SystemContentObserver() @@ -81,11 +86,6 @@ class IndexerService : Service(), Indexer.Controller, Settings.Callback { onStartIndexing() } - wakeLock = - getSystemServiceSafe(PowerManager::class) - .newWakeLock( - PowerManager.PARTIAL_WAKE_LOCK, BuildConfig.APPLICATION_ID + ".IndexerService") - logD("Service created.") } 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 d96e3012b..3d98735da 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/PlaybackViewModel.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/PlaybackViewModel.kt @@ -36,6 +36,7 @@ import org.oxycblt.auxio.playback.state.PlaybackStateManager import org.oxycblt.auxio.playback.state.RepeatMode import org.oxycblt.auxio.settings.Settings import org.oxycblt.auxio.util.application +import org.oxycblt.auxio.util.logD import org.oxycblt.auxio.util.logE /** @@ -195,6 +196,19 @@ class PlaybackViewModel(application: Application) : playbackManager.prev() } + /** + * Go to an item in the queue using it's recyclerview adapter index. No-ops if out of bounds. + */ + fun goto(adapterIndex: Int) { + val index = adapterIndex + (playbackManager.queue.size - _nextUp.value.size) + logD(adapterIndex) + logD(playbackManager.queue.size - _nextUp.value.size) + + if (index in playbackManager.queue.indices) { + playbackManager.goto(index) + } + } + /** Remove a queue item using it's recyclerview adapter index. */ fun removeQueueDataItem(adapterIndex: Int) { val index = adapterIndex + (playbackManager.queue.size - _nextUp.value.size) 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 1937e6b73..832dace5a 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 @@ -19,32 +19,26 @@ package org.oxycblt.auxio.playback.queue import android.annotation.SuppressLint import android.content.Context -import android.graphics.drawable.ColorDrawable import android.view.MotionEvent import android.view.View import androidx.core.view.isInvisible import androidx.recyclerview.widget.RecyclerView import com.google.android.material.shape.MaterialShapeDrawable import org.oxycblt.auxio.IntegerTable +import org.oxycblt.auxio.R import org.oxycblt.auxio.databinding.ItemQueueSongBinding import org.oxycblt.auxio.music.Song -import org.oxycblt.auxio.ui.recycler.BindingViewHolder -import org.oxycblt.auxio.ui.recycler.MonoAdapter -import org.oxycblt.auxio.ui.recycler.SongViewHolder -import org.oxycblt.auxio.ui.recycler.SyncBackingData -import org.oxycblt.auxio.util.context -import org.oxycblt.auxio.util.disableDropShadowCompat -import org.oxycblt.auxio.util.inflater -import org.oxycblt.auxio.util.stateList -import org.oxycblt.auxio.util.textSafe +import org.oxycblt.auxio.ui.recycler.* +import org.oxycblt.auxio.util.* -class QueueAdapter(listener: QueueItemListener) : +class QueueAdapter(private val listener: QueueItemListener) : MonoAdapter(listener) { override val data = SyncBackingData(this, QueueSongViewHolder.DIFFER) override val creator = QueueSongViewHolder.CREATOR } interface QueueItemListener { + fun onClick(viewHolder: RecyclerView.ViewHolder) fun onPickUp(viewHolder: RecyclerView.ViewHolder) } @@ -57,13 +51,13 @@ private constructor( val backgroundView: View get() = binding.background - init { - binding.body.background = - MaterialShapeDrawable.createWithElevationOverlay(binding.root.context).apply { - fillColor = (binding.body.background as ColorDrawable).color.stateList - } + val backgroundDrawable = + MaterialShapeDrawable.createWithElevationOverlay(binding.root.context).apply { + fillColor = binding.context.getAttrColorSafe(R.attr.colorSurface).stateList + } - binding.root.disableDropShadowCompat() + init { + binding.body.background = backgroundDrawable } @SuppressLint("ClickableViewAccessibility") @@ -77,6 +71,8 @@ private constructor( binding.songName.requestLayout() binding.songInfo.requestLayout() + binding.body.setOnClickListener { listener.onClick(this) } + // Roll our own drag handlers as the default ones suck binding.songDragHandle.setOnTouchListener { _, motionEvent -> binding.songDragHandle.performClick() @@ -85,11 +81,6 @@ private constructor( true } else false } - - binding.body.setOnLongClickListener { - listener.onPickUp(this) - true - } } companion object { 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 05495b1a0..8892f18d2 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 @@ -22,7 +22,6 @@ import android.view.animation.AccelerateDecelerateInterpolator import androidx.core.view.isInvisible import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.RecyclerView -import com.google.android.material.shape.MaterialShapeDrawable import kotlin.math.abs import kotlin.math.max import kotlin.math.min @@ -87,7 +86,7 @@ class QueueDragCallback(private val playbackModel: PlaybackViewModel) : ItemTouc if (shouldLift && isCurrentlyActive && actionState == ItemTouchHelper.ACTION_STATE_DRAG) { logD("Lifting queue item") - val bg = holder.bodyView.background as MaterialShapeDrawable + val bg = holder.backgroundDrawable val elevation = recyclerView.context.getDimenSafe(R.dimen.elevation_small) holder.itemView .animate() @@ -125,7 +124,7 @@ class QueueDragCallback(private val playbackModel: PlaybackViewModel) : ItemTouc if (holder.itemView.translationZ != 0f) { logD("Dropping queue item") - val bg = holder.bodyView.background as MaterialShapeDrawable + val bg = holder.backgroundDrawable holder.itemView .animate() .translationZ(0.0f) 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 9cd22e6a0..1489e2c12 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 @@ -61,6 +61,10 @@ class QueueFragment : ViewBindingFragment(), QueueItemList binding.queueRecycler.adapter = null } + override fun onClick(viewHolder: RecyclerView.ViewHolder) { + playbackModel.goto(viewHolder.bindingAdapterPosition) + } + override fun onPickUp(viewHolder: RecyclerView.ViewHolder) { touchHelper.startDrag(viewHolder) } 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 fc4c363cc..c0f16e0f5 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 @@ -200,9 +200,9 @@ class PlaybackStateManager private constructor() { // Increment the index, if it cannot be incremented any further, then // repeat and pause/resume playback depending on the setting if (index < _queue.lastIndex) { - goto(index + 1, true) + gotoImpl(index + 1, true) } else { - goto(0, repeatMode == RepeatMode.ALL) + gotoImpl(0, repeatMode == RepeatMode.ALL) } } @@ -214,11 +214,16 @@ class PlaybackStateManager private constructor() { rewind() isPlaying = true } else { - goto(max(index - 1, 0), true) + gotoImpl(max(index - 1, 0), true) } } - private fun goto(idx: Int, play: Boolean) { + @Synchronized + fun goto(index: Int) { + gotoImpl(index, true) + } + + private fun gotoImpl(idx: Int, play: Boolean) { index = idx seekTo(0) notifyIndexMoved() diff --git a/app/src/main/res/layout/item_queue_song.xml b/app/src/main/res/layout/item_queue_song.xml index 6d4ee8899..2f46a1e93 100644 --- a/app/src/main/res/layout/item_queue_song.xml +++ b/app/src/main/res/layout/item_queue_song.xml @@ -21,11 +21,15 @@ android:src="@drawable/ic_delete_24" app:tint="?attr/colorSurface" /> - + android:layout_height="wrap_content"> + + - + + +