music: try to deduplicate multi-artist code

Try to move multi-artist playback/navigation into a single function.
This function is really bad and is tacked onto the most convienent
location without much thought. I really wish to move this into the
ViewModel flow eventually, but I have no idea how to architecture
that. Oh well.
This commit is contained in:
Alexander Capehart 2022-11-13 19:04:58 -07:00
parent 3a236bdaf4
commit aa805e351c
No known key found for this signature in database
GPG key ID: 37DBE3621FE9AD47
14 changed files with 79 additions and 120 deletions

View file

@ -28,7 +28,7 @@
#### What's Fixed
- Fixed issue where the scroll popup would not display correctly in landscape mode [#230]
- Fixed issue where the playback progress would continue in the notification even if
- Fixed issue where the playback progress would continue in the notification when
audio focus was lost
- Fixed issue where the app would crash if a song menu in the genre UI was opened
- Fixed issue where the artist name would not be shown in the OS audio switcher menu

View file

@ -43,7 +43,7 @@ import org.oxycblt.auxio.music.Sort
import org.oxycblt.auxio.music.picker.PickerMode
import org.oxycblt.auxio.settings.Settings
import org.oxycblt.auxio.ui.MainNavigationAction
import org.oxycblt.auxio.ui.fragment.MenuFragment
import org.oxycblt.auxio.ui.fragment.MusicFragment
import org.oxycblt.auxio.ui.recycler.Item
import org.oxycblt.auxio.util.canScroll
import org.oxycblt.auxio.util.collect
@ -58,7 +58,7 @@ import org.oxycblt.auxio.util.unlikelyToBeNull
* @author OxygenCobalt
*/
class AlbumDetailFragment :
MenuFragment<FragmentDetailBinding>(),
MusicFragment<FragmentDetailBinding>(),
Toolbar.OnMenuItemClickListener,
AlbumDetailAdapter.Listener {
private val detailModel: DetailViewModel by activityViewModels()
@ -132,17 +132,7 @@ class AlbumDetailFragment :
when (settings.detailPlaybackMode) {
null, MusicMode.ALBUMS -> playbackModel.playFromAlbum(item)
MusicMode.SONGS -> playbackModel.playFromAll(item)
MusicMode.ARTISTS -> {
if (item.artists.size == 1) {
playbackModel.playFromArtist(item, item.artists[0])
} else {
navModel.mainNavigateTo(
MainNavigationAction.Directions(
MainFragmentDirections.actionPickArtist(item.uid, PickerMode.PLAY)
)
)
}
}
MusicMode.ARTISTS -> doArtistDependentAction(item, PickerMode.PLAY)
else -> error("Unexpected playback mode: ${settings.detailPlaybackMode}")
}
}
@ -179,16 +169,7 @@ class AlbumDetailFragment :
}
override fun onNavigateToArtist() {
val album = unlikelyToBeNull(detailModel.currentAlbum.value)
if (album.artists.size == 1) {
navModel.exploreNavigateTo(album.artists[0])
} else {
navModel.mainNavigateTo(
MainNavigationAction.Directions(
MainFragmentDirections.actionPickArtist(album.uid, PickerMode.SHOW)
)
)
}
navigateToArtist(unlikelyToBeNull(detailModel.currentAlbum.value))
}
private fun handleItemChange(album: Album?) {

View file

@ -41,7 +41,7 @@ import org.oxycblt.auxio.music.Sort
import org.oxycblt.auxio.music.picker.PickerMode
import org.oxycblt.auxio.settings.Settings
import org.oxycblt.auxio.ui.MainNavigationAction
import org.oxycblt.auxio.ui.fragment.MenuFragment
import org.oxycblt.auxio.ui.fragment.MusicFragment
import org.oxycblt.auxio.ui.recycler.Item
import org.oxycblt.auxio.util.collect
import org.oxycblt.auxio.util.collectImmediately
@ -55,7 +55,7 @@ import org.oxycblt.auxio.util.unlikelyToBeNull
* @author OxygenCobalt
*/
class ArtistDetailFragment :
MenuFragment<FragmentDetailBinding>(), Toolbar.OnMenuItemClickListener, DetailAdapter.Listener {
MusicFragment<FragmentDetailBinding>(), Toolbar.OnMenuItemClickListener, DetailAdapter.Listener {
private val detailModel: DetailViewModel by activityViewModels()
private val args: ArtistDetailFragmentArgs by navArgs()
@ -125,17 +125,7 @@ class ArtistDetailFragment :
null -> playbackModel.playFromArtist(item, unlikelyToBeNull(detailModel.currentArtist.value))
MusicMode.SONGS -> playbackModel.playFromAll(item)
MusicMode.ALBUMS -> playbackModel.playFromAlbum(item)
MusicMode.ARTISTS -> {
if (item.artists.size == 1) {
playbackModel.playFromArtist(item, item.artists[0])
} else {
navModel.mainNavigateTo(
MainNavigationAction.Directions(
MainFragmentDirections.actionPickArtist(item.uid, PickerMode.PLAY)
)
)
}
}
MusicMode.ARTISTS -> doArtistDependentAction(item, PickerMode.PLAY)
else -> error("Unexpected playback mode: ${settings.detailPlaybackMode}")
}
}

View file

@ -42,7 +42,7 @@ import org.oxycblt.auxio.music.Sort
import org.oxycblt.auxio.music.picker.PickerMode
import org.oxycblt.auxio.settings.Settings
import org.oxycblt.auxio.ui.MainNavigationAction
import org.oxycblt.auxio.ui.fragment.MenuFragment
import org.oxycblt.auxio.ui.fragment.MusicFragment
import org.oxycblt.auxio.ui.recycler.Item
import org.oxycblt.auxio.util.collect
import org.oxycblt.auxio.util.collectImmediately
@ -56,7 +56,7 @@ import org.oxycblt.auxio.util.unlikelyToBeNull
* @author OxygenCobalt
*/
class GenreDetailFragment :
MenuFragment<FragmentDetailBinding>(), Toolbar.OnMenuItemClickListener, DetailAdapter.Listener {
MusicFragment<FragmentDetailBinding>(), Toolbar.OnMenuItemClickListener, DetailAdapter.Listener {
private val detailModel: DetailViewModel by activityViewModels()
private val args: GenreDetailFragmentArgs by navArgs()
@ -120,29 +120,27 @@ class GenreDetailFragment :
}
override fun onItemClick(item: Item) {
check(item is Song) { "Unexpected datatype: ${item::class.simpleName}" }
when (settings.detailPlaybackMode) {
null -> playbackModel.playFromGenre(item, unlikelyToBeNull(detailModel.currentGenre.value))
MusicMode.SONGS -> playbackModel.playFromAll(item)
MusicMode.ALBUMS -> playbackModel.playFromAlbum(item)
MusicMode.ARTISTS -> {
if (item.artists.size == 1) {
playbackModel.playFromArtist(item, item.artists[0])
} else {
navModel.mainNavigateTo(
MainNavigationAction.Directions(
MainFragmentDirections.actionPickArtist(item.uid, PickerMode.PLAY)
)
)
}
when (item) {
is Artist -> navModel.exploreNavigateTo(item)
is Song -> when (settings.detailPlaybackMode) {
null -> playbackModel.playFromGenre(item, unlikelyToBeNull(detailModel.currentGenre.value))
MusicMode.SONGS -> playbackModel.playFromAll(item)
MusicMode.ALBUMS -> playbackModel.playFromAlbum(item)
MusicMode.ARTISTS -> doArtistDependentAction(item, PickerMode.PLAY)
else -> error("Unexpected playback mode: ${settings.detailPlaybackMode}")
}
else -> error("Unexpected playback mode: ${settings.detailPlaybackMode}")
else -> error("Unexpected datatype: ${item::class.simpleName}")
}
}
override fun onOpenMenu(item: Item, anchor: View) {
check(item is Song) { "Unexpected datatype: ${item::class.simpleName}" }
musicMenu(anchor, R.menu.menu_song_actions, item)
when (item) {
is Artist -> musicMenu(anchor, R.menu.menu_artist_actions, item)
is Song -> musicMenu(anchor, R.menu.menu_song_actions, item)
else -> error("Unexpected datatype: ${item::class.simpleName}")
}
}
override fun onPlayParent() {

View file

@ -87,6 +87,8 @@ class GenreDetailAdapter(private val listener: Listener) :
return when {
oldItem is Genre && newItem is Genre ->
GenreDetailViewHolder.DIFFER.areContentsTheSame(oldItem, newItem)
oldItem is Artist && newItem is Artist ->
ArtistViewHolder.DIFFER.areContentsTheSame(oldItem, newItem)
oldItem is Song && newItem is Song ->
SongViewHolder.DIFFER.areContentsTheSame(oldItem, newItem)
else -> DetailAdapter.DIFFER.areContentsTheSame(oldItem, newItem)

View file

@ -80,7 +80,7 @@ class ArtistListFragment : HomeListFragment<Artist>() {
override fun onOpenMenu(item: Item, anchor: View) {
check(item is Artist) { "Unexpected datatype: ${item::class.java}" }
musicMenu(anchor, R.menu.menu_genre_artist_actions, item)
musicMenu(anchor, R.menu.menu_artist_actions, item)
}
private fun handleParent(parent: MusicParent?, isPlaying: Boolean) {

View file

@ -79,7 +79,7 @@ class GenreListFragment : HomeListFragment<Genre>() {
override fun onOpenMenu(item: Item, anchor: View) {
check(item is Genre) { "Unexpected datatype: ${item::class.java}" }
musicMenu(anchor, R.menu.menu_genre_artist_actions, item)
musicMenu(anchor, R.menu.menu_artist_actions, item)
}
private fun handlePlayback(parent: MusicParent?, isPlaying: Boolean) {

View file

@ -23,7 +23,7 @@ import androidx.fragment.app.Fragment
import org.oxycblt.auxio.databinding.FragmentHomeListBinding
import org.oxycblt.auxio.home.HomeViewModel
import org.oxycblt.auxio.ui.fastscroll.FastScrollRecyclerView
import org.oxycblt.auxio.ui.fragment.MenuFragment
import org.oxycblt.auxio.ui.fragment.MusicFragment
import org.oxycblt.auxio.ui.recycler.Item
import org.oxycblt.auxio.ui.recycler.MenuItemListener
import org.oxycblt.auxio.util.androidActivityViewModels
@ -33,7 +33,7 @@ import org.oxycblt.auxio.util.androidActivityViewModels
* @author OxygenCobalt
*/
abstract class HomeListFragment<T : Item> :
MenuFragment<FragmentHomeListBinding>(),
MusicFragment<FragmentHomeListBinding>(),
MenuItemListener,
FastScrollRecyclerView.PopupProvider,
FastScrollRecyclerView.OnFastScrollListener {

View file

@ -115,17 +115,7 @@ class SongListFragment : HomeListFragment<Song>() {
when (settings.libPlaybackMode) {
MusicMode.SONGS -> playbackModel.playFromAll(item)
MusicMode.ALBUMS -> playbackModel.playFromAlbum(item)
MusicMode.ARTISTS -> {
if (item.artists.size == 1) {
playbackModel.playFromArtist(item, item.artists[0])
} else {
navModel.mainNavigateTo(
MainNavigationAction.Directions(
MainFragmentDirections.actionPickArtist(item.uid, PickerMode.PLAY)
)
)
}
}
MusicMode.ARTISTS -> doArtistDependentAction(item, PickerMode.PLAY)
else -> error("Unexpected playback mode: ${settings.libPlaybackMode}")
}
}

View file

@ -52,7 +52,7 @@ import org.oxycblt.auxio.util.logW
* (and hacky garbage) in order to produce the best possible experience. It is split into three
* distinct steps:
*
* 1. Creating the chain of layers to extract metadata with
* 1. Creating the chain of extractors to extract metadata with
* 2. Running the chain process
* 3. Using the songs to build the library, which primarily involves linking up all data objects
* with their corresponding parents/children.

View file

@ -36,7 +36,7 @@ import org.oxycblt.auxio.music.picker.PickerMode
import org.oxycblt.auxio.playback.state.RepeatMode
import org.oxycblt.auxio.playback.ui.StyledSeekBar
import org.oxycblt.auxio.ui.MainNavigationAction
import org.oxycblt.auxio.ui.fragment.MenuFragment
import org.oxycblt.auxio.ui.fragment.MusicFragment
import org.oxycblt.auxio.util.collectImmediately
import org.oxycblt.auxio.util.showToast
import org.oxycblt.auxio.util.systemBarInsetsCompat
@ -49,7 +49,7 @@ import org.oxycblt.auxio.util.systemBarInsetsCompat
* TODO: Make seek thumb grow when selected
*/
class PlaybackPanelFragment :
MenuFragment<FragmentPlaybackPanelBinding>(),
MusicFragment<FragmentPlaybackPanelBinding>(),
StyledSeekBar.Callback,
Toolbar.OnMenuItemClickListener {
// AudioEffect expects you to use startActivityForResult with the panel intent. Use
@ -215,15 +215,7 @@ class PlaybackPanelFragment :
private fun showCurrentArtist() {
val song = playbackModel.song.value ?: return
if (song.artists.size == 1) {
navModel.exploreNavigateTo(song.artists[0])
} else {
navModel.mainNavigateTo(
MainNavigationAction.Directions(
MainFragmentDirections.actionPickArtist(song.uid, PickerMode.SHOW)
)
)
}
doArtistDependentAction(song, PickerMode.SHOW)
}
private fun showCurrentAlbum() {
val song = playbackModel.song.value ?: return

View file

@ -29,7 +29,6 @@ import androidx.core.widget.addTextChangedListener
import androidx.fragment.app.Fragment
import androidx.navigation.fragment.findNavController
import com.google.android.material.transition.MaterialSharedAxis
import org.oxycblt.auxio.MainFragmentDirections
import org.oxycblt.auxio.R
import org.oxycblt.auxio.databinding.FragmentSearchBinding
import org.oxycblt.auxio.music.Album
@ -41,8 +40,7 @@ import org.oxycblt.auxio.music.MusicParent
import org.oxycblt.auxio.music.Song
import org.oxycblt.auxio.music.picker.PickerMode
import org.oxycblt.auxio.settings.Settings
import org.oxycblt.auxio.ui.MainNavigationAction
import org.oxycblt.auxio.ui.fragment.MenuFragment
import org.oxycblt.auxio.ui.fragment.MusicFragment
import org.oxycblt.auxio.ui.recycler.Item
import org.oxycblt.auxio.ui.recycler.MenuItemListener
import org.oxycblt.auxio.util.androidViewModels
@ -57,7 +55,7 @@ import org.oxycblt.auxio.util.logW
* @author OxygenCobalt
*/
class SearchFragment :
MenuFragment<FragmentSearchBinding>(), MenuItemListener, Toolbar.OnMenuItemClickListener {
MusicFragment<FragmentSearchBinding>(), MenuItemListener, Toolbar.OnMenuItemClickListener {
// SearchViewModel is only scoped to this Fragment
private val searchModel: SearchViewModel by androidViewModels()
@ -153,17 +151,7 @@ class SearchFragment :
is Song -> when (settings.libPlaybackMode) {
MusicMode.SONGS -> playbackModel.playFromAll(item)
MusicMode.ALBUMS -> playbackModel.playFromAlbum(item)
MusicMode.ARTISTS -> {
if (item.artists.size == 1) {
playbackModel.playFromArtist(item, item.artists[0])
} else {
navModel.mainNavigateTo(
MainNavigationAction.Directions(
MainFragmentDirections.actionPickArtist(item.uid, PickerMode.PLAY)
)
)
}
}
MusicMode.ARTISTS -> doArtistDependentAction(item, PickerMode.PLAY)
else -> error("Unexpected playback mode: ${settings.libPlaybackMode}")
}
is MusicParent -> navModel.exploreNavigateTo(item)
@ -174,8 +162,8 @@ class SearchFragment :
when (item) {
is Song -> musicMenu(anchor, R.menu.menu_song_actions, item)
is Album -> musicMenu(anchor, R.menu.menu_album_actions, item)
is Artist -> musicMenu(anchor, R.menu.menu_genre_artist_actions, item)
is Genre -> musicMenu(anchor, R.menu.menu_genre_artist_actions, item)
is Artist -> musicMenu(anchor, R.menu.menu_artist_actions, item)
is Genre -> musicMenu(anchor, R.menu.menu_artist_actions, item)
else -> logW("Unexpected datatype when opening menu: ${item::class.java}")
}
}

View file

@ -42,12 +42,46 @@ import org.oxycblt.auxio.util.showToast
* preventing UI issues and memory leaks.
* @author OxygenCobalt
*/
abstract class MenuFragment<T : ViewBinding> : ViewBindingFragment<T>() {
abstract class MusicFragment<T : ViewBinding> : ViewBindingFragment<T>() {
private var currentMenu: PopupMenu? = null
protected val playbackModel: PlaybackViewModel by androidActivityViewModels()
protected val navModel: NavigationViewModel by activityViewModels()
/**
* Run the UI flow to perform a specific [PickerMode] action with a particular
* artist from [song].
*/
fun doArtistDependentAction(song: Song, mode: PickerMode) {
if (song.artists.size == 1) {
when (mode) {
PickerMode.PLAY -> playbackModel.playFromArtist(song, song.artists[0])
PickerMode.SHOW -> navModel.exploreNavigateTo(song.artists[0])
}
} else {
navModel.mainNavigateTo(
MainNavigationAction.Directions(
MainFragmentDirections.actionPickArtist(song.uid, mode)
)
)
}
}
/**
* Run the UI flow to navigate to a particular artist from [album].
*/
fun navigateToArtist(album: Album) {
if (album.artists.size == 1) {
navModel.exploreNavigateTo(album.artists[0])
} else {
navModel.mainNavigateTo(
MainNavigationAction.Directions(
MainFragmentDirections.actionPickArtist(album.uid, PickerMode.SHOW)
)
)
}
}
/**
* Opens the given menu in context of [song]. Assumes that the menu is only composed of common
* [Song] options.
@ -66,15 +100,7 @@ abstract class MenuFragment<T : ViewBinding> : ViewBindingFragment<T>() {
requireContext().showToast(R.string.lng_queue_added)
}
R.id.action_go_artist -> {
if (song.artists.size == 1) {
navModel.exploreNavigateTo(song.artists[0])
} else {
navModel.mainNavigateTo(
MainNavigationAction.Directions(
MainFragmentDirections.actionPickArtist(song.uid, PickerMode.SHOW)
)
)
}
doArtistDependentAction(song, PickerMode.SHOW)
}
R.id.action_go_album -> {
navModel.exploreNavigateTo(song.album)
@ -119,15 +145,7 @@ abstract class MenuFragment<T : ViewBinding> : ViewBindingFragment<T>() {
requireContext().showToast(R.string.lng_queue_added)
}
R.id.action_go_artist -> {
if (album.artists.size == 1) {
navModel.exploreNavigateTo(album.artists[0])
} else {
navModel.mainNavigateTo(
MainNavigationAction.Directions(
MainFragmentDirections.actionPickArtist(album.uid, PickerMode.SHOW)
)
)
}
navigateToArtist(album)
}
else -> {
error("Unexpected menu item selected")