menu: move item logic from listfragment

Move menu action logic from ListFragment into corresponding
MenuFragments.
This commit is contained in:
Alexander Capehart 2023-07-03 17:04:54 -06:00
parent d6a20fedb3
commit 39b1fd1057
No known key found for this signature in database
GPG key ID: 37DBE3621FE9AD47
4 changed files with 112 additions and 294 deletions

View file

@ -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<Album>) {

View file

@ -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<in T : Music, VB : ViewBinding> :
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.

View file

@ -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<T : Music> :
ViewBindingBottomSheetDialogFragment<DialogMenuBinding>(), ClickableListListener<MenuItem> {
@ -50,7 +51,7 @@ abstract class MenuDialogFragment<T : Music> :
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<T : Music> :
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<T : Music> :
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)
}
}

View file

@ -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<Song>() {
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<Song>() {
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<Album>() {
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<Album>() {
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<Artist>() {
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<Artist>() {
})
}
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<Genre>() {
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<Genre>() {
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<Playlist>() {
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<Playlist>() {
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")
}
}
}