Highlight sort menu item when selected

Highlight the menu item for the currently shown order when its selected.
This commit is contained in:
OxygenCobalt 2020-09-28 18:11:56 -06:00
parent 37c52d9e5c
commit 08f8e50bbf
10 changed files with 94 additions and 40 deletions

View file

@ -32,7 +32,9 @@ class AlbumDetailFragment : Fragment() {
// If DetailViewModel isn't already storing the album, get it from MusicViewModel // If DetailViewModel isn't already storing the album, get it from MusicViewModel
// using the ID given by the navigation arguments. // using the ID given by the navigation arguments.
if (detailModel.currentAlbum.value!!.id != args.albumId) { if (detailModel.currentAlbum.value == null ||
detailModel.currentAlbum.value?.id != args.albumId
) {
val musicModel: MusicViewModel by activityViewModels() val musicModel: MusicViewModel by activityViewModels()
detailModel.updateAlbum( detailModel.updateAlbum(

View file

@ -33,7 +33,9 @@ class ArtistDetailFragment : Fragment() {
// If DetailViewModel isn't already storing the artist, get it from MusicViewModel // If DetailViewModel isn't already storing the artist, get it from MusicViewModel
// using the ID given by the navigation arguments // using the ID given by the navigation arguments
if (detailModel.currentArtist.value!!.id != args.artistId) { if (detailModel.currentArtist.value == null ||
detailModel.currentArtist.value?.id != args.artistId
) {
val musicModel: MusicViewModel by activityViewModels() val musicModel: MusicViewModel by activityViewModels()
detailModel.updateArtist( detailModel.updateArtist(

View file

@ -21,20 +21,13 @@ 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
// These have placeholder values initially so that they don't have to be checked if they're null private val mCurrentGenre = MutableLiveData<Genre>()
private val mCurrentGenre = MutableLiveData(
Genre(id = Long.MIN_VALUE, name = "")
)
val currentGenre: LiveData<Genre> get() = mCurrentGenre val currentGenre: LiveData<Genre> get() = mCurrentGenre
private val mCurrentArtist = MutableLiveData( private val mCurrentArtist = MutableLiveData<Artist>()
Artist(id = Long.MIN_VALUE, name = "")
)
val currentArtist: LiveData<Artist> get() = mCurrentArtist val currentArtist: LiveData<Artist> get() = mCurrentArtist
private val mCurrentAlbum = MutableLiveData( private val mCurrentAlbum = MutableLiveData<Album>()
Album(id = Long.MIN_VALUE, name = "", artistName = "")
)
val currentAlbum: LiveData<Album> get() = mCurrentAlbum val currentAlbum: LiveData<Album> get() = mCurrentAlbum
private val mNavToParent = MutableLiveData<Boolean>() private val mNavToParent = MutableLiveData<Boolean>()

View file

@ -33,7 +33,9 @@ class GenreDetailFragment : Fragment() {
// If DetailViewModel isn't already storing the genre, get it from MusicViewModel // If DetailViewModel isn't already storing the genre, get it from MusicViewModel
// using the ID given by the navigation arguments // using the ID given by the navigation arguments
if (detailModel.currentGenre.value!!.id != args.genreId) { if (detailModel.currentGenre.value == null ||
detailModel.currentGenre.value?.id != args.genreId
) {
val musicModel: MusicViewModel by activityViewModels() val musicModel: MusicViewModel by activityViewModels()
detailModel.updateGenre( detailModel.updateGenre(

View file

@ -6,6 +6,7 @@ import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.view.forEach
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
@ -19,7 +20,9 @@ import org.oxycblt.auxio.music.BaseModel
import org.oxycblt.auxio.music.Genre import org.oxycblt.auxio.music.Genre
import org.oxycblt.auxio.music.MusicViewModel import org.oxycblt.auxio.music.MusicViewModel
import org.oxycblt.auxio.recycler.ClickListener import org.oxycblt.auxio.recycler.ClickListener
import org.oxycblt.auxio.theme.applyColor
import org.oxycblt.auxio.theme.applyDivider import org.oxycblt.auxio.theme.applyDivider
import org.oxycblt.auxio.theme.resolveAttr
class LibraryFragment : Fragment() { class LibraryFragment : Fragment() {
@ -42,15 +45,28 @@ class LibraryFragment : Fragment() {
binding.libraryRecycler.setHasFixedSize(true) binding.libraryRecycler.setHasFixedSize(true)
libraryModel.sortMode.observe(viewLifecycleOwner) { mode -> libraryModel.sortMode.observe(viewLifecycleOwner) { mode ->
binding.libraryToolbar.overflowIcon = ContextCompat.getDrawable( Log.d(this::class.simpleName, "Updating sort mode to $mode")
requireContext(), mode.iconRes
)
// Update the adapter with the new data
artistAdapter.updateData( artistAdapter.updateData(
mode.getSortedArtistList( mode.getSortedArtistList(
musicModel.artists.value!! musicModel.artists.value!!
) )
) )
// Then update the shown icon & the currently highlighted sort icon to reflect
// the new sorting mode.
binding.libraryToolbar.overflowIcon = ContextCompat.getDrawable(
requireContext(), mode.iconRes
)
binding.libraryToolbar.menu.forEach {
if (it.itemId == libraryModel.sortMode.value!!.toMenuId()) {
it.applyColor(resolveAttr(requireContext(), R.attr.colorPrimary))
} else {
it.applyColor(resolveAttr(requireContext(), android.R.attr.textColorPrimary))
}
}
} }
binding.libraryToolbar.setOnMenuItemClickListener { binding.libraryToolbar.setOnMenuItemClickListener {

View file

@ -0,0 +1,27 @@
package org.oxycblt.auxio.recycler
import androidx.databinding.ViewDataBinding
import androidx.recyclerview.widget.RecyclerView
import org.oxycblt.auxio.music.BaseModel
// ViewHolder abstraction that automates some of the things that are common for all ViewHolders.
abstract class BaseViewHolder<T : BaseModel>(
private val baseBinding: ViewDataBinding,
protected val listener: ClickListener<T>
) : RecyclerView.ViewHolder(baseBinding.root) {
init {
baseBinding.root.layoutParams = RecyclerView.LayoutParams(
RecyclerView.LayoutParams.MATCH_PARENT, RecyclerView.LayoutParams.WRAP_CONTENT
)
}
fun bind(model: T) {
baseBinding.root.setOnClickListener { listener.onClick(model) }
onBind(model)
baseBinding.executePendingBindings()
}
abstract fun onBind(model: T)
}

View file

@ -1,8 +1,6 @@
package org.oxycblt.auxio.recycler package org.oxycblt.auxio.recycler
import androidx.databinding.ViewDataBinding
import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView
import org.oxycblt.auxio.music.BaseModel import org.oxycblt.auxio.music.BaseModel
// RecyclerView click listener // RecyclerView click listener
@ -18,25 +16,3 @@ class DiffCallback<T : BaseModel> : DiffUtil.ItemCallback<T>() {
return oldItem == newItem return oldItem == newItem
} }
} }
// ViewHolder abstraction that automates some of the things that are common for all ViewHolders.
abstract class BaseViewHolder<T : BaseModel>(
private val baseBinding: ViewDataBinding,
protected val listener: ClickListener<T>
) : RecyclerView.ViewHolder(baseBinding.root) {
init {
baseBinding.root.layoutParams = RecyclerView.LayoutParams(
RecyclerView.LayoutParams.MATCH_PARENT, RecyclerView.LayoutParams.WRAP_CONTENT
)
}
fun bind(model: T) {
baseBinding.root.setOnClickListener { listener.onClick(model) }
onBind(model)
baseBinding.executePendingBindings()
}
abstract fun onBind(model: T)
}

View file

@ -2,6 +2,11 @@ package org.oxycblt.auxio.theme
import android.content.Context import android.content.Context
import android.graphics.drawable.ColorDrawable import android.graphics.drawable.ColorDrawable
import android.text.SpannableString
import android.text.style.ForegroundColorSpan
import android.util.TypedValue
import android.view.MenuItem
import androidx.annotation.AttrRes
import androidx.annotation.ColorInt import androidx.annotation.ColorInt
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.graphics.ColorUtils import androidx.core.graphics.ColorUtils
@ -60,6 +65,30 @@ fun Int.toColor(context: Context): Int {
} }
} }
@ColorInt
fun resolveAttr(context: Context, @AttrRes attr: Int): Int {
// Convert the attribute into its color
val resolvedAttr = TypedValue().apply {
context.theme.resolveAttribute(attr, this, true)
}
// Then convert it to a proper color
val color = if (resolvedAttr.resourceId != 0) {
resolvedAttr.resourceId
} else {
resolvedAttr.data
}
return color.toColor(context)
}
fun MenuItem.applyColor(color: Int) {
SpannableString(title).apply {
setSpan(ForegroundColorSpan(color), 0, length, SpannableString.SPAN_EXCLUSIVE_EXCLUSIVE)
title = this
}
}
// Apply a custom vertical divider // Apply a custom vertical divider
fun RecyclerView.applyDivider() { fun RecyclerView.applyDivider() {
val div = DividerItemDecoration( val div = DividerItemDecoration(

View file

@ -16,6 +16,7 @@
android:layout_height="?android:attr/actionBarSize" android:layout_height="?android:attr/actionBarSize"
android:background="?android:attr/windowBackground" android:background="?android:attr/windowBackground"
android:elevation="@dimen/elevation_normal" android:elevation="@dimen/elevation_normal"
app:popupTheme="@style/AppThemeOverlay.Popup"
app:titleTextAppearance="@style/TextAppearance.Toolbar.Bold" app:titleTextAppearance="@style/TextAppearance.Toolbar.Bold"
app:title="@string/title_library_fragment" /> app:title="@string/title_library_fragment" />

View file

@ -5,6 +5,7 @@
<item name="android:windowBackground">@color/background</item> <item name="android:windowBackground">@color/background</item>
<item name="android:statusBarColor">@android:color/black</item> <item name="android:statusBarColor">@android:color/black</item>
<item name="android:fontFamily">@font/inter</item> <item name="android:fontFamily">@font/inter</item>
<item name="actionBarPopupTheme">@style/AppThemeOverlay.Popup</item>
</style> </style>
<style name="TextAppearance.Toolbar.Bold" parent="TextAppearance.Widget.AppCompat.Toolbar.Title"> <style name="TextAppearance.Toolbar.Bold" parent="TextAppearance.Widget.AppCompat.Toolbar.Title">
@ -18,4 +19,9 @@
<item name="android:fontFamily">@font/inter_black</item> <item name="android:fontFamily">@font/inter_black</item>
<item name="android:textSize">@dimen/detail_header_size_max</item> <item name="android:textSize">@dimen/detail_header_size_max</item>
</style> </style>
<style name="AppThemeOverlay.Popup" parent="ThemeOverlay.AppCompat.DayNight">
<item name="android:colorBackground">@color/background</item>
<item name="colorControlHighlight">@color/selection_color</item>
</style>
</resources> </resources>