playback: refactor navigation commands

Unify the artist and genre picker commands into a single event
(like others), and also make the playback panel correctly respond to
album/artist navigation events.
This commit is contained in:
Alexander Capehart 2023-07-04 14:24:46 -06:00
parent a6a3eceb7b
commit 9e5b737d1a
No known key found for this signature in database
GPG key ID: 37DBE3621FE9AD47
11 changed files with 197 additions and 148 deletions

View file

@ -42,7 +42,7 @@ import org.oxycblt.auxio.detail.DetailViewModel
import org.oxycblt.auxio.list.selection.SelectionViewModel import org.oxycblt.auxio.list.selection.SelectionViewModel
import org.oxycblt.auxio.music.Music import org.oxycblt.auxio.music.Music
import org.oxycblt.auxio.music.Song import org.oxycblt.auxio.music.Song
import org.oxycblt.auxio.playback.Panel import org.oxycblt.auxio.playback.OpenPanel
import org.oxycblt.auxio.playback.PlaybackBottomSheetBehavior import org.oxycblt.auxio.playback.PlaybackBottomSheetBehavior
import org.oxycblt.auxio.playback.PlaybackViewModel import org.oxycblt.auxio.playback.PlaybackViewModel
import org.oxycblt.auxio.playback.queue.QueueBottomSheetBehavior import org.oxycblt.auxio.playback.queue.QueueBottomSheetBehavior
@ -300,13 +300,13 @@ class MainFragment :
} }
} }
private fun handlePanel(panel: Panel?) { private fun handlePanel(panel: OpenPanel?) {
if (panel == null) return if (panel == null) return
logD("Trying to update panel to $panel") logD("Trying to update panel to $panel")
when (panel) { when (panel) {
is Panel.Main -> tryClosePlaybackPanel() is OpenPanel.Main -> tryClosePlaybackPanel()
is Panel.Playback -> tryOpenPlaybackPanel() is OpenPanel.Playback -> tryOpenPlaybackPanel()
is Panel.Queue -> tryOpenQueuePanel() is OpenPanel.Queue -> tryOpenQueuePanel()
} }
playbackModel.openPanel.consume() playbackModel.openPanel.consume()
} }

View file

@ -51,6 +51,7 @@ import org.oxycblt.auxio.music.MusicViewModel
import org.oxycblt.auxio.music.PlaylistDecision import org.oxycblt.auxio.music.PlaylistDecision
import org.oxycblt.auxio.music.Song import org.oxycblt.auxio.music.Song
import org.oxycblt.auxio.music.info.Disc import org.oxycblt.auxio.music.info.Disc
import org.oxycblt.auxio.playback.PlaybackDecision
import org.oxycblt.auxio.playback.PlaybackViewModel import org.oxycblt.auxio.playback.PlaybackViewModel
import org.oxycblt.auxio.util.canScroll import org.oxycblt.auxio.util.canScroll
import org.oxycblt.auxio.util.collect import org.oxycblt.auxio.util.collect
@ -129,11 +130,10 @@ class AlbumDetailFragment :
collect(detailModel.toShow.flow, ::handleShow) collect(detailModel.toShow.flow, ::handleShow)
collect(menuModel.pendingMenu.flow, ::handleMenu) collect(menuModel.pendingMenu.flow, ::handleMenu)
collectImmediately(selectionModel.selected, ::updateSelection) collectImmediately(selectionModel.selected, ::updateSelection)
collect(musicModel.playlistDecision.flow, ::handleDecision) collect(musicModel.playlistDecision.flow, ::handlePlaylistDecision)
collectImmediately( collectImmediately(
playbackModel.song, playbackModel.parent, playbackModel.isPlaying, ::updatePlayback) playbackModel.song, playbackModel.parent, playbackModel.isPlaying, ::updatePlayback)
collect(playbackModel.artistPickerSong.flow, ::handlePlayFromArtist) collect(playbackModel.playbackDecision.flow, ::handlePlaybackDecision)
collect(playbackModel.genrePickerSong.flow, ::handlePlayFromGenre)
} }
override fun onDestroyBinding(binding: FragmentDetailBinding) { override fun onDestroyBinding(binding: FragmentDetailBinding) {
@ -331,7 +331,7 @@ class AlbumDetailFragment :
} }
} }
private fun handleDecision(decision: PlaylistDecision?) { private fun handlePlaylistDecision(decision: PlaylistDecision?) {
if (decision == null) return if (decision == null) return
val directions = val directions =
when (decision) { when (decision) {
@ -342,7 +342,7 @@ class AlbumDetailFragment :
} }
is PlaylistDecision.New, is PlaylistDecision.New,
is PlaylistDecision.Rename, is PlaylistDecision.Rename,
is PlaylistDecision.Delete -> error("Unexpected decision $decision") is PlaylistDecision.Delete -> error("Unexpected playlist decision $decision")
} }
findNavController().navigateSafe(directions) findNavController().navigateSafe(directions)
} }
@ -352,16 +352,20 @@ class AlbumDetailFragment :
song.takeIf { parent == detailModel.currentAlbum.value }, isPlaying) song.takeIf { parent == detailModel.currentAlbum.value }, isPlaying)
} }
private fun handlePlayFromArtist(song: Song?) { private fun handlePlaybackDecision(decision: PlaybackDecision?) {
if (song == null) return if (decision == null) return
logD("Launching play from artist dialog for $song") val directions =
findNavController().navigateSafe(AlbumDetailFragmentDirections.playFromArtist(song.uid)) when (decision) {
is PlaybackDecision.PlayFromArtist -> {
logD("Launching play from artist dialog for $decision")
AlbumDetailFragmentDirections.playFromArtist(decision.song.uid)
} }
is PlaybackDecision.PlayFromGenre -> {
private fun handlePlayFromGenre(song: Song?) { logD("Launching play from artist dialog for $decision")
if (song == null) return AlbumDetailFragmentDirections.playFromGenre(decision.song.uid)
logD("Launching play from genre dialog for $song") }
findNavController().navigateSafe(AlbumDetailFragmentDirections.playFromGenre(song.uid)) }
findNavController().navigateSafe(directions)
} }
private fun scrollToAlbumSong(song: Song) { private fun scrollToAlbumSong(song: Song) {

View file

@ -50,6 +50,7 @@ import org.oxycblt.auxio.music.MusicParent
import org.oxycblt.auxio.music.MusicViewModel import org.oxycblt.auxio.music.MusicViewModel
import org.oxycblt.auxio.music.PlaylistDecision import org.oxycblt.auxio.music.PlaylistDecision
import org.oxycblt.auxio.music.Song import org.oxycblt.auxio.music.Song
import org.oxycblt.auxio.playback.PlaybackDecision
import org.oxycblt.auxio.playback.PlaybackViewModel import org.oxycblt.auxio.playback.PlaybackViewModel
import org.oxycblt.auxio.util.collect import org.oxycblt.auxio.util.collect
import org.oxycblt.auxio.util.collectImmediately import org.oxycblt.auxio.util.collectImmediately
@ -130,11 +131,10 @@ class ArtistDetailFragment :
collect(detailModel.toShow.flow, ::handleShow) collect(detailModel.toShow.flow, ::handleShow)
collect(menuModel.pendingMenu.flow, ::handleMenu) collect(menuModel.pendingMenu.flow, ::handleMenu)
collectImmediately(selectionModel.selected, ::updateSelection) collectImmediately(selectionModel.selected, ::updateSelection)
collect(musicModel.playlistDecision.flow, ::handleDecision) collect(musicModel.playlistDecision.flow, ::handlePlaylistDecision)
collectImmediately( collectImmediately(
playbackModel.song, playbackModel.parent, playbackModel.isPlaying, ::updatePlayback) playbackModel.song, playbackModel.parent, playbackModel.isPlaying, ::updatePlayback)
collect(playbackModel.artistPickerSong.flow, ::handlePlayFromArtist) collect(playbackModel.playbackDecision.flow, ::handlePlaybackDecision)
collect(playbackModel.genrePickerSong.flow, ::handlePlayFromGenre)
} }
override fun onDestroyBinding(binding: FragmentDetailBinding) { override fun onDestroyBinding(binding: FragmentDetailBinding) {
@ -343,7 +343,7 @@ class ArtistDetailFragment :
} }
} }
private fun handleDecision(decision: PlaylistDecision?) { private fun handlePlaylistDecision(decision: PlaylistDecision?) {
if (decision == null) return if (decision == null) return
val directions = val directions =
when (decision) { when (decision) {
@ -354,7 +354,7 @@ class ArtistDetailFragment :
} }
is PlaylistDecision.New, is PlaylistDecision.New,
is PlaylistDecision.Rename, is PlaylistDecision.Rename,
is PlaylistDecision.Delete -> error("Unexpected decision $decision") is PlaylistDecision.Delete -> error("Unexpected playlist decision $decision")
} }
findNavController().navigateSafe(directions) findNavController().navigateSafe(directions)
} }
@ -374,15 +374,17 @@ class ArtistDetailFragment :
artistListAdapter.setPlaying(playingItem, isPlaying) artistListAdapter.setPlaying(playingItem, isPlaying)
} }
private fun handlePlayFromArtist(song: Song?) { private fun handlePlaybackDecision(decision: PlaybackDecision?) {
if (song == null) return if (decision == null) return
logD("Launching play from artist dialog for $song") val directions =
findNavController().navigateSafe(AlbumDetailFragmentDirections.playFromArtist(song.uid)) when (decision) {
is PlaybackDecision.PlayFromArtist ->
error("Unexpected playback decision $decision")
is PlaybackDecision.PlayFromGenre -> {
logD("Launching play from artist dialog for $decision")
ArtistDetailFragmentDirections.playFromGenre(decision.song.uid)
} }
}
private fun handlePlayFromGenre(song: Song?) { findNavController().navigateSafe(directions)
if (song == null) return
logD("Launching play from genre dialog for $song")
findNavController().navigateSafe(AlbumDetailFragmentDirections.playFromGenre(song.uid))
} }
} }

View file

@ -50,6 +50,7 @@ import org.oxycblt.auxio.music.MusicParent
import org.oxycblt.auxio.music.MusicViewModel import org.oxycblt.auxio.music.MusicViewModel
import org.oxycblt.auxio.music.PlaylistDecision import org.oxycblt.auxio.music.PlaylistDecision
import org.oxycblt.auxio.music.Song import org.oxycblt.auxio.music.Song
import org.oxycblt.auxio.playback.PlaybackDecision
import org.oxycblt.auxio.playback.PlaybackViewModel import org.oxycblt.auxio.playback.PlaybackViewModel
import org.oxycblt.auxio.util.collect import org.oxycblt.auxio.util.collect
import org.oxycblt.auxio.util.collectImmediately import org.oxycblt.auxio.util.collectImmediately
@ -131,8 +132,7 @@ class GenreDetailFragment :
collect(musicModel.playlistDecision.flow, ::handleDecision) collect(musicModel.playlistDecision.flow, ::handleDecision)
collectImmediately( collectImmediately(
playbackModel.song, playbackModel.parent, playbackModel.isPlaying, ::updatePlayback) playbackModel.song, playbackModel.parent, playbackModel.isPlaying, ::updatePlayback)
collect(playbackModel.artistPickerSong.flow, ::handlePlayFromArtist) collect(playbackModel.playbackDecision.flow, ::handlePlaybackDecision)
collect(playbackModel.genrePickerSong.flow, ::handlePlayFromGenre)
} }
override fun onDestroyBinding(binding: FragmentDetailBinding) { override fun onDestroyBinding(binding: FragmentDetailBinding) {
@ -337,12 +337,12 @@ class GenreDetailFragment :
when (decision) { when (decision) {
is PlaylistDecision.Add -> { is PlaylistDecision.Add -> {
logD("Adding ${decision.songs.size} songs to a playlist") logD("Adding ${decision.songs.size} songs to a playlist")
ArtistDetailFragmentDirections.addToPlaylist( GenreDetailFragmentDirections.addToPlaylist(
decision.songs.map { it.uid }.toTypedArray()) decision.songs.map { it.uid }.toTypedArray())
} }
is PlaylistDecision.New, is PlaylistDecision.New,
is PlaylistDecision.Rename, is PlaylistDecision.Rename,
is PlaylistDecision.Delete -> error("Unexpected decision $decision") is PlaylistDecision.Delete -> error("Unexpected playlist decision $decision")
} }
findNavController().navigateSafe(directions) findNavController().navigateSafe(directions)
} }
@ -362,15 +362,16 @@ class GenreDetailFragment :
genreListAdapter.setPlaying(playingItem, isPlaying) genreListAdapter.setPlaying(playingItem, isPlaying)
} }
private fun handlePlayFromArtist(song: Song?) { private fun handlePlaybackDecision(decision: PlaybackDecision?) {
if (song == null) return if (decision == null) return
logD("Launching play from artist dialog for $song") val directions =
findNavController().navigateSafe(AlbumDetailFragmentDirections.playFromArtist(song.uid)) when (decision) {
is PlaybackDecision.PlayFromArtist -> {
logD("Launching play from artist dialog for $decision")
GenreDetailFragmentDirections.playFromArtist(decision.song.uid)
} }
is PlaybackDecision.PlayFromGenre -> error("Unexpected playback decision $decision")
private fun handlePlayFromGenre(song: Song?) { }
if (song == null) return findNavController().navigateSafe(directions)
logD("Launching play from genre dialog for $song")
findNavController().navigateSafe(AlbumDetailFragmentDirections.playFromGenre(song.uid))
} }
} }

View file

@ -52,6 +52,7 @@ import org.oxycblt.auxio.music.MusicViewModel
import org.oxycblt.auxio.music.Playlist import org.oxycblt.auxio.music.Playlist
import org.oxycblt.auxio.music.PlaylistDecision import org.oxycblt.auxio.music.PlaylistDecision
import org.oxycblt.auxio.music.Song import org.oxycblt.auxio.music.Song
import org.oxycblt.auxio.playback.PlaybackDecision
import org.oxycblt.auxio.playback.PlaybackViewModel import org.oxycblt.auxio.playback.PlaybackViewModel
import org.oxycblt.auxio.util.collect import org.oxycblt.auxio.util.collect
import org.oxycblt.auxio.util.collectImmediately import org.oxycblt.auxio.util.collectImmediately
@ -146,8 +147,7 @@ class PlaylistDetailFragment :
collect(musicModel.playlistDecision.flow, ::handleDecision) collect(musicModel.playlistDecision.flow, ::handleDecision)
collectImmediately( collectImmediately(
playbackModel.song, playbackModel.parent, playbackModel.isPlaying, ::updatePlayback) playbackModel.song, playbackModel.parent, playbackModel.isPlaying, ::updatePlayback)
collect(playbackModel.artistPickerSong.flow, ::handlePlayFromArtist) collect(playbackModel.playbackDecision.flow, ::handlePlaybackDecision)
collect(playbackModel.genrePickerSong.flow, ::handlePlayFromGenre)
} }
override fun onStart() { override fun onStart() {
@ -387,7 +387,7 @@ class PlaylistDetailFragment :
PlaylistDetailFragmentDirections.deletePlaylist(decision.playlist.uid) PlaylistDetailFragmentDirections.deletePlaylist(decision.playlist.uid)
} }
is PlaylistDecision.Add, is PlaylistDecision.Add,
is PlaylistDecision.New -> error("Unexpected decision $decision") is PlaylistDecision.New -> error("Unexpected playlist decision $decision")
} }
findNavController().navigateSafe(directions) findNavController().navigateSafe(directions)
} }
@ -398,17 +398,22 @@ class PlaylistDetailFragment :
song.takeIf { parent == detailModel.currentPlaylist.value }, isPlaying) song.takeIf { parent == detailModel.currentPlaylist.value }, isPlaying)
} }
private fun handlePlayFromArtist(song: Song?) { private fun handlePlaybackDecision(decision: PlaybackDecision?) {
if (song == null) return if (decision == null) return
logD("Launching play from artist dialog for $song") val directions =
findNavController().navigateSafe(AlbumDetailFragmentDirections.playFromArtist(song.uid)) when (decision) {
is PlaybackDecision.PlayFromArtist -> {
logD("Launching play from artist dialog for $decision")
PlaylistDetailFragmentDirections.playFromArtist(decision.song.uid)
}
is PlaybackDecision.PlayFromGenre -> {
logD("Launching play from artist dialog for $decision")
PlaylistDetailFragmentDirections.playFromGenre(decision.song.uid)
}
}
findNavController().navigateSafe(directions)
} }
private fun handlePlayFromGenre(song: Song?) {
if (song == null) return
logD("Launching play from genre dialog for $song")
findNavController().navigateSafe(AlbumDetailFragmentDirections.playFromGenre(song.uid))
}
private fun updateMultiToolbar() { private fun updateMultiToolbar() {
val id = val id =
when { when {

View file

@ -69,7 +69,7 @@ class ShowArtistDialog :
detailModel.toShow.consume() detailModel.toShow.consume()
pickerModel.setArtistChoiceUid(args.itemUid) pickerModel.setArtistChoiceUid(args.itemUid)
collectImmediately(pickerModel.artistChoices, ::handleChoices) collectImmediately(pickerModel.artistChoices, ::updateChoices)
} }
override fun onDestroyBinding(binding: DialogMusicChoicesBinding) { override fun onDestroyBinding(binding: DialogMusicChoicesBinding) {
@ -83,7 +83,7 @@ class ShowArtistDialog :
detailModel.showArtist(item) detailModel.showArtist(item)
} }
private fun handleChoices(choices: ArtistShowChoices?) { private fun updateChoices(choices: ArtistShowChoices?) {
if (choices == null) { if (choices == null) {
logD("No choices to show, navigating away") logD("No choices to show, navigating away")
findNavController().navigateUp() findNavController().navigateUp()

View file

@ -33,6 +33,7 @@ import dagger.hilt.android.AndroidEntryPoint
import org.oxycblt.auxio.R import org.oxycblt.auxio.R
import org.oxycblt.auxio.databinding.FragmentPlaybackPanelBinding import org.oxycblt.auxio.databinding.FragmentPlaybackPanelBinding
import org.oxycblt.auxio.detail.DetailViewModel import org.oxycblt.auxio.detail.DetailViewModel
import org.oxycblt.auxio.detail.Show
import org.oxycblt.auxio.music.MusicParent import org.oxycblt.auxio.music.MusicParent
import org.oxycblt.auxio.music.MusicViewModel import org.oxycblt.auxio.music.MusicViewModel
import org.oxycblt.auxio.music.Song import org.oxycblt.auxio.music.Song
@ -40,6 +41,7 @@ import org.oxycblt.auxio.music.resolveNames
import org.oxycblt.auxio.playback.state.RepeatMode import org.oxycblt.auxio.playback.state.RepeatMode
import org.oxycblt.auxio.playback.ui.StyledSeekBar import org.oxycblt.auxio.playback.ui.StyledSeekBar
import org.oxycblt.auxio.ui.ViewBindingFragment import org.oxycblt.auxio.ui.ViewBindingFragment
import org.oxycblt.auxio.util.collect
import org.oxycblt.auxio.util.collectImmediately import org.oxycblt.auxio.util.collectImmediately
import org.oxycblt.auxio.util.logD import org.oxycblt.auxio.util.logD
import org.oxycblt.auxio.util.share import org.oxycblt.auxio.util.share
@ -129,6 +131,7 @@ class PlaybackPanelFragment :
collectImmediately(playbackModel.repeatMode, ::updateRepeat) collectImmediately(playbackModel.repeatMode, ::updateRepeat)
collectImmediately(playbackModel.isPlaying, ::updatePlaying) collectImmediately(playbackModel.isPlaying, ::updatePlaying)
collectImmediately(playbackModel.isShuffled, ::updateShuffled) collectImmediately(playbackModel.isShuffled, ::updateShuffled)
collect(detailModel.toShow.flow, ::handleShow)
} }
override fun onDestroyBinding(binding: FragmentPlaybackPanelBinding) { override fun onDestroyBinding(binding: FragmentPlaybackPanelBinding) {
@ -233,17 +236,25 @@ class PlaybackPanelFragment :
requireBinding().playbackShuffle.isActivated = isShuffled requireBinding().playbackShuffle.isActivated = isShuffled
} }
private fun navigateToCurrentArtist() { private fun handleShow(show: Show?) {
playbackModel.song.value?.let { when (show) {
detailModel.showArtist(it) is Show.ArtistDetails,
playbackModel.openMain() is Show.AlbumDetails -> playbackModel.openMain()
is Show.SongDetails,
is Show.SongAlbumDetails,
is Show.SongArtistDetails,
is Show.AlbumArtistDetails,
is Show.GenreDetails,
is Show.PlaylistDetails,
null -> {}
} }
} }
private fun navigateToCurrentAlbum() { private fun navigateToCurrentArtist() {
playbackModel.song.value?.let { playbackModel.song.value?.let(detailModel::showArtist)
detailModel.showAlbum(it.album)
playbackModel.openMain()
} }
private fun navigateToCurrentAlbum() {
playbackModel.song.value?.let { detailModel.showAlbum(it.album) }
} }
} }

View file

@ -89,31 +89,15 @@ constructor(
val isShuffled: StateFlow<Boolean> val isShuffled: StateFlow<Boolean>
get() = _isShuffled get() = _isShuffled
private val _openPanel = MutableEvent<Panel>() val currentBarAction: ActionMode = playbackSettings.barAction
val openPanel: Event<Panel>
private val _openPanel = MutableEvent<OpenPanel>()
val openPanel: Event<OpenPanel>
get() = _openPanel get() = _openPanel
private val _artistPlaybackPickerSong = MutableEvent<Song>() private val _playbackDecision = MutableEvent<PlaybackDecision>()
/** val playbackDecision: Event<PlaybackDecision>
* Flag signaling to open a picker dialog in order to resolve an ambiguous choice when playing a get() = _playbackDecision
* [Song] from one of it's [Artist]s.
*
* @see playFromArtist
*/
val artistPickerSong: Event<Song>
get() = _artistPlaybackPickerSong
private val _genrePlaybackPickerSong = MutableEvent<Song>()
/**
* Flag signaling to open a picker dialog in order to resolve an ambiguous choice when playing a
* [Song] from one of it's [Genre]s.
*/
val genrePickerSong: Event<Song>
get() = _genrePlaybackPickerSong
/** The current action to show on the playback bar. */
val currentBarAction: ActionMode
get() = playbackSettings.barAction
/** /**
* The current audio session ID of the internal player. Null if no [InternalPlayer] is * The current audio session ID of the internal player. Null if no [InternalPlayer] is
@ -223,7 +207,7 @@ constructor(
playImpl(song, song.artists[0]) playImpl(song, song.artists[0])
} else { } else {
logD("$song has multiple artists, showing choice dialog") logD("$song has multiple artists, showing choice dialog")
_artistPlaybackPickerSong.put(song) startPlaybackDecisionImpl(PlaybackDecision.PlayFromArtist(song))
} }
} }
@ -243,10 +227,19 @@ constructor(
playImpl(song, song.genres[0]) playImpl(song, song.genres[0])
} else { } else {
logD("$song has multiple genres, showing choice dialog") logD("$song has multiple genres, showing choice dialog")
_genrePlaybackPickerSong.put(song) startPlaybackDecisionImpl(PlaybackDecision.PlayFromGenre(song))
} }
} }
private fun startPlaybackDecisionImpl(decision: PlaybackDecision) {
val existing = _playbackDecision.flow.value
if (existing != null) {
logD("Already handling decision $existing, ignoring $decision")
return
}
_playbackDecision.put(decision)
}
/** /**
* PLay a [Song] from one of it's [Playlist]s. * PLay a [Song] from one of it's [Playlist]s.
* *
@ -560,11 +553,11 @@ constructor(
} }
// --- UI CONTROL --- // --- UI CONTROL ---
fun openMain() = openImpl(Panel.Main) fun openMain() = openImpl(OpenPanel.Main)
fun openPlayback() = openImpl(Panel.Playback) fun openPlayback() = openImpl(OpenPanel.Playback)
fun openQueue() = openImpl(Panel.Queue) fun openQueue() = openImpl(OpenPanel.Queue)
private fun openImpl(panel: Panel) { private fun openImpl(panel: OpenPanel) {
val existing = openPanel.flow.value val existing = openPanel.flow.value
if (existing != null) { if (existing != null) {
logD("Already opening $existing, ignoring opening $panel") logD("Already opening $existing, ignoring opening $panel")
@ -617,8 +610,15 @@ constructor(
} }
} }
sealed interface Panel { sealed interface OpenPanel {
object Main : Panel object Main : OpenPanel
object Playback : Panel object Playback : OpenPanel
object Queue : Panel object Queue : OpenPanel
}
sealed interface PlaybackDecision {
val song: Song
class PlayFromArtist(override val song: Song) : PlaybackDecision
class PlayFromGenre(override val song: Song) : PlaybackDecision
} }

View file

@ -32,6 +32,7 @@ import org.oxycblt.auxio.databinding.DialogMusicChoicesBinding
import org.oxycblt.auxio.list.ClickableListListener import org.oxycblt.auxio.list.ClickableListListener
import org.oxycblt.auxio.list.adapter.UpdateInstructions import org.oxycblt.auxio.list.adapter.UpdateInstructions
import org.oxycblt.auxio.music.Artist import org.oxycblt.auxio.music.Artist
import org.oxycblt.auxio.music.Song
import org.oxycblt.auxio.playback.PlaybackViewModel import org.oxycblt.auxio.playback.PlaybackViewModel
import org.oxycblt.auxio.ui.ViewBindingMaterialDialogFragment import org.oxycblt.auxio.ui.ViewBindingMaterialDialogFragment
import org.oxycblt.auxio.util.collectImmediately import org.oxycblt.auxio.util.collectImmediately
@ -68,15 +69,9 @@ class PlayFromArtistDialog :
adapter = choiceAdapter adapter = choiceAdapter
} }
playbackModel.playbackDecision.consume()
pickerModel.setPickerSongUid(args.artistUid) pickerModel.setPickerSongUid(args.artistUid)
collectImmediately(pickerModel.currentPickerSong) { collectImmediately(pickerModel.currentPickerSong, ::updateSong)
if (it != null) {
choiceAdapter.update(it.artists, UpdateInstructions.Replace(0))
} else {
logD("No song to show choices for, navigating away")
findNavController().navigateUp()
}
}
} }
override fun onDestroyBinding(binding: DialogMusicChoicesBinding) { override fun onDestroyBinding(binding: DialogMusicChoicesBinding) {
@ -90,4 +85,13 @@ class PlayFromArtistDialog :
playbackModel.playFromArtist(song, item) playbackModel.playFromArtist(song, item)
findNavController().navigateUp() findNavController().navigateUp()
} }
private fun updateSong(song: Song?) {
if (song == null) {
logD("No song to show choices for, navigating away")
findNavController().navigateUp()
return
}
choiceAdapter.update(song.artists, UpdateInstructions.Replace(0))
}
} }

View file

@ -32,6 +32,7 @@ import org.oxycblt.auxio.databinding.DialogMusicChoicesBinding
import org.oxycblt.auxio.list.ClickableListListener import org.oxycblt.auxio.list.ClickableListListener
import org.oxycblt.auxio.list.adapter.UpdateInstructions import org.oxycblt.auxio.list.adapter.UpdateInstructions
import org.oxycblt.auxio.music.Genre import org.oxycblt.auxio.music.Genre
import org.oxycblt.auxio.music.Song
import org.oxycblt.auxio.playback.PlaybackViewModel import org.oxycblt.auxio.playback.PlaybackViewModel
import org.oxycblt.auxio.ui.ViewBindingMaterialDialogFragment import org.oxycblt.auxio.ui.ViewBindingMaterialDialogFragment
import org.oxycblt.auxio.util.collectImmediately import org.oxycblt.auxio.util.collectImmediately
@ -68,15 +69,9 @@ class PlayFromGenreDialog :
adapter = choiceAdapter adapter = choiceAdapter
} }
playbackModel.playbackDecision.consume()
pickerModel.setPickerSongUid(args.genreUid) pickerModel.setPickerSongUid(args.genreUid)
collectImmediately(pickerModel.currentPickerSong) { collectImmediately(pickerModel.currentPickerSong, ::updateSong)
if (it != null) {
choiceAdapter.update(it.genres, UpdateInstructions.Replace(0))
} else {
logD("No song to show choices for, navigating away")
findNavController().navigateUp()
}
}
} }
override fun onDestroyBinding(binding: DialogMusicChoicesBinding) { override fun onDestroyBinding(binding: DialogMusicChoicesBinding) {
@ -90,4 +85,13 @@ class PlayFromGenreDialog :
playbackModel.playFromGenre(song, item) playbackModel.playFromGenre(song, item)
findNavController().navigateUp() findNavController().navigateUp()
} }
private fun updateSong(song: Song?) {
if (song == null) {
logD("No song to show choices for, navigating away")
findNavController().navigateUp()
return
}
choiceAdapter.update(song.genres, UpdateInstructions.Replace(0))
}
} }

View file

@ -52,6 +52,7 @@ import org.oxycblt.auxio.music.MusicViewModel
import org.oxycblt.auxio.music.Playlist import org.oxycblt.auxio.music.Playlist
import org.oxycblt.auxio.music.PlaylistDecision import org.oxycblt.auxio.music.PlaylistDecision
import org.oxycblt.auxio.music.Song import org.oxycblt.auxio.music.Song
import org.oxycblt.auxio.playback.PlaybackDecision
import org.oxycblt.auxio.playback.PlaybackViewModel import org.oxycblt.auxio.playback.PlaybackViewModel
import org.oxycblt.auxio.util.collect import org.oxycblt.auxio.util.collect
import org.oxycblt.auxio.util.collectImmediately import org.oxycblt.auxio.util.collectImmediately
@ -145,6 +146,7 @@ class SearchFragment : ListFragment<Music, FragmentSearchBinding>() {
collect(musicModel.playlistDecision.flow, ::handleDecision) collect(musicModel.playlistDecision.flow, ::handleDecision)
collectImmediately( collectImmediately(
playbackModel.song, playbackModel.parent, playbackModel.isPlaying, ::updatePlayback) playbackModel.song, playbackModel.parent, playbackModel.isPlaying, ::updatePlayback)
collect(playbackModel.playbackDecision.flow, ::handlePlaybackDecision)
collect(detailModel.toShow.flow, ::handleShow) collect(detailModel.toShow.flow, ::handleShow)
} }
@ -260,34 +262,6 @@ class SearchFragment : ListFragment<Music, FragmentSearchBinding>() {
hideKeyboard() hideKeyboard()
} }
private fun handleDecision(decision: PlaylistDecision?) {
if (decision == null) return
val directions =
when (decision) {
is PlaylistDecision.Rename -> {
logD("Renaming ${decision.playlist}")
SearchFragmentDirections.renamePlaylist(decision.playlist.uid)
}
is PlaylistDecision.Delete -> {
logD("Deleting ${decision.playlist}")
SearchFragmentDirections.deletePlaylist(decision.playlist.uid)
}
is PlaylistDecision.Add -> {
logD("Adding ${decision.songs.size} to a playlist")
SearchFragmentDirections.addToPlaylist(
decision.songs.map { it.uid }.toTypedArray())
}
is PlaylistDecision.New -> {
error("Unexpected decision $decision")
}
}
findNavController().navigateSafe(directions)
}
private fun updatePlayback(song: Song?, parent: MusicParent?, isPlaying: Boolean) {
searchAdapter.setPlaying(parent ?: song, isPlaying)
}
private fun handleMenu(pendingMenu: PendingMenu?) { private fun handleMenu(pendingMenu: PendingMenu?) {
if (pendingMenu == null) return if (pendingMenu == null) return
val directions = val directions =
@ -326,6 +300,50 @@ class SearchFragment : ListFragment<Music, FragmentSearchBinding>() {
} }
} }
private fun handleDecision(decision: PlaylistDecision?) {
if (decision == null) return
val directions =
when (decision) {
is PlaylistDecision.Rename -> {
logD("Renaming ${decision.playlist}")
SearchFragmentDirections.renamePlaylist(decision.playlist.uid)
}
is PlaylistDecision.Delete -> {
logD("Deleting ${decision.playlist}")
SearchFragmentDirections.deletePlaylist(decision.playlist.uid)
}
is PlaylistDecision.Add -> {
logD("Adding ${decision.songs.size} to a playlist")
SearchFragmentDirections.addToPlaylist(
decision.songs.map { it.uid }.toTypedArray())
}
is PlaylistDecision.New -> {
error("Unexpected decision $decision")
}
}
findNavController().navigateSafe(directions)
}
private fun updatePlayback(song: Song?, parent: MusicParent?, isPlaying: Boolean) {
searchAdapter.setPlaying(parent ?: song, isPlaying)
}
private fun handlePlaybackDecision(decision: PlaybackDecision?) {
if (decision == null) return
val directions =
when (decision) {
is PlaybackDecision.PlayFromArtist -> {
logD("Launching play from artist dialog for $decision")
SearchFragmentDirections.playFromArtist(decision.song.uid)
}
is PlaybackDecision.PlayFromGenre -> {
logD("Launching play from artist dialog for $decision")
SearchFragmentDirections.playFromGenre(decision.song.uid)
}
}
findNavController().navigateSafe(directions)
}
/** /**
* Safely focus the keyboard on a particular [View]. * Safely focus the keyboard on a particular [View].
* *