From 39b1fd10577a32f2f1a6c19db7e315c9c277443e Mon Sep 17 00:00:00 2001 From: Alexander Capehart Date: Mon, 3 Jul 2023 17:04:54 -0600 Subject: [PATCH] menu: move item logic from listfragment Move menu action logic from ListFragment into corresponding MenuFragments. --- .../auxio/home/list/AlbumListFragment.kt | 2 +- .../org/oxycblt/auxio/list/ListFragment.kt | 284 ------------------ .../auxio/list/menu/MenuDialogFragment.kt | 9 +- .../auxio/list/menu/MenuDialogFragmentImpl.kt | 111 ++++++- 4 files changed, 112 insertions(+), 294 deletions(-) diff --git a/app/src/main/java/org/oxycblt/auxio/home/list/AlbumListFragment.kt b/app/src/main/java/org/oxycblt/auxio/home/list/AlbumListFragment.kt index 9bf2bc05c..8c9ea7413 100644 --- a/app/src/main/java/org/oxycblt/auxio/home/list/AlbumListFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/home/list/AlbumListFragment.kt @@ -144,7 +144,7 @@ class AlbumListFragment : } override fun onOpenMenu(item: Album, anchor: View) { - openMusicMenu(anchor, R.menu.item_album, item) + menuModel.openMenu(R.menu.item_album, item) } private fun updateAlbums(albums: List) { diff --git a/app/src/main/java/org/oxycblt/auxio/list/ListFragment.kt b/app/src/main/java/org/oxycblt/auxio/list/ListFragment.kt index 7931f63e6..9348d64b1 100644 --- a/app/src/main/java/org/oxycblt/auxio/list/ListFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/list/ListFragment.kt @@ -24,19 +24,10 @@ import androidx.appcompat.widget.PopupMenu import androidx.core.view.MenuCompat import androidx.recyclerview.widget.RecyclerView import androidx.viewbinding.ViewBinding -import org.oxycblt.auxio.R import org.oxycblt.auxio.detail.DetailViewModel import org.oxycblt.auxio.list.selection.SelectionFragment -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.Playlist -import org.oxycblt.auxio.music.Song import org.oxycblt.auxio.util.logD -import org.oxycblt.auxio.util.logW -import org.oxycblt.auxio.util.share -import org.oxycblt.auxio.util.showToast /** * A Fragment containing a selectable list. @@ -76,281 +67,6 @@ abstract class ListFragment : selectionModel.select(item) } - /** - * Opens a menu in the context of a [Song]. This menu will be managed by the Fragment and closed - * when the view is destroyed. If a menu is already opened, this call is ignored. - * - * @param anchor The [View] to anchor the menu to. - * @param menuRes The resource of the menu to load. - * @param song The [Song] to create the menu for. - */ - protected fun openMusicMenu(anchor: View, @MenuRes menuRes: Int, song: Song) { - logD("Launching new song menu: ${song.name}") - - openMenu(anchor, menuRes) { - setOnMenuItemClickListener { - when (it.itemId) { - R.id.action_play_next -> { - playbackModel.playNext(song) - requireContext().showToast(R.string.lng_queue_added) - true - } - R.id.action_queue_add -> { - playbackModel.addToQueue(song) - requireContext().showToast(R.string.lng_queue_added) - true - } - R.id.action_go_artist -> { - detailModel.showArtist(song) - true - } - R.id.action_go_album -> { - detailModel.showAlbum(song.album) - true - } - R.id.action_share -> { - requireContext().share(song) - true - } - R.id.action_playlist_add -> { - musicModel.addToPlaylist(song) - true - } - R.id.action_song_detail -> { - detailModel.showSong(song) - true - } - else -> { - logW("Unexpected menu item selected") - false - } - } - } - } - } - - /** - * Opens a menu in the context of a [Album]. This menu will be managed by the Fragment and - * closed when the view is destroyed. If a menu is already opened, this call is ignored. - * - * @param anchor The [View] to anchor the menu to. - * @param menuRes The resource of the menu to load. - * @param album The [Album] to create the menu for. - */ - protected fun openMusicMenu(anchor: View, @MenuRes menuRes: Int, album: Album) { - logD("Launching new album menu: ${album.name}") - - openMenu(anchor, menuRes) { - setOnMenuItemClickListener { - when (it.itemId) { - R.id.action_play -> { - playbackModel.play(album) - true - } - R.id.action_shuffle -> { - playbackModel.shuffle(album) - true - } - R.id.action_play_next -> { - playbackModel.playNext(album) - requireContext().showToast(R.string.lng_queue_added) - true - } - R.id.action_queue_add -> { - playbackModel.addToQueue(album) - requireContext().showToast(R.string.lng_queue_added) - true - } - R.id.action_go_artist -> { - detailModel.showArtist(album) - true - } - R.id.action_playlist_add -> { - musicModel.addToPlaylist(album) - true - } - R.id.action_share -> { - requireContext().share(album) - true - } - else -> { - logW("Unexpected menu item selected") - false - } - } - } - } - } - - /** - * Opens a menu in the context of a [Artist]. This menu will be managed by the Fragment and - * closed when the view is destroyed. If a menu is already opened, this call is ignored. - * - * @param anchor The [View] to anchor the menu to. - * @param menuRes The resource of the menu to load. - * @param artist The [Artist] to create the menu for. - */ - protected fun openMusicMenu(anchor: View, @MenuRes menuRes: Int, artist: Artist) { - logD("Launching new artist menu: ${artist.name}") - - openMenu(anchor, menuRes) { - val playable = artist.songs.isNotEmpty() - if (!playable) { - logD("Artist is empty, disabling playback/playlist/share options") - } - menu.findItem(R.id.action_play).isEnabled = playable - menu.findItem(R.id.action_shuffle).isEnabled = playable - menu.findItem(R.id.action_play_next).isEnabled = playable - menu.findItem(R.id.action_queue_add).isEnabled = playable - menu.findItem(R.id.action_playlist_add).isEnabled = playable - menu.findItem(R.id.action_share).isEnabled = playable - - setOnMenuItemClickListener { - when (it.itemId) { - R.id.action_play -> { - playbackModel.play(artist) - true - } - R.id.action_shuffle -> { - playbackModel.shuffle(artist) - true - } - R.id.action_play_next -> { - playbackModel.playNext(artist) - requireContext().showToast(R.string.lng_queue_added) - true - } - R.id.action_queue_add -> { - playbackModel.addToQueue(artist) - requireContext().showToast(R.string.lng_queue_added) - true - } - R.id.action_playlist_add -> { - musicModel.addToPlaylist(artist) - true - } - R.id.action_share -> { - requireContext().share(artist) - true - } - else -> { - logW("Unexpected menu item selected") - false - } - } - } - } - } - - /** - * Opens a menu in the context of a [Genre]. This menu will be managed by the Fragment and - * closed when the view is destroyed. If a menu is already opened, this call is ignored. - * - * @param anchor The [View] to anchor the menu to. - * @param menuRes The resource of the menu to load. - * @param genre The [Genre] to create the menu for. - */ - protected fun openMusicMenu(anchor: View, @MenuRes menuRes: Int, genre: Genre) { - logD("Launching new genre menu: ${genre.name}") - - openMenu(anchor, menuRes) { - setOnMenuItemClickListener { - when (it.itemId) { - R.id.action_play -> { - playbackModel.play(genre) - true - } - R.id.action_shuffle -> { - playbackModel.shuffle(genre) - true - } - R.id.action_play_next -> { - playbackModel.playNext(genre) - requireContext().showToast(R.string.lng_queue_added) - true - } - R.id.action_queue_add -> { - playbackModel.addToQueue(genre) - requireContext().showToast(R.string.lng_queue_added) - true - } - R.id.action_playlist_add -> { - musicModel.addToPlaylist(genre) - true - } - R.id.action_share -> { - requireContext().share(genre) - true - } - else -> { - logW("Unexpected menu item selected") - false - } - } - } - } - } - - /** - * Opens a menu in the context of a [Playlist]. This menu will be managed by the Fragment and - * closed when the view is destroyed. If a menu is already opened, this call is ignored. - * - * @param anchor The [View] to anchor the menu to. - * @param menuRes The resource of the menu to load. - * @param playlist The [Playlist] to create the menu for. - */ - protected fun openMusicMenu(anchor: View, @MenuRes menuRes: Int, playlist: Playlist) { - logD("Launching new playlist menu: ${playlist.name}") - - openMenu(anchor, menuRes) { - val playable = playlist.songs.isNotEmpty() - menu.findItem(R.id.action_play).isEnabled = playable - menu.findItem(R.id.action_shuffle).isEnabled = playable - menu.findItem(R.id.action_play_next).isEnabled = playable - menu.findItem(R.id.action_queue_add).isEnabled = playable - menu.findItem(R.id.action_share).isEnabled = playable - - setOnMenuItemClickListener { - when (it.itemId) { - R.id.action_play -> { - playbackModel.play(playlist) - true - } - R.id.action_shuffle -> { - playbackModel.shuffle(playlist) - true - } - R.id.action_play_next -> { - playbackModel.playNext(playlist) - requireContext().showToast(R.string.lng_queue_added) - true - } - R.id.action_queue_add -> { - playbackModel.addToQueue(playlist) - requireContext().showToast(R.string.lng_queue_added) - true - } - R.id.action_rename -> { - musicModel.renamePlaylist(playlist) - true - } - R.id.action_delete -> { - musicModel.deletePlaylist(playlist) - true - } - R.id.action_share -> { - requireContext().share(playlist) - true - } - else -> { - logW("Unexpected menu item selected") - false - } - } - } - } - } - /** * Open a menu. This menu will be managed by the Fragment and closed when the view is destroyed. * If a menu is already opened, this call is ignored. diff --git a/app/src/main/java/org/oxycblt/auxio/list/menu/MenuDialogFragment.kt b/app/src/main/java/org/oxycblt/auxio/list/menu/MenuDialogFragment.kt index e9d20edff..88df9577e 100644 --- a/app/src/main/java/org/oxycblt/auxio/list/menu/MenuDialogFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/list/menu/MenuDialogFragment.kt @@ -23,7 +23,6 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.MenuInflater import android.view.MenuItem -import androidx.annotation.IdRes import androidx.appcompat.view.menu.MenuBuilder import androidx.core.view.children import androidx.navigation.fragment.findNavController @@ -41,6 +40,8 @@ import org.oxycblt.auxio.util.logD * options. * * @author Alexander Capehart (OxygenCobalt) + * + * TODO: Handle enabled/disabled states */ abstract class MenuDialogFragment : ViewBindingBottomSheetDialogFragment(), ClickableListListener { @@ -50,7 +51,7 @@ abstract class MenuDialogFragment : abstract val menuRes: Int abstract val uid: Music.UID abstract fun updateMusic(binding: DialogMenuBinding, music: T) - abstract fun onClick(music: T, @IdRes optionId: Int) + abstract fun onClick(music: T, item: MenuItem) override fun onCreateBinding(inflater: LayoutInflater) = DialogMenuBinding.inflate(inflater) @@ -65,7 +66,7 @@ abstract class MenuDialogFragment : itemAnimator = null } - // Avoid having to use a dummy view and rely on what AndroidX Toolbar uses. + // Avoid having to use a dummy view and just rely on what AndroidX Toolbar uses. @SuppressLint("RestrictedApi") val builder = MenuBuilder(requireContext()) MenuInflater(requireContext()).inflate(menuRes, builder) menuAdapter.update(builder.children.toList(), UpdateInstructions.Diff) @@ -92,6 +93,6 @@ abstract class MenuDialogFragment : final override fun onClick(item: MenuItem, viewHolder: RecyclerView.ViewHolder) { findNavController().navigateUp() - @Suppress("UNCHECKED_CAST") onClick(menuModel.currentMusic.value as T, item.itemId) + @Suppress("UNCHECKED_CAST") onClick(menuModel.currentMusic.value as T, item) } } diff --git a/app/src/main/java/org/oxycblt/auxio/list/menu/MenuDialogFragmentImpl.kt b/app/src/main/java/org/oxycblt/auxio/list/menu/MenuDialogFragmentImpl.kt index e68af70ca..121484753 100644 --- a/app/src/main/java/org/oxycblt/auxio/list/menu/MenuDialogFragmentImpl.kt +++ b/app/src/main/java/org/oxycblt/auxio/list/menu/MenuDialogFragmentImpl.kt @@ -18,23 +18,32 @@ package org.oxycblt.auxio.list.menu +import android.view.MenuItem import androidx.fragment.app.activityViewModels import androidx.navigation.fragment.navArgs import dagger.hilt.android.AndroidEntryPoint import org.oxycblt.auxio.R import org.oxycblt.auxio.databinding.DialogMenuBinding +import org.oxycblt.auxio.detail.DetailViewModel 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.MusicViewModel import org.oxycblt.auxio.music.Playlist import org.oxycblt.auxio.music.Song import org.oxycblt.auxio.music.resolveNames +import org.oxycblt.auxio.playback.PlaybackViewModel import org.oxycblt.auxio.util.getPlural +import org.oxycblt.auxio.util.share +import org.oxycblt.auxio.util.showToast @AndroidEntryPoint class SongMenuDialogFragment : MenuDialogFragment() { override val menuModel: MenuViewModel by activityViewModels() + private val detailModel: DetailViewModel by activityViewModels() + private val musicModel: MusicViewModel by activityViewModels() + private val playbackModel: PlaybackViewModel by activityViewModels() private val args: SongMenuDialogFragmentArgs by navArgs() override val menuRes: Int @@ -50,12 +59,32 @@ class SongMenuDialogFragment : MenuDialogFragment() { binding.menuInfo.text = music.artists.resolveNames(context) } - override fun onClick(music: Song, optionId: Int) {} + override fun onClick(music: Song, item: MenuItem) { + when (item.itemId) { + R.id.action_play_next -> { + playbackModel.playNext(music) + requireContext().showToast(R.string.lng_queue_added) + } + R.id.action_queue_add -> { + playbackModel.addToQueue(music) + requireContext().showToast(R.string.lng_queue_added) + } + R.id.action_go_artist -> detailModel.showArtist(music) + R.id.action_go_album -> detailModel.showAlbum(music) + R.id.action_share -> requireContext().share(music) + R.id.action_playlist_add -> musicModel.addToPlaylist(music) + R.id.action_song_detail -> detailModel.showSong(music) + else -> error("Unexpected menu item selected $item") + } + } } @AndroidEntryPoint class AlbumMenuDialogFragment : MenuDialogFragment() { override val menuModel: MenuViewModel by activityViewModels() + private val detailModel: DetailViewModel by activityViewModels() + private val musicModel: MusicViewModel by activityViewModels() + private val playbackModel: PlaybackViewModel by activityViewModels() private val args: AlbumMenuDialogFragmentArgs by navArgs() override val menuRes: Int @@ -71,12 +100,31 @@ class AlbumMenuDialogFragment : MenuDialogFragment() { binding.menuInfo.text = music.artists.resolveNames(context) } - override fun onClick(music: Album, optionId: Int) {} + override fun onClick(music: Album, item: MenuItem) { + when (item.itemId) { + R.id.action_play -> playbackModel.play(music) + R.id.action_shuffle -> playbackModel.shuffle(music) + R.id.action_play_next -> { + playbackModel.playNext(music) + requireContext().showToast(R.string.lng_queue_added) + } + R.id.action_queue_add -> { + playbackModel.addToQueue(music) + requireContext().showToast(R.string.lng_queue_added) + } + R.id.action_go_artist -> detailModel.showArtist(music) + R.id.action_playlist_add -> musicModel.addToPlaylist(music) + R.id.action_share -> requireContext().share(music) + else -> error("Unexpected menu item selected $item") + } + } } @AndroidEntryPoint class ArtistMenuDialogFragment : MenuDialogFragment() { override val menuModel: MenuViewModel by activityViewModels() + private val musicModel: MusicViewModel by activityViewModels() + private val playbackModel: PlaybackViewModel by activityViewModels() private val args: ArtistMenuDialogFragmentArgs by navArgs() override val menuRes: Int @@ -100,12 +148,30 @@ class ArtistMenuDialogFragment : MenuDialogFragment() { }) } - override fun onClick(music: Artist, optionId: Int) {} + override fun onClick(music: Artist, item: MenuItem) { + when (item.itemId) { + R.id.action_play -> playbackModel.play(music) + R.id.action_shuffle -> playbackModel.shuffle(music) + R.id.action_play_next -> { + playbackModel.playNext(music) + requireContext().showToast(R.string.lng_queue_added) + } + R.id.action_queue_add -> { + playbackModel.addToQueue(music) + requireContext().showToast(R.string.lng_queue_added) + } + R.id.action_playlist_add -> musicModel.addToPlaylist(music) + R.id.action_share -> requireContext().share(music) + else -> error("Unexpected menu item $item") + } + } } @AndroidEntryPoint class GenreMenuDialogFragment : MenuDialogFragment() { override val menuModel: MenuViewModel by activityViewModels() + private val musicModel: MusicViewModel by activityViewModels() + private val playbackModel: PlaybackViewModel by activityViewModels() private val args: GenreMenuDialogFragmentArgs by navArgs() override val menuRes: Int @@ -125,12 +191,30 @@ class GenreMenuDialogFragment : MenuDialogFragment() { context.getPlural(R.plurals.fmt_song_count, music.songs.size)) } - override fun onClick(music: Genre, optionId: Int) {} + override fun onClick(music: Genre, item: MenuItem) { + when (item.itemId) { + R.id.action_play -> playbackModel.play(music) + R.id.action_shuffle -> playbackModel.shuffle(music) + R.id.action_play_next -> { + playbackModel.playNext(music) + requireContext().showToast(R.string.lng_queue_added) + } + R.id.action_queue_add -> { + playbackModel.addToQueue(music) + requireContext().showToast(R.string.lng_queue_added) + } + R.id.action_playlist_add -> musicModel.addToPlaylist(music) + R.id.action_share -> requireContext().share(music) + else -> error("Unexpected menu item $item") + } + } } @AndroidEntryPoint class PlaylistMenuDialogFragment : MenuDialogFragment() { override val menuModel: MenuViewModel by activityViewModels() + private val musicModel: MusicViewModel by activityViewModels() + private val playbackModel: PlaybackViewModel by activityViewModels() private val args: PlaylistMenuDialogFragmentArgs by navArgs() override val menuRes: Int @@ -146,5 +230,22 @@ class PlaylistMenuDialogFragment : MenuDialogFragment() { binding.menuInfo.text = context.getPlural(R.plurals.fmt_song_count, music.songs.size) } - override fun onClick(music: Playlist, optionId: Int) {} + override fun onClick(music: Playlist, item: MenuItem) { + when (item.itemId) { + R.id.action_play -> playbackModel.play(music) + R.id.action_shuffle -> playbackModel.shuffle(music) + R.id.action_play_next -> { + playbackModel.playNext(music) + requireContext().showToast(R.string.lng_queue_added) + } + R.id.action_queue_add -> { + playbackModel.addToQueue(music) + requireContext().showToast(R.string.lng_queue_added) + } + R.id.action_rename -> musicModel.renamePlaylist(music) + R.id.action_delete -> musicModel.deletePlaylist(music) + R.id.action_share -> requireContext().share(music) + else -> error("Unexpected menu item $item") + } + } }