Highlight sort menu item when selected
Highlight the menu item for the currently shown order when its selected.
This commit is contained in:
parent
37c52d9e5c
commit
08f8e50bbf
10 changed files with 94 additions and 40 deletions
|
@ -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(
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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>()
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
|
@ -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)
|
|
||||||
}
|
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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" />
|
||||||
|
|
||||||
|
|
|
@ -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>
|
Loading…
Reference in a new issue