picker: refactor into module-specific impls
Refactor the weird picker god module into specific sub-impls in playback and a new navigation package. I cannot keep this unified. The needs are too different among each picker. Better to keep it separate, especially in preparation for the playlist dialogs.
This commit is contained in:
parent
5de1e221ac
commit
f2a90bf0af
33 changed files with 472 additions and 400 deletions
|
@ -2,6 +2,9 @@
|
||||||
|
|
||||||
## dev
|
## dev
|
||||||
|
|
||||||
|
## What's Fixed
|
||||||
|
- Fixed inconsistent corner radius on widget cover art
|
||||||
|
|
||||||
## What's Improved
|
## What's Improved
|
||||||
- Added ability to click on the playback bar to exit the queue view
|
- Added ability to click on the playback bar to exit the queue view
|
||||||
|
|
||||||
|
|
|
@ -48,6 +48,9 @@ import org.oxycblt.auxio.util.systemBarInsetsCompat
|
||||||
* TODO: Use proper material attributes (Not the weird dimen attributes I currently have)
|
* TODO: Use proper material attributes (Not the weird dimen attributes I currently have)
|
||||||
* TODO: Migrate to material animation system
|
* TODO: Migrate to material animation system
|
||||||
* TODO: Unit testing
|
* TODO: Unit testing
|
||||||
|
* TODO: Use sealed interface where applicable
|
||||||
|
* TODO: Fix UID naming
|
||||||
|
* TODO: Leverage FlexibleListAdapter more in dialogs (Disable item anims)
|
||||||
*/
|
*/
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
class MainActivity : AppCompatActivity() {
|
class MainActivity : AppCompatActivity() {
|
||||||
|
|
|
@ -41,11 +41,11 @@ import org.oxycblt.auxio.databinding.FragmentMainBinding
|
||||||
import org.oxycblt.auxio.list.selection.SelectionViewModel
|
import org.oxycblt.auxio.list.selection.SelectionViewModel
|
||||||
import org.oxycblt.auxio.music.Music
|
import org.oxycblt.auxio.music.Music
|
||||||
import org.oxycblt.auxio.music.Song
|
import org.oxycblt.auxio.music.Song
|
||||||
|
import org.oxycblt.auxio.navigation.MainNavigationAction
|
||||||
|
import org.oxycblt.auxio.navigation.NavigationViewModel
|
||||||
import org.oxycblt.auxio.playback.PlaybackBottomSheetBehavior
|
import org.oxycblt.auxio.playback.PlaybackBottomSheetBehavior
|
||||||
import org.oxycblt.auxio.playback.PlaybackViewModel
|
import org.oxycblt.auxio.playback.PlaybackViewModel
|
||||||
import org.oxycblt.auxio.playback.queue.QueueBottomSheetBehavior
|
import org.oxycblt.auxio.playback.queue.QueueBottomSheetBehavior
|
||||||
import org.oxycblt.auxio.ui.MainNavigationAction
|
|
||||||
import org.oxycblt.auxio.ui.NavigationViewModel
|
|
||||||
import org.oxycblt.auxio.ui.ViewBindingFragment
|
import org.oxycblt.auxio.ui.ViewBindingFragment
|
||||||
import org.oxycblt.auxio.util.*
|
import org.oxycblt.auxio.util.*
|
||||||
|
|
||||||
|
|
|
@ -44,8 +44,8 @@ import org.oxycblt.auxio.music.Music
|
||||||
import org.oxycblt.auxio.music.MusicMode
|
import org.oxycblt.auxio.music.MusicMode
|
||||||
import org.oxycblt.auxio.music.MusicParent
|
import org.oxycblt.auxio.music.MusicParent
|
||||||
import org.oxycblt.auxio.music.Song
|
import org.oxycblt.auxio.music.Song
|
||||||
|
import org.oxycblt.auxio.navigation.NavigationViewModel
|
||||||
import org.oxycblt.auxio.playback.PlaybackViewModel
|
import org.oxycblt.auxio.playback.PlaybackViewModel
|
||||||
import org.oxycblt.auxio.ui.NavigationViewModel
|
|
||||||
import org.oxycblt.auxio.util.*
|
import org.oxycblt.auxio.util.*
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -43,8 +43,8 @@ import org.oxycblt.auxio.music.Artist
|
||||||
import org.oxycblt.auxio.music.Music
|
import org.oxycblt.auxio.music.Music
|
||||||
import org.oxycblt.auxio.music.MusicParent
|
import org.oxycblt.auxio.music.MusicParent
|
||||||
import org.oxycblt.auxio.music.Song
|
import org.oxycblt.auxio.music.Song
|
||||||
|
import org.oxycblt.auxio.navigation.NavigationViewModel
|
||||||
import org.oxycblt.auxio.playback.PlaybackViewModel
|
import org.oxycblt.auxio.playback.PlaybackViewModel
|
||||||
import org.oxycblt.auxio.ui.NavigationViewModel
|
|
||||||
import org.oxycblt.auxio.util.*
|
import org.oxycblt.auxio.util.*
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -39,8 +39,8 @@ import org.oxycblt.auxio.list.ListFragment
|
||||||
import org.oxycblt.auxio.list.Sort
|
import org.oxycblt.auxio.list.Sort
|
||||||
import org.oxycblt.auxio.list.selection.SelectionViewModel
|
import org.oxycblt.auxio.list.selection.SelectionViewModel
|
||||||
import org.oxycblt.auxio.music.*
|
import org.oxycblt.auxio.music.*
|
||||||
|
import org.oxycblt.auxio.navigation.NavigationViewModel
|
||||||
import org.oxycblt.auxio.playback.PlaybackViewModel
|
import org.oxycblt.auxio.playback.PlaybackViewModel
|
||||||
import org.oxycblt.auxio.ui.NavigationViewModel
|
|
||||||
import org.oxycblt.auxio.util.*
|
import org.oxycblt.auxio.util.*
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -39,8 +39,8 @@ import org.oxycblt.auxio.list.ListFragment
|
||||||
import org.oxycblt.auxio.list.Sort
|
import org.oxycblt.auxio.list.Sort
|
||||||
import org.oxycblt.auxio.list.selection.SelectionViewModel
|
import org.oxycblt.auxio.list.selection.SelectionViewModel
|
||||||
import org.oxycblt.auxio.music.*
|
import org.oxycblt.auxio.music.*
|
||||||
|
import org.oxycblt.auxio.navigation.NavigationViewModel
|
||||||
import org.oxycblt.auxio.playback.PlaybackViewModel
|
import org.oxycblt.auxio.playback.PlaybackViewModel
|
||||||
import org.oxycblt.auxio.ui.NavigationViewModel
|
|
||||||
import org.oxycblt.auxio.util.*
|
import org.oxycblt.auxio.util.*
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -66,7 +66,7 @@ class SongDetailDialog : ViewBindingDialogFragment<DialogSongDetailBinding>() {
|
||||||
super.onBindingCreated(binding, savedInstanceState)
|
super.onBindingCreated(binding, savedInstanceState)
|
||||||
binding.detailProperties.adapter = detailAdapter
|
binding.detailProperties.adapter = detailAdapter
|
||||||
// DetailViewModel handles most initialization from the navigation argument.
|
// DetailViewModel handles most initialization from the navigation argument.
|
||||||
detailModel.setSongUid(args.itemUid)
|
detailModel.setSongUid(args.songUid)
|
||||||
collectImmediately(detailModel.currentSong, detailModel.songAudioInfo, ::updateSong)
|
collectImmediately(detailModel.currentSong, detailModel.songAudioInfo, ::updateSong)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -52,9 +52,9 @@ import org.oxycblt.auxio.list.Sort
|
||||||
import org.oxycblt.auxio.list.selection.SelectionFragment
|
import org.oxycblt.auxio.list.selection.SelectionFragment
|
||||||
import org.oxycblt.auxio.list.selection.SelectionViewModel
|
import org.oxycblt.auxio.list.selection.SelectionViewModel
|
||||||
import org.oxycblt.auxio.music.*
|
import org.oxycblt.auxio.music.*
|
||||||
|
import org.oxycblt.auxio.navigation.MainNavigationAction
|
||||||
|
import org.oxycblt.auxio.navigation.NavigationViewModel
|
||||||
import org.oxycblt.auxio.playback.PlaybackViewModel
|
import org.oxycblt.auxio.playback.PlaybackViewModel
|
||||||
import org.oxycblt.auxio.ui.MainNavigationAction
|
|
||||||
import org.oxycblt.auxio.ui.NavigationViewModel
|
|
||||||
import org.oxycblt.auxio.util.*
|
import org.oxycblt.auxio.util.*
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -37,10 +37,10 @@ import org.oxycblt.auxio.list.adapter.SelectionIndicatorAdapter
|
||||||
import org.oxycblt.auxio.list.recycler.AlbumViewHolder
|
import org.oxycblt.auxio.list.recycler.AlbumViewHolder
|
||||||
import org.oxycblt.auxio.list.selection.SelectionViewModel
|
import org.oxycblt.auxio.list.selection.SelectionViewModel
|
||||||
import org.oxycblt.auxio.music.*
|
import org.oxycblt.auxio.music.*
|
||||||
|
import org.oxycblt.auxio.navigation.NavigationViewModel
|
||||||
import org.oxycblt.auxio.playback.PlaybackViewModel
|
import org.oxycblt.auxio.playback.PlaybackViewModel
|
||||||
import org.oxycblt.auxio.playback.formatDurationMs
|
import org.oxycblt.auxio.playback.formatDurationMs
|
||||||
import org.oxycblt.auxio.playback.secsToMs
|
import org.oxycblt.auxio.playback.secsToMs
|
||||||
import org.oxycblt.auxio.ui.NavigationViewModel
|
|
||||||
import org.oxycblt.auxio.util.collectImmediately
|
import org.oxycblt.auxio.util.collectImmediately
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -38,9 +38,9 @@ import org.oxycblt.auxio.music.Artist
|
||||||
import org.oxycblt.auxio.music.Music
|
import org.oxycblt.auxio.music.Music
|
||||||
import org.oxycblt.auxio.music.MusicMode
|
import org.oxycblt.auxio.music.MusicMode
|
||||||
import org.oxycblt.auxio.music.MusicParent
|
import org.oxycblt.auxio.music.MusicParent
|
||||||
|
import org.oxycblt.auxio.navigation.NavigationViewModel
|
||||||
import org.oxycblt.auxio.playback.PlaybackViewModel
|
import org.oxycblt.auxio.playback.PlaybackViewModel
|
||||||
import org.oxycblt.auxio.playback.formatDurationMs
|
import org.oxycblt.auxio.playback.formatDurationMs
|
||||||
import org.oxycblt.auxio.ui.NavigationViewModel
|
|
||||||
import org.oxycblt.auxio.util.collectImmediately
|
import org.oxycblt.auxio.util.collectImmediately
|
||||||
import org.oxycblt.auxio.util.logD
|
import org.oxycblt.auxio.util.logD
|
||||||
import org.oxycblt.auxio.util.nonZeroOrNull
|
import org.oxycblt.auxio.util.nonZeroOrNull
|
||||||
|
|
|
@ -38,9 +38,9 @@ import org.oxycblt.auxio.music.Genre
|
||||||
import org.oxycblt.auxio.music.Music
|
import org.oxycblt.auxio.music.Music
|
||||||
import org.oxycblt.auxio.music.MusicMode
|
import org.oxycblt.auxio.music.MusicMode
|
||||||
import org.oxycblt.auxio.music.MusicParent
|
import org.oxycblt.auxio.music.MusicParent
|
||||||
|
import org.oxycblt.auxio.navigation.NavigationViewModel
|
||||||
import org.oxycblt.auxio.playback.PlaybackViewModel
|
import org.oxycblt.auxio.playback.PlaybackViewModel
|
||||||
import org.oxycblt.auxio.playback.formatDurationMs
|
import org.oxycblt.auxio.playback.formatDurationMs
|
||||||
import org.oxycblt.auxio.ui.NavigationViewModel
|
|
||||||
import org.oxycblt.auxio.util.collectImmediately
|
import org.oxycblt.auxio.util.collectImmediately
|
||||||
import org.oxycblt.auxio.util.logD
|
import org.oxycblt.auxio.util.logD
|
||||||
|
|
||||||
|
|
|
@ -37,9 +37,9 @@ import org.oxycblt.auxio.music.Music
|
||||||
import org.oxycblt.auxio.music.MusicMode
|
import org.oxycblt.auxio.music.MusicMode
|
||||||
import org.oxycblt.auxio.music.MusicParent
|
import org.oxycblt.auxio.music.MusicParent
|
||||||
import org.oxycblt.auxio.music.Playlist
|
import org.oxycblt.auxio.music.Playlist
|
||||||
|
import org.oxycblt.auxio.navigation.NavigationViewModel
|
||||||
import org.oxycblt.auxio.playback.PlaybackViewModel
|
import org.oxycblt.auxio.playback.PlaybackViewModel
|
||||||
import org.oxycblt.auxio.playback.formatDurationMs
|
import org.oxycblt.auxio.playback.formatDurationMs
|
||||||
import org.oxycblt.auxio.ui.NavigationViewModel
|
|
||||||
import org.oxycblt.auxio.util.collectImmediately
|
import org.oxycblt.auxio.util.collectImmediately
|
||||||
import org.oxycblt.auxio.util.logD
|
import org.oxycblt.auxio.util.logD
|
||||||
|
|
||||||
|
|
|
@ -40,10 +40,10 @@ import org.oxycblt.auxio.music.Music
|
||||||
import org.oxycblt.auxio.music.MusicMode
|
import org.oxycblt.auxio.music.MusicMode
|
||||||
import org.oxycblt.auxio.music.MusicParent
|
import org.oxycblt.auxio.music.MusicParent
|
||||||
import org.oxycblt.auxio.music.Song
|
import org.oxycblt.auxio.music.Song
|
||||||
|
import org.oxycblt.auxio.navigation.NavigationViewModel
|
||||||
import org.oxycblt.auxio.playback.PlaybackViewModel
|
import org.oxycblt.auxio.playback.PlaybackViewModel
|
||||||
import org.oxycblt.auxio.playback.formatDurationMs
|
import org.oxycblt.auxio.playback.formatDurationMs
|
||||||
import org.oxycblt.auxio.playback.secsToMs
|
import org.oxycblt.auxio.playback.secsToMs
|
||||||
import org.oxycblt.auxio.ui.NavigationViewModel
|
|
||||||
import org.oxycblt.auxio.util.collectImmediately
|
import org.oxycblt.auxio.util.collectImmediately
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -29,8 +29,8 @@ import org.oxycblt.auxio.MainFragmentDirections
|
||||||
import org.oxycblt.auxio.R
|
import org.oxycblt.auxio.R
|
||||||
import org.oxycblt.auxio.list.selection.SelectionFragment
|
import org.oxycblt.auxio.list.selection.SelectionFragment
|
||||||
import org.oxycblt.auxio.music.*
|
import org.oxycblt.auxio.music.*
|
||||||
import org.oxycblt.auxio.ui.MainNavigationAction
|
import org.oxycblt.auxio.navigation.MainNavigationAction
|
||||||
import org.oxycblt.auxio.ui.NavigationViewModel
|
import org.oxycblt.auxio.navigation.NavigationViewModel
|
||||||
import org.oxycblt.auxio.util.logD
|
import org.oxycblt.auxio.util.logD
|
||||||
import org.oxycblt.auxio.util.showToast
|
import org.oxycblt.auxio.util.showToast
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
/*
|
||||||
|
* 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 class 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()
|
||||||
|
}
|
|
@ -16,10 +16,9 @@
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.oxycblt.auxio.ui
|
package org.oxycblt.auxio.navigation
|
||||||
|
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.navigation.NavDirections
|
|
||||||
import org.oxycblt.auxio.music.Album
|
import org.oxycblt.auxio.music.Album
|
||||||
import org.oxycblt.auxio.music.Artist
|
import org.oxycblt.auxio.music.Artist
|
||||||
import org.oxycblt.auxio.music.Music
|
import org.oxycblt.auxio.music.Music
|
||||||
|
@ -28,7 +27,13 @@ import org.oxycblt.auxio.util.Event
|
||||||
import org.oxycblt.auxio.util.MutableEvent
|
import org.oxycblt.auxio.util.MutableEvent
|
||||||
import org.oxycblt.auxio.util.logD
|
import org.oxycblt.auxio.util.logD
|
||||||
|
|
||||||
/** A [ViewModel] that handles complicated navigation functionality. */
|
/**
|
||||||
|
* A [ViewModel] that handles complicated navigation functionality.
|
||||||
|
*
|
||||||
|
* @author Alexander Capehart (OxygenCobalt)
|
||||||
|
*
|
||||||
|
* TODO: This whole system is very jankily designed, perhaps it's time for a refactor?
|
||||||
|
*/
|
||||||
class NavigationViewModel : ViewModel() {
|
class NavigationViewModel : ViewModel() {
|
||||||
private val _mainNavigationAction = MutableEvent<MainNavigationAction>()
|
private val _mainNavigationAction = MutableEvent<MainNavigationAction>()
|
||||||
/**
|
/**
|
||||||
|
@ -118,26 +123,3 @@ class NavigationViewModel : ViewModel() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 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 class 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()
|
|
||||||
}
|
|
|
@ -16,32 +16,41 @@
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.oxycblt.auxio.picker
|
package org.oxycblt.auxio.navigation.picker
|
||||||
|
|
||||||
import android.os.Bundle
|
|
||||||
import androidx.fragment.app.activityViewModels
|
import androidx.fragment.app.activityViewModels
|
||||||
|
import androidx.fragment.app.viewModels
|
||||||
import androidx.navigation.fragment.navArgs
|
import androidx.navigation.fragment.navArgs
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
import org.oxycblt.auxio.databinding.DialogMusicPickerBinding
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
import org.oxycblt.auxio.R
|
||||||
import org.oxycblt.auxio.music.Artist
|
import org.oxycblt.auxio.music.Artist
|
||||||
import org.oxycblt.auxio.ui.NavigationViewModel
|
import org.oxycblt.auxio.navigation.NavigationViewModel
|
||||||
|
import org.oxycblt.auxio.picker.PickerChoices
|
||||||
|
import org.oxycblt.auxio.picker.PickerDialogFragment
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An [ArtistPickerDialog] intended for when [Artist] navigation is ambiguous.
|
* A [PickerDialogFragment] intended for when [Artist] navigation is ambiguous.
|
||||||
*
|
*
|
||||||
* @author Alexander Capehart (OxygenCobalt)
|
* @author Alexander Capehart (OxygenCobalt)
|
||||||
*/
|
*/
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
class ArtistNavigationPickerDialog : ArtistPickerDialog() {
|
class ArtistNavigationPickerDialog : PickerDialogFragment<Artist>() {
|
||||||
private val navModel: NavigationViewModel by activityViewModels()
|
private val navModel: NavigationViewModel by activityViewModels()
|
||||||
|
private val pickerModel: NavigationPickerViewModel by viewModels()
|
||||||
// Information about what Song to show choices for is initially within the navigation arguments
|
// Information about what Song to show choices for is initially within the navigation arguments
|
||||||
// as UIDs, as that is the only safe way to parcel a Song.
|
// as UIDs, as that is the only safe way to parcel a Song.
|
||||||
private val args: ArtistNavigationPickerDialogArgs by navArgs()
|
private val args: ArtistNavigationPickerDialogArgs by navArgs()
|
||||||
|
|
||||||
override fun onBindingCreated(binding: DialogMusicPickerBinding, savedInstanceState: Bundle?) {
|
override val titleRes: Int
|
||||||
pickerModel.setItemUid(args.itemUid)
|
get() = R.string.lbl_artists
|
||||||
super.onBindingCreated(binding, savedInstanceState)
|
|
||||||
|
override val pickerChoices: StateFlow<PickerChoices<Artist>?>
|
||||||
|
get() = pickerModel.currentArtistChoices
|
||||||
|
|
||||||
|
override fun initChoices() {
|
||||||
|
pickerModel.setArtistChoiceUid(args.artistUid)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onClick(item: Artist, viewHolder: RecyclerView.ViewHolder) {
|
override fun onClick(item: Artist, viewHolder: RecyclerView.ViewHolder) {
|
|
@ -0,0 +1,93 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2023 Auxio Project
|
||||||
|
* NavigationPickerViewModel.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.picker
|
||||||
|
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
|
import javax.inject.Inject
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
import org.oxycblt.auxio.music.*
|
||||||
|
import org.oxycblt.auxio.picker.PickerChoices
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A [ViewModel] that stores the current information required for [ArtistNavigationPickerDialog].
|
||||||
|
*
|
||||||
|
* @author Alexander Capehart (OxygenCobalt)
|
||||||
|
*/
|
||||||
|
@HiltViewModel
|
||||||
|
class NavigationPickerViewModel @Inject constructor(private val musicRepository: MusicRepository) :
|
||||||
|
ViewModel(), MusicRepository.UpdateListener {
|
||||||
|
private val _currentArtistChoices = MutableStateFlow<ArtistNavigationChoices?>(null)
|
||||||
|
/** The current set of [Artist] choices to show in the picker, or null if to show nothing. */
|
||||||
|
val currentArtistChoices: StateFlow<PickerChoices<Artist>?>
|
||||||
|
get() = _currentArtistChoices
|
||||||
|
|
||||||
|
init {
|
||||||
|
musicRepository.addUpdateListener(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onMusicChanges(changes: MusicRepository.Changes) {
|
||||||
|
if (!changes.deviceLibrary) return
|
||||||
|
val deviceLibrary = musicRepository.deviceLibrary ?: return
|
||||||
|
// Need to sanitize different items depending on the current set of choices.
|
||||||
|
_currentArtistChoices.value =
|
||||||
|
when (val choices = _currentArtistChoices.value) {
|
||||||
|
is ArtistNavigationChoices.FromSong ->
|
||||||
|
deviceLibrary.findSong(choices.song.uid)?.let {
|
||||||
|
ArtistNavigationChoices.FromSong(it)
|
||||||
|
}
|
||||||
|
is ArtistNavigationChoices.FromAlbum ->
|
||||||
|
deviceLibrary.findAlbum(choices.album.uid)?.let {
|
||||||
|
ArtistNavigationChoices.FromAlbum(it)
|
||||||
|
}
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCleared() {
|
||||||
|
super.onCleared()
|
||||||
|
musicRepository.removeUpdateListener(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the [Music.UID] of the item to show artist choices for.
|
||||||
|
*
|
||||||
|
* @param uid The [Music.UID] of the item to show. Must be a [Song] or [Album].
|
||||||
|
*/
|
||||||
|
fun setArtistChoiceUid(uid: Music.UID) {
|
||||||
|
// Support Songs and Albums, which have parent artists.
|
||||||
|
_currentArtistChoices.value =
|
||||||
|
when (val music = musicRepository.find(uid)) {
|
||||||
|
is Song -> ArtistNavigationChoices.FromSong(music)
|
||||||
|
is Album -> ArtistNavigationChoices.FromAlbum(music)
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private sealed interface ArtistNavigationChoices : PickerChoices<Artist> {
|
||||||
|
data class FromSong(val song: Song) : ArtistNavigationChoices {
|
||||||
|
override val choices = song.artists
|
||||||
|
}
|
||||||
|
|
||||||
|
data class FromAlbum(val album: Album) : ArtistNavigationChoices {
|
||||||
|
override val choices = album.artists
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,90 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2022 Auxio Project
|
|
||||||
* ArtistChoiceAdapter.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.picker
|
|
||||||
|
|
||||||
import android.view.View
|
|
||||||
import android.view.ViewGroup
|
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
|
||||||
import org.oxycblt.auxio.databinding.ItemPickerChoiceBinding
|
|
||||||
import org.oxycblt.auxio.list.ClickableListListener
|
|
||||||
import org.oxycblt.auxio.list.recycler.DialogRecyclerView
|
|
||||||
import org.oxycblt.auxio.music.Artist
|
|
||||||
import org.oxycblt.auxio.util.context
|
|
||||||
import org.oxycblt.auxio.util.inflater
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An [RecyclerView.Adapter] that displays a list of [Artist] choices.
|
|
||||||
*
|
|
||||||
* @param listener A [ClickableListListener] to bind interactions to.
|
|
||||||
* @author OxygenCobalt.
|
|
||||||
*/
|
|
||||||
class ArtistChoiceAdapter(private val listener: ClickableListListener<Artist>) :
|
|
||||||
RecyclerView.Adapter<ArtistChoiceViewHolder>() {
|
|
||||||
private var artists = listOf<Artist>()
|
|
||||||
|
|
||||||
override fun getItemCount() = artists.size
|
|
||||||
|
|
||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) =
|
|
||||||
ArtistChoiceViewHolder.from(parent)
|
|
||||||
|
|
||||||
override fun onBindViewHolder(holder: ArtistChoiceViewHolder, position: Int) =
|
|
||||||
holder.bind(artists[position], listener)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Immediately update the [Artist] choices.
|
|
||||||
*
|
|
||||||
* @param newArtists The new [Artist]s to show.
|
|
||||||
*/
|
|
||||||
fun submitList(newArtists: List<Artist>) {
|
|
||||||
if (newArtists != artists) {
|
|
||||||
artists = newArtists
|
|
||||||
@Suppress("NotifyDataSetChanged") notifyDataSetChanged()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A [DialogRecyclerView.ViewHolder] that displays a smaller variant of a typical [Artist] item, for
|
|
||||||
* use with [ArtistChoiceAdapter]. Use [from] to create an instance.
|
|
||||||
*/
|
|
||||||
class ArtistChoiceViewHolder(private val binding: ItemPickerChoiceBinding) :
|
|
||||||
DialogRecyclerView.ViewHolder(binding.root) {
|
|
||||||
/**
|
|
||||||
* Bind new data to this instance.
|
|
||||||
*
|
|
||||||
* @param artist The new [Artist] to bind.
|
|
||||||
* @param listener A [ClickableListListener] to bind interactions to.
|
|
||||||
*/
|
|
||||||
fun bind(artist: Artist, listener: ClickableListListener<Artist>) {
|
|
||||||
listener.bind(artist, this)
|
|
||||||
binding.pickerImage.bind(artist)
|
|
||||||
binding.pickerName.text = artist.resolveName(binding.context)
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
/**
|
|
||||||
* Create a new instance.
|
|
||||||
*
|
|
||||||
* @param parent The parent to inflate this instance from.
|
|
||||||
* @return A new instance.
|
|
||||||
*/
|
|
||||||
fun from(parent: View) =
|
|
||||||
ArtistChoiceViewHolder(ItemPickerChoiceBinding.inflate(parent.context.inflater))
|
|
||||||
}
|
|
||||||
}
|
|
87
app/src/main/java/org/oxycblt/auxio/picker/ChoiceAdapter.kt
Normal file
87
app/src/main/java/org/oxycblt/auxio/picker/ChoiceAdapter.kt
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2023 Auxio Project
|
||||||
|
* ChoiceAdapter.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.picker
|
||||||
|
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import org.oxycblt.auxio.databinding.ItemPickerChoiceBinding
|
||||||
|
import org.oxycblt.auxio.list.ClickableListListener
|
||||||
|
import org.oxycblt.auxio.list.adapter.FlexibleListAdapter
|
||||||
|
import org.oxycblt.auxio.list.adapter.SimpleDiffCallback
|
||||||
|
import org.oxycblt.auxio.list.recycler.DialogRecyclerView
|
||||||
|
import org.oxycblt.auxio.music.*
|
||||||
|
import org.oxycblt.auxio.util.context
|
||||||
|
import org.oxycblt.auxio.util.inflater
|
||||||
|
|
||||||
|
/** A [RecyclerView.Adapter] that shows a list */
|
||||||
|
class ChoiceAdapter<T : Music>(private val listener: ClickableListListener<T>) :
|
||||||
|
FlexibleListAdapter<T, ChoiceViewHolder<T>>(ChoiceViewHolder.diffCallback()) {
|
||||||
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) =
|
||||||
|
ChoiceViewHolder.from<T>(parent)
|
||||||
|
|
||||||
|
override fun onBindViewHolder(holder: ChoiceViewHolder<T>, position: Int) =
|
||||||
|
holder.bind(getItem(position), listener)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A [DialogRecyclerView.ViewHolder] that displays a smaller variant of a typical [T] item, for use
|
||||||
|
* with [ChoiceAdapter]. Use [from] to create an instance.
|
||||||
|
*/
|
||||||
|
class ChoiceViewHolder<T : Music>
|
||||||
|
private constructor(private val binding: ItemPickerChoiceBinding) :
|
||||||
|
DialogRecyclerView.ViewHolder(binding.root) {
|
||||||
|
/**
|
||||||
|
* Bind new data to this instance.
|
||||||
|
*
|
||||||
|
* @param music The new [T] to bind.
|
||||||
|
* @param listener A [ClickableListListener] to bind interactions to.
|
||||||
|
*/
|
||||||
|
fun bind(music: T, listener: ClickableListListener<T>) {
|
||||||
|
listener.bind(music, this)
|
||||||
|
// ImageGroup is not generic, so we must downcast to specific types for now.
|
||||||
|
when (music) {
|
||||||
|
is Song -> binding.pickerImage.bind(music)
|
||||||
|
is Album -> binding.pickerImage.bind(music)
|
||||||
|
is Artist -> binding.pickerImage.bind(music)
|
||||||
|
is Genre -> binding.pickerImage.bind(music)
|
||||||
|
is Playlist -> binding.pickerImage.bind(music)
|
||||||
|
}
|
||||||
|
binding.pickerName.text = music.resolveName(binding.context)
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new instance.
|
||||||
|
*
|
||||||
|
* @param parent The parent to inflate this instance from.
|
||||||
|
* @return A new instance.
|
||||||
|
*/
|
||||||
|
fun <T : Music> from(parent: View) =
|
||||||
|
ChoiceViewHolder<T>(ItemPickerChoiceBinding.inflate(parent.context.inflater))
|
||||||
|
|
||||||
|
/** Get a comparator that can be used with DiffUtil. */
|
||||||
|
fun <T : Music> diffCallback() =
|
||||||
|
object : SimpleDiffCallback<T>() {
|
||||||
|
override fun areContentsTheSame(oldItem: T, newItem: T) =
|
||||||
|
oldItem.rawName == newItem.rawName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,90 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2022 Auxio Project
|
|
||||||
* GenreChoiceAdapter.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.picker
|
|
||||||
|
|
||||||
import android.view.View
|
|
||||||
import android.view.ViewGroup
|
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
|
||||||
import org.oxycblt.auxio.databinding.ItemPickerChoiceBinding
|
|
||||||
import org.oxycblt.auxio.list.ClickableListListener
|
|
||||||
import org.oxycblt.auxio.list.recycler.DialogRecyclerView
|
|
||||||
import org.oxycblt.auxio.music.Genre
|
|
||||||
import org.oxycblt.auxio.util.context
|
|
||||||
import org.oxycblt.auxio.util.inflater
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An [RecyclerView.Adapter] that displays a list of [Genre] choices.
|
|
||||||
*
|
|
||||||
* @param listener A [ClickableListListener] to bind interactions to.
|
|
||||||
* @author OxygenCobalt.
|
|
||||||
*/
|
|
||||||
class GenreChoiceAdapter(private val listener: ClickableListListener<Genre>) :
|
|
||||||
RecyclerView.Adapter<GenreChoiceViewHolder>() {
|
|
||||||
private var genres = listOf<Genre>()
|
|
||||||
|
|
||||||
override fun getItemCount() = genres.size
|
|
||||||
|
|
||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) =
|
|
||||||
GenreChoiceViewHolder.from(parent)
|
|
||||||
|
|
||||||
override fun onBindViewHolder(holder: GenreChoiceViewHolder, position: Int) =
|
|
||||||
holder.bind(genres[position], listener)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Immediately update the [Genre] choices.
|
|
||||||
*
|
|
||||||
* @param newGenres The new [Genre]s to show.
|
|
||||||
*/
|
|
||||||
fun submitList(newGenres: List<Genre>) {
|
|
||||||
if (newGenres != genres) {
|
|
||||||
genres = newGenres
|
|
||||||
@Suppress("NotifyDataSetChanged") notifyDataSetChanged()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A [DialogRecyclerView.ViewHolder] that displays a smaller variant of a typical [Genre] item, for
|
|
||||||
* use with [GenreChoiceAdapter]. Use [from] to create an instance.
|
|
||||||
*/
|
|
||||||
class GenreChoiceViewHolder(private val binding: ItemPickerChoiceBinding) :
|
|
||||||
DialogRecyclerView.ViewHolder(binding.root) {
|
|
||||||
/**
|
|
||||||
* Bind new data to this instance.
|
|
||||||
*
|
|
||||||
* @param genre The new [Genre] to bind.
|
|
||||||
* @param listener A [ClickableListListener] to bind interactions to.
|
|
||||||
*/
|
|
||||||
fun bind(genre: Genre, listener: ClickableListListener<Genre>) {
|
|
||||||
listener.bind(genre, this)
|
|
||||||
binding.pickerImage.bind(genre)
|
|
||||||
binding.pickerName.text = genre.resolveName(binding.context)
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
/**
|
|
||||||
* Create a new instance.
|
|
||||||
*
|
|
||||||
* @param parent The parent to inflate this instance from.
|
|
||||||
* @return A new instance.
|
|
||||||
*/
|
|
||||||
fun from(parent: View) =
|
|
||||||
GenreChoiceViewHolder(ItemPickerChoiceBinding.inflate(parent.context.inflater))
|
|
||||||
}
|
|
||||||
}
|
|
31
app/src/main/java/org/oxycblt/auxio/picker/PickerChoices.kt
Normal file
31
app/src/main/java/org/oxycblt/auxio/picker/PickerChoices.kt
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2023 Auxio Project
|
||||||
|
* PickerChoices.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.picker
|
||||||
|
|
||||||
|
import org.oxycblt.auxio.music.Music
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a list of [Music] to show in a picker UI.
|
||||||
|
*
|
||||||
|
* @author Alexander Capehart (OxygenCobalt)
|
||||||
|
*/
|
||||||
|
interface PickerChoices<T : Music> {
|
||||||
|
/** The list of choices to show. */
|
||||||
|
val choices: List<T>
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2022 Auxio Project
|
* Copyright (c) 2023 Auxio Project
|
||||||
* ArtistPickerDialog.kt is part of Auxio.
|
* PickerDialogFragment.kt is part of Auxio.
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -21,45 +21,53 @@ package org.oxycblt.auxio.picker
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import androidx.appcompat.app.AlertDialog
|
import androidx.appcompat.app.AlertDialog
|
||||||
import androidx.fragment.app.viewModels
|
|
||||||
import androidx.navigation.fragment.findNavController
|
import androidx.navigation.fragment.findNavController
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import org.oxycblt.auxio.R
|
import org.oxycblt.auxio.R
|
||||||
import org.oxycblt.auxio.databinding.DialogMusicPickerBinding
|
import org.oxycblt.auxio.databinding.DialogMusicPickerBinding
|
||||||
import org.oxycblt.auxio.list.ClickableListListener
|
import org.oxycblt.auxio.list.ClickableListListener
|
||||||
import org.oxycblt.auxio.music.Artist
|
import org.oxycblt.auxio.list.adapter.UpdateInstructions
|
||||||
|
import org.oxycblt.auxio.music.Music
|
||||||
import org.oxycblt.auxio.ui.ViewBindingDialogFragment
|
import org.oxycblt.auxio.ui.ViewBindingDialogFragment
|
||||||
import org.oxycblt.auxio.util.collectImmediately
|
import org.oxycblt.auxio.util.collectImmediately
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The base class for dialogs that implements common behavior across all [Artist] pickers. These are
|
* A [ViewBindingDialogFragment] that acts as the base for a "picker" UI, shown when a given choice
|
||||||
* shown whenever what to do with an item's [Artist] is ambiguous, as there are multiple [Artist]'s
|
* is ambiguous.
|
||||||
* to choose from.
|
|
||||||
*
|
*
|
||||||
* @author Alexander Capehart (OxygenCobalt)
|
* @author Alexander Capehart (OxygenCobalt)
|
||||||
*/
|
*/
|
||||||
@AndroidEntryPoint
|
abstract class PickerDialogFragment<T : Music> :
|
||||||
abstract class ArtistPickerDialog :
|
ViewBindingDialogFragment<DialogMusicPickerBinding>(), ClickableListListener<T> {
|
||||||
ViewBindingDialogFragment<DialogMusicPickerBinding>(), ClickableListListener<Artist> {
|
|
||||||
protected val pickerModel: PickerViewModel by viewModels()
|
|
||||||
// Okay to leak this since the Listener will not be called until after initialization.
|
// Okay to leak this since the Listener will not be called until after initialization.
|
||||||
private val artistAdapter = ArtistChoiceAdapter(@Suppress("LeakingThis") this)
|
private val choiceAdapter = ChoiceAdapter(@Suppress("LeakingThis") this)
|
||||||
|
|
||||||
|
/** The string resource to use in the dialog title. */
|
||||||
|
abstract val titleRes: Int
|
||||||
|
/** The [StateFlow] of choices to show in the picker. */
|
||||||
|
abstract val pickerChoices: StateFlow<PickerChoices<T>?>
|
||||||
|
/** Called when the choice list should be initialized from the stored arguments. */
|
||||||
|
abstract fun initChoices()
|
||||||
|
|
||||||
override fun onCreateBinding(inflater: LayoutInflater) =
|
override fun onCreateBinding(inflater: LayoutInflater) =
|
||||||
DialogMusicPickerBinding.inflate(inflater)
|
DialogMusicPickerBinding.inflate(inflater)
|
||||||
|
|
||||||
override fun onConfigDialog(builder: AlertDialog.Builder) {
|
override fun onConfigDialog(builder: AlertDialog.Builder) {
|
||||||
builder.setTitle(R.string.lbl_artists).setNegativeButton(R.string.lbl_cancel, null)
|
builder.setTitle(titleRes).setNegativeButton(R.string.lbl_cancel, null)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onBindingCreated(binding: DialogMusicPickerBinding, savedInstanceState: Bundle?) {
|
override fun onBindingCreated(binding: DialogMusicPickerBinding, savedInstanceState: Bundle?) {
|
||||||
binding.pickerRecycler.adapter = artistAdapter
|
binding.pickerRecycler.apply {
|
||||||
|
itemAnimator = null
|
||||||
|
adapter = choiceAdapter
|
||||||
|
}
|
||||||
|
|
||||||
collectImmediately(pickerModel.artistChoices) { artists ->
|
initChoices()
|
||||||
if (artists.isNotEmpty()) {
|
collectImmediately(pickerChoices) { item ->
|
||||||
// Make sure the artist choices align with any changes in the music library.
|
if (item != null) {
|
||||||
artistAdapter.submitList(artists)
|
// Make sure the choices align with any changes in the music library.
|
||||||
|
choiceAdapter.update(item.choices, UpdateInstructions.Diff)
|
||||||
} else {
|
} else {
|
||||||
// Not showing any choices, navigate up.
|
// Not showing any choices, navigate up.
|
||||||
findNavController().navigateUp()
|
findNavController().navigateUp()
|
||||||
|
@ -71,7 +79,7 @@ abstract class ArtistPickerDialog :
|
||||||
binding.pickerRecycler.adapter = null
|
binding.pickerRecycler.adapter = null
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onClick(item: Artist, viewHolder: RecyclerView.ViewHolder) {
|
override fun onClick(item: T, viewHolder: RecyclerView.ViewHolder) {
|
||||||
findNavController().navigateUp()
|
findNavController().navigateUp()
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,87 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2022 Auxio Project
|
|
||||||
* PickerViewModel.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.picker
|
|
||||||
|
|
||||||
import androidx.lifecycle.ViewModel
|
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
|
||||||
import javax.inject.Inject
|
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
|
||||||
import org.oxycblt.auxio.music.*
|
|
||||||
|
|
||||||
/**
|
|
||||||
* a [ViewModel] that manages the current music picker state. Make it so that the dialogs just
|
|
||||||
* contain the music themselves and then exit if the library changes.
|
|
||||||
*
|
|
||||||
* @author Alexander Capehart (OxygenCobalt)
|
|
||||||
*/
|
|
||||||
@HiltViewModel
|
|
||||||
class PickerViewModel @Inject constructor(private val musicRepository: MusicRepository) :
|
|
||||||
ViewModel(), MusicRepository.UpdateListener {
|
|
||||||
|
|
||||||
private val _currentItem = MutableStateFlow<Music?>(null)
|
|
||||||
/** The current item whose artists should be shown in the picker. Null if there is no item. */
|
|
||||||
val currentItem: StateFlow<Music?>
|
|
||||||
get() = _currentItem
|
|
||||||
|
|
||||||
private val _artistChoices = MutableStateFlow<List<Artist>>(listOf())
|
|
||||||
/** The current [Artist] choices. Empty if no item is shown in the picker. */
|
|
||||||
val artistChoices: StateFlow<List<Artist>>
|
|
||||||
get() = _artistChoices
|
|
||||||
|
|
||||||
private val _genreChoices = MutableStateFlow<List<Genre>>(listOf())
|
|
||||||
/** The current [Genre] choices. Empty if no item is shown in the picker. */
|
|
||||||
val genreChoices: StateFlow<List<Genre>>
|
|
||||||
get() = _genreChoices
|
|
||||||
|
|
||||||
init {
|
|
||||||
musicRepository.addUpdateListener(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCleared() {
|
|
||||||
musicRepository.removeUpdateListener(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onMusicChanges(changes: MusicRepository.Changes) {
|
|
||||||
if (changes.deviceLibrary && musicRepository.deviceLibrary != null) {
|
|
||||||
refreshChoices()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set a new [currentItem] from it's [Music.UID].
|
|
||||||
*
|
|
||||||
* @param uid The [Music.UID] of the [Song] to update to.
|
|
||||||
*/
|
|
||||||
fun setItemUid(uid: Music.UID) {
|
|
||||||
_currentItem.value = musicRepository.find(uid)
|
|
||||||
refreshChoices()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun refreshChoices() {
|
|
||||||
when (val item = _currentItem.value) {
|
|
||||||
is Song -> {
|
|
||||||
_artistChoices.value = item.artists
|
|
||||||
_genreChoices.value = item.genres
|
|
||||||
}
|
|
||||||
is Album -> _artistChoices.value = item.artists
|
|
||||||
else -> {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -26,9 +26,9 @@ import org.oxycblt.auxio.R
|
||||||
import org.oxycblt.auxio.databinding.FragmentPlaybackBarBinding
|
import org.oxycblt.auxio.databinding.FragmentPlaybackBarBinding
|
||||||
import org.oxycblt.auxio.music.Song
|
import org.oxycblt.auxio.music.Song
|
||||||
import org.oxycblt.auxio.music.resolveNames
|
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.state.RepeatMode
|
||||||
import org.oxycblt.auxio.ui.MainNavigationAction
|
|
||||||
import org.oxycblt.auxio.ui.NavigationViewModel
|
|
||||||
import org.oxycblt.auxio.ui.ViewBindingFragment
|
import org.oxycblt.auxio.ui.ViewBindingFragment
|
||||||
import org.oxycblt.auxio.util.collectImmediately
|
import org.oxycblt.auxio.util.collectImmediately
|
||||||
import org.oxycblt.auxio.util.getAttrColorCompat
|
import org.oxycblt.auxio.util.getAttrColorCompat
|
||||||
|
|
|
@ -36,10 +36,10 @@ import org.oxycblt.auxio.databinding.FragmentPlaybackPanelBinding
|
||||||
import org.oxycblt.auxio.music.MusicParent
|
import org.oxycblt.auxio.music.MusicParent
|
||||||
import org.oxycblt.auxio.music.Song
|
import org.oxycblt.auxio.music.Song
|
||||||
import org.oxycblt.auxio.music.resolveNames
|
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.state.RepeatMode
|
||||||
import org.oxycblt.auxio.playback.ui.StyledSeekBar
|
import org.oxycblt.auxio.playback.ui.StyledSeekBar
|
||||||
import org.oxycblt.auxio.ui.MainNavigationAction
|
|
||||||
import org.oxycblt.auxio.ui.NavigationViewModel
|
|
||||||
import org.oxycblt.auxio.ui.ViewBindingFragment
|
import org.oxycblt.auxio.ui.ViewBindingFragment
|
||||||
import org.oxycblt.auxio.util.collectImmediately
|
import org.oxycblt.auxio.util.collectImmediately
|
||||||
import org.oxycblt.auxio.util.showToast
|
import org.oxycblt.auxio.util.showToast
|
||||||
|
|
|
@ -16,18 +16,19 @@
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.oxycblt.auxio.picker
|
package org.oxycblt.auxio.playback.picker
|
||||||
|
|
||||||
import android.os.Bundle
|
|
||||||
import androidx.fragment.app.activityViewModels
|
import androidx.fragment.app.activityViewModels
|
||||||
|
import androidx.fragment.app.viewModels
|
||||||
import androidx.navigation.fragment.navArgs
|
import androidx.navigation.fragment.navArgs
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
import org.oxycblt.auxio.databinding.DialogMusicPickerBinding
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
import org.oxycblt.auxio.R
|
||||||
import org.oxycblt.auxio.music.Artist
|
import org.oxycblt.auxio.music.Artist
|
||||||
import org.oxycblt.auxio.music.Song
|
import org.oxycblt.auxio.picker.PickerChoices
|
||||||
|
import org.oxycblt.auxio.picker.PickerDialogFragment
|
||||||
import org.oxycblt.auxio.playback.PlaybackViewModel
|
import org.oxycblt.auxio.playback.PlaybackViewModel
|
||||||
import org.oxycblt.auxio.util.requireIs
|
|
||||||
import org.oxycblt.auxio.util.unlikelyToBeNull
|
import org.oxycblt.auxio.util.unlikelyToBeNull
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -36,21 +37,27 @@ import org.oxycblt.auxio.util.unlikelyToBeNull
|
||||||
* @author Alexander Capehart (OxygenCobalt)
|
* @author Alexander Capehart (OxygenCobalt)
|
||||||
*/
|
*/
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
class ArtistPlaybackPickerDialog : ArtistPickerDialog() {
|
class ArtistPlaybackPickerDialog : PickerDialogFragment<Artist>() {
|
||||||
private val playbackModel: PlaybackViewModel by activityViewModels()
|
private val playbackModel: PlaybackViewModel by activityViewModels()
|
||||||
|
private val pickerModel: PlaybackPickerViewModel by viewModels()
|
||||||
// Information about what Song to show choices for is initially within the navigation arguments
|
// Information about what Song to show choices for is initially within the navigation arguments
|
||||||
// as UIDs, as that is the only safe way to parcel a Song.
|
// as UIDs, as that is the only safe way to parcel a Song.
|
||||||
private val args: ArtistPlaybackPickerDialogArgs by navArgs()
|
private val args: ArtistPlaybackPickerDialogArgs by navArgs()
|
||||||
|
|
||||||
override fun onBindingCreated(binding: DialogMusicPickerBinding, savedInstanceState: Bundle?) {
|
override val titleRes: Int
|
||||||
pickerModel.setItemUid(args.itemUid)
|
get() = R.string.lbl_artists
|
||||||
super.onBindingCreated(binding, savedInstanceState)
|
|
||||||
|
override val pickerChoices: StateFlow<PickerChoices<Artist>?>
|
||||||
|
get() = pickerModel.currentArtistChoices
|
||||||
|
|
||||||
|
override fun initChoices() {
|
||||||
|
pickerModel.setArtistChoiceUid(args.artistUid)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onClick(item: Artist, viewHolder: RecyclerView.ViewHolder) {
|
override fun onClick(item: Artist, viewHolder: RecyclerView.ViewHolder) {
|
||||||
super.onClick(item, viewHolder)
|
super.onClick(item, viewHolder)
|
||||||
// User made a choice, play the given song from that artist.
|
// User made a choice, play the given song from that artist.
|
||||||
val song = requireIs<Song>(unlikelyToBeNull(pickerModel.currentItem.value))
|
val song = unlikelyToBeNull(pickerModel.currentArtistChoices.value).song
|
||||||
playbackModel.playFromArtist(song, item)
|
playbackModel.playFromArtist(song, item)
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -16,26 +16,20 @@
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.oxycblt.auxio.picker
|
package org.oxycblt.auxio.playback.picker
|
||||||
|
|
||||||
import android.os.Bundle
|
|
||||||
import android.view.LayoutInflater
|
|
||||||
import androidx.appcompat.app.AlertDialog
|
|
||||||
import androidx.fragment.app.activityViewModels
|
import androidx.fragment.app.activityViewModels
|
||||||
import androidx.fragment.app.viewModels
|
import androidx.fragment.app.viewModels
|
||||||
import androidx.navigation.fragment.findNavController
|
|
||||||
import androidx.navigation.fragment.navArgs
|
import androidx.navigation.fragment.navArgs
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import org.oxycblt.auxio.R
|
import org.oxycblt.auxio.R
|
||||||
import org.oxycblt.auxio.databinding.DialogMusicPickerBinding
|
|
||||||
import org.oxycblt.auxio.list.ClickableListListener
|
|
||||||
import org.oxycblt.auxio.music.Genre
|
import org.oxycblt.auxio.music.Genre
|
||||||
import org.oxycblt.auxio.music.Song
|
import org.oxycblt.auxio.picker.PickerChoices
|
||||||
|
import org.oxycblt.auxio.picker.PickerDialogFragment
|
||||||
import org.oxycblt.auxio.playback.PlaybackViewModel
|
import org.oxycblt.auxio.playback.PlaybackViewModel
|
||||||
import org.oxycblt.auxio.ui.ViewBindingDialogFragment
|
import org.oxycblt.auxio.ui.ViewBindingDialogFragment
|
||||||
import org.oxycblt.auxio.util.collectImmediately
|
|
||||||
import org.oxycblt.auxio.util.requireIs
|
|
||||||
import org.oxycblt.auxio.util.unlikelyToBeNull
|
import org.oxycblt.auxio.util.unlikelyToBeNull
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -44,45 +38,27 @@ import org.oxycblt.auxio.util.unlikelyToBeNull
|
||||||
* @author Alexander Capehart (OxygenCobalt)
|
* @author Alexander Capehart (OxygenCobalt)
|
||||||
*/
|
*/
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
class GenrePlaybackPickerDialog :
|
class GenrePlaybackPickerDialog : PickerDialogFragment<Genre>() {
|
||||||
ViewBindingDialogFragment<DialogMusicPickerBinding>(), ClickableListListener<Genre> {
|
|
||||||
private val pickerModel: PickerViewModel by viewModels()
|
|
||||||
private val playbackModel: PlaybackViewModel by activityViewModels()
|
private val playbackModel: PlaybackViewModel by activityViewModels()
|
||||||
|
private val pickerModel: PlaybackPickerViewModel by viewModels()
|
||||||
// Information about what Song to show choices for is initially within the navigation arguments
|
// Information about what Song to show choices for is initially within the navigation arguments
|
||||||
// as UIDs, as that is the only safe way to parcel a Song.
|
// as UIDs, as that is the only safe way to parcel a Song.
|
||||||
private val args: GenrePlaybackPickerDialogArgs by navArgs()
|
private val args: GenrePlaybackPickerDialogArgs by navArgs()
|
||||||
// Okay to leak this since the Listener will not be called until after initialization.
|
|
||||||
private val genreAdapter = GenreChoiceAdapter(@Suppress("LeakingThis") this)
|
|
||||||
|
|
||||||
override fun onCreateBinding(inflater: LayoutInflater) =
|
override val titleRes: Int
|
||||||
DialogMusicPickerBinding.inflate(inflater)
|
get() = R.string.lbl_genres
|
||||||
|
|
||||||
override fun onConfigDialog(builder: AlertDialog.Builder) {
|
override val pickerChoices: StateFlow<PickerChoices<Genre>?>
|
||||||
builder.setTitle(R.string.lbl_genres).setNegativeButton(R.string.lbl_cancel, null)
|
get() = pickerModel.currentGenreChoices
|
||||||
}
|
|
||||||
|
|
||||||
override fun onBindingCreated(binding: DialogMusicPickerBinding, savedInstanceState: Bundle?) {
|
override fun initChoices() {
|
||||||
binding.pickerRecycler.adapter = genreAdapter
|
pickerModel.setGenreChoiceUid(args.genreUid)
|
||||||
|
|
||||||
pickerModel.setItemUid(args.itemUid)
|
|
||||||
collectImmediately(pickerModel.genreChoices) { genres ->
|
|
||||||
if (genres.isNotEmpty()) {
|
|
||||||
// Make sure the genre choices align with any changes in the music library.
|
|
||||||
genreAdapter.submitList(genres)
|
|
||||||
} else {
|
|
||||||
// Not showing any choices, navigate up.
|
|
||||||
findNavController().navigateUp()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onDestroyBinding(binding: DialogMusicPickerBinding) {
|
|
||||||
binding.pickerRecycler.adapter = null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onClick(item: Genre, viewHolder: RecyclerView.ViewHolder) {
|
override fun onClick(item: Genre, viewHolder: RecyclerView.ViewHolder) {
|
||||||
|
super.onClick(item, viewHolder)
|
||||||
// User made a choice, play the given song from that genre.
|
// User made a choice, play the given song from that genre.
|
||||||
val song = requireIs<Song>(unlikelyToBeNull(pickerModel.currentItem.value))
|
val song = unlikelyToBeNull(pickerModel.currentGenreChoices.value).song
|
||||||
playbackModel.playFromGenre(song, item)
|
playbackModel.playFromGenre(song, item)
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,96 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2023 Auxio Project
|
||||||
|
* PlaybackPickerViewModel.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.playback.picker
|
||||||
|
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
|
import javax.inject.Inject
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
import org.oxycblt.auxio.music.*
|
||||||
|
import org.oxycblt.auxio.picker.PickerChoices
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A [ViewModel] that stores the choices shown in the playback picker dialogs.
|
||||||
|
*
|
||||||
|
* @author OxygenCobalt (Alexander Capehart)
|
||||||
|
*/
|
||||||
|
@HiltViewModel
|
||||||
|
class PlaybackPickerViewModel @Inject constructor(private val musicRepository: MusicRepository) :
|
||||||
|
ViewModel(), MusicRepository.UpdateListener {
|
||||||
|
private val _currentArtistChoices = MutableStateFlow<ArtistPlaybackChoices?>(null)
|
||||||
|
/** The current set of [Artist] choices to show in the picker, or null if to show nothing. */
|
||||||
|
val currentArtistChoices: StateFlow<ArtistPlaybackChoices?>
|
||||||
|
get() = _currentArtistChoices
|
||||||
|
|
||||||
|
private val _currentGenreChoices = MutableStateFlow<GenrePlaybackChoices?>(null)
|
||||||
|
/** The current set of [Genre] choices to show in the picker, or null if to show nothing. */
|
||||||
|
val currentGenreChoices: StateFlow<GenrePlaybackChoices?>
|
||||||
|
get() = _currentGenreChoices
|
||||||
|
|
||||||
|
init {
|
||||||
|
musicRepository.addUpdateListener(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onMusicChanges(changes: MusicRepository.Changes) {
|
||||||
|
if (!changes.deviceLibrary) return
|
||||||
|
val deviceLibrary = musicRepository.deviceLibrary ?: return
|
||||||
|
_currentArtistChoices.value =
|
||||||
|
_currentArtistChoices.value?.run {
|
||||||
|
deviceLibrary.findSong(song.uid)?.let { newSong -> ArtistPlaybackChoices(newSong) }
|
||||||
|
}
|
||||||
|
_currentGenreChoices.value =
|
||||||
|
_currentGenreChoices.value?.run {
|
||||||
|
deviceLibrary.findSong(song.uid)?.let { newSong -> GenrePlaybackChoices(newSong) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCleared() {
|
||||||
|
super.onCleared()
|
||||||
|
musicRepository.removeUpdateListener(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the [Music.UID] of the item to show [Artist] choices for.
|
||||||
|
*
|
||||||
|
* @param uid The [Music.UID] of the item to show. Must be a [Song].
|
||||||
|
*/
|
||||||
|
fun setArtistChoiceUid(uid: Music.UID) {
|
||||||
|
_currentArtistChoices.value =
|
||||||
|
musicRepository.deviceLibrary?.findSong(uid)?.let { ArtistPlaybackChoices(it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the [Music.UID] of the item to show [Genre] choices for.
|
||||||
|
*
|
||||||
|
* @param uid The [Music.UID] of the item to show. Must be a [Song].
|
||||||
|
*/
|
||||||
|
fun setGenreChoiceUid(uid: Music.UID) {
|
||||||
|
_currentGenreChoices.value =
|
||||||
|
musicRepository.deviceLibrary?.findSong(uid)?.let { GenrePlaybackChoices(it) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data class ArtistPlaybackChoices(val song: Song) : PickerChoices<Artist> {
|
||||||
|
override val choices = song.artists
|
||||||
|
}
|
||||||
|
|
||||||
|
data class GenrePlaybackChoices(val song: Song) : PickerChoices<Genre> {
|
||||||
|
override val choices = song.genres
|
||||||
|
}
|
|
@ -37,8 +37,8 @@ import org.oxycblt.auxio.list.Item
|
||||||
import org.oxycblt.auxio.list.ListFragment
|
import org.oxycblt.auxio.list.ListFragment
|
||||||
import org.oxycblt.auxio.list.selection.SelectionViewModel
|
import org.oxycblt.auxio.list.selection.SelectionViewModel
|
||||||
import org.oxycblt.auxio.music.*
|
import org.oxycblt.auxio.music.*
|
||||||
|
import org.oxycblt.auxio.navigation.NavigationViewModel
|
||||||
import org.oxycblt.auxio.playback.PlaybackViewModel
|
import org.oxycblt.auxio.playback.PlaybackViewModel
|
||||||
import org.oxycblt.auxio.ui.NavigationViewModel
|
|
||||||
import org.oxycblt.auxio.util.*
|
import org.oxycblt.auxio.util.*
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -30,31 +30,31 @@
|
||||||
|
|
||||||
<dialog
|
<dialog
|
||||||
android:id="@+id/artist_playback_picker_dialog"
|
android:id="@+id/artist_playback_picker_dialog"
|
||||||
android:name="org.oxycblt.auxio.picker.ArtistPlaybackPickerDialog"
|
android:name="org.oxycblt.auxio.playback.picker.ArtistPlaybackPickerDialog"
|
||||||
android:label="artist_playback_picker_dialog"
|
android:label="artist_playback_picker_dialog"
|
||||||
tools:layout="@layout/dialog_music_picker">
|
tools:layout="@layout/dialog_music_picker">
|
||||||
<argument
|
<argument
|
||||||
android:name="itemUid"
|
android:name="artistUid"
|
||||||
app:argType="org.oxycblt.auxio.music.Music$UID" />
|
app:argType="org.oxycblt.auxio.music.Music$UID" />
|
||||||
</dialog>
|
</dialog>
|
||||||
|
|
||||||
<dialog
|
<dialog
|
||||||
android:id="@+id/artist_navigation_picker_dialog"
|
android:id="@+id/artist_navigation_picker_dialog"
|
||||||
android:name="org.oxycblt.auxio.picker.ArtistNavigationPickerDialog"
|
android:name="org.oxycblt.auxio.navigation.picker.ArtistNavigationPickerDialog"
|
||||||
android:label="artist_navigation_picker_dialog"
|
android:label="artist_navigation_picker_dialog"
|
||||||
tools:layout="@layout/dialog_music_picker">
|
tools:layout="@layout/dialog_music_picker">
|
||||||
<argument
|
<argument
|
||||||
android:name="itemUid"
|
android:name="artistUid"
|
||||||
app:argType="org.oxycblt.auxio.music.Music$UID" />
|
app:argType="org.oxycblt.auxio.music.Music$UID" />
|
||||||
</dialog>
|
</dialog>
|
||||||
|
|
||||||
<dialog
|
<dialog
|
||||||
android:id="@+id/genre_playback_picker_dialog"
|
android:id="@+id/genre_playback_picker_dialog"
|
||||||
android:name="org.oxycblt.auxio.picker.GenrePlaybackPickerDialog"
|
android:name="org.oxycblt.auxio.playback.picker.GenrePlaybackPickerDialog"
|
||||||
android:label="genre_playback_picker_dialog"
|
android:label="genre_playback_picker_dialog"
|
||||||
tools:layout="@layout/dialog_music_picker">
|
tools:layout="@layout/dialog_music_picker">
|
||||||
<argument
|
<argument
|
||||||
android:name="itemUid"
|
android:name="genreUid"
|
||||||
app:argType="org.oxycblt.auxio.music.Music$UID" />
|
app:argType="org.oxycblt.auxio.music.Music$UID" />
|
||||||
</dialog>
|
</dialog>
|
||||||
|
|
||||||
|
@ -64,7 +64,7 @@
|
||||||
android:label="song_detail_dialog"
|
android:label="song_detail_dialog"
|
||||||
tools:layout="@layout/dialog_song_detail">
|
tools:layout="@layout/dialog_song_detail">
|
||||||
<argument
|
<argument
|
||||||
android:name="itemUid"
|
android:name="songUid"
|
||||||
app:argType="org.oxycblt.auxio.music.Music$UID" />
|
app:argType="org.oxycblt.auxio.music.Music$UID" />
|
||||||
</dialog>
|
</dialog>
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue