all: use menu everywhere

Use the new menu system in all applicable places.

More consideration is needed right now on whether the toolbars should
also have menu items, so they remain unchanged right now.
This commit is contained in:
Alexander Capehart 2023-07-03 16:52:42 -06:00
parent 0d896c04e3
commit d6a20fedb3
No known key found for this signature in database
GPG key ID: 37DBE3621FE9AD47
16 changed files with 325 additions and 78 deletions

View file

@ -40,6 +40,8 @@ import org.oxycblt.auxio.list.Header
import org.oxycblt.auxio.list.Item
import org.oxycblt.auxio.list.ListFragment
import org.oxycblt.auxio.list.Sort
import org.oxycblt.auxio.list.menu.MenuViewModel
import org.oxycblt.auxio.list.menu.PendingMenu
import org.oxycblt.auxio.list.selection.SelectionViewModel
import org.oxycblt.auxio.music.Album
import org.oxycblt.auxio.music.Music
@ -72,6 +74,7 @@ class AlbumDetailFragment :
AlbumDetailHeaderAdapter.Listener,
DetailListAdapter.Listener<Song> {
override val detailModel: DetailViewModel by activityViewModels()
private val menuModel: MenuViewModel by activityViewModels()
override val selectionModel: SelectionViewModel by activityViewModels()
override val musicModel: MusicViewModel by activityViewModels()
override val playbackModel: PlaybackViewModel by activityViewModels()
@ -124,6 +127,7 @@ class AlbumDetailFragment :
collectImmediately(detailModel.currentAlbum, ::updateAlbum)
collectImmediately(detailModel.albumList, ::updateList)
collect(detailModel.toShow.flow, ::handleShow)
collect(menuModel.pendingMenu.flow, ::handleMenu)
collectImmediately(selectionModel.selected, ::updateSelection)
collect(musicModel.playlistDecision.flow, ::handleDecision)
collectImmediately(
@ -183,7 +187,7 @@ class AlbumDetailFragment :
}
override fun onOpenMenu(item: Song, anchor: View) {
openMusicMenu(anchor, R.menu.item_album_song, item)
menuModel.openMenu(R.menu.item_album_song, item)
}
override fun onPlay() {
@ -300,6 +304,22 @@ class AlbumDetailFragment :
}
}
private fun handleMenu(pendingMenu: PendingMenu?) {
if (pendingMenu == null) return
val directions =
when (pendingMenu) {
is PendingMenu.ForSong ->
AlbumDetailFragmentDirections.openSongMenu(
pendingMenu.menuRes, pendingMenu.music.uid)
is PendingMenu.ForAlbum,
is PendingMenu.ForArtist,
is PendingMenu.ForGenre,
is PendingMenu.ForPlaylist -> error("Unexpected menu $pendingMenu")
}
findNavController().navigateSafe(directions)
menuModel.pendingMenu.consume()
}
private fun updateSelection(selected: List<Music>) {
albumListAdapter.setSelected(selected.toSet())

View file

@ -40,6 +40,8 @@ import org.oxycblt.auxio.list.Header
import org.oxycblt.auxio.list.Item
import org.oxycblt.auxio.list.ListFragment
import org.oxycblt.auxio.list.Sort
import org.oxycblt.auxio.list.menu.MenuViewModel
import org.oxycblt.auxio.list.menu.PendingMenu
import org.oxycblt.auxio.list.selection.SelectionViewModel
import org.oxycblt.auxio.music.Album
import org.oxycblt.auxio.music.Artist
@ -70,6 +72,7 @@ class ArtistDetailFragment :
DetailHeaderAdapter.Listener,
DetailListAdapter.Listener<Music> {
override val detailModel: DetailViewModel by activityViewModels()
private val menuModel: MenuViewModel by activityViewModels()
override val selectionModel: SelectionViewModel by activityViewModels()
override val musicModel: MusicViewModel by activityViewModels()
override val playbackModel: PlaybackViewModel by activityViewModels()
@ -125,6 +128,7 @@ class ArtistDetailFragment :
collectImmediately(detailModel.currentArtist, ::updateArtist)
collectImmediately(detailModel.artistList, ::updateList)
collect(detailModel.toShow.flow, ::handleShow)
collect(menuModel.pendingMenu.flow, ::handleMenu)
collectImmediately(selectionModel.selected, ::updateSelection)
collect(musicModel.playlistDecision.flow, ::handleDecision)
collectImmediately(
@ -194,8 +198,8 @@ class ArtistDetailFragment :
override fun onOpenMenu(item: Music, anchor: View) {
when (item) {
is Song -> openMusicMenu(anchor, R.menu.item_artist_song, item)
is Album -> openMusicMenu(anchor, R.menu.item_artist_album, item)
is Song -> menuModel.openMenu(R.menu.item_artist_song, item)
is Album -> menuModel.openMenu(R.menu.item_artist_album, item)
else -> error("Unexpected datatype: ${item::class.simpleName}")
}
}
@ -310,6 +314,24 @@ class ArtistDetailFragment :
}
}
private fun handleMenu(pendingMenu: PendingMenu?) {
if (pendingMenu == null) return
val directions =
when (pendingMenu) {
is PendingMenu.ForSong ->
ArtistDetailFragmentDirections.openSongMenu(
pendingMenu.menuRes, pendingMenu.music.uid)
is PendingMenu.ForAlbum ->
ArtistDetailFragmentDirections.openAlbumMenu(
pendingMenu.menuRes, pendingMenu.music.uid)
is PendingMenu.ForArtist,
is PendingMenu.ForGenre,
is PendingMenu.ForPlaylist -> error("Unexpected menu $pendingMenu")
}
findNavController().navigateSafe(directions)
menuModel.pendingMenu.consume()
}
private fun updateSelection(selected: List<Music>) {
artistListAdapter.setSelected(selected.toSet())

View file

@ -40,6 +40,8 @@ import org.oxycblt.auxio.list.Header
import org.oxycblt.auxio.list.Item
import org.oxycblt.auxio.list.ListFragment
import org.oxycblt.auxio.list.Sort
import org.oxycblt.auxio.list.menu.MenuViewModel
import org.oxycblt.auxio.list.menu.PendingMenu
import org.oxycblt.auxio.list.selection.SelectionViewModel
import org.oxycblt.auxio.music.Artist
import org.oxycblt.auxio.music.Genre
@ -70,6 +72,7 @@ class GenreDetailFragment :
DetailHeaderAdapter.Listener,
DetailListAdapter.Listener<Music> {
override val detailModel: DetailViewModel by activityViewModels()
private val menuModel: MenuViewModel by activityViewModels()
override val selectionModel: SelectionViewModel by activityViewModels()
override val musicModel: MusicViewModel by activityViewModels()
override val playbackModel: PlaybackViewModel by activityViewModels()
@ -123,6 +126,7 @@ class GenreDetailFragment :
collectImmediately(detailModel.currentGenre, ::updatePlaylist)
collectImmediately(detailModel.genreList, ::updateList)
collect(detailModel.toShow.flow, ::handleShow)
collect(menuModel.pendingMenu.flow, ::handleMenu)
collectImmediately(selectionModel.selected, ::updateSelection)
collect(musicModel.playlistDecision.flow, ::handleDecision)
collectImmediately(
@ -192,8 +196,8 @@ class GenreDetailFragment :
override fun onOpenMenu(item: Music, anchor: View) {
when (item) {
is Artist -> openMusicMenu(anchor, R.menu.item_parent, item)
is Song -> openMusicMenu(anchor, R.menu.item_song, item)
is Artist -> menuModel.openMenu(R.menu.item_parent, item)
is Song -> menuModel.openMenu(R.menu.item_song, item)
else -> error("Unexpected datatype: ${item::class.simpleName}")
}
}
@ -298,6 +302,24 @@ class GenreDetailFragment :
}
}
private fun handleMenu(pendingMenu: PendingMenu?) {
if (pendingMenu == null) return
val directions =
when (pendingMenu) {
is PendingMenu.ForSong ->
GenreDetailFragmentDirections.openSongMenu(
pendingMenu.menuRes, pendingMenu.music.uid)
is PendingMenu.ForArtist ->
GenreDetailFragmentDirections.openArtistMenu(
pendingMenu.menuRes, pendingMenu.music.uid)
is PendingMenu.ForAlbum,
is PendingMenu.ForGenre,
is PendingMenu.ForPlaylist -> error("Unexpected menu $pendingMenu")
}
findNavController().navigateSafe(directions)
menuModel.pendingMenu.consume()
}
private fun updateSelection(selected: List<Music>) {
genreListAdapter.setSelected(selected.toSet())

View file

@ -43,6 +43,8 @@ import org.oxycblt.auxio.list.Divider
import org.oxycblt.auxio.list.Header
import org.oxycblt.auxio.list.Item
import org.oxycblt.auxio.list.ListFragment
import org.oxycblt.auxio.list.menu.MenuViewModel
import org.oxycblt.auxio.list.menu.PendingMenu
import org.oxycblt.auxio.list.selection.SelectionViewModel
import org.oxycblt.auxio.music.Music
import org.oxycblt.auxio.music.MusicParent
@ -73,6 +75,7 @@ class PlaylistDetailFragment :
PlaylistDetailListAdapter.Listener,
NavController.OnDestinationChangedListener {
override val detailModel: DetailViewModel by activityViewModels()
private val menuModel: MenuViewModel by activityViewModels()
override val selectionModel: SelectionViewModel by activityViewModels()
override val musicModel: MusicViewModel by activityViewModels()
override val playbackModel: PlaybackViewModel by activityViewModels()
@ -138,6 +141,7 @@ class PlaylistDetailFragment :
collectImmediately(detailModel.playlistList, ::updateList)
collectImmediately(detailModel.editedPlaylist, ::updateEditedList)
collect(detailModel.toShow.flow, ::handleShow)
collect(menuModel.pendingMenu.flow, ::handleMenu)
collectImmediately(selectionModel.selected, ::updateSelection)
collect(musicModel.playlistDecision.flow, ::handleDecision)
collectImmediately(
@ -235,7 +239,7 @@ class PlaylistDetailFragment :
}
override fun onOpenMenu(item: Song, anchor: View) {
openMusicMenu(anchor, R.menu.item_playlist_song, item)
menuModel.openMenu(R.menu.item_playlist_song, item)
}
override fun onPlay() {
@ -345,6 +349,22 @@ class PlaylistDetailFragment :
}
}
private fun handleMenu(pendingMenu: PendingMenu?) {
if (pendingMenu == null) return
val directions =
when (pendingMenu) {
is PendingMenu.ForSong ->
PlaylistDetailFragmentDirections.openSongMenu(
pendingMenu.menuRes, pendingMenu.music.uid)
is PendingMenu.ForArtist,
is PendingMenu.ForAlbum,
is PendingMenu.ForGenre,
is PendingMenu.ForPlaylist -> error("Unexpected menu $pendingMenu")
}
findNavController().navigateSafe(directions)
menuModel.pendingMenu.consume()
}
private fun updateSelection(selected: List<Music>) {
playlistListAdapter.setSelected(selected.toSet())

View file

@ -56,6 +56,8 @@ import org.oxycblt.auxio.home.list.SongListFragment
import org.oxycblt.auxio.home.tabs.AdaptiveTabStrategy
import org.oxycblt.auxio.home.tabs.Tab
import org.oxycblt.auxio.list.Sort
import org.oxycblt.auxio.list.menu.MenuViewModel
import org.oxycblt.auxio.list.menu.PendingMenu
import org.oxycblt.auxio.list.selection.SelectionFragment
import org.oxycblt.auxio.list.selection.SelectionViewModel
import org.oxycblt.auxio.music.IndexingProgress
@ -87,6 +89,7 @@ import org.oxycblt.auxio.util.unlikelyToBeNull
@AndroidEntryPoint
class HomeFragment :
SelectionFragment<FragmentHomeBinding>(), AppBarLayout.OnOffsetChangedListener {
private val menuModel: MenuViewModel by activityViewModels()
override val selectionModel: SelectionViewModel by activityViewModels()
override val musicModel: MusicViewModel by activityViewModels()
override val playbackModel: PlaybackViewModel by activityViewModels()
@ -175,6 +178,7 @@ class HomeFragment :
collect(homeModel.recreateTabs.flow, ::handleRecreate)
collectImmediately(homeModel.currentTabMode, ::updateCurrentTab)
collectImmediately(homeModel.songsList, homeModel.isFastScrolling, ::updateFab)
collect(menuModel.pendingMenu.flow, ::handleMenu)
collectImmediately(selectionModel.selected, ::updateSelection)
collectImmediately(musicModel.indexingState, ::updateIndexerState)
collect(musicModel.playlistDecision.flow, ::handleDecision)
@ -584,6 +588,27 @@ class HomeFragment :
}
}
private fun handleMenu(pendingMenu: PendingMenu?) {
if (pendingMenu == null) return
val directions =
when (pendingMenu) {
is PendingMenu.ForSong ->
HomeFragmentDirections.openSongMenu(pendingMenu.menuRes, pendingMenu.music.uid)
is PendingMenu.ForAlbum ->
HomeFragmentDirections.openAlbumMenu(pendingMenu.menuRes, pendingMenu.music.uid)
is PendingMenu.ForArtist ->
HomeFragmentDirections.openArtistMenu(
pendingMenu.menuRes, pendingMenu.music.uid)
is PendingMenu.ForGenre ->
HomeFragmentDirections.openGenreMenu(pendingMenu.menuRes, pendingMenu.music.uid)
is PendingMenu.ForPlaylist ->
HomeFragmentDirections.openPlaylistMenu(
pendingMenu.menuRes, pendingMenu.music.uid)
}
findNavController().navigateSafe(directions)
menuModel.pendingMenu.consume()
}
private fun updateSelection(selected: List<Music>) {
val binding = requireBinding()
if (selected.isNotEmpty()) {

View file

@ -35,6 +35,7 @@ import org.oxycblt.auxio.list.ListFragment
import org.oxycblt.auxio.list.SelectableListListener
import org.oxycblt.auxio.list.Sort
import org.oxycblt.auxio.list.adapter.SelectionIndicatorAdapter
import org.oxycblt.auxio.list.menu.MenuViewModel
import org.oxycblt.auxio.list.recycler.AlbumViewHolder
import org.oxycblt.auxio.list.selection.SelectionViewModel
import org.oxycblt.auxio.music.Album
@ -60,9 +61,10 @@ class AlbumListFragment :
FastScrollRecyclerView.PopupProvider {
private val homeModel: HomeViewModel by activityViewModels()
override val detailModel: DetailViewModel by activityViewModels()
override val playbackModel: PlaybackViewModel by activityViewModels()
override val musicModel: MusicViewModel by activityViewModels()
private val menuModel: MenuViewModel by activityViewModels()
override val selectionModel: SelectionViewModel by activityViewModels()
override val musicModel: MusicViewModel by activityViewModels()
override val playbackModel: PlaybackViewModel by activityViewModels()
private val albumAdapter = AlbumAdapter(this)
// Save memory by re-using the same formatter and string builder when creating popup text
private val formatterSb = StringBuilder(64)

View file

@ -33,6 +33,7 @@ import org.oxycblt.auxio.list.ListFragment
import org.oxycblt.auxio.list.SelectableListListener
import org.oxycblt.auxio.list.Sort
import org.oxycblt.auxio.list.adapter.SelectionIndicatorAdapter
import org.oxycblt.auxio.list.menu.MenuViewModel
import org.oxycblt.auxio.list.recycler.ArtistViewHolder
import org.oxycblt.auxio.list.selection.SelectionViewModel
import org.oxycblt.auxio.music.Artist
@ -58,9 +59,10 @@ class ArtistListFragment :
FastScrollRecyclerView.Listener {
private val homeModel: HomeViewModel by activityViewModels()
override val detailModel: DetailViewModel by activityViewModels()
override val playbackModel: PlaybackViewModel by activityViewModels()
override val musicModel: MusicViewModel by activityViewModels()
private val menuModel: MenuViewModel by activityViewModels()
override val selectionModel: SelectionViewModel by activityViewModels()
override val musicModel: MusicViewModel by activityViewModels()
override val playbackModel: PlaybackViewModel by activityViewModels()
private val artistAdapter = ArtistAdapter(this)
override fun onCreateBinding(inflater: LayoutInflater) =
@ -118,7 +120,7 @@ class ArtistListFragment :
}
override fun onOpenMenu(item: Artist, anchor: View) {
openMusicMenu(anchor, R.menu.item_parent, item)
menuModel.openMenu(R.menu.item_parent, item)
}
private fun updateArtists(artists: List<Artist>) {

View file

@ -33,6 +33,7 @@ import org.oxycblt.auxio.list.ListFragment
import org.oxycblt.auxio.list.SelectableListListener
import org.oxycblt.auxio.list.Sort
import org.oxycblt.auxio.list.adapter.SelectionIndicatorAdapter
import org.oxycblt.auxio.list.menu.MenuViewModel
import org.oxycblt.auxio.list.recycler.GenreViewHolder
import org.oxycblt.auxio.list.selection.SelectionViewModel
import org.oxycblt.auxio.music.Genre
@ -57,9 +58,10 @@ class GenreListFragment :
FastScrollRecyclerView.Listener {
private val homeModel: HomeViewModel by activityViewModels()
override val detailModel: DetailViewModel by activityViewModels()
override val playbackModel: PlaybackViewModel by activityViewModels()
override val musicModel: MusicViewModel by activityViewModels()
private val menuModel: MenuViewModel by activityViewModels()
override val selectionModel: SelectionViewModel by activityViewModels()
override val musicModel: MusicViewModel by activityViewModels()
override val playbackModel: PlaybackViewModel by activityViewModels()
private val genreAdapter = GenreAdapter(this)
override fun onCreateBinding(inflater: LayoutInflater) =
@ -117,7 +119,7 @@ class GenreListFragment :
}
override fun onOpenMenu(item: Genre, anchor: View) {
openMusicMenu(anchor, R.menu.item_parent, item)
menuModel.openMenu(R.menu.item_parent, item)
}
private fun updateGenres(genres: List<Genre>) {

View file

@ -32,6 +32,7 @@ import org.oxycblt.auxio.list.ListFragment
import org.oxycblt.auxio.list.SelectableListListener
import org.oxycblt.auxio.list.Sort
import org.oxycblt.auxio.list.adapter.SelectionIndicatorAdapter
import org.oxycblt.auxio.list.menu.MenuViewModel
import org.oxycblt.auxio.list.recycler.PlaylistViewHolder
import org.oxycblt.auxio.list.selection.SelectionViewModel
import org.oxycblt.auxio.music.Music
@ -55,9 +56,10 @@ class PlaylistListFragment :
FastScrollRecyclerView.Listener {
private val homeModel: HomeViewModel by activityViewModels()
override val detailModel: DetailViewModel by activityViewModels()
override val playbackModel: PlaybackViewModel by activityViewModels()
override val musicModel: MusicViewModel by activityViewModels()
private val menuModel: MenuViewModel by activityViewModels()
override val selectionModel: SelectionViewModel by activityViewModels()
override val musicModel: MusicViewModel by activityViewModels()
override val playbackModel: PlaybackViewModel by activityViewModels()
private val playlistAdapter = PlaylistAdapter(this)
override fun onCreateBinding(inflater: LayoutInflater) =
@ -115,7 +117,7 @@ class PlaylistListFragment :
}
override fun onOpenMenu(item: Playlist, anchor: View) {
openMusicMenu(anchor, R.menu.item_playlist, item)
menuModel.openMenu(R.menu.item_playlist, item)
}
private fun updatePlaylists(playlists: List<Playlist>) {

View file

@ -35,6 +35,7 @@ import org.oxycblt.auxio.list.ListFragment
import org.oxycblt.auxio.list.SelectableListListener
import org.oxycblt.auxio.list.Sort
import org.oxycblt.auxio.list.adapter.SelectionIndicatorAdapter
import org.oxycblt.auxio.list.menu.MenuViewModel
import org.oxycblt.auxio.list.recycler.SongViewHolder
import org.oxycblt.auxio.list.selection.SelectionViewModel
import org.oxycblt.auxio.music.Music
@ -59,9 +60,10 @@ class SongListFragment :
FastScrollRecyclerView.Listener {
private val homeModel: HomeViewModel by activityViewModels()
override val detailModel: DetailViewModel by activityViewModels()
override val playbackModel: PlaybackViewModel by activityViewModels()
override val musicModel: MusicViewModel by activityViewModels()
private val menuModel: MenuViewModel by activityViewModels()
override val selectionModel: SelectionViewModel by activityViewModels()
override val musicModel: MusicViewModel by activityViewModels()
override val playbackModel: PlaybackViewModel by activityViewModels()
private val songAdapter = SongAdapter(this)
// Save memory by re-using the same formatter and string builder when creating popup text
private val formatterSb = StringBuilder(64)
@ -143,7 +145,7 @@ class SongListFragment :
}
override fun onOpenMenu(item: Song, anchor: View) {
openMusicMenu(anchor, R.menu.item_song, item)
menuModel.openMenu(R.menu.item_song, item)
}
private fun updateSongs(songs: List<Song>) {

View file

@ -71,7 +71,7 @@ abstract class MenuDialogFragment<T : Music> :
menuAdapter.update(builder.children.toList(), UpdateInstructions.Diff)
// --- VIEWMODEL SETUP ---
menuModel.setMusic(uid)
menuModel.setCurrentMenu(uid)
collectImmediately(menuModel.currentMusic, this::updateMusic)
}

View file

@ -18,7 +18,7 @@
package org.oxycblt.auxio.list.menu
import androidx.fragment.app.viewModels
import androidx.fragment.app.activityViewModels
import androidx.navigation.fragment.navArgs
import dagger.hilt.android.AndroidEntryPoint
import org.oxycblt.auxio.R
@ -34,7 +34,7 @@ import org.oxycblt.auxio.util.getPlural
@AndroidEntryPoint
class SongMenuDialogFragment : MenuDialogFragment<Song>() {
override val menuModel: MenuViewModel by viewModels()
override val menuModel: MenuViewModel by activityViewModels()
private val args: SongMenuDialogFragmentArgs by navArgs()
override val menuRes: Int
@ -55,7 +55,7 @@ class SongMenuDialogFragment : MenuDialogFragment<Song>() {
@AndroidEntryPoint
class AlbumMenuDialogFragment : MenuDialogFragment<Album>() {
override val menuModel: MenuViewModel by viewModels()
override val menuModel: MenuViewModel by activityViewModels()
private val args: AlbumMenuDialogFragmentArgs by navArgs()
override val menuRes: Int
@ -76,7 +76,7 @@ class AlbumMenuDialogFragment : MenuDialogFragment<Album>() {
@AndroidEntryPoint
class ArtistMenuDialogFragment : MenuDialogFragment<Artist>() {
override val menuModel: MenuViewModel by viewModels()
override val menuModel: MenuViewModel by activityViewModels()
private val args: ArtistMenuDialogFragmentArgs by navArgs()
override val menuRes: Int
@ -105,7 +105,7 @@ class ArtistMenuDialogFragment : MenuDialogFragment<Artist>() {
@AndroidEntryPoint
class GenreMenuDialogFragment : MenuDialogFragment<Genre>() {
override val menuModel: MenuViewModel by viewModels()
override val menuModel: MenuViewModel by activityViewModels()
private val args: GenreMenuDialogFragmentArgs by navArgs()
override val menuRes: Int
@ -130,7 +130,7 @@ class GenreMenuDialogFragment : MenuDialogFragment<Genre>() {
@AndroidEntryPoint
class PlaylistMenuDialogFragment : MenuDialogFragment<Playlist>() {
override val menuModel: MenuViewModel by viewModels()
override val menuModel: MenuViewModel by activityViewModels()
private val args: PlaylistMenuDialogFragmentArgs by navArgs()
override val menuRes: Int

View file

@ -18,18 +18,29 @@
package org.oxycblt.auxio.list.menu
import androidx.annotation.MenuRes
import androidx.lifecycle.ViewModel
import dagger.hilt.android.lifecycle.HiltViewModel
import javax.inject.Inject
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import org.oxycblt.auxio.music.Album
import org.oxycblt.auxio.music.Artist
import org.oxycblt.auxio.music.Genre
import org.oxycblt.auxio.music.Music
import org.oxycblt.auxio.music.MusicRepository
import org.oxycblt.auxio.music.Playlist
import org.oxycblt.auxio.music.Song
import org.oxycblt.auxio.util.Event
import org.oxycblt.auxio.util.MutableEvent
import org.oxycblt.auxio.util.logW
@HiltViewModel
class MenuViewModel @Inject constructor(private val musicRepository: MusicRepository) :
ViewModel(), MusicRepository.UpdateListener {
private val _pendingMenu = MutableEvent<PendingMenu>()
val pendingMenu: Event<PendingMenu> = _pendingMenu
private val _currentMusic = MutableStateFlow<Music?>(null)
val currentMusic: StateFlow<Music?> = _currentMusic
@ -45,10 +56,46 @@ class MenuViewModel @Inject constructor(private val musicRepository: MusicReposi
musicRepository.removeUpdateListener(this)
}
fun setMusic(uid: Music.UID) {
fun openMenu(@MenuRes menuRes: Int, song: Song) =
openMenuImpl(PendingMenu.ForSong(menuRes, song))
fun openMenu(@MenuRes menuRes: Int, album: Album) =
openMenuImpl(PendingMenu.ForAlbum(menuRes, album))
fun openMenu(@MenuRes menuRes: Int, artist: Artist) =
openMenuImpl(PendingMenu.ForArtist(menuRes, artist))
fun openMenu(@MenuRes menuRes: Int, genre: Genre) =
openMenuImpl(PendingMenu.ForGenre(menuRes, genre))
fun openMenu(@MenuRes menuRes: Int, playlist: Playlist) =
openMenuImpl(PendingMenu.ForPlaylist(menuRes, playlist))
private fun openMenuImpl(pendingMenu: PendingMenu) {
val existing = _pendingMenu.flow.value
if (existing != null) {
logW("Already opening $existing, ignoring $pendingMenu")
return
}
_pendingMenu.put(pendingMenu)
}
fun setCurrentMenu(uid: Music.UID) {
_currentMusic.value = musicRepository.find(uid)
if (_currentMusic.value == null) {
logW("Given Music UID to show was invalid")
}
}
}
sealed interface PendingMenu {
val menuRes: Int
val music: Music
class ForSong(@MenuRes override val menuRes: Int, override val music: Song) : PendingMenu
class ForAlbum(@MenuRes override val menuRes: Int, override val music: Album) : PendingMenu
class ForArtist(@MenuRes override val menuRes: Int, override val music: Artist) : PendingMenu
class ForGenre(@MenuRes override val menuRes: Int, override val music: Genre) : PendingMenu
class ForPlaylist(@MenuRes override val menuRes: Int, override val music: Playlist) :
PendingMenu
}

View file

@ -36,11 +36,12 @@ import org.oxycblt.auxio.R
import org.oxycblt.auxio.databinding.FragmentSearchBinding
import org.oxycblt.auxio.detail.DetailViewModel
import org.oxycblt.auxio.detail.Show
import org.oxycblt.auxio.home.HomeFragmentDirections
import org.oxycblt.auxio.list.Divider
import org.oxycblt.auxio.list.Header
import org.oxycblt.auxio.list.Item
import org.oxycblt.auxio.list.ListFragment
import org.oxycblt.auxio.list.menu.MenuViewModel
import org.oxycblt.auxio.list.menu.PendingMenu
import org.oxycblt.auxio.list.selection.SelectionViewModel
import org.oxycblt.auxio.music.Album
import org.oxycblt.auxio.music.Artist
@ -71,9 +72,10 @@ import org.oxycblt.auxio.util.setFullWidthLookup
@AndroidEntryPoint
class SearchFragment : ListFragment<Music, FragmentSearchBinding>() {
override val detailModel: DetailViewModel by activityViewModels()
private val menuModel: MenuViewModel by activityViewModels()
override val selectionModel: SelectionViewModel by activityViewModels()
override val playbackModel: PlaybackViewModel by activityViewModels()
override val musicModel: MusicViewModel by activityViewModels()
override val selectionModel: SelectionViewModel by activityViewModels()
private val searchModel: SearchViewModel by viewModels()
private val searchAdapter = SearchAdapter(this)
private var imm: InputMethodManager? = null
@ -138,6 +140,7 @@ class SearchFragment : ListFragment<Music, FragmentSearchBinding>() {
// --- VIEWMODEL SETUP ---
collectImmediately(searchModel.searchResults, ::updateSearchResults)
collect(menuModel.pendingMenu.flow, ::handleMenu)
collectImmediately(selectionModel.selected, ::updateSelection)
collect(musicModel.playlistDecision.flow, ::handleDecision)
collectImmediately(
@ -181,11 +184,11 @@ class SearchFragment : ListFragment<Music, FragmentSearchBinding>() {
override fun onOpenMenu(item: Music, anchor: View) {
when (item) {
is Song -> openMusicMenu(anchor, R.menu.item_song, item)
is Album -> openMusicMenu(anchor, R.menu.item_album, item)
is Artist -> openMusicMenu(anchor, R.menu.item_parent, item)
is Genre -> openMusicMenu(anchor, R.menu.item_parent, item)
is Playlist -> openMusicMenu(anchor, R.menu.item_playlist, item)
is Song -> menuModel.openMenu(R.menu.item_song, item)
is Album -> menuModel.openMenu(R.menu.item_album, item)
is Artist -> menuModel.openMenu(R.menu.item_parent, item)
is Genre -> menuModel.openMenu(R.menu.item_parent, item)
is Playlist -> menuModel.openMenu(R.menu.item_playlist, item)
}
}
@ -247,7 +250,7 @@ class SearchFragment : ListFragment<Music, FragmentSearchBinding>() {
is Show.PlaylistDetails -> {
logD("Navigating to ${show.playlist}")
findNavController()
.navigateSafe(SearchFragmentDirections.showGenre(show.playlist.uid))
.navigateSafe(SearchFragmentDirections.showPlaylist(show.playlist.uid))
}
null -> {}
}
@ -258,32 +261,26 @@ class SearchFragment : ListFragment<Music, FragmentSearchBinding>() {
private fun handleDecision(decision: PlaylistDecision?) {
if (decision == null) return
when (decision) {
is PlaylistDecision.New -> {
logD("Creating new playlist")
findNavController()
.navigateSafe(
HomeFragmentDirections.newPlaylist(
decision.songs.map { it.uid }.toTypedArray()))
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")
}
}
is PlaylistDecision.Rename -> {
logD("Renaming ${decision.playlist}")
findNavController()
.navigateSafe(HomeFragmentDirections.renamePlaylist(decision.playlist.uid))
}
is PlaylistDecision.Delete -> {
logD("Deleting ${decision.playlist}")
findNavController()
.navigateSafe(SearchFragmentDirections.deletePlaylist(decision.playlist.uid))
}
is PlaylistDecision.Add -> {
logD("Adding ${decision.songs.size} to a playlist")
findNavController()
.navigateSafe(
HomeFragmentDirections.addToPlaylist(
decision.songs.map { it.uid }.toTypedArray()))
}
}
findNavController().navigateSafe(directions)
musicModel.playlistDecision.consume()
}
@ -291,6 +288,30 @@ class SearchFragment : ListFragment<Music, FragmentSearchBinding>() {
searchAdapter.setPlaying(parent ?: song, isPlaying)
}
private fun handleMenu(pendingMenu: PendingMenu?) {
if (pendingMenu == null) return
val directions =
when (pendingMenu) {
is PendingMenu.ForSong ->
SearchFragmentDirections.openSongMenu(
pendingMenu.menuRes, pendingMenu.music.uid)
is PendingMenu.ForAlbum ->
SearchFragmentDirections.openAlbumMenu(
pendingMenu.menuRes, pendingMenu.music.uid)
is PendingMenu.ForArtist ->
SearchFragmentDirections.openArtistMenu(
pendingMenu.menuRes, pendingMenu.music.uid)
is PendingMenu.ForGenre ->
SearchFragmentDirections.openGenreMenu(
pendingMenu.menuRes, pendingMenu.music.uid)
is PendingMenu.ForPlaylist ->
SearchFragmentDirections.openPlaylistMenu(
pendingMenu.menuRes, pendingMenu.music.uid)
}
findNavController().navigateSafe(directions)
menuModel.pendingMenu.consume()
}
private fun updateSelection(selected: List<Music>) {
searchAdapter.setSelected(selected.toSet())
val binding = requireBinding()

View file

@ -2,26 +2,28 @@
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
style="@style/Widget.Auxio.RecyclerView.Linear"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<org.oxycblt.auxio.image.CoverView
android:id="@+id/menu_cover"
style="@style/Widget.Auxio.Image.MidLarge"
android:layout_margin="@dimen/spacing_medium"
style="@style/Widget.Auxio.Image.Full"
android:layout_marginVertical="@dimen/spacing_medium"
android:layout_marginStart="@dimen/spacing_medium"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/menu_type"
android:layout_width="wrap_content"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:textAppearance="@style/TextAppearance.Auxio.LabelLarge"
android:layout_marginHorizontal="@dimen/spacing_medium"
android:layout_marginTop="@dimen/spacing_small"
android:textAppearance="@style/TextAppearance.Auxio.LabelMedium"
android:textColor="?attr/colorSecondary"
android:layout_marginStart="@dimen/spacing_medium"
app:layout_constraintBottom_toTopOf="@+id/menu_name"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/menu_cover"
app:layout_constraintTop_toTopOf="@+id/menu_cover"
app:layout_constraintVertical_chainStyle="packed"
@ -29,32 +31,42 @@
<TextView
android:id="@+id/menu_name"
android:layout_width="wrap_content"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:textAppearance="@style/TextAppearance.Auxio.TitleLarge"
android:textColor="?android:attr/textColorPrimary"
style="@style/Widget.Auxio.TextView.Primary"
app:layout_constraintBottom_toTopOf="@+id/menu_info"
app:layout_constraintEnd_toEndOf="@+id/menu_type"
app:layout_constraintStart_toStartOf="@+id/menu_type"
app:layout_constraintTop_toBottomOf="@+id/menu_type"
tools:text="Name" />
<TextView
android:id="@+id/menu_info"
android:layout_width="wrap_content"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:textAppearance="@style/TextAppearance.Auxio.TitleMedium"
style="@style/Widget.Auxio.TextView.Secondary.Marquee"
android:textColor="?android:attr/textColorSecondary"
app:layout_constraintBottom_toBottomOf="@+id/menu_cover"
app:layout_constraintEnd_toEndOf="@+id/menu_type"
app:layout_constraintStart_toStartOf="@+id/menu_name"
app:layout_constraintTop_toBottomOf="@+id/menu_name"
app:layout_constraintBottom_toBottomOf="@+id/menu_cover"
android:layout_marginBottom="@dimen/spacing_small"
tools:text="Info A" />
<org.oxycblt.auxio.list.recycler.DialogRecyclerView
<com.google.android.material.divider.MaterialDivider
android:id="@+id/materialDivider"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/spacing_medium"
app:layout_constraintTop_toBottomOf="@+id/menu_cover" />
<org.oxycblt.auxio.list.recycler.AuxioRecyclerView
android:id="@+id/menu_option_recycler"
style="@style/Widget.Auxio.RecyclerView.Linear"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintTop_toBottomOf="@+id/menu_cover"
android:overScrollMode="never"
app:layout_constraintTop_toBottomOf="@+id/materialDivider"
tools:listitem="@layout/item_menu_option" />
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -33,6 +33,21 @@
<action
android:id="@+id/show_playlist"
app:destination="@id/playlist_detail_fragment" />
<action
android:id="@+id/open_song_menu"
app:destination="@id/song_menu_dialog" />
<action
android:id="@+id/open_album_menu"
app:destination="@id/album_menu_dialog" />
<action
android:id="@+id/open_artist_menu"
app:destination="@id/artist_menu_dialog" />
<action
android:id="@+id/open_genre_menu"
app:destination="@id/genre_menu_dialog" />
<action
android:id="@+id/open_playlist_menu"
app:destination="@id/playlist_menu_dialog" />
<action
android:id="@+id/new_playlist"
app:destination="@id/new_playlist_dialog" />
@ -86,6 +101,21 @@
<action
android:id="@+id/show_playlist"
app:destination="@id/playlist_detail_fragment" />
<action
android:id="@+id/open_song_menu"
app:destination="@id/song_menu_dialog" />
<action
android:id="@+id/open_album_menu"
app:destination="@id/album_menu_dialog" />
<action
android:id="@+id/open_artist_menu"
app:destination="@id/artist_menu_dialog" />
<action
android:id="@+id/open_genre_menu"
app:destination="@id/genre_menu_dialog" />
<action
android:id="@+id/open_playlist_menu"
app:destination="@id/playlist_menu_dialog" />
<action
android:id="@+id/rename_playlist"
app:destination="@id/rename_playlist_dialog" />
@ -123,12 +153,15 @@
<action
android:id="@+id/show_artist"
app:destination="@id/artist_detail_fragment" />
<action
android:id="@+id/add_to_playlist"
app:destination="@id/add_to_playlist_dialog" />
<action
android:id="@+id/show_artists"
app:destination="@id/show_artists_dialog" />
<action
android:id="@+id/open_song_menu"
app:destination="@id/song_menu_dialog" />
<action
android:id="@+id/add_to_playlist"
app:destination="@id/add_to_playlist_dialog" />
<action
android:id="@+id/play_from_artist"
app:destination="@id/play_from_artist_dialog" />
@ -154,6 +187,12 @@
<action
android:id="@+id/show_artist"
app:destination="@id/artist_detail_fragment" />
<action
android:id="@+id/open_song_menu"
app:destination="@id/song_menu_dialog" />
<action
android:id="@+id/open_album_menu"
app:destination="@id/album_menu_dialog" />
<action
android:id="@+id/add_to_playlist"
app:destination="@id/add_to_playlist_dialog" />
@ -182,6 +221,12 @@
<action
android:id="@+id/show_artists"
app:destination="@id/show_artists_dialog" />
<action
android:id="@+id/open_song_menu"
app:destination="@id/song_menu_dialog" />
<action
android:id="@+id/open_artist_menu"
app:destination="@id/artist_menu_dialog" />
<action
android:id="@+id/add_to_playlist"
app:destination="@id/add_to_playlist_dialog" />
@ -210,6 +255,9 @@
<action
android:id="@+id/show_artists"
app:destination="@id/show_artists_dialog" />
<action
android:id="@+id/open_song_menu"
app:destination="@id/song_menu_dialog" />
<action
android:id="@+id/rename_playlist"
app:destination="@id/rename_playlist_dialog" />