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:
OxygenCobalt 2021-01-12 16:24:30 -07:00
parent eab260a9c1
commit e236eff997
No known key found for this signature in database
GPG key ID: 37DBE3621FE9AD47
6 changed files with 116 additions and 148 deletions

View file

@ -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)

View file

@ -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.")

View file

@ -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() {

View file

@ -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)
}

View file

@ -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)
}

View file

@ -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"