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 kotlin.math.min
|
||||||
import org.oxycblt.auxio.databinding.FragmentMainBinding
|
import org.oxycblt.auxio.databinding.FragmentMainBinding
|
||||||
import org.oxycblt.auxio.detail.DetailViewModel
|
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.Music
|
||||||
import org.oxycblt.auxio.music.Song
|
import org.oxycblt.auxio.music.Song
|
||||||
import org.oxycblt.auxio.playback.OpenPanel
|
import org.oxycblt.auxio.playback.OpenPanel
|
||||||
|
@ -67,7 +67,7 @@ class MainFragment :
|
||||||
ViewTreeObserver.OnPreDrawListener,
|
ViewTreeObserver.OnPreDrawListener,
|
||||||
NavController.OnDestinationChangedListener {
|
NavController.OnDestinationChangedListener {
|
||||||
private val playbackModel: PlaybackViewModel by activityViewModels()
|
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 val detailModel: DetailViewModel by activityViewModels()
|
||||||
private var sheetBackCallback: SheetBackPressedCallback? = null
|
private var sheetBackCallback: SheetBackPressedCallback? = null
|
||||||
private var detailBackCallback: DetailBackPressedCallback? = null
|
private var detailBackCallback: DetailBackPressedCallback? = null
|
||||||
|
@ -100,7 +100,7 @@ class MainFragment :
|
||||||
val detailBackCallback =
|
val detailBackCallback =
|
||||||
DetailBackPressedCallback(detailModel).also { detailBackCallback = it }
|
DetailBackPressedCallback(detailModel).also { detailBackCallback = it }
|
||||||
val selectionBackCallback =
|
val selectionBackCallback =
|
||||||
SelectionBackPressedCallback(selectionModel).also { selectionBackCallback = it }
|
SelectionBackPressedCallback(listModel).also { selectionBackCallback = it }
|
||||||
val exploreBackCallback =
|
val exploreBackCallback =
|
||||||
ExploreBackPressedCallback(binding.exploreNavHost).also { exploreBackCallback = it }
|
ExploreBackPressedCallback(binding.exploreNavHost).also { exploreBackCallback = it }
|
||||||
|
|
||||||
|
@ -152,7 +152,7 @@ class MainFragment :
|
||||||
|
|
||||||
// --- VIEWMODEL SETUP ---
|
// --- VIEWMODEL SETUP ---
|
||||||
collectImmediately(detailModel.editedPlaylist, detailBackCallback::invalidateEnabled)
|
collectImmediately(detailModel.editedPlaylist, detailBackCallback::invalidateEnabled)
|
||||||
collectImmediately(selectionModel.selected, selectionBackCallback::invalidateEnabled)
|
collectImmediately(listModel.selected, selectionBackCallback::invalidateEnabled)
|
||||||
collectImmediately(playbackModel.song, ::updateSong)
|
collectImmediately(playbackModel.song, ::updateSong)
|
||||||
collectImmediately(playbackModel.openPanel.flow, ::handlePanel)
|
collectImmediately(playbackModel.openPanel.flow, ::handlePanel)
|
||||||
}
|
}
|
||||||
|
@ -289,7 +289,7 @@ class MainFragment :
|
||||||
initialNavDestinationChange = true
|
initialNavDestinationChange = true
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
selectionModel.drop()
|
listModel.dropSelection()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateSong(song: Song?) {
|
private fun updateSong(song: Song?) {
|
||||||
|
@ -450,11 +450,10 @@ class MainFragment :
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private inner class SelectionBackPressedCallback(
|
private inner class SelectionBackPressedCallback(private val listModel: ListViewModel) :
|
||||||
private val selectionModel: SelectionViewModel
|
OnBackPressedCallback(false) {
|
||||||
) : OnBackPressedCallback(false) {
|
|
||||||
override fun handleOnBackPressed() {
|
override fun handleOnBackPressed() {
|
||||||
if (selectionModel.drop()) {
|
if (listModel.dropSelection()) {
|
||||||
logD("Dropped selection")
|
logD("Dropped selection")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,10 +39,9 @@ import org.oxycblt.auxio.list.Divider
|
||||||
import org.oxycblt.auxio.list.Header
|
import org.oxycblt.auxio.list.Header
|
||||||
import org.oxycblt.auxio.list.Item
|
import org.oxycblt.auxio.list.Item
|
||||||
import org.oxycblt.auxio.list.ListFragment
|
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.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.Album
|
||||||
import org.oxycblt.auxio.music.Music
|
import org.oxycblt.auxio.music.Music
|
||||||
import org.oxycblt.auxio.music.MusicMode
|
import org.oxycblt.auxio.music.MusicMode
|
||||||
|
@ -75,8 +74,7 @@ class AlbumDetailFragment :
|
||||||
AlbumDetailHeaderAdapter.Listener,
|
AlbumDetailHeaderAdapter.Listener,
|
||||||
DetailListAdapter.Listener<Song> {
|
DetailListAdapter.Listener<Song> {
|
||||||
private val detailModel: DetailViewModel by activityViewModels()
|
private val detailModel: DetailViewModel by activityViewModels()
|
||||||
private val menuModel: MenuViewModel by activityViewModels()
|
override val listModel: ListViewModel by activityViewModels()
|
||||||
override val selectionModel: SelectionViewModel by activityViewModels()
|
|
||||||
override val musicModel: MusicViewModel by activityViewModels()
|
override val musicModel: MusicViewModel by activityViewModels()
|
||||||
override val playbackModel: PlaybackViewModel by activityViewModels()
|
override val playbackModel: PlaybackViewModel by activityViewModels()
|
||||||
// Information about what album to display is initially within the navigation arguments
|
// Information about what album to display is initially within the navigation arguments
|
||||||
|
@ -128,8 +126,8 @@ class AlbumDetailFragment :
|
||||||
collectImmediately(detailModel.currentAlbum, ::updateAlbum)
|
collectImmediately(detailModel.currentAlbum, ::updateAlbum)
|
||||||
collectImmediately(detailModel.albumList, ::updateList)
|
collectImmediately(detailModel.albumList, ::updateList)
|
||||||
collect(detailModel.toShow.flow, ::handleShow)
|
collect(detailModel.toShow.flow, ::handleShow)
|
||||||
collect(menuModel.pendingMenu.flow, ::handleMenu)
|
collect(listModel.menu.flow, ::handleMenu)
|
||||||
collectImmediately(selectionModel.selected, ::updateSelection)
|
collectImmediately(listModel.selected, ::updateSelection)
|
||||||
collect(musicModel.playlistDecision.flow, ::handlePlaylistDecision)
|
collect(musicModel.playlistDecision.flow, ::handlePlaylistDecision)
|
||||||
collectImmediately(
|
collectImmediately(
|
||||||
playbackModel.song, playbackModel.parent, playbackModel.isPlaying, ::updatePlayback)
|
playbackModel.song, playbackModel.parent, playbackModel.isPlaying, ::updatePlayback)
|
||||||
|
@ -187,7 +185,7 @@ class AlbumDetailFragment :
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onOpenMenu(item: Song, anchor: View) {
|
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() {
|
override fun onPlay() {
|
||||||
|
@ -304,17 +302,16 @@ class AlbumDetailFragment :
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleMenu(pendingMenu: PendingMenu?) {
|
private fun handleMenu(menu: Menu?) {
|
||||||
if (pendingMenu == null) return
|
if (menu == null) return
|
||||||
val directions =
|
val directions =
|
||||||
when (pendingMenu) {
|
when (menu) {
|
||||||
is PendingMenu.ForSong ->
|
is Menu.ForSong ->
|
||||||
AlbumDetailFragmentDirections.openSongMenu(
|
AlbumDetailFragmentDirections.openSongMenu(menu.menuRes, menu.music.uid)
|
||||||
pendingMenu.menuRes, pendingMenu.music.uid)
|
is Menu.ForAlbum,
|
||||||
is PendingMenu.ForAlbum,
|
is Menu.ForArtist,
|
||||||
is PendingMenu.ForArtist,
|
is Menu.ForGenre,
|
||||||
is PendingMenu.ForGenre,
|
is Menu.ForPlaylist -> error("Unexpected menu $menu")
|
||||||
is PendingMenu.ForPlaylist -> error("Unexpected menu $pendingMenu")
|
|
||||||
}
|
}
|
||||||
findNavController().navigateSafe(directions)
|
findNavController().navigateSafe(directions)
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,10 +39,9 @@ import org.oxycblt.auxio.list.Divider
|
||||||
import org.oxycblt.auxio.list.Header
|
import org.oxycblt.auxio.list.Header
|
||||||
import org.oxycblt.auxio.list.Item
|
import org.oxycblt.auxio.list.Item
|
||||||
import org.oxycblt.auxio.list.ListFragment
|
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.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.Album
|
||||||
import org.oxycblt.auxio.music.Artist
|
import org.oxycblt.auxio.music.Artist
|
||||||
import org.oxycblt.auxio.music.Music
|
import org.oxycblt.auxio.music.Music
|
||||||
|
@ -73,8 +72,7 @@ class ArtistDetailFragment :
|
||||||
DetailHeaderAdapter.Listener,
|
DetailHeaderAdapter.Listener,
|
||||||
DetailListAdapter.Listener<Music> {
|
DetailListAdapter.Listener<Music> {
|
||||||
private val detailModel: DetailViewModel by activityViewModels()
|
private val detailModel: DetailViewModel by activityViewModels()
|
||||||
private val menuModel: MenuViewModel by activityViewModels()
|
override val listModel: ListViewModel by activityViewModels()
|
||||||
override val selectionModel: SelectionViewModel by activityViewModels()
|
|
||||||
override val musicModel: MusicViewModel by activityViewModels()
|
override val musicModel: MusicViewModel by activityViewModels()
|
||||||
override val playbackModel: PlaybackViewModel by activityViewModels()
|
override val playbackModel: PlaybackViewModel by activityViewModels()
|
||||||
// Information about what artist to display is initially within the navigation arguments
|
// Information about what artist to display is initially within the navigation arguments
|
||||||
|
@ -129,8 +127,8 @@ class ArtistDetailFragment :
|
||||||
collectImmediately(detailModel.currentArtist, ::updateArtist)
|
collectImmediately(detailModel.currentArtist, ::updateArtist)
|
||||||
collectImmediately(detailModel.artistList, ::updateList)
|
collectImmediately(detailModel.artistList, ::updateList)
|
||||||
collect(detailModel.toShow.flow, ::handleShow)
|
collect(detailModel.toShow.flow, ::handleShow)
|
||||||
collect(menuModel.pendingMenu.flow, ::handleMenu)
|
collect(listModel.menu.flow, ::handleMenu)
|
||||||
collectImmediately(selectionModel.selected, ::updateSelection)
|
collectImmediately(listModel.selected, ::updateSelection)
|
||||||
collect(musicModel.playlistDecision.flow, ::handlePlaylistDecision)
|
collect(musicModel.playlistDecision.flow, ::handlePlaylistDecision)
|
||||||
collectImmediately(
|
collectImmediately(
|
||||||
playbackModel.song, playbackModel.parent, playbackModel.isPlaying, ::updatePlayback)
|
playbackModel.song, playbackModel.parent, playbackModel.isPlaying, ::updatePlayback)
|
||||||
|
@ -198,8 +196,8 @@ class ArtistDetailFragment :
|
||||||
|
|
||||||
override fun onOpenMenu(item: Music, anchor: View) {
|
override fun onOpenMenu(item: Music, anchor: View) {
|
||||||
when (item) {
|
when (item) {
|
||||||
is Song -> menuModel.open(R.menu.item_artist_song, item)
|
is Song -> listModel.openMenu(R.menu.item_artist_song, item)
|
||||||
is Album -> menuModel.open(R.menu.item_artist_album, item)
|
is Album -> listModel.openMenu(R.menu.item_artist_album, item)
|
||||||
else -> error("Unexpected datatype: ${item::class.simpleName}")
|
else -> error("Unexpected datatype: ${item::class.simpleName}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -314,19 +312,17 @@ class ArtistDetailFragment :
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleMenu(pendingMenu: PendingMenu?) {
|
private fun handleMenu(menu: Menu?) {
|
||||||
if (pendingMenu == null) return
|
if (menu == null) return
|
||||||
val directions =
|
val directions =
|
||||||
when (pendingMenu) {
|
when (menu) {
|
||||||
is PendingMenu.ForSong ->
|
is Menu.ForSong ->
|
||||||
ArtistDetailFragmentDirections.openSongMenu(
|
ArtistDetailFragmentDirections.openSongMenu(menu.menuRes, menu.music.uid)
|
||||||
pendingMenu.menuRes, pendingMenu.music.uid)
|
is Menu.ForAlbum ->
|
||||||
is PendingMenu.ForAlbum ->
|
ArtistDetailFragmentDirections.openAlbumMenu(menu.menuRes, menu.music.uid)
|
||||||
ArtistDetailFragmentDirections.openAlbumMenu(
|
is Menu.ForArtist,
|
||||||
pendingMenu.menuRes, pendingMenu.music.uid)
|
is Menu.ForGenre,
|
||||||
is PendingMenu.ForArtist,
|
is Menu.ForPlaylist -> error("Unexpected menu $menu")
|
||||||
is PendingMenu.ForGenre,
|
|
||||||
is PendingMenu.ForPlaylist -> error("Unexpected menu $pendingMenu")
|
|
||||||
}
|
}
|
||||||
findNavController().navigateSafe(directions)
|
findNavController().navigateSafe(directions)
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,10 +39,9 @@ import org.oxycblt.auxio.list.Divider
|
||||||
import org.oxycblt.auxio.list.Header
|
import org.oxycblt.auxio.list.Header
|
||||||
import org.oxycblt.auxio.list.Item
|
import org.oxycblt.auxio.list.Item
|
||||||
import org.oxycblt.auxio.list.ListFragment
|
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.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.Artist
|
||||||
import org.oxycblt.auxio.music.Genre
|
import org.oxycblt.auxio.music.Genre
|
||||||
import org.oxycblt.auxio.music.Music
|
import org.oxycblt.auxio.music.Music
|
||||||
|
@ -73,8 +72,7 @@ class GenreDetailFragment :
|
||||||
DetailHeaderAdapter.Listener,
|
DetailHeaderAdapter.Listener,
|
||||||
DetailListAdapter.Listener<Music> {
|
DetailListAdapter.Listener<Music> {
|
||||||
private val detailModel: DetailViewModel by activityViewModels()
|
private val detailModel: DetailViewModel by activityViewModels()
|
||||||
private val menuModel: MenuViewModel by activityViewModels()
|
override val listModel: ListViewModel by activityViewModels()
|
||||||
override val selectionModel: SelectionViewModel by activityViewModels()
|
|
||||||
override val musicModel: MusicViewModel by activityViewModels()
|
override val musicModel: MusicViewModel by activityViewModels()
|
||||||
override val playbackModel: PlaybackViewModel by activityViewModels()
|
override val playbackModel: PlaybackViewModel by activityViewModels()
|
||||||
// Information about what genre to display is initially within the navigation arguments
|
// Information about what genre to display is initially within the navigation arguments
|
||||||
|
@ -127,8 +125,8 @@ class GenreDetailFragment :
|
||||||
collectImmediately(detailModel.currentGenre, ::updatePlaylist)
|
collectImmediately(detailModel.currentGenre, ::updatePlaylist)
|
||||||
collectImmediately(detailModel.genreList, ::updateList)
|
collectImmediately(detailModel.genreList, ::updateList)
|
||||||
collect(detailModel.toShow.flow, ::handleShow)
|
collect(detailModel.toShow.flow, ::handleShow)
|
||||||
collect(menuModel.pendingMenu.flow, ::handleMenu)
|
collect(listModel.menu.flow, ::handleMenu)
|
||||||
collectImmediately(selectionModel.selected, ::updateSelection)
|
collectImmediately(listModel.selected, ::updateSelection)
|
||||||
collect(musicModel.playlistDecision.flow, ::handleDecision)
|
collect(musicModel.playlistDecision.flow, ::handleDecision)
|
||||||
collectImmediately(
|
collectImmediately(
|
||||||
playbackModel.song, playbackModel.parent, playbackModel.isPlaying, ::updatePlayback)
|
playbackModel.song, playbackModel.parent, playbackModel.isPlaying, ::updatePlayback)
|
||||||
|
@ -196,8 +194,8 @@ class GenreDetailFragment :
|
||||||
|
|
||||||
override fun onOpenMenu(item: Music, anchor: View) {
|
override fun onOpenMenu(item: Music, anchor: View) {
|
||||||
when (item) {
|
when (item) {
|
||||||
is Artist -> menuModel.open(R.menu.item_parent, item)
|
is Artist -> listModel.openMenu(R.menu.item_parent, item)
|
||||||
is Song -> menuModel.open(R.menu.item_song, item)
|
is Song -> listModel.openMenu(R.menu.item_song, item)
|
||||||
else -> error("Unexpected datatype: ${item::class.simpleName}")
|
else -> error("Unexpected datatype: ${item::class.simpleName}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -302,19 +300,17 @@ class GenreDetailFragment :
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleMenu(pendingMenu: PendingMenu?) {
|
private fun handleMenu(menu: Menu?) {
|
||||||
if (pendingMenu == null) return
|
if (menu == null) return
|
||||||
val directions =
|
val directions =
|
||||||
when (pendingMenu) {
|
when (menu) {
|
||||||
is PendingMenu.ForSong ->
|
is Menu.ForSong ->
|
||||||
GenreDetailFragmentDirections.openSongMenu(
|
GenreDetailFragmentDirections.openSongMenu(menu.menuRes, menu.music.uid)
|
||||||
pendingMenu.menuRes, pendingMenu.music.uid)
|
is Menu.ForArtist ->
|
||||||
is PendingMenu.ForArtist ->
|
GenreDetailFragmentDirections.openArtistMenu(menu.menuRes, menu.music.uid)
|
||||||
GenreDetailFragmentDirections.openArtistMenu(
|
is Menu.ForAlbum,
|
||||||
pendingMenu.menuRes, pendingMenu.music.uid)
|
is Menu.ForGenre,
|
||||||
is PendingMenu.ForAlbum,
|
is Menu.ForPlaylist -> error("Unexpected menu $menu")
|
||||||
is PendingMenu.ForGenre,
|
|
||||||
is PendingMenu.ForPlaylist -> error("Unexpected menu $pendingMenu")
|
|
||||||
}
|
}
|
||||||
findNavController().navigateSafe(directions)
|
findNavController().navigateSafe(directions)
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,9 +43,8 @@ import org.oxycblt.auxio.list.Divider
|
||||||
import org.oxycblt.auxio.list.Header
|
import org.oxycblt.auxio.list.Header
|
||||||
import org.oxycblt.auxio.list.Item
|
import org.oxycblt.auxio.list.Item
|
||||||
import org.oxycblt.auxio.list.ListFragment
|
import org.oxycblt.auxio.list.ListFragment
|
||||||
import org.oxycblt.auxio.list.menu.MenuViewModel
|
import org.oxycblt.auxio.list.ListViewModel
|
||||||
import org.oxycblt.auxio.list.menu.PendingMenu
|
import org.oxycblt.auxio.list.Menu
|
||||||
import org.oxycblt.auxio.list.selection.SelectionViewModel
|
|
||||||
import org.oxycblt.auxio.music.Music
|
import org.oxycblt.auxio.music.Music
|
||||||
import org.oxycblt.auxio.music.MusicParent
|
import org.oxycblt.auxio.music.MusicParent
|
||||||
import org.oxycblt.auxio.music.MusicViewModel
|
import org.oxycblt.auxio.music.MusicViewModel
|
||||||
|
@ -76,8 +75,7 @@ class PlaylistDetailFragment :
|
||||||
PlaylistDetailListAdapter.Listener,
|
PlaylistDetailListAdapter.Listener,
|
||||||
NavController.OnDestinationChangedListener {
|
NavController.OnDestinationChangedListener {
|
||||||
private val detailModel: DetailViewModel by activityViewModels()
|
private val detailModel: DetailViewModel by activityViewModels()
|
||||||
private val menuModel: MenuViewModel by activityViewModels()
|
override val listModel: ListViewModel by activityViewModels()
|
||||||
override val selectionModel: SelectionViewModel by activityViewModels()
|
|
||||||
override val musicModel: MusicViewModel by activityViewModels()
|
override val musicModel: MusicViewModel by activityViewModels()
|
||||||
override val playbackModel: PlaybackViewModel by activityViewModels()
|
override val playbackModel: PlaybackViewModel by activityViewModels()
|
||||||
// Information about what playlist to display is initially within the navigation arguments
|
// Information about what playlist to display is initially within the navigation arguments
|
||||||
|
@ -142,8 +140,8 @@ class PlaylistDetailFragment :
|
||||||
collectImmediately(detailModel.playlistList, ::updateList)
|
collectImmediately(detailModel.playlistList, ::updateList)
|
||||||
collectImmediately(detailModel.editedPlaylist, ::updateEditedList)
|
collectImmediately(detailModel.editedPlaylist, ::updateEditedList)
|
||||||
collect(detailModel.toShow.flow, ::handleShow)
|
collect(detailModel.toShow.flow, ::handleShow)
|
||||||
collect(menuModel.pendingMenu.flow, ::handleMenu)
|
collect(listModel.menu.flow, ::handleMenu)
|
||||||
collectImmediately(selectionModel.selected, ::updateSelection)
|
collectImmediately(listModel.selected, ::updateSelection)
|
||||||
collect(musicModel.playlistDecision.flow, ::handleDecision)
|
collect(musicModel.playlistDecision.flow, ::handleDecision)
|
||||||
collectImmediately(
|
collectImmediately(
|
||||||
playbackModel.song, playbackModel.parent, playbackModel.isPlaying, ::updatePlayback)
|
playbackModel.song, playbackModel.parent, playbackModel.isPlaying, ::updatePlayback)
|
||||||
|
@ -239,7 +237,7 @@ class PlaylistDetailFragment :
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onOpenMenu(item: Song, anchor: View) {
|
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() {
|
override fun onPlay() {
|
||||||
|
@ -286,7 +284,7 @@ class PlaylistDetailFragment :
|
||||||
private fun updateEditedList(editedPlaylist: List<Song>?) {
|
private fun updateEditedList(editedPlaylist: List<Song>?) {
|
||||||
playlistListAdapter.setEditing(editedPlaylist != null)
|
playlistListAdapter.setEditing(editedPlaylist != null)
|
||||||
playlistHeaderAdapter.setEditedPlaylist(editedPlaylist)
|
playlistHeaderAdapter.setEditedPlaylist(editedPlaylist)
|
||||||
selectionModel.drop()
|
listModel.dropSelection()
|
||||||
|
|
||||||
if (editedPlaylist != null) {
|
if (editedPlaylist != null) {
|
||||||
logD("Updating save button state")
|
logD("Updating save button state")
|
||||||
|
@ -349,17 +347,16 @@ class PlaylistDetailFragment :
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleMenu(pendingMenu: PendingMenu?) {
|
private fun handleMenu(menu: Menu?) {
|
||||||
if (pendingMenu == null) return
|
if (menu == null) return
|
||||||
val directions =
|
val directions =
|
||||||
when (pendingMenu) {
|
when (menu) {
|
||||||
is PendingMenu.ForSong ->
|
is Menu.ForSong ->
|
||||||
PlaylistDetailFragmentDirections.openSongMenu(
|
PlaylistDetailFragmentDirections.openSongMenu(menu.menuRes, menu.music.uid)
|
||||||
pendingMenu.menuRes, pendingMenu.music.uid)
|
is Menu.ForArtist,
|
||||||
is PendingMenu.ForArtist,
|
is Menu.ForAlbum,
|
||||||
is PendingMenu.ForAlbum,
|
is Menu.ForGenre,
|
||||||
is PendingMenu.ForGenre,
|
is Menu.ForPlaylist -> error("Unexpected menu $menu")
|
||||||
is PendingMenu.ForPlaylist -> error("Unexpected menu $pendingMenu")
|
|
||||||
}
|
}
|
||||||
findNavController().navigateSafe(directions)
|
findNavController().navigateSafe(directions)
|
||||||
}
|
}
|
||||||
|
@ -421,7 +418,7 @@ class PlaylistDetailFragment :
|
||||||
logD("Currently editing playlist, showing edit toolbar")
|
logD("Currently editing playlist, showing edit toolbar")
|
||||||
R.id.detail_edit_toolbar
|
R.id.detail_edit_toolbar
|
||||||
}
|
}
|
||||||
selectionModel.selected.value.isNotEmpty() -> {
|
listModel.selected.value.isNotEmpty() -> {
|
||||||
logD("Currently selecting, showing selection toolbar")
|
logD("Currently selecting, showing selection toolbar")
|
||||||
R.id.detail_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.list.SongListFragment
|
||||||
import org.oxycblt.auxio.home.tabs.AdaptiveTabStrategy
|
import org.oxycblt.auxio.home.tabs.AdaptiveTabStrategy
|
||||||
import org.oxycblt.auxio.home.tabs.Tab
|
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.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.IndexingProgress
|
||||||
import org.oxycblt.auxio.music.IndexingState
|
import org.oxycblt.auxio.music.IndexingState
|
||||||
import org.oxycblt.auxio.music.Music
|
import org.oxycblt.auxio.music.Music
|
||||||
|
@ -89,8 +88,7 @@ import org.oxycblt.auxio.util.unlikelyToBeNull
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
class HomeFragment :
|
class HomeFragment :
|
||||||
SelectionFragment<FragmentHomeBinding>(), AppBarLayout.OnOffsetChangedListener {
|
SelectionFragment<FragmentHomeBinding>(), AppBarLayout.OnOffsetChangedListener {
|
||||||
private val menuModel: MenuViewModel by activityViewModels()
|
override val listModel: ListViewModel by activityViewModels()
|
||||||
override val selectionModel: SelectionViewModel by activityViewModels()
|
|
||||||
override val musicModel: MusicViewModel by activityViewModels()
|
override val musicModel: MusicViewModel by activityViewModels()
|
||||||
override val playbackModel: PlaybackViewModel by activityViewModels()
|
override val playbackModel: PlaybackViewModel by activityViewModels()
|
||||||
private val homeModel: HomeViewModel 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
|
// 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
|
// result in no transition when the user navigates back. Make sure we re-initialize
|
||||||
// our transitions.
|
// our transitions.
|
||||||
val id = savedInstanceState.getInt(KEY_LAST_TRANSITION_ID, -2)
|
when (val id = savedInstanceState.getInt(KEY_LAST_TRANSITION_ID, -2)) {
|
||||||
when (id) {
|
|
||||||
-2 -> {}
|
-2 -> {}
|
||||||
-1 -> applyFadeTransition()
|
-1 -> applyFadeTransition()
|
||||||
else -> applyAxisTransition(id)
|
else -> applyAxisTransition(id)
|
||||||
|
@ -178,8 +175,8 @@ class HomeFragment :
|
||||||
collect(homeModel.recreateTabs.flow, ::handleRecreate)
|
collect(homeModel.recreateTabs.flow, ::handleRecreate)
|
||||||
collectImmediately(homeModel.currentTabMode, ::updateCurrentTab)
|
collectImmediately(homeModel.currentTabMode, ::updateCurrentTab)
|
||||||
collectImmediately(homeModel.songsList, homeModel.isFastScrolling, ::updateFab)
|
collectImmediately(homeModel.songsList, homeModel.isFastScrolling, ::updateFab)
|
||||||
collect(menuModel.pendingMenu.flow, ::handleMenu)
|
collect(listModel.menu.flow, ::handleMenu)
|
||||||
collectImmediately(selectionModel.selected, ::updateSelection)
|
collectImmediately(listModel.selected, ::updateSelection)
|
||||||
collectImmediately(musicModel.indexingState, ::updateIndexerState)
|
collectImmediately(musicModel.indexingState, ::updateIndexerState)
|
||||||
collect(musicModel.playlistDecision.flow, ::handleDecision)
|
collect(musicModel.playlistDecision.flow, ::handleDecision)
|
||||||
collect(detailModel.toShow.flow, ::handleShow)
|
collect(detailModel.toShow.flow, ::handleShow)
|
||||||
|
@ -582,22 +579,19 @@ class HomeFragment :
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleMenu(pendingMenu: PendingMenu?) {
|
private fun handleMenu(menu: Menu?) {
|
||||||
if (pendingMenu == null) return
|
if (menu == null) return
|
||||||
val directions =
|
val directions =
|
||||||
when (pendingMenu) {
|
when (menu) {
|
||||||
is PendingMenu.ForSong ->
|
is Menu.ForSong -> HomeFragmentDirections.openSongMenu(menu.menuRes, menu.music.uid)
|
||||||
HomeFragmentDirections.openSongMenu(pendingMenu.menuRes, pendingMenu.music.uid)
|
is Menu.ForAlbum ->
|
||||||
is PendingMenu.ForAlbum ->
|
HomeFragmentDirections.openAlbumMenu(menu.menuRes, menu.music.uid)
|
||||||
HomeFragmentDirections.openAlbumMenu(pendingMenu.menuRes, pendingMenu.music.uid)
|
is Menu.ForArtist ->
|
||||||
is PendingMenu.ForArtist ->
|
HomeFragmentDirections.openArtistMenu(menu.menuRes, menu.music.uid)
|
||||||
HomeFragmentDirections.openArtistMenu(
|
is Menu.ForGenre ->
|
||||||
pendingMenu.menuRes, pendingMenu.music.uid)
|
HomeFragmentDirections.openGenreMenu(menu.menuRes, menu.music.uid)
|
||||||
is PendingMenu.ForGenre ->
|
is Menu.ForPlaylist ->
|
||||||
HomeFragmentDirections.openGenreMenu(pendingMenu.menuRes, pendingMenu.music.uid)
|
HomeFragmentDirections.openPlaylistMenu(menu.menuRes, menu.music.uid)
|
||||||
is PendingMenu.ForPlaylist ->
|
|
||||||
HomeFragmentDirections.openPlaylistMenu(
|
|
||||||
pendingMenu.menuRes, pendingMenu.music.uid)
|
|
||||||
}
|
}
|
||||||
findNavController().navigateSafe(directions)
|
findNavController().navigateSafe(directions)
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,12 +32,11 @@ import org.oxycblt.auxio.detail.DetailViewModel
|
||||||
import org.oxycblt.auxio.home.HomeViewModel
|
import org.oxycblt.auxio.home.HomeViewModel
|
||||||
import org.oxycblt.auxio.home.fastscroll.FastScrollRecyclerView
|
import org.oxycblt.auxio.home.fastscroll.FastScrollRecyclerView
|
||||||
import org.oxycblt.auxio.list.ListFragment
|
import org.oxycblt.auxio.list.ListFragment
|
||||||
|
import org.oxycblt.auxio.list.ListViewModel
|
||||||
import org.oxycblt.auxio.list.SelectableListListener
|
import org.oxycblt.auxio.list.SelectableListListener
|
||||||
import org.oxycblt.auxio.list.Sort
|
import org.oxycblt.auxio.list.Sort
|
||||||
import org.oxycblt.auxio.list.adapter.SelectionIndicatorAdapter
|
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.recycler.AlbumViewHolder
|
||||||
import org.oxycblt.auxio.list.selection.SelectionViewModel
|
|
||||||
import org.oxycblt.auxio.music.Album
|
import org.oxycblt.auxio.music.Album
|
||||||
import org.oxycblt.auxio.music.Music
|
import org.oxycblt.auxio.music.Music
|
||||||
import org.oxycblt.auxio.music.MusicMode
|
import org.oxycblt.auxio.music.MusicMode
|
||||||
|
@ -61,8 +60,7 @@ class AlbumListFragment :
|
||||||
FastScrollRecyclerView.PopupProvider {
|
FastScrollRecyclerView.PopupProvider {
|
||||||
private val homeModel: HomeViewModel by activityViewModels()
|
private val homeModel: HomeViewModel by activityViewModels()
|
||||||
private val detailModel: DetailViewModel by activityViewModels()
|
private val detailModel: DetailViewModel by activityViewModels()
|
||||||
private val menuModel: MenuViewModel by activityViewModels()
|
override val listModel: ListViewModel by activityViewModels()
|
||||||
override val selectionModel: SelectionViewModel by activityViewModels()
|
|
||||||
override val musicModel: MusicViewModel by activityViewModels()
|
override val musicModel: MusicViewModel by activityViewModels()
|
||||||
override val playbackModel: PlaybackViewModel by activityViewModels()
|
override val playbackModel: PlaybackViewModel by activityViewModels()
|
||||||
private val albumAdapter = AlbumAdapter(this)
|
private val albumAdapter = AlbumAdapter(this)
|
||||||
|
@ -84,7 +82,7 @@ class AlbumListFragment :
|
||||||
}
|
}
|
||||||
|
|
||||||
collectImmediately(homeModel.albumsList, ::updateAlbums)
|
collectImmediately(homeModel.albumsList, ::updateAlbums)
|
||||||
collectImmediately(selectionModel.selected, ::updateSelection)
|
collectImmediately(listModel.selected, ::updateSelection)
|
||||||
collectImmediately(
|
collectImmediately(
|
||||||
playbackModel.song, playbackModel.parent, playbackModel.isPlaying, ::updatePlayback)
|
playbackModel.song, playbackModel.parent, playbackModel.isPlaying, ::updatePlayback)
|
||||||
}
|
}
|
||||||
|
@ -144,7 +142,7 @@ class AlbumListFragment :
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onOpenMenu(item: Album, anchor: View) {
|
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>) {
|
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.HomeViewModel
|
||||||
import org.oxycblt.auxio.home.fastscroll.FastScrollRecyclerView
|
import org.oxycblt.auxio.home.fastscroll.FastScrollRecyclerView
|
||||||
import org.oxycblt.auxio.list.ListFragment
|
import org.oxycblt.auxio.list.ListFragment
|
||||||
|
import org.oxycblt.auxio.list.ListViewModel
|
||||||
import org.oxycblt.auxio.list.SelectableListListener
|
import org.oxycblt.auxio.list.SelectableListListener
|
||||||
import org.oxycblt.auxio.list.Sort
|
import org.oxycblt.auxio.list.Sort
|
||||||
import org.oxycblt.auxio.list.adapter.SelectionIndicatorAdapter
|
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.recycler.ArtistViewHolder
|
||||||
import org.oxycblt.auxio.list.selection.SelectionViewModel
|
|
||||||
import org.oxycblt.auxio.music.Artist
|
import org.oxycblt.auxio.music.Artist
|
||||||
import org.oxycblt.auxio.music.Music
|
import org.oxycblt.auxio.music.Music
|
||||||
import org.oxycblt.auxio.music.MusicMode
|
import org.oxycblt.auxio.music.MusicMode
|
||||||
|
@ -59,8 +58,7 @@ class ArtistListFragment :
|
||||||
FastScrollRecyclerView.Listener {
|
FastScrollRecyclerView.Listener {
|
||||||
private val homeModel: HomeViewModel by activityViewModels()
|
private val homeModel: HomeViewModel by activityViewModels()
|
||||||
private val detailModel: DetailViewModel by activityViewModels()
|
private val detailModel: DetailViewModel by activityViewModels()
|
||||||
private val menuModel: MenuViewModel by activityViewModels()
|
override val listModel: ListViewModel by activityViewModels()
|
||||||
override val selectionModel: SelectionViewModel by activityViewModels()
|
|
||||||
override val musicModel: MusicViewModel by activityViewModels()
|
override val musicModel: MusicViewModel by activityViewModels()
|
||||||
override val playbackModel: PlaybackViewModel by activityViewModels()
|
override val playbackModel: PlaybackViewModel by activityViewModels()
|
||||||
private val artistAdapter = ArtistAdapter(this)
|
private val artistAdapter = ArtistAdapter(this)
|
||||||
|
@ -79,7 +77,7 @@ class ArtistListFragment :
|
||||||
}
|
}
|
||||||
|
|
||||||
collectImmediately(homeModel.artistsList, ::updateArtists)
|
collectImmediately(homeModel.artistsList, ::updateArtists)
|
||||||
collectImmediately(selectionModel.selected, ::updateSelection)
|
collectImmediately(listModel.selected, ::updateSelection)
|
||||||
collectImmediately(
|
collectImmediately(
|
||||||
playbackModel.song, playbackModel.parent, playbackModel.isPlaying, ::updatePlayback)
|
playbackModel.song, playbackModel.parent, playbackModel.isPlaying, ::updatePlayback)
|
||||||
}
|
}
|
||||||
|
@ -120,7 +118,7 @@ class ArtistListFragment :
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onOpenMenu(item: Artist, anchor: View) {
|
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>) {
|
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.HomeViewModel
|
||||||
import org.oxycblt.auxio.home.fastscroll.FastScrollRecyclerView
|
import org.oxycblt.auxio.home.fastscroll.FastScrollRecyclerView
|
||||||
import org.oxycblt.auxio.list.ListFragment
|
import org.oxycblt.auxio.list.ListFragment
|
||||||
|
import org.oxycblt.auxio.list.ListViewModel
|
||||||
import org.oxycblt.auxio.list.SelectableListListener
|
import org.oxycblt.auxio.list.SelectableListListener
|
||||||
import org.oxycblt.auxio.list.Sort
|
import org.oxycblt.auxio.list.Sort
|
||||||
import org.oxycblt.auxio.list.adapter.SelectionIndicatorAdapter
|
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.recycler.GenreViewHolder
|
||||||
import org.oxycblt.auxio.list.selection.SelectionViewModel
|
|
||||||
import org.oxycblt.auxio.music.Genre
|
import org.oxycblt.auxio.music.Genre
|
||||||
import org.oxycblt.auxio.music.Music
|
import org.oxycblt.auxio.music.Music
|
||||||
import org.oxycblt.auxio.music.MusicMode
|
import org.oxycblt.auxio.music.MusicMode
|
||||||
|
@ -58,8 +57,7 @@ class GenreListFragment :
|
||||||
FastScrollRecyclerView.Listener {
|
FastScrollRecyclerView.Listener {
|
||||||
private val homeModel: HomeViewModel by activityViewModels()
|
private val homeModel: HomeViewModel by activityViewModels()
|
||||||
private val detailModel: DetailViewModel by activityViewModels()
|
private val detailModel: DetailViewModel by activityViewModels()
|
||||||
private val menuModel: MenuViewModel by activityViewModels()
|
override val listModel: ListViewModel by activityViewModels()
|
||||||
override val selectionModel: SelectionViewModel by activityViewModels()
|
|
||||||
override val musicModel: MusicViewModel by activityViewModels()
|
override val musicModel: MusicViewModel by activityViewModels()
|
||||||
override val playbackModel: PlaybackViewModel by activityViewModels()
|
override val playbackModel: PlaybackViewModel by activityViewModels()
|
||||||
private val genreAdapter = GenreAdapter(this)
|
private val genreAdapter = GenreAdapter(this)
|
||||||
|
@ -78,7 +76,7 @@ class GenreListFragment :
|
||||||
}
|
}
|
||||||
|
|
||||||
collectImmediately(homeModel.genresList, ::updateGenres)
|
collectImmediately(homeModel.genresList, ::updateGenres)
|
||||||
collectImmediately(selectionModel.selected, ::updateSelection)
|
collectImmediately(listModel.selected, ::updateSelection)
|
||||||
collectImmediately(
|
collectImmediately(
|
||||||
playbackModel.song, playbackModel.parent, playbackModel.isPlaying, ::updatePlayback)
|
playbackModel.song, playbackModel.parent, playbackModel.isPlaying, ::updatePlayback)
|
||||||
}
|
}
|
||||||
|
@ -119,7 +117,7 @@ class GenreListFragment :
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onOpenMenu(item: Genre, anchor: View) {
|
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>) {
|
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.HomeViewModel
|
||||||
import org.oxycblt.auxio.home.fastscroll.FastScrollRecyclerView
|
import org.oxycblt.auxio.home.fastscroll.FastScrollRecyclerView
|
||||||
import org.oxycblt.auxio.list.ListFragment
|
import org.oxycblt.auxio.list.ListFragment
|
||||||
|
import org.oxycblt.auxio.list.ListViewModel
|
||||||
import org.oxycblt.auxio.list.SelectableListListener
|
import org.oxycblt.auxio.list.SelectableListListener
|
||||||
import org.oxycblt.auxio.list.Sort
|
import org.oxycblt.auxio.list.Sort
|
||||||
import org.oxycblt.auxio.list.adapter.SelectionIndicatorAdapter
|
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.recycler.PlaylistViewHolder
|
||||||
import org.oxycblt.auxio.list.selection.SelectionViewModel
|
|
||||||
import org.oxycblt.auxio.music.Music
|
import org.oxycblt.auxio.music.Music
|
||||||
import org.oxycblt.auxio.music.MusicMode
|
import org.oxycblt.auxio.music.MusicMode
|
||||||
import org.oxycblt.auxio.music.MusicParent
|
import org.oxycblt.auxio.music.MusicParent
|
||||||
|
@ -56,8 +55,7 @@ class PlaylistListFragment :
|
||||||
FastScrollRecyclerView.Listener {
|
FastScrollRecyclerView.Listener {
|
||||||
private val homeModel: HomeViewModel by activityViewModels()
|
private val homeModel: HomeViewModel by activityViewModels()
|
||||||
private val detailModel: DetailViewModel by activityViewModels()
|
private val detailModel: DetailViewModel by activityViewModels()
|
||||||
private val menuModel: MenuViewModel by activityViewModels()
|
override val listModel: ListViewModel by activityViewModels()
|
||||||
override val selectionModel: SelectionViewModel by activityViewModels()
|
|
||||||
override val musicModel: MusicViewModel by activityViewModels()
|
override val musicModel: MusicViewModel by activityViewModels()
|
||||||
override val playbackModel: PlaybackViewModel by activityViewModels()
|
override val playbackModel: PlaybackViewModel by activityViewModels()
|
||||||
private val playlistAdapter = PlaylistAdapter(this)
|
private val playlistAdapter = PlaylistAdapter(this)
|
||||||
|
@ -76,7 +74,7 @@ class PlaylistListFragment :
|
||||||
}
|
}
|
||||||
|
|
||||||
collectImmediately(homeModel.playlistsList, ::updatePlaylists)
|
collectImmediately(homeModel.playlistsList, ::updatePlaylists)
|
||||||
collectImmediately(selectionModel.selected, ::updateSelection)
|
collectImmediately(listModel.selected, ::updateSelection)
|
||||||
collectImmediately(
|
collectImmediately(
|
||||||
playbackModel.song, playbackModel.parent, playbackModel.isPlaying, ::updatePlayback)
|
playbackModel.song, playbackModel.parent, playbackModel.isPlaying, ::updatePlayback)
|
||||||
}
|
}
|
||||||
|
@ -117,7 +115,7 @@ class PlaylistListFragment :
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onOpenMenu(item: Playlist, anchor: View) {
|
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>) {
|
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.HomeViewModel
|
||||||
import org.oxycblt.auxio.home.fastscroll.FastScrollRecyclerView
|
import org.oxycblt.auxio.home.fastscroll.FastScrollRecyclerView
|
||||||
import org.oxycblt.auxio.list.ListFragment
|
import org.oxycblt.auxio.list.ListFragment
|
||||||
|
import org.oxycblt.auxio.list.ListViewModel
|
||||||
import org.oxycblt.auxio.list.SelectableListListener
|
import org.oxycblt.auxio.list.SelectableListListener
|
||||||
import org.oxycblt.auxio.list.Sort
|
import org.oxycblt.auxio.list.Sort
|
||||||
import org.oxycblt.auxio.list.adapter.SelectionIndicatorAdapter
|
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.recycler.SongViewHolder
|
||||||
import org.oxycblt.auxio.list.selection.SelectionViewModel
|
|
||||||
import org.oxycblt.auxio.music.Music
|
import org.oxycblt.auxio.music.Music
|
||||||
import org.oxycblt.auxio.music.MusicMode
|
import org.oxycblt.auxio.music.MusicMode
|
||||||
import org.oxycblt.auxio.music.MusicParent
|
import org.oxycblt.auxio.music.MusicParent
|
||||||
|
@ -58,8 +57,7 @@ class SongListFragment :
|
||||||
FastScrollRecyclerView.PopupProvider,
|
FastScrollRecyclerView.PopupProvider,
|
||||||
FastScrollRecyclerView.Listener {
|
FastScrollRecyclerView.Listener {
|
||||||
private val homeModel: HomeViewModel by activityViewModels()
|
private val homeModel: HomeViewModel by activityViewModels()
|
||||||
private val menuModel: MenuViewModel by activityViewModels()
|
override val listModel: ListViewModel by activityViewModels()
|
||||||
override val selectionModel: SelectionViewModel by activityViewModels()
|
|
||||||
override val musicModel: MusicViewModel by activityViewModels()
|
override val musicModel: MusicViewModel by activityViewModels()
|
||||||
override val playbackModel: PlaybackViewModel by activityViewModels()
|
override val playbackModel: PlaybackViewModel by activityViewModels()
|
||||||
private val songAdapter = SongAdapter(this)
|
private val songAdapter = SongAdapter(this)
|
||||||
|
@ -81,7 +79,7 @@ class SongListFragment :
|
||||||
}
|
}
|
||||||
|
|
||||||
collectImmediately(homeModel.songsList, ::updateSongs)
|
collectImmediately(homeModel.songsList, ::updateSongs)
|
||||||
collectImmediately(selectionModel.selected, ::updateSelection)
|
collectImmediately(listModel.selected, ::updateSelection)
|
||||||
collectImmediately(
|
collectImmediately(
|
||||||
playbackModel.song, playbackModel.parent, playbackModel.isPlaying, ::updatePlayback)
|
playbackModel.song, playbackModel.parent, playbackModel.isPlaying, ::updatePlayback)
|
||||||
}
|
}
|
||||||
|
@ -143,7 +141,7 @@ class SongListFragment :
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onOpenMenu(item: Song, anchor: View) {
|
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>) {
|
private fun updateSongs(songs: List<Song>) {
|
||||||
|
|
|
@ -24,7 +24,6 @@ import androidx.appcompat.widget.PopupMenu
|
||||||
import androidx.core.view.MenuCompat
|
import androidx.core.view.MenuCompat
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import androidx.viewbinding.ViewBinding
|
import androidx.viewbinding.ViewBinding
|
||||||
import org.oxycblt.auxio.list.selection.SelectionFragment
|
|
||||||
import org.oxycblt.auxio.music.Music
|
import org.oxycblt.auxio.music.Music
|
||||||
import org.oxycblt.auxio.util.logD
|
import org.oxycblt.auxio.util.logD
|
||||||
|
|
||||||
|
@ -52,9 +51,9 @@ abstract class ListFragment<in T : Music, VB : ViewBinding> :
|
||||||
abstract fun onRealClick(item: T)
|
abstract fun onRealClick(item: T)
|
||||||
|
|
||||||
final override fun onClick(item: T, viewHolder: RecyclerView.ViewHolder) {
|
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.
|
// Map clicking an item to selecting an item when items are already selected.
|
||||||
selectionModel.select(item)
|
listModel.select(item)
|
||||||
} else {
|
} else {
|
||||||
// Delegate to the concrete implementation when we don't select the item.
|
// Delegate to the concrete implementation when we don't select the item.
|
||||||
onRealClick(item)
|
onRealClick(item)
|
||||||
|
@ -62,7 +61,7 @@ abstract class ListFragment<in T : Music, VB : ViewBinding> :
|
||||||
}
|
}
|
||||||
|
|
||||||
final override fun onSelect(item: T) {
|
final override fun onSelect(item: T) {
|
||||||
selectionModel.select(item)
|
listModel.select(item)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2022 Auxio Project
|
* Copyright (c) 2023 Auxio Project
|
||||||
* SelectionViewModel.kt is part of Auxio.
|
* ListViewModel.kt is part of Auxio.
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* 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/>.
|
* 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 androidx.lifecycle.ViewModel
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
import javax.inject.Inject
|
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.MusicSettings
|
||||||
import org.oxycblt.auxio.music.Playlist
|
import org.oxycblt.auxio.music.Playlist
|
||||||
import org.oxycblt.auxio.music.Song
|
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.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)
|
* @author Alexander Capehart (OxygenCobalt)
|
||||||
*/
|
*/
|
||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
class SelectionViewModel
|
class ListViewModel
|
||||||
@Inject
|
@Inject
|
||||||
constructor(
|
constructor(
|
||||||
private val musicRepository: MusicRepository,
|
private val musicRepository: MusicRepository,
|
||||||
|
@ -51,6 +55,9 @@ constructor(
|
||||||
val selected: StateFlow<List<Music>>
|
val selected: StateFlow<List<Music>>
|
||||||
get() = _selected
|
get() = _selected
|
||||||
|
|
||||||
|
private val _Menu = MutableEvent<Menu>()
|
||||||
|
val menu: Event<Menu> = _Menu
|
||||||
|
|
||||||
init {
|
init {
|
||||||
musicRepository.addUpdateListener(this)
|
musicRepository.addUpdateListener(this)
|
||||||
}
|
}
|
||||||
|
@ -105,7 +112,7 @@ constructor(
|
||||||
*
|
*
|
||||||
* @return A list of [Song]s collated from each item selected.
|
* @return A list of [Song]s collated from each item selected.
|
||||||
*/
|
*/
|
||||||
fun take(): List<Song> {
|
fun takeSelection(): List<Song> {
|
||||||
logD("Taking selection")
|
logD("Taking selection")
|
||||||
return _selected.value
|
return _selected.value
|
||||||
.flatMap {
|
.flatMap {
|
||||||
|
@ -125,8 +132,88 @@ constructor(
|
||||||
*
|
*
|
||||||
* @return true if the prior selection was non-empty, false otherwise.
|
* @return true if the prior selection was non-empty, false otherwise.
|
||||||
*/
|
*/
|
||||||
fun drop(): Boolean {
|
fun dropSelection(): Boolean {
|
||||||
logD("Dropping selection [empty=${_selected.value.isEmpty()}]")
|
logD("Dropping selection [empty=${_selected.value.isEmpty()}]")
|
||||||
return _selected.value.isNotEmpty().also { _selected.value = listOf() }
|
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/>.
|
* 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.os.Bundle
|
||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
|
@ -36,7 +36,7 @@ import org.oxycblt.auxio.util.showToast
|
||||||
*/
|
*/
|
||||||
abstract class SelectionFragment<VB : ViewBinding> :
|
abstract class SelectionFragment<VB : ViewBinding> :
|
||||||
ViewBindingFragment<VB>(), Toolbar.OnMenuItemClickListener {
|
ViewBindingFragment<VB>(), Toolbar.OnMenuItemClickListener {
|
||||||
protected abstract val selectionModel: SelectionViewModel
|
protected abstract val listModel: ListViewModel
|
||||||
protected abstract val musicModel: MusicViewModel
|
protected abstract val musicModel: MusicViewModel
|
||||||
protected abstract val playbackModel: PlaybackViewModel
|
protected abstract val playbackModel: PlaybackViewModel
|
||||||
|
|
||||||
|
@ -46,7 +46,7 @@ abstract class SelectionFragment<VB : ViewBinding> :
|
||||||
super.onBindingCreated(binding, savedInstanceState)
|
super.onBindingCreated(binding, savedInstanceState)
|
||||||
getSelectionToolbar(binding)?.apply {
|
getSelectionToolbar(binding)?.apply {
|
||||||
// Add cancel and menu item listeners to manage what occurs with the selection.
|
// Add cancel and menu item listeners to manage what occurs with the selection.
|
||||||
setNavigationOnClickListener { selectionModel.drop() }
|
setNavigationOnClickListener { listModel.dropSelection() }
|
||||||
setOnMenuItemClickListener(this@SelectionFragment)
|
setOnMenuItemClickListener(this@SelectionFragment)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -59,29 +59,29 @@ abstract class SelectionFragment<VB : ViewBinding> :
|
||||||
override fun onMenuItemClick(item: MenuItem) =
|
override fun onMenuItemClick(item: MenuItem) =
|
||||||
when (item.itemId) {
|
when (item.itemId) {
|
||||||
R.id.action_selection_play_next -> {
|
R.id.action_selection_play_next -> {
|
||||||
playbackModel.playNext(selectionModel.take())
|
playbackModel.playNext(listModel.takeSelection())
|
||||||
requireContext().showToast(R.string.lng_queue_added)
|
requireContext().showToast(R.string.lng_queue_added)
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
R.id.action_selection_playlist_add -> {
|
R.id.action_selection_playlist_add -> {
|
||||||
musicModel.addToPlaylist(selectionModel.take())
|
musicModel.addToPlaylist(listModel.takeSelection())
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
R.id.action_selection_queue_add -> {
|
R.id.action_selection_queue_add -> {
|
||||||
playbackModel.addToQueue(selectionModel.take())
|
playbackModel.addToQueue(listModel.takeSelection())
|
||||||
requireContext().showToast(R.string.lng_queue_added)
|
requireContext().showToast(R.string.lng_queue_added)
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
R.id.action_selection_play -> {
|
R.id.action_selection_play -> {
|
||||||
playbackModel.play(selectionModel.take())
|
playbackModel.play(listModel.takeSelection())
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
R.id.action_selection_shuffle -> {
|
R.id.action_selection_shuffle -> {
|
||||||
playbackModel.shuffle(selectionModel.take())
|
playbackModel.shuffle(listModel.takeSelection())
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
R.id.action_selection_share -> {
|
R.id.action_selection_share -> {
|
||||||
requireContext().share(selectionModel.take())
|
requireContext().share(listModel.takeSelection())
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
else -> false
|
else -> false
|
|
@ -29,6 +29,7 @@ import androidx.navigation.fragment.findNavController
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import org.oxycblt.auxio.databinding.DialogMenuBinding
|
import org.oxycblt.auxio.databinding.DialogMenuBinding
|
||||||
import org.oxycblt.auxio.list.ClickableListListener
|
import org.oxycblt.auxio.list.ClickableListListener
|
||||||
|
import org.oxycblt.auxio.list.ListViewModel
|
||||||
import org.oxycblt.auxio.list.adapter.UpdateInstructions
|
import org.oxycblt.auxio.list.adapter.UpdateInstructions
|
||||||
import org.oxycblt.auxio.music.Music
|
import org.oxycblt.auxio.music.Music
|
||||||
import org.oxycblt.auxio.ui.ViewBindingBottomSheetDialogFragment
|
import org.oxycblt.auxio.ui.ViewBindingBottomSheetDialogFragment
|
||||||
|
@ -44,6 +45,7 @@ import org.oxycblt.auxio.util.logD
|
||||||
abstract class MenuDialogFragment<T : Music> :
|
abstract class MenuDialogFragment<T : Music> :
|
||||||
ViewBindingBottomSheetDialogFragment<DialogMenuBinding>(), ClickableListListener<MenuItem> {
|
ViewBindingBottomSheetDialogFragment<DialogMenuBinding>(), ClickableListListener<MenuItem> {
|
||||||
protected abstract val menuModel: MenuViewModel
|
protected abstract val menuModel: MenuViewModel
|
||||||
|
protected abstract val listModel: ListViewModel
|
||||||
private val menuAdapter = MenuItemAdapter(@Suppress("LeakingThis") this)
|
private val menuAdapter = MenuItemAdapter(@Suppress("LeakingThis") this)
|
||||||
|
|
||||||
abstract val menuRes: Int
|
abstract val menuRes: Int
|
||||||
|
@ -66,7 +68,7 @@ abstract class MenuDialogFragment<T : Music> :
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- VIEWMODEL SETUP ---
|
// --- VIEWMODEL SETUP ---
|
||||||
menuModel.pendingMenu.consume()
|
listModel.menu.consume()
|
||||||
menuModel.setCurrentMenu(uid)
|
menuModel.setCurrentMenu(uid)
|
||||||
collectImmediately(menuModel.currentMusic, this::updateMusic)
|
collectImmediately(menuModel.currentMusic, this::updateMusic)
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,11 +20,13 @@ package org.oxycblt.auxio.list.menu
|
||||||
|
|
||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
import androidx.fragment.app.activityViewModels
|
import androidx.fragment.app.activityViewModels
|
||||||
|
import androidx.fragment.app.viewModels
|
||||||
import androidx.navigation.fragment.navArgs
|
import androidx.navigation.fragment.navArgs
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
import org.oxycblt.auxio.R
|
import org.oxycblt.auxio.R
|
||||||
import org.oxycblt.auxio.databinding.DialogMenuBinding
|
import org.oxycblt.auxio.databinding.DialogMenuBinding
|
||||||
import org.oxycblt.auxio.detail.DetailViewModel
|
import org.oxycblt.auxio.detail.DetailViewModel
|
||||||
|
import org.oxycblt.auxio.list.ListViewModel
|
||||||
import org.oxycblt.auxio.music.Album
|
import org.oxycblt.auxio.music.Album
|
||||||
import org.oxycblt.auxio.music.Artist
|
import org.oxycblt.auxio.music.Artist
|
||||||
import org.oxycblt.auxio.music.Genre
|
import org.oxycblt.auxio.music.Genre
|
||||||
|
@ -41,6 +43,7 @@ import org.oxycblt.auxio.util.showToast
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
class SongMenuDialogFragment : MenuDialogFragment<Song>() {
|
class SongMenuDialogFragment : MenuDialogFragment<Song>() {
|
||||||
override val menuModel: MenuViewModel by activityViewModels()
|
override val menuModel: MenuViewModel by activityViewModels()
|
||||||
|
override val listModel: ListViewModel by activityViewModels()
|
||||||
private val detailModel: DetailViewModel by activityViewModels()
|
private val detailModel: DetailViewModel by activityViewModels()
|
||||||
private val musicModel: MusicViewModel by activityViewModels()
|
private val musicModel: MusicViewModel by activityViewModels()
|
||||||
private val playbackModel: PlaybackViewModel by activityViewModels()
|
private val playbackModel: PlaybackViewModel by activityViewModels()
|
||||||
|
@ -83,7 +86,8 @@ class SongMenuDialogFragment : MenuDialogFragment<Song>() {
|
||||||
|
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
class AlbumMenuDialogFragment : MenuDialogFragment<Album>() {
|
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 detailModel: DetailViewModel by activityViewModels()
|
||||||
private val musicModel: MusicViewModel by activityViewModels()
|
private val musicModel: MusicViewModel by activityViewModels()
|
||||||
private val playbackModel: PlaybackViewModel by activityViewModels()
|
private val playbackModel: PlaybackViewModel by activityViewModels()
|
||||||
|
@ -127,7 +131,8 @@ class AlbumMenuDialogFragment : MenuDialogFragment<Album>() {
|
||||||
|
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
class ArtistMenuDialogFragment : MenuDialogFragment<Artist>() {
|
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 detailModel: DetailViewModel by activityViewModels()
|
||||||
private val musicModel: MusicViewModel by activityViewModels()
|
private val musicModel: MusicViewModel by activityViewModels()
|
||||||
private val playbackModel: PlaybackViewModel by activityViewModels()
|
private val playbackModel: PlaybackViewModel by activityViewModels()
|
||||||
|
@ -189,7 +194,8 @@ class ArtistMenuDialogFragment : MenuDialogFragment<Artist>() {
|
||||||
|
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
class GenreMenuDialogFragment : MenuDialogFragment<Genre>() {
|
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 detailModel: DetailViewModel by activityViewModels()
|
||||||
private val musicModel: MusicViewModel by activityViewModels()
|
private val musicModel: MusicViewModel by activityViewModels()
|
||||||
private val playbackModel: PlaybackViewModel by activityViewModels()
|
private val playbackModel: PlaybackViewModel by activityViewModels()
|
||||||
|
@ -236,7 +242,8 @@ class GenreMenuDialogFragment : MenuDialogFragment<Genre>() {
|
||||||
|
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
class PlaylistMenuDialogFragment : MenuDialogFragment<Playlist>() {
|
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 detailModel: DetailViewModel by activityViewModels()
|
||||||
private val musicModel: MusicViewModel by activityViewModels()
|
private val musicModel: MusicViewModel by activityViewModels()
|
||||||
private val playbackModel: PlaybackViewModel by activityViewModels()
|
private val playbackModel: PlaybackViewModel by activityViewModels()
|
||||||
|
|
|
@ -18,29 +18,18 @@
|
||||||
|
|
||||||
package org.oxycblt.auxio.list.menu
|
package org.oxycblt.auxio.list.menu
|
||||||
|
|
||||||
import androidx.annotation.MenuRes
|
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
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.Music
|
||||||
import org.oxycblt.auxio.music.MusicRepository
|
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
|
import org.oxycblt.auxio.util.logW
|
||||||
|
|
||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
class MenuViewModel @Inject constructor(private val musicRepository: MusicRepository) :
|
class MenuViewModel @Inject constructor(private val musicRepository: MusicRepository) :
|
||||||
ViewModel(), MusicRepository.UpdateListener {
|
ViewModel(), MusicRepository.UpdateListener {
|
||||||
private val _pendingMenu = MutableEvent<PendingMenu>()
|
|
||||||
val pendingMenu: Event<PendingMenu> = _pendingMenu
|
|
||||||
|
|
||||||
private val _currentMusic = MutableStateFlow<Music?>(null)
|
private val _currentMusic = MutableStateFlow<Music?>(null)
|
||||||
val currentMusic: StateFlow<Music?> = _currentMusic
|
val currentMusic: StateFlow<Music?> = _currentMusic
|
||||||
|
|
||||||
|
@ -56,27 +45,6 @@ class MenuViewModel @Inject constructor(private val musicRepository: MusicReposi
|
||||||
musicRepository.removeUpdateListener(this)
|
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) {
|
fun setCurrentMenu(uid: Music.UID) {
|
||||||
_currentMusic.value = musicRepository.find(uid)
|
_currentMusic.value = musicRepository.find(uid)
|
||||||
if (_currentMusic.value == null) {
|
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.Header
|
||||||
import org.oxycblt.auxio.list.Item
|
import org.oxycblt.auxio.list.Item
|
||||||
import org.oxycblt.auxio.list.ListFragment
|
import org.oxycblt.auxio.list.ListFragment
|
||||||
import org.oxycblt.auxio.list.menu.MenuViewModel
|
import org.oxycblt.auxio.list.ListViewModel
|
||||||
import org.oxycblt.auxio.list.menu.PendingMenu
|
import org.oxycblt.auxio.list.Menu
|
||||||
import org.oxycblt.auxio.list.selection.SelectionViewModel
|
|
||||||
import org.oxycblt.auxio.music.Album
|
import org.oxycblt.auxio.music.Album
|
||||||
import org.oxycblt.auxio.music.Artist
|
import org.oxycblt.auxio.music.Artist
|
||||||
import org.oxycblt.auxio.music.Genre
|
import org.oxycblt.auxio.music.Genre
|
||||||
|
@ -74,8 +73,7 @@ import org.oxycblt.auxio.util.setFullWidthLookup
|
||||||
class SearchFragment : ListFragment<Music, FragmentSearchBinding>() {
|
class SearchFragment : ListFragment<Music, FragmentSearchBinding>() {
|
||||||
private val searchModel: SearchViewModel by viewModels()
|
private val searchModel: SearchViewModel by viewModels()
|
||||||
private val detailModel: DetailViewModel by activityViewModels()
|
private val detailModel: DetailViewModel by activityViewModels()
|
||||||
private val menuModel: MenuViewModel by activityViewModels()
|
override val listModel: ListViewModel by activityViewModels()
|
||||||
override val selectionModel: SelectionViewModel by activityViewModels()
|
|
||||||
override val playbackModel: PlaybackViewModel by activityViewModels()
|
override val playbackModel: PlaybackViewModel by activityViewModels()
|
||||||
override val musicModel: MusicViewModel by activityViewModels()
|
override val musicModel: MusicViewModel by activityViewModels()
|
||||||
private val searchAdapter = SearchAdapter(this)
|
private val searchAdapter = SearchAdapter(this)
|
||||||
|
@ -141,8 +139,8 @@ class SearchFragment : ListFragment<Music, FragmentSearchBinding>() {
|
||||||
// --- VIEWMODEL SETUP ---
|
// --- VIEWMODEL SETUP ---
|
||||||
|
|
||||||
collectImmediately(searchModel.searchResults, ::updateSearchResults)
|
collectImmediately(searchModel.searchResults, ::updateSearchResults)
|
||||||
collect(menuModel.pendingMenu.flow, ::handleMenu)
|
collectImmediately(listModel.selected, ::updateSelection)
|
||||||
collectImmediately(selectionModel.selected, ::updateSelection)
|
collect(listModel.menu.flow, ::handleMenu)
|
||||||
collect(musicModel.playlistDecision.flow, ::handleDecision)
|
collect(musicModel.playlistDecision.flow, ::handleDecision)
|
||||||
collectImmediately(
|
collectImmediately(
|
||||||
playbackModel.song, playbackModel.parent, playbackModel.isPlaying, ::updatePlayback)
|
playbackModel.song, playbackModel.parent, playbackModel.isPlaying, ::updatePlayback)
|
||||||
|
@ -186,11 +184,11 @@ class SearchFragment : ListFragment<Music, FragmentSearchBinding>() {
|
||||||
|
|
||||||
override fun onOpenMenu(item: Music, anchor: View) {
|
override fun onOpenMenu(item: Music, anchor: View) {
|
||||||
when (item) {
|
when (item) {
|
||||||
is Song -> menuModel.open(R.menu.item_song, item)
|
is Song -> listModel.openMenu(R.menu.item_song, item)
|
||||||
is Album -> menuModel.open(R.menu.item_album, item)
|
is Album -> listModel.openMenu(R.menu.item_album, item)
|
||||||
is Artist -> menuModel.open(R.menu.item_parent, item)
|
is Artist -> listModel.openMenu(R.menu.item_parent, item)
|
||||||
is Genre -> menuModel.open(R.menu.item_parent, item)
|
is Genre -> listModel.openMenu(R.menu.item_parent, item)
|
||||||
is Playlist -> menuModel.open(R.menu.item_playlist, item)
|
is Playlist -> listModel.openMenu(R.menu.item_playlist, item)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -262,25 +260,20 @@ class SearchFragment : ListFragment<Music, FragmentSearchBinding>() {
|
||||||
hideKeyboard()
|
hideKeyboard()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleMenu(pendingMenu: PendingMenu?) {
|
private fun handleMenu(menu: Menu?) {
|
||||||
if (pendingMenu == null) return
|
if (menu == null) return
|
||||||
val directions =
|
val directions =
|
||||||
when (pendingMenu) {
|
when (menu) {
|
||||||
is PendingMenu.ForSong ->
|
is Menu.ForSong ->
|
||||||
SearchFragmentDirections.openSongMenu(
|
SearchFragmentDirections.openSongMenu(menu.menuRes, menu.music.uid)
|
||||||
pendingMenu.menuRes, pendingMenu.music.uid)
|
is Menu.ForAlbum ->
|
||||||
is PendingMenu.ForAlbum ->
|
SearchFragmentDirections.openAlbumMenu(menu.menuRes, menu.music.uid)
|
||||||
SearchFragmentDirections.openAlbumMenu(
|
is Menu.ForArtist ->
|
||||||
pendingMenu.menuRes, pendingMenu.music.uid)
|
SearchFragmentDirections.openArtistMenu(menu.menuRes, menu.music.uid)
|
||||||
is PendingMenu.ForArtist ->
|
is Menu.ForGenre ->
|
||||||
SearchFragmentDirections.openArtistMenu(
|
SearchFragmentDirections.openGenreMenu(menu.menuRes, menu.music.uid)
|
||||||
pendingMenu.menuRes, pendingMenu.music.uid)
|
is Menu.ForPlaylist ->
|
||||||
is PendingMenu.ForGenre ->
|
SearchFragmentDirections.openPlaylistMenu(menu.menuRes, menu.music.uid)
|
||||||
SearchFragmentDirections.openGenreMenu(
|
|
||||||
pendingMenu.menuRes, pendingMenu.music.uid)
|
|
||||||
is PendingMenu.ForPlaylist ->
|
|
||||||
SearchFragmentDirections.openPlaylistMenu(
|
|
||||||
pendingMenu.menuRes, pendingMenu.music.uid)
|
|
||||||
}
|
}
|
||||||
findNavController().navigateSafe(directions)
|
findNavController().navigateSafe(directions)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue