diff --git a/app/build.gradle b/app/build.gradle index 478ffc4f3..ea0781d88 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -5,7 +5,7 @@ apply plugin: 'androidx.navigation.safeargs' android { compileSdkVersion 30 - buildToolsVersion "30.0.1" + buildToolsVersion "30.0.3" defaultConfig { applicationId "org.oxycblt.auxio" diff --git a/app/src/main/java/org/oxycblt/auxio/MainFragment.kt b/app/src/main/java/org/oxycblt/auxio/MainFragment.kt index ee5d5eebd..7175ab913 100644 --- a/app/src/main/java/org/oxycblt/auxio/MainFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/MainFragment.kt @@ -90,7 +90,7 @@ class MainFragment : Fragment() { } } - playbackModel.navToItem.observe(viewLifecycleOwner) { + detailModel.navToItem.observe(viewLifecycleOwner) { if (it != null) { // If the current destination isn't even LibraryFragment, then navigate there first if (binding.navBar.selectedItemId != R.id.library_fragment) { diff --git a/app/src/main/java/org/oxycblt/auxio/detail/AlbumDetailFragment.kt b/app/src/main/java/org/oxycblt/auxio/detail/AlbumDetailFragment.kt index 33baf231c..6753f4ac9 100644 --- a/app/src/main/java/org/oxycblt/auxio/detail/AlbumDetailFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/detail/AlbumDetailFragment.kt @@ -7,9 +7,7 @@ import android.view.ViewGroup import androidx.appcompat.widget.PopupMenu import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.navArgs -import androidx.recyclerview.widget.GridLayoutManager import org.oxycblt.auxio.R -import org.oxycblt.auxio.databinding.FragmentDetailBinding import org.oxycblt.auxio.detail.adapters.AlbumDetailAdapter import org.oxycblt.auxio.logD import org.oxycblt.auxio.music.Album @@ -19,7 +17,6 @@ import org.oxycblt.auxio.music.Song import org.oxycblt.auxio.playback.state.PlaybackMode import org.oxycblt.auxio.recycler.CenterSmoothScroller import org.oxycblt.auxio.ui.createToast -import org.oxycblt.auxio.ui.isLandscape import org.oxycblt.auxio.ui.setupAlbumSongActions /** @@ -88,27 +85,7 @@ class AlbumDetailFragment : DetailFragment() { } } - binding.detailRecycler.apply { - adapter = detailAdapter - setHasFixedSize(true) - - if (isLandscape(resources)) { - layoutManager = GridLayoutManager(requireContext(), 2).also { - it.spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() { - override fun getSpanSize(position: Int): Int { - return if (position == 0) 2 else 1 - } - } - } - } - } - - // If this fragment was created in order to nav to an item, then snap scroll to that item. - playbackModel.navToItem.value?.let { - if (it is Song) { - scrollToPlayingItem(binding, smooth = false) - } - } + setupRecycler(detailAdapter) // -- VIEWMODEL SETUP --- @@ -126,7 +103,7 @@ class AlbumDetailFragment : DetailFragment() { detailModel.navToParent.observe(viewLifecycleOwner) { if (it) { - if (!args.enableParentNav) { + if (args.fromArtist) { findNavController().navigateUp() } else { findNavController().navigate( @@ -135,17 +112,19 @@ class AlbumDetailFragment : DetailFragment() { ) ) } + + detailModel.doneWithNavToParent() } } - playbackModel.navToItem.observe(viewLifecycleOwner) { + detailModel.navToItem.observe(viewLifecycleOwner) { if (it != null) { if (it is Song) { - scrollToPlayingItem(binding, smooth = true) + scrollToPlayingItem() } if (it is Album && it.id == detailModel.currentAlbum.value!!.id) { - playbackModel.doneWithNavToItem() + detailModel.doneWithNavToItem() } } } @@ -157,23 +136,21 @@ class AlbumDetailFragment : DetailFragment() { /** * Calculate the position and and scroll to a currently playing item. - * @param binding The binding required - * @param smooth Whether to scroll smoothly or not, true for yes, false for no. */ - private fun scrollToPlayingItem(binding: FragmentDetailBinding, smooth: Boolean) { + private fun scrollToPlayingItem() { // Calculate where the item for the currently played song is, and scroll to there val pos = detailModel.albumSortMode.value!!.getSortedSongList( detailModel.currentAlbum.value!!.songs - ).indexOf(playbackModel.song.value).inc() + ).indexOf(playbackModel.song.value) - if (pos != 0) { + if (pos != -1) { binding.detailRecycler.post { binding.detailRecycler.layoutManager?.startSmoothScroll( - CenterSmoothScroller(requireContext(), pos) + CenterSmoothScroller(requireContext(), pos.inc()) ) } - playbackModel.doneWithNavToItem() + detailModel.doneWithNavToItem() } } } diff --git a/app/src/main/java/org/oxycblt/auxio/detail/ArtistDetailFragment.kt b/app/src/main/java/org/oxycblt/auxio/detail/ArtistDetailFragment.kt index 65e44b434..31df71038 100644 --- a/app/src/main/java/org/oxycblt/auxio/detail/ArtistDetailFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/detail/ArtistDetailFragment.kt @@ -7,14 +7,12 @@ import android.view.ViewGroup import androidx.appcompat.widget.PopupMenu import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.navArgs -import androidx.recyclerview.widget.GridLayoutManager import org.oxycblt.auxio.R import org.oxycblt.auxio.detail.adapters.ArtistDetailAdapter import org.oxycblt.auxio.logD import org.oxycblt.auxio.music.Artist import org.oxycblt.auxio.music.BaseModel import org.oxycblt.auxio.music.MusicStore -import org.oxycblt.auxio.ui.isLandscape import org.oxycblt.auxio.ui.setupAlbumActions /** @@ -48,7 +46,7 @@ class ArtistDetailFragment : DetailFragment() { detailModel.updateNavigationStatus(true) findNavController().navigate( - ArtistDetailFragmentDirections.actionShowAlbum(it.id, false) + ArtistDetailFragmentDirections.actionShowAlbum(it.id, true) ) } }, @@ -86,20 +84,7 @@ class ArtistDetailFragment : DetailFragment() { } } - binding.detailRecycler.apply { - adapter = detailAdapter - setHasFixedSize(true) - - if (isLandscape(resources)) { - layoutManager = GridLayoutManager(requireContext(), 2).also { - it.spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() { - override fun getSpanSize(position: Int): Int { - return if (position == 0) 2 else 1 - } - } - } - } - } + setupRecycler(detailAdapter) // --- VIEWMODEL SETUP --- @@ -113,9 +98,9 @@ class ArtistDetailFragment : DetailFragment() { detailAdapter.submitList(data) } - playbackModel.navToItem.observe(viewLifecycleOwner) { + detailModel.navToItem.observe(viewLifecycleOwner) { if (it != null && it is Artist) { - playbackModel.doneWithNavToItem() + detailModel.doneWithNavToItem() } } diff --git a/app/src/main/java/org/oxycblt/auxio/detail/DetailFragment.kt b/app/src/main/java/org/oxycblt/auxio/detail/DetailFragment.kt index b1bdd0478..7ed402728 100644 --- a/app/src/main/java/org/oxycblt/auxio/detail/DetailFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/detail/DetailFragment.kt @@ -7,8 +7,13 @@ import androidx.annotation.MenuRes import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels import androidx.navigation.fragment.findNavController +import androidx.recyclerview.widget.GridLayoutManager +import androidx.recyclerview.widget.ListAdapter +import androidx.recyclerview.widget.RecyclerView import org.oxycblt.auxio.databinding.FragmentDetailBinding +import org.oxycblt.auxio.music.BaseModel import org.oxycblt.auxio.playback.PlaybackViewModel +import org.oxycblt.auxio.ui.isLandscape import org.oxycblt.auxio.ui.memberBinding /** @@ -57,6 +62,26 @@ abstract class DetailFragment : Fragment() { } } + /** + * Shortcut method for recyclerview setup + */ + protected fun setupRecycler(detailAdapter: ListAdapter) { + binding.detailRecycler.apply { + adapter = detailAdapter + setHasFixedSize(true) + + if (isLandscape(resources)) { + layoutManager = GridLayoutManager(requireContext(), 2).also { + it.spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() { + override fun getSpanSize(position: Int): Int { + return if (position == 0) 2 else 1 + } + } + } + } + } + } + // Override the back button so that going back will only exit the detail fragments instead of // the entire app. private val callback = object : OnBackPressedCallback(false) { diff --git a/app/src/main/java/org/oxycblt/auxio/detail/DetailViewModel.kt b/app/src/main/java/org/oxycblt/auxio/detail/DetailViewModel.kt index bd4d5bfb5..e2ce1a406 100644 --- a/app/src/main/java/org/oxycblt/auxio/detail/DetailViewModel.kt +++ b/app/src/main/java/org/oxycblt/auxio/detail/DetailViewModel.kt @@ -5,6 +5,7 @@ import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import org.oxycblt.auxio.music.Album import org.oxycblt.auxio.music.Artist +import org.oxycblt.auxio.music.BaseModel import org.oxycblt.auxio.music.Genre import org.oxycblt.auxio.recycler.SortMode @@ -26,18 +27,25 @@ class DetailViewModel : ViewModel() { val albumSortMode: LiveData get() = mAlbumSortMode // Current music models being shown - private val mCurrentGenre = MutableLiveData() - val currentGenre: LiveData get() = mCurrentGenre + private val mCurrentGenre = MutableLiveData() + val currentGenre: LiveData get() = mCurrentGenre - private val mCurrentArtist = MutableLiveData() - val currentArtist: LiveData get() = mCurrentArtist + private val mCurrentArtist = MutableLiveData() + val currentArtist: LiveData get() = mCurrentArtist - private val mCurrentAlbum = MutableLiveData() - val currentAlbum: LiveData get() = mCurrentAlbum + private val mCurrentAlbum = MutableLiveData() + val currentAlbum: LiveData get() = mCurrentAlbum + + // Navigation flags + private val mNavToItem = MutableLiveData() + val navToItem: LiveData get() = mNavToItem private val mNavToParent = MutableLiveData() val navToParent: LiveData get() = mNavToParent + private val mNavToChild = MutableLiveData() + val navToChild: LiveData get() = mNavToChild + /** * Update the current navigation status * @param value Whether the current [DetailFragment] is navigating or not. @@ -96,8 +104,18 @@ class DetailViewModel : ViewModel() { mCurrentAlbum.value = album } + /** Navigate to an item, whether a song/album/artist */ + fun navToItem(item: BaseModel) { + mNavToItem.value = item + } + + /** Mark that the navigation process is done. */ + fun doneWithNavToItem() { + mNavToItem.value = null + } + /** Mark that parent navigation should occur */ - fun doNavToParent() { + fun navToParent() { mNavToParent.value = true } @@ -105,4 +123,13 @@ class DetailViewModel : ViewModel() { fun doneWithNavToParent() { mNavToParent.value = false } + + /** Navigate to some child item (Primarily used by GenreDetailFragment) */ + fun navToChild(child: BaseModel) { + mNavToChild.value = child + } + + fun doneWithNavToChild() { + mNavToChild.value = null + } } diff --git a/app/src/main/java/org/oxycblt/auxio/detail/GenreDetailFragment.kt b/app/src/main/java/org/oxycblt/auxio/detail/GenreDetailFragment.kt index 7debf9571..314743b8e 100644 --- a/app/src/main/java/org/oxycblt/auxio/detail/GenreDetailFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/detail/GenreDetailFragment.kt @@ -5,15 +5,16 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.appcompat.widget.PopupMenu +import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.navArgs -import androidx.recyclerview.widget.GridLayoutManager import org.oxycblt.auxio.R import org.oxycblt.auxio.detail.adapters.GenreDetailAdapter import org.oxycblt.auxio.logD +import org.oxycblt.auxio.music.Album +import org.oxycblt.auxio.music.Artist import org.oxycblt.auxio.music.BaseModel import org.oxycblt.auxio.music.MusicStore import org.oxycblt.auxio.playback.state.PlaybackMode -import org.oxycblt.auxio.ui.isLandscape import org.oxycblt.auxio.ui.setupGenreSongActions /** @@ -47,7 +48,7 @@ class GenreDetailFragment : DetailFragment() { }, doOnLongClick = { data, view -> PopupMenu(requireContext(), view).setupGenreSongActions( - requireContext(), data, playbackModel + requireContext(), data, playbackModel, detailModel ) } ) @@ -71,20 +72,7 @@ class GenreDetailFragment : DetailFragment() { } } - binding.detailRecycler.apply { - adapter = detailAdapter - setHasFixedSize(true) - - if (isLandscape(resources)) { - layoutManager = GridLayoutManager(requireContext(), 2).also { - it.spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() { - override fun getSpanSize(position: Int): Int { - return if (position == 0) 2 else 1 - } - } - } - } - } + setupRecycler(detailAdapter) // --- VIEWMODEL SETUP --- @@ -98,6 +86,22 @@ class GenreDetailFragment : DetailFragment() { detailAdapter.submitList(data) } + detailModel.navToChild.observe(viewLifecycleOwner) { + if (it != null) { + if (it is Artist) { + findNavController().navigate( + GenreDetailFragmentDirections.actionGoArtist(it.id) + ) + } else if (it is Album) { + findNavController().navigate( + GenreDetailFragmentDirections.actionGoAlbum(it.id, false) + ) + } + + detailModel.doneWithNavToChild() + } + } + logD("Fragment created.") return binding.root diff --git a/app/src/main/java/org/oxycblt/auxio/library/LibraryFragment.kt b/app/src/main/java/org/oxycblt/auxio/library/LibraryFragment.kt index 2b13b7d39..2f55ff2f0 100644 --- a/app/src/main/java/org/oxycblt/auxio/library/LibraryFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/library/LibraryFragment.kt @@ -17,6 +17,7 @@ import androidx.transition.Fade import androidx.transition.TransitionManager import org.oxycblt.auxio.R import org.oxycblt.auxio.databinding.FragmentLibraryBinding +import org.oxycblt.auxio.detail.DetailViewModel import org.oxycblt.auxio.library.adapters.LibraryAdapter import org.oxycblt.auxio.library.adapters.SearchAdapter import org.oxycblt.auxio.logD @@ -46,6 +47,7 @@ import org.oxycblt.auxio.ui.setupSongActions class LibraryFragment : Fragment(), SearchView.OnQueryTextListener { private val libraryModel: LibraryViewModel by activityViewModels() + private val detailModel: DetailViewModel by activityViewModels() private val playbackModel: PlaybackViewModel by activityViewModels() override fun onCreateView( @@ -167,15 +169,11 @@ class LibraryFragment : Fragment(), SearchView.OnQueryTextListener { } } - playbackModel.navToItem.observe(viewLifecycleOwner) { + detailModel.navToItem.observe(viewLifecycleOwner) { if (it != null) { libraryModel.updateNavigationStatus(false) - if (it is Song || it is Album) { - onItemSelection(playbackModel.song.value!!.album) - } else { - onItemSelection(it) - } + onItemSelection(it) } } @@ -207,7 +205,7 @@ class LibraryFragment : Fragment(), SearchView.OnQueryTextListener { val menu = PopupMenu(requireContext(), view) when (data) { - is Song -> menu.setupSongActions(requireContext(), data, playbackModel) + is Song -> menu.setupSongActions(requireContext(), data, playbackModel, detailModel) is Album -> menu.setupAlbumActions(requireContext(), data, playbackModel) is Artist -> menu.setupArtistActions(data, playbackModel) is Genre -> menu.setupGenreActions(data, playbackModel) @@ -240,7 +238,7 @@ class LibraryFragment : Fragment(), SearchView.OnQueryTextListener { when (baseModel) { is Genre -> LibraryFragmentDirections.actionShowGenre(baseModel.id) is Artist -> LibraryFragmentDirections.actionShowArtist(baseModel.id) - is Album -> LibraryFragmentDirections.actionShowAlbum(baseModel.id, true) + is Album -> LibraryFragmentDirections.actionShowAlbum(baseModel.id, false) // If given model wasn't valid, then reset the navigation status // and abort the navigation. diff --git a/app/src/main/java/org/oxycblt/auxio/playback/CompactPlaybackFragment.kt b/app/src/main/java/org/oxycblt/auxio/playback/CompactPlaybackFragment.kt index 4f7dddb5b..46a002a4a 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/CompactPlaybackFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/CompactPlaybackFragment.kt @@ -12,6 +12,7 @@ import androidx.navigation.fragment.findNavController import org.oxycblt.auxio.MainFragmentDirections import org.oxycblt.auxio.R import org.oxycblt.auxio.databinding.FragmentCompactPlaybackBinding +import org.oxycblt.auxio.detail.DetailViewModel import org.oxycblt.auxio.logD import org.oxycblt.auxio.music.MusicStore import org.oxycblt.auxio.ui.isLandscape @@ -26,6 +27,7 @@ import org.oxycblt.auxio.ui.memberBinding */ class CompactPlaybackFragment : Fragment() { private val playbackModel: PlaybackViewModel by activityViewModels() + private val detailModel: DetailViewModel by activityViewModels() private val binding: FragmentCompactPlaybackBinding by memberBinding( FragmentCompactPlaybackBinding::inflate ) @@ -57,7 +59,7 @@ class CompactPlaybackFragment : Fragment() { } setOnLongClickListener { - playbackModel.navToItem(playbackModel.song.value!!) + detailModel.navToItem(playbackModel.song.value!!) true } } diff --git a/app/src/main/java/org/oxycblt/auxio/playback/NotificationUtils.kt b/app/src/main/java/org/oxycblt/auxio/playback/NotificationUtils.kt index ba8c5908e..992591ed0 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/NotificationUtils.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/NotificationUtils.kt @@ -24,14 +24,13 @@ object NotificationUtils { const val NOTIFICATION_ID = 0xA0A0 const val REQUEST_CODE = 0xA0C0 - // Strings for each action, version name is applied so that broadcasts will only reach - // one instance of Auxio. - const val ACTION_LOOP = "ACTION_AUXIO_LOOP_" + BuildConfig.VERSION_NAME - const val ACTION_SHUFFLE = "ACTION_AUXIO_SHUFFLE_" + BuildConfig.VERSION_NAME - const val ACTION_SKIP_PREV = "ACTION_AUXIO_SKIP_PREV_" + BuildConfig.VERSION_NAME - const val ACTION_PLAY_PAUSE = "ACTION_AUXIO_PLAY_PAUSE_" + BuildConfig.VERSION_NAME - const val ACTION_SKIP_NEXT = "ACTION_AUXIO_SKIP_NEXT_" + BuildConfig.VERSION_NAME - const val ACTION_EXIT = "ACTION_AUXIO_EXIT_" + BuildConfig.VERSION_NAME + // The build type is applied to each action so that broadcasts will not conflict with debug/release builds. + const val ACTION_LOOP = "ACTION_AUXIO_LOOP_" + BuildConfig.BUILD_TYPE + const val ACTION_SHUFFLE = "ACTION_AUXIO_SHUFFLE_" + BuildConfig.BUILD_TYPE + const val ACTION_SKIP_PREV = "ACTION_AUXIO_SKIP_PREV_" + BuildConfig.BUILD_TYPE + const val ACTION_PLAY_PAUSE = "ACTION_AUXIO_PLAY_PAUSE_" + BuildConfig.BUILD_TYPE + const val ACTION_SKIP_NEXT = "ACTION_AUXIO_SKIP_NEXT_" + BuildConfig.BUILD_TYPE + const val ACTION_EXIT = "ACTION_AUXIO_EXIT_" + BuildConfig.BUILD_TYPE } /** @@ -44,7 +43,7 @@ fun NotificationManager.createMediaNotification( context: Context, mediaSession: MediaSessionCompat ): NotificationCompat.Builder { - // Create a notification channel if required + // Create a notification channel if requireds if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { val channel = NotificationChannel( NotificationUtils.CHANNEL_ID, diff --git a/app/src/main/java/org/oxycblt/auxio/playback/PlaybackFragment.kt b/app/src/main/java/org/oxycblt/auxio/playback/PlaybackFragment.kt index fe433bccc..94162df4f 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/PlaybackFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/PlaybackFragment.kt @@ -14,6 +14,7 @@ import androidx.fragment.app.activityViewModels import androidx.navigation.fragment.findNavController import org.oxycblt.auxio.R import org.oxycblt.auxio.databinding.FragmentPlaybackBinding +import org.oxycblt.auxio.detail.DetailViewModel import org.oxycblt.auxio.logD import org.oxycblt.auxio.playback.state.LoopMode import org.oxycblt.auxio.ui.accent @@ -27,12 +28,13 @@ import org.oxycblt.auxio.ui.toColor */ class PlaybackFragment : Fragment(), SeekBar.OnSeekBarChangeListener { private val playbackModel: PlaybackViewModel by activityViewModels() + private val detailModel: DetailViewModel by activityViewModels() private val binding: FragmentPlaybackBinding by memberBinding(FragmentPlaybackBinding::inflate) { // Marquee must be disabled on destroy to prevent memory leaks binding.playbackSong.isSelected = false } - // Colors/Icons + // Colors private val accentColor: ColorStateList by lazy { ColorStateList.valueOf(accent.first.toColor(requireContext())) } @@ -64,6 +66,7 @@ class PlaybackFragment : Fragment(), SeekBar.OnSeekBarChangeListener { binding.lifecycleOwner = viewLifecycleOwner binding.playbackModel = playbackModel + binding.detailModel = detailModel binding.song = playbackModel.song.value!! binding.playbackToolbar.apply { @@ -84,7 +87,6 @@ class PlaybackFragment : Fragment(), SeekBar.OnSeekBarChangeListener { // Make marquee of song title work binding.playbackSong.isSelected = true - binding.playbackSeekBar.setOnSeekBarChangeListener(this) // --- VIEWMODEL SETUP -- @@ -171,7 +173,7 @@ class PlaybackFragment : Fragment(), SeekBar.OnSeekBarChangeListener { } } - playbackModel.navToItem.observe(viewLifecycleOwner) { + detailModel.navToItem.observe(viewLifecycleOwner) { if (it != null) { findNavController().navigateUp() } diff --git a/app/src/main/java/org/oxycblt/auxio/playback/PlaybackViewModel.kt b/app/src/main/java/org/oxycblt/auxio/playback/PlaybackViewModel.kt index d73ac589f..4eb9d4f56 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/PlaybackViewModel.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/PlaybackViewModel.kt @@ -44,7 +44,6 @@ class PlaybackViewModel : ViewModel(), PlaybackStateManager.Callback { // Other private val mIsSeeking = MutableLiveData(false) - private val mNavToItem = MutableLiveData() private var mCanAnimate = false /** The current song. */ @@ -66,7 +65,6 @@ class PlaybackViewModel : ViewModel(), PlaybackStateManager.Callback { /** The current repeat mode, see [LoopMode] for more information */ val loopMode: LiveData get() = mLoopMode val isSeeking: LiveData get() = mIsSeeking - val navToItem: LiveData get() = mNavToItem val canAnimate: Boolean get() = mCanAnimate /** The position as a duration string. */ @@ -335,16 +333,6 @@ class PlaybackViewModel : ViewModel(), PlaybackStateManager.Callback { mIsSeeking.value = value } - /** Navigate to an item, whether a song/album/artist */ - fun navToItem(item: BaseModel) { - mNavToItem.value = item - } - - /** Mark that the navigation process is done. */ - fun doneWithNavToItem() { - mNavToItem.value = null - } - /** Enable animation on CompactPlaybackFragment */ fun enableAnimation() { mCanAnimate = true diff --git a/app/src/main/java/org/oxycblt/auxio/playback/state/PlaybackStateManager.kt b/app/src/main/java/org/oxycblt/auxio/playback/state/PlaybackStateManager.kt index 7727dd3d5..353a581fb 100644 --- a/app/src/main/java/org/oxycblt/auxio/playback/state/PlaybackStateManager.kt +++ b/app/src/main/java/org/oxycblt/auxio/playback/state/PlaybackStateManager.kt @@ -196,7 +196,7 @@ class PlaybackStateManager private constructor() { fun playParentModel(baseModel: BaseModel, shuffled: Boolean) { if (baseModel is Song || baseModel is Header) { // This should never occur. - logE("playParentModel does not support ${baseModel::class.simpleName}.") + logE("playParentModel is not meant to play ${baseModel::class.simpleName}.") return } @@ -575,7 +575,7 @@ class PlaybackStateManager private constructor() { /** * Set the [LoopMode] - * @param value The [LoopMode] to be used + * @param mode The [LoopMode] to be used */ fun setLoopMode(mode: LoopMode) { mLoopMode = mode diff --git a/app/src/main/java/org/oxycblt/auxio/recycler/DisplayMode.kt b/app/src/main/java/org/oxycblt/auxio/recycler/DisplayMode.kt index 0c3c1546d..889bfd8de 100644 --- a/app/src/main/java/org/oxycblt/auxio/recycler/DisplayMode.kt +++ b/app/src/main/java/org/oxycblt/auxio/recycler/DisplayMode.kt @@ -11,7 +11,6 @@ enum class DisplayMode(@DrawableRes val iconRes: Int) { SHOW_GENRES(R.drawable.ic_genre), SHOW_ARTISTS(R.drawable.ic_artist), SHOW_ALBUMS(R.drawable.ic_album), - SHOW_SONGS(R.drawable.ic_song); /** * Make a slice of all the values that this DisplayMode covers. diff --git a/app/src/main/java/org/oxycblt/auxio/songs/SongsFragment.kt b/app/src/main/java/org/oxycblt/auxio/songs/SongsFragment.kt index 4d87b627f..7a87eb415 100644 --- a/app/src/main/java/org/oxycblt/auxio/songs/SongsFragment.kt +++ b/app/src/main/java/org/oxycblt/auxio/songs/SongsFragment.kt @@ -16,6 +16,7 @@ import com.reddit.indicatorfastscroll.FastScrollItemIndicator import com.reddit.indicatorfastscroll.FastScrollerView import org.oxycblt.auxio.R import org.oxycblt.auxio.databinding.FragmentSongsBinding +import org.oxycblt.auxio.detail.DetailViewModel import org.oxycblt.auxio.logD import org.oxycblt.auxio.music.MusicStore import org.oxycblt.auxio.playback.PlaybackViewModel @@ -33,6 +34,7 @@ import kotlin.math.ceil */ class SongsFragment : Fragment() { private val playbackModel: PlaybackViewModel by activityViewModels() + private val detailModel: DetailViewModel by activityViewModels() // Lazy init the text size so that it doesn't have to be calculated every time. private val indicatorTextSize: Float by lazy { @@ -57,7 +59,7 @@ class SongsFragment : Fragment() { doOnClick = { playbackModel.playSong(it, settingsManager.songPlaybackMode) }, doOnLongClick = { data, view -> PopupMenu(requireContext(), view).setupSongActions( - requireContext(), data, playbackModel + requireContext(), data, playbackModel, detailModel ) } ) diff --git a/app/src/main/java/org/oxycblt/auxio/ui/InterfaceUtils.kt b/app/src/main/java/org/oxycblt/auxio/ui/InterfaceUtils.kt index c09c34fe1..c9ece3240 100644 --- a/app/src/main/java/org/oxycblt/auxio/ui/InterfaceUtils.kt +++ b/app/src/main/java/org/oxycblt/auxio/ui/InterfaceUtils.kt @@ -22,7 +22,6 @@ import org.oxycblt.auxio.music.Genre import org.oxycblt.auxio.music.Song import org.oxycblt.auxio.playback.PlaybackViewModel import org.oxycblt.auxio.playback.state.PlaybackMode -import org.oxycblt.auxio.settings.SettingsManager /** * Apply a text color to a [MenuItem] @@ -79,10 +78,14 @@ fun Spanned.render(): Spanned { * @param context [Context] required * @param song [Song] The menu should correspond to * @param playbackModel The [PlaybackViewModel] the menu should dispatch actions to. + * @param detailModel The [DetailViewModel] the menu should dispatch actions to. */ -fun PopupMenu.setupSongActions(context: Context, song: Song, playbackModel: PlaybackViewModel) { - inflateAndShow(R.menu.menu_song_actions) - +fun PopupMenu.setupSongActions( + context: Context, + song: Song, + playbackModel: PlaybackViewModel, + detailModel: DetailViewModel +) { setOnMenuItemClickListener { when (it.itemId) { R.id.action_queue_add -> { @@ -91,18 +94,13 @@ fun PopupMenu.setupSongActions(context: Context, song: Song, playbackModel: Play true } - R.id.action_play_artist -> { - playbackModel.playSong(song, PlaybackMode.IN_ARTIST) + R.id.action_go_artist -> { + detailModel.navToItem(song.album.artist) true } - R.id.action_play_album -> { - playbackModel.playSong(song, PlaybackMode.IN_ALBUM) - true - } - - R.id.action_play_all_songs -> { - playbackModel.playSong(song, PlaybackMode.ALL_SONGS) + R.id.action_go_album -> { + detailModel.navToItem(song.album) true } @@ -110,18 +108,7 @@ fun PopupMenu.setupSongActions(context: Context, song: Song, playbackModel: Play } } - val settingsManager = SettingsManager.getInstance() - - // Find the action that is redundant from the menu and hide it. - val idToRemove = when (settingsManager.songPlaybackMode) { - PlaybackMode.ALL_SONGS -> R.id.action_play_all_songs - PlaybackMode.IN_ARTIST -> R.id.action_play_artist - PlaybackMode.IN_ALBUM -> R.id.action_play_album - - else -> -1 - } - - menu.findItem(idToRemove)?.isVisible = false + inflateAndShow(R.menu.menu_song_actions) } /** @@ -148,7 +135,7 @@ fun PopupMenu.setupAlbumSongActions( } R.id.action_go_artist -> { - detailModel.doNavToParent() + detailModel.navToParent() true } @@ -243,12 +230,19 @@ fun PopupMenu.setupGenreActions(genre: Genre, playbackModel: PlaybackViewModel) } /** - * Show actions for a song in a genre. + * Show actions for a [Genre] song. Mostly identical to [setupSongActions] aside from a different + * flag being used for navigation. * @param context [Context] required - * @param song [Song] the menu should correspond to - * @param playbackModel [PlaybackViewModel] to dispatch actions to + * @param song [Song] The menu should correspond to + * @param playbackModel The [PlaybackViewModel] the menu should dispatch actions to. + * @param detailModel The [DetailViewModel] the menu should dispatch actions to. */ -fun PopupMenu.setupGenreSongActions(context: Context, song: Song, playbackModel: PlaybackViewModel) { +fun PopupMenu.setupGenreSongActions( + context: Context, + song: Song, + playbackModel: PlaybackViewModel, + detailModel: DetailViewModel +) { setOnMenuItemClickListener { when (it.itemId) { R.id.action_queue_add -> { @@ -257,20 +251,21 @@ fun PopupMenu.setupGenreSongActions(context: Context, song: Song, playbackModel: true } - R.id.action_play_artist -> { - playbackModel.playSong(song, PlaybackMode.IN_ARTIST) + R.id.action_go_artist -> { + detailModel.navToChild(song.album.artist) true } - R.id.action_play_album -> { - playbackModel.playSong(song, PlaybackMode.IN_ALBUM) + R.id.action_go_album -> { + detailModel.navToChild(song.album) true } else -> false } } - inflateAndShow(R.menu.menu_genre_song_actions) + + inflateAndShow(R.menu.menu_song_actions) } /** diff --git a/app/src/main/res/drawable/ic_libraries.xml b/app/src/main/res/drawable/ic_libraries.xml deleted file mode 100644 index b8d3bafd1..000000000 --- a/app/src/main/res/drawable/ic_libraries.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - \ No newline at end of file diff --git a/app/src/main/res/layout-land/fragment_playback.xml b/app/src/main/res/layout-land/fragment_playback.xml index e14f0e309..39f5a5d96 100644 --- a/app/src/main/res/layout-land/fragment_playback.xml +++ b/app/src/main/res/layout-land/fragment_playback.xml @@ -13,6 +13,10 @@ + + @@ -70,7 +74,7 @@ android:ellipsize="marquee" android:fontFamily="@font/inter_semibold" android:marqueeRepeatLimit="marquee_forever" - android:onClick="@{() -> playbackModel.navToItem(playbackModel.song)}" + android:onClick="@{() -> detailModel.navToItem(playbackModel.song)}" android:singleLine="true" android:text="@{song.name}" android:textAppearance="@style/TextAppearance.MaterialComponents.Headline6" @@ -85,7 +89,7 @@ android:layout_marginStart="@dimen/margin_mid_large" android:layout_marginEnd="@dimen/margin_mid_large" android:ellipsize="end" - android:onClick="@{() -> playbackModel.navToItem(playbackModel.song.album.artist)}" + android:onClick="@{() -> detailModel.navToItem(playbackModel.song.album.artist)}" android:singleLine="true" android:text="@{song.album.artist.name}" android:textAppearance="@style/TextAppearance.MaterialComponents.Subtitle1" @@ -103,7 +107,7 @@ android:layout_marginStart="@dimen/margin_mid_large" android:layout_marginEnd="@dimen/margin_mid_large" android:ellipsize="end" - android:onClick="@{() -> playbackModel.navToItem(playbackModel.song.album)}" + android:onClick="@{() -> detailModel.navToItem(playbackModel.song.album)}" android:singleLine="true" android:text="@{song.album.name}" android:textAppearance="@style/TextAppearance.MaterialComponents.Subtitle1" diff --git a/app/src/main/res/layout-land/item_album_header.xml b/app/src/main/res/layout-land/item_album_header.xml index a736160bd..5844dfe25 100644 --- a/app/src/main/res/layout-land/item_album_header.xml +++ b/app/src/main/res/layout-land/item_album_header.xml @@ -55,7 +55,7 @@ android:background="@drawable/ui_ripple" android:clickable="true" android:focusable="true" - android:onClick="@{() -> detailModel.doNavToParent()}" + android:onClick="@{() -> detailModel.navToParent()}" android:text="@{album.artist.name}" android:textAppearance="?android:attr/textAppearanceListItem" android:textColor="?android:attr/textColorSecondary" diff --git a/app/src/main/res/layout/fragment_playback.xml b/app/src/main/res/layout/fragment_playback.xml index 83069b46a..4ba50dcc9 100644 --- a/app/src/main/res/layout/fragment_playback.xml +++ b/app/src/main/res/layout/fragment_playback.xml @@ -13,6 +13,10 @@ + + @@ -59,7 +63,7 @@ android:ellipsize="marquee" android:fontFamily="@font/inter_semibold" android:marqueeRepeatLimit="marquee_forever" - android:onClick="@{() -> playbackModel.navToItem(playbackModel.song)}" + android:onClick="@{() -> detailModel.navToItem(playbackModel.song)}" android:singleLine="true" android:text="@{song.name}" android:textAppearance="@style/TextAppearance.MaterialComponents.Headline6" @@ -76,7 +80,7 @@ android:layout_marginStart="@dimen/margin_mid_large" android:layout_marginEnd="@dimen/margin_mid_large" android:ellipsize="end" - android:onClick="@{() -> playbackModel.navToItem(playbackModel.song.album.artist)}" + android:onClick="@{() -> detailModel.navToItem(playbackModel.song.album.artist)}" android:singleLine="true" android:text="@{song.album.artist.name}" android:textAppearance="@style/TextAppearance.MaterialComponents.Subtitle1" @@ -94,7 +98,7 @@ android:layout_marginEnd="@dimen/margin_mid_large" android:layout_marginBottom="16dp" android:ellipsize="end" - android:onClick="@{() -> playbackModel.navToItem(playbackModel.song.album)}" + android:onClick="@{() -> detailModel.navToItem(playbackModel.song.album)}" android:singleLine="true" android:text="@{song.album.name}" android:textAppearance="@style/TextAppearance.MaterialComponents.Subtitle1" diff --git a/app/src/main/res/layout/item_album_header.xml b/app/src/main/res/layout/item_album_header.xml index 80a62b5e6..56040ab62 100644 --- a/app/src/main/res/layout/item_album_header.xml +++ b/app/src/main/res/layout/item_album_header.xml @@ -54,7 +54,7 @@ android:background="@drawable/ui_ripple" android:clickable="true" android:focusable="true" - android:onClick="@{() -> detailModel.doNavToParent()}" + android:onClick="@{() -> detailModel.navToParent()}" android:text="@{album.artist.name}" android:textAppearance="?android:attr/textAppearanceListItem" android:textColor="?android:attr/textColorSecondary" diff --git a/app/src/main/res/layout/item_genre_artist.xml b/app/src/main/res/layout/item_genre_artist.xml deleted file mode 100644 index 3a7f65a46..000000000 --- a/app/src/main/res/layout/item_genre_artist.xml +++ /dev/null @@ -1,49 +0,0 @@ - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/menu/menu_genre_song_actions.xml b/app/src/main/res/menu/menu_genre_song_actions.xml deleted file mode 100644 index 961d7635d..000000000 --- a/app/src/main/res/menu/menu_genre_song_actions.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/app/src/main/res/menu/menu_song_actions.xml b/app/src/main/res/menu/menu_song_actions.xml index 47f54a960..cf8bdb5e3 100644 --- a/app/src/main/res/menu/menu_song_actions.xml +++ b/app/src/main/res/menu/menu_song_actions.xml @@ -5,15 +5,11 @@ android:icon="@drawable/ic_queue_add" android:title="@string/label_queue_add" /> - + android:title="@string/label_go_artist" /> + android:title="@string/label_go_album" /> \ No newline at end of file diff --git a/app/src/main/res/navigation/nav_explore.xml b/app/src/main/res/navigation/nav_explore.xml index 16a1b996a..8231569f4 100644 --- a/app/src/main/res/navigation/nav_explore.xml +++ b/app/src/main/res/navigation/nav_explore.xml @@ -57,7 +57,7 @@ android:name="albumId" app:argType="long" /> + - + - - - 6dp - \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 68d1253df..288b2c368 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -19,17 +19,17 @@ All Songs Songs - Now Playing Play Shuffle Play from all songs - Play from album Play from artist + Play from album + Go to artist + Go to album Play albums - Shuffle albums Queue Add to queue @@ -57,8 +57,6 @@ Accent Unknown Accent - Edge-To-Edge - Enable edge-to-edge Display Library Items @@ -127,7 +125,6 @@ Turn shuffle off Change Repeat Mode Auxio icon - Code Unknown Genre @@ -172,8 +169,4 @@ %s Albums - - %s Artist - %s Artists - \ No newline at end of file