Change library sort menu to checkable behavior
Switch to a checkable behavior for the library sorting menu instead of manually highlighting the menu items
This commit is contained in:
parent
eab260a9c1
commit
e236eff997
6 changed files with 116 additions and 148 deletions
|
@ -4,7 +4,6 @@ import android.os.Bundle
|
|||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.view.forEach
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.activityViewModels
|
||||
import androidx.navigation.fragment.findNavController
|
||||
|
@ -20,13 +19,9 @@ import org.oxycblt.auxio.music.BaseModel
|
|||
import org.oxycblt.auxio.music.Genre
|
||||
import org.oxycblt.auxio.music.Song
|
||||
import org.oxycblt.auxio.ui.ActionMenu
|
||||
import org.oxycblt.auxio.ui.accent
|
||||
import org.oxycblt.auxio.ui.applyColor
|
||||
import org.oxycblt.auxio.ui.getLandscapeSpans
|
||||
import org.oxycblt.auxio.ui.isLandscape
|
||||
import org.oxycblt.auxio.ui.requireCompatActivity
|
||||
import org.oxycblt.auxio.ui.resolveAttr
|
||||
import org.oxycblt.auxio.ui.toColor
|
||||
|
||||
/**
|
||||
* A [Fragment] that shows a custom list of [Genre], [Artist], or [Album] data. Also allows for
|
||||
|
@ -47,16 +42,17 @@ class LibraryFragment : Fragment() {
|
|||
ActionMenu(requireCompatActivity(), view, data, ActionMenu.FLAG_NONE)
|
||||
}
|
||||
|
||||
val sortAction = binding.libraryToolbar.menu.findItem(R.id.submenu_sorting)
|
||||
|
||||
// --- UI SETUP ---
|
||||
|
||||
binding.libraryToolbar.apply {
|
||||
menu.findItem(libraryModel.sortMode.toMenuId()).isChecked = true
|
||||
|
||||
setOnMenuItemClickListener {
|
||||
when (it.itemId) {
|
||||
R.id.submenu_sorting -> {}
|
||||
|
||||
else -> {
|
||||
it.isChecked = true
|
||||
libraryModel.updateSortMode(it.itemId)
|
||||
}
|
||||
}
|
||||
|
@ -80,22 +76,6 @@ class LibraryFragment : Fragment() {
|
|||
libraryAdapter.updateData(it)
|
||||
}
|
||||
|
||||
libraryModel.sortMode.observe(viewLifecycleOwner) { mode ->
|
||||
logD("Updating sort mode to $mode")
|
||||
|
||||
val modeId = mode.toMenuId()
|
||||
|
||||
// Highlight the item instead of using a checkable since the checkables just...wont
|
||||
// respond to any attempts to make them checked or not.
|
||||
sortAction.subMenu.forEach {
|
||||
if (it.itemId == modeId) {
|
||||
it.applyColor(accent.first.toColor(requireContext()))
|
||||
} else {
|
||||
it.applyColor(resolveAttr(requireContext(), android.R.attr.textColorPrimary))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
detailModel.navToItem.observe(viewLifecycleOwner) {
|
||||
if (it != null) {
|
||||
libraryModel.updateNavigationStatus(false)
|
||||
|
|
|
@ -17,15 +17,15 @@ import org.oxycblt.auxio.settings.SettingsManager
|
|||
* @author OxygenCobalt
|
||||
*/
|
||||
class LibraryViewModel : ViewModel(), SettingsManager.Callback {
|
||||
private val mSortMode = MutableLiveData(SortMode.ALPHA_DOWN)
|
||||
val sortMode: LiveData<SortMode> get() = mSortMode
|
||||
|
||||
private val mLibraryData = MutableLiveData(listOf<BaseModel>())
|
||||
val libraryData: LiveData<List<BaseModel>> get() = mLibraryData
|
||||
|
||||
private var mIsNavigating = false
|
||||
val isNavigating: Boolean get() = mIsNavigating
|
||||
|
||||
private var mSortMode = SortMode.ALPHA_DOWN
|
||||
val sortMode: SortMode get() = mSortMode
|
||||
|
||||
private var mDisplayMode = DisplayMode.SHOW_ARTISTS
|
||||
|
||||
private val settingsManager = SettingsManager.getInstance()
|
||||
|
@ -36,7 +36,7 @@ class LibraryViewModel : ViewModel(), SettingsManager.Callback {
|
|||
|
||||
// Set up the display/sort modes
|
||||
mDisplayMode = settingsManager.libraryDisplayMode
|
||||
mSortMode.value = settingsManager.librarySortMode
|
||||
mSortMode = settingsManager.librarySortMode
|
||||
|
||||
updateLibraryData()
|
||||
}
|
||||
|
@ -54,8 +54,8 @@ class LibraryViewModel : ViewModel(), SettingsManager.Callback {
|
|||
else -> SortMode.NONE
|
||||
}
|
||||
|
||||
if (mode != mSortMode.value) {
|
||||
mSortMode.value = mode
|
||||
if (mode != mSortMode) {
|
||||
mSortMode = mode
|
||||
settingsManager.librarySortMode = mode
|
||||
|
||||
updateLibraryData()
|
||||
|
@ -92,15 +92,15 @@ class LibraryViewModel : ViewModel(), SettingsManager.Callback {
|
|||
private fun updateLibraryData() {
|
||||
mLibraryData.value = when (mDisplayMode) {
|
||||
DisplayMode.SHOW_GENRES -> {
|
||||
mSortMode.value!!.getSortedGenreList(musicStore.genres)
|
||||
mSortMode.getSortedGenreList(musicStore.genres)
|
||||
}
|
||||
|
||||
DisplayMode.SHOW_ARTISTS -> {
|
||||
mSortMode.value!!.getSortedBaseModelList(musicStore.artists)
|
||||
mSortMode.getSortedBaseModelList(musicStore.artists)
|
||||
}
|
||||
|
||||
DisplayMode.SHOW_ALBUMS -> {
|
||||
mSortMode.value!!.getSortedAlbumList(musicStore.albums)
|
||||
mSortMode.getSortedAlbumList(musicStore.albums)
|
||||
}
|
||||
|
||||
else -> error("DisplayMode $mDisplayMode is unsupported.")
|
||||
|
|
|
@ -34,7 +34,6 @@ import org.oxycblt.auxio.ui.toColor
|
|||
/**
|
||||
* A [Fragment] that allows for the searching of the entire music library.
|
||||
* TODO: Add "Recent Searches" & No Results indicator
|
||||
* TODO: Filtering
|
||||
* @author OxygenCobalt
|
||||
*/
|
||||
class SearchFragment : Fragment() {
|
||||
|
|
|
@ -1,64 +0,0 @@
|
|||
@file:Suppress("DEPRECATION")
|
||||
@file:TargetApi(Build.VERSION_CODES.O_MR1)
|
||||
|
||||
package org.oxycblt.auxio.ui
|
||||
|
||||
import android.annotation.TargetApi
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.graphics.Point
|
||||
import android.os.Build
|
||||
import android.util.DisplayMetrics
|
||||
import android.view.WindowManager
|
||||
|
||||
/**
|
||||
* Check if we are in the "Irregular" landscape mode [e.g landscape, but nav bar is on the sides]
|
||||
* Used to disable most of edge-to-edge if that's the case, as I cant get it to work on this mode.
|
||||
* @return True if we are in the irregular landscape mode, false if not.
|
||||
*/
|
||||
fun Activity.isIrregularLandscape(): Boolean {
|
||||
return isLandscape(resources) &&
|
||||
!isSystemBarOnBottom(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if edge is on. Really a glorified version check.
|
||||
* @return Whether edge is on.
|
||||
*/
|
||||
fun isEdgeOn(): Boolean = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1
|
||||
|
||||
/**
|
||||
* Check if the system bars are on the bottom.
|
||||
* @return If the system bars are on the bottom, false if no.
|
||||
*/
|
||||
private fun isSystemBarOnBottom(activity: Activity): Boolean {
|
||||
val realPoint = Point()
|
||||
val metrics = DisplayMetrics()
|
||||
|
||||
var width = 0
|
||||
var height = 0
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||
activity.display?.let { display ->
|
||||
display.getRealSize(realPoint)
|
||||
|
||||
activity.windowManager.currentWindowMetrics.bounds.also {
|
||||
width = it.width()
|
||||
height = it.height()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
(activity.getSystemService(Context.WINDOW_SERVICE) as WindowManager).apply {
|
||||
defaultDisplay.getRealSize(realPoint)
|
||||
defaultDisplay.getMetrics(metrics)
|
||||
|
||||
width = metrics.widthPixels
|
||||
height = metrics.heightPixels
|
||||
}
|
||||
}
|
||||
|
||||
val config = activity.resources.configuration
|
||||
val canMove = (width != height && config.smallestScreenWidthDp < 600)
|
||||
|
||||
return (!canMove || width < height)
|
||||
}
|
|
@ -1,13 +1,18 @@
|
|||
package org.oxycblt.auxio.ui
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.content.res.ColorStateList
|
||||
import android.content.res.Configuration
|
||||
import android.content.res.Resources
|
||||
import android.graphics.Point
|
||||
import android.os.Build
|
||||
import android.text.SpannableString
|
||||
import android.text.Spanned
|
||||
import android.text.style.ForegroundColorSpan
|
||||
import android.util.DisplayMetrics
|
||||
import android.view.MenuItem
|
||||
import android.view.WindowManager
|
||||
import android.widget.ImageButton
|
||||
import android.widget.TextView
|
||||
import android.widget.Toast
|
||||
|
@ -20,16 +25,7 @@ import androidx.fragment.app.Fragment
|
|||
import com.google.android.material.button.MaterialButton
|
||||
import org.oxycblt.auxio.R
|
||||
|
||||
/**
|
||||
* Apply a text color to a [MenuItem]
|
||||
* @param color The text color that should be applied.
|
||||
*/
|
||||
fun MenuItem.applyColor(@ColorInt color: Int) {
|
||||
SpannableString(title).apply {
|
||||
setSpan(ForegroundColorSpan(color), 0, length, SpannableString.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||
title = this
|
||||
}
|
||||
}
|
||||
// --- VIEW CONFIGURATION ---
|
||||
|
||||
/**
|
||||
* Disable an image button.
|
||||
|
@ -44,40 +40,6 @@ fun ImageButton.disable() {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the device is currently in landscape.
|
||||
* @param resources [Resources] required
|
||||
*/
|
||||
fun isLandscape(resources: Resources): Boolean {
|
||||
return resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the span count for most RecyclerViews when in landscape mode.
|
||||
* @return 3 if landscape mode is tablet, 2 if landscape mode is phone
|
||||
*/
|
||||
fun getLandscapeSpans(resources: Resources): Int {
|
||||
return if (resources.configuration.screenLayout == Configuration.SCREENLAYOUT_SIZE_LARGE) 3 else 2
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a [Toast] from a [String]
|
||||
* @param context [Context] required to create the toast
|
||||
*/
|
||||
fun String.createToast(context: Context) {
|
||||
Toast.makeText(context.applicationContext, this, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
|
||||
/**
|
||||
* "Render" a [Spanned] using [HtmlCompat].
|
||||
* @return A [Spanned] that actually works.
|
||||
*/
|
||||
fun Spanned.render(): Spanned {
|
||||
return HtmlCompat.fromHtml(
|
||||
this.toString(), HtmlCompat.FROM_HTML_OPTION_USE_CSS_COLORS
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a [TextView] text color, without having to resolve the resource.
|
||||
*/
|
||||
|
@ -100,6 +62,26 @@ fun MaterialButton.applyAccents(highlighted: Boolean) {
|
|||
}
|
||||
}
|
||||
|
||||
// --- CONVENIENCE ---
|
||||
|
||||
/**
|
||||
* Convenience method for getting a plural.
|
||||
* @param pluralsRes Resource for the plural
|
||||
* @param value Int value for the plural.
|
||||
* @return The formatted string requested
|
||||
*/
|
||||
fun Context.getPlural(@PluralsRes pluralsRes: Int, value: Int): String {
|
||||
return resources.getQuantityString(pluralsRes, value, value)
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a [Toast] from a [String]
|
||||
* @param context [Context] required to create the toast
|
||||
*/
|
||||
fun String.createToast(context: Context) {
|
||||
Toast.makeText(context.applicationContext, this, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
|
||||
/**
|
||||
* Require an [AppCompatActivity]
|
||||
*/
|
||||
|
@ -114,11 +96,81 @@ fun Fragment.requireCompatActivity(): AppCompatActivity {
|
|||
}
|
||||
|
||||
/**
|
||||
* Convenience method for getting a plural.
|
||||
* @param pluralsRes Resource for the plural
|
||||
* @param value Int value for the plural.
|
||||
* @return The formatted string requested
|
||||
* "Render" a [Spanned] using [HtmlCompat].
|
||||
* @return A [Spanned] that actually works.
|
||||
*/
|
||||
fun Context.getPlural(@PluralsRes pluralsRes: Int, value: Int): String {
|
||||
return resources.getQuantityString(pluralsRes, value, value)
|
||||
fun Spanned.render(): Spanned {
|
||||
return HtmlCompat.fromHtml(
|
||||
this.toString(), HtmlCompat.FROM_HTML_OPTION_USE_CSS_COLORS
|
||||
)
|
||||
}
|
||||
|
||||
// --- CONFIGURATION ---
|
||||
|
||||
/**
|
||||
* Check if edge is on. Really a glorified version check.
|
||||
* @return Whether edge is on.
|
||||
*/
|
||||
fun isEdgeOn(): Boolean = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1
|
||||
|
||||
/**
|
||||
* Determine if the device is currently in landscape.
|
||||
* @param resources [Resources] required
|
||||
*/
|
||||
fun isLandscape(resources: Resources): Boolean {
|
||||
return resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the span count for most RecyclerViews when in landscape mode.
|
||||
* @return 3 if landscape mode is tablet, 2 if landscape mode is phone
|
||||
*/
|
||||
fun getLandscapeSpans(resources: Resources): Int {
|
||||
return if (resources.configuration.screenLayout == Configuration.SCREENLAYOUT_SIZE_LARGE) 3 else 2
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if we are in the "Irregular" landscape mode [e.g landscape, but nav bar is on the sides]
|
||||
* Used to disable most of edge-to-edge if that's the case, as I cant get it to work on this mode.
|
||||
* @return True if we are in the irregular landscape mode, false if not.
|
||||
*/
|
||||
fun Activity.isIrregularLandscape(): Boolean {
|
||||
return isLandscape(resources) &&
|
||||
!isSystemBarOnBottom(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the system bars are on the bottom.
|
||||
* @return If the system bars are on the bottom, false if no.
|
||||
*/
|
||||
private fun isSystemBarOnBottom(activity: Activity): Boolean {
|
||||
val realPoint = Point()
|
||||
val metrics = DisplayMetrics()
|
||||
|
||||
var width = 0
|
||||
var height = 0
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||
activity.display?.let { display ->
|
||||
display.getRealSize(realPoint)
|
||||
|
||||
activity.windowManager.currentWindowMetrics.bounds.also {
|
||||
width = it.width()
|
||||
height = it.height()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
(activity.getSystemService(Context.WINDOW_SERVICE) as WindowManager).apply {
|
||||
defaultDisplay.getRealSize(realPoint)
|
||||
defaultDisplay.getMetrics(metrics)
|
||||
|
||||
width = metrics.widthPixels
|
||||
height = metrics.heightPixels
|
||||
}
|
||||
}
|
||||
|
||||
val config = activity.resources.configuration
|
||||
val canMove = (width != height && config.smallestScreenWidthDp < 600)
|
||||
|
||||
return (!canMove || width < height)
|
||||
}
|
||||
|
|
|
@ -13,7 +13,8 @@
|
|||
android:title="@string/label_sort"
|
||||
app:showAsAction="always">
|
||||
<menu>
|
||||
<group android:id="@+id/group_sorting">
|
||||
<group android:id="@+id/group_sorting"
|
||||
android:checkableBehavior="single">
|
||||
<item
|
||||
android:id="@+id/option_sort_none"
|
||||
android:contentDescription="@string/description_sort_none"
|
||||
|
|
Loading…
Reference in a new issue