list: unify viewmodels
Unify the disjoint selection and menu viewmodels into a single ListViewModel. This is technically not fully done, as MenuViewModel remains as a backing element of the menu dialogs, but otherwise that's it.
This commit is contained in:
parent
b4ffffedfd
commit
7239256e27
18 changed files with 256 additions and 243 deletions
|
@ -39,7 +39,7 @@ import kotlin.math.max
|
|||
import kotlin.math.min
|
||||
import org.oxycblt.auxio.databinding.FragmentMainBinding
|
||||
import org.oxycblt.auxio.detail.DetailViewModel
|
||||
import org.oxycblt.auxio.list.selection.SelectionViewModel
|
||||
import org.oxycblt.auxio.list.ListViewModel
|
||||
import org.oxycblt.auxio.music.Music
|
||||
import org.oxycblt.auxio.music.Song
|
||||
import org.oxycblt.auxio.playback.OpenPanel
|
||||
|
@ -67,7 +67,7 @@ class MainFragment :
|
|||
ViewTreeObserver.OnPreDrawListener,
|
||||
NavController.OnDestinationChangedListener {
|
||||
private val playbackModel: PlaybackViewModel by activityViewModels()
|
||||
private val selectionModel: SelectionViewModel by activityViewModels()
|
||||
private val listModel: ListViewModel by activityViewModels()
|
||||
private val detailModel: DetailViewModel by activityViewModels()
|
||||
private var sheetBackCallback: SheetBackPressedCallback? = null
|
||||
private var detailBackCallback: DetailBackPressedCallback? = null
|
||||
|
@ -100,7 +100,7 @@ class MainFragment :
|
|||
val detailBackCallback =
|
||||
DetailBackPressedCallback(detailModel).also { detailBackCallback = it }
|
||||
val selectionBackCallback =
|
||||
SelectionBackPressedCallback(selectionModel).also { selectionBackCallback = it }
|
||||
SelectionBackPressedCallback(listModel).also { selectionBackCallback = it }
|
||||
val exploreBackCallback =
|
||||
ExploreBackPressedCallback(binding.exploreNavHost).also { exploreBackCallback = it }
|
||||
|
||||
|
@ -152,7 +152,7 @@ class MainFragment :
|
|||
|
||||
// --- VIEWMODEL SETUP ---
|
||||
collectImmediately(detailModel.editedPlaylist, detailBackCallback::invalidateEnabled)
|
||||
collectImmediately(selectionModel.selected, selectionBackCallback::invalidateEnabled)
|
||||
collectImmediately(listModel.selected, selectionBackCallback::invalidateEnabled)
|
||||
collectImmediately(playbackModel.song, ::updateSong)
|
||||
collectImmediately(playbackModel.openPanel.flow, ::handlePanel)
|
||||
}
|
||||
|
@ -289,7 +289,7 @@ class MainFragment :
|
|||
initialNavDestinationChange = true
|
||||
return
|
||||
}
|
||||
selectionModel.drop()
|
||||
listModel.dropSelection()
|
||||
}
|
||||
|
||||
private fun updateSong(song: Song?) {
|
||||
|
@ -450,11 +450,10 @@ class MainFragment :
|
|||
}
|
||||
}
|
||||
|
||||
private inner class SelectionBackPressedCallback(
|
||||
private val selectionModel: SelectionViewModel
|
||||
) : OnBackPressedCallback(false) {
|
||||
private inner class SelectionBackPressedCallback(private val listModel: ListViewModel) :
|
||||
OnBackPressedCallback(false) {
|
||||
override fun handleOnBackPressed() {
|
||||
if (selectionModel.drop()) {
|
||||
if (listModel.dropSelection()) {
|
||||
logD("Dropped selection")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -39,10 +39,9 @@ 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.ListViewModel
|
||||
import org.oxycblt.auxio.list.Menu
|
||||
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
|
||||
import org.oxycblt.auxio.music.MusicMode
|
||||
|
@ -75,8 +74,7 @@ class AlbumDetailFragment :
|
|||
AlbumDetailHeaderAdapter.Listener,
|
||||
DetailListAdapter.Listener<Song> {
|
||||
private val detailModel: DetailViewModel by activityViewModels()
|
||||
private val menuModel: MenuViewModel by activityViewModels()
|
||||
override val selectionModel: SelectionViewModel by activityViewModels()
|
||||
override val listModel: ListViewModel by activityViewModels()
|
||||
override val musicModel: MusicViewModel by activityViewModels()
|
||||
override val playbackModel: PlaybackViewModel by activityViewModels()
|
||||
// Information about what album to display is initially within the navigation arguments
|
||||
|
@ -128,8 +126,8 @@ 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(listModel.menu.flow, ::handleMenu)
|
||||
collectImmediately(listModel.selected, ::updateSelection)
|
||||
collect(musicModel.playlistDecision.flow, ::handlePlaylistDecision)
|
||||
collectImmediately(
|
||||
playbackModel.song, playbackModel.parent, playbackModel.isPlaying, ::updatePlayback)
|
||||
|
@ -187,7 +185,7 @@ class AlbumDetailFragment :
|
|||
}
|
||||
|
||||
override fun onOpenMenu(item: Song, anchor: View) {
|
||||
menuModel.open(R.menu.item_album_song, item)
|
||||
listModel.openMenu(R.menu.item_album_song, item)
|
||||
}
|
||||
|
||||
override fun onPlay() {
|
||||
|
@ -304,17 +302,16 @@ class AlbumDetailFragment :
|
|||
}
|
||||
}
|
||||
|
||||
private fun handleMenu(pendingMenu: PendingMenu?) {
|
||||
if (pendingMenu == null) return
|
||||
private fun handleMenu(menu: Menu?) {
|
||||
if (menu == 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")
|
||||
when (menu) {
|
||||
is Menu.ForSong ->
|
||||
AlbumDetailFragmentDirections.openSongMenu(menu.menuRes, menu.music.uid)
|
||||
is Menu.ForAlbum,
|
||||
is Menu.ForArtist,
|
||||
is Menu.ForGenre,
|
||||
is Menu.ForPlaylist -> error("Unexpected menu $menu")
|
||||
}
|
||||
findNavController().navigateSafe(directions)
|
||||
}
|
||||
|
|
|
@ -39,10 +39,9 @@ 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.ListViewModel
|
||||
import org.oxycblt.auxio.list.Menu
|
||||
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
|
||||
import org.oxycblt.auxio.music.Music
|
||||
|
@ -73,8 +72,7 @@ class ArtistDetailFragment :
|
|||
DetailHeaderAdapter.Listener,
|
||||
DetailListAdapter.Listener<Music> {
|
||||
private val detailModel: DetailViewModel by activityViewModels()
|
||||
private val menuModel: MenuViewModel by activityViewModels()
|
||||
override val selectionModel: SelectionViewModel by activityViewModels()
|
||||
override val listModel: ListViewModel by activityViewModels()
|
||||
override val musicModel: MusicViewModel by activityViewModels()
|
||||
override val playbackModel: PlaybackViewModel by activityViewModels()
|
||||
// Information about what artist to display is initially within the navigation arguments
|
||||
|
@ -129,8 +127,8 @@ 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(listModel.menu.flow, ::handleMenu)
|
||||
collectImmediately(listModel.selected, ::updateSelection)
|
||||
collect(musicModel.playlistDecision.flow, ::handlePlaylistDecision)
|
||||
collectImmediately(
|
||||
playbackModel.song, playbackModel.parent, playbackModel.isPlaying, ::updatePlayback)
|
||||
|
@ -198,8 +196,8 @@ class ArtistDetailFragment :
|
|||
|
||||
override fun onOpenMenu(item: Music, anchor: View) {
|
||||
when (item) {
|
||||
is Song -> menuModel.open(R.menu.item_artist_song, item)
|
||||
is Album -> menuModel.open(R.menu.item_artist_album, item)
|
||||
is Song -> listModel.openMenu(R.menu.item_artist_song, item)
|
||||
is Album -> listModel.openMenu(R.menu.item_artist_album, item)
|
||||
else -> error("Unexpected datatype: ${item::class.simpleName}")
|
||||
}
|
||||
}
|
||||
|
@ -314,19 +312,17 @@ class ArtistDetailFragment :
|
|||
}
|
||||
}
|
||||
|
||||
private fun handleMenu(pendingMenu: PendingMenu?) {
|
||||
if (pendingMenu == null) return
|
||||
private fun handleMenu(menu: Menu?) {
|
||||
if (menu == 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")
|
||||
when (menu) {
|
||||
is Menu.ForSong ->
|
||||
ArtistDetailFragmentDirections.openSongMenu(menu.menuRes, menu.music.uid)
|
||||
is Menu.ForAlbum ->
|
||||
ArtistDetailFragmentDirections.openAlbumMenu(menu.menuRes, menu.music.uid)
|
||||
is Menu.ForArtist,
|
||||
is Menu.ForGenre,
|
||||
is Menu.ForPlaylist -> error("Unexpected menu $menu")
|
||||
}
|
||||
findNavController().navigateSafe(directions)
|
||||
}
|
||||
|
|
|
@ -39,10 +39,9 @@ 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.ListViewModel
|
||||
import org.oxycblt.auxio.list.Menu
|
||||
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
|
||||
import org.oxycblt.auxio.music.Music
|
||||
|
@ -73,8 +72,7 @@ class GenreDetailFragment :
|
|||
DetailHeaderAdapter.Listener,
|
||||
DetailListAdapter.Listener<Music> {
|
||||
private val detailModel: DetailViewModel by activityViewModels()
|
||||
private val menuModel: MenuViewModel by activityViewModels()
|
||||
override val selectionModel: SelectionViewModel by activityViewModels()
|
||||
override val listModel: ListViewModel by activityViewModels()
|
||||
override val musicModel: MusicViewModel by activityViewModels()
|
||||
override val playbackModel: PlaybackViewModel by activityViewModels()
|
||||
// Information about what genre to display is initially within the navigation arguments
|
||||
|
@ -127,8 +125,8 @@ 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(listModel.menu.flow, ::handleMenu)
|
||||
collectImmediately(listModel.selected, ::updateSelection)
|
||||
collect(musicModel.playlistDecision.flow, ::handleDecision)
|
||||
collectImmediately(
|
||||
playbackModel.song, playbackModel.parent, playbackModel.isPlaying, ::updatePlayback)
|
||||
|
@ -196,8 +194,8 @@ class GenreDetailFragment :
|
|||
|
||||
override fun onOpenMenu(item: Music, anchor: View) {
|
||||
when (item) {
|
||||
is Artist -> menuModel.open(R.menu.item_parent, item)
|
||||
is Song -> menuModel.open(R.menu.item_song, item)
|
||||
is Artist -> listModel.openMenu(R.menu.item_parent, item)
|
||||
is Song -> listModel.openMenu(R.menu.item_song, item)
|
||||
else -> error("Unexpected datatype: ${item::class.simpleName}")
|
||||
}
|
||||
}
|
||||
|
@ -302,19 +300,17 @@ class GenreDetailFragment :
|
|||
}
|
||||
}
|
||||
|
||||
private fun handleMenu(pendingMenu: PendingMenu?) {
|
||||
if (pendingMenu == null) return
|
||||
private fun handleMenu(menu: Menu?) {
|
||||
if (menu == 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")
|
||||
when (menu) {
|
||||
is Menu.ForSong ->
|
||||
GenreDetailFragmentDirections.openSongMenu(menu.menuRes, menu.music.uid)
|
||||
is Menu.ForArtist ->
|
||||
GenreDetailFragmentDirections.openArtistMenu(menu.menuRes, menu.music.uid)
|
||||
is Menu.ForAlbum,
|
||||
is Menu.ForGenre,
|
||||
is Menu.ForPlaylist -> error("Unexpected menu $menu")
|
||||
}
|
||||
findNavController().navigateSafe(directions)
|
||||
}
|
||||
|
|
|
@ -43,9 +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.list.ListViewModel
|
||||
import org.oxycblt.auxio.list.Menu
|
||||
import org.oxycblt.auxio.music.Music
|
||||
import org.oxycblt.auxio.music.MusicParent
|
||||
import org.oxycblt.auxio.music.MusicViewModel
|
||||
|
@ -76,8 +75,7 @@ class PlaylistDetailFragment :
|
|||
PlaylistDetailListAdapter.Listener,
|
||||
NavController.OnDestinationChangedListener {
|
||||
private val detailModel: DetailViewModel by activityViewModels()
|
||||
private val menuModel: MenuViewModel by activityViewModels()
|
||||
override val selectionModel: SelectionViewModel by activityViewModels()
|
||||
override val listModel: ListViewModel by activityViewModels()
|
||||
override val musicModel: MusicViewModel by activityViewModels()
|
||||
override val playbackModel: PlaybackViewModel by activityViewModels()
|
||||
// Information about what playlist to display is initially within the navigation arguments
|
||||
|
@ -142,8 +140,8 @@ 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(listModel.menu.flow, ::handleMenu)
|
||||
collectImmediately(listModel.selected, ::updateSelection)
|
||||
collect(musicModel.playlistDecision.flow, ::handleDecision)
|
||||
collectImmediately(
|
||||
playbackModel.song, playbackModel.parent, playbackModel.isPlaying, ::updatePlayback)
|
||||
|
@ -239,7 +237,7 @@ class PlaylistDetailFragment :
|
|||
}
|
||||
|
||||
override fun onOpenMenu(item: Song, anchor: View) {
|
||||
menuModel.open(R.menu.item_playlist_song, item)
|
||||
listModel.openMenu(R.menu.item_playlist_song, item)
|
||||
}
|
||||
|
||||
override fun onPlay() {
|
||||
|
@ -286,7 +284,7 @@ class PlaylistDetailFragment :
|
|||
private fun updateEditedList(editedPlaylist: List<Song>?) {
|
||||
playlistListAdapter.setEditing(editedPlaylist != null)
|
||||
playlistHeaderAdapter.setEditedPlaylist(editedPlaylist)
|
||||
selectionModel.drop()
|
||||
listModel.dropSelection()
|
||||
|
||||
if (editedPlaylist != null) {
|
||||
logD("Updating save button state")
|
||||
|
@ -349,17 +347,16 @@ class PlaylistDetailFragment :
|
|||
}
|
||||
}
|
||||
|
||||
private fun handleMenu(pendingMenu: PendingMenu?) {
|
||||
if (pendingMenu == null) return
|
||||
private fun handleMenu(menu: Menu?) {
|
||||
if (menu == 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")
|
||||
when (menu) {
|
||||
is Menu.ForSong ->
|
||||
PlaylistDetailFragmentDirections.openSongMenu(menu.menuRes, menu.music.uid)
|
||||
is Menu.ForArtist,
|
||||
is Menu.ForAlbum,
|
||||
is Menu.ForGenre,
|
||||
is Menu.ForPlaylist -> error("Unexpected menu $menu")
|
||||
}
|
||||
findNavController().navigateSafe(directions)
|
||||
}
|
||||
|
@ -421,7 +418,7 @@ class PlaylistDetailFragment :
|
|||
logD("Currently editing playlist, showing edit toolbar")
|
||||
R.id.detail_edit_toolbar
|
||||
}
|
||||
selectionModel.selected.value.isNotEmpty() -> {
|
||||
listModel.selected.value.isNotEmpty() -> {
|
||||
logD("Currently selecting, showing selection toolbar")
|
||||
R.id.detail_selection_toolbar
|
||||
}
|
||||
|
|
|
@ -55,11 +55,10 @@ import org.oxycblt.auxio.home.list.PlaylistListFragment
|
|||
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.ListViewModel
|
||||
import org.oxycblt.auxio.list.Menu
|
||||
import org.oxycblt.auxio.list.SelectionFragment
|
||||
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
|
||||
import org.oxycblt.auxio.music.IndexingState
|
||||
import org.oxycblt.auxio.music.Music
|
||||
|
@ -89,8 +88,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 listModel: ListViewModel by activityViewModels()
|
||||
override val musicModel: MusicViewModel by activityViewModels()
|
||||
override val playbackModel: PlaybackViewModel by activityViewModels()
|
||||
private val homeModel: HomeViewModel by activityViewModels()
|
||||
|
@ -104,8 +102,7 @@ class HomeFragment :
|
|||
// Orientation change will wipe whatever transition we were using prior, which will
|
||||
// result in no transition when the user navigates back. Make sure we re-initialize
|
||||
// our transitions.
|
||||
val id = savedInstanceState.getInt(KEY_LAST_TRANSITION_ID, -2)
|
||||
when (id) {
|
||||
when (val id = savedInstanceState.getInt(KEY_LAST_TRANSITION_ID, -2)) {
|
||||
-2 -> {}
|
||||
-1 -> applyFadeTransition()
|
||||
else -> applyAxisTransition(id)
|
||||
|
@ -178,8 +175,8 @@ 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)
|
||||
collect(listModel.menu.flow, ::handleMenu)
|
||||
collectImmediately(listModel.selected, ::updateSelection)
|
||||
collectImmediately(musicModel.indexingState, ::updateIndexerState)
|
||||
collect(musicModel.playlistDecision.flow, ::handleDecision)
|
||||
collect(detailModel.toShow.flow, ::handleShow)
|
||||
|
@ -582,22 +579,19 @@ class HomeFragment :
|
|||
}
|
||||
}
|
||||
|
||||
private fun handleMenu(pendingMenu: PendingMenu?) {
|
||||
if (pendingMenu == null) return
|
||||
private fun handleMenu(menu: Menu?) {
|
||||
if (menu == 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)
|
||||
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)
|
||||
}
|
||||
findNavController().navigateSafe(directions)
|
||||
}
|
||||
|
|
|
@ -32,12 +32,11 @@ import org.oxycblt.auxio.detail.DetailViewModel
|
|||
import org.oxycblt.auxio.home.HomeViewModel
|
||||
import org.oxycblt.auxio.home.fastscroll.FastScrollRecyclerView
|
||||
import org.oxycblt.auxio.list.ListFragment
|
||||
import org.oxycblt.auxio.list.ListViewModel
|
||||
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
|
||||
import org.oxycblt.auxio.music.Music
|
||||
import org.oxycblt.auxio.music.MusicMode
|
||||
|
@ -61,8 +60,7 @@ class AlbumListFragment :
|
|||
FastScrollRecyclerView.PopupProvider {
|
||||
private val homeModel: HomeViewModel by activityViewModels()
|
||||
private val detailModel: DetailViewModel by activityViewModels()
|
||||
private val menuModel: MenuViewModel by activityViewModels()
|
||||
override val selectionModel: SelectionViewModel by activityViewModels()
|
||||
override val listModel: ListViewModel by activityViewModels()
|
||||
override val musicModel: MusicViewModel by activityViewModels()
|
||||
override val playbackModel: PlaybackViewModel by activityViewModels()
|
||||
private val albumAdapter = AlbumAdapter(this)
|
||||
|
@ -84,7 +82,7 @@ class AlbumListFragment :
|
|||
}
|
||||
|
||||
collectImmediately(homeModel.albumsList, ::updateAlbums)
|
||||
collectImmediately(selectionModel.selected, ::updateSelection)
|
||||
collectImmediately(listModel.selected, ::updateSelection)
|
||||
collectImmediately(
|
||||
playbackModel.song, playbackModel.parent, playbackModel.isPlaying, ::updatePlayback)
|
||||
}
|
||||
|
@ -144,7 +142,7 @@ class AlbumListFragment :
|
|||
}
|
||||
|
||||
override fun onOpenMenu(item: Album, anchor: View) {
|
||||
menuModel.open(R.menu.item_album, item)
|
||||
listModel.openMenu(R.menu.item_album, item)
|
||||
}
|
||||
|
||||
private fun updateAlbums(albums: List<Album>) {
|
||||
|
|
|
@ -30,12 +30,11 @@ import org.oxycblt.auxio.detail.DetailViewModel
|
|||
import org.oxycblt.auxio.home.HomeViewModel
|
||||
import org.oxycblt.auxio.home.fastscroll.FastScrollRecyclerView
|
||||
import org.oxycblt.auxio.list.ListFragment
|
||||
import org.oxycblt.auxio.list.ListViewModel
|
||||
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
|
||||
import org.oxycblt.auxio.music.Music
|
||||
import org.oxycblt.auxio.music.MusicMode
|
||||
|
@ -59,8 +58,7 @@ class ArtistListFragment :
|
|||
FastScrollRecyclerView.Listener {
|
||||
private val homeModel: HomeViewModel by activityViewModels()
|
||||
private val detailModel: DetailViewModel by activityViewModels()
|
||||
private val menuModel: MenuViewModel by activityViewModels()
|
||||
override val selectionModel: SelectionViewModel by activityViewModels()
|
||||
override val listModel: ListViewModel by activityViewModels()
|
||||
override val musicModel: MusicViewModel by activityViewModels()
|
||||
override val playbackModel: PlaybackViewModel by activityViewModels()
|
||||
private val artistAdapter = ArtistAdapter(this)
|
||||
|
@ -79,7 +77,7 @@ class ArtistListFragment :
|
|||
}
|
||||
|
||||
collectImmediately(homeModel.artistsList, ::updateArtists)
|
||||
collectImmediately(selectionModel.selected, ::updateSelection)
|
||||
collectImmediately(listModel.selected, ::updateSelection)
|
||||
collectImmediately(
|
||||
playbackModel.song, playbackModel.parent, playbackModel.isPlaying, ::updatePlayback)
|
||||
}
|
||||
|
@ -120,7 +118,7 @@ class ArtistListFragment :
|
|||
}
|
||||
|
||||
override fun onOpenMenu(item: Artist, anchor: View) {
|
||||
menuModel.open(R.menu.item_parent, item)
|
||||
listModel.openMenu(R.menu.item_parent, item)
|
||||
}
|
||||
|
||||
private fun updateArtists(artists: List<Artist>) {
|
||||
|
|
|
@ -30,12 +30,11 @@ import org.oxycblt.auxio.detail.DetailViewModel
|
|||
import org.oxycblt.auxio.home.HomeViewModel
|
||||
import org.oxycblt.auxio.home.fastscroll.FastScrollRecyclerView
|
||||
import org.oxycblt.auxio.list.ListFragment
|
||||
import org.oxycblt.auxio.list.ListViewModel
|
||||
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
|
||||
import org.oxycblt.auxio.music.Music
|
||||
import org.oxycblt.auxio.music.MusicMode
|
||||
|
@ -58,8 +57,7 @@ class GenreListFragment :
|
|||
FastScrollRecyclerView.Listener {
|
||||
private val homeModel: HomeViewModel by activityViewModels()
|
||||
private val detailModel: DetailViewModel by activityViewModels()
|
||||
private val menuModel: MenuViewModel by activityViewModels()
|
||||
override val selectionModel: SelectionViewModel by activityViewModels()
|
||||
override val listModel: ListViewModel by activityViewModels()
|
||||
override val musicModel: MusicViewModel by activityViewModels()
|
||||
override val playbackModel: PlaybackViewModel by activityViewModels()
|
||||
private val genreAdapter = GenreAdapter(this)
|
||||
|
@ -78,7 +76,7 @@ class GenreListFragment :
|
|||
}
|
||||
|
||||
collectImmediately(homeModel.genresList, ::updateGenres)
|
||||
collectImmediately(selectionModel.selected, ::updateSelection)
|
||||
collectImmediately(listModel.selected, ::updateSelection)
|
||||
collectImmediately(
|
||||
playbackModel.song, playbackModel.parent, playbackModel.isPlaying, ::updatePlayback)
|
||||
}
|
||||
|
@ -119,7 +117,7 @@ class GenreListFragment :
|
|||
}
|
||||
|
||||
override fun onOpenMenu(item: Genre, anchor: View) {
|
||||
menuModel.open(R.menu.item_parent, item)
|
||||
listModel.openMenu(R.menu.item_parent, item)
|
||||
}
|
||||
|
||||
private fun updateGenres(genres: List<Genre>) {
|
||||
|
|
|
@ -29,12 +29,11 @@ import org.oxycblt.auxio.detail.DetailViewModel
|
|||
import org.oxycblt.auxio.home.HomeViewModel
|
||||
import org.oxycblt.auxio.home.fastscroll.FastScrollRecyclerView
|
||||
import org.oxycblt.auxio.list.ListFragment
|
||||
import org.oxycblt.auxio.list.ListViewModel
|
||||
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
|
||||
import org.oxycblt.auxio.music.MusicMode
|
||||
import org.oxycblt.auxio.music.MusicParent
|
||||
|
@ -56,8 +55,7 @@ class PlaylistListFragment :
|
|||
FastScrollRecyclerView.Listener {
|
||||
private val homeModel: HomeViewModel by activityViewModels()
|
||||
private val detailModel: DetailViewModel by activityViewModels()
|
||||
private val menuModel: MenuViewModel by activityViewModels()
|
||||
override val selectionModel: SelectionViewModel by activityViewModels()
|
||||
override val listModel: ListViewModel by activityViewModels()
|
||||
override val musicModel: MusicViewModel by activityViewModels()
|
||||
override val playbackModel: PlaybackViewModel by activityViewModels()
|
||||
private val playlistAdapter = PlaylistAdapter(this)
|
||||
|
@ -76,7 +74,7 @@ class PlaylistListFragment :
|
|||
}
|
||||
|
||||
collectImmediately(homeModel.playlistsList, ::updatePlaylists)
|
||||
collectImmediately(selectionModel.selected, ::updateSelection)
|
||||
collectImmediately(listModel.selected, ::updateSelection)
|
||||
collectImmediately(
|
||||
playbackModel.song, playbackModel.parent, playbackModel.isPlaying, ::updatePlayback)
|
||||
}
|
||||
|
@ -117,7 +115,7 @@ class PlaylistListFragment :
|
|||
}
|
||||
|
||||
override fun onOpenMenu(item: Playlist, anchor: View) {
|
||||
menuModel.open(R.menu.item_playlist, item)
|
||||
listModel.openMenu(R.menu.item_playlist, item)
|
||||
}
|
||||
|
||||
private fun updatePlaylists(playlists: List<Playlist>) {
|
||||
|
|
|
@ -31,12 +31,11 @@ import org.oxycblt.auxio.databinding.FragmentHomeListBinding
|
|||
import org.oxycblt.auxio.home.HomeViewModel
|
||||
import org.oxycblt.auxio.home.fastscroll.FastScrollRecyclerView
|
||||
import org.oxycblt.auxio.list.ListFragment
|
||||
import org.oxycblt.auxio.list.ListViewModel
|
||||
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
|
||||
import org.oxycblt.auxio.music.MusicMode
|
||||
import org.oxycblt.auxio.music.MusicParent
|
||||
|
@ -58,8 +57,7 @@ class SongListFragment :
|
|||
FastScrollRecyclerView.PopupProvider,
|
||||
FastScrollRecyclerView.Listener {
|
||||
private val homeModel: HomeViewModel by activityViewModels()
|
||||
private val menuModel: MenuViewModel by activityViewModels()
|
||||
override val selectionModel: SelectionViewModel by activityViewModels()
|
||||
override val listModel: ListViewModel by activityViewModels()
|
||||
override val musicModel: MusicViewModel by activityViewModels()
|
||||
override val playbackModel: PlaybackViewModel by activityViewModels()
|
||||
private val songAdapter = SongAdapter(this)
|
||||
|
@ -81,7 +79,7 @@ class SongListFragment :
|
|||
}
|
||||
|
||||
collectImmediately(homeModel.songsList, ::updateSongs)
|
||||
collectImmediately(selectionModel.selected, ::updateSelection)
|
||||
collectImmediately(listModel.selected, ::updateSelection)
|
||||
collectImmediately(
|
||||
playbackModel.song, playbackModel.parent, playbackModel.isPlaying, ::updatePlayback)
|
||||
}
|
||||
|
@ -143,7 +141,7 @@ class SongListFragment :
|
|||
}
|
||||
|
||||
override fun onOpenMenu(item: Song, anchor: View) {
|
||||
menuModel.open(R.menu.item_song, item)
|
||||
listModel.openMenu(R.menu.item_song, item)
|
||||
}
|
||||
|
||||
private fun updateSongs(songs: List<Song>) {
|
||||
|
|
|
@ -24,7 +24,6 @@ import androidx.appcompat.widget.PopupMenu
|
|||
import androidx.core.view.MenuCompat
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.viewbinding.ViewBinding
|
||||
import org.oxycblt.auxio.list.selection.SelectionFragment
|
||||
import org.oxycblt.auxio.music.Music
|
||||
import org.oxycblt.auxio.util.logD
|
||||
|
||||
|
@ -52,9 +51,9 @@ abstract class ListFragment<in T : Music, VB : ViewBinding> :
|
|||
abstract fun onRealClick(item: T)
|
||||
|
||||
final override fun onClick(item: T, viewHolder: RecyclerView.ViewHolder) {
|
||||
if (selectionModel.selected.value.isNotEmpty()) {
|
||||
if (listModel.selected.value.isNotEmpty()) {
|
||||
// Map clicking an item to selecting an item when items are already selected.
|
||||
selectionModel.select(item)
|
||||
listModel.select(item)
|
||||
} else {
|
||||
// Delegate to the concrete implementation when we don't select the item.
|
||||
onRealClick(item)
|
||||
|
@ -62,7 +61,7 @@ abstract class ListFragment<in T : Music, VB : ViewBinding> :
|
|||
}
|
||||
|
||||
final override fun onSelect(item: T) {
|
||||
selectionModel.select(item)
|
||||
listModel.select(item)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* Copyright (c) 2022 Auxio Project
|
||||
* SelectionViewModel.kt is part of Auxio.
|
||||
* Copyright (c) 2023 Auxio Project
|
||||
* ListViewModel.kt is part of Auxio.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
@ -16,8 +16,9 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.oxycblt.auxio.list.selection
|
||||
package org.oxycblt.auxio.list
|
||||
|
||||
import androidx.annotation.MenuRes
|
||||
import androidx.lifecycle.ViewModel
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import javax.inject.Inject
|
||||
|
@ -32,15 +33,18 @@ 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.util.Event
|
||||
import org.oxycblt.auxio.util.MutableEvent
|
||||
import org.oxycblt.auxio.util.logD
|
||||
import org.oxycblt.auxio.util.logW
|
||||
|
||||
/**
|
||||
* A [ViewModel] that manages the current selection.
|
||||
* A [ViewModel] that orchestrates menu dialogs and selection state.
|
||||
*
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
@HiltViewModel
|
||||
class SelectionViewModel
|
||||
class ListViewModel
|
||||
@Inject
|
||||
constructor(
|
||||
private val musicRepository: MusicRepository,
|
||||
|
@ -51,6 +55,9 @@ constructor(
|
|||
val selected: StateFlow<List<Music>>
|
||||
get() = _selected
|
||||
|
||||
private val _Menu = MutableEvent<Menu>()
|
||||
val menu: Event<Menu> = _Menu
|
||||
|
||||
init {
|
||||
musicRepository.addUpdateListener(this)
|
||||
}
|
||||
|
@ -105,7 +112,7 @@ constructor(
|
|||
*
|
||||
* @return A list of [Song]s collated from each item selected.
|
||||
*/
|
||||
fun take(): List<Song> {
|
||||
fun takeSelection(): List<Song> {
|
||||
logD("Taking selection")
|
||||
return _selected.value
|
||||
.flatMap {
|
||||
|
@ -125,8 +132,88 @@ constructor(
|
|||
*
|
||||
* @return true if the prior selection was non-empty, false otherwise.
|
||||
*/
|
||||
fun drop(): Boolean {
|
||||
fun dropSelection(): Boolean {
|
||||
logD("Dropping selection [empty=${_selected.value.isEmpty()}]")
|
||||
return _selected.value.isNotEmpty().also { _selected.value = listOf() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Open a menu for a [Song]. This is not a popup menu, instead actually a dialog of menu options
|
||||
* with additional information.
|
||||
*
|
||||
* @param menuRes The resource of the menu to use.
|
||||
* @param song The [Song] to show.
|
||||
*/
|
||||
fun openMenu(@MenuRes menuRes: Int, song: Song) {
|
||||
logD("Opening menu for $song")
|
||||
openImpl(Menu.ForSong(menuRes, song))
|
||||
}
|
||||
|
||||
/**
|
||||
* Open a menu for a [Album]. This is not a popup menu, instead actually a dialog of menu
|
||||
* options with additional information.
|
||||
*
|
||||
* @param menuRes The resource of the menu to use.
|
||||
* @param album The [Album] to show.
|
||||
*/
|
||||
fun openMenu(@MenuRes menuRes: Int, album: Album) {
|
||||
logD("Opening menu for $album")
|
||||
openImpl(Menu.ForAlbum(menuRes, album))
|
||||
}
|
||||
|
||||
/**
|
||||
* Open a menu for a [Artist]. This is not a popup menu, instead actually a dialog of menu
|
||||
* options with additional information.
|
||||
*
|
||||
* @param menuRes The resource of the menu to use.
|
||||
* @param artist The [Artist] to show.
|
||||
*/
|
||||
fun openMenu(@MenuRes menuRes: Int, artist: Artist) {
|
||||
logD("Opening menu for $artist")
|
||||
openImpl(Menu.ForArtist(menuRes, artist))
|
||||
}
|
||||
|
||||
/**
|
||||
* Open a menu for a [Genre]. This is not a popup menu, instead actually a dialog of menu
|
||||
* options with additional information.
|
||||
*
|
||||
* @param menuRes The resource of the menu to use.
|
||||
* @param genre The [Genre] to show.
|
||||
*/
|
||||
fun openMenu(@MenuRes menuRes: Int, genre: Genre) {
|
||||
logD("Opening menu for $genre")
|
||||
openImpl(Menu.ForGenre(menuRes, genre))
|
||||
}
|
||||
|
||||
/**
|
||||
* Open a menu for a [Playlist]. This is not a popup menu, instead actually a dialog of menu
|
||||
* options with additional information.
|
||||
*
|
||||
* @param menuRes The resource of the menu to use.
|
||||
* @param playlist The [Playlist] to show.
|
||||
*/
|
||||
fun openMenu(@MenuRes menuRes: Int, playlist: Playlist) {
|
||||
logD("Opening menu for $playlist")
|
||||
openImpl(Menu.ForPlaylist(menuRes, playlist))
|
||||
}
|
||||
|
||||
private fun openImpl(menu: Menu) {
|
||||
val existing = _Menu.flow.value
|
||||
if (existing != null) {
|
||||
logW("Already opening $existing, ignoring $menu")
|
||||
return
|
||||
}
|
||||
_Menu.put(menu)
|
||||
}
|
||||
}
|
||||
|
||||
sealed interface Menu {
|
||||
val menuRes: Int
|
||||
val music: Music
|
||||
|
||||
class ForSong(@MenuRes override val menuRes: Int, override val music: Song) : Menu
|
||||
class ForAlbum(@MenuRes override val menuRes: Int, override val music: Album) : Menu
|
||||
class ForArtist(@MenuRes override val menuRes: Int, override val music: Artist) : Menu
|
||||
class ForGenre(@MenuRes override val menuRes: Int, override val music: Genre) : Menu
|
||||
class ForPlaylist(@MenuRes override val menuRes: Int, override val music: Playlist) : Menu
|
||||
}
|
|
@ -16,7 +16,7 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.oxycblt.auxio.list.selection
|
||||
package org.oxycblt.auxio.list
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.MenuItem
|
||||
|
@ -36,7 +36,7 @@ import org.oxycblt.auxio.util.showToast
|
|||
*/
|
||||
abstract class SelectionFragment<VB : ViewBinding> :
|
||||
ViewBindingFragment<VB>(), Toolbar.OnMenuItemClickListener {
|
||||
protected abstract val selectionModel: SelectionViewModel
|
||||
protected abstract val listModel: ListViewModel
|
||||
protected abstract val musicModel: MusicViewModel
|
||||
protected abstract val playbackModel: PlaybackViewModel
|
||||
|
||||
|
@ -46,7 +46,7 @@ abstract class SelectionFragment<VB : ViewBinding> :
|
|||
super.onBindingCreated(binding, savedInstanceState)
|
||||
getSelectionToolbar(binding)?.apply {
|
||||
// Add cancel and menu item listeners to manage what occurs with the selection.
|
||||
setNavigationOnClickListener { selectionModel.drop() }
|
||||
setNavigationOnClickListener { listModel.dropSelection() }
|
||||
setOnMenuItemClickListener(this@SelectionFragment)
|
||||
}
|
||||
}
|
||||
|
@ -59,29 +59,29 @@ abstract class SelectionFragment<VB : ViewBinding> :
|
|||
override fun onMenuItemClick(item: MenuItem) =
|
||||
when (item.itemId) {
|
||||
R.id.action_selection_play_next -> {
|
||||
playbackModel.playNext(selectionModel.take())
|
||||
playbackModel.playNext(listModel.takeSelection())
|
||||
requireContext().showToast(R.string.lng_queue_added)
|
||||
true
|
||||
}
|
||||
R.id.action_selection_playlist_add -> {
|
||||
musicModel.addToPlaylist(selectionModel.take())
|
||||
musicModel.addToPlaylist(listModel.takeSelection())
|
||||
true
|
||||
}
|
||||
R.id.action_selection_queue_add -> {
|
||||
playbackModel.addToQueue(selectionModel.take())
|
||||
playbackModel.addToQueue(listModel.takeSelection())
|
||||
requireContext().showToast(R.string.lng_queue_added)
|
||||
true
|
||||
}
|
||||
R.id.action_selection_play -> {
|
||||
playbackModel.play(selectionModel.take())
|
||||
playbackModel.play(listModel.takeSelection())
|
||||
true
|
||||
}
|
||||
R.id.action_selection_shuffle -> {
|
||||
playbackModel.shuffle(selectionModel.take())
|
||||
playbackModel.shuffle(listModel.takeSelection())
|
||||
true
|
||||
}
|
||||
R.id.action_selection_share -> {
|
||||
requireContext().share(selectionModel.take())
|
||||
requireContext().share(listModel.takeSelection())
|
||||
true
|
||||
}
|
||||
else -> false
|
|
@ -29,6 +29,7 @@ import androidx.navigation.fragment.findNavController
|
|||
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.adapter.UpdateInstructions
|
||||
import org.oxycblt.auxio.music.Music
|
||||
import org.oxycblt.auxio.ui.ViewBindingBottomSheetDialogFragment
|
||||
|
@ -44,6 +45,7 @@ import org.oxycblt.auxio.util.logD
|
|||
abstract class MenuDialogFragment<T : Music> :
|
||||
ViewBindingBottomSheetDialogFragment<DialogMenuBinding>(), ClickableListListener<MenuItem> {
|
||||
protected abstract val menuModel: MenuViewModel
|
||||
protected abstract val listModel: ListViewModel
|
||||
private val menuAdapter = MenuItemAdapter(@Suppress("LeakingThis") this)
|
||||
|
||||
abstract val menuRes: Int
|
||||
|
@ -66,7 +68,7 @@ abstract class MenuDialogFragment<T : Music> :
|
|||
}
|
||||
|
||||
// --- VIEWMODEL SETUP ---
|
||||
menuModel.pendingMenu.consume()
|
||||
listModel.menu.consume()
|
||||
menuModel.setCurrentMenu(uid)
|
||||
collectImmediately(menuModel.currentMusic, this::updateMusic)
|
||||
}
|
||||
|
|
|
@ -20,11 +20,13 @@ package org.oxycblt.auxio.list.menu
|
|||
|
||||
import android.view.MenuItem
|
||||
import androidx.fragment.app.activityViewModels
|
||||
import androidx.fragment.app.viewModels
|
||||
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.list.ListViewModel
|
||||
import org.oxycblt.auxio.music.Album
|
||||
import org.oxycblt.auxio.music.Artist
|
||||
import org.oxycblt.auxio.music.Genre
|
||||
|
@ -41,6 +43,7 @@ import org.oxycblt.auxio.util.showToast
|
|||
@AndroidEntryPoint
|
||||
class SongMenuDialogFragment : MenuDialogFragment<Song>() {
|
||||
override val menuModel: MenuViewModel by activityViewModels()
|
||||
override val listModel: ListViewModel by activityViewModels()
|
||||
private val detailModel: DetailViewModel by activityViewModels()
|
||||
private val musicModel: MusicViewModel by activityViewModels()
|
||||
private val playbackModel: PlaybackViewModel by activityViewModels()
|
||||
|
@ -83,7 +86,8 @@ class SongMenuDialogFragment : MenuDialogFragment<Song>() {
|
|||
|
||||
@AndroidEntryPoint
|
||||
class AlbumMenuDialogFragment : MenuDialogFragment<Album>() {
|
||||
override val menuModel: MenuViewModel by activityViewModels()
|
||||
override val menuModel: MenuViewModel by viewModels()
|
||||
override val listModel: ListViewModel by activityViewModels()
|
||||
private val detailModel: DetailViewModel by activityViewModels()
|
||||
private val musicModel: MusicViewModel by activityViewModels()
|
||||
private val playbackModel: PlaybackViewModel by activityViewModels()
|
||||
|
@ -127,7 +131,8 @@ class AlbumMenuDialogFragment : MenuDialogFragment<Album>() {
|
|||
|
||||
@AndroidEntryPoint
|
||||
class ArtistMenuDialogFragment : MenuDialogFragment<Artist>() {
|
||||
override val menuModel: MenuViewModel by activityViewModels()
|
||||
override val menuModel: MenuViewModel by viewModels()
|
||||
override val listModel: ListViewModel by activityViewModels()
|
||||
private val detailModel: DetailViewModel by activityViewModels()
|
||||
private val musicModel: MusicViewModel by activityViewModels()
|
||||
private val playbackModel: PlaybackViewModel by activityViewModels()
|
||||
|
@ -189,7 +194,8 @@ class ArtistMenuDialogFragment : MenuDialogFragment<Artist>() {
|
|||
|
||||
@AndroidEntryPoint
|
||||
class GenreMenuDialogFragment : MenuDialogFragment<Genre>() {
|
||||
override val menuModel: MenuViewModel by activityViewModels()
|
||||
override val menuModel: MenuViewModel by viewModels()
|
||||
override val listModel: ListViewModel by activityViewModels()
|
||||
private val detailModel: DetailViewModel by activityViewModels()
|
||||
private val musicModel: MusicViewModel by activityViewModels()
|
||||
private val playbackModel: PlaybackViewModel by activityViewModels()
|
||||
|
@ -236,7 +242,8 @@ class GenreMenuDialogFragment : MenuDialogFragment<Genre>() {
|
|||
|
||||
@AndroidEntryPoint
|
||||
class PlaylistMenuDialogFragment : MenuDialogFragment<Playlist>() {
|
||||
override val menuModel: MenuViewModel by activityViewModels()
|
||||
override val menuModel: MenuViewModel by viewModels()
|
||||
override val listModel: ListViewModel by activityViewModels()
|
||||
private val detailModel: DetailViewModel by activityViewModels()
|
||||
private val musicModel: MusicViewModel by activityViewModels()
|
||||
private val playbackModel: PlaybackViewModel by activityViewModels()
|
||||
|
|
|
@ -18,29 +18,18 @@
|
|||
|
||||
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
|
||||
|
||||
|
@ -56,27 +45,6 @@ class MenuViewModel @Inject constructor(private val musicRepository: MusicReposi
|
|||
musicRepository.removeUpdateListener(this)
|
||||
}
|
||||
|
||||
fun open(@MenuRes menuRes: Int, song: Song) = openImpl(PendingMenu.ForSong(menuRes, song))
|
||||
|
||||
fun open(@MenuRes menuRes: Int, album: Album) = openImpl(PendingMenu.ForAlbum(menuRes, album))
|
||||
|
||||
fun open(@MenuRes menuRes: Int, artist: Artist) =
|
||||
openImpl(PendingMenu.ForArtist(menuRes, artist))
|
||||
|
||||
fun open(@MenuRes menuRes: Int, genre: Genre) = openImpl(PendingMenu.ForGenre(menuRes, genre))
|
||||
|
||||
fun open(@MenuRes menuRes: Int, playlist: Playlist) =
|
||||
openImpl(PendingMenu.ForPlaylist(menuRes, playlist))
|
||||
|
||||
private fun openImpl(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) {
|
||||
|
@ -84,15 +52,3 @@ class MenuViewModel @Inject constructor(private val musicRepository: MusicReposi
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
|
|
@ -40,9 +40,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.list.ListViewModel
|
||||
import org.oxycblt.auxio.list.Menu
|
||||
import org.oxycblt.auxio.music.Album
|
||||
import org.oxycblt.auxio.music.Artist
|
||||
import org.oxycblt.auxio.music.Genre
|
||||
|
@ -74,8 +73,7 @@ import org.oxycblt.auxio.util.setFullWidthLookup
|
|||
class SearchFragment : ListFragment<Music, FragmentSearchBinding>() {
|
||||
private val searchModel: SearchViewModel by viewModels()
|
||||
private val detailModel: DetailViewModel by activityViewModels()
|
||||
private val menuModel: MenuViewModel by activityViewModels()
|
||||
override val selectionModel: SelectionViewModel by activityViewModels()
|
||||
override val listModel: ListViewModel by activityViewModels()
|
||||
override val playbackModel: PlaybackViewModel by activityViewModels()
|
||||
override val musicModel: MusicViewModel by activityViewModels()
|
||||
private val searchAdapter = SearchAdapter(this)
|
||||
|
@ -141,8 +139,8 @@ class SearchFragment : ListFragment<Music, FragmentSearchBinding>() {
|
|||
// --- VIEWMODEL SETUP ---
|
||||
|
||||
collectImmediately(searchModel.searchResults, ::updateSearchResults)
|
||||
collect(menuModel.pendingMenu.flow, ::handleMenu)
|
||||
collectImmediately(selectionModel.selected, ::updateSelection)
|
||||
collectImmediately(listModel.selected, ::updateSelection)
|
||||
collect(listModel.menu.flow, ::handleMenu)
|
||||
collect(musicModel.playlistDecision.flow, ::handleDecision)
|
||||
collectImmediately(
|
||||
playbackModel.song, playbackModel.parent, playbackModel.isPlaying, ::updatePlayback)
|
||||
|
@ -186,11 +184,11 @@ class SearchFragment : ListFragment<Music, FragmentSearchBinding>() {
|
|||
|
||||
override fun onOpenMenu(item: Music, anchor: View) {
|
||||
when (item) {
|
||||
is Song -> menuModel.open(R.menu.item_song, item)
|
||||
is Album -> menuModel.open(R.menu.item_album, item)
|
||||
is Artist -> menuModel.open(R.menu.item_parent, item)
|
||||
is Genre -> menuModel.open(R.menu.item_parent, item)
|
||||
is Playlist -> menuModel.open(R.menu.item_playlist, item)
|
||||
is Song -> listModel.openMenu(R.menu.item_song, item)
|
||||
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)
|
||||
is Playlist -> listModel.openMenu(R.menu.item_playlist, item)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -262,25 +260,20 @@ class SearchFragment : ListFragment<Music, FragmentSearchBinding>() {
|
|||
hideKeyboard()
|
||||
}
|
||||
|
||||
private fun handleMenu(pendingMenu: PendingMenu?) {
|
||||
if (pendingMenu == null) return
|
||||
private fun handleMenu(menu: Menu?) {
|
||||
if (menu == 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)
|
||||
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)
|
||||
}
|
||||
findNavController().navigateSafe(directions)
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue