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.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
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
@ -20,13 +19,9 @@ import org.oxycblt.auxio.music.BaseModel
import org.oxycblt.auxio.music.Genre import org.oxycblt.auxio.music.Genre
import org.oxycblt.auxio.music.Song import org.oxycblt.auxio.music.Song
import org.oxycblt.auxio.ui.ActionMenu 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.getLandscapeSpans
import org.oxycblt.auxio.ui.isLandscape import org.oxycblt.auxio.ui.isLandscape
import org.oxycblt.auxio.ui.requireCompatActivity 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 * 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) ActionMenu(requireCompatActivity(), view, data, ActionMenu.FLAG_NONE)
} }
val sortAction = binding.libraryToolbar.menu.findItem(R.id.submenu_sorting)
// --- UI SETUP --- // --- UI SETUP ---
binding.libraryToolbar.apply { binding.libraryToolbar.apply {
menu.findItem(libraryModel.sortMode.toMenuId()).isChecked = true
setOnMenuItemClickListener { setOnMenuItemClickListener {
when (it.itemId) { when (it.itemId) {
R.id.submenu_sorting -> {} R.id.submenu_sorting -> {}
else -> { else -> {
it.isChecked = true
libraryModel.updateSortMode(it.itemId) libraryModel.updateSortMode(it.itemId)
} }
} }
@ -80,22 +76,6 @@ class LibraryFragment : Fragment() {
libraryAdapter.updateData(it) 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) { detailModel.navToItem.observe(viewLifecycleOwner) {
if (it != null) { if (it != null) {
libraryModel.updateNavigationStatus(false) libraryModel.updateNavigationStatus(false)

View file

@ -17,15 +17,15 @@ import org.oxycblt.auxio.settings.SettingsManager
* @author OxygenCobalt * @author OxygenCobalt
*/ */
class LibraryViewModel : ViewModel(), SettingsManager.Callback { class LibraryViewModel : ViewModel(), SettingsManager.Callback {
private val mSortMode = MutableLiveData(SortMode.ALPHA_DOWN)
val sortMode: LiveData<SortMode> get() = mSortMode
private val mLibraryData = MutableLiveData(listOf<BaseModel>()) private val mLibraryData = MutableLiveData(listOf<BaseModel>())
val libraryData: LiveData<List<BaseModel>> get() = mLibraryData val libraryData: LiveData<List<BaseModel>> get() = mLibraryData
private var mIsNavigating = false private var mIsNavigating = false
val isNavigating: Boolean get() = mIsNavigating val isNavigating: Boolean get() = mIsNavigating
private var mSortMode = SortMode.ALPHA_DOWN
val sortMode: SortMode get() = mSortMode
private var mDisplayMode = DisplayMode.SHOW_ARTISTS private var mDisplayMode = DisplayMode.SHOW_ARTISTS
private val settingsManager = SettingsManager.getInstance() private val settingsManager = SettingsManager.getInstance()
@ -36,7 +36,7 @@ class LibraryViewModel : ViewModel(), SettingsManager.Callback {
// Set up the display/sort modes // Set up the display/sort modes
mDisplayMode = settingsManager.libraryDisplayMode mDisplayMode = settingsManager.libraryDisplayMode
mSortMode.value = settingsManager.librarySortMode mSortMode = settingsManager.librarySortMode
updateLibraryData() updateLibraryData()
} }
@ -54,8 +54,8 @@ class LibraryViewModel : ViewModel(), SettingsManager.Callback {
else -> SortMode.NONE else -> SortMode.NONE
} }
if (mode != mSortMode.value) { if (mode != mSortMode) {
mSortMode.value = mode mSortMode = mode
settingsManager.librarySortMode = mode settingsManager.librarySortMode = mode
updateLibraryData() updateLibraryData()
@ -92,15 +92,15 @@ class LibraryViewModel : ViewModel(), SettingsManager.Callback {
private fun updateLibraryData() { private fun updateLibraryData() {
mLibraryData.value = when (mDisplayMode) { mLibraryData.value = when (mDisplayMode) {
DisplayMode.SHOW_GENRES -> { DisplayMode.SHOW_GENRES -> {
mSortMode.value!!.getSortedGenreList(musicStore.genres) mSortMode.getSortedGenreList(musicStore.genres)
} }
DisplayMode.SHOW_ARTISTS -> { DisplayMode.SHOW_ARTISTS -> {
mSortMode.value!!.getSortedBaseModelList(musicStore.artists) mSortMode.getSortedBaseModelList(musicStore.artists)
} }
DisplayMode.SHOW_ALBUMS -> { DisplayMode.SHOW_ALBUMS -> {
mSortMode.value!!.getSortedAlbumList(musicStore.albums) mSortMode.getSortedAlbumList(musicStore.albums)
} }
else -> error("DisplayMode $mDisplayMode is unsupported.") 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. * A [Fragment] that allows for the searching of the entire music library.
* TODO: Add "Recent Searches" & No Results indicator * TODO: Add "Recent Searches" & No Results indicator
* TODO: Filtering
* @author OxygenCobalt * @author OxygenCobalt
*/ */
class SearchFragment : Fragment() { 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 package org.oxycblt.auxio.ui
import android.app.Activity
import android.content.Context import android.content.Context
import android.content.res.ColorStateList import android.content.res.ColorStateList
import android.content.res.Configuration import android.content.res.Configuration
import android.content.res.Resources import android.content.res.Resources
import android.graphics.Point
import android.os.Build
import android.text.SpannableString import android.text.SpannableString
import android.text.Spanned import android.text.Spanned
import android.text.style.ForegroundColorSpan import android.text.style.ForegroundColorSpan
import android.util.DisplayMetrics
import android.view.MenuItem import android.view.MenuItem
import android.view.WindowManager
import android.widget.ImageButton import android.widget.ImageButton
import android.widget.TextView import android.widget.TextView
import android.widget.Toast import android.widget.Toast
@ -20,16 +25,7 @@ import androidx.fragment.app.Fragment
import com.google.android.material.button.MaterialButton import com.google.android.material.button.MaterialButton
import org.oxycblt.auxio.R import org.oxycblt.auxio.R
/** // --- VIEW CONFIGURATION ---
* 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
}
}
/** /**
* Disable an image button. * 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. * 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] * Require an [AppCompatActivity]
*/ */
@ -114,11 +96,81 @@ fun Fragment.requireCompatActivity(): AppCompatActivity {
} }
/** /**
* Convenience method for getting a plural. * "Render" a [Spanned] using [HtmlCompat].
* @param pluralsRes Resource for the plural * @return A [Spanned] that actually works.
* @param value Int value for the plural.
* @return The formatted string requested
*/ */
fun Context.getPlural(@PluralsRes pluralsRes: Int, value: Int): String { fun Spanned.render(): Spanned {
return resources.getQuantityString(pluralsRes, value, value) 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" android:title="@string/label_sort"
app:showAsAction="always"> app:showAsAction="always">
<menu> <menu>
<group android:id="@+id/group_sorting"> <group android:id="@+id/group_sorting"
android:checkableBehavior="single">
<item <item
android:id="@+id/option_sort_none" android:id="@+id/option_sort_none"
android:contentDescription="@string/description_sort_none" android:contentDescription="@string/description_sort_none"