list: add ability to play/shuffle songs in menu

Add play and shuffle options for all song menus.

These will override the shuffle state, unlike other song play
interactions.

This required a good bit of refactoring to menu, some of which
might be ported to other commands in future changes.
This commit is contained in:
Alexander Capehart 2023-07-11 14:45:08 -06:00
parent ecc84dd8c8
commit 32a0d97e5d
No known key found for this signature in database
GPG key ID: 37DBE3621FE9AD47
22 changed files with 373 additions and 280 deletions

View file

@ -121,16 +121,16 @@ object IntegerTable {
const val COVER_MODE_MEDIA_STORE = 0xA11D
/** CoverMode.Quality */
const val COVER_MODE_QUALITY = 0xA11E
/** PlaySong.ByItself */
const val PLAY_SONG_BY_ITSELF = 0xA11F
/** PlaySong.FromAll */
const val PLAY_SONG_FROM_ALL = 0xA120
const val PLAY_SONG_FROM_ALL = 0xA11F
/** PlaySong.FromAlbum */
const val PLAY_SONG_FROM_ALBUM = 0xA121
const val PLAY_SONG_FROM_ALBUM = 0xA120
/** PlaySong.FromArtist */
const val PLAY_SONG_FROM_ARTIST = 0xA122
const val PLAY_SONG_FROM_ARTIST = 0xA121
/** PlaySong.FromGenre */
const val PLAY_SONG_FROM_GENRE = 0xA123
const val PLAY_SONG_FROM_GENRE = 0xA122
/** PlaySong.FromPlaylist */
const val PLAY_SONG_FROM_PLAYLIST = 0xA124
const val PLAY_SONG_FROM_PLAYLIST = 0xA123
/** PlaySong.ByItself */
const val PLAY_SONG_BY_ITSELF = 0xA124
}

View file

@ -183,7 +183,7 @@ class AlbumDetailFragment :
}
override fun onOpenMenu(item: Song, anchor: View) {
listModel.openMenu(R.menu.item_album_song, item)
listModel.openMenu(R.menu.item_album_song, item, detailModel.playInAlbumWith)
}
override fun onPlay() {
@ -302,8 +302,7 @@ class AlbumDetailFragment :
if (menu == null) return
val directions =
when (menu) {
is Menu.ForSong ->
AlbumDetailFragmentDirections.openSongMenu(menu.menuRes, menu.music.uid)
is Menu.ForSong -> AlbumDetailFragmentDirections.openSongMenu(menu.parcel)
is Menu.ForAlbum,
is Menu.ForArtist,
is Menu.ForGenre,

View file

@ -186,7 +186,8 @@ class ArtistDetailFragment :
override fun onOpenMenu(item: Music, anchor: View) {
when (item) {
is Song -> listModel.openMenu(R.menu.item_artist_song, item)
is Song ->
listModel.openMenu(R.menu.item_artist_song, item, detailModel.playInArtistWith)
is Album -> listModel.openMenu(R.menu.item_artist_album, item)
else -> error("Unexpected datatype: ${item::class.simpleName}")
}
@ -306,10 +307,8 @@ class ArtistDetailFragment :
if (menu == null) return
val directions =
when (menu) {
is Menu.ForSong ->
ArtistDetailFragmentDirections.openSongMenu(menu.menuRes, menu.music.uid)
is Menu.ForAlbum ->
ArtistDetailFragmentDirections.openAlbumMenu(menu.menuRes, menu.music.uid)
is Menu.ForSong -> ArtistDetailFragmentDirections.openSongMenu(menu.parcel)
is Menu.ForAlbum -> ArtistDetailFragmentDirections.openAlbumMenu(menu.parcel)
is Menu.ForArtist,
is Menu.ForGenre,
is Menu.ForPlaylist -> error("Unexpected menu $menu")

View file

@ -185,7 +185,7 @@ class GenreDetailFragment :
override fun onOpenMenu(item: Music, anchor: View) {
when (item) {
is Artist -> listModel.openMenu(R.menu.item_parent, item)
is Song -> listModel.openMenu(R.menu.item_song, item)
is Song -> listModel.openMenu(R.menu.item_song, item, detailModel.playInGenreWith)
else -> error("Unexpected datatype: ${item::class.simpleName}")
}
}
@ -294,10 +294,8 @@ class GenreDetailFragment :
if (menu == null) return
val directions =
when (menu) {
is Menu.ForSong ->
GenreDetailFragmentDirections.openSongMenu(menu.menuRes, menu.music.uid)
is Menu.ForArtist ->
GenreDetailFragmentDirections.openArtistMenu(menu.menuRes, menu.music.uid)
is Menu.ForSong -> GenreDetailFragmentDirections.openSongMenu(menu.parcel)
is Menu.ForArtist -> GenreDetailFragmentDirections.openArtistMenu(menu.parcel)
is Menu.ForAlbum,
is Menu.ForGenre,
is Menu.ForPlaylist -> error("Unexpected menu $menu")

View file

@ -237,7 +237,7 @@ class PlaylistDetailFragment :
}
override fun onOpenMenu(item: Song, anchor: View) {
listModel.openMenu(R.menu.item_playlist_song, item)
listModel.openMenu(R.menu.item_playlist_song, item, detailModel.playInPlaylistWith)
}
override fun onPlay() {
@ -344,8 +344,7 @@ class PlaylistDetailFragment :
if (menu == null) return
val directions =
when (menu) {
is Menu.ForSong ->
PlaylistDetailFragmentDirections.openSongMenu(menu.menuRes, menu.music.uid)
is Menu.ForSong -> PlaylistDetailFragmentDirections.openSongMenu(menu.parcel)
is Menu.ForArtist,
is Menu.ForAlbum,
is Menu.ForGenre,

View file

@ -577,15 +577,11 @@ class HomeFragment :
if (menu == null) return
val directions =
when (menu) {
is Menu.ForSong -> HomeFragmentDirections.openSongMenu(menu.menuRes, menu.music.uid)
is Menu.ForAlbum ->
HomeFragmentDirections.openAlbumMenu(menu.menuRes, menu.music.uid)
is Menu.ForArtist ->
HomeFragmentDirections.openArtistMenu(menu.menuRes, menu.music.uid)
is Menu.ForGenre ->
HomeFragmentDirections.openGenreMenu(menu.menuRes, menu.music.uid)
is Menu.ForPlaylist ->
HomeFragmentDirections.openPlaylistMenu(menu.menuRes, menu.music.uid)
is Menu.ForSong -> HomeFragmentDirections.openSongMenu(menu.parcel)
is Menu.ForAlbum -> HomeFragmentDirections.openAlbumMenu(menu.parcel)
is Menu.ForArtist -> HomeFragmentDirections.openArtistMenu(menu.parcel)
is Menu.ForGenre -> HomeFragmentDirections.openGenreMenu(menu.parcel)
is Menu.ForPlaylist -> HomeFragmentDirections.openPlaylistMenu(menu.parcel)
}
findNavController().navigateSafe(directions)
}

View file

@ -141,7 +141,7 @@ class SongListFragment :
}
override fun onOpenMenu(item: Song, anchor: View) {
listModel.openMenu(R.menu.item_song, item)
listModel.openMenu(R.menu.item_song, item, homeModel.playWith)
}
private fun updateSongs(songs: List<Song>) {

View file

@ -18,12 +18,14 @@
package org.oxycblt.auxio.list
import android.os.Parcelable
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 kotlinx.parcelize.Parcelize
import org.oxycblt.auxio.music.Album
import org.oxycblt.auxio.music.Artist
import org.oxycblt.auxio.music.Genre
@ -33,6 +35,7 @@ import org.oxycblt.auxio.music.MusicRepository
import org.oxycblt.auxio.music.MusicSettings
import org.oxycblt.auxio.music.Playlist
import org.oxycblt.auxio.music.Song
import org.oxycblt.auxio.playback.PlaySong
import org.oxycblt.auxio.util.Event
import org.oxycblt.auxio.util.MutableEvent
import org.oxycblt.auxio.util.logD
@ -146,10 +149,12 @@ constructor(
*
* @param menuRes The resource of the menu to use.
* @param song The [Song] to show.
* @param playWith A [PlaySong] command to give context to what "Play" and "Shuffle" actions
* should do.
*/
fun openMenu(@MenuRes menuRes: Int, song: Song) {
fun openMenu(@MenuRes menuRes: Int, song: Song, playWith: PlaySong) {
logD("Opening menu for $song")
openImpl(Menu.ForSong(menuRes, song))
openImpl(Menu.ForSong(menuRes, song, playWith))
}
/**
@ -216,19 +221,63 @@ constructor(
* @author Alexander Capehart (OxygenCobalt)
*/
sealed interface Menu {
/** The android resource ID of the menu options to display in the dialog. */
val menuRes: Int
/** The [Music] that the menu should act on. */
val music: Music
/** The menu resource to inflate in the menu dialog. */
@get:MenuRes val res: Int
/** A [Parcel] version of this instance that can be used as a navigation argument. */
val parcel: Parcel
sealed interface Parcel : Parcelable
/** Navigate to a [Song] menu dialog. */
class ForSong(@MenuRes override val menuRes: Int, override val music: Song) : Menu
/** Navigate to a [Album] menu dialog. */
class ForAlbum(@MenuRes override val menuRes: Int, override val music: Album) : Menu
/** Navigate to a [Artist] menu dialog. */
class ForArtist(@MenuRes override val menuRes: Int, override val music: Artist) : Menu
/** Navigate to a [Genre] menu dialog. */
class ForGenre(@MenuRes override val menuRes: Int, override val music: Genre) : Menu
/** Navigate to a [Playlist] menu dialog. */
class ForPlaylist(@MenuRes override val menuRes: Int, override val music: Playlist) : Menu
class ForSong(@MenuRes override val res: Int, val song: Song, val playWith: PlaySong) : Menu {
override val parcel: Parcel
get() {
val playWithUid =
when (playWith) {
is PlaySong.FromArtist -> playWith.which?.uid
is PlaySong.FromGenre -> playWith.which?.uid
is PlaySong.FromPlaylist -> playWith.which.uid
is PlaySong.FromAll,
is PlaySong.FromAlbum,
is PlaySong.ByItself -> null
}
return Parcel(res, song.uid, playWith.intCode, playWithUid)
}
@Parcelize
data class Parcel(
val res: Int,
val songUid: Music.UID,
val playWithCode: Int,
val playWithUid: Music.UID?
) : Menu.Parcel
}
/** Navigate to a [Album] menu dialog. */
class ForAlbum(@MenuRes override val res: Int, val album: Album) : Menu {
override val parcel
get() = Parcel(res, album.uid)
@Parcelize data class Parcel(val res: Int, val albumUid: Music.UID) : Menu.Parcel
}
/** Navigate to a [Artist] menu dialog. */
class ForArtist(@MenuRes override val res: Int, val artist: Artist) : Menu {
override val parcel
get() = Parcel(res, artist.uid)
@Parcelize data class Parcel(val res: Int, val artistUid: Music.UID) : Menu.Parcel
}
/** Navigate to a [Genre] menu dialog. */
class ForGenre(@MenuRes override val res: Int, val genre: Genre) : Menu {
override val parcel
get() = Parcel(res, genre.uid)
@Parcelize data class Parcel(val res: Int, val genreUid: Music.UID) : Menu.Parcel
}
/** Navigate to a [Playlist] menu dialog. */
class ForPlaylist(@MenuRes override val res: Int, val playlist: Playlist) : Menu {
override val parcel
get() = Parcel(res, playlist.uid)
@Parcelize data class Parcel(val res: Int, val playlistUid: Music.UID) : Menu.Parcel
}
}

View file

@ -30,8 +30,8 @@ import androidx.recyclerview.widget.RecyclerView
import org.oxycblt.auxio.databinding.DialogMenuBinding
import org.oxycblt.auxio.list.ClickableListListener
import org.oxycblt.auxio.list.ListViewModel
import org.oxycblt.auxio.list.Menu
import org.oxycblt.auxio.list.adapter.UpdateInstructions
import org.oxycblt.auxio.music.Music
import org.oxycblt.auxio.ui.ViewBindingBottomSheetDialogFragment
import org.oxycblt.auxio.util.collectImmediately
import org.oxycblt.auxio.util.logD
@ -44,40 +44,36 @@ import org.oxycblt.auxio.util.logD
*
* TODO: Extend the amount of music info shown in the dialog
*/
abstract class MenuDialogFragment<T : Music> :
abstract class MenuDialogFragment<M : Menu> :
ViewBindingBottomSheetDialogFragment<DialogMenuBinding>(), ClickableListListener<MenuItem> {
protected abstract val menuModel: MenuViewModel
protected abstract val listModel: ListViewModel
private val menuAdapter = MenuItemAdapter(@Suppress("LeakingThis") this)
/** The android resource ID of the menu options to display in the dialog. */
abstract val menuRes: Int
/** The [Music.UID] of the [T] to display menu options for. */
abstract val uid: Music.UID
abstract val parcel: Menu.Parcel
/**
* Get the options to disable in the context of the currently shown [T].
* Get the options to disable in the context of the currently shown [M].
*
* @param music The currently-shown music [T].
* @param menu The currently-shown menu [M].
*/
abstract fun getDisabledItemIds(music: T): Set<Int>
abstract fun getDisabledItemIds(menu: M): Set<Int>
/**
* Update the displayed information about the currently shown [T].
* Update the displayed information about the currently shown [M].
*
* @param binding The [DialogMenuBinding] to bind information to.
* @param music The currently-shown music [T].
* @param menu The currently-shown menu [M].
*/
abstract fun updateMusic(binding: DialogMenuBinding, music: T)
abstract fun updateMenu(binding: DialogMenuBinding, menu: M)
/**
* Forward the clicked [MenuItem] to it's corresponding handler in another module.
*
* @param item The [MenuItem] that was clicked.
* @param music The currently-shown music [T].
* @param menu The currently-shown menu [M].
*/
abstract fun onClick(item: MenuItem, music: T)
abstract fun onClick(item: MenuItem, menu: M)
override fun onCreateBinding(inflater: LayoutInflater) = DialogMenuBinding.inflate(inflater)
@ -94,8 +90,8 @@ abstract class MenuDialogFragment<T : Music> :
// --- VIEWMODEL SETUP ---
listModel.menu.consume()
menuModel.setMusic(uid)
collectImmediately(menuModel.currentMusic, this::updateMusic)
menuModel.setMenu(parcel)
collectImmediately(menuModel.currentMenu, this::updateMenu)
}
override fun onDestroyBinding(binding: DialogMenuBinding) {
@ -105,23 +101,25 @@ abstract class MenuDialogFragment<T : Music> :
binding.menuOptionRecycler.adapter = null
}
private fun updateMusic(music: Music?) {
if (music == null) {
logD("No music to show, navigating away")
private fun updateMenu(menu: Menu?) {
if (menu == null) {
logD("No menu to show, navigating away")
findNavController().navigateUp()
return
}
@Suppress("UNCHECKED_CAST") val castedMusic = music as T
@Suppress("UNCHECKED_CAST") val casted = menu as? M
check(casted != null) { "Unexpected menu instance ${menu::class.simpleName}" }
// We need to inflate the menu on every music update since it might have changed
// We need to inflate the menu on every menu update since it might have changed
// what options are available (ex. if an artist with no songs has had new songs added).
// Since we don't have (and don't want) a dummy view to inflate this menu, just
// depend on the AndroidX Toolbar internal API and hope for the best.
@SuppressLint("RestrictedApi") val builder = MenuBuilder(requireContext())
MenuInflater(requireContext()).inflate(menuRes, builder)
MenuInflater(requireContext()).inflate(casted.res, builder)
// Disable any menu options as specified by the impl
val disabledIds = getDisabledItemIds(castedMusic)
val disabledIds = getDisabledItemIds(casted)
val visible =
builder.children.mapTo(mutableListOf()) {
it.isEnabled = !disabledIds.contains(it.itemId)
@ -130,7 +128,7 @@ abstract class MenuDialogFragment<T : Music> :
menuAdapter.update(visible, UpdateInstructions.Diff)
// Delegate to impl how to show music
updateMusic(requireBinding(), castedMusic)
updateMenu(requireBinding(), casted)
}
final override fun onClick(item: MenuItem, viewHolder: RecyclerView.ViewHolder) {
@ -138,6 +136,6 @@ abstract class MenuDialogFragment<T : Music> :
// TODO: This should change if the app is 100% migrated to menu dialogs
findNavController().navigateUp()
// Delegate to impl on how to handle items
@Suppress("UNCHECKED_CAST") onClick(item, menuModel.currentMusic.value as T)
@Suppress("UNCHECKED_CAST") onClick(item, menuModel.currentMenu.value as M)
}
}

View file

@ -27,10 +27,9 @@ import org.oxycblt.auxio.R
import org.oxycblt.auxio.databinding.DialogMenuBinding
import org.oxycblt.auxio.detail.DetailViewModel
import org.oxycblt.auxio.list.ListViewModel
import org.oxycblt.auxio.music.Album
import org.oxycblt.auxio.list.Menu
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
@ -46,7 +45,7 @@ import org.oxycblt.auxio.util.showToast
* @author Alexander Capehart (OxygenCobalt)
*/
@AndroidEntryPoint
class SongMenuDialogFragment : MenuDialogFragment<Song>() {
class SongMenuDialogFragment : MenuDialogFragment<Menu.ForSong>() {
override val menuModel: MenuViewModel by activityViewModels()
override val listModel: ListViewModel by activityViewModels()
private val detailModel: DetailViewModel by activityViewModels()
@ -54,38 +53,37 @@ class SongMenuDialogFragment : MenuDialogFragment<Song>() {
private val playbackModel: PlaybackViewModel by activityViewModels()
private val args: SongMenuDialogFragmentArgs by navArgs()
override val menuRes: Int
get() = args.menuRes
override val uid: Music.UID
get() = args.songUid
override val parcel
get() = args.parcel
// Nothing to disable in song menus.
override fun getDisabledItemIds(music: Song) = setOf<Int>()
override fun getDisabledItemIds(menu: Menu.ForSong) = setOf<Int>()
override fun updateMusic(binding: DialogMenuBinding, music: Song) {
override fun updateMenu(binding: DialogMenuBinding, menu: Menu.ForSong) {
val context = requireContext()
binding.menuCover.bind(music)
binding.menuCover.bind(menu.song)
binding.menuType.text = getString(R.string.lbl_song)
binding.menuName.text = music.name.resolve(context)
binding.menuInfo.text = music.artists.resolveNames(context)
binding.menuName.text = menu.song.name.resolve(context)
binding.menuInfo.text = menu.song.artists.resolveNames(context)
}
override fun onClick(item: MenuItem, music: Song) {
override fun onClick(item: MenuItem, menu: Menu.ForSong) {
when (item.itemId) {
// TODO: Song play and shuffle as soon as PlaybackMode is refactored
R.id.action_play -> playbackModel.playExplicit(menu.song, menu.playWith)
R.id.action_shuffle -> playbackModel.shuffleExplicit(menu.song, menu.playWith)
R.id.action_play_next -> {
playbackModel.playNext(music)
playbackModel.playNext(menu.song)
requireContext().showToast(R.string.lng_queue_added)
}
R.id.action_queue_add -> {
playbackModel.addToQueue(music)
playbackModel.addToQueue(menu.song)
requireContext().showToast(R.string.lng_queue_added)
}
R.id.action_artist_details -> detailModel.showArtist(music)
R.id.action_album_details -> detailModel.showAlbum(music)
R.id.action_share -> requireContext().share(music)
R.id.action_playlist_add -> musicModel.addToPlaylist(music)
R.id.action_detail -> detailModel.showSong(music)
R.id.action_artist_details -> detailModel.showArtist(menu.song)
R.id.action_album_details -> detailModel.showAlbum(menu.song)
R.id.action_share -> requireContext().share(menu.song)
R.id.action_playlist_add -> musicModel.addToPlaylist(menu.song)
R.id.action_detail -> detailModel.showSong(menu.song)
else -> error("Unexpected menu item selected $item")
}
}
@ -97,7 +95,7 @@ class SongMenuDialogFragment : MenuDialogFragment<Song>() {
* @author Alexander Capehart (OxygenCobalt)
*/
@AndroidEntryPoint
class AlbumMenuDialogFragment : MenuDialogFragment<Album>() {
class AlbumMenuDialogFragment : MenuDialogFragment<Menu.ForAlbum>() {
override val menuModel: MenuViewModel by viewModels()
override val listModel: ListViewModel by activityViewModels()
private val detailModel: DetailViewModel by activityViewModels()
@ -105,38 +103,36 @@ class AlbumMenuDialogFragment : MenuDialogFragment<Album>() {
private val playbackModel: PlaybackViewModel by activityViewModels()
private val args: AlbumMenuDialogFragmentArgs by navArgs()
override val menuRes: Int
get() = args.menuRes
override val uid: Music.UID
get() = args.albumUid
override val parcel
get() = args.parcel
// Nothing to disable in album menus.
override fun getDisabledItemIds(music: Album) = setOf<Int>()
override fun getDisabledItemIds(menu: Menu.ForAlbum) = setOf<Int>()
override fun updateMusic(binding: DialogMenuBinding, music: Album) {
override fun updateMenu(binding: DialogMenuBinding, menu: Menu.ForAlbum) {
val context = requireContext()
binding.menuCover.bind(music)
binding.menuType.text = getString(music.releaseType.stringRes)
binding.menuName.text = music.name.resolve(context)
binding.menuInfo.text = music.artists.resolveNames(context)
binding.menuCover.bind(menu.album)
binding.menuType.text = getString(menu.album.releaseType.stringRes)
binding.menuName.text = menu.album.name.resolve(context)
binding.menuInfo.text = menu.album.artists.resolveNames(context)
}
override fun onClick(item: MenuItem, music: Album) {
override fun onClick(item: MenuItem, menu: Menu.ForAlbum) {
when (item.itemId) {
R.id.action_play -> playbackModel.play(music)
R.id.action_shuffle -> playbackModel.shuffle(music)
R.id.action_detail -> detailModel.showAlbum(music)
R.id.action_play -> playbackModel.play(menu.album)
R.id.action_shuffle -> playbackModel.shuffle(menu.album)
R.id.action_detail -> detailModel.showAlbum(menu.album)
R.id.action_play_next -> {
playbackModel.playNext(music)
playbackModel.playNext(menu.album)
requireContext().showToast(R.string.lng_queue_added)
}
R.id.action_queue_add -> {
playbackModel.addToQueue(music)
playbackModel.addToQueue(menu.album)
requireContext().showToast(R.string.lng_queue_added)
}
R.id.action_artist_details -> detailModel.showArtist(music)
R.id.action_playlist_add -> musicModel.addToPlaylist(music)
R.id.action_share -> requireContext().share(music)
R.id.action_artist_details -> detailModel.showArtist(menu.album)
R.id.action_playlist_add -> musicModel.addToPlaylist(menu.album)
R.id.action_share -> requireContext().share(menu.album)
else -> error("Unexpected menu item selected $item")
}
}
@ -148,7 +144,7 @@ class AlbumMenuDialogFragment : MenuDialogFragment<Album>() {
* @author Alexander Capehart (OxygenCobalt)
*/
@AndroidEntryPoint
class ArtistMenuDialogFragment : MenuDialogFragment<Artist>() {
class ArtistMenuDialogFragment : MenuDialogFragment<Menu.ForArtist>() {
override val menuModel: MenuViewModel by viewModels()
override val listModel: ListViewModel by activityViewModels()
private val detailModel: DetailViewModel by activityViewModels()
@ -156,13 +152,11 @@ class ArtistMenuDialogFragment : MenuDialogFragment<Artist>() {
private val playbackModel: PlaybackViewModel by activityViewModels()
private val args: ArtistMenuDialogFragmentArgs by navArgs()
override val menuRes: Int
get() = args.menuRes
override val uid: Music.UID
get() = args.artistUid
override val parcel
get() = args.parcel
override fun getDisabledItemIds(music: Artist) =
if (music.songs.isEmpty()) {
override fun getDisabledItemIds(menu: Menu.ForArtist) =
if (menu.artist.songs.isEmpty()) {
// Disable any operations that require some kind of songs to work with, as there won't
// be any in an empty artist.
setOf(
@ -176,37 +170,37 @@ class ArtistMenuDialogFragment : MenuDialogFragment<Artist>() {
setOf()
}
override fun updateMusic(binding: DialogMenuBinding, music: Artist) {
override fun updateMenu(binding: DialogMenuBinding, menu: Menu.ForArtist) {
val context = requireContext()
binding.menuCover.bind(music)
binding.menuCover.bind(menu.artist)
binding.menuType.text = getString(R.string.lbl_artist)
binding.menuName.text = music.name.resolve(context)
binding.menuName.text = menu.artist.name.resolve(context)
binding.menuInfo.text =
getString(
R.string.fmt_two,
context.getPlural(R.plurals.fmt_album_count, music.albums.size),
if (music.songs.isNotEmpty()) {
context.getPlural(R.plurals.fmt_song_count, music.songs.size)
context.getPlural(R.plurals.fmt_album_count, menu.artist.albums.size),
if (menu.artist.songs.isNotEmpty()) {
context.getPlural(R.plurals.fmt_song_count, menu.artist.songs.size)
} else {
getString(R.string.def_song_count)
})
}
override fun onClick(item: MenuItem, music: Artist) {
override fun onClick(item: MenuItem, menu: Menu.ForArtist) {
when (item.itemId) {
R.id.action_play -> playbackModel.play(music)
R.id.action_shuffle -> playbackModel.shuffle(music)
R.id.action_detail -> detailModel.showArtist(music)
R.id.action_play -> playbackModel.play(menu.artist)
R.id.action_shuffle -> playbackModel.shuffle(menu.artist)
R.id.action_detail -> detailModel.showArtist(menu.artist)
R.id.action_play_next -> {
playbackModel.playNext(music)
playbackModel.playNext(menu.artist)
requireContext().showToast(R.string.lng_queue_added)
}
R.id.action_queue_add -> {
playbackModel.addToQueue(music)
playbackModel.addToQueue(menu.artist)
requireContext().showToast(R.string.lng_queue_added)
}
R.id.action_playlist_add -> musicModel.addToPlaylist(music)
R.id.action_share -> requireContext().share(music)
R.id.action_playlist_add -> musicModel.addToPlaylist(menu.artist)
R.id.action_share -> requireContext().share(menu.artist)
else -> error("Unexpected menu item $item")
}
}
@ -218,7 +212,7 @@ class ArtistMenuDialogFragment : MenuDialogFragment<Artist>() {
* @author Alexander Capehart (OxygenCobalt)
*/
@AndroidEntryPoint
class GenreMenuDialogFragment : MenuDialogFragment<Genre>() {
class GenreMenuDialogFragment : MenuDialogFragment<Menu.ForGenre>() {
override val menuModel: MenuViewModel by viewModels()
override val listModel: ListViewModel by activityViewModels()
private val detailModel: DetailViewModel by activityViewModels()
@ -226,40 +220,38 @@ class GenreMenuDialogFragment : MenuDialogFragment<Genre>() {
private val playbackModel: PlaybackViewModel by activityViewModels()
private val args: GenreMenuDialogFragmentArgs by navArgs()
override val menuRes: Int
get() = args.menuRes
override val uid: Music.UID
get() = args.genreUid
override val parcel
get() = args.parcel
override fun getDisabledItemIds(music: Genre) = setOf<Int>()
override fun getDisabledItemIds(menu: Menu.ForGenre) = setOf<Int>()
override fun updateMusic(binding: DialogMenuBinding, music: Genre) {
override fun updateMenu(binding: DialogMenuBinding, menu: Menu.ForGenre) {
val context = requireContext()
binding.menuCover.bind(music)
binding.menuCover.bind(menu.genre)
binding.menuType.text = getString(R.string.lbl_genre)
binding.menuName.text = music.name.resolve(context)
binding.menuName.text = menu.genre.name.resolve(context)
binding.menuInfo.text =
getString(
R.string.fmt_two,
context.getPlural(R.plurals.fmt_artist_count, music.artists.size),
context.getPlural(R.plurals.fmt_song_count, music.songs.size))
context.getPlural(R.plurals.fmt_artist_count, menu.genre.artists.size),
context.getPlural(R.plurals.fmt_song_count, menu.genre.songs.size))
}
override fun onClick(item: MenuItem, music: Genre) {
override fun onClick(item: MenuItem, menu: Menu.ForGenre) {
when (item.itemId) {
R.id.action_play -> playbackModel.play(music)
R.id.action_shuffle -> playbackModel.shuffle(music)
R.id.action_detail -> detailModel.showGenre(music)
R.id.action_play -> playbackModel.play(menu.genre)
R.id.action_shuffle -> playbackModel.shuffle(menu.genre)
R.id.action_detail -> detailModel.showGenre(menu.genre)
R.id.action_play_next -> {
playbackModel.playNext(music)
playbackModel.playNext(menu.genre)
requireContext().showToast(R.string.lng_queue_added)
}
R.id.action_queue_add -> {
playbackModel.addToQueue(music)
playbackModel.addToQueue(menu.genre)
requireContext().showToast(R.string.lng_queue_added)
}
R.id.action_playlist_add -> musicModel.addToPlaylist(music)
R.id.action_share -> requireContext().share(music)
R.id.action_playlist_add -> musicModel.addToPlaylist(menu.genre)
R.id.action_share -> requireContext().share(menu.genre)
else -> error("Unexpected menu item $item")
}
}
@ -271,7 +263,7 @@ class GenreMenuDialogFragment : MenuDialogFragment<Genre>() {
* @author Alexander Capehart (OxygenCobalt)
*/
@AndroidEntryPoint
class PlaylistMenuDialogFragment : MenuDialogFragment<Playlist>() {
class PlaylistMenuDialogFragment : MenuDialogFragment<Menu.ForPlaylist>() {
override val menuModel: MenuViewModel by viewModels()
override val listModel: ListViewModel by activityViewModels()
private val detailModel: DetailViewModel by activityViewModels()
@ -279,13 +271,11 @@ class PlaylistMenuDialogFragment : MenuDialogFragment<Playlist>() {
private val playbackModel: PlaybackViewModel by activityViewModels()
private val args: PlaylistMenuDialogFragmentArgs by navArgs()
override val menuRes: Int
get() = args.menuRes
override val uid: Music.UID
get() = args.playlistUid
override val parcel
get() = args.parcel
override fun getDisabledItemIds(music: Playlist) =
if (music.songs.isEmpty()) {
override fun getDisabledItemIds(menu: Menu.ForPlaylist) =
if (menu.playlist.songs.isEmpty()) {
// Disable any operations that require some kind of songs to work with, as there won't
// be any in an empty playlist.
setOf(
@ -299,35 +289,35 @@ class PlaylistMenuDialogFragment : MenuDialogFragment<Playlist>() {
setOf()
}
override fun updateMusic(binding: DialogMenuBinding, music: Playlist) {
override fun updateMenu(binding: DialogMenuBinding, menu: Menu.ForPlaylist) {
val context = requireContext()
binding.menuCover.bind(music)
binding.menuCover.bind(menu.playlist)
binding.menuType.text = getString(R.string.lbl_playlist)
binding.menuName.text = music.name.resolve(context)
binding.menuName.text = menu.playlist.name.resolve(context)
binding.menuInfo.text =
if (music.songs.isNotEmpty()) {
context.getPlural(R.plurals.fmt_song_count, music.songs.size)
if (menu.playlist.songs.isNotEmpty()) {
context.getPlural(R.plurals.fmt_song_count, menu.playlist.songs.size)
} else {
getString(R.string.def_song_count)
}
}
override fun onClick(item: MenuItem, music: Playlist) {
override fun onClick(item: MenuItem, menu: Menu.ForPlaylist) {
when (item.itemId) {
R.id.action_play -> playbackModel.play(music)
R.id.action_shuffle -> playbackModel.shuffle(music)
R.id.action_detail -> detailModel.showPlaylist(music)
R.id.action_play -> playbackModel.play(menu.playlist)
R.id.action_shuffle -> playbackModel.shuffle(menu.playlist)
R.id.action_detail -> detailModel.showPlaylist(menu.playlist)
R.id.action_play_next -> {
playbackModel.playNext(music)
playbackModel.playNext(menu.playlist)
requireContext().showToast(R.string.lng_queue_added)
}
R.id.action_queue_add -> {
playbackModel.addToQueue(music)
playbackModel.addToQueue(menu.playlist)
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)
R.id.action_rename -> musicModel.renamePlaylist(menu.playlist)
R.id.action_delete -> musicModel.deletePlaylist(menu.playlist)
R.id.action_share -> requireContext().share(menu.playlist)
else -> error("Unexpected menu item $item")
}
}

View file

@ -23,8 +23,10 @@ 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.Music
import org.oxycblt.auxio.list.Menu
import org.oxycblt.auxio.music.MusicParent
import org.oxycblt.auxio.music.MusicRepository
import org.oxycblt.auxio.playback.PlaySong
import org.oxycblt.auxio.util.logW
/**
@ -35,32 +37,62 @@ import org.oxycblt.auxio.util.logW
@HiltViewModel
class MenuViewModel @Inject constructor(private val musicRepository: MusicRepository) :
ViewModel(), MusicRepository.UpdateListener {
private val _currentMusic = MutableStateFlow<Music?>(null)
/** The current [Music] information being shown in a menu dialog. */
val currentMusic: StateFlow<Music?> = _currentMusic
private val _currentMenu = MutableStateFlow<Menu?>(null)
/** The current [Menu] information being shown in a dialog. */
val currentMenu: StateFlow<Menu?> = _currentMenu
init {
musicRepository.addUpdateListener(this)
}
override fun onMusicChanges(changes: MusicRepository.Changes) {
_currentMusic.value = _currentMusic.value?.let { musicRepository.find(it.uid) }
_currentMenu.value = _currentMenu.value?.let { unpackParcel(it.parcel) }
}
override fun onCleared() {
musicRepository.removeUpdateListener(this)
}
/**
* Set a new [currentMusic] from it's [Music.UID]. [currentMusic] will be updated to align with
* the new album.
*
* @param uid The [Music.UID] of the [Music] to update [currentMusic] to. Must be valid.
*/
fun setMusic(uid: Music.UID) {
_currentMusic.value = musicRepository.find(uid)
if (_currentMusic.value == null) {
logW("Given Music UID to show was invalid")
fun setMenu(parcel: Menu.Parcel) {
_currentMenu.value = unpackParcel(parcel)
if (_currentMenu.value == null) {
logW("Given menu parcel $parcel was invalid")
}
}
private fun unpackParcel(parcel: Menu.Parcel) =
when (parcel) {
is Menu.ForSong.Parcel -> unpackSongParcel(parcel)
is Menu.ForAlbum.Parcel -> unpackAlbumParcel(parcel)
is Menu.ForArtist.Parcel -> unpackArtistParcel(parcel)
is Menu.ForGenre.Parcel -> unpackGenreParcel(parcel)
is Menu.ForPlaylist.Parcel -> unpackPlaylistParcel(parcel)
}
private fun unpackSongParcel(parcel: Menu.ForSong.Parcel): Menu.ForSong? {
val song = musicRepository.deviceLibrary?.findSong(parcel.songUid) ?: return null
val parent = parcel.playWithUid?.let(musicRepository::find) as MusicParent?
val playWith = PlaySong.fromIntCode(parcel.playWithCode, parent) ?: return null
return Menu.ForSong(parcel.res, song, playWith)
}
private fun unpackAlbumParcel(parcel: Menu.ForAlbum.Parcel): Menu.ForAlbum? {
val album = musicRepository.deviceLibrary?.findAlbum(parcel.albumUid) ?: return null
return Menu.ForAlbum(parcel.res, album)
}
private fun unpackArtistParcel(parcel: Menu.ForArtist.Parcel): Menu.ForArtist? {
val artist = musicRepository.deviceLibrary?.findArtist(parcel.artistUid) ?: return null
return Menu.ForArtist(parcel.res, artist)
}
private fun unpackGenreParcel(parcel: Menu.ForGenre.Parcel): Menu.ForGenre? {
val genre = musicRepository.deviceLibrary?.findGenre(parcel.genreUid) ?: return null
return Menu.ForGenre(parcel.res, genre)
}
private fun unpackPlaylistParcel(parcel: Menu.ForPlaylist.Parcel): Menu.ForPlaylist? {
val playlist = musicRepository.userLibrary?.findPlaylist(parcel.playlistUid) ?: return null
return Menu.ForPlaylist(parcel.res, playlist)
}
}

View file

@ -75,7 +75,7 @@ interface DeviceLibrary {
* Find a [Album] instance corresponding to the given [Music.UID].
*
* @param uid The [Music.UID] to search for.
* @return The corresponding [Song], or null if one was not found.
* @return The corresponding [Album], or null if one was not found.
*/
fun findAlbum(uid: Music.UID): Album?
@ -83,7 +83,7 @@ interface DeviceLibrary {
* Find a [Artist] instance corresponding to the given [Music.UID].
*
* @param uid The [Music.UID] to search for.
* @return The corresponding [Song], or null if one was not found.
* @return The corresponding [Artist], or null if one was not found.
*/
fun findArtist(uid: Music.UID): Artist?
@ -91,7 +91,7 @@ interface DeviceLibrary {
* Find a [Genre] instance corresponding to the given [Music.UID].
*
* @param uid The [Music.UID] to search for.
* @return The corresponding [Song], or null if one was not found.
* @return The corresponding [Genre], or null if one was not found.
*/
fun findGenre(uid: Music.UID): Genre?

View file

@ -21,56 +21,103 @@ package org.oxycblt.auxio.playback
import org.oxycblt.auxio.IntegerTable
import org.oxycblt.auxio.music.Artist
import org.oxycblt.auxio.music.Genre
import org.oxycblt.auxio.music.Music
import org.oxycblt.auxio.music.MusicParent
import org.oxycblt.auxio.music.Playlist
/**
* Configuration to play a song in a desired way.
*
* Since songs are not [MusicParent]s, the way the queue is generated around them has a lot more
* flexibility. The particular strategy used can be configured the user, but it also needs to be
* transferred between views at points (such as menus). [PlaySong] provides both of these, being a
* enum-like datatype when configuration is needed, and an algebraic datatype when data transfer is
* needed.
*
* @author Alexander Capehart (OxygenCobalt)
*/
sealed interface PlaySong {
/**
* The integer representation of this instance.
*
* @see fromIntCode
*/
val intCode: Int
/** Play a Song from the entire library of songs. */
object FromAll : PlaySong {
override val intCode = IntegerTable.PLAY_SONG_FROM_ALL
}
/** Play a song from it's album. */
object FromAlbum : PlaySong {
override val intCode = IntegerTable.PLAY_SONG_FROM_ALBUM
}
/**
* Play a song from (possibly) one of it's [Artist]s.
*
* @param which The [Artist] to specifically play from. If null, the user will be prompted for
* an [Artist] to choose of the song has multiple. Otherwise, the only [Artist] will be used.
*/
data class FromArtist(val which: Artist?) : PlaySong {
override val intCode = IntegerTable.PLAY_SONG_FROM_ARTIST
}
/**
* Play a song from (possibly) one of it's [Genre]s.
*
* @param which The [Genre] to specifically play from. If null, the user will be prompted for a
* [Genre] to choose of the song has multiple. Otherwise, the only [Genre] will be used.
*/
data class FromGenre(val which: Genre?) : PlaySong {
override val intCode = IntegerTable.PLAY_SONG_FROM_GENRE
}
/**
* Play a song from one of it's [Playlist]s.
*
* @param which The [Playlist] to specifically play from. This must be provided.
*/
data class FromPlaylist(val which: Playlist) : PlaySong {
override val intCode = IntegerTable.PLAY_SONG_FROM_PLAYLIST
}
/** Only play the given song, include nothing else in the queue. */
object ByItself : PlaySong {
override val intCode = IntegerTable.PLAY_SONG_BY_ITSELF
}
companion object {
fun fromIntCode(intCode: Int, inner: Music?): PlaySong? =
/**
* Convert a [PlaySong] integer representation into an instance.
*
* @param intCode An integer representation of a [PlaySong]
* @param which Optional [MusicParent] to automatically populate a [FromArtist],
* [FromGenre], or [FromPlaylist] instance. If the type of the [MusicParent] does not
* match, it will be considered invalid and null will be returned.
* @return The corresponding [PlaySong], or null if the [PlaySong] is invalid.
* @see PlaySong.intCode
*/
fun fromIntCode(intCode: Int, which: MusicParent? = null): PlaySong? =
when (intCode) {
IntegerTable.PLAY_SONG_BY_ITSELF -> ByItself
IntegerTable.PLAY_SONG_FROM_ALL -> FromAll
IntegerTable.PLAY_SONG_FROM_ALBUM -> FromAlbum
IntegerTable.PLAY_SONG_FROM_ARTIST ->
if (inner is Artist?) {
FromArtist(inner)
if (which is Artist?) {
FromArtist(which)
} else {
null
}
IntegerTable.PLAY_SONG_FROM_GENRE ->
if (inner is Genre?) {
FromGenre(inner)
if (which is Genre?) {
FromGenre(which)
} else {
null
}
IntegerTable.PLAY_SONG_FROM_PLAYLIST ->
if (inner is Playlist) {
FromPlaylist(inner)
if (which is Playlist) {
FromPlaylist(which)
} else {
null
}

View file

@ -75,17 +75,14 @@ class PlaybackSettingsImpl @Inject constructor(@ApplicationContext context: Cont
get() =
PlaySong.fromIntCode(
sharedPreferences.getInt(
getString(R.string.set_key_play_in_list_with), Int.MIN_VALUE),
null)
getString(R.string.set_key_play_in_list_with), Int.MIN_VALUE))
?: PlaySong.FromAll
override val inParentPlaybackMode: PlaySong?
get() =
PlaySong.fromIntCode(
sharedPreferences
.getInt(getString(R.string.set_key_play_in_parent_with), Int.MIN_VALUE)
.also { logD(it) },
null)
sharedPreferences.getInt(
getString(R.string.set_key_play_in_parent_with), Int.MIN_VALUE))
override val barAction: ActionMode
get() =

View file

@ -184,13 +184,13 @@ constructor(
playWithImpl(song, with, isImplicitlyShuffled())
}
// fun playExplicit(song: Song, with: PlaySong) {
// playWithImpl(song, with, false)
// }
//
// fun shuffleExplicit(song: Song, with: PlaySong) {
// playWithImpl(song, with, true)
// }
fun playExplicit(song: Song, with: PlaySong) {
playWithImpl(song, with, false)
}
fun shuffleExplicit(song: Song, with: PlaySong) {
playWithImpl(song, with, true)
}
/** Shuffle all songs in the music library. */
fun shuffleAll() {

View file

@ -184,7 +184,7 @@ class SearchFragment : ListFragment<Music, FragmentSearchBinding>() {
override fun onOpenMenu(item: Music, anchor: View) {
when (item) {
is Song -> listModel.openMenu(R.menu.item_song, item)
is Song -> listModel.openMenu(R.menu.item_song, item, searchModel.playWith)
is Album -> listModel.openMenu(R.menu.item_album, item)
is Artist -> listModel.openMenu(R.menu.item_parent, item)
is Genre -> listModel.openMenu(R.menu.item_parent, item)
@ -256,16 +256,11 @@ class SearchFragment : ListFragment<Music, FragmentSearchBinding>() {
if (menu == null) return
val directions =
when (menu) {
is Menu.ForSong ->
SearchFragmentDirections.openSongMenu(menu.menuRes, menu.music.uid)
is Menu.ForAlbum ->
SearchFragmentDirections.openAlbumMenu(menu.menuRes, menu.music.uid)
is Menu.ForArtist ->
SearchFragmentDirections.openArtistMenu(menu.menuRes, menu.music.uid)
is Menu.ForGenre ->
SearchFragmentDirections.openGenreMenu(menu.menuRes, menu.music.uid)
is Menu.ForPlaylist ->
SearchFragmentDirections.openPlaylistMenu(menu.menuRes, menu.music.uid)
is Menu.ForSong -> SearchFragmentDirections.openSongMenu(menu.parcel)
is Menu.ForAlbum -> SearchFragmentDirections.openAlbumMenu(menu.parcel)
is Menu.ForArtist -> SearchFragmentDirections.openArtistMenu(menu.parcel)
is Menu.ForGenre -> SearchFragmentDirections.openGenreMenu(menu.parcel)
is Menu.ForPlaylist -> SearchFragmentDirections.openPlaylistMenu(menu.parcel)
}
findNavController().navigateSafe(directions)
// Keyboard is no longer needed.

View file

@ -1,13 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<!-- <item-->
<!-- android:id="@+id/action_play"-->
<!-- android:title="@string/lbl_play"-->
<!-- android:icon="@drawable/ic_play_24" />-->
<!-- <item-->
<!-- android:id="@+id/action_shuffle"-->
<!-- android:title="@string/lbl_shuffle"-->
<!-- android:icon="@drawable/ic_shuffle_off_24"/>-->
<item
android:id="@+id/action_play"
android:title="@string/lbl_play"
android:icon="@drawable/ic_play_24" />
<item
android:id="@+id/action_shuffle"
android:title="@string/lbl_shuffle"
android:icon="@drawable/ic_shuffle_off_24"/>
<item
android:id="@+id/action_play_next"
android:title="@string/lbl_play_next"

View file

@ -1,13 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<!-- <item-->
<!-- android:id="@+id/action_play"-->
<!-- android:title="@string/lbl_play"-->
<!-- android:icon="@drawable/ic_play_24" />-->
<!-- <item-->
<!-- android:id="@+id/action_shuffle"-->
<!-- android:title="@string/lbl_shuffle"-->
<!-- android:icon="@drawable/ic_shuffle_off_24"/>-->
<item
android:id="@+id/action_play"
android:title="@string/lbl_play"
android:icon="@drawable/ic_play_24" />
<item
android:id="@+id/action_shuffle"
android:title="@string/lbl_shuffle"
android:icon="@drawable/ic_shuffle_off_24"/>
<item
android:id="@+id/action_play_next"
android:title="@string/lbl_play_next"

View file

@ -1,13 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<!-- <item-->
<!-- android:id="@+id/action_play"-->
<!-- android:title="@string/lbl_play"-->
<!-- android:icon="@drawable/ic_play_24" />-->
<!-- <item-->
<!-- android:id="@+id/action_shuffle"-->
<!-- android:title="@string/lbl_shuffle"-->
<!-- android:icon="@drawable/ic_shuffle_off_24" />-->
<item
android:id="@+id/action_play"
android:title="@string/lbl_play"
android:icon="@drawable/ic_play_24" />
<item
android:id="@+id/action_shuffle"
android:title="@string/lbl_shuffle"
android:icon="@drawable/ic_shuffle_off_24"/>
<item
android:id="@+id/action_play_next"
android:title="@string/lbl_play_next"

View file

@ -1,5 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/action_play"
android:title="@string/lbl_play"
android:icon="@drawable/ic_play_24" />
<item
android:id="@+id/action_shuffle"
android:title="@string/lbl_shuffle"
android:icon="@drawable/ic_shuffle_off_24"/>
<item
android:id="@+id/action_play_next"
android:title="@string/lbl_play_next"

View file

@ -351,11 +351,8 @@
android:label="song_menu_dialog"
tools:layout="@layout/dialog_menu">
<argument
android:name="menuRes"
app:argType="integer" />
<argument
android:name="songUid"
app:argType="org.oxycblt.auxio.music.Music$UID" />
android:name="parcel"
app:argType="org.oxycblt.auxio.list.Menu$ForSong$Parcel" />
</dialog>
<dialog
@ -364,11 +361,8 @@
android:label="album_menu_dialog"
tools:layout="@layout/dialog_menu">
<argument
android:name="menuRes"
app:argType="integer"/>
<argument
android:name="albumUid"
app:argType="org.oxycblt.auxio.music.Music$UID" />
android:name="parcel"
app:argType="org.oxycblt.auxio.list.Menu$ForAlbum$Parcel" />
</dialog>
<dialog
@ -377,11 +371,8 @@
android:label="artist_menu_dialog"
tools:layout="@layout/dialog_menu">
<argument
android:name="menuRes"
app:argType="integer" />
<argument
android:name="artistUid"
app:argType="org.oxycblt.auxio.music.Music$UID" />
android:name="parcel"
app:argType="org.oxycblt.auxio.list.Menu$ForArtist$Parcel" />
</dialog>
<dialog
@ -390,11 +381,8 @@
android:label="genre_menu_dialog"
tools:layout="@layout/dialog_menu">
<argument
android:name="menuRes"
app:argType="integer" />
<argument
android:name="genreUid"
app:argType="org.oxycblt.auxio.music.Music$UID" />
android:name="parcel"
app:argType="org.oxycblt.auxio.list.Menu$ForGenre$Parcel" />
</dialog>
<dialog
@ -403,11 +391,8 @@
android:label="playlist_menu_dialog"
tools:layout="@layout/dialog_menu">
<argument
android:name="menuRes"
app:argType="integer" />
<argument
android:name="playlistUid"
app:argType="org.oxycblt.auxio.music.Music$UID" />
android:name="parcel"
app:argType="org.oxycblt.auxio.list.Menu$ForPlaylist$Parcel" />
</dialog>
<fragment

View file

@ -108,8 +108,8 @@
<string-array name="entries_play_in_list_with">
<item>@string/set_play_song_from_all</item>
<item>@string/set_play_song_from_artist</item>
<item>@string/set_play_song_from_album</item>
<item>@string/set_play_song_from_artist</item>
<item>@string/set_play_song_from_genre</item>
<item>@string/set_play_song_by_itself</item>
</string-array>
@ -119,14 +119,14 @@
<item>@integer/play_song_from_album</item>
<item>@integer/play_song_from_artist</item>
<item>@integer/play_song_from_genre</item>
<item>@integer/play_song_itself</item>
<item>@integer/play_song_by_itself</item>
</integer-array>
<string-array name="entries_play_in_parent_with">
<item>@string/set_play_song_none</item>
<item>@string/set_play_song_from_all</item>
<item>@string/set_play_song_from_artist</item>
<item>@string/set_play_song_from_album</item>
<item>@string/set_play_song_from_artist</item>
<item>@string/set_play_song_from_genre</item>
<item>@string/set_play_song_by_itself</item>
</string-array>
@ -137,7 +137,7 @@
<item>@integer/play_song_from_album</item>
<item>@integer/play_song_from_artist</item>
<item>@integer/play_song_from_genre</item>
<item>@integer/play_song_itself</item>
<item>@integer/play_song_by_itself</item>
</integer-array>
<string-array name="entries_replay_gain">
@ -157,11 +157,12 @@
<integer name="theme_dark">2</integer>
<integer name="play_song_none">-2147483648</integer>
<integer name="play_song_itself">0xA11F</integer>
<integer name="play_song_from_all">0xA120</integer>
<integer name="play_song_from_album">0xA121</integer>
<integer name="play_song_from_artist">0xA122</integer>
<integer name="play_song_from_genre">0xA123</integer>
<integer name="play_song_from_all">0xA11F</integer>
<integer name="play_song_from_album">0xA120</integer>
<integer name="play_song_from_artist">0xA121</integer>
<integer name="play_song_from_genre">0xA122</integer>
<integer name="play_song_from_playlist">0xA123</integer>
<integer name="play_song_by_itself">0xA124</integer>
<integer name="replay_gain_track">0xA111</integer>
<integer name="replay_gain_album">0xA112</integer>