Refactor menus

Heavily refactor how certain menus are used when it comes to songs.
This commit is contained in:
OxygenCobalt 2020-12-29 14:45:55 -07:00
parent 98320fcd8d
commit 8fe0734ca1
No known key found for this signature in database
GPG key ID: 37DBE3621FE9AD47
27 changed files with 190 additions and 263 deletions

View file

@ -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"

View file

@ -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) {

View file

@ -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()
}
}
}

View file

@ -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()
}
}

View file

@ -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<BaseModel, RecyclerView.ViewHolder>) {
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) {

View file

@ -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<SortMode> get() = mAlbumSortMode
// Current music models being shown
private val mCurrentGenre = MutableLiveData<Genre>()
val currentGenre: LiveData<Genre> get() = mCurrentGenre
private val mCurrentGenre = MutableLiveData<Genre?>()
val currentGenre: LiveData<Genre?> get() = mCurrentGenre
private val mCurrentArtist = MutableLiveData<Artist>()
val currentArtist: LiveData<Artist> get() = mCurrentArtist
private val mCurrentArtist = MutableLiveData<Artist?>()
val currentArtist: LiveData<Artist?> get() = mCurrentArtist
private val mCurrentAlbum = MutableLiveData<Album>()
val currentAlbum: LiveData<Album> get() = mCurrentAlbum
private val mCurrentAlbum = MutableLiveData<Album?>()
val currentAlbum: LiveData<Album?> get() = mCurrentAlbum
// Navigation flags
private val mNavToItem = MutableLiveData<BaseModel?>()
val navToItem: LiveData<BaseModel?> get() = mNavToItem
private val mNavToParent = MutableLiveData<Boolean>()
val navToParent: LiveData<Boolean> get() = mNavToParent
private val mNavToChild = MutableLiveData<BaseModel?>()
val navToChild: LiveData<BaseModel?> 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
}
}

View file

@ -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

View file

@ -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.

View file

@ -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
}
}

View file

@ -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,

View file

@ -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()
}

View file

@ -44,7 +44,6 @@ class PlaybackViewModel : ViewModel(), PlaybackStateManager.Callback {
// Other
private val mIsSeeking = MutableLiveData(false)
private val mNavToItem = MutableLiveData<BaseModel?>()
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<LoopMode> get() = mLoopMode
val isSeeking: LiveData<Boolean> get() = mIsSeeking
val navToItem: LiveData<BaseModel?> 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

View file

@ -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

View file

@ -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.

View file

@ -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
)
}
)

View file

@ -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)
}
/**

View file

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="?attr/colorControlNormal"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M4,6L2,6v16h16v-2L4,20L4,6zM22,2L6,2v16h16L22,2zM19,11L9,11L9,9h10v2zM15,15L9,15v-2h6v2zM19,7L9,7L9,5h10v2z" />
</vector>

View file

@ -13,6 +13,10 @@
<variable
name="playbackModel"
type="org.oxycblt.auxio.playback.PlaybackViewModel" />
<variable
name="detailModel"
type="org.oxycblt.auxio.detail.DetailViewModel" />
</data>
@ -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"

View file

@ -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"

View file

@ -13,6 +13,10 @@
<variable
name="playbackModel"
type="org.oxycblt.auxio.playback.PlaybackViewModel" />
<variable
name="detailModel"
type="org.oxycblt.auxio.detail.DetailViewModel" />
</data>
@ -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"

View file

@ -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"

View file

@ -1,49 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
tools:context=".detail.adapters.DetailArtistAdapter.ViewHolder">
<data>
<variable
name="artist"
type="org.oxycblt.auxio.music.Artist" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout style="@style/ItemSurroundings">
<ImageView
android:id="@+id/artist_image"
android:layout_width="@dimen/size_cover_large"
android:layout_height="@dimen/size_cover_large"
android:contentDescription="@{@string/description_artist_image(artist.name)}"
app:artistImage="@{artist}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:src="@drawable/ic_artist" />
<TextView
android:id="@+id/artist_name"
style="@style/ItemText.Primary"
android:text="@{artist.name}"
app:layout_constraintBottom_toTopOf="@+id/artist_counts"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/artist_image"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_chainStyle="packed"
tools:text="Artist Name" />
<TextView
android:id="@+id/artist_counts"
style='@style/ItemText.Secondary'
app:artistCounts="@{artist}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/artist_image"
app:layout_constraintTop_toBottomOf="@+id/artist_name"
tools:text="2 Albums, 20 Songs" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

View file

@ -1,15 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/action_queue_add"
android:icon="@drawable/ic_queue_add"
android:title="@string/label_queue_add" />
<item
android:id="@+id/action_play_artist"
android:icon="@drawable/ic_artist"
android:title="@string/label_play_artist" />
<item
android:id="@+id/action_play_album"
android:icon="@drawable/ic_album"
android:title="@string/label_play_album" />
</menu>

View file

@ -5,15 +5,11 @@
android:icon="@drawable/ic_queue_add"
android:title="@string/label_queue_add" />
<item
android:id="@+id/action_play_all_songs"
android:icon="@drawable/ic_song"
android:title="@string/label_play_all_songs" />
<item
android:id="@+id/action_play_artist"
android:id="@+id/action_go_artist"
android:icon="@drawable/ic_artist"
android:title="@string/label_play_artist" />
android:title="@string/label_go_artist" />
<item
android:id="@+id/action_play_album"
android:id="@+id/action_go_album"
android:icon="@drawable/ic_album"
android:title="@string/label_play_album" />
android:title="@string/label_go_album" />
</menu>

View file

@ -57,7 +57,7 @@
android:name="albumId"
app:argType="long" />
<argument
android:name="enableParentNav"
android:name="fromArtist"
app:argType="boolean" />
<action
android:id="@+id/action_show_parent_artist"
@ -72,16 +72,23 @@
android:name="org.oxycblt.auxio.detail.GenreDetailFragment"
android:label="GenreDetailFragment"
tools:layout="@layout/fragment_detail">
<argument
android:name="genreId"
app:argType="long" />
<action
android:id="@+id/action_show_artist"
android:id="@+id/action_go_artist"
app:destination="@id/artist_detail_fragment"
app:enterAnim="@anim/nav_default_enter_anim"
app:exitAnim="@anim/nav_default_exit_anim"
app:popEnterAnim="@anim/nav_default_pop_enter_anim"
app:popExitAnim="@anim/nav_default_pop_exit_anim" />
<argument
android:name="genreId"
app:argType="long" />
<action
android:id="@+id/action_go_album"
app:destination="@id/album_detail_fragment"
app:enterAnim="@anim/nav_default_enter_anim"
app:exitAnim="@anim/nav_default_exit_anim"
app:popEnterAnim="@anim/nav_default_pop_enter_anim"
app:popExitAnim="@anim/nav_default_pop_exit_anim" />
</fragment>
<fragment
android:id="@+id/songs_fragment"

View file

@ -1,5 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- FIXME: Use custom drawable for < API 23 -->
<dimen name="height_compact_progress">6dp</dimen>
</resources>

View file

@ -19,17 +19,17 @@
<string name="label_all_songs">All Songs</string>
<string name="label_songs">Songs</string>
<string name="label_playback">Now Playing</string>
<string name="label_play">Play</string>
<string name="label_shuffle">Shuffle</string>
<string name="label_play_all_songs">Play from all songs</string>
<string name="label_play_album">Play from album</string>
<string name="label_play_artist">Play from artist</string>
<string name="label_play_album">Play from album</string>
<string name="label_go_artist">Go to artist</string>
<string name="label_go_album">Go to album</string>
<string name="label_play_albums">Play albums</string>
<string name="label_shuffle_albums">Shuffle albums</string>
<string name="label_queue">Queue</string>
<string name="label_queue_add">Add to queue</string>
@ -57,8 +57,6 @@
<string name="setting_accent">Accent</string>
<string name="setting_accent_unknown">Unknown Accent</string>
<string name="setting_edge">Edge-To-Edge</string>
<string name="setting_edge_desc">Enable edge-to-edge</string>
<string name="setting_display">Display</string>
<string name="setting_lib_display">Library Items</string>
@ -127,7 +125,6 @@
<string name="description_shuffle_off">Turn shuffle off</string>
<string name="description_change_loop">Change Repeat Mode</string>
<string name="description_auxio_icon">Auxio icon</string>
<string name="description_code">Code</string>
<!-- Placeholder Namespace | Placeholder values -->
<string name="placeholder_genre">Unknown Genre</string>
@ -172,8 +169,4 @@
<item quantity="other">%s Albums</item>
</plurals>
<plurals name="format_artist_count">
<item quantity="one">%s Artist</item>
<item quantity="other">%s Artists</item>
</plurals>
</resources>