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 { android {
compileSdkVersion 30 compileSdkVersion 30
buildToolsVersion "30.0.1" buildToolsVersion "30.0.3"
defaultConfig { defaultConfig {
applicationId "org.oxycblt.auxio" 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 (it != null) {
// If the current destination isn't even LibraryFragment, then navigate there first // If the current destination isn't even LibraryFragment, then navigate there first
if (binding.navBar.selectedItemId != R.id.library_fragment) { 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.appcompat.widget.PopupMenu
import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.findNavController
import androidx.navigation.fragment.navArgs import androidx.navigation.fragment.navArgs
import androidx.recyclerview.widget.GridLayoutManager
import org.oxycblt.auxio.R import org.oxycblt.auxio.R
import org.oxycblt.auxio.databinding.FragmentDetailBinding
import org.oxycblt.auxio.detail.adapters.AlbumDetailAdapter import org.oxycblt.auxio.detail.adapters.AlbumDetailAdapter
import org.oxycblt.auxio.logD import org.oxycblt.auxio.logD
import org.oxycblt.auxio.music.Album 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.playback.state.PlaybackMode
import org.oxycblt.auxio.recycler.CenterSmoothScroller import org.oxycblt.auxio.recycler.CenterSmoothScroller
import org.oxycblt.auxio.ui.createToast import org.oxycblt.auxio.ui.createToast
import org.oxycblt.auxio.ui.isLandscape
import org.oxycblt.auxio.ui.setupAlbumSongActions import org.oxycblt.auxio.ui.setupAlbumSongActions
/** /**
@ -88,27 +85,7 @@ class AlbumDetailFragment : DetailFragment() {
} }
} }
binding.detailRecycler.apply { setupRecycler(detailAdapter)
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)
}
}
// -- VIEWMODEL SETUP --- // -- VIEWMODEL SETUP ---
@ -126,7 +103,7 @@ class AlbumDetailFragment : DetailFragment() {
detailModel.navToParent.observe(viewLifecycleOwner) { detailModel.navToParent.observe(viewLifecycleOwner) {
if (it) { if (it) {
if (!args.enableParentNav) { if (args.fromArtist) {
findNavController().navigateUp() findNavController().navigateUp()
} else { } else {
findNavController().navigate( findNavController().navigate(
@ -135,17 +112,19 @@ class AlbumDetailFragment : DetailFragment() {
) )
) )
} }
detailModel.doneWithNavToParent()
} }
} }
playbackModel.navToItem.observe(viewLifecycleOwner) { detailModel.navToItem.observe(viewLifecycleOwner) {
if (it != null) { if (it != null) {
if (it is Song) { if (it is Song) {
scrollToPlayingItem(binding, smooth = true) scrollToPlayingItem()
} }
if (it is Album && it.id == detailModel.currentAlbum.value!!.id) { 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. * 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 // Calculate where the item for the currently played song is, and scroll to there
val pos = detailModel.albumSortMode.value!!.getSortedSongList( val pos = detailModel.albumSortMode.value!!.getSortedSongList(
detailModel.currentAlbum.value!!.songs detailModel.currentAlbum.value!!.songs
).indexOf(playbackModel.song.value).inc() ).indexOf(playbackModel.song.value)
if (pos != 0) { if (pos != -1) {
binding.detailRecycler.post { binding.detailRecycler.post {
binding.detailRecycler.layoutManager?.startSmoothScroll( 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.appcompat.widget.PopupMenu
import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.findNavController
import androidx.navigation.fragment.navArgs import androidx.navigation.fragment.navArgs
import androidx.recyclerview.widget.GridLayoutManager
import org.oxycblt.auxio.R import org.oxycblt.auxio.R
import org.oxycblt.auxio.detail.adapters.ArtistDetailAdapter import org.oxycblt.auxio.detail.adapters.ArtistDetailAdapter
import org.oxycblt.auxio.logD import org.oxycblt.auxio.logD
import org.oxycblt.auxio.music.Artist import org.oxycblt.auxio.music.Artist
import org.oxycblt.auxio.music.BaseModel import org.oxycblt.auxio.music.BaseModel
import org.oxycblt.auxio.music.MusicStore import org.oxycblt.auxio.music.MusicStore
import org.oxycblt.auxio.ui.isLandscape
import org.oxycblt.auxio.ui.setupAlbumActions import org.oxycblt.auxio.ui.setupAlbumActions
/** /**
@ -48,7 +46,7 @@ class ArtistDetailFragment : DetailFragment() {
detailModel.updateNavigationStatus(true) detailModel.updateNavigationStatus(true)
findNavController().navigate( findNavController().navigate(
ArtistDetailFragmentDirections.actionShowAlbum(it.id, false) ArtistDetailFragmentDirections.actionShowAlbum(it.id, true)
) )
} }
}, },
@ -86,20 +84,7 @@ class ArtistDetailFragment : DetailFragment() {
} }
} }
binding.detailRecycler.apply { setupRecycler(detailAdapter)
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
}
}
}
}
}
// --- VIEWMODEL SETUP --- // --- VIEWMODEL SETUP ---
@ -113,9 +98,9 @@ class ArtistDetailFragment : DetailFragment() {
detailAdapter.submitList(data) detailAdapter.submitList(data)
} }
playbackModel.navToItem.observe(viewLifecycleOwner) { detailModel.navToItem.observe(viewLifecycleOwner) {
if (it != null && it is Artist) { 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.Fragment
import androidx.fragment.app.activityViewModels import androidx.fragment.app.activityViewModels
import androidx.navigation.fragment.findNavController 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.databinding.FragmentDetailBinding
import org.oxycblt.auxio.music.BaseModel
import org.oxycblt.auxio.playback.PlaybackViewModel import org.oxycblt.auxio.playback.PlaybackViewModel
import org.oxycblt.auxio.ui.isLandscape
import org.oxycblt.auxio.ui.memberBinding 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 // Override the back button so that going back will only exit the detail fragments instead of
// the entire app. // the entire app.
private val callback = object : OnBackPressedCallback(false) { private val callback = object : OnBackPressedCallback(false) {

View file

@ -5,6 +5,7 @@ import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
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.BaseModel
import org.oxycblt.auxio.music.Genre import org.oxycblt.auxio.music.Genre
import org.oxycblt.auxio.recycler.SortMode import org.oxycblt.auxio.recycler.SortMode
@ -26,18 +27,25 @@ class DetailViewModel : ViewModel() {
val albumSortMode: LiveData<SortMode> get() = mAlbumSortMode val albumSortMode: LiveData<SortMode> get() = mAlbumSortMode
// Current music models being shown // Current music models being shown
private val mCurrentGenre = MutableLiveData<Genre>() private val mCurrentGenre = MutableLiveData<Genre?>()
val currentGenre: LiveData<Genre> get() = mCurrentGenre val currentGenre: LiveData<Genre?> get() = mCurrentGenre
private val mCurrentArtist = MutableLiveData<Artist>() private val mCurrentArtist = MutableLiveData<Artist?>()
val currentArtist: LiveData<Artist> get() = mCurrentArtist val currentArtist: LiveData<Artist?> get() = mCurrentArtist
private val mCurrentAlbum = MutableLiveData<Album>() private val mCurrentAlbum = MutableLiveData<Album?>()
val currentAlbum: LiveData<Album> get() = mCurrentAlbum val currentAlbum: LiveData<Album?> get() = mCurrentAlbum
// Navigation flags
private val mNavToItem = MutableLiveData<BaseModel?>()
val navToItem: LiveData<BaseModel?> get() = mNavToItem
private val mNavToParent = MutableLiveData<Boolean>() private val mNavToParent = MutableLiveData<Boolean>()
val navToParent: LiveData<Boolean> get() = mNavToParent val navToParent: LiveData<Boolean> get() = mNavToParent
private val mNavToChild = MutableLiveData<BaseModel?>()
val navToChild: LiveData<BaseModel?> get() = mNavToChild
/** /**
* Update the current navigation status * Update the current navigation status
* @param value Whether the current [DetailFragment] is navigating or not. * @param value Whether the current [DetailFragment] is navigating or not.
@ -96,8 +104,18 @@ class DetailViewModel : ViewModel() {
mCurrentAlbum.value = album 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 */ /** Mark that parent navigation should occur */
fun doNavToParent() { fun navToParent() {
mNavToParent.value = true mNavToParent.value = true
} }
@ -105,4 +123,13 @@ class DetailViewModel : ViewModel() {
fun doneWithNavToParent() { fun doneWithNavToParent() {
mNavToParent.value = false 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.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.appcompat.widget.PopupMenu import androidx.appcompat.widget.PopupMenu
import androidx.navigation.fragment.findNavController
import androidx.navigation.fragment.navArgs import androidx.navigation.fragment.navArgs
import androidx.recyclerview.widget.GridLayoutManager
import org.oxycblt.auxio.R import org.oxycblt.auxio.R
import org.oxycblt.auxio.detail.adapters.GenreDetailAdapter import org.oxycblt.auxio.detail.adapters.GenreDetailAdapter
import org.oxycblt.auxio.logD 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.BaseModel
import org.oxycblt.auxio.music.MusicStore import org.oxycblt.auxio.music.MusicStore
import org.oxycblt.auxio.playback.state.PlaybackMode import org.oxycblt.auxio.playback.state.PlaybackMode
import org.oxycblt.auxio.ui.isLandscape
import org.oxycblt.auxio.ui.setupGenreSongActions import org.oxycblt.auxio.ui.setupGenreSongActions
/** /**
@ -47,7 +48,7 @@ class GenreDetailFragment : DetailFragment() {
}, },
doOnLongClick = { data, view -> doOnLongClick = { data, view ->
PopupMenu(requireContext(), view).setupGenreSongActions( PopupMenu(requireContext(), view).setupGenreSongActions(
requireContext(), data, playbackModel requireContext(), data, playbackModel, detailModel
) )
} }
) )
@ -71,20 +72,7 @@ class GenreDetailFragment : DetailFragment() {
} }
} }
binding.detailRecycler.apply { setupRecycler(detailAdapter)
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
}
}
}
}
}
// --- VIEWMODEL SETUP --- // --- VIEWMODEL SETUP ---
@ -98,6 +86,22 @@ class GenreDetailFragment : DetailFragment() {
detailAdapter.submitList(data) 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.") logD("Fragment created.")
return binding.root return binding.root

View file

@ -17,6 +17,7 @@ import androidx.transition.Fade
import androidx.transition.TransitionManager import androidx.transition.TransitionManager
import org.oxycblt.auxio.R import org.oxycblt.auxio.R
import org.oxycblt.auxio.databinding.FragmentLibraryBinding 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.LibraryAdapter
import org.oxycblt.auxio.library.adapters.SearchAdapter import org.oxycblt.auxio.library.adapters.SearchAdapter
import org.oxycblt.auxio.logD import org.oxycblt.auxio.logD
@ -46,6 +47,7 @@ import org.oxycblt.auxio.ui.setupSongActions
class LibraryFragment : Fragment(), SearchView.OnQueryTextListener { class LibraryFragment : Fragment(), SearchView.OnQueryTextListener {
private val libraryModel: LibraryViewModel by activityViewModels() private val libraryModel: LibraryViewModel by activityViewModels()
private val detailModel: DetailViewModel by activityViewModels()
private val playbackModel: PlaybackViewModel by activityViewModels() private val playbackModel: PlaybackViewModel by activityViewModels()
override fun onCreateView( override fun onCreateView(
@ -167,17 +169,13 @@ class LibraryFragment : Fragment(), SearchView.OnQueryTextListener {
} }
} }
playbackModel.navToItem.observe(viewLifecycleOwner) { detailModel.navToItem.observe(viewLifecycleOwner) {
if (it != null) { if (it != null) {
libraryModel.updateNavigationStatus(false) libraryModel.updateNavigationStatus(false)
if (it is Song || it is Album) {
onItemSelection(playbackModel.song.value!!.album)
} else {
onItemSelection(it) onItemSelection(it)
} }
} }
}
logD("Fragment created.") logD("Fragment created.")
@ -207,7 +205,7 @@ class LibraryFragment : Fragment(), SearchView.OnQueryTextListener {
val menu = PopupMenu(requireContext(), view) val menu = PopupMenu(requireContext(), view)
when (data) { 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 Album -> menu.setupAlbumActions(requireContext(), data, playbackModel)
is Artist -> menu.setupArtistActions(data, playbackModel) is Artist -> menu.setupArtistActions(data, playbackModel)
is Genre -> menu.setupGenreActions(data, playbackModel) is Genre -> menu.setupGenreActions(data, playbackModel)
@ -240,7 +238,7 @@ class LibraryFragment : Fragment(), SearchView.OnQueryTextListener {
when (baseModel) { when (baseModel) {
is Genre -> LibraryFragmentDirections.actionShowGenre(baseModel.id) is Genre -> LibraryFragmentDirections.actionShowGenre(baseModel.id)
is Artist -> LibraryFragmentDirections.actionShowArtist(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 // If given model wasn't valid, then reset the navigation status
// and abort the navigation. // and abort the navigation.

View file

@ -12,6 +12,7 @@ import androidx.navigation.fragment.findNavController
import org.oxycblt.auxio.MainFragmentDirections import org.oxycblt.auxio.MainFragmentDirections
import org.oxycblt.auxio.R import org.oxycblt.auxio.R
import org.oxycblt.auxio.databinding.FragmentCompactPlaybackBinding import org.oxycblt.auxio.databinding.FragmentCompactPlaybackBinding
import org.oxycblt.auxio.detail.DetailViewModel
import org.oxycblt.auxio.logD import org.oxycblt.auxio.logD
import org.oxycblt.auxio.music.MusicStore import org.oxycblt.auxio.music.MusicStore
import org.oxycblt.auxio.ui.isLandscape import org.oxycblt.auxio.ui.isLandscape
@ -26,6 +27,7 @@ import org.oxycblt.auxio.ui.memberBinding
*/ */
class CompactPlaybackFragment : Fragment() { class CompactPlaybackFragment : Fragment() {
private val playbackModel: PlaybackViewModel by activityViewModels() private val playbackModel: PlaybackViewModel by activityViewModels()
private val detailModel: DetailViewModel by activityViewModels()
private val binding: FragmentCompactPlaybackBinding by memberBinding( private val binding: FragmentCompactPlaybackBinding by memberBinding(
FragmentCompactPlaybackBinding::inflate FragmentCompactPlaybackBinding::inflate
) )
@ -57,7 +59,7 @@ class CompactPlaybackFragment : Fragment() {
} }
setOnLongClickListener { setOnLongClickListener {
playbackModel.navToItem(playbackModel.song.value!!) detailModel.navToItem(playbackModel.song.value!!)
true true
} }
} }

View file

@ -24,14 +24,13 @@ object NotificationUtils {
const val NOTIFICATION_ID = 0xA0A0 const val NOTIFICATION_ID = 0xA0A0
const val REQUEST_CODE = 0xA0C0 const val REQUEST_CODE = 0xA0C0
// Strings for each action, version name is applied so that broadcasts will only reach // The build type is applied to each action so that broadcasts will not conflict with debug/release builds.
// one instance of Auxio. const val ACTION_LOOP = "ACTION_AUXIO_LOOP_" + BuildConfig.BUILD_TYPE
const val ACTION_LOOP = "ACTION_AUXIO_LOOP_" + BuildConfig.VERSION_NAME const val ACTION_SHUFFLE = "ACTION_AUXIO_SHUFFLE_" + BuildConfig.BUILD_TYPE
const val ACTION_SHUFFLE = "ACTION_AUXIO_SHUFFLE_" + BuildConfig.VERSION_NAME const val ACTION_SKIP_PREV = "ACTION_AUXIO_SKIP_PREV_" + BuildConfig.BUILD_TYPE
const val ACTION_SKIP_PREV = "ACTION_AUXIO_SKIP_PREV_" + BuildConfig.VERSION_NAME const val ACTION_PLAY_PAUSE = "ACTION_AUXIO_PLAY_PAUSE_" + BuildConfig.BUILD_TYPE
const val ACTION_PLAY_PAUSE = "ACTION_AUXIO_PLAY_PAUSE_" + BuildConfig.VERSION_NAME const val ACTION_SKIP_NEXT = "ACTION_AUXIO_SKIP_NEXT_" + BuildConfig.BUILD_TYPE
const val ACTION_SKIP_NEXT = "ACTION_AUXIO_SKIP_NEXT_" + BuildConfig.VERSION_NAME const val ACTION_EXIT = "ACTION_AUXIO_EXIT_" + BuildConfig.BUILD_TYPE
const val ACTION_EXIT = "ACTION_AUXIO_EXIT_" + BuildConfig.VERSION_NAME
} }
/** /**
@ -44,7 +43,7 @@ fun NotificationManager.createMediaNotification(
context: Context, context: Context,
mediaSession: MediaSessionCompat mediaSession: MediaSessionCompat
): NotificationCompat.Builder { ): NotificationCompat.Builder {
// Create a notification channel if required // Create a notification channel if requireds
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val channel = NotificationChannel( val channel = NotificationChannel(
NotificationUtils.CHANNEL_ID, NotificationUtils.CHANNEL_ID,

View file

@ -14,6 +14,7 @@ import androidx.fragment.app.activityViewModels
import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.findNavController
import org.oxycblt.auxio.R import org.oxycblt.auxio.R
import org.oxycblt.auxio.databinding.FragmentPlaybackBinding import org.oxycblt.auxio.databinding.FragmentPlaybackBinding
import org.oxycblt.auxio.detail.DetailViewModel
import org.oxycblt.auxio.logD import org.oxycblt.auxio.logD
import org.oxycblt.auxio.playback.state.LoopMode import org.oxycblt.auxio.playback.state.LoopMode
import org.oxycblt.auxio.ui.accent import org.oxycblt.auxio.ui.accent
@ -27,12 +28,13 @@ import org.oxycblt.auxio.ui.toColor
*/ */
class PlaybackFragment : Fragment(), SeekBar.OnSeekBarChangeListener { class PlaybackFragment : Fragment(), SeekBar.OnSeekBarChangeListener {
private val playbackModel: PlaybackViewModel by activityViewModels() private val playbackModel: PlaybackViewModel by activityViewModels()
private val detailModel: DetailViewModel by activityViewModels()
private val binding: FragmentPlaybackBinding by memberBinding(FragmentPlaybackBinding::inflate) { private val binding: FragmentPlaybackBinding by memberBinding(FragmentPlaybackBinding::inflate) {
// Marquee must be disabled on destroy to prevent memory leaks // Marquee must be disabled on destroy to prevent memory leaks
binding.playbackSong.isSelected = false binding.playbackSong.isSelected = false
} }
// Colors/Icons // Colors
private val accentColor: ColorStateList by lazy { private val accentColor: ColorStateList by lazy {
ColorStateList.valueOf(accent.first.toColor(requireContext())) ColorStateList.valueOf(accent.first.toColor(requireContext()))
} }
@ -64,6 +66,7 @@ class PlaybackFragment : Fragment(), SeekBar.OnSeekBarChangeListener {
binding.lifecycleOwner = viewLifecycleOwner binding.lifecycleOwner = viewLifecycleOwner
binding.playbackModel = playbackModel binding.playbackModel = playbackModel
binding.detailModel = detailModel
binding.song = playbackModel.song.value!! binding.song = playbackModel.song.value!!
binding.playbackToolbar.apply { binding.playbackToolbar.apply {
@ -84,7 +87,6 @@ class PlaybackFragment : Fragment(), SeekBar.OnSeekBarChangeListener {
// Make marquee of song title work // Make marquee of song title work
binding.playbackSong.isSelected = true binding.playbackSong.isSelected = true
binding.playbackSeekBar.setOnSeekBarChangeListener(this) binding.playbackSeekBar.setOnSeekBarChangeListener(this)
// --- VIEWMODEL SETUP -- // --- VIEWMODEL SETUP --
@ -171,7 +173,7 @@ class PlaybackFragment : Fragment(), SeekBar.OnSeekBarChangeListener {
} }
} }
playbackModel.navToItem.observe(viewLifecycleOwner) { detailModel.navToItem.observe(viewLifecycleOwner) {
if (it != null) { if (it != null) {
findNavController().navigateUp() findNavController().navigateUp()
} }

View file

@ -44,7 +44,6 @@ class PlaybackViewModel : ViewModel(), PlaybackStateManager.Callback {
// Other // Other
private val mIsSeeking = MutableLiveData(false) private val mIsSeeking = MutableLiveData(false)
private val mNavToItem = MutableLiveData<BaseModel?>()
private var mCanAnimate = false private var mCanAnimate = false
/** The current song. */ /** The current song. */
@ -66,7 +65,6 @@ class PlaybackViewModel : ViewModel(), PlaybackStateManager.Callback {
/** The current repeat mode, see [LoopMode] for more information */ /** The current repeat mode, see [LoopMode] for more information */
val loopMode: LiveData<LoopMode> get() = mLoopMode val loopMode: LiveData<LoopMode> get() = mLoopMode
val isSeeking: LiveData<Boolean> get() = mIsSeeking val isSeeking: LiveData<Boolean> get() = mIsSeeking
val navToItem: LiveData<BaseModel?> get() = mNavToItem
val canAnimate: Boolean get() = mCanAnimate val canAnimate: Boolean get() = mCanAnimate
/** The position as a duration string. */ /** The position as a duration string. */
@ -335,16 +333,6 @@ class PlaybackViewModel : ViewModel(), PlaybackStateManager.Callback {
mIsSeeking.value = value 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 */ /** Enable animation on CompactPlaybackFragment */
fun enableAnimation() { fun enableAnimation() {
mCanAnimate = true mCanAnimate = true

View file

@ -196,7 +196,7 @@ class PlaybackStateManager private constructor() {
fun playParentModel(baseModel: BaseModel, shuffled: Boolean) { fun playParentModel(baseModel: BaseModel, shuffled: Boolean) {
if (baseModel is Song || baseModel is Header) { if (baseModel is Song || baseModel is Header) {
// This should never occur. // This should never occur.
logE("playParentModel does not support ${baseModel::class.simpleName}.") logE("playParentModel is not meant to play ${baseModel::class.simpleName}.")
return return
} }
@ -575,7 +575,7 @@ class PlaybackStateManager private constructor() {
/** /**
* Set the [LoopMode] * Set the [LoopMode]
* @param value The [LoopMode] to be used * @param mode The [LoopMode] to be used
*/ */
fun setLoopMode(mode: LoopMode) { fun setLoopMode(mode: LoopMode) {
mLoopMode = mode mLoopMode = mode

View file

@ -11,7 +11,6 @@ enum class DisplayMode(@DrawableRes val iconRes: Int) {
SHOW_GENRES(R.drawable.ic_genre), SHOW_GENRES(R.drawable.ic_genre),
SHOW_ARTISTS(R.drawable.ic_artist), SHOW_ARTISTS(R.drawable.ic_artist),
SHOW_ALBUMS(R.drawable.ic_album), SHOW_ALBUMS(R.drawable.ic_album),
SHOW_SONGS(R.drawable.ic_song);
/** /**
* Make a slice of all the values that this DisplayMode covers. * 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 com.reddit.indicatorfastscroll.FastScrollerView
import org.oxycblt.auxio.R import org.oxycblt.auxio.R
import org.oxycblt.auxio.databinding.FragmentSongsBinding import org.oxycblt.auxio.databinding.FragmentSongsBinding
import org.oxycblt.auxio.detail.DetailViewModel
import org.oxycblt.auxio.logD import org.oxycblt.auxio.logD
import org.oxycblt.auxio.music.MusicStore import org.oxycblt.auxio.music.MusicStore
import org.oxycblt.auxio.playback.PlaybackViewModel import org.oxycblt.auxio.playback.PlaybackViewModel
@ -33,6 +34,7 @@ import kotlin.math.ceil
*/ */
class SongsFragment : Fragment() { class SongsFragment : Fragment() {
private val playbackModel: PlaybackViewModel by activityViewModels() 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. // Lazy init the text size so that it doesn't have to be calculated every time.
private val indicatorTextSize: Float by lazy { private val indicatorTextSize: Float by lazy {
@ -57,7 +59,7 @@ class SongsFragment : Fragment() {
doOnClick = { playbackModel.playSong(it, settingsManager.songPlaybackMode) }, doOnClick = { playbackModel.playSong(it, settingsManager.songPlaybackMode) },
doOnLongClick = { data, view -> doOnLongClick = { data, view ->
PopupMenu(requireContext(), view).setupSongActions( 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.music.Song
import org.oxycblt.auxio.playback.PlaybackViewModel import org.oxycblt.auxio.playback.PlaybackViewModel
import org.oxycblt.auxio.playback.state.PlaybackMode import org.oxycblt.auxio.playback.state.PlaybackMode
import org.oxycblt.auxio.settings.SettingsManager
/** /**
* Apply a text color to a [MenuItem] * Apply a text color to a [MenuItem]
@ -79,10 +78,14 @@ fun Spanned.render(): Spanned {
* @param context [Context] required * @param context [Context] required
* @param song [Song] The menu should correspond to * @param song [Song] The menu should correspond to
* @param playbackModel The [PlaybackViewModel] the menu should dispatch actions 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) { fun PopupMenu.setupSongActions(
inflateAndShow(R.menu.menu_song_actions) context: Context,
song: Song,
playbackModel: PlaybackViewModel,
detailModel: DetailViewModel
) {
setOnMenuItemClickListener { setOnMenuItemClickListener {
when (it.itemId) { when (it.itemId) {
R.id.action_queue_add -> { R.id.action_queue_add -> {
@ -91,18 +94,13 @@ fun PopupMenu.setupSongActions(context: Context, song: Song, playbackModel: Play
true true
} }
R.id.action_play_artist -> { R.id.action_go_artist -> {
playbackModel.playSong(song, PlaybackMode.IN_ARTIST) detailModel.navToItem(song.album.artist)
true true
} }
R.id.action_play_album -> { R.id.action_go_album -> {
playbackModel.playSong(song, PlaybackMode.IN_ALBUM) detailModel.navToItem(song.album)
true
}
R.id.action_play_all_songs -> {
playbackModel.playSong(song, PlaybackMode.ALL_SONGS)
true true
} }
@ -110,18 +108,7 @@ fun PopupMenu.setupSongActions(context: Context, song: Song, playbackModel: Play
} }
} }
val settingsManager = SettingsManager.getInstance() inflateAndShow(R.menu.menu_song_actions)
// 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
} }
/** /**
@ -148,7 +135,7 @@ fun PopupMenu.setupAlbumSongActions(
} }
R.id.action_go_artist -> { R.id.action_go_artist -> {
detailModel.doNavToParent() detailModel.navToParent()
true 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 context [Context] required
* @param song [Song] the menu should correspond to * @param song [Song] The menu should correspond to
* @param playbackModel [PlaybackViewModel] to dispatch actions 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 { setOnMenuItemClickListener {
when (it.itemId) { when (it.itemId) {
R.id.action_queue_add -> { R.id.action_queue_add -> {
@ -257,20 +251,21 @@ fun PopupMenu.setupGenreSongActions(context: Context, song: Song, playbackModel:
true true
} }
R.id.action_play_artist -> { R.id.action_go_artist -> {
playbackModel.playSong(song, PlaybackMode.IN_ARTIST) detailModel.navToChild(song.album.artist)
true true
} }
R.id.action_play_album -> { R.id.action_go_album -> {
playbackModel.playSong(song, PlaybackMode.IN_ALBUM) detailModel.navToChild(song.album)
true true
} }
else -> false 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 <variable
name="playbackModel" name="playbackModel"
type="org.oxycblt.auxio.playback.PlaybackViewModel" /> type="org.oxycblt.auxio.playback.PlaybackViewModel" />
<variable
name="detailModel"
type="org.oxycblt.auxio.detail.DetailViewModel" />
</data> </data>
@ -70,7 +74,7 @@
android:ellipsize="marquee" android:ellipsize="marquee"
android:fontFamily="@font/inter_semibold" android:fontFamily="@font/inter_semibold"
android:marqueeRepeatLimit="marquee_forever" android:marqueeRepeatLimit="marquee_forever"
android:onClick="@{() -> playbackModel.navToItem(playbackModel.song)}" android:onClick="@{() -> detailModel.navToItem(playbackModel.song)}"
android:singleLine="true" android:singleLine="true"
android:text="@{song.name}" android:text="@{song.name}"
android:textAppearance="@style/TextAppearance.MaterialComponents.Headline6" android:textAppearance="@style/TextAppearance.MaterialComponents.Headline6"
@ -85,7 +89,7 @@
android:layout_marginStart="@dimen/margin_mid_large" android:layout_marginStart="@dimen/margin_mid_large"
android:layout_marginEnd="@dimen/margin_mid_large" android:layout_marginEnd="@dimen/margin_mid_large"
android:ellipsize="end" android:ellipsize="end"
android:onClick="@{() -> playbackModel.navToItem(playbackModel.song.album.artist)}" android:onClick="@{() -> detailModel.navToItem(playbackModel.song.album.artist)}"
android:singleLine="true" android:singleLine="true"
android:text="@{song.album.artist.name}" android:text="@{song.album.artist.name}"
android:textAppearance="@style/TextAppearance.MaterialComponents.Subtitle1" android:textAppearance="@style/TextAppearance.MaterialComponents.Subtitle1"
@ -103,7 +107,7 @@
android:layout_marginStart="@dimen/margin_mid_large" android:layout_marginStart="@dimen/margin_mid_large"
android:layout_marginEnd="@dimen/margin_mid_large" android:layout_marginEnd="@dimen/margin_mid_large"
android:ellipsize="end" android:ellipsize="end"
android:onClick="@{() -> playbackModel.navToItem(playbackModel.song.album)}" android:onClick="@{() -> detailModel.navToItem(playbackModel.song.album)}"
android:singleLine="true" android:singleLine="true"
android:text="@{song.album.name}" android:text="@{song.album.name}"
android:textAppearance="@style/TextAppearance.MaterialComponents.Subtitle1" android:textAppearance="@style/TextAppearance.MaterialComponents.Subtitle1"

View file

@ -55,7 +55,7 @@
android:background="@drawable/ui_ripple" android:background="@drawable/ui_ripple"
android:clickable="true" android:clickable="true"
android:focusable="true" android:focusable="true"
android:onClick="@{() -> detailModel.doNavToParent()}" android:onClick="@{() -> detailModel.navToParent()}"
android:text="@{album.artist.name}" android:text="@{album.artist.name}"
android:textAppearance="?android:attr/textAppearanceListItem" android:textAppearance="?android:attr/textAppearanceListItem"
android:textColor="?android:attr/textColorSecondary" android:textColor="?android:attr/textColorSecondary"

View file

@ -13,6 +13,10 @@
<variable <variable
name="playbackModel" name="playbackModel"
type="org.oxycblt.auxio.playback.PlaybackViewModel" /> type="org.oxycblt.auxio.playback.PlaybackViewModel" />
<variable
name="detailModel"
type="org.oxycblt.auxio.detail.DetailViewModel" />
</data> </data>
@ -59,7 +63,7 @@
android:ellipsize="marquee" android:ellipsize="marquee"
android:fontFamily="@font/inter_semibold" android:fontFamily="@font/inter_semibold"
android:marqueeRepeatLimit="marquee_forever" android:marqueeRepeatLimit="marquee_forever"
android:onClick="@{() -> playbackModel.navToItem(playbackModel.song)}" android:onClick="@{() -> detailModel.navToItem(playbackModel.song)}"
android:singleLine="true" android:singleLine="true"
android:text="@{song.name}" android:text="@{song.name}"
android:textAppearance="@style/TextAppearance.MaterialComponents.Headline6" android:textAppearance="@style/TextAppearance.MaterialComponents.Headline6"
@ -76,7 +80,7 @@
android:layout_marginStart="@dimen/margin_mid_large" android:layout_marginStart="@dimen/margin_mid_large"
android:layout_marginEnd="@dimen/margin_mid_large" android:layout_marginEnd="@dimen/margin_mid_large"
android:ellipsize="end" android:ellipsize="end"
android:onClick="@{() -> playbackModel.navToItem(playbackModel.song.album.artist)}" android:onClick="@{() -> detailModel.navToItem(playbackModel.song.album.artist)}"
android:singleLine="true" android:singleLine="true"
android:text="@{song.album.artist.name}" android:text="@{song.album.artist.name}"
android:textAppearance="@style/TextAppearance.MaterialComponents.Subtitle1" android:textAppearance="@style/TextAppearance.MaterialComponents.Subtitle1"
@ -94,7 +98,7 @@
android:layout_marginEnd="@dimen/margin_mid_large" android:layout_marginEnd="@dimen/margin_mid_large"
android:layout_marginBottom="16dp" android:layout_marginBottom="16dp"
android:ellipsize="end" android:ellipsize="end"
android:onClick="@{() -> playbackModel.navToItem(playbackModel.song.album)}" android:onClick="@{() -> detailModel.navToItem(playbackModel.song.album)}"
android:singleLine="true" android:singleLine="true"
android:text="@{song.album.name}" android:text="@{song.album.name}"
android:textAppearance="@style/TextAppearance.MaterialComponents.Subtitle1" android:textAppearance="@style/TextAppearance.MaterialComponents.Subtitle1"

View file

@ -54,7 +54,7 @@
android:background="@drawable/ui_ripple" android:background="@drawable/ui_ripple"
android:clickable="true" android:clickable="true"
android:focusable="true" android:focusable="true"
android:onClick="@{() -> detailModel.doNavToParent()}" android:onClick="@{() -> detailModel.navToParent()}"
android:text="@{album.artist.name}" android:text="@{album.artist.name}"
android:textAppearance="?android:attr/textAppearanceListItem" android:textAppearance="?android:attr/textAppearanceListItem"
android:textColor="?android:attr/textColorSecondary" 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:icon="@drawable/ic_queue_add"
android:title="@string/label_queue_add" /> android:title="@string/label_queue_add" />
<item <item
android:id="@+id/action_play_all_songs" android:id="@+id/action_go_artist"
android:icon="@drawable/ic_song"
android:title="@string/label_play_all_songs" />
<item
android:id="@+id/action_play_artist"
android:icon="@drawable/ic_artist" android:icon="@drawable/ic_artist"
android:title="@string/label_play_artist" /> android:title="@string/label_go_artist" />
<item <item
android:id="@+id/action_play_album" android:id="@+id/action_go_album"
android:icon="@drawable/ic_album" android:icon="@drawable/ic_album"
android:title="@string/label_play_album" /> android:title="@string/label_go_album" />
</menu> </menu>

View file

@ -57,7 +57,7 @@
android:name="albumId" android:name="albumId"
app:argType="long" /> app:argType="long" />
<argument <argument
android:name="enableParentNav" android:name="fromArtist"
app:argType="boolean" /> app:argType="boolean" />
<action <action
android:id="@+id/action_show_parent_artist" android:id="@+id/action_show_parent_artist"
@ -72,16 +72,23 @@
android:name="org.oxycblt.auxio.detail.GenreDetailFragment" android:name="org.oxycblt.auxio.detail.GenreDetailFragment"
android:label="GenreDetailFragment" android:label="GenreDetailFragment"
tools:layout="@layout/fragment_detail"> tools:layout="@layout/fragment_detail">
<argument
android:name="genreId"
app:argType="long" />
<action <action
android:id="@+id/action_show_artist" android:id="@+id/action_go_artist"
app:destination="@id/artist_detail_fragment" app:destination="@id/artist_detail_fragment"
app:enterAnim="@anim/nav_default_enter_anim" app:enterAnim="@anim/nav_default_enter_anim"
app:exitAnim="@anim/nav_default_exit_anim" app:exitAnim="@anim/nav_default_exit_anim"
app:popEnterAnim="@anim/nav_default_pop_enter_anim" app:popEnterAnim="@anim/nav_default_pop_enter_anim"
app:popExitAnim="@anim/nav_default_pop_exit_anim" /> app:popExitAnim="@anim/nav_default_pop_exit_anim" />
<argument <action
android:name="genreId" android:id="@+id/action_go_album"
app:argType="long" /> 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>
<fragment <fragment
android:id="@+id/songs_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_all_songs">All Songs</string>
<string name="label_songs">Songs</string> <string name="label_songs">Songs</string>
<string name="label_playback">Now Playing</string> <string name="label_playback">Now Playing</string>
<string name="label_play">Play</string> <string name="label_play">Play</string>
<string name="label_shuffle">Shuffle</string> <string name="label_shuffle">Shuffle</string>
<string name="label_play_all_songs">Play from all songs</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_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_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_play_albums">Play albums</string>
<string name="label_shuffle_albums">Shuffle albums</string>
<string name="label_queue">Queue</string> <string name="label_queue">Queue</string>
<string name="label_queue_add">Add to 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">Accent</string>
<string name="setting_accent_unknown">Unknown 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_display">Display</string>
<string name="setting_lib_display">Library Items</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_shuffle_off">Turn shuffle off</string>
<string name="description_change_loop">Change Repeat Mode</string> <string name="description_change_loop">Change Repeat Mode</string>
<string name="description_auxio_icon">Auxio icon</string> <string name="description_auxio_icon">Auxio icon</string>
<string name="description_code">Code</string>
<!-- Placeholder Namespace | Placeholder values --> <!-- Placeholder Namespace | Placeholder values -->
<string name="placeholder_genre">Unknown Genre</string> <string name="placeholder_genre">Unknown Genre</string>
@ -172,8 +169,4 @@
<item quantity="other">%s Albums</item> <item quantity="other">%s Albums</item>
</plurals> </plurals>
<plurals name="format_artist_count">
<item quantity="one">%s Artist</item>
<item quantity="other">%s Artists</item>
</plurals>
</resources> </resources>