Improve settings

Improve how the current settings look/function, and also how settings work in the background.
This commit is contained in:
OxygenCobalt 2020-11-29 10:52:28 -07:00
parent a5f59858bd
commit e15df4ce73
14 changed files with 158 additions and 53 deletions

View file

@ -14,7 +14,7 @@ import org.oxycblt.auxio.ui.accent
// lead to nothing being displayed [Possibly Un-fixable] // lead to nothing being displayed [Possibly Un-fixable]
// TODO: Landscape UI layouts // TODO: Landscape UI layouts
// FIXME: Compat issue with Versions 5 that leads to progress bar looking off // FIXME: Compat issue with Versions 5 that leads to progress bar looking off
class MainActivity : AppCompatActivity(R.layout.activity_main), SettingsManager.Callback { class MainActivity : AppCompatActivity(R.layout.activity_main) {
override fun onCreateView(name: String, context: Context, attrs: AttributeSet): View? { override fun onCreateView(name: String, context: Context, attrs: AttributeSet): View? {
val settingsManager = SettingsManager.init(applicationContext) val settingsManager = SettingsManager.init(applicationContext)
@ -38,25 +38,11 @@ class MainActivity : AppCompatActivity(R.layout.activity_main), SettingsManager.
} }
} }
override fun onResume() { fun doThemeRecreate(newTheme: Int) {
super.onResume()
// Perform callback additions/removals in onPause/onResume so that they are always
// ran when the activity is recreated.
SettingsManager.getInstance().addCallback(this)
}
override fun onPause() {
super.onPause()
SettingsManager.getInstance().removeCallback(this)
}
override fun onThemeUpdate(newTheme: Int) {
AppCompatDelegate.setDefaultNightMode(newTheme) AppCompatDelegate.setDefaultNightMode(newTheme)
} }
override fun onAccentUpdate(newAccent: Pair<Int, Int>) { fun doAccentRecreate() {
recreate() recreate()
} }
} }

View file

@ -11,7 +11,7 @@ enum class LoopMode {
} }
} }
fun toConstant(): Int { fun toInt(): Int {
return when (this) { return when (this) {
NONE -> CONSTANT_NONE NONE -> CONSTANT_NONE
ONCE -> CONSTANT_ONCE ONCE -> CONSTANT_ONCE
@ -24,7 +24,7 @@ enum class LoopMode {
const val CONSTANT_ONCE = 0xA051 const val CONSTANT_ONCE = 0xA051
const val CONSTANT_INFINITE = 0xA052 const val CONSTANT_INFINITE = 0xA052
fun fromConstant(constant: Int): LoopMode? { fun fromInt(constant: Int): LoopMode? {
return when (constant) { return when (constant) {
CONSTANT_NONE -> NONE CONSTANT_NONE -> NONE
CONSTANT_ONCE -> ONCE CONSTANT_ONCE -> ONCE

View file

@ -7,7 +7,7 @@ package org.oxycblt.auxio.playback.state
enum class PlaybackMode { enum class PlaybackMode {
IN_ARTIST, IN_GENRE, IN_ALBUM, ALL_SONGS; IN_ARTIST, IN_GENRE, IN_ALBUM, ALL_SONGS;
fun toConstant(): Int { fun toInt(): Int {
return when (this) { return when (this) {
IN_ARTIST -> CONSTANT_IN_ARTIST IN_ARTIST -> CONSTANT_IN_ARTIST
IN_GENRE -> CONSTANT_IN_GENRE IN_GENRE -> CONSTANT_IN_GENRE
@ -22,7 +22,7 @@ enum class PlaybackMode {
const val CONSTANT_IN_ALBUM = 0xA042 const val CONSTANT_IN_ALBUM = 0xA042
const val CONSTANT_ALL_SONGS = 0xA043 const val CONSTANT_ALL_SONGS = 0xA043
fun fromConstant(constant: Int): PlaybackMode? { fun fromInt(constant: Int): PlaybackMode? {
return when (constant) { return when (constant) {
CONSTANT_IN_ARTIST -> IN_ARTIST CONSTANT_IN_ARTIST -> IN_ARTIST
CONSTANT_IN_ALBUM -> IN_ALBUM CONSTANT_IN_ALBUM -> IN_ALBUM

View file

@ -511,8 +511,8 @@ class PlaybackStateManager private constructor() {
private fun packToPlaybackState(): PlaybackState { private fun packToPlaybackState(): PlaybackState {
val songId = mSong?.id ?: -1L val songId = mSong?.id ?: -1L
val parentId = mParent?.id ?: -1L val parentId = mParent?.id ?: -1L
val intMode = mMode.toConstant() val intMode = mMode.toInt()
val intLoopMode = mLoopMode.toConstant() val intLoopMode = mLoopMode.toInt()
return PlaybackState( return PlaybackState(
songId = songId, songId = songId,
@ -552,8 +552,8 @@ class PlaybackStateManager private constructor() {
mSong = musicStore.songs.find { it.id == playbackState.songId } mSong = musicStore.songs.find { it.id == playbackState.songId }
mPosition = playbackState.position mPosition = playbackState.position
mParent = musicStore.parents.find { it.id == playbackState.parentId } mParent = musicStore.parents.find { it.id == playbackState.parentId }
mMode = PlaybackMode.fromConstant(playbackState.mode) ?: PlaybackMode.ALL_SONGS mMode = PlaybackMode.fromInt(playbackState.mode) ?: PlaybackMode.ALL_SONGS
mLoopMode = LoopMode.fromConstant(playbackState.loopMode) ?: LoopMode.NONE mLoopMode = LoopMode.fromInt(playbackState.loopMode) ?: LoopMode.NONE
mIsShuffling = playbackState.isShuffling mIsShuffling = playbackState.isShuffling
mShuffleSeed = playbackState.shuffleSeed mShuffleSeed = playbackState.shuffleSeed
mIsInUserQueue = playbackState.inUserQueue mIsInUserQueue = playbackState.inUserQueue

View file

@ -95,7 +95,7 @@ enum class SortMode(val iconRes: Int) {
} }
} }
fun toConstant(): Int { fun toInt(): Int {
return when (this) { return when (this) {
NONE -> CONSTANT_NONE NONE -> CONSTANT_NONE
ALPHA_UP -> CONSTANT_ALPHA_UP ALPHA_UP -> CONSTANT_ALPHA_UP
@ -112,8 +112,8 @@ enum class SortMode(val iconRes: Int) {
const val CONSTANT_NUMERIC_UP = 0xA063 const val CONSTANT_NUMERIC_UP = 0xA063
const val CONSTANT_NUMERIC_DOWN = 0xA065 const val CONSTANT_NUMERIC_DOWN = 0xA065
fun fromConstant(constant: Int): SortMode? { fun fromInt(value: Int): SortMode? {
return when (constant) { return when (value) {
CONSTANT_NONE -> NONE CONSTANT_NONE -> NONE
CONSTANT_ALPHA_UP -> ALPHA_UP CONSTANT_ALPHA_UP -> ALPHA_UP
CONSTANT_ALPHA_DOWN -> ALPHA_DOWN CONSTANT_ALPHA_DOWN -> ALPHA_DOWN

View file

@ -3,30 +3,74 @@ package org.oxycblt.auxio.settings
import android.os.Bundle import android.os.Bundle
import android.util.Log import android.util.Log
import android.view.View import android.view.View
import androidx.appcompat.app.AppCompatDelegate
import androidx.fragment.app.activityViewModels
import androidx.preference.Preference import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat import androidx.preference.PreferenceFragmentCompat
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.afollestad.materialdialogs.MaterialDialog import com.afollestad.materialdialogs.MaterialDialog
import com.afollestad.materialdialogs.customview.customView import com.afollestad.materialdialogs.customview.customView
import org.oxycblt.auxio.MainActivity
import org.oxycblt.auxio.R import org.oxycblt.auxio.R
import org.oxycblt.auxio.settings.adapters.AccentAdapter import org.oxycblt.auxio.settings.adapters.AccentAdapter
import org.oxycblt.auxio.ui.ACCENTS import org.oxycblt.auxio.ui.ACCENTS
import org.oxycblt.auxio.ui.accent import org.oxycblt.auxio.ui.accent
import org.oxycblt.auxio.ui.getAccentItemSummary import org.oxycblt.auxio.ui.getDetailedAccentSummary
class SettingListFragment : PreferenceFragmentCompat(), SettingsManager.Callback { class SettingListFragment : PreferenceFragmentCompat() {
private val settingsModel: SettingsViewModel by activityViewModels()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
findPreference<Preference>(SettingsManager.Keys.KEY_ACCENT)?.apply { val themePref = findPreference<Preference>(SettingsManager.Keys.KEY_THEME)?.apply {
setIcon(
when (AppCompatDelegate.getDefaultNightMode()) {
AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM -> R.drawable.ic_auto
AppCompatDelegate.MODE_NIGHT_NO -> R.drawable.ic_day
AppCompatDelegate.MODE_NIGHT_YES -> R.drawable.ic_night
else -> R.drawable.ic_auto
}
)
}
val accentPref = findPreference<Preference>(SettingsManager.Keys.KEY_ACCENT)?.apply {
onPreferenceClickListener = Preference.OnPreferenceClickListener { onPreferenceClickListener = Preference.OnPreferenceClickListener {
showAccentDialog() showAccentDialog()
true true
} }
summary = getAccentItemSummary(requireActivity(), accent) summary = getDetailedAccentSummary(requireActivity(), accent)
}
settingsModel.theme.observe(viewLifecycleOwner) {
if (it != null) {
themePref?.setIcon(
when (it) {
AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM -> R.drawable.ic_auto
AppCompatDelegate.MODE_NIGHT_NO -> R.drawable.ic_day
AppCompatDelegate.MODE_NIGHT_YES -> R.drawable.ic_night
else -> R.drawable.ic_auto
}
)
(requireActivity() as MainActivity).doThemeRecreate(it)
settingsModel.doneWithThemeUpdate()
}
}
settingsModel.accent.observe(viewLifecycleOwner) {
if (it != null) {
accentPref?.summary = getDetailedAccentSummary(requireActivity(), it)
(requireActivity() as MainActivity).doAccentRecreate()
settingsModel.doneWithAccentUpdate()
}
} }
Log.d(this::class.simpleName, "Fragment created.") Log.d(this::class.simpleName, "Fragment created.")
@ -36,18 +80,6 @@ class SettingListFragment : PreferenceFragmentCompat(), SettingsManager.Callback
setPreferencesFromResource(R.xml.prefs_main, rootKey) setPreferencesFromResource(R.xml.prefs_main, rootKey)
} }
override fun onResume() {
super.onResume()
SettingsManager.getInstance().addCallback(this)
}
override fun onPause() {
super.onPause()
SettingsManager.getInstance().removeCallback(this)
}
private fun showAccentDialog() { private fun showAccentDialog() {
MaterialDialog(requireActivity()).show { MaterialDialog(requireActivity()).show {
title(R.string.label_settings_accent) title(R.string.label_settings_accent)
@ -90,10 +122,4 @@ class SettingListFragment : PreferenceFragmentCompat(), SettingsManager.Callback
show() show()
} }
} }
override fun onAccentUpdate(newAccent: Pair<Int, Int>) {
findPreference<Preference>(getString(R.string.label_settings_accent))?.apply {
summary = getAccentItemSummary(requireActivity(), accent)
}
}
} }

View file

@ -66,12 +66,12 @@ class SettingsManager private constructor(context: Context) : SharedPreferences.
fun setLibrarySortMode(sortMode: SortMode) { fun setLibrarySortMode(sortMode: SortMode) {
sharedPrefs.edit() sharedPrefs.edit()
.putInt(Keys.KEY_LIBRARY_SORT_MODE, sortMode.toConstant()) .putInt(Keys.KEY_LIBRARY_SORT_MODE, sortMode.toInt())
.apply() .apply()
} }
fun getLibrarySortMode(): SortMode { fun getLibrarySortMode(): SortMode {
return SortMode.fromConstant( return SortMode.fromInt(
sharedPrefs.getInt( sharedPrefs.getInt(
Keys.KEY_LIBRARY_SORT_MODE, Keys.KEY_LIBRARY_SORT_MODE,
SortMode.CONSTANT_ALPHA_DOWN SortMode.CONSTANT_ALPHA_DOWN

View file

@ -0,0 +1,45 @@
package org.oxycblt.auxio.settings
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
class SettingsViewModel : ViewModel(), SettingsManager.Callback {
private val mTheme = MutableLiveData<Int?>()
val theme: LiveData<Int?> get() = mTheme
private val mAccent = MutableLiveData<Pair<Int, Int>?>()
val accent: LiveData<Pair<Int, Int>?> get() = mAccent
private val settingsManager = SettingsManager.getInstance()
init {
settingsManager.addCallback(this)
}
fun doneWithThemeUpdate() {
mTheme.value = null
}
fun doneWithAccentUpdate() {
mAccent.value = null
}
override fun onThemeUpdate(newTheme: Int) {
super.onThemeUpdate(newTheme)
mTheme.value = newTheme
}
override fun onAccentUpdate(newAccent: Pair<Int, Int>) {
super.onAccentUpdate(newAccent)
mAccent.value = newAccent
}
override fun onCleared() {
super.onCleared()
settingsManager.removeCallback(this)
}
}

View file

@ -3,6 +3,7 @@ package org.oxycblt.auxio.ui
import android.content.Context import android.content.Context
import android.content.res.ColorStateList import android.content.res.ColorStateList
import android.text.SpannableString import android.text.SpannableString
import android.text.Spanned
import android.text.style.ForegroundColorSpan import android.text.style.ForegroundColorSpan
import android.view.MenuItem import android.view.MenuItem
import android.widget.ImageButton import android.widget.ImageButton
@ -10,6 +11,7 @@ import android.widget.Toast
import androidx.annotation.ColorInt import androidx.annotation.ColorInt
import androidx.annotation.MenuRes import androidx.annotation.MenuRes
import androidx.appcompat.widget.PopupMenu import androidx.appcompat.widget.PopupMenu
import androidx.core.text.HtmlCompat
import org.oxycblt.auxio.R import org.oxycblt.auxio.R
import org.oxycblt.auxio.detail.DetailViewModel import org.oxycblt.auxio.detail.DetailViewModel
import org.oxycblt.auxio.music.Album import org.oxycblt.auxio.music.Album
@ -54,6 +56,16 @@ fun String.createToast(context: Context) {
Toast.makeText(context.applicationContext, this, Toast.LENGTH_SHORT).show() 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
)
}
/** /**
* Show actions for a song item, such as the ones found in [org.oxycblt.auxio.songs.SongsFragment] * Show actions for a song item, such as the ones found in [org.oxycblt.auxio.songs.SongsFragment]
*/ */

View file

@ -1,12 +1,14 @@
package org.oxycblt.auxio.ui package org.oxycblt.auxio.ui
import android.content.Context import android.content.Context
import android.text.Spanned
import android.util.TypedValue import android.util.TypedValue
import androidx.annotation.AttrRes import androidx.annotation.AttrRes
import androidx.annotation.ColorInt import androidx.annotation.ColorInt
import androidx.annotation.ColorRes import androidx.annotation.ColorRes
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.graphics.ColorUtils import androidx.core.graphics.ColorUtils
import androidx.core.text.toSpanned
import org.oxycblt.auxio.R import org.oxycblt.auxio.R
import java.util.Locale import java.util.Locale
@ -102,3 +104,13 @@ fun getAccentItemSummary(context: Context, newAccent: Pair<Int, Int>): String {
return context.resources.getResourceEntryName(newAccent.first) return context.resources.getResourceEntryName(newAccent.first)
.replace("_", " ").capitalize(Locale.getDefault()) .replace("_", " ").capitalize(Locale.getDefault())
} }
fun getDetailedAccentSummary(context: Context, newAccent: Pair<Int, Int>): Spanned {
val name = getAccentItemSummary(context, newAccent)
val hex = context.getString(accent.first).toUpperCase(Locale.getDefault())
return context.getString(
R.string.format_accent_summary,
name, hex
).toSpanned().render()
}

View file

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorPrimary">
<path
android:fillColor="@android:color/white"
android:pathData="M10.85,12.65h2.3L12,9l-1.15,3.65zM20,8.69V4h-4.69L12,0.69 8.69,4H4v4.69L0.69,12 4,15.31V20h4.69L12,23.31 15.31,20H20v-4.69L23.31,12 20,8.69zM14.3,16l-0.7,-2h-3.2l-0.7,2H7.8L11,7h2l3.2,9h-1.9z"/>
</vector>

View file

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorPrimary">
<path
android:fillColor="@android:color/white"
android:pathData="M20,15.31L23.31,12 20,8.69V4h-4.69L12,0.69 8.69,4H4v4.69L0.69,12 4,15.31V20h4.69L12,23.31 15.31,20H20v-4.69zM12,18c-3.31,0 -6,-2.69 -6,-6s2.69,-6 6,-6 6,2.69 6,6 -2.69,6 -6,6z"/>
</vector>

View file

@ -30,7 +30,7 @@
<string name="label_settings">Settings</string> <string name="label_settings">Settings</string>
<string name="label_settings_ui">Appearance</string> <string name="label_settings_ui">Appearance</string>
<string name="label_settings_theme">Theme</string> <string name="label_settings_theme">Theme</string>
<string name="label_settings_theme_auto">Auto</string> <string name="label_settings_theme_auto">Automatic</string>
<string name="label_settings_theme_light">Light</string> <string name="label_settings_theme_light">Light</string>
<string name="label_settings_theme_dark">Dark</string> <string name="label_settings_theme_dark">Dark</string>
<string name="label_settings_theme_choose">Choose theme</string> <string name="label_settings_theme_choose">Choose theme</string>
@ -77,6 +77,7 @@
<string name="format_double_info">%1$s / %2$s / %3$s</string> <string name="format_double_info">%1$s / %2$s / %3$s</string>
<string name="format_double_counts">%1$s, %2$s</string> <string name="format_double_counts">%1$s, %2$s</string>
<string name="format_next_from">Next From: %s</string> <string name="format_next_from">Next From: %s</string>
<string name="format_accent_summary">&lt;b>%1s&lt;/b>:&#160;%2s</string>
<plurals name="format_song_count"> <plurals name="format_song_count">
<item quantity="one">%s Song</item> <item quantity="one">%s Song</item>

View file

@ -10,6 +10,7 @@
android:icon="@drawable/ic_day" android:icon="@drawable/ic_day"
android:entries="@array/theme_entries" android:entries="@array/theme_entries"
android:entryValues="@array/theme_values" android:entryValues="@array/theme_values"
app:defaultValue="AUTO"
app:useSimpleSummaryProvider="true" /> app:useSimpleSummaryProvider="true" />
<Preference <Preference