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
This commit is contained in:
Alexander Capehart 2022-12-16 13:35:48 -07:00
parent 04e25eb90a
commit f3365fc40b
No known key found for this signature in database
GPG key ID: 37DBE3621FE9AD47
6 changed files with 87 additions and 31 deletions

View file

@ -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<FragmentHomeBinding>(), Toolbar.OnMenuI
}
}
binding.homeToolbarOverlay.registerListeners(
onExit = { selectionModel.consume() },
onMenuItemClick = this
)
binding.homeToolbar.setOnMenuItemClickListener(this@HomeFragment)
updateTabConfiguration()
@ -175,11 +175,14 @@ class HomeFragment : ViewBindingFragment<FragmentHomeBinding>(), 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<FragmentHomeBinding>(), 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

View file

@ -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<Music>) {
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<Music>) {
playbackManager.addToQueue(selectionToSongs(selection))
}
private fun selectionToSongs(selection: List<Music>): List<Song> {
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 */

View file

@ -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)
}

View file

@ -40,4 +40,11 @@ class SelectionViewModel : ViewModel() {
_selected.value = items
}
}
/** Clear and return all selected items. */
fun consume(): List<Music> {
return _selected.value.also {
_selected.value = listOf()
}
}
}

View file

@ -185,12 +185,6 @@ abstract class MenuFragment<T : ViewBinding> : ViewBindingFragment<T>() {
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) }
}
}

View file

@ -2,20 +2,21 @@
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/action_play_next"
android:id="@+id/action_play_next_selection"
android:title="@string/lbl_play_next"
android:icon="@drawable/ic_play_next_24"
app:showAsAction="ifRoom"/>
<item
android:id="@+id/action_queue_add"
android:id="@+id/action_queue_add_selection"
android:title="@string/lbl_queue_add"
app:showAsAction="never" />
<item
android:id="@+id/action_play"
android:title="@string/lbl_play_selected"
app:showAsAction="never"/>
<item
android:id="@+id/action_shuffle"
android:title="@string/lbl_shuffle_selected"
app:showAsAction="never"/>
<!-- TOOD: Disabled until able to get queue system into shape -->
<!-- <item-->
<!-- android:id="@+id/action_play_selection"-->
<!-- android:title="@string/lbl_play_selected"-->
<!-- app:showAsAction="never"/>-->
<!-- <item-->
<!-- android:id="@+id/action_shuffle_selection"-->
<!-- android:title="@string/lbl_shuffle_selected"-->
<!-- app:showAsAction="never"/>-->
</menu>