ui: flatten nav graph
Flatten the navigation graph into a single "main" graph that links home to both explore and preference fragments. ***This massively breaks the app in it's current state***. Further changes and refactors are needed to get this back to working.
This commit is contained in:
parent
3ddf0347ea
commit
07e9ca8ef6
35 changed files with 974 additions and 887 deletions
|
@ -31,7 +31,6 @@ import androidx.fragment.app.activityViewModels
|
|||
import androidx.navigation.NavController
|
||||
import androidx.navigation.NavDestination
|
||||
import androidx.navigation.findNavController
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import com.google.android.material.R as MR
|
||||
import com.google.android.material.bottomsheet.BackportBottomSheetBehavior
|
||||
import com.google.android.material.shape.MaterialShapeDrawable
|
||||
|
@ -43,23 +42,18 @@ import org.oxycblt.auxio.databinding.FragmentMainBinding
|
|||
import org.oxycblt.auxio.detail.DetailViewModel
|
||||
import org.oxycblt.auxio.list.selection.SelectionViewModel
|
||||
import org.oxycblt.auxio.music.Music
|
||||
import org.oxycblt.auxio.music.MusicViewModel
|
||||
import org.oxycblt.auxio.music.Playlist
|
||||
import org.oxycblt.auxio.music.Song
|
||||
import org.oxycblt.auxio.navigation.MainNavigationAction
|
||||
import org.oxycblt.auxio.navigation.NavigationViewModel
|
||||
import org.oxycblt.auxio.playback.Panel
|
||||
import org.oxycblt.auxio.playback.PlaybackBottomSheetBehavior
|
||||
import org.oxycblt.auxio.playback.PlaybackViewModel
|
||||
import org.oxycblt.auxio.playback.queue.QueueBottomSheetBehavior
|
||||
import org.oxycblt.auxio.ui.ViewBindingFragment
|
||||
import org.oxycblt.auxio.util.collect
|
||||
import org.oxycblt.auxio.util.collectImmediately
|
||||
import org.oxycblt.auxio.util.context
|
||||
import org.oxycblt.auxio.util.coordinatorLayoutBehavior
|
||||
import org.oxycblt.auxio.util.getAttrColorCompat
|
||||
import org.oxycblt.auxio.util.getDimen
|
||||
import org.oxycblt.auxio.util.logD
|
||||
import org.oxycblt.auxio.util.navigateSafe
|
||||
import org.oxycblt.auxio.util.systemBarInsetsCompat
|
||||
import org.oxycblt.auxio.util.unlikelyToBeNull
|
||||
|
||||
|
@ -76,8 +70,6 @@ class MainFragment :
|
|||
ViewBindingFragment<FragmentMainBinding>(),
|
||||
ViewTreeObserver.OnPreDrawListener,
|
||||
NavController.OnDestinationChangedListener {
|
||||
private val navModel: NavigationViewModel by activityViewModels()
|
||||
private val musicModel: MusicViewModel by activityViewModels()
|
||||
private val playbackModel: PlaybackViewModel by activityViewModels()
|
||||
private val selectionModel: SelectionViewModel by activityViewModels()
|
||||
private val detailModel: DetailViewModel by activityViewModels()
|
||||
|
@ -148,11 +140,7 @@ class MainFragment :
|
|||
// In portrait mode, set up click listeners on the stacked sheets.
|
||||
logD("Configuring stacked bottom sheets")
|
||||
unlikelyToBeNull(binding.queueHandleWrapper).setOnClickListener {
|
||||
if (playbackSheetBehavior.state == BackportBottomSheetBehavior.STATE_EXPANDED &&
|
||||
queueSheetBehavior.targetState == BackportBottomSheetBehavior.STATE_COLLAPSED) {
|
||||
// Playback sheet is expanded and queue sheet is collapsed, we can expand it.
|
||||
queueSheetBehavior.state = BackportBottomSheetBehavior.STATE_EXPANDED
|
||||
}
|
||||
playbackModel.openQueue()
|
||||
}
|
||||
} else {
|
||||
// Dual-pane mode, manually style the static queue sheet.
|
||||
|
@ -175,16 +163,8 @@ class MainFragment :
|
|||
// --- VIEWMODEL SETUP ---
|
||||
collectImmediately(detailModel.editedPlaylist, detailBackCallback::invalidateEnabled)
|
||||
collectImmediately(selectionModel.selected, selectionBackCallback::invalidateEnabled)
|
||||
collect(musicModel.newPlaylistSongs.flow, ::handleNewPlaylist)
|
||||
collect(musicModel.playlistToRename.flow, ::handleRenamePlaylist)
|
||||
collect(musicModel.playlistToDelete.flow, ::handleDeletePlaylist)
|
||||
collect(musicModel.songsToAdd.flow, ::handleAddToPlaylist)
|
||||
collect(navModel.mainNavigationAction.flow, ::handleMainNavigation)
|
||||
collect(navModel.exploreNavigationItem.flow, ::handleExploreNavigation)
|
||||
collect(navModel.exploreArtistNavigationItem.flow, ::handleArtistNavigationPicker)
|
||||
collectImmediately(playbackModel.song, ::updateSong)
|
||||
collect(playbackModel.artistPickerSong.flow, ::handlePlaybackArtistPicker)
|
||||
collect(playbackModel.genrePickerSong.flow, ::handlePlaybackGenrePicker)
|
||||
collectImmediately(playbackModel.openPanel.flow, ::handlePanel)
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
|
@ -322,33 +302,6 @@ class MainFragment :
|
|||
selectionModel.drop()
|
||||
}
|
||||
|
||||
private fun handleMainNavigation(action: MainNavigationAction?) {
|
||||
if (action != null) {
|
||||
when (action) {
|
||||
is MainNavigationAction.OpenPlaybackPanel -> tryOpenPlaybackPanel()
|
||||
is MainNavigationAction.ClosePlaybackPanel -> tryClosePlaybackPanel()
|
||||
is MainNavigationAction.Directions ->
|
||||
findNavController().navigateSafe(action.directions)
|
||||
}
|
||||
navModel.mainNavigationAction.consume()
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleExploreNavigation(item: Music?) {
|
||||
if (item != null) {
|
||||
tryClosePlaybackPanel()
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleArtistNavigationPicker(item: Music?) {
|
||||
if (item != null) {
|
||||
navModel.mainNavigateTo(
|
||||
MainNavigationAction.Directions(
|
||||
MainFragmentDirections.actionPickNavigationArtist(item.uid)))
|
||||
navModel.exploreArtistNavigationItem.consume()
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateSong(song: Song?) {
|
||||
if (song != null) {
|
||||
tryShowSheets()
|
||||
|
@ -357,56 +310,15 @@ class MainFragment :
|
|||
}
|
||||
}
|
||||
|
||||
private fun handleNewPlaylist(songs: List<Song>?) {
|
||||
if (songs != null) {
|
||||
findNavController()
|
||||
.navigateSafe(
|
||||
MainFragmentDirections.actionNewPlaylist(songs.map { it.uid }.toTypedArray()))
|
||||
musicModel.newPlaylistSongs.consume()
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleRenamePlaylist(playlist: Playlist?) {
|
||||
if (playlist != null) {
|
||||
findNavController()
|
||||
.navigateSafe(MainFragmentDirections.actionRenamePlaylist(playlist.uid))
|
||||
musicModel.playlistToRename.consume()
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleDeletePlaylist(playlist: Playlist?) {
|
||||
if (playlist != null) {
|
||||
findNavController()
|
||||
.navigateSafe(MainFragmentDirections.actionDeletePlaylist(playlist.uid))
|
||||
musicModel.playlistToDelete.consume()
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleAddToPlaylist(songs: List<Song>?) {
|
||||
if (songs != null) {
|
||||
findNavController()
|
||||
.navigateSafe(
|
||||
MainFragmentDirections.actionAddToPlaylist(songs.map { it.uid }.toTypedArray()))
|
||||
musicModel.songsToAdd.consume()
|
||||
}
|
||||
}
|
||||
|
||||
private fun handlePlaybackArtistPicker(song: Song?) {
|
||||
if (song != null) {
|
||||
navModel.mainNavigateTo(
|
||||
MainNavigationAction.Directions(
|
||||
MainFragmentDirections.actionPickPlaybackArtist(song.uid)))
|
||||
playbackModel.artistPickerSong.consume()
|
||||
}
|
||||
}
|
||||
|
||||
private fun handlePlaybackGenrePicker(song: Song?) {
|
||||
if (song != null) {
|
||||
navModel.mainNavigateTo(
|
||||
MainNavigationAction.Directions(
|
||||
MainFragmentDirections.actionPickPlaybackGenre(song.uid)))
|
||||
playbackModel.genrePickerSong.consume()
|
||||
private fun handlePanel(panel: Panel?) {
|
||||
if (panel == null) return
|
||||
logD("Trying to update panel to $panel")
|
||||
when (panel) {
|
||||
is Panel.Main -> tryClosePlaybackPanel()
|
||||
is Panel.Playback -> tryOpenPlaybackPanel()
|
||||
is Panel.Queue -> tryOpenQueuePanel()
|
||||
}
|
||||
playbackModel.openPanel.consume()
|
||||
}
|
||||
|
||||
private fun tryOpenPlaybackPanel() {
|
||||
|
@ -446,6 +358,19 @@ class MainFragment :
|
|||
}
|
||||
}
|
||||
|
||||
private fun tryOpenQueuePanel() {
|
||||
val binding = requireBinding()
|
||||
val playbackSheetBehavior =
|
||||
binding.playbackSheet.coordinatorLayoutBehavior as PlaybackBottomSheetBehavior
|
||||
val queueSheetBehavior =
|
||||
(binding.queueSheet.coordinatorLayoutBehavior ?: return) as QueueBottomSheetBehavior
|
||||
if (playbackSheetBehavior.state == BackportBottomSheetBehavior.STATE_EXPANDED &&
|
||||
queueSheetBehavior.targetState == BackportBottomSheetBehavior.STATE_COLLAPSED) {
|
||||
// Playback sheet is expanded and queue sheet is collapsed, we can expand it.
|
||||
queueSheetBehavior.state = BackportBottomSheetBehavior.STATE_EXPANDED
|
||||
}
|
||||
}
|
||||
|
||||
private fun tryShowSheets() {
|
||||
val binding = requireBinding()
|
||||
val playbackSheetBehavior =
|
||||
|
|
|
@ -42,14 +42,12 @@ import org.oxycblt.auxio.list.ListFragment
|
|||
import org.oxycblt.auxio.list.Sort
|
||||
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
|
||||
import org.oxycblt.auxio.music.MusicMode
|
||||
import org.oxycblt.auxio.music.MusicParent
|
||||
import org.oxycblt.auxio.music.MusicViewModel
|
||||
import org.oxycblt.auxio.music.Song
|
||||
import org.oxycblt.auxio.music.info.Disc
|
||||
import org.oxycblt.auxio.navigation.NavigationViewModel
|
||||
import org.oxycblt.auxio.playback.PlaybackViewModel
|
||||
import org.oxycblt.auxio.util.canScroll
|
||||
import org.oxycblt.auxio.util.collect
|
||||
|
@ -72,11 +70,10 @@ class AlbumDetailFragment :
|
|||
ListFragment<Song, FragmentDetailBinding>(),
|
||||
AlbumDetailHeaderAdapter.Listener,
|
||||
DetailListAdapter.Listener<Song> {
|
||||
private val detailModel: DetailViewModel by activityViewModels()
|
||||
override val navModel: NavigationViewModel by activityViewModels()
|
||||
override val playbackModel: PlaybackViewModel by activityViewModels()
|
||||
override val musicModel: MusicViewModel by activityViewModels()
|
||||
override val detailModel: DetailViewModel by activityViewModels()
|
||||
override val selectionModel: SelectionViewModel 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
|
||||
// as a UID, as that is the only safe way to parcel an album.
|
||||
private val args: AlbumDetailFragmentArgs by navArgs()
|
||||
|
@ -125,10 +122,12 @@ class AlbumDetailFragment :
|
|||
detailModel.setAlbum(args.albumUid)
|
||||
collectImmediately(detailModel.currentAlbum, ::updateAlbum)
|
||||
collectImmediately(detailModel.albumList, ::updateList)
|
||||
collect(detailModel.toShow.flow, ::handleShow)
|
||||
collectImmediately(selectionModel.selected, ::updateSelection)
|
||||
collectImmediately(
|
||||
playbackModel.song, playbackModel.parent, playbackModel.isPlaying, ::updatePlayback)
|
||||
collect(navModel.exploreNavigationItem.flow, ::handleNavigation)
|
||||
collectImmediately(selectionModel.selected, ::updateSelection)
|
||||
collect(playbackModel.artistPickerSong.flow, ::handlePlayFromArtist)
|
||||
collect(playbackModel.genrePickerSong.flow, ::handlePlayFromGenre)
|
||||
}
|
||||
|
||||
override fun onDestroyBinding(binding: FragmentDetailBinding) {
|
||||
|
@ -221,7 +220,7 @@ class AlbumDetailFragment :
|
|||
}
|
||||
|
||||
override fun onNavigateToParentArtist() {
|
||||
navModel.exploreNavigateToParentArtist(unlikelyToBeNull(detailModel.currentAlbum.value))
|
||||
detailModel.showAlbum(unlikelyToBeNull(detailModel.currentAlbum.value))
|
||||
}
|
||||
|
||||
private fun updateAlbum(album: Album?) {
|
||||
|
@ -234,53 +233,88 @@ class AlbumDetailFragment :
|
|||
albumHeaderAdapter.setParent(album)
|
||||
}
|
||||
|
||||
private fun updatePlayback(song: Song?, parent: MusicParent?, isPlaying: Boolean) {
|
||||
albumListAdapter.setPlaying(
|
||||
song.takeIf { parent == detailModel.currentAlbum.value }, isPlaying)
|
||||
private fun updateList(list: List<Item>) {
|
||||
albumListAdapter.update(list, detailModel.albumInstructions.consume())
|
||||
}
|
||||
|
||||
private fun handleNavigation(item: Music?) {
|
||||
private fun handleShow(show: Show?) {
|
||||
val binding = requireBinding()
|
||||
when (item) {
|
||||
when (show) {
|
||||
is Show.SongDetails -> {
|
||||
logD("Navigating to ${show.song}")
|
||||
findNavController()
|
||||
.navigateSafe(AlbumDetailFragmentDirections.showSong(show.song.uid))
|
||||
}
|
||||
|
||||
// Songs should be scrolled to if the album matches, or a new detail
|
||||
// fragment should be launched otherwise.
|
||||
is Song -> {
|
||||
if (unlikelyToBeNull(detailModel.currentAlbum.value) == item.album) {
|
||||
logD("Navigating to a song in this album")
|
||||
scrollToAlbumSong(item)
|
||||
navModel.exploreNavigationItem.consume()
|
||||
is Show.SongAlbumDetails -> {
|
||||
if (unlikelyToBeNull(detailModel.currentAlbum.value) == show.song.album) {
|
||||
logD("Navigating to a ${show.song} in this album")
|
||||
scrollToAlbumSong(show.song)
|
||||
detailModel.toShow.consume()
|
||||
} else {
|
||||
logD("Navigating to another album")
|
||||
logD("Navigating to the album of ${show.song}")
|
||||
findNavController()
|
||||
.navigateSafe(AlbumDetailFragmentDirections.actionShowAlbum(item.album.uid))
|
||||
.navigateSafe(AlbumDetailFragmentDirections.showAlbum(show.song.album.uid))
|
||||
}
|
||||
}
|
||||
|
||||
// If the album matches, no need to do anything. Otherwise launch a new
|
||||
// detail fragment.
|
||||
is Album -> {
|
||||
if (unlikelyToBeNull(detailModel.currentAlbum.value) == item) {
|
||||
is Show.AlbumDetails -> {
|
||||
if (unlikelyToBeNull(detailModel.currentAlbum.value) == show.album) {
|
||||
logD("Navigating to the top of this album")
|
||||
binding.detailRecycler.scrollToPosition(0)
|
||||
navModel.exploreNavigationItem.consume()
|
||||
detailModel.toShow.consume()
|
||||
} else {
|
||||
logD("Navigating to another album")
|
||||
logD("Navigating to ${show.album}")
|
||||
findNavController()
|
||||
.navigateSafe(AlbumDetailFragmentDirections.actionShowAlbum(item.uid))
|
||||
.navigateSafe(AlbumDetailFragmentDirections.showAlbum(show.album.uid))
|
||||
}
|
||||
}
|
||||
|
||||
// Always launch a new ArtistDetailFragment.
|
||||
is Artist -> {
|
||||
logD("Navigating to another artist")
|
||||
is Show.ArtistDetails -> {
|
||||
logD("Navigating to ${show.artist}")
|
||||
findNavController()
|
||||
.navigateSafe(AlbumDetailFragmentDirections.actionShowArtist(item.uid))
|
||||
.navigateSafe(AlbumDetailFragmentDirections.showArtist(show.artist.uid))
|
||||
}
|
||||
is Show.SongArtistDetails -> {
|
||||
logD("Navigating to artist choices for ${show.song}")
|
||||
findNavController()
|
||||
.navigateSafe(AlbumDetailFragmentDirections.showArtist(show.song.uid))
|
||||
}
|
||||
is Show.AlbumArtistDetails -> {
|
||||
logD("Navigating to artist choices for ${show.album}")
|
||||
findNavController()
|
||||
.navigateSafe(AlbumDetailFragmentDirections.showArtist(show.album.uid))
|
||||
}
|
||||
is Show.GenreDetails,
|
||||
is Show.PlaylistDetails -> {
|
||||
error("Unexpected show command $show")
|
||||
}
|
||||
null -> {}
|
||||
else -> error("Unexpected datatype: ${item::class.java}")
|
||||
}
|
||||
}
|
||||
|
||||
private fun updatePlayback(song: Song?, parent: MusicParent?, isPlaying: Boolean) {
|
||||
albumListAdapter.setPlaying(
|
||||
song.takeIf { parent == detailModel.currentAlbum.value }, isPlaying)
|
||||
}
|
||||
|
||||
private fun handlePlayFromArtist(song: Song?) {
|
||||
if (song == null) return
|
||||
logD("Launching play from artist dialog for $song")
|
||||
findNavController().navigateSafe(AlbumDetailFragmentDirections.playFromArtist(song.uid))
|
||||
}
|
||||
|
||||
private fun handlePlayFromGenre(song: Song?) {
|
||||
if (song == null) return
|
||||
logD("Launching play from genre dialog for $song")
|
||||
findNavController().navigateSafe(AlbumDetailFragmentDirections.playFromGenre(song.uid))
|
||||
}
|
||||
|
||||
private fun scrollToAlbumSong(song: Song) {
|
||||
// Calculate where the item for the currently played song is
|
||||
val pos = detailModel.albumList.value.indexOf(song)
|
||||
|
@ -319,10 +353,6 @@ class AlbumDetailFragment :
|
|||
}
|
||||
}
|
||||
|
||||
private fun updateList(list: List<Item>) {
|
||||
albumListAdapter.update(list, detailModel.albumInstructions.consume())
|
||||
}
|
||||
|
||||
private fun updateSelection(selected: List<Music>) {
|
||||
albumListAdapter.setSelected(selected.toSet())
|
||||
|
||||
|
|
|
@ -47,7 +47,6 @@ import org.oxycblt.auxio.music.Music
|
|||
import org.oxycblt.auxio.music.MusicParent
|
||||
import org.oxycblt.auxio.music.MusicViewModel
|
||||
import org.oxycblt.auxio.music.Song
|
||||
import org.oxycblt.auxio.navigation.NavigationViewModel
|
||||
import org.oxycblt.auxio.playback.PlaybackViewModel
|
||||
import org.oxycblt.auxio.util.collect
|
||||
import org.oxycblt.auxio.util.collectImmediately
|
||||
|
@ -69,11 +68,10 @@ class ArtistDetailFragment :
|
|||
ListFragment<Music, FragmentDetailBinding>(),
|
||||
DetailHeaderAdapter.Listener,
|
||||
DetailListAdapter.Listener<Music> {
|
||||
private val detailModel: DetailViewModel by activityViewModels()
|
||||
override val navModel: NavigationViewModel by activityViewModels()
|
||||
override val playbackModel: PlaybackViewModel by activityViewModels()
|
||||
override val musicModel: MusicViewModel by activityViewModels()
|
||||
override val detailModel: DetailViewModel by activityViewModels()
|
||||
override val selectionModel: SelectionViewModel 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
|
||||
// as a UID, as that is the only safe way to parcel an artist.
|
||||
private val args: ArtistDetailFragmentArgs by navArgs()
|
||||
|
@ -125,9 +123,11 @@ class ArtistDetailFragment :
|
|||
detailModel.setArtist(args.artistUid)
|
||||
collectImmediately(detailModel.currentArtist, ::updateArtist)
|
||||
collectImmediately(detailModel.artistList, ::updateList)
|
||||
collect(detailModel.toShow.flow, ::handleShow)
|
||||
collectImmediately(
|
||||
playbackModel.song, playbackModel.parent, playbackModel.isPlaying, ::updatePlayback)
|
||||
collect(navModel.exploreNavigationItem.flow, ::handleNavigation)
|
||||
collect(playbackModel.artistPickerSong.flow, ::handlePlayFromArtist)
|
||||
collect(playbackModel.genrePickerSong.flow, ::handlePlayFromGenre)
|
||||
collectImmediately(selectionModel.selected, ::updateSelection)
|
||||
}
|
||||
|
||||
|
@ -174,7 +174,7 @@ class ArtistDetailFragment :
|
|||
|
||||
override fun onRealClick(item: Music) {
|
||||
when (item) {
|
||||
is Album -> navModel.exploreNavigateTo(item)
|
||||
is Album -> detailModel.showAlbum(item)
|
||||
is Song -> {
|
||||
val playbackMode = detailModel.playbackMode
|
||||
if (playbackMode != null) {
|
||||
|
@ -257,6 +257,57 @@ class ArtistDetailFragment :
|
|||
artistHeaderAdapter.setParent(artist)
|
||||
}
|
||||
|
||||
private fun updateList(list: List<Item>) {
|
||||
artistListAdapter.update(list, detailModel.artistInstructions.consume())
|
||||
}
|
||||
|
||||
private fun handleShow(show: Show?) {
|
||||
val binding = requireBinding()
|
||||
when (show) {
|
||||
is Show.SongDetails -> {
|
||||
logD("Navigating to ${show.song}")
|
||||
findNavController()
|
||||
.navigateSafe(ArtistDetailFragmentDirections.showSong(show.song.uid))
|
||||
}
|
||||
|
||||
// Songs should be shown in their album, not in their artist.
|
||||
is Show.SongAlbumDetails -> {
|
||||
logD("Navigating to the album of ${show.song}")
|
||||
findNavController()
|
||||
.navigateSafe(ArtistDetailFragmentDirections.showAlbum(show.song.album.uid))
|
||||
}
|
||||
|
||||
// Launch a new detail view for an album, even if it is part of
|
||||
// this artist.
|
||||
is Show.AlbumDetails -> {
|
||||
logD("Navigating to ${show.album}")
|
||||
findNavController()
|
||||
.navigateSafe(ArtistDetailFragmentDirections.showAlbum(show.album.uid))
|
||||
}
|
||||
|
||||
// If the artist that should be navigated to is this artist, then
|
||||
// scroll back to the top. Otherwise launch a new detail view.
|
||||
is Show.ArtistDetails -> {
|
||||
if (show.artist == detailModel.currentArtist.value) {
|
||||
logD("Navigating to the top of this artist")
|
||||
binding.detailRecycler.scrollToPosition(0)
|
||||
detailModel.toShow.consume()
|
||||
} else {
|
||||
logD("Navigating to ${show.artist}")
|
||||
findNavController()
|
||||
.navigateSafe(ArtistDetailFragmentDirections.showArtist(show.artist.uid))
|
||||
}
|
||||
}
|
||||
is Show.SongArtistDetails,
|
||||
is Show.AlbumArtistDetails,
|
||||
is Show.GenreDetails,
|
||||
is Show.PlaylistDetails -> {
|
||||
error("Unexpected show command $show")
|
||||
}
|
||||
null -> {}
|
||||
}
|
||||
}
|
||||
|
||||
private fun updatePlayback(song: Song?, parent: MusicParent?, isPlaying: Boolean) {
|
||||
val currentArtist = unlikelyToBeNull(detailModel.currentArtist.value)
|
||||
val playingItem =
|
||||
|
@ -272,43 +323,16 @@ class ArtistDetailFragment :
|
|||
artistListAdapter.setPlaying(playingItem, isPlaying)
|
||||
}
|
||||
|
||||
private fun handleNavigation(item: Music?) {
|
||||
val binding = requireBinding()
|
||||
|
||||
when (item) {
|
||||
// Songs should be shown in their album, not in their artist.
|
||||
is Song -> {
|
||||
logD("Navigating to another album")
|
||||
findNavController()
|
||||
.navigateSafe(ArtistDetailFragmentDirections.actionShowAlbum(item.album.uid))
|
||||
}
|
||||
// Launch a new detail view for an album, even if it is part of
|
||||
// this artist.
|
||||
is Album -> {
|
||||
logD("Navigating to another album")
|
||||
findNavController()
|
||||
.navigateSafe(ArtistDetailFragmentDirections.actionShowAlbum(item.uid))
|
||||
}
|
||||
// If the artist that should be navigated to is this artist, then
|
||||
// scroll back to the top. Otherwise launch a new detail view.
|
||||
is Artist -> {
|
||||
if (item.uid == detailModel.currentArtist.value?.uid) {
|
||||
logD("Navigating to the top of this artist")
|
||||
binding.detailRecycler.scrollToPosition(0)
|
||||
navModel.exploreNavigationItem.consume()
|
||||
} else {
|
||||
logD("Navigating to another artist")
|
||||
findNavController()
|
||||
.navigateSafe(ArtistDetailFragmentDirections.actionShowArtist(item.uid))
|
||||
}
|
||||
}
|
||||
null -> {}
|
||||
else -> error("Unexpected datatype: ${item::class.java}")
|
||||
}
|
||||
private fun handlePlayFromArtist(song: Song?) {
|
||||
if (song == null) return
|
||||
logD("Launching play from artist dialog for $song")
|
||||
findNavController().navigateSafe(AlbumDetailFragmentDirections.playFromArtist(song.uid))
|
||||
}
|
||||
|
||||
private fun updateList(list: List<Item>) {
|
||||
artistListAdapter.update(list, detailModel.artistInstructions.consume())
|
||||
private fun handlePlayFromGenre(song: Song?) {
|
||||
if (song == null) return
|
||||
logD("Launching play from genre dialog for $song")
|
||||
findNavController().navigateSafe(AlbumDetailFragmentDirections.playFromGenre(song.uid))
|
||||
}
|
||||
|
||||
private fun updateSelection(selected: List<Music>) {
|
||||
|
|
|
@ -70,6 +70,10 @@ constructor(
|
|||
private val musicSettings: MusicSettings,
|
||||
private val playbackSettings: PlaybackSettings
|
||||
) : ViewModel(), MusicRepository.UpdateListener {
|
||||
private val _toShow = MutableEvent<Show>()
|
||||
val toShow: Event<Show>
|
||||
get() = _toShow
|
||||
|
||||
// --- SONG ---
|
||||
|
||||
private var currentSongJob: Job? = null
|
||||
|
@ -237,6 +241,43 @@ constructor(
|
|||
}
|
||||
}
|
||||
|
||||
fun showSong(song: Song) = showImpl(Show.SongDetails(song))
|
||||
|
||||
fun showAlbum(song: Song) = showImpl(Show.SongAlbumDetails(song))
|
||||
|
||||
fun showAlbum(album: Album) = showImpl(Show.AlbumDetails(album))
|
||||
|
||||
fun showArtist(song: Song) =
|
||||
showImpl(
|
||||
if (song.artists.size > 1) {
|
||||
Show.SongArtistDetails(song)
|
||||
} else {
|
||||
Show.ArtistDetails(song.artists.first())
|
||||
})
|
||||
|
||||
fun showArtist(album: Album) =
|
||||
showImpl(
|
||||
if (album.artists.size > 1) {
|
||||
Show.AlbumArtistDetails(album)
|
||||
} else {
|
||||
Show.ArtistDetails(album.artists.first())
|
||||
})
|
||||
|
||||
fun showArtist(artist: Artist) = showImpl(Show.ArtistDetails(artist))
|
||||
|
||||
fun showGenre(genre: Genre) = showImpl(Show.GenreDetails(genre))
|
||||
|
||||
fun showPlaylist(playlist: Playlist) = showImpl(Show.PlaylistDetails(playlist))
|
||||
|
||||
private fun showImpl(show: Show) {
|
||||
val existing = toShow.flow.value
|
||||
if (existing != null) {
|
||||
logD("Already have pending show command $existing, ignoring $show")
|
||||
return
|
||||
}
|
||||
_toShow.put(show)
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a new [currentSong] from it's [Music.UID]. [currentSong] and [songAudioProperties] will
|
||||
* be updated to align with the new [Song].
|
||||
|
@ -582,3 +623,14 @@ constructor(
|
|||
val GENRE_ARTIST_SORT = Sort(Sort.Mode.ByName, Sort.Direction.ASCENDING)
|
||||
}
|
||||
}
|
||||
|
||||
sealed interface Show {
|
||||
data class SongDetails(val song: Song) : Show
|
||||
data class AlbumDetails(val album: Album) : Show
|
||||
data class SongAlbumDetails(val song: Song) : Show
|
||||
data class ArtistDetails(val artist: Artist) : Show
|
||||
data class SongArtistDetails(val song: Song) : Show
|
||||
data class AlbumArtistDetails(val album: Album) : Show
|
||||
data class GenreDetails(val genre: Genre) : Show
|
||||
data class PlaylistDetails(val playlist: Playlist) : Show
|
||||
}
|
||||
|
|
|
@ -41,14 +41,12 @@ import org.oxycblt.auxio.list.Item
|
|||
import org.oxycblt.auxio.list.ListFragment
|
||||
import org.oxycblt.auxio.list.Sort
|
||||
import org.oxycblt.auxio.list.selection.SelectionViewModel
|
||||
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.MusicParent
|
||||
import org.oxycblt.auxio.music.MusicViewModel
|
||||
import org.oxycblt.auxio.music.Song
|
||||
import org.oxycblt.auxio.navigation.NavigationViewModel
|
||||
import org.oxycblt.auxio.playback.PlaybackViewModel
|
||||
import org.oxycblt.auxio.util.collect
|
||||
import org.oxycblt.auxio.util.collectImmediately
|
||||
|
@ -70,11 +68,10 @@ class GenreDetailFragment :
|
|||
ListFragment<Music, FragmentDetailBinding>(),
|
||||
DetailHeaderAdapter.Listener,
|
||||
DetailListAdapter.Listener<Music> {
|
||||
private val detailModel: DetailViewModel by activityViewModels()
|
||||
override val navModel: NavigationViewModel by activityViewModels()
|
||||
override val playbackModel: PlaybackViewModel by activityViewModels()
|
||||
override val musicModel: MusicViewModel by activityViewModels()
|
||||
override val detailModel: DetailViewModel by activityViewModels()
|
||||
override val selectionModel: SelectionViewModel 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
|
||||
// as a UID, as that is the only safe way to parcel an genre.
|
||||
private val args: GenreDetailFragmentArgs by navArgs()
|
||||
|
@ -124,9 +121,11 @@ class GenreDetailFragment :
|
|||
detailModel.setGenre(args.genreUid)
|
||||
collectImmediately(detailModel.currentGenre, ::updatePlaylist)
|
||||
collectImmediately(detailModel.genreList, ::updateList)
|
||||
collect(detailModel.toShow.flow, ::handleShow)
|
||||
collectImmediately(
|
||||
playbackModel.song, playbackModel.parent, playbackModel.isPlaying, ::updatePlayback)
|
||||
collect(navModel.exploreNavigationItem.flow, ::handleNavigation)
|
||||
collect(playbackModel.artistPickerSong.flow, ::handlePlayFromArtist)
|
||||
collect(playbackModel.genrePickerSong.flow, ::handlePlayFromGenre)
|
||||
collectImmediately(selectionModel.selected, ::updateSelection)
|
||||
}
|
||||
|
||||
|
@ -173,7 +172,7 @@ class GenreDetailFragment :
|
|||
|
||||
override fun onRealClick(item: Music) {
|
||||
when (item) {
|
||||
is Artist -> navModel.exploreNavigateTo(item)
|
||||
is Artist -> detailModel.showArtist(item)
|
||||
is Song -> {
|
||||
val playbackMode = detailModel.playbackMode
|
||||
if (playbackMode != null) {
|
||||
|
@ -242,6 +241,60 @@ class GenreDetailFragment :
|
|||
genreHeaderAdapter.setParent(genre)
|
||||
}
|
||||
|
||||
private fun updateList(list: List<Item>) {
|
||||
genreListAdapter.update(list, detailModel.genreInstructions.consume())
|
||||
}
|
||||
|
||||
private fun handleShow(show: Show?) {
|
||||
when (show) {
|
||||
is Show.SongDetails -> {
|
||||
logD("Navigating to ${show.song}")
|
||||
findNavController()
|
||||
.navigateSafe(GenreDetailFragmentDirections.showSong(show.song.uid))
|
||||
}
|
||||
|
||||
// Songs should be scrolled to if the album matches, or a new detail
|
||||
// fragment should be launched otherwise.
|
||||
is Show.SongAlbumDetails -> {
|
||||
logD("Navigating to the album of ${show.song}")
|
||||
findNavController()
|
||||
.navigateSafe(GenreDetailFragmentDirections.showAlbum(show.song.album.uid))
|
||||
}
|
||||
|
||||
// If the album matches, no need to do anything. Otherwise launch a new
|
||||
// detail fragment.
|
||||
is Show.AlbumDetails -> {
|
||||
logD("Navigating to ${show.album}")
|
||||
findNavController()
|
||||
.navigateSafe(GenreDetailFragmentDirections.showAlbum(show.album.uid))
|
||||
}
|
||||
|
||||
// Always launch a new ArtistDetailFragment.
|
||||
is Show.ArtistDetails -> {
|
||||
logD("Navigating to ${show.artist}")
|
||||
findNavController()
|
||||
.navigateSafe(GenreDetailFragmentDirections.showArtist(show.artist.uid))
|
||||
}
|
||||
is Show.SongArtistDetails -> {
|
||||
logD("Navigating to artist choices for ${show.song}")
|
||||
findNavController()
|
||||
.navigateSafe(GenreDetailFragmentDirections.showArtist(show.song.uid))
|
||||
}
|
||||
is Show.AlbumArtistDetails -> {
|
||||
logD("Navigating to artist choices for ${show.album}")
|
||||
findNavController()
|
||||
.navigateSafe(GenreDetailFragmentDirections.showArtist(show.album.uid))
|
||||
}
|
||||
is Show.GenreDetails -> {
|
||||
logD("Navigated to this genre")
|
||||
}
|
||||
is Show.PlaylistDetails -> {
|
||||
error("Unexpected show command $show")
|
||||
}
|
||||
null -> {}
|
||||
}
|
||||
}
|
||||
|
||||
private fun updatePlayback(song: Song?, parent: MusicParent?, isPlaying: Boolean) {
|
||||
val currentGenre = unlikelyToBeNull(detailModel.currentGenre.value)
|
||||
val playingItem =
|
||||
|
@ -257,32 +310,16 @@ class GenreDetailFragment :
|
|||
genreListAdapter.setPlaying(playingItem, isPlaying)
|
||||
}
|
||||
|
||||
private fun handleNavigation(item: Music?) {
|
||||
when (item) {
|
||||
is Song -> {
|
||||
logD("Navigating to another song")
|
||||
findNavController()
|
||||
.navigateSafe(GenreDetailFragmentDirections.actionShowAlbum(item.album.uid))
|
||||
}
|
||||
is Album -> {
|
||||
logD("Navigating to another album")
|
||||
findNavController()
|
||||
.navigateSafe(GenreDetailFragmentDirections.actionShowAlbum(item.uid))
|
||||
}
|
||||
is Artist -> {
|
||||
logD("Navigating to another artist")
|
||||
findNavController()
|
||||
.navigateSafe(GenreDetailFragmentDirections.actionShowArtist(item.uid))
|
||||
}
|
||||
is Genre -> {
|
||||
navModel.exploreNavigationItem.consume()
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
private fun handlePlayFromArtist(song: Song?) {
|
||||
if (song == null) return
|
||||
logD("Launching play from artist dialog for $song")
|
||||
findNavController().navigateSafe(AlbumDetailFragmentDirections.playFromArtist(song.uid))
|
||||
}
|
||||
|
||||
private fun updateList(list: List<Item>) {
|
||||
genreListAdapter.update(list, detailModel.genreInstructions.consume())
|
||||
private fun handlePlayFromGenre(song: Song?) {
|
||||
if (song == null) return
|
||||
logD("Launching play from genre dialog for $song")
|
||||
findNavController().navigateSafe(AlbumDetailFragmentDirections.playFromGenre(song.uid))
|
||||
}
|
||||
|
||||
private fun updateSelection(selected: List<Music>) {
|
||||
|
|
|
@ -44,14 +44,11 @@ import org.oxycblt.auxio.list.Header
|
|||
import org.oxycblt.auxio.list.Item
|
||||
import org.oxycblt.auxio.list.ListFragment
|
||||
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
|
||||
import org.oxycblt.auxio.music.MusicParent
|
||||
import org.oxycblt.auxio.music.MusicViewModel
|
||||
import org.oxycblt.auxio.music.Playlist
|
||||
import org.oxycblt.auxio.music.Song
|
||||
import org.oxycblt.auxio.navigation.NavigationViewModel
|
||||
import org.oxycblt.auxio.playback.PlaybackViewModel
|
||||
import org.oxycblt.auxio.util.collect
|
||||
import org.oxycblt.auxio.util.collectImmediately
|
||||
|
@ -74,11 +71,10 @@ class PlaylistDetailFragment :
|
|||
DetailHeaderAdapter.Listener,
|
||||
PlaylistDetailListAdapter.Listener,
|
||||
NavController.OnDestinationChangedListener {
|
||||
private val detailModel: DetailViewModel by activityViewModels()
|
||||
override val navModel: NavigationViewModel by activityViewModels()
|
||||
override val playbackModel: PlaybackViewModel by activityViewModels()
|
||||
override val musicModel: MusicViewModel by activityViewModels()
|
||||
override val detailModel: DetailViewModel by activityViewModels()
|
||||
override val selectionModel: SelectionViewModel 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
|
||||
// as a UID, as that is the only safe way to parcel an playlist.
|
||||
private val args: PlaylistDetailFragmentArgs by navArgs()
|
||||
|
@ -139,10 +135,12 @@ class PlaylistDetailFragment :
|
|||
detailModel.setPlaylist(args.playlistUid)
|
||||
collectImmediately(detailModel.currentPlaylist, ::updatePlaylist)
|
||||
collectImmediately(detailModel.playlistList, ::updateList)
|
||||
collectImmediately(detailModel.editedPlaylist, ::updateEditedPlaylist)
|
||||
collectImmediately(detailModel.editedPlaylist, ::updateEditedList)
|
||||
collect(detailModel.toShow.flow, ::handleShow)
|
||||
collectImmediately(
|
||||
playbackModel.song, playbackModel.parent, playbackModel.isPlaying, ::updatePlayback)
|
||||
collect(navModel.exploreNavigationItem.flow, ::handleNavigation)
|
||||
collect(playbackModel.artistPickerSong.flow, ::handlePlayFromArtist)
|
||||
collect(playbackModel.genrePickerSong.flow, ::handlePlayFromGenre)
|
||||
collectImmediately(selectionModel.selected, ::updateSelection)
|
||||
}
|
||||
|
||||
|
@ -275,41 +273,11 @@ class PlaylistDetailFragment :
|
|||
playlistHeaderAdapter.setParent(playlist)
|
||||
}
|
||||
|
||||
private fun updatePlayback(song: Song?, parent: MusicParent?, isPlaying: Boolean) {
|
||||
// Prefer songs that are playing from this playlist.
|
||||
playlistListAdapter.setPlaying(
|
||||
song.takeIf { parent == detailModel.currentPlaylist.value }, isPlaying)
|
||||
}
|
||||
|
||||
private fun handleNavigation(item: Music?) {
|
||||
when (item) {
|
||||
is Song -> {
|
||||
logD("Navigating to another song")
|
||||
findNavController()
|
||||
.navigateSafe(PlaylistDetailFragmentDirections.actionShowAlbum(item.album.uid))
|
||||
}
|
||||
is Album -> {
|
||||
logD("Navigating to another album")
|
||||
findNavController()
|
||||
.navigateSafe(PlaylistDetailFragmentDirections.actionShowAlbum(item.uid))
|
||||
}
|
||||
is Artist -> {
|
||||
logD("Navigating to another artist")
|
||||
findNavController()
|
||||
.navigateSafe(PlaylistDetailFragmentDirections.actionShowArtist(item.uid))
|
||||
}
|
||||
is Playlist -> {
|
||||
navModel.exploreNavigationItem.consume()
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateList(list: List<Item>) {
|
||||
playlistListAdapter.update(list, detailModel.playlistInstructions.consume())
|
||||
}
|
||||
|
||||
private fun updateEditedPlaylist(editedPlaylist: List<Song>?) {
|
||||
private fun updateEditedList(editedPlaylist: List<Song>?) {
|
||||
playlistListAdapter.setEditing(editedPlaylist != null)
|
||||
playlistHeaderAdapter.setEditedPlaylist(editedPlaylist)
|
||||
selectionModel.drop()
|
||||
|
@ -324,6 +292,74 @@ class PlaylistDetailFragment :
|
|||
updateMultiToolbar()
|
||||
}
|
||||
|
||||
private fun handleShow(show: Show?) {
|
||||
when (show) {
|
||||
is Show.SongDetails -> {
|
||||
logD("Navigating to ${show.song}")
|
||||
findNavController()
|
||||
.navigateSafe(PlaylistDetailFragmentDirections.showSong(show.song.uid))
|
||||
}
|
||||
|
||||
// Songs should be scrolled to if the album matches, or a new detail
|
||||
// fragment should be launched otherwise.
|
||||
is Show.SongAlbumDetails -> {
|
||||
logD("Navigating to the album of ${show.song}")
|
||||
findNavController()
|
||||
.navigateSafe(PlaylistDetailFragmentDirections.showAlbum(show.song.album.uid))
|
||||
}
|
||||
|
||||
// If the album matches, no need to do anything. Otherwise launch a new
|
||||
// detail fragment.
|
||||
is Show.AlbumDetails -> {
|
||||
logD("Navigating to ${show.album}")
|
||||
findNavController()
|
||||
.navigateSafe(PlaylistDetailFragmentDirections.showAlbum(show.album.uid))
|
||||
}
|
||||
|
||||
// Always launch a new ArtistDetailFragment.
|
||||
is Show.ArtistDetails -> {
|
||||
logD("Navigating to ${show.artist}")
|
||||
findNavController()
|
||||
.navigateSafe(PlaylistDetailFragmentDirections.showArtist(show.artist.uid))
|
||||
}
|
||||
is Show.SongArtistDetails -> {
|
||||
logD("Navigating to artist choices for ${show.song}")
|
||||
findNavController()
|
||||
.navigateSafe(PlaylistDetailFragmentDirections.showArtist(show.song.uid))
|
||||
}
|
||||
is Show.AlbumArtistDetails -> {
|
||||
logD("Navigating to artist choices for ${show.album}")
|
||||
findNavController()
|
||||
.navigateSafe(PlaylistDetailFragmentDirections.showArtist(show.album.uid))
|
||||
}
|
||||
is Show.PlaylistDetails -> {
|
||||
logD("Navigated to this playlist")
|
||||
}
|
||||
is Show.GenreDetails -> {
|
||||
error("Unexpected show command $show")
|
||||
}
|
||||
null -> {}
|
||||
}
|
||||
}
|
||||
|
||||
private fun updatePlayback(song: Song?, parent: MusicParent?, isPlaying: Boolean) {
|
||||
// Prefer songs that are playing from this playlist.
|
||||
playlistListAdapter.setPlaying(
|
||||
song.takeIf { parent == detailModel.currentPlaylist.value }, isPlaying)
|
||||
}
|
||||
|
||||
private fun handlePlayFromArtist(song: Song?) {
|
||||
if (song == null) return
|
||||
logD("Launching play from artist dialog for $song")
|
||||
findNavController().navigateSafe(AlbumDetailFragmentDirections.playFromArtist(song.uid))
|
||||
}
|
||||
|
||||
private fun handlePlayFromGenre(song: Song?) {
|
||||
if (song == null) return
|
||||
logD("Launching play from genre dialog for $song")
|
||||
findNavController().navigateSafe(AlbumDetailFragmentDirections.playFromGenre(song.uid))
|
||||
}
|
||||
|
||||
private fun updateSelection(selected: List<Music>) {
|
||||
playlistListAdapter.setSelected(selected.toSet())
|
||||
|
||||
|
|
|
@ -70,6 +70,7 @@ class SongDetailDialog : ViewBindingDialogFragment<DialogSongDetailBinding>() {
|
|||
// DetailViewModel handles most initialization from the navigation argument.
|
||||
detailModel.setSong(args.songUid)
|
||||
collectImmediately(detailModel.currentSong, detailModel.songAudioProperties, ::updateSong)
|
||||
collectImmediately(detailModel.toShow.flow, ::handleShow)
|
||||
}
|
||||
|
||||
private fun updateSong(song: Song?, info: AudioProperties?) {
|
||||
|
@ -125,6 +126,15 @@ class SongDetailDialog : ViewBindingDialogFragment<DialogSongDetailBinding>() {
|
|||
}
|
||||
}
|
||||
|
||||
private fun handleShow(show: Show?) {
|
||||
if (show == null) return
|
||||
if (show is Show.SongDetails) {
|
||||
logD("Navigated to this song")
|
||||
} else {
|
||||
error("Unexpected show command $show")
|
||||
}
|
||||
}
|
||||
|
||||
private fun <T : Music> T.zipName(context: Context): String {
|
||||
val name = name
|
||||
return if (name is Name.Known && name.sort != null) {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* Copyright (c) 2023 Auxio Project
|
||||
* ArtistNavigationChoiceAdapter.kt is part of Auxio.
|
||||
* ArtistShowChoice.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,7 +16,7 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.oxycblt.auxio.navigation.picker
|
||||
package org.oxycblt.auxio.detail.picker
|
||||
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
|
@ -31,11 +31,11 @@ import org.oxycblt.auxio.util.inflater
|
|||
|
||||
/**
|
||||
* A [FlexibleListAdapter] that displays a list of [Artist] navigation choices, for use with
|
||||
* [NavigateToArtistDialog].
|
||||
* [ShowArtistDialog].
|
||||
*
|
||||
* @param listener A [ClickableListListener] to bind interactions to.
|
||||
*/
|
||||
class ArtistNavigationChoiceAdapter(private val listener: ClickableListListener<Artist>) :
|
||||
class ArtistShowChoice(private val listener: ClickableListListener<Artist>) :
|
||||
FlexibleListAdapter<Artist, ArtistNavigationChoiceViewHolder>(
|
||||
ArtistNavigationChoiceViewHolder.DIFF_CALLBACK) {
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) =
|
||||
|
@ -48,7 +48,7 @@ class ArtistNavigationChoiceAdapter(private val listener: ClickableListListener<
|
|||
|
||||
/**
|
||||
* A [DialogRecyclerView.ViewHolder] that displays a smaller variant of a typical [Artist] item, for
|
||||
* use [ArtistNavigationChoiceAdapter]. Use [from] to create an instance.
|
||||
* use [ArtistShowChoice]. Use [from] to create an instance.
|
||||
*
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
|
@ -16,7 +16,7 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.oxycblt.auxio.navigation.picker
|
||||
package org.oxycblt.auxio.detail.picker
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
|
@ -28,7 +28,9 @@ import org.oxycblt.auxio.music.Artist
|
|||
import org.oxycblt.auxio.music.Music
|
||||
import org.oxycblt.auxio.music.MusicRepository
|
||||
import org.oxycblt.auxio.music.Song
|
||||
import org.oxycblt.auxio.music.device.DeviceLibrary
|
||||
import org.oxycblt.auxio.util.logD
|
||||
import org.oxycblt.auxio.util.logW
|
||||
|
||||
/**
|
||||
* A [ViewModel] that stores the current information required for navigation picker dialogs
|
||||
|
@ -38,9 +40,9 @@ import org.oxycblt.auxio.util.logD
|
|||
@HiltViewModel
|
||||
class NavigationPickerViewModel @Inject constructor(private val musicRepository: MusicRepository) :
|
||||
ViewModel(), MusicRepository.UpdateListener {
|
||||
private val _artistChoices = MutableStateFlow<ArtistNavigationChoices?>(null)
|
||||
private val _artistChoices = MutableStateFlow<ArtistShowChoices?>(null)
|
||||
/** The current set of [Artist] choices to show in the picker, or null if to show nothing. */
|
||||
val artistChoices: StateFlow<ArtistNavigationChoices?>
|
||||
val artistChoices: StateFlow<ArtistShowChoices?>
|
||||
get() = _artistChoices
|
||||
|
||||
init {
|
||||
|
@ -51,18 +53,7 @@ class NavigationPickerViewModel @Inject constructor(private val musicRepository:
|
|||
if (!changes.deviceLibrary) return
|
||||
val deviceLibrary = musicRepository.deviceLibrary ?: return
|
||||
// Need to sanitize different items depending on the current set of choices.
|
||||
_artistChoices.value =
|
||||
when (val choices = _artistChoices.value) {
|
||||
is SongArtistNavigationChoices ->
|
||||
deviceLibrary.findSong(choices.song.uid)?.let {
|
||||
SongArtistNavigationChoices(it)
|
||||
}
|
||||
is AlbumArtistNavigationChoices ->
|
||||
deviceLibrary.findAlbum(choices.album.uid)?.let {
|
||||
AlbumArtistNavigationChoices(it)
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
_artistChoices.value = _artistChoices.value?.sanitize(deviceLibrary)
|
||||
logD("Updated artist choices: ${_artistChoices.value}")
|
||||
}
|
||||
|
||||
|
@ -83,14 +74,14 @@ class NavigationPickerViewModel @Inject constructor(private val musicRepository:
|
|||
when (val music = musicRepository.find(itemUid)) {
|
||||
is Song -> {
|
||||
logD("Creating navigation choices for song")
|
||||
SongArtistNavigationChoices(music)
|
||||
ArtistShowChoices.FromSong(music)
|
||||
}
|
||||
is Album -> {
|
||||
logD("Creating navigation choices for album")
|
||||
AlbumArtistNavigationChoices(music)
|
||||
ArtistShowChoices.FromAlbum(music)
|
||||
}
|
||||
else -> {
|
||||
logD("Given song/album UID was invalid")
|
||||
logW("Given song/album UID was invalid")
|
||||
null
|
||||
}
|
||||
}
|
||||
|
@ -102,20 +93,29 @@ class NavigationPickerViewModel @Inject constructor(private val musicRepository:
|
|||
*
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
sealed interface ArtistNavigationChoices {
|
||||
sealed interface ArtistShowChoices {
|
||||
/** The UID of the item. */
|
||||
val uid: Music.UID
|
||||
/** The current [Artist] choices. */
|
||||
val choices: List<Artist>
|
||||
}
|
||||
/** Sanitize this instance with a [DeviceLibrary]. */
|
||||
fun sanitize(newLibrary: DeviceLibrary): ArtistShowChoices?
|
||||
|
||||
/** Backing implementation of [ArtistNavigationChoices] that is based on a [Song]. */
|
||||
private data class SongArtistNavigationChoices(val song: Song) : ArtistNavigationChoices {
|
||||
/** Backing implementation of [ArtistShowChoices] that is based on a [Song]. */
|
||||
class FromSong(val song: Song) : ArtistShowChoices {
|
||||
override val uid = song.uid
|
||||
override val choices = song.artists
|
||||
}
|
||||
override fun sanitize(newLibrary: DeviceLibrary) =
|
||||
newLibrary.findSong(uid)?.let { FromSong(it) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Backing implementation of [ArtistNavigationChoices] that is based on an
|
||||
* [AlbumArtistNavigationChoices].
|
||||
/**
|
||||
* Backing implementation of [ArtistShowChoices] that is based on an [AlbumArtistShowChoices].
|
||||
*/
|
||||
private data class AlbumArtistNavigationChoices(val album: Album) : ArtistNavigationChoices {
|
||||
data class FromAlbum(val album: Album) : ArtistShowChoices {
|
||||
override val uid = album.uid
|
||||
override val choices = album.artists
|
||||
override fun sanitize(newLibrary: DeviceLibrary) =
|
||||
newLibrary.findAlbum(uid)?.let { FromAlbum(it) }
|
||||
}
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* Copyright (c) 2022 Auxio Project
|
||||
* NavigateToArtistDialog.kt is part of Auxio.
|
||||
* ShowArtistDialog.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,7 +16,7 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.oxycblt.auxio.navigation.picker
|
||||
package org.oxycblt.auxio.detail.picker
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
|
@ -29,27 +29,27 @@ import androidx.recyclerview.widget.RecyclerView
|
|||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import org.oxycblt.auxio.R
|
||||
import org.oxycblt.auxio.databinding.DialogMusicChoicesBinding
|
||||
import org.oxycblt.auxio.detail.DetailViewModel
|
||||
import org.oxycblt.auxio.list.ClickableListListener
|
||||
import org.oxycblt.auxio.list.adapter.UpdateInstructions
|
||||
import org.oxycblt.auxio.music.Artist
|
||||
import org.oxycblt.auxio.navigation.NavigationViewModel
|
||||
import org.oxycblt.auxio.ui.ViewBindingDialogFragment
|
||||
import org.oxycblt.auxio.util.collectImmediately
|
||||
|
||||
/**
|
||||
* A picker [ViewBindingDialogFragment] intended for when [Artist] navigation is ambiguous.
|
||||
* A picker [ViewBindingDialogFragment] intended for when the [Artist] to show is ambiguous.
|
||||
*
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
@AndroidEntryPoint
|
||||
class NavigateToArtistDialog :
|
||||
class ShowArtistDialog :
|
||||
ViewBindingDialogFragment<DialogMusicChoicesBinding>(), ClickableListListener<Artist> {
|
||||
private val navigationModel: NavigationViewModel by activityViewModels()
|
||||
private val detailModel: DetailViewModel by activityViewModels()
|
||||
private val pickerModel: NavigationPickerViewModel by viewModels()
|
||||
// Information about what artists to show choices for is initially within the navigation
|
||||
// arguments as UIDs, as that is the only safe way to parcel an artist.
|
||||
private val args: NavigateToArtistDialogArgs by navArgs()
|
||||
private val choiceAdapter = ArtistNavigationChoiceAdapter(this)
|
||||
private val args: ShowArtistDialogArgs by navArgs()
|
||||
private val choiceAdapter = ArtistShowChoice(this)
|
||||
|
||||
override fun onConfigDialog(builder: AlertDialog.Builder) {
|
||||
builder.setTitle(R.string.lbl_artists).setNegativeButton(R.string.lbl_cancel, null)
|
||||
|
@ -83,7 +83,7 @@ class NavigateToArtistDialog :
|
|||
|
||||
override fun onClick(item: Artist, viewHolder: RecyclerView.ViewHolder) {
|
||||
// User made a choice, navigate to the artist.
|
||||
navigationModel.exploreNavigateTo(item)
|
||||
detailModel.showArtist(item)
|
||||
findNavController().navigateUp()
|
||||
}
|
||||
}
|
|
@ -43,9 +43,10 @@ import dagger.hilt.android.AndroidEntryPoint
|
|||
import java.lang.reflect.Field
|
||||
import kotlin.math.abs
|
||||
import org.oxycblt.auxio.BuildConfig
|
||||
import org.oxycblt.auxio.MainFragmentDirections
|
||||
import org.oxycblt.auxio.R
|
||||
import org.oxycblt.auxio.databinding.FragmentHomeBinding
|
||||
import org.oxycblt.auxio.detail.DetailViewModel
|
||||
import org.oxycblt.auxio.detail.Show
|
||||
import org.oxycblt.auxio.home.list.AlbumListFragment
|
||||
import org.oxycblt.auxio.home.list.ArtistListFragment
|
||||
import org.oxycblt.auxio.home.list.GenreListFragment
|
||||
|
@ -56,9 +57,6 @@ import org.oxycblt.auxio.home.tabs.Tab
|
|||
import org.oxycblt.auxio.list.Sort
|
||||
import org.oxycblt.auxio.list.selection.SelectionFragment
|
||||
import org.oxycblt.auxio.list.selection.SelectionViewModel
|
||||
import org.oxycblt.auxio.music.Album
|
||||
import org.oxycblt.auxio.music.Artist
|
||||
import org.oxycblt.auxio.music.Genre
|
||||
import org.oxycblt.auxio.music.IndexingProgress
|
||||
import org.oxycblt.auxio.music.IndexingState
|
||||
import org.oxycblt.auxio.music.Music
|
||||
|
@ -67,10 +65,7 @@ import org.oxycblt.auxio.music.MusicViewModel
|
|||
import org.oxycblt.auxio.music.NoAudioPermissionException
|
||||
import org.oxycblt.auxio.music.NoMusicException
|
||||
import org.oxycblt.auxio.music.PERMISSION_READ_AUDIO
|
||||
import org.oxycblt.auxio.music.Playlist
|
||||
import org.oxycblt.auxio.music.Song
|
||||
import org.oxycblt.auxio.navigation.MainNavigationAction
|
||||
import org.oxycblt.auxio.navigation.NavigationViewModel
|
||||
import org.oxycblt.auxio.playback.PlaybackViewModel
|
||||
import org.oxycblt.auxio.util.collect
|
||||
import org.oxycblt.auxio.util.collectImmediately
|
||||
|
@ -94,7 +89,7 @@ class HomeFragment :
|
|||
override val selectionModel: SelectionViewModel by activityViewModels()
|
||||
override val musicModel: MusicViewModel by activityViewModels()
|
||||
private val homeModel: HomeViewModel by activityViewModels()
|
||||
private val navModel: NavigationViewModel by activityViewModels()
|
||||
private val detailModel: DetailViewModel by activityViewModels()
|
||||
private var storagePermissionLauncher: ActivityResultLauncher<String>? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
|
@ -177,7 +172,7 @@ class HomeFragment :
|
|||
collectImmediately(homeModel.currentTabMode, ::updateCurrentTab)
|
||||
collectImmediately(homeModel.songsList, homeModel.isFastScrolling, ::updateFab)
|
||||
collectImmediately(musicModel.indexingState, ::updateIndexerState)
|
||||
collect(navModel.exploreNavigationItem.flow, ::handleNavigation)
|
||||
collect(detailModel.toShow.flow, ::handleShow)
|
||||
collectImmediately(selectionModel.selected, ::updateSelection)
|
||||
}
|
||||
|
||||
|
@ -218,19 +213,17 @@ class HomeFragment :
|
|||
R.id.action_search -> {
|
||||
logD("Navigating to search")
|
||||
setupAxisTransitions(MaterialSharedAxis.Z)
|
||||
findNavController().navigateSafe(HomeFragmentDirections.actionShowSearch())
|
||||
findNavController().navigateSafe(HomeFragmentDirections.search())
|
||||
true
|
||||
}
|
||||
R.id.action_settings -> {
|
||||
logD("Navigating to settings")
|
||||
navModel.mainNavigateTo(
|
||||
MainNavigationAction.Directions(MainFragmentDirections.actionShowSettings()))
|
||||
logD("Navigating to preferences")
|
||||
findNavController().navigateSafe(HomeFragmentDirections.preferences())
|
||||
true
|
||||
}
|
||||
R.id.action_about -> {
|
||||
logD("Navigating to about")
|
||||
navModel.mainNavigateTo(
|
||||
MainNavigationAction.Directions(MainFragmentDirections.actionShowAbout()))
|
||||
findNavController().navigateSafe(HomeFragmentDirections.about())
|
||||
true
|
||||
}
|
||||
|
||||
|
@ -500,19 +493,55 @@ class HomeFragment :
|
|||
}
|
||||
}
|
||||
|
||||
private fun handleNavigation(item: Music?) {
|
||||
val action =
|
||||
when (item) {
|
||||
is Song -> HomeFragmentDirections.actionShowAlbum(item.album.uid)
|
||||
is Album -> HomeFragmentDirections.actionShowAlbum(item.uid)
|
||||
is Artist -> HomeFragmentDirections.actionShowArtist(item.uid)
|
||||
is Genre -> HomeFragmentDirections.actionShowGenre(item.uid)
|
||||
is Playlist -> HomeFragmentDirections.actionShowPlaylist(item.uid)
|
||||
null -> return
|
||||
private fun handleShow(show: Show?) {
|
||||
when (show) {
|
||||
is Show.SongDetails -> {
|
||||
logD("Navigating to ${show.song}")
|
||||
findNavController().navigateSafe(HomeFragmentDirections.showSong(show.song.uid))
|
||||
}
|
||||
|
||||
// Songs should be scrolled to if the album matches, or a new detail
|
||||
// fragment should be launched otherwise.
|
||||
is Show.SongAlbumDetails -> {
|
||||
logD("Navigating to the album of ${show.song}")
|
||||
setupAxisTransitions(MaterialSharedAxis.X)
|
||||
findNavController().navigateSafe(action)
|
||||
findNavController()
|
||||
.navigateSafe(HomeFragmentDirections.showAlbum(show.song.album.uid))
|
||||
}
|
||||
|
||||
// If the album matches, no need to do anything. Otherwise launch a new
|
||||
// detail fragment.
|
||||
is Show.AlbumDetails -> {
|
||||
logD("Navigating to ${show.album}")
|
||||
setupAxisTransitions(MaterialSharedAxis.X)
|
||||
findNavController().navigateSafe(HomeFragmentDirections.showAlbum(show.album.uid))
|
||||
}
|
||||
|
||||
// Always launch a new ArtistDetailFragment.
|
||||
is Show.ArtistDetails -> {
|
||||
logD("Navigating to ${show.artist}")
|
||||
setupAxisTransitions(MaterialSharedAxis.X)
|
||||
findNavController().navigateSafe(HomeFragmentDirections.showArtist(show.artist.uid))
|
||||
}
|
||||
is Show.SongArtistDetails -> {
|
||||
logD("Navigating to artist choices for ${show.song}")
|
||||
findNavController().navigateSafe(HomeFragmentDirections.showArtist(show.song.uid))
|
||||
}
|
||||
is Show.AlbumArtistDetails -> {
|
||||
logD("Navigating to artist choices for ${show.album}")
|
||||
findNavController().navigateSafe(HomeFragmentDirections.showArtist(show.album.uid))
|
||||
}
|
||||
is Show.GenreDetails -> {
|
||||
logD("Navigating to ${show.genre}")
|
||||
findNavController().navigateSafe(HomeFragmentDirections.showGenre(show.genre.uid))
|
||||
}
|
||||
is Show.PlaylistDetails -> {
|
||||
logD("Navigating to ${show.playlist}")
|
||||
findNavController()
|
||||
.navigateSafe(HomeFragmentDirections.showGenre(show.playlist.uid))
|
||||
}
|
||||
null -> {}
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateSelection(selected: List<Music>) {
|
||||
|
|
|
@ -28,6 +28,7 @@ import dagger.hilt.android.AndroidEntryPoint
|
|||
import java.util.Formatter
|
||||
import org.oxycblt.auxio.R
|
||||
import org.oxycblt.auxio.databinding.FragmentHomeListBinding
|
||||
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
|
||||
|
@ -42,7 +43,6 @@ import org.oxycblt.auxio.music.MusicMode
|
|||
import org.oxycblt.auxio.music.MusicParent
|
||||
import org.oxycblt.auxio.music.MusicViewModel
|
||||
import org.oxycblt.auxio.music.Song
|
||||
import org.oxycblt.auxio.navigation.NavigationViewModel
|
||||
import org.oxycblt.auxio.playback.PlaybackViewModel
|
||||
import org.oxycblt.auxio.playback.formatDurationMs
|
||||
import org.oxycblt.auxio.playback.secsToMs
|
||||
|
@ -59,7 +59,7 @@ class AlbumListFragment :
|
|||
FastScrollRecyclerView.Listener,
|
||||
FastScrollRecyclerView.PopupProvider {
|
||||
private val homeModel: HomeViewModel by activityViewModels()
|
||||
override val navModel: NavigationViewModel by activityViewModels()
|
||||
override val detailModel: DetailViewModel by activityViewModels()
|
||||
override val playbackModel: PlaybackViewModel by activityViewModels()
|
||||
override val musicModel: MusicViewModel by activityViewModels()
|
||||
override val selectionModel: SelectionViewModel by activityViewModels()
|
||||
|
@ -138,7 +138,7 @@ class AlbumListFragment :
|
|||
}
|
||||
|
||||
override fun onRealClick(item: Album) {
|
||||
navModel.exploreNavigateTo(item)
|
||||
detailModel.showAlbum(item)
|
||||
}
|
||||
|
||||
override fun onOpenMenu(item: Album, anchor: View) {
|
||||
|
|
|
@ -26,6 +26,7 @@ import androidx.fragment.app.activityViewModels
|
|||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import org.oxycblt.auxio.R
|
||||
import org.oxycblt.auxio.databinding.FragmentHomeListBinding
|
||||
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
|
||||
|
@ -40,7 +41,6 @@ import org.oxycblt.auxio.music.MusicMode
|
|||
import org.oxycblt.auxio.music.MusicParent
|
||||
import org.oxycblt.auxio.music.MusicViewModel
|
||||
import org.oxycblt.auxio.music.Song
|
||||
import org.oxycblt.auxio.navigation.NavigationViewModel
|
||||
import org.oxycblt.auxio.playback.PlaybackViewModel
|
||||
import org.oxycblt.auxio.playback.formatDurationMs
|
||||
import org.oxycblt.auxio.util.collectImmediately
|
||||
|
@ -57,7 +57,7 @@ class ArtistListFragment :
|
|||
FastScrollRecyclerView.PopupProvider,
|
||||
FastScrollRecyclerView.Listener {
|
||||
private val homeModel: HomeViewModel by activityViewModels()
|
||||
override val navModel: NavigationViewModel by activityViewModels()
|
||||
override val detailModel: DetailViewModel by activityViewModels()
|
||||
override val playbackModel: PlaybackViewModel by activityViewModels()
|
||||
override val musicModel: MusicViewModel by activityViewModels()
|
||||
override val selectionModel: SelectionViewModel by activityViewModels()
|
||||
|
@ -114,7 +114,7 @@ class ArtistListFragment :
|
|||
}
|
||||
|
||||
override fun onRealClick(item: Artist) {
|
||||
navModel.exploreNavigateTo(item)
|
||||
detailModel.showArtist(item)
|
||||
}
|
||||
|
||||
override fun onOpenMenu(item: Artist, anchor: View) {
|
||||
|
|
|
@ -26,6 +26,7 @@ import androidx.fragment.app.activityViewModels
|
|||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import org.oxycblt.auxio.R
|
||||
import org.oxycblt.auxio.databinding.FragmentHomeListBinding
|
||||
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
|
||||
|
@ -40,7 +41,6 @@ import org.oxycblt.auxio.music.MusicMode
|
|||
import org.oxycblt.auxio.music.MusicParent
|
||||
import org.oxycblt.auxio.music.MusicViewModel
|
||||
import org.oxycblt.auxio.music.Song
|
||||
import org.oxycblt.auxio.navigation.NavigationViewModel
|
||||
import org.oxycblt.auxio.playback.PlaybackViewModel
|
||||
import org.oxycblt.auxio.playback.formatDurationMs
|
||||
import org.oxycblt.auxio.util.collectImmediately
|
||||
|
@ -56,7 +56,7 @@ class GenreListFragment :
|
|||
FastScrollRecyclerView.PopupProvider,
|
||||
FastScrollRecyclerView.Listener {
|
||||
private val homeModel: HomeViewModel by activityViewModels()
|
||||
override val navModel: NavigationViewModel by activityViewModels()
|
||||
override val detailModel: DetailViewModel by activityViewModels()
|
||||
override val playbackModel: PlaybackViewModel by activityViewModels()
|
||||
override val musicModel: MusicViewModel by activityViewModels()
|
||||
override val selectionModel: SelectionViewModel by activityViewModels()
|
||||
|
@ -113,7 +113,7 @@ class GenreListFragment :
|
|||
}
|
||||
|
||||
override fun onRealClick(item: Genre) {
|
||||
navModel.exploreNavigateTo(item)
|
||||
detailModel.showGenre(item)
|
||||
}
|
||||
|
||||
override fun onOpenMenu(item: Genre, anchor: View) {
|
||||
|
|
|
@ -25,6 +25,7 @@ import android.view.ViewGroup
|
|||
import androidx.fragment.app.activityViewModels
|
||||
import org.oxycblt.auxio.R
|
||||
import org.oxycblt.auxio.databinding.FragmentHomeListBinding
|
||||
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
|
||||
|
@ -39,7 +40,6 @@ import org.oxycblt.auxio.music.MusicParent
|
|||
import org.oxycblt.auxio.music.MusicViewModel
|
||||
import org.oxycblt.auxio.music.Playlist
|
||||
import org.oxycblt.auxio.music.Song
|
||||
import org.oxycblt.auxio.navigation.NavigationViewModel
|
||||
import org.oxycblt.auxio.playback.PlaybackViewModel
|
||||
import org.oxycblt.auxio.playback.formatDurationMs
|
||||
import org.oxycblt.auxio.util.collectImmediately
|
||||
|
@ -54,7 +54,7 @@ class PlaylistListFragment :
|
|||
FastScrollRecyclerView.PopupProvider,
|
||||
FastScrollRecyclerView.Listener {
|
||||
private val homeModel: HomeViewModel by activityViewModels()
|
||||
override val navModel: NavigationViewModel by activityViewModels()
|
||||
override val detailModel: DetailViewModel by activityViewModels()
|
||||
override val playbackModel: PlaybackViewModel by activityViewModels()
|
||||
override val musicModel: MusicViewModel by activityViewModels()
|
||||
override val selectionModel: SelectionViewModel by activityViewModels()
|
||||
|
@ -111,7 +111,7 @@ class PlaylistListFragment :
|
|||
}
|
||||
|
||||
override fun onRealClick(item: Playlist) {
|
||||
navModel.exploreNavigateTo(item)
|
||||
detailModel.showPlaylist(item)
|
||||
}
|
||||
|
||||
override fun onOpenMenu(item: Playlist, anchor: View) {
|
||||
|
|
|
@ -28,6 +28,7 @@ import dagger.hilt.android.AndroidEntryPoint
|
|||
import java.util.Formatter
|
||||
import org.oxycblt.auxio.R
|
||||
import org.oxycblt.auxio.databinding.FragmentHomeListBinding
|
||||
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
|
||||
|
@ -41,7 +42,6 @@ import org.oxycblt.auxio.music.MusicMode
|
|||
import org.oxycblt.auxio.music.MusicParent
|
||||
import org.oxycblt.auxio.music.MusicViewModel
|
||||
import org.oxycblt.auxio.music.Song
|
||||
import org.oxycblt.auxio.navigation.NavigationViewModel
|
||||
import org.oxycblt.auxio.playback.PlaybackViewModel
|
||||
import org.oxycblt.auxio.playback.formatDurationMs
|
||||
import org.oxycblt.auxio.playback.secsToMs
|
||||
|
@ -58,7 +58,7 @@ class SongListFragment :
|
|||
FastScrollRecyclerView.PopupProvider,
|
||||
FastScrollRecyclerView.Listener {
|
||||
private val homeModel: HomeViewModel by activityViewModels()
|
||||
override val navModel: NavigationViewModel by activityViewModels()
|
||||
override val detailModel: DetailViewModel by activityViewModels()
|
||||
override val playbackModel: PlaybackViewModel by activityViewModels()
|
||||
override val musicModel: MusicViewModel by activityViewModels()
|
||||
override val selectionModel: SelectionViewModel by activityViewModels()
|
||||
|
|
|
@ -24,8 +24,8 @@ import androidx.appcompat.widget.PopupMenu
|
|||
import androidx.core.view.MenuCompat
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.viewbinding.ViewBinding
|
||||
import org.oxycblt.auxio.MainFragmentDirections
|
||||
import org.oxycblt.auxio.R
|
||||
import org.oxycblt.auxio.detail.DetailViewModel
|
||||
import org.oxycblt.auxio.list.selection.SelectionFragment
|
||||
import org.oxycblt.auxio.music.Album
|
||||
import org.oxycblt.auxio.music.Artist
|
||||
|
@ -33,8 +33,6 @@ import org.oxycblt.auxio.music.Genre
|
|||
import org.oxycblt.auxio.music.Music
|
||||
import org.oxycblt.auxio.music.Playlist
|
||||
import org.oxycblt.auxio.music.Song
|
||||
import org.oxycblt.auxio.navigation.MainNavigationAction
|
||||
import org.oxycblt.auxio.navigation.NavigationViewModel
|
||||
import org.oxycblt.auxio.util.logD
|
||||
import org.oxycblt.auxio.util.logW
|
||||
import org.oxycblt.auxio.util.share
|
||||
|
@ -47,7 +45,7 @@ import org.oxycblt.auxio.util.showToast
|
|||
*/
|
||||
abstract class ListFragment<in T : Music, VB : ViewBinding> :
|
||||
SelectionFragment<VB>(), SelectableListListener<T> {
|
||||
protected abstract val navModel: NavigationViewModel
|
||||
protected abstract val detailModel: DetailViewModel
|
||||
private var currentMenu: PopupMenu? = null
|
||||
|
||||
override fun onDestroyBinding(binding: VB) {
|
||||
|
@ -103,11 +101,11 @@ abstract class ListFragment<in T : Music, VB : ViewBinding> :
|
|||
true
|
||||
}
|
||||
R.id.action_go_artist -> {
|
||||
navModel.exploreNavigateToParentArtist(song)
|
||||
detailModel.showArtist(song)
|
||||
true
|
||||
}
|
||||
R.id.action_go_album -> {
|
||||
navModel.exploreNavigateTo(song.album)
|
||||
detailModel.showAlbum(song.album)
|
||||
true
|
||||
}
|
||||
R.id.action_share -> {
|
||||
|
@ -119,9 +117,7 @@ abstract class ListFragment<in T : Music, VB : ViewBinding> :
|
|||
true
|
||||
}
|
||||
R.id.action_song_detail -> {
|
||||
navModel.mainNavigateTo(
|
||||
MainNavigationAction.Directions(
|
||||
MainFragmentDirections.actionShowDetails(song.uid)))
|
||||
detailModel.showSong(song)
|
||||
true
|
||||
}
|
||||
else -> {
|
||||
|
@ -166,7 +162,7 @@ abstract class ListFragment<in T : Music, VB : ViewBinding> :
|
|||
true
|
||||
}
|
||||
R.id.action_go_artist -> {
|
||||
navModel.exploreNavigateToParentArtist(album)
|
||||
detailModel.showArtist(album)
|
||||
true
|
||||
}
|
||||
R.id.action_playlist_add -> {
|
||||
|
|
|
@ -1,44 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2023 Auxio Project
|
||||
* MainNavigationAction.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
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.oxycblt.auxio.navigation
|
||||
|
||||
import androidx.navigation.NavDirections
|
||||
|
||||
/**
|
||||
* Represents the possible actions within the main navigation graph. This can be used with
|
||||
* [NavigationViewModel] to initiate navigation in the main navigation graph from anywhere in the
|
||||
* app, including outside the main navigation graph.
|
||||
*
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
sealed interface MainNavigationAction {
|
||||
/** Expand the playback panel. */
|
||||
object OpenPlaybackPanel : MainNavigationAction
|
||||
|
||||
/** Collapse the playback bottom sheet. */
|
||||
object ClosePlaybackPanel : MainNavigationAction
|
||||
|
||||
/**
|
||||
* Navigate to the given [NavDirections].
|
||||
*
|
||||
* @param directions The [NavDirections] to navigate to. Assumed to be part of the main
|
||||
* navigation graph.
|
||||
*/
|
||||
data class Directions(val directions: NavDirections) : MainNavigationAction
|
||||
}
|
|
@ -1,127 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2022 Auxio Project
|
||||
* NavigationViewModel.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
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.oxycblt.auxio.navigation
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import org.oxycblt.auxio.music.Album
|
||||
import org.oxycblt.auxio.music.Artist
|
||||
import org.oxycblt.auxio.music.Music
|
||||
import org.oxycblt.auxio.music.Song
|
||||
import org.oxycblt.auxio.util.Event
|
||||
import org.oxycblt.auxio.util.MutableEvent
|
||||
import org.oxycblt.auxio.util.logD
|
||||
|
||||
/**
|
||||
* A [ViewModel] that handles complicated navigation functionality.
|
||||
*
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*
|
||||
* TODO: Unwind this into ViewModel-specific actions, and then reference those.
|
||||
*/
|
||||
class NavigationViewModel : ViewModel() {
|
||||
private val _mainNavigationAction = MutableEvent<MainNavigationAction>()
|
||||
/**
|
||||
* Flag for navigation within the main navigation graph. Only intended for use by MainFragment.
|
||||
*/
|
||||
val mainNavigationAction: Event<MainNavigationAction>
|
||||
get() = _mainNavigationAction
|
||||
|
||||
private val _exploreNavigationItem = MutableEvent<Music>()
|
||||
/**
|
||||
* Flag for navigation within the explore navigation graph. Observe this to coordinate
|
||||
* navigation to a specific [Music] item.
|
||||
*/
|
||||
val exploreNavigationItem: Event<Music>
|
||||
get() = _exploreNavigationItem
|
||||
|
||||
private val _exploreArtistNavigationItem = MutableEvent<Music>()
|
||||
/**
|
||||
* Variation of [exploreNavigationItem] for situations where the choice of parent [Artist] to
|
||||
* navigate to is ambiguous. Only intended for use by MainFragment, as the resolved choice will
|
||||
* eventually be assigned to [exploreNavigationItem].
|
||||
*/
|
||||
val exploreArtistNavigationItem: Event<Music>
|
||||
get() = _exploreArtistNavigationItem
|
||||
|
||||
/**
|
||||
* Navigate to something in the main navigation graph. This can be used by UIs in the explore
|
||||
* navigation graph to trigger navigation in the higher-level main navigation graph. Will do
|
||||
* nothing if already navigating.
|
||||
*
|
||||
* @param action The [MainNavigationAction] to perform.
|
||||
*/
|
||||
fun mainNavigateTo(action: MainNavigationAction) {
|
||||
if (_mainNavigationAction.flow.value != null) {
|
||||
logD("Already navigating, not doing main action")
|
||||
return
|
||||
}
|
||||
logD("Navigating with action $action")
|
||||
_mainNavigationAction.put(action)
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigate to a given [Music] item. Will do nothing if already navigating.
|
||||
*
|
||||
* @param music The [Music] to navigate to.
|
||||
*/
|
||||
fun exploreNavigateTo(music: Music) {
|
||||
if (_exploreNavigationItem.flow.value != null) {
|
||||
logD("Already navigating, not doing explore action")
|
||||
return
|
||||
}
|
||||
logD("Navigating to ${music.name}")
|
||||
_exploreNavigationItem.put(music)
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigate to one of the parent [Artist]'s of the given [Song].
|
||||
*
|
||||
* @param song The [Song] to navigate with. If there are multiple parent [Artist]s, a picker
|
||||
* dialog will be shown.
|
||||
*/
|
||||
fun exploreNavigateToParentArtist(song: Song) {
|
||||
logD("Navigating to parent artist of $song")
|
||||
exploreNavigateToParentArtistImpl(song, song.artists)
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigate to one of the parent [Artist]'s of the given [Album].
|
||||
*
|
||||
* @param album The [Album] to navigate with. If there are multiple parent [Artist]s, a picker
|
||||
* dialog will be shown.
|
||||
*/
|
||||
fun exploreNavigateToParentArtist(album: Album) {
|
||||
logD("Navigating to parent artist of $album")
|
||||
exploreNavigateToParentArtistImpl(album, album.artists)
|
||||
}
|
||||
|
||||
private fun exploreNavigateToParentArtistImpl(item: Music, artists: List<Artist>) {
|
||||
if (_exploreArtistNavigationItem.flow.value != null) {
|
||||
logD("Already navigating, not doing explore action")
|
||||
return
|
||||
}
|
||||
|
||||
if (artists.size == 1) {
|
||||
exploreNavigateTo(artists[0])
|
||||
} else {
|
||||
logD("Navigating to a choice of ${artists.map { it.name }}")
|
||||
_exploreArtistNavigationItem.put(item)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -25,10 +25,9 @@ import com.google.android.material.R as MR
|
|||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import org.oxycblt.auxio.R
|
||||
import org.oxycblt.auxio.databinding.FragmentPlaybackBarBinding
|
||||
import org.oxycblt.auxio.detail.DetailViewModel
|
||||
import org.oxycblt.auxio.music.Song
|
||||
import org.oxycblt.auxio.music.resolveNames
|
||||
import org.oxycblt.auxio.navigation.MainNavigationAction
|
||||
import org.oxycblt.auxio.navigation.NavigationViewModel
|
||||
import org.oxycblt.auxio.playback.state.RepeatMode
|
||||
import org.oxycblt.auxio.ui.ViewBindingFragment
|
||||
import org.oxycblt.auxio.util.collectImmediately
|
||||
|
@ -44,7 +43,7 @@ import org.oxycblt.auxio.util.logD
|
|||
@AndroidEntryPoint
|
||||
class PlaybackBarFragment : ViewBindingFragment<FragmentPlaybackBarBinding>() {
|
||||
private val playbackModel: PlaybackViewModel by activityViewModels()
|
||||
private val navModel: NavigationViewModel by activityViewModels()
|
||||
private val detailModel: DetailViewModel by activityViewModels()
|
||||
|
||||
override fun onCreateBinding(inflater: LayoutInflater) =
|
||||
FragmentPlaybackBarBinding.inflate(inflater)
|
||||
|
@ -58,9 +57,9 @@ class PlaybackBarFragment : ViewBindingFragment<FragmentPlaybackBarBinding>() {
|
|||
|
||||
// --- UI SETUP ---
|
||||
binding.root.apply {
|
||||
setOnClickListener { navModel.mainNavigateTo(MainNavigationAction.OpenPlaybackPanel) }
|
||||
setOnClickListener { playbackModel.openPlayback() }
|
||||
setOnLongClickListener {
|
||||
playbackModel.song.value?.let(navModel::exploreNavigateTo)
|
||||
playbackModel.song.value?.let(detailModel::showAlbum)
|
||||
true
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,15 +30,13 @@ import androidx.appcompat.widget.Toolbar
|
|||
import androidx.core.view.updatePadding
|
||||
import androidx.fragment.app.activityViewModels
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import org.oxycblt.auxio.MainFragmentDirections
|
||||
import org.oxycblt.auxio.R
|
||||
import org.oxycblt.auxio.databinding.FragmentPlaybackPanelBinding
|
||||
import org.oxycblt.auxio.detail.DetailViewModel
|
||||
import org.oxycblt.auxio.music.MusicParent
|
||||
import org.oxycblt.auxio.music.MusicViewModel
|
||||
import org.oxycblt.auxio.music.Song
|
||||
import org.oxycblt.auxio.music.resolveNames
|
||||
import org.oxycblt.auxio.navigation.MainNavigationAction
|
||||
import org.oxycblt.auxio.navigation.NavigationViewModel
|
||||
import org.oxycblt.auxio.playback.state.RepeatMode
|
||||
import org.oxycblt.auxio.playback.ui.StyledSeekBar
|
||||
import org.oxycblt.auxio.ui.ViewBindingFragment
|
||||
|
@ -63,7 +61,7 @@ class PlaybackPanelFragment :
|
|||
StyledSeekBar.Listener {
|
||||
private val playbackModel: PlaybackViewModel by activityViewModels()
|
||||
private val musicModel: MusicViewModel by activityViewModels()
|
||||
private val navModel: NavigationViewModel by activityViewModels()
|
||||
private val detailModel: DetailViewModel by activityViewModels()
|
||||
private var equalizerLauncher: ActivityResultLauncher<Intent>? = null
|
||||
|
||||
override fun onCreateBinding(inflater: LayoutInflater) =
|
||||
|
@ -90,9 +88,7 @@ class PlaybackPanelFragment :
|
|||
}
|
||||
|
||||
binding.playbackToolbar.apply {
|
||||
setNavigationOnClickListener {
|
||||
navModel.mainNavigateTo(MainNavigationAction.ClosePlaybackPanel)
|
||||
}
|
||||
setNavigationOnClickListener { playbackModel.openMain() }
|
||||
setOnMenuItemClickListener(this@PlaybackPanelFragment)
|
||||
}
|
||||
|
||||
|
@ -100,7 +96,7 @@ class PlaybackPanelFragment :
|
|||
// respective item.
|
||||
binding.playbackSong.apply {
|
||||
isSelected = true
|
||||
setOnClickListener { playbackModel.song.value?.let(navModel::exploreNavigateTo) }
|
||||
setOnClickListener { playbackModel.song.value?.let(detailModel::showAlbum) }
|
||||
}
|
||||
binding.playbackArtist.apply {
|
||||
isSelected = true
|
||||
|
@ -176,11 +172,7 @@ class PlaybackPanelFragment :
|
|||
true
|
||||
}
|
||||
R.id.action_song_detail -> {
|
||||
playbackModel.song.value?.let { song ->
|
||||
navModel.mainNavigateTo(
|
||||
MainNavigationAction.Directions(
|
||||
MainFragmentDirections.actionShowDetails(song.uid)))
|
||||
}
|
||||
playbackModel.song.value?.let(detailModel::showSong)
|
||||
true
|
||||
}
|
||||
R.id.action_share -> {
|
||||
|
@ -237,12 +229,10 @@ class PlaybackPanelFragment :
|
|||
}
|
||||
|
||||
private fun navigateToCurrentArtist() {
|
||||
val song = playbackModel.song.value ?: return
|
||||
navModel.exploreNavigateToParentArtist(song)
|
||||
playbackModel.song.value?.let(detailModel::showArtist)
|
||||
}
|
||||
|
||||
private fun navigateToCurrentAlbum() {
|
||||
val song = playbackModel.song.value ?: return
|
||||
navModel.exploreNavigateTo(song.album)
|
||||
playbackModel.song.value?.let(detailModel::showAlbum)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -89,6 +89,10 @@ constructor(
|
|||
val isShuffled: StateFlow<Boolean>
|
||||
get() = _isShuffled
|
||||
|
||||
private val _openPanel = MutableEvent<Panel>()
|
||||
val openPanel: Event<Panel>
|
||||
get() = _openPanel
|
||||
|
||||
private val _artistPlaybackPickerSong = MutableEvent<Song>()
|
||||
/**
|
||||
* Flag signaling to open a picker dialog in order to resolve an ambiguous choice when playing a
|
||||
|
@ -555,6 +559,20 @@ constructor(
|
|||
playbackManager.repeatMode = playbackManager.repeatMode.increment()
|
||||
}
|
||||
|
||||
// --- UI CONTROL ---
|
||||
fun openMain() = openImpl(Panel.Main)
|
||||
fun openPlayback() = openImpl(Panel.Playback)
|
||||
fun openQueue() = openImpl(Panel.Queue)
|
||||
|
||||
private fun openImpl(panel: Panel) {
|
||||
val existing = openPanel.flow.value
|
||||
if (existing != null) {
|
||||
logD("Already opening $existing, ignoring opening $panel")
|
||||
return
|
||||
}
|
||||
_openPanel.put(panel)
|
||||
}
|
||||
|
||||
// --- SAVE/RESTORE FUNCTIONS ---
|
||||
|
||||
/**
|
||||
|
@ -598,3 +616,9 @@ constructor(
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
sealed interface Panel {
|
||||
object Main : Panel
|
||||
object Playback : Panel
|
||||
object Queue : Panel
|
||||
}
|
||||
|
|
|
@ -34,6 +34,8 @@ import com.google.android.material.transition.MaterialSharedAxis
|
|||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import org.oxycblt.auxio.R
|
||||
import org.oxycblt.auxio.databinding.FragmentSearchBinding
|
||||
import org.oxycblt.auxio.detail.DetailViewModel
|
||||
import org.oxycblt.auxio.detail.Show
|
||||
import org.oxycblt.auxio.list.Divider
|
||||
import org.oxycblt.auxio.list.Header
|
||||
import org.oxycblt.auxio.list.Item
|
||||
|
@ -47,7 +49,6 @@ import org.oxycblt.auxio.music.MusicParent
|
|||
import org.oxycblt.auxio.music.MusicViewModel
|
||||
import org.oxycblt.auxio.music.Playlist
|
||||
import org.oxycblt.auxio.music.Song
|
||||
import org.oxycblt.auxio.navigation.NavigationViewModel
|
||||
import org.oxycblt.auxio.playback.PlaybackViewModel
|
||||
import org.oxycblt.auxio.util.collect
|
||||
import org.oxycblt.auxio.util.collectImmediately
|
||||
|
@ -67,7 +68,7 @@ import org.oxycblt.auxio.util.setFullWidthLookup
|
|||
*/
|
||||
@AndroidEntryPoint
|
||||
class SearchFragment : ListFragment<Music, FragmentSearchBinding>() {
|
||||
override val navModel: NavigationViewModel by activityViewModels()
|
||||
override val detailModel: DetailViewModel by activityViewModels()
|
||||
override val playbackModel: PlaybackViewModel by activityViewModels()
|
||||
override val musicModel: MusicViewModel by activityViewModels()
|
||||
override val selectionModel: SelectionViewModel by activityViewModels()
|
||||
|
@ -137,7 +138,7 @@ class SearchFragment : ListFragment<Music, FragmentSearchBinding>() {
|
|||
collectImmediately(searchModel.searchResults, ::updateSearchResults)
|
||||
collectImmediately(
|
||||
playbackModel.song, playbackModel.parent, playbackModel.isPlaying, ::updatePlayback)
|
||||
collect(navModel.exploreNavigationItem.flow, ::handleNavigation)
|
||||
collect(detailModel.toShow.flow, ::handleShow)
|
||||
collectImmediately(selectionModel.selected, ::updateSelection)
|
||||
}
|
||||
|
||||
|
@ -167,8 +168,11 @@ class SearchFragment : ListFragment<Music, FragmentSearchBinding>() {
|
|||
|
||||
override fun onRealClick(item: Music) {
|
||||
when (item) {
|
||||
is MusicParent -> navModel.exploreNavigateTo(item)
|
||||
is Song -> playbackModel.playFrom(item, searchModel.playbackMode)
|
||||
is Album -> detailModel.showAlbum(item)
|
||||
is Artist -> detailModel.showArtist(item)
|
||||
is Genre -> detailModel.showGenre(item)
|
||||
is Playlist -> detailModel.showPlaylist(item)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -200,19 +204,57 @@ class SearchFragment : ListFragment<Music, FragmentSearchBinding>() {
|
|||
searchAdapter.setPlaying(parent ?: song, isPlaying)
|
||||
}
|
||||
|
||||
private fun handleNavigation(item: Music?) {
|
||||
val action =
|
||||
when (item) {
|
||||
is Song -> SearchFragmentDirections.actionShowAlbum(item.album.uid)
|
||||
is Album -> SearchFragmentDirections.actionShowAlbum(item.uid)
|
||||
is Artist -> SearchFragmentDirections.actionShowArtist(item.uid)
|
||||
is Genre -> SearchFragmentDirections.actionShowGenre(item.uid)
|
||||
is Playlist -> SearchFragmentDirections.actionShowPlaylist(item.uid)
|
||||
null -> return
|
||||
private fun handleShow(show: Show?) {
|
||||
when (show) {
|
||||
is Show.SongDetails -> {
|
||||
logD("Navigating to ${show.song}")
|
||||
findNavController().navigateSafe(SearchFragmentDirections.showSong(show.song.uid))
|
||||
}
|
||||
|
||||
// Songs should be scrolled to if the album matches, or a new detail
|
||||
// fragment should be launched otherwise.
|
||||
is Show.SongAlbumDetails -> {
|
||||
logD("Navigating to the album of ${show.song}")
|
||||
findNavController()
|
||||
.navigateSafe(SearchFragmentDirections.showAlbum(show.song.album.uid))
|
||||
}
|
||||
|
||||
// If the album matches, no need to do anything. Otherwise launch a new
|
||||
// detail fragment.
|
||||
is Show.AlbumDetails -> {
|
||||
logD("Navigating to ${show.album}")
|
||||
findNavController().navigateSafe(SearchFragmentDirections.showAlbum(show.album.uid))
|
||||
}
|
||||
|
||||
// Always launch a new ArtistDetailFragment.
|
||||
is Show.ArtistDetails -> {
|
||||
logD("Navigating to ${show.artist}")
|
||||
findNavController()
|
||||
.navigateSafe(SearchFragmentDirections.showArtist(show.artist.uid))
|
||||
}
|
||||
is Show.SongArtistDetails -> {
|
||||
logD("Navigating to artist choices for ${show.song}")
|
||||
findNavController().navigateSafe(SearchFragmentDirections.showArtist(show.song.uid))
|
||||
}
|
||||
is Show.AlbumArtistDetails -> {
|
||||
logD("Navigating to artist choices for ${show.album}")
|
||||
findNavController()
|
||||
.navigateSafe(SearchFragmentDirections.showArtist(show.album.uid))
|
||||
}
|
||||
is Show.GenreDetails -> {
|
||||
logD("Navigating to ${show.genre}")
|
||||
findNavController().navigateSafe(SearchFragmentDirections.showGenre(show.genre.uid))
|
||||
}
|
||||
is Show.PlaylistDetails -> {
|
||||
logD("Navigating to ${show.playlist}")
|
||||
findNavController()
|
||||
.navigateSafe(SearchFragmentDirections.showGenre(show.playlist.uid))
|
||||
}
|
||||
null -> {}
|
||||
}
|
||||
|
||||
// Keyboard is no longer needed.
|
||||
hideKeyboard()
|
||||
findNavController().navigateSafe(action)
|
||||
}
|
||||
|
||||
private fun updateSelection(selected: List<Music>) {
|
||||
|
|
|
@ -55,7 +55,7 @@ class RootPreferenceFragment : BasePreferenceFragment(R.xml.preferences_root) {
|
|||
|
||||
override fun onOpenDialogPreference(preference: WrappedDialogPreference) {
|
||||
if (preference.key == getString(R.string.set_key_music_dirs)) {
|
||||
findNavController().navigate(RootPreferenceFragmentDirections.goToMusicDirsDialog())
|
||||
findNavController().navigate(RootPreferenceFragmentDirections.musicDirsSettings())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -66,23 +66,21 @@ class RootPreferenceFragment : BasePreferenceFragment(R.xml.preferences_root) {
|
|||
when (preference.key) {
|
||||
getString(R.string.set_key_ui) -> {
|
||||
logD("Navigating to UI preferences")
|
||||
findNavController()
|
||||
.navigateSafe(RootPreferenceFragmentDirections.goToUiPreferences())
|
||||
findNavController().navigateSafe(RootPreferenceFragmentDirections.uiPreferences())
|
||||
}
|
||||
getString(R.string.set_key_personalize) -> {
|
||||
logD("Navigating to personalization preferences")
|
||||
findNavController()
|
||||
.navigateSafe(RootPreferenceFragmentDirections.goToPersonalizePreferences())
|
||||
.navigateSafe(RootPreferenceFragmentDirections.personalizePreferences())
|
||||
}
|
||||
getString(R.string.set_key_music) -> {
|
||||
logD("Navigating to music preferences")
|
||||
findNavController()
|
||||
.navigateSafe(RootPreferenceFragmentDirections.goToMusicPreferences())
|
||||
.navigateSafe(RootPreferenceFragmentDirections.musicPreferences())
|
||||
}
|
||||
getString(R.string.set_key_audio) -> {
|
||||
logD("Navigating to audio preferences")
|
||||
findNavController()
|
||||
.navigateSafe(RootPreferenceFragmentDirections.goToAudioPreferences())
|
||||
findNavController().navigateSafe(RootPreferenceFragmentDirections.audioPeferences())
|
||||
}
|
||||
getString(R.string.set_key_reindex) -> musicModel.refresh()
|
||||
getString(R.string.set_key_rescan) -> musicModel.rescan()
|
||||
|
|
|
@ -35,7 +35,7 @@ class AudioPreferenceFragment : BasePreferenceFragment(R.xml.preferences_audio)
|
|||
override fun onOpenDialogPreference(preference: WrappedDialogPreference) {
|
||||
if (preference.key == getString(R.string.set_key_pre_amp)) {
|
||||
logD("Navigating to pre-amp dialog")
|
||||
findNavController().navigateSafe(AudioPreferenceFragmentDirections.goToPreAmpDialog())
|
||||
findNavController().navigateSafe(AudioPreferenceFragmentDirections.preAmpSettings())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,8 +41,7 @@ class MusicPreferenceFragment : BasePreferenceFragment(R.xml.preferences_music)
|
|||
override fun onOpenDialogPreference(preference: WrappedDialogPreference) {
|
||||
if (preference.key == getString(R.string.set_key_separators)) {
|
||||
logD("Navigating to separator dialog")
|
||||
findNavController()
|
||||
.navigateSafe(MusicPreferenceFragmentDirections.goToSeparatorsDialog())
|
||||
findNavController().navigateSafe(MusicPreferenceFragmentDirections.separatorsSettings())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -34,8 +34,7 @@ class PersonalizePreferenceFragment : BasePreferenceFragment(R.xml.preferences_p
|
|||
override fun onOpenDialogPreference(preference: WrappedDialogPreference) {
|
||||
if (preference.key == getString(R.string.set_key_home_tabs)) {
|
||||
logD("Navigating to home tab dialog")
|
||||
findNavController()
|
||||
.navigateSafe(PersonalizePreferenceFragmentDirections.goToTabDialog())
|
||||
findNavController().navigateSafe(PersonalizePreferenceFragmentDirections.tabSettings())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,7 +43,7 @@ class UIPreferenceFragment : BasePreferenceFragment(R.xml.preferences_ui) {
|
|||
override fun onOpenDialogPreference(preference: WrappedDialogPreference) {
|
||||
if (preference.key == getString(R.string.set_key_accent)) {
|
||||
logD("Navigating to accent dialog")
|
||||
findNavController().navigateSafe(UIPreferenceFragmentDirections.goToAccentDialog())
|
||||
findNavController().navigateSafe(UIPreferenceFragmentDirections.accentSettings())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -13,7 +13,8 @@
|
|||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:layout_behavior="org.oxycblt.auxio.ui.BottomSheetContentBehavior"
|
||||
app:navGraph="@navigation/nav_explore"
|
||||
app:navGraph="@navigation/main"
|
||||
app:defaultNavHost="true"
|
||||
tools:layout="@layout/fragment_home" />
|
||||
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
|
|
|
@ -3,10 +3,8 @@
|
|||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/nav_host"
|
||||
android:name="androidx.navigation.fragment.NavHostFragment"
|
||||
android:name="org.oxycblt.auxio.MainFragment"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="?attr/colorSurface"
|
||||
app:defaultNavHost="true"
|
||||
app:navGraph="@navigation/nav_main"
|
||||
tools:layout="@layout/fragment_main" />
|
|
@ -14,7 +14,8 @@
|
|||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:layout_behavior="org.oxycblt.auxio.ui.BottomSheetContentBehavior"
|
||||
app:navGraph="@navigation/nav_explore"
|
||||
app:navGraph="@navigation/main"
|
||||
app:defaultNavHost="true"
|
||||
tools:layout="@layout/fragment_home" />
|
||||
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
|
|
388
app/src/main/res/navigation/main.xml
Normal file
388
app/src/main/res/navigation/main.xml
Normal file
|
@ -0,0 +1,388 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
app:startDestination="@id/home_fragment">
|
||||
|
||||
<fragment
|
||||
android:id="@+id/home_fragment"
|
||||
android:name="org.oxycblt.auxio.home.HomeFragment"
|
||||
android:label="fragment_home"
|
||||
tools:layout="@layout/fragment_home">
|
||||
<action
|
||||
android:id="@+id/search"
|
||||
app:destination="@id/search_fragment" />
|
||||
<action
|
||||
android:id="@+id/preferences"
|
||||
app:destination="@id/root_preferences_fragment" />
|
||||
<action
|
||||
android:id="@+id/about"
|
||||
app:destination="@id/about_fragment" />
|
||||
<action
|
||||
android:id="@+id/show_song"
|
||||
app:destination="@id/song_detail_dialog" />
|
||||
<action
|
||||
android:id="@+id/show_album"
|
||||
app:destination="@id/album_detail_fragment" />
|
||||
<action
|
||||
android:id="@+id/show_artist"
|
||||
app:destination="@id/artist_detail_fragment" />
|
||||
<action
|
||||
android:id="@+id/show_genre"
|
||||
app:destination="@id/genre_detail_fragment" />
|
||||
<action
|
||||
android:id="@+id/show_playlist"
|
||||
app:destination="@id/playlist_detail_fragment" />
|
||||
<action
|
||||
android:id="@+id/new_playlist"
|
||||
app:destination="@id/new_playlist_dialog" />
|
||||
<action
|
||||
android:id="@+id/rename_playlist"
|
||||
app:destination="@id/rename_playlist_dialog" />
|
||||
<action
|
||||
android:id="@+id/delete_playlist"
|
||||
app:destination="@id/delete_playlist_dialog" />
|
||||
<action
|
||||
android:id="@+id/add_to_playlist"
|
||||
app:destination="@id/add_to_playlist_dialog" />
|
||||
<action
|
||||
android:id="@+id/show_artists"
|
||||
app:destination="@id/show_artists_dialog" />
|
||||
<action
|
||||
android:id="@+id/play_from_artist"
|
||||
app:destination="@id/play_from_artist_dialog" />
|
||||
<action
|
||||
android:id="@+id/play_from_genre"
|
||||
app:destination="@id/play_from_genre_dialog" />
|
||||
</fragment>
|
||||
|
||||
<dialog
|
||||
android:id="@+id/song_detail_dialog"
|
||||
android:name="org.oxycblt.auxio.detail.SongDetailDialog"
|
||||
android:label="song_detail_dialog"
|
||||
tools:layout="@layout/dialog_song_detail">
|
||||
<argument
|
||||
android:name="songUid"
|
||||
app:argType="org.oxycblt.auxio.music.Music$UID" />
|
||||
</dialog>
|
||||
|
||||
<fragment
|
||||
android:id="@+id/search_fragment"
|
||||
android:name="org.oxycblt.auxio.search.SearchFragment"
|
||||
android:label="SearchFragment"
|
||||
tools:layout="@layout/fragment_search">
|
||||
<action
|
||||
android:id="@+id/show_song"
|
||||
app:destination="@id/song_detail_dialog" />
|
||||
<action
|
||||
android:id="@+id/show_album"
|
||||
app:destination="@id/album_detail_fragment" />
|
||||
<action
|
||||
android:id="@+id/show_artist"
|
||||
app:destination="@id/artist_detail_fragment" />
|
||||
<action
|
||||
android:id="@+id/show_genre"
|
||||
app:destination="@id/genre_detail_fragment" />
|
||||
<action
|
||||
android:id="@+id/show_playlist"
|
||||
app:destination="@id/playlist_detail_fragment" />
|
||||
<action
|
||||
android:id="@+id/rename_playlist"
|
||||
app:destination="@id/rename_playlist_dialog" />
|
||||
<action
|
||||
android:id="@+id/delete_playlist"
|
||||
app:destination="@id/delete_playlist_dialog" />
|
||||
<action
|
||||
android:id="@+id/add_to_playlist"
|
||||
app:destination="@id/add_to_playlist_dialog" />
|
||||
<action
|
||||
android:id="@+id/show_artists"
|
||||
app:destination="@id/show_artists_dialog" />
|
||||
<action
|
||||
android:id="@+id/play_from_artist"
|
||||
app:destination="@id/play_from_artist_dialog" />
|
||||
<action
|
||||
android:id="@+id/play_from_genre"
|
||||
app:destination="@id/play_from_genre_dialog" />
|
||||
</fragment>
|
||||
|
||||
<fragment
|
||||
android:id="@+id/album_detail_fragment"
|
||||
android:name="org.oxycblt.auxio.detail.AlbumDetailFragment"
|
||||
android:label="AlbumDetailFragment"
|
||||
tools:layout="@layout/fragment_detail">
|
||||
<argument
|
||||
android:name="albumUid"
|
||||
app:argType="org.oxycblt.auxio.music.Music$UID" />
|
||||
<action
|
||||
android:id="@+id/show_song"
|
||||
app:destination="@id/song_detail_dialog" />
|
||||
<action
|
||||
android:id="@+id/show_album"
|
||||
app:destination="@id/album_detail_fragment" />
|
||||
<action
|
||||
android:id="@+id/show_artist"
|
||||
app:destination="@id/artist_detail_fragment" />
|
||||
<action
|
||||
android:id="@+id/add_to_playlist"
|
||||
app:destination="@id/add_to_playlist_dialog" />
|
||||
<action
|
||||
android:id="@+id/show_artists"
|
||||
app:destination="@id/show_artists_dialog" />
|
||||
<action
|
||||
android:id="@+id/play_from_artist"
|
||||
app:destination="@id/play_from_artist_dialog" />
|
||||
<action
|
||||
android:id="@+id/play_from_genre"
|
||||
app:destination="@id/play_from_genre_dialog" />
|
||||
</fragment>
|
||||
|
||||
<fragment
|
||||
android:id="@+id/artist_detail_fragment"
|
||||
android:name="org.oxycblt.auxio.detail.ArtistDetailFragment"
|
||||
android:label="ArtistDetailFragment"
|
||||
tools:layout="@layout/fragment_detail">
|
||||
<argument
|
||||
android:name="artistUid"
|
||||
app:argType="org.oxycblt.auxio.music.Music$UID" />
|
||||
<action
|
||||
android:id="@+id/show_song"
|
||||
app:destination="@id/song_detail_dialog" />
|
||||
<action
|
||||
android:id="@+id/show_album"
|
||||
app:destination="@id/album_detail_fragment" />
|
||||
<action
|
||||
android:id="@+id/show_artist"
|
||||
app:destination="@id/artist_detail_fragment" />
|
||||
<action
|
||||
android:id="@+id/add_to_playlist"
|
||||
app:destination="@id/add_to_playlist_dialog" />
|
||||
<action
|
||||
android:id="@+id/play_from_genre"
|
||||
app:destination="@id/play_from_genre_dialog" />
|
||||
</fragment>
|
||||
|
||||
<fragment
|
||||
android:id="@+id/genre_detail_fragment"
|
||||
android:name="org.oxycblt.auxio.detail.GenreDetailFragment"
|
||||
android:label="GenreDetailFragment"
|
||||
tools:layout="@layout/fragment_detail">
|
||||
<argument
|
||||
android:name="genreUid"
|
||||
app:argType="org.oxycblt.auxio.music.Music$UID" />
|
||||
<action
|
||||
android:id="@+id/show_song"
|
||||
app:destination="@id/song_detail_dialog" />
|
||||
<action
|
||||
android:id="@+id/show_album"
|
||||
app:destination="@id/album_detail_fragment" />
|
||||
<action
|
||||
android:id="@+id/show_artist"
|
||||
app:destination="@id/artist_detail_fragment" />
|
||||
<action
|
||||
android:id="@+id/show_artists"
|
||||
app:destination="@id/show_artists_dialog" />
|
||||
<action
|
||||
android:id="@+id/add_to_playlist"
|
||||
app:destination="@id/add_to_playlist_dialog" />
|
||||
<action
|
||||
android:id="@+id/play_from_artist"
|
||||
app:destination="@id/play_from_artist_dialog" />
|
||||
</fragment>
|
||||
|
||||
<fragment
|
||||
android:id="@+id/playlist_detail_fragment"
|
||||
android:name="org.oxycblt.auxio.detail.PlaylistDetailFragment"
|
||||
android:label="PlaylistDetailFragment"
|
||||
tools:layout="@layout/fragment_detail">
|
||||
<argument
|
||||
android:name="playlistUid"
|
||||
app:argType="org.oxycblt.auxio.music.Music$UID" />
|
||||
<action
|
||||
android:id="@+id/show_song"
|
||||
app:destination="@id/song_detail_dialog" />
|
||||
<action
|
||||
android:id="@+id/show_album"
|
||||
app:destination="@id/album_detail_fragment" />
|
||||
<action
|
||||
android:id="@+id/show_artist"
|
||||
app:destination="@id/artist_detail_fragment" />
|
||||
<action
|
||||
android:id="@+id/show_artists"
|
||||
app:destination="@id/show_artists_dialog" />
|
||||
<action
|
||||
android:id="@+id/rename_playlist"
|
||||
app:destination="@id/rename_playlist_dialog" />
|
||||
<action
|
||||
android:id="@+id/delete_playlist"
|
||||
app:destination="@id/delete_playlist_dialog" />
|
||||
<action
|
||||
android:id="@+id/play_from_artist"
|
||||
app:destination="@id/play_from_artist_dialog" />
|
||||
<action
|
||||
android:id="@+id/play_from_genre"
|
||||
app:destination="@id/play_from_genre_dialog" />
|
||||
</fragment>
|
||||
|
||||
<dialog
|
||||
android:id="@+id/new_playlist_dialog"
|
||||
android:name="org.oxycblt.auxio.music.picker.NewPlaylistDialog"
|
||||
android:label="new_playlist_dialog"
|
||||
tools:layout="@layout/dialog_playlist_name">
|
||||
<argument
|
||||
android:name="songUids"
|
||||
app:argType="org.oxycblt.auxio.music.Music$UID[]" />
|
||||
</dialog>
|
||||
|
||||
<dialog
|
||||
android:id="@+id/rename_playlist_dialog"
|
||||
android:name="org.oxycblt.auxio.music.picker.RenamePlaylistDialog"
|
||||
android:label="rename_playlist_dialog"
|
||||
tools:layout="@layout/dialog_playlist_name">
|
||||
<argument
|
||||
android:name="playlistUid"
|
||||
app:argType="org.oxycblt.auxio.music.Music$UID" />
|
||||
</dialog>
|
||||
|
||||
<dialog
|
||||
android:id="@+id/delete_playlist_dialog"
|
||||
android:name="org.oxycblt.auxio.music.picker.DeletePlaylistDialog"
|
||||
android:label="delete_playlist_dialog"
|
||||
tools:layout="@layout/dialog_playlist_name">
|
||||
<argument
|
||||
android:name="playlistUid"
|
||||
app:argType="org.oxycblt.auxio.music.Music$UID" />
|
||||
</dialog>
|
||||
|
||||
<dialog
|
||||
android:id="@+id/add_to_playlist_dialog"
|
||||
android:name="org.oxycblt.auxio.music.picker.AddToPlaylistDialog"
|
||||
android:label="new_playlist_dialog"
|
||||
tools:layout="@layout/dialog_playlist_name">
|
||||
<argument
|
||||
android:name="songUids"
|
||||
app:argType="org.oxycblt.auxio.music.Music$UID[]" />
|
||||
<action
|
||||
android:id="@+id/new_playlist"
|
||||
app:destination="@id/new_playlist_dialog" />
|
||||
</dialog>
|
||||
|
||||
<dialog
|
||||
android:id="@+id/show_artists_dialog"
|
||||
android:name="org.oxycblt.auxio.detail.picker.ShowArtistDialog"
|
||||
android:label="show_artists_dialog"
|
||||
tools:layout="@layout/dialog_music_choices">
|
||||
<argument
|
||||
android:name="itemUid"
|
||||
app:argType="org.oxycblt.auxio.music.Music$UID" />
|
||||
</dialog>
|
||||
|
||||
<dialog
|
||||
android:id="@+id/play_from_artist_dialog"
|
||||
android:name="org.oxycblt.auxio.playback.picker.PlayFromArtistDialog"
|
||||
android:label="play_from_artist_dialog"
|
||||
tools:layout="@layout/dialog_music_choices">
|
||||
<argument
|
||||
android:name="artistUid"
|
||||
app:argType="org.oxycblt.auxio.music.Music$UID" />
|
||||
</dialog>
|
||||
|
||||
<dialog
|
||||
android:id="@+id/play_from_genre_dialog"
|
||||
android:name="org.oxycblt.auxio.playback.picker.PlayFromGenreDialog"
|
||||
android:label="play_from_genre_dialog"
|
||||
tools:layout="@layout/dialog_music_choices">
|
||||
<argument
|
||||
android:name="genreUid"
|
||||
app:argType="org.oxycblt.auxio.music.Music$UID" />
|
||||
</dialog>
|
||||
|
||||
<fragment
|
||||
android:id="@+id/root_preferences_fragment"
|
||||
android:name="org.oxycblt.auxio.settings.RootPreferenceFragment"
|
||||
android:label="fragment_settings">
|
||||
<action
|
||||
android:id="@+id/ui_preferences"
|
||||
app:destination="@id/ui_preferences_fragment" />
|
||||
<action
|
||||
android:id="@+id/personalize_preferences"
|
||||
app:destination="@id/personalize_preferences_fragment" />
|
||||
<action
|
||||
android:id="@+id/music_preferences"
|
||||
app:destination="@id/music_preferences_fragment" />
|
||||
<action
|
||||
android:id="@+id/audio_peferences"
|
||||
app:destination="@id/audio_preferences_fragment" />
|
||||
<action
|
||||
android:id="@+id/music_dirs_settings"
|
||||
app:destination="@id/music_dirs_dialog" />
|
||||
</fragment>
|
||||
|
||||
<fragment
|
||||
android:id="@+id/ui_preferences_fragment"
|
||||
android:name="org.oxycblt.auxio.settings.categories.UIPreferenceFragment"
|
||||
android:label="fragment_ui_preferences">
|
||||
<action
|
||||
android:id="@+id/accent_settings"
|
||||
app:destination="@id/accent_dialog" />
|
||||
</fragment>
|
||||
|
||||
<fragment
|
||||
android:id="@+id/personalize_preferences_fragment"
|
||||
android:name="org.oxycblt.auxio.settings.categories.PersonalizePreferenceFragment"
|
||||
android:label="fragment_personalize_preferences">
|
||||
<action
|
||||
android:id="@+id/tab_settings"
|
||||
app:destination="@id/tab_dialog" />
|
||||
</fragment>
|
||||
|
||||
<fragment
|
||||
android:id="@+id/music_preferences_fragment"
|
||||
android:name="org.oxycblt.auxio.settings.categories.MusicPreferenceFragment"
|
||||
android:label="fragment_personalize_preferences">
|
||||
<action
|
||||
android:id="@+id/separators_settings"
|
||||
app:destination="@id/separators_dialog" />
|
||||
</fragment>
|
||||
|
||||
<fragment
|
||||
android:id="@+id/audio_preferences_fragment"
|
||||
android:name="org.oxycblt.auxio.settings.categories.AudioPreferenceFragment"
|
||||
android:label="fragment_personalize_preferences">
|
||||
<action
|
||||
android:id="@+id/pre_amp_settings"
|
||||
app:destination="@id/pre_amp_dialog" />
|
||||
</fragment>
|
||||
|
||||
<dialog
|
||||
android:id="@+id/accent_dialog"
|
||||
android:name="org.oxycblt.auxio.ui.accent.AccentCustomizeDialog"
|
||||
android:label="accent_dialog"
|
||||
tools:layout="@layout/dialog_accent" />
|
||||
<dialog
|
||||
android:id="@+id/tab_dialog"
|
||||
android:name="org.oxycblt.auxio.home.tabs.TabCustomizeDialog"
|
||||
android:label="tab_dialog"
|
||||
tools:layout="@layout/dialog_tabs" />
|
||||
<dialog
|
||||
android:id="@+id/pre_amp_dialog"
|
||||
android:name="org.oxycblt.auxio.playback.replaygain.PreAmpCustomizeDialog"
|
||||
android:label="pre_amp_dialog"
|
||||
tools:layout="@layout/dialog_pre_amp" />
|
||||
<dialog
|
||||
android:id="@+id/music_dirs_dialog"
|
||||
android:name="org.oxycblt.auxio.music.fs.MusicDirsDialog"
|
||||
android:label="music_dirs_dialog"
|
||||
tools:layout="@layout/dialog_music_dirs" />
|
||||
<dialog
|
||||
android:id="@+id/separators_dialog"
|
||||
android:name="org.oxycblt.auxio.music.metadata.SeparatorsDialog"
|
||||
android:label="music_dirs_dialog"
|
||||
tools:layout="@layout/dialog_separators" />
|
||||
|
||||
<fragment
|
||||
android:id="@+id/about_fragment"
|
||||
android:name="org.oxycblt.auxio.settings.AboutFragment"
|
||||
android:label="dialog_about"
|
||||
tools:layout="@layout/fragment_about" />
|
||||
</navigation>
|
|
@ -1,106 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
app:startDestination="@id/home_fragment">
|
||||
|
||||
<fragment
|
||||
android:id="@+id/artist_detail_fragment"
|
||||
android:name="org.oxycblt.auxio.detail.ArtistDetailFragment"
|
||||
android:label="ArtistDetailFragment"
|
||||
tools:layout="@layout/fragment_detail">
|
||||
<argument
|
||||
android:name="artistUid"
|
||||
app:argType="org.oxycblt.auxio.music.Music$UID" />
|
||||
<action
|
||||
android:id="@+id/action_show_artist"
|
||||
app:destination="@id/artist_detail_fragment" />
|
||||
<action
|
||||
android:id="@+id/action_show_album"
|
||||
app:destination="@id/album_detail_fragment" />
|
||||
</fragment>
|
||||
<fragment
|
||||
android:id="@+id/album_detail_fragment"
|
||||
android:name="org.oxycblt.auxio.detail.AlbumDetailFragment"
|
||||
android:label="AlbumDetailFragment"
|
||||
tools:layout="@layout/fragment_detail">
|
||||
<argument
|
||||
android:name="albumUid"
|
||||
app:argType="org.oxycblt.auxio.music.Music$UID" />
|
||||
<action
|
||||
android:id="@+id/action_show_artist"
|
||||
app:destination="@id/artist_detail_fragment" />
|
||||
<action
|
||||
android:id="@+id/action_show_album"
|
||||
app:destination="@id/album_detail_fragment" />
|
||||
</fragment>
|
||||
<fragment
|
||||
android:id="@+id/genre_detail_fragment"
|
||||
android:name="org.oxycblt.auxio.detail.GenreDetailFragment"
|
||||
android:label="GenreDetailFragment"
|
||||
tools:layout="@layout/fragment_detail">
|
||||
<argument
|
||||
android:name="genreUid"
|
||||
app:argType="org.oxycblt.auxio.music.Music$UID" />
|
||||
<action
|
||||
android:id="@+id/action_show_artist"
|
||||
app:destination="@id/artist_detail_fragment" />
|
||||
<action
|
||||
android:id="@+id/action_show_album"
|
||||
app:destination="@id/album_detail_fragment" />
|
||||
</fragment>
|
||||
<fragment
|
||||
android:id="@+id/playlist_detail_fragment"
|
||||
android:name="org.oxycblt.auxio.detail.PlaylistDetailFragment"
|
||||
android:label="PlaylistDetailFragment"
|
||||
tools:layout="@layout/fragment_detail">
|
||||
<argument
|
||||
android:name="playlistUid"
|
||||
app:argType="org.oxycblt.auxio.music.Music$UID" />
|
||||
<action
|
||||
android:id="@+id/action_show_artist"
|
||||
app:destination="@id/artist_detail_fragment" />
|
||||
<action
|
||||
android:id="@+id/action_show_album"
|
||||
app:destination="@id/album_detail_fragment" />
|
||||
</fragment>
|
||||
<fragment
|
||||
android:id="@+id/search_fragment"
|
||||
android:name="org.oxycblt.auxio.search.SearchFragment"
|
||||
android:label="SearchFragment"
|
||||
tools:layout="@layout/fragment_search">
|
||||
<action
|
||||
android:id="@+id/action_show_playlist"
|
||||
app:destination="@id/playlist_detail_fragment" />
|
||||
<action
|
||||
android:id="@+id/action_show_genre"
|
||||
app:destination="@id/genre_detail_fragment" />
|
||||
<action
|
||||
android:id="@+id/action_show_artist"
|
||||
app:destination="@id/artist_detail_fragment" />
|
||||
<action
|
||||
android:id="@+id/action_show_album"
|
||||
app:destination="@id/album_detail_fragment" />
|
||||
</fragment>
|
||||
<fragment
|
||||
android:id="@+id/home_fragment"
|
||||
android:name="org.oxycblt.auxio.home.HomeFragment"
|
||||
android:label="fragment_home"
|
||||
tools:layout="@layout/fragment_home">
|
||||
<action
|
||||
android:id="@+id/action_show_search"
|
||||
app:destination="@id/search_fragment" />
|
||||
<action
|
||||
android:id="@+id/action_show_playlist"
|
||||
app:destination="@id/playlist_detail_fragment" />
|
||||
<action
|
||||
android:id="@+id/action_show_genre"
|
||||
app:destination="@id/genre_detail_fragment" />
|
||||
<action
|
||||
android:id="@+id/action_show_artist"
|
||||
app:destination="@id/artist_detail_fragment" />
|
||||
<action
|
||||
android:id="@+id/action_show_album"
|
||||
app:destination="@id/album_detail_fragment" />
|
||||
</fragment>
|
||||
</navigation>
|
|
@ -1,214 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
app:startDestination="@id/main_fragment">
|
||||
<fragment
|
||||
android:id="@+id/main_fragment"
|
||||
android:name="org.oxycblt.auxio.MainFragment"
|
||||
android:label="MainFragment"
|
||||
tools:layout="@layout/fragment_main">
|
||||
<action
|
||||
android:id="@+id/action_show_settings"
|
||||
app:destination="@id/root_preferences_fragment" />
|
||||
<action
|
||||
android:id="@+id/action_show_about"
|
||||
app:destination="@id/about_fragment" />
|
||||
<action
|
||||
android:id="@+id/action_show_details"
|
||||
app:destination="@id/song_detail_dialog" />
|
||||
<action
|
||||
android:id="@+id/action_new_playlist"
|
||||
app:destination="@id/new_playlist_dialog" />
|
||||
<action
|
||||
android:id="@+id/action_rename_playlist"
|
||||
app:destination="@id/rename_playlist_dialog" />
|
||||
<action
|
||||
android:id="@+id/action_delete_playlist"
|
||||
app:destination="@id/delete_playlist_dialog" />
|
||||
<action
|
||||
android:id="@+id/action_add_to_playlist"
|
||||
app:destination="@id/add_to_playlist_dialog" />
|
||||
<action
|
||||
android:id="@+id/action_pick_navigation_artist"
|
||||
app:destination="@id/navigate_to_artist_dialog" />
|
||||
<action
|
||||
android:id="@+id/action_pick_playback_artist"
|
||||
app:destination="@id/play_from_artist_dialog" />
|
||||
<action
|
||||
android:id="@+id/action_pick_playback_genre"
|
||||
app:destination="@id/play_from_genre_dialog" />
|
||||
</fragment>
|
||||
|
||||
<dialog
|
||||
android:id="@+id/song_detail_dialog"
|
||||
android:name="org.oxycblt.auxio.detail.SongDetailDialog"
|
||||
android:label="song_detail_dialog"
|
||||
tools:layout="@layout/dialog_song_detail">
|
||||
<argument
|
||||
android:name="songUid"
|
||||
app:argType="org.oxycblt.auxio.music.Music$UID" />
|
||||
</dialog>
|
||||
|
||||
<dialog
|
||||
android:id="@+id/new_playlist_dialog"
|
||||
android:name="org.oxycblt.auxio.music.picker.NewPlaylistDialog"
|
||||
android:label="new_playlist_dialog"
|
||||
tools:layout="@layout/dialog_playlist_name">
|
||||
<argument
|
||||
android:name="songUids"
|
||||
app:argType="org.oxycblt.auxio.music.Music$UID[]" />
|
||||
</dialog>
|
||||
|
||||
<dialog
|
||||
android:id="@+id/rename_playlist_dialog"
|
||||
android:name="org.oxycblt.auxio.music.picker.RenamePlaylistDialog"
|
||||
android:label="rename_playlist_dialog"
|
||||
tools:layout="@layout/dialog_playlist_name">
|
||||
<argument
|
||||
android:name="playlistUid"
|
||||
app:argType="org.oxycblt.auxio.music.Music$UID" />
|
||||
</dialog>
|
||||
|
||||
<dialog
|
||||
android:id="@+id/delete_playlist_dialog"
|
||||
android:name="org.oxycblt.auxio.music.picker.DeletePlaylistDialog"
|
||||
android:label="delete_playlist_dialog"
|
||||
tools:layout="@layout/dialog_playlist_name">
|
||||
<argument
|
||||
android:name="playlistUid"
|
||||
app:argType="org.oxycblt.auxio.music.Music$UID" />
|
||||
</dialog>
|
||||
|
||||
<dialog
|
||||
android:id="@+id/add_to_playlist_dialog"
|
||||
android:name="org.oxycblt.auxio.music.picker.AddToPlaylistDialog"
|
||||
android:label="new_playlist_dialog"
|
||||
tools:layout="@layout/dialog_playlist_name">
|
||||
<argument
|
||||
android:name="songUids"
|
||||
app:argType="org.oxycblt.auxio.music.Music$UID[]" />
|
||||
<action
|
||||
android:id="@+id/action_new_playlist"
|
||||
app:destination="@id/new_playlist_dialog" />
|
||||
</dialog>
|
||||
|
||||
<dialog
|
||||
android:id="@+id/navigate_to_artist_dialog"
|
||||
android:name="org.oxycblt.auxio.navigation.picker.NavigateToArtistDialog"
|
||||
android:label="navigate_to_artist_dialog"
|
||||
tools:layout="@layout/dialog_music_choices">
|
||||
<argument
|
||||
android:name="itemUid"
|
||||
app:argType="org.oxycblt.auxio.music.Music$UID" />
|
||||
</dialog>
|
||||
|
||||
<dialog
|
||||
android:id="@+id/play_from_artist_dialog"
|
||||
android:name="org.oxycblt.auxio.playback.picker.PlayFromArtistDialog"
|
||||
android:label="play_from_artist_dialog"
|
||||
tools:layout="@layout/dialog_music_choices">
|
||||
<argument
|
||||
android:name="artistUid"
|
||||
app:argType="org.oxycblt.auxio.music.Music$UID" />
|
||||
</dialog>
|
||||
|
||||
<dialog
|
||||
android:id="@+id/play_from_genre_dialog"
|
||||
android:name="org.oxycblt.auxio.playback.picker.PlayFromGenreDialog"
|
||||
android:label="play_from_genre_dialog"
|
||||
tools:layout="@layout/dialog_music_choices">
|
||||
<argument
|
||||
android:name="genreUid"
|
||||
app:argType="org.oxycblt.auxio.music.Music$UID" />
|
||||
</dialog>
|
||||
|
||||
<fragment
|
||||
android:id="@+id/root_preferences_fragment"
|
||||
android:name="org.oxycblt.auxio.settings.RootPreferenceFragment"
|
||||
android:label="fragment_settings">
|
||||
<action
|
||||
android:id="@+id/go_to_ui_preferences"
|
||||
app:destination="@id/ui_preferences_fragment" />
|
||||
<action
|
||||
android:id="@+id/go_to_personalize_preferences"
|
||||
app:destination="@id/personalize_preferences_fragment" />
|
||||
<action
|
||||
android:id="@+id/go_to_music_preferences"
|
||||
app:destination="@id/music_preferences_fragment" />
|
||||
<action
|
||||
android:id="@+id/go_to_audio_preferences"
|
||||
app:destination="@id/audio_preferences_fragment" />
|
||||
<action
|
||||
android:id="@+id/go_to_music_dirs_dialog"
|
||||
app:destination="@id/music_dirs_dialog" />
|
||||
</fragment>
|
||||
|
||||
<fragment
|
||||
android:id="@+id/ui_preferences_fragment"
|
||||
android:name="org.oxycblt.auxio.settings.categories.UIPreferenceFragment"
|
||||
android:label="fragment_ui_preferences">
|
||||
<action
|
||||
android:id="@+id/go_to_accent_dialog"
|
||||
app:destination="@id/accent_dialog" />
|
||||
</fragment>
|
||||
|
||||
<fragment
|
||||
android:id="@+id/personalize_preferences_fragment"
|
||||
android:name="org.oxycblt.auxio.settings.categories.PersonalizePreferenceFragment"
|
||||
android:label="fragment_personalize_preferences">
|
||||
<action
|
||||
android:id="@+id/go_to_tab_dialog"
|
||||
app:destination="@id/tab_dialog" />
|
||||
</fragment>
|
||||
|
||||
<fragment
|
||||
android:id="@+id/music_preferences_fragment"
|
||||
android:name="org.oxycblt.auxio.settings.categories.MusicPreferenceFragment"
|
||||
android:label="fragment_personalize_preferences">
|
||||
<action
|
||||
android:id="@+id/go_to_separators_dialog"
|
||||
app:destination="@id/separators_dialog" />
|
||||
</fragment>
|
||||
|
||||
<fragment
|
||||
android:id="@+id/audio_preferences_fragment"
|
||||
android:name="org.oxycblt.auxio.settings.categories.AudioPreferenceFragment"
|
||||
android:label="fragment_personalize_preferences">
|
||||
<action
|
||||
android:id="@+id/go_to_pre_amp_dialog"
|
||||
app:destination="@id/pre_amp_dialog" />
|
||||
</fragment>
|
||||
|
||||
<dialog
|
||||
android:id="@+id/accent_dialog"
|
||||
android:name="org.oxycblt.auxio.ui.accent.AccentCustomizeDialog"
|
||||
android:label="accent_dialog"
|
||||
tools:layout="@layout/dialog_accent" />
|
||||
<dialog
|
||||
android:id="@+id/tab_dialog"
|
||||
android:name="org.oxycblt.auxio.home.tabs.TabCustomizeDialog"
|
||||
android:label="tab_dialog"
|
||||
tools:layout="@layout/dialog_tabs" />
|
||||
<dialog
|
||||
android:id="@+id/pre_amp_dialog"
|
||||
android:name="org.oxycblt.auxio.playback.replaygain.PreAmpCustomizeDialog"
|
||||
android:label="pre_amp_dialog"
|
||||
tools:layout="@layout/dialog_pre_amp" />
|
||||
<dialog
|
||||
android:id="@+id/music_dirs_dialog"
|
||||
android:name="org.oxycblt.auxio.music.fs.MusicDirsDialog"
|
||||
android:label="music_dirs_dialog"
|
||||
tools:layout="@layout/dialog_music_dirs" />
|
||||
<dialog
|
||||
android:id="@+id/separators_dialog"
|
||||
android:name="org.oxycblt.auxio.music.metadata.SeparatorsDialog"
|
||||
android:label="music_dirs_dialog"
|
||||
tools:layout="@layout/dialog_separators" />
|
||||
|
||||
<fragment
|
||||
android:id="@+id/about_fragment"
|
||||
android:name="org.oxycblt.auxio.settings.AboutFragment"
|
||||
android:label="dialog_about"
|
||||
tools:layout="@layout/fragment_about" />
|
||||
</navigation>
|
|
@ -1,7 +1,7 @@
|
|||
buildscript {
|
||||
ext {
|
||||
kotlin_version = '1.8.22'
|
||||
navigation_version = "2.5.0"
|
||||
navigation_version = "2.6.0"
|
||||
hilt_version = '2.46.1'
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue