Refactor menus
Heavily refactor how certain menus are used when it comes to songs.
This commit is contained in:
parent
98320fcd8d
commit
8fe0734ca1
27 changed files with 190 additions and 263 deletions
|
@ -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"
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
)
|
||||
}
|
||||
)
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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>
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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"
|
||||
|
|
|
@ -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>
|
|
@ -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>
|
Loading…
Reference in a new issue