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:
parent
04e25eb90a
commit
f3365fc40b
6 changed files with 87 additions and 31 deletions
|
@ -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
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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,
|
||||
fun registerListeners(onExit: OnClickListener,
|
||||
onMenuItemClick: Toolbar.OnMenuItemClickListener) {
|
||||
// TODO: Sub
|
||||
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)
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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>
|
Loading…
Reference in a new issue