From f3365fc40bff3ab0dd41369fa8f560c99e7268e4 Mon Sep 17 00:00:00 2001 From: Alexander Capehart Date: Fri, 16 Dec 2022 13:35:48 -0700 Subject: [PATCH] home: add selection actions Implement actions for selections in the home view. Play selected and shuffle selected have been removed for now until the queue can be properly reworked --- .../org/oxycblt/auxio/home/HomeFragment.kt | 28 ++++++++++++++---- .../auxio/playback/PlaybackViewModel.kt | 29 +++++++++++++++---- .../auxio/ui/SelectionToolbarOverlay.kt | 27 ++++++++++++++--- .../oxycblt/auxio/ui/SelectionViewModel.kt | 7 +++++ .../oxycblt/auxio/ui/fragment/MenuFragment.kt | 6 ---- .../main/res/menu/menu_selection_actions.xml | 21 +++++++------- 6 files changed, 87 insertions(+), 31 deletions(-) diff --git a/app/src/main/java/org/oxycblt/auxio/home/HomeFragment.kt b/app/src/main/java/org/oxycblt/auxio/home/HomeFragment.kt index c38d36349..44e8604a7 100644 --- a/app/src/main/java/org/oxycblt/auxio/home/HomeFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/home/HomeFragment.kt @@ -60,12 +60,7 @@ import org.oxycblt.auxio.ui.MainNavigationAction import org.oxycblt.auxio.ui.NavigationViewModel import org.oxycblt.auxio.ui.SelectionViewModel import org.oxycblt.auxio.ui.fragment.ViewBindingFragment -import org.oxycblt.auxio.util.androidActivityViewModels -import org.oxycblt.auxio.util.collect -import org.oxycblt.auxio.util.collectImmediately -import org.oxycblt.auxio.util.getColorCompat -import org.oxycblt.auxio.util.lazyReflectedField -import org.oxycblt.auxio.util.logD +import org.oxycblt.auxio.util.* /** * The main "Launching Point" fragment of Auxio, allowing navigation to the detail views for each @@ -118,6 +113,11 @@ class HomeFragment : ViewBindingFragment(), Toolbar.OnMenuI } } + binding.homeToolbarOverlay.registerListeners( + onExit = { selectionModel.consume() }, + onMenuItemClick = this + ) + binding.homeToolbar.setOnMenuItemClickListener(this@HomeFragment) updateTabConfiguration() @@ -175,11 +175,14 @@ class HomeFragment : ViewBindingFragment(), Toolbar.OnMenuI override fun onDestroyBinding(binding: FragmentHomeBinding) { super.onDestroyBinding(binding) + binding.homeToolbarOverlay.unregisterListeners() binding.homeToolbar.setOnMenuItemClickListener(null) } override fun onMenuItemClick(item: MenuItem): Boolean { when (item.itemId) { + // HOME + R.id.action_search -> { logD("Navigating to search") initAxisTransitions(MaterialSharedAxis.Z) @@ -205,6 +208,19 @@ class HomeFragment : ViewBindingFragment(), Toolbar.OnMenuI .getSortForTab(homeModel.currentTab.value) .withAscending(item.isChecked)) } + + // SELECTION + + R.id.action_play_next_selection -> { + playbackModel.playNext(selectionModel.consume()) + requireContext().showToast(R.string.lng_queue_added) + } + + R.id.action_queue_add_selection -> { + playbackModel.addToQueue(selectionModel.consume()) + requireContext().showToast(R.string.lng_queue_added) + } + else -> { // Sorting option was selected, mark it as selected and update the mode item.isChecked = true 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 47a29e3f7..3289428f2 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/PlaybackViewModel.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/PlaybackViewModel.kt @@ -25,11 +25,7 @@ import kotlinx.coroutines.delay import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.launch -import org.oxycblt.auxio.music.Album -import org.oxycblt.auxio.music.Artist -import org.oxycblt.auxio.music.Genre -import org.oxycblt.auxio.music.MusicParent -import org.oxycblt.auxio.music.Song +import org.oxycblt.auxio.music.* import org.oxycblt.auxio.playback.state.InternalPlayer import org.oxycblt.auxio.playback.state.PlaybackStateDatabase import org.oxycblt.auxio.playback.state.PlaybackStateManager @@ -44,6 +40,8 @@ import org.oxycblt.auxio.util.application * an interface that properly sanitizes input and abstracts functions unlike the master class.** * * @author OxygenCobalt + * + * TODO: Queue additions without a song should map to playing selected */ class PlaybackViewModel(application: Application) : AndroidViewModel(application), PlaybackStateManager.Callback { @@ -217,6 +215,11 @@ class PlaybackViewModel(application: Application) : playbackManager.playNext(settings.detailGenreSort.songs(genre.songs)) } + /** Add a selection [selection] to the top of the queue. */ + fun playNext(selection: List) { + playbackManager.playNext(selectionToSongs(selection)) + } + /** Add a [Song] to the end of the queue. */ fun addToQueue(song: Song) { playbackManager.addToQueue(song) @@ -237,6 +240,22 @@ class PlaybackViewModel(application: Application) : playbackManager.addToQueue(settings.detailGenreSort.songs(genre.songs)) } + /** Add a selection [selection] to the top of the queue. */ + fun addToQueue(selection: List) { + playbackManager.addToQueue(selectionToSongs(selection)) + } + + private fun selectionToSongs(selection: List): List { + return selection.flatMap { + when (it) { + is Album -> settings.detailAlbumSort.songs(it.songs) + is Artist -> settings.detailArtistSort.songs(it.songs) + is Genre -> settings.detailGenreSort.songs(it.songs) + is Song -> listOf(it) + } + } + } + // --- STATUS FUNCTIONS --- /** Flip the playing status, e.g from playing to paused */ diff --git a/app/src/main/java/org/oxycblt/auxio/ui/SelectionToolbarOverlay.kt b/app/src/main/java/org/oxycblt/auxio/ui/SelectionToolbarOverlay.kt index 3e0cca415..b650ec4bb 100644 --- a/app/src/main/java/org/oxycblt/auxio/ui/SelectionToolbarOverlay.kt +++ b/app/src/main/java/org/oxycblt/auxio/ui/SelectionToolbarOverlay.kt @@ -3,6 +3,7 @@ package org.oxycblt.auxio.ui import android.animation.ValueAnimator import android.content.Context import android.util.AttributeSet +import android.view.View import android.widget.FrameLayout import androidx.annotation.AttrRes import androidx.appcompat.widget.Toolbar @@ -30,6 +31,8 @@ constructor(context: Context, attrs: AttributeSet? = null, @AttrRes defStyleAttr setNavigationIcon(R.drawable.ic_close_24) } + private val selectionMenu = selectionToolbar.menu + private var fadeThroughAnimator: ValueAnimator? = null override fun onFinishInflate() { @@ -45,9 +48,22 @@ constructor(context: Context, attrs: AttributeSet? = null, @AttrRes defStyleAttr /** * Add listeners for the selection toolbar. */ - fun setListeners(onExit: Toolbar.OnMenuItemClickListener, - onMenuItemClick: Toolbar.OnMenuItemClickListener) { - // TODO: Sub + fun registerListeners(onExit: OnClickListener, + onMenuItemClick: Toolbar.OnMenuItemClickListener) { + selectionToolbar.apply { + setNavigationOnClickListener(onExit) + setOnMenuItemClickListener(onMenuItemClick) + } + } + + /** + * Unregister listeners for this instance. + */ + fun unregisterListeners() { + selectionToolbar.apply { + setNavigationOnClickListener(null) + setOnMenuItemClickListener(null) + } } /** @@ -69,13 +85,16 @@ constructor(context: Context, attrs: AttributeSet? = null, @AttrRes defStyleAttr // don't work due to translation) val targetInnerAlpha: Float val targetSelectionAlpha: Float + val targetDuration: Long if (selectionVisible) { targetInnerAlpha = 0f targetSelectionAlpha = 1f + targetDuration = context.resources.getInteger(R.integer.anim_fade_enter_duration).toLong() } else { targetInnerAlpha = 1f targetSelectionAlpha = 0f + targetDuration = context.resources.getInteger(R.integer.anim_fade_exit_duration).toLong() } if (innerToolbar.alpha == targetInnerAlpha && @@ -94,7 +113,7 @@ constructor(context: Context, attrs: AttributeSet? = null, @AttrRes defStyleAttr } fadeThroughAnimator = ValueAnimator.ofFloat(innerToolbar.alpha, targetInnerAlpha).apply { - duration = context.resources.getInteger(R.integer.anim_fade_enter_duration).toLong() + duration = targetDuration addUpdateListener { changeToolbarAlpha(it.animatedValue as Float) } diff --git a/app/src/main/java/org/oxycblt/auxio/ui/SelectionViewModel.kt b/app/src/main/java/org/oxycblt/auxio/ui/SelectionViewModel.kt index 588ed5ceb..e276a2411 100644 --- a/app/src/main/java/org/oxycblt/auxio/ui/SelectionViewModel.kt +++ b/app/src/main/java/org/oxycblt/auxio/ui/SelectionViewModel.kt @@ -40,4 +40,11 @@ class SelectionViewModel : ViewModel() { _selected.value = items } } + + /** Clear and return all selected items. */ + fun consume(): List { + return _selected.value.also { + _selected.value = listOf() + } + } } diff --git a/app/src/main/java/org/oxycblt/auxio/ui/fragment/MenuFragment.kt b/app/src/main/java/org/oxycblt/auxio/ui/fragment/MenuFragment.kt index edb42989a..495217ec6 100644 --- a/app/src/main/java/org/oxycblt/auxio/ui/fragment/MenuFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/ui/fragment/MenuFragment.kt @@ -185,12 +185,6 @@ abstract class MenuFragment : ViewBindingFragment() { private fun musicMenuImpl(anchor: View, @MenuRes menuRes: Int, onSelect: (Int) -> Boolean) { menu(anchor, menuRes) { - for (item in menu.children) { - if (item.itemId == R.id.action_play_next || item.itemId == R.id.action_queue_add) { - item.isEnabled = playbackModel.song.value != null - } - } - setOnMenuItemClickListener { item -> onSelect(item.itemId) } } } diff --git a/app/src/main/res/menu/menu_selection_actions.xml b/app/src/main/res/menu/menu_selection_actions.xml index 368295fd4..f29b78e1e 100644 --- a/app/src/main/res/menu/menu_selection_actions.xml +++ b/app/src/main/res/menu/menu_selection_actions.xml @@ -2,20 +2,21 @@ - - + + + + + + + + + \ No newline at end of file