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:
parent
a6a3eceb7b
commit
9e5b737d1a
11 changed files with 197 additions and 148 deletions
|
@ -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()
|
||||||
}
|
}
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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].
|
||||||
*
|
*
|
||||||
|
|
Loading…
Reference in a new issue