Add accent customization
Add the ability to customize the accent.
This commit is contained in:
parent
2dc7ba3420
commit
a5f59858bd
15 changed files with 272 additions and 19 deletions
|
@ -94,6 +94,8 @@ dependencies {
|
|||
// Fast-Scroll
|
||||
implementation 'com.reddit:indicator-fast-scroll:1.3.0'
|
||||
|
||||
implementation 'com.afollestad.material-dialogs:core:3.3.0'
|
||||
|
||||
// --- DEV ---
|
||||
|
||||
// Lint
|
||||
|
|
4
app/proguard-rules.pro
vendored
4
app/proguard-rules.pro
vendored
|
@ -20,4 +20,6 @@
|
|||
# hide the original source file name.
|
||||
#-renamesourcefileattribute SourceFile
|
||||
|
||||
-dontobfuscate
|
||||
-dontobfuscate
|
||||
|
||||
-keep class org.oxycblt.auxio.settings.SettingListFragment
|
|
@ -22,6 +22,8 @@ class MainActivity : AppCompatActivity(R.layout.activity_main), SettingsManager.
|
|||
settingsManager.getTheme()
|
||||
)
|
||||
|
||||
accent = settingsManager.getAccent()
|
||||
|
||||
// Apply the theme
|
||||
setTheme(accent.second)
|
||||
|
||||
|
@ -50,7 +52,11 @@ class MainActivity : AppCompatActivity(R.layout.activity_main), SettingsManager.
|
|||
SettingsManager.getInstance().removeCallback(this)
|
||||
}
|
||||
|
||||
override fun onThemeUpdate(value: Int) {
|
||||
AppCompatDelegate.setDefaultNightMode(value)
|
||||
override fun onThemeUpdate(newTheme: Int) {
|
||||
AppCompatDelegate.setDefaultNightMode(newTheme)
|
||||
}
|
||||
|
||||
override fun onAccentUpdate(newAccent: Pair<Int, Int>) {
|
||||
recreate()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,11 +1,99 @@
|
|||
package org.oxycblt.auxio.settings
|
||||
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.view.View
|
||||
import androidx.preference.Preference
|
||||
import androidx.preference.PreferenceFragmentCompat
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.afollestad.materialdialogs.MaterialDialog
|
||||
import com.afollestad.materialdialogs.customview.customView
|
||||
import org.oxycblt.auxio.R
|
||||
import org.oxycblt.auxio.settings.adapters.AccentAdapter
|
||||
import org.oxycblt.auxio.ui.ACCENTS
|
||||
import org.oxycblt.auxio.ui.accent
|
||||
import org.oxycblt.auxio.ui.getAccentItemSummary
|
||||
|
||||
class SettingListFragment : PreferenceFragmentCompat(), SettingsManager.Callback {
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
findPreference<Preference>(SettingsManager.Keys.KEY_ACCENT)?.apply {
|
||||
onPreferenceClickListener = Preference.OnPreferenceClickListener {
|
||||
showAccentDialog()
|
||||
true
|
||||
}
|
||||
|
||||
summary = getAccentItemSummary(requireActivity(), accent)
|
||||
}
|
||||
|
||||
Log.d(this::class.simpleName, "Fragment created.")
|
||||
}
|
||||
|
||||
class SettingListFragment : PreferenceFragmentCompat() {
|
||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||
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() {
|
||||
MaterialDialog(requireActivity()).show {
|
||||
title(R.string.label_settings_accent)
|
||||
|
||||
// Roll my own RecyclerView since [To no surprise whatsoever] Material Dialogs
|
||||
// has a bug where ugly dividers will show with the RecyclerView even if you disable them.
|
||||
// This is why I hate using third party libraries.
|
||||
val recycler = RecyclerView(requireContext()).apply {
|
||||
adapter = AccentAdapter {
|
||||
if (it.first != accent.first) {
|
||||
SettingsManager.getInstance().setAccent(it)
|
||||
}
|
||||
|
||||
this@show.dismiss()
|
||||
}
|
||||
|
||||
post {
|
||||
// Combine the width of the recyclerview with the width of an item in order
|
||||
// to center the currently selected accent.
|
||||
val childWidth = getChildAt(0).width / 2
|
||||
|
||||
(layoutManager as LinearLayoutManager)
|
||||
.scrollToPositionWithOffset(
|
||||
ACCENTS.indexOf(accent),
|
||||
(width / 2) - childWidth
|
||||
)
|
||||
}
|
||||
|
||||
layoutManager = LinearLayoutManager(
|
||||
requireContext()
|
||||
).also { it.orientation = LinearLayoutManager.HORIZONTAL }
|
||||
}
|
||||
|
||||
customView(view = recycler)
|
||||
|
||||
view.invalidateDividers(showTop = false, showBottom = false)
|
||||
|
||||
negativeButton(android.R.string.cancel)
|
||||
|
||||
show()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onAccentUpdate(newAccent: Pair<Int, Int>) {
|
||||
findPreference<Preference>(getString(R.string.label_settings_accent))?.apply {
|
||||
summary = getAccentItemSummary(requireActivity(), accent)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import android.content.SharedPreferences
|
|||
import androidx.appcompat.app.AppCompatDelegate
|
||||
import androidx.preference.PreferenceManager
|
||||
import org.oxycblt.auxio.recycler.SortMode
|
||||
import org.oxycblt.auxio.ui.ACCENTS
|
||||
|
||||
/**
|
||||
* Wrapper around the [SharedPreferences] class that writes & reads values without a context.
|
||||
|
@ -30,6 +31,39 @@ class SettingsManager private constructor(context: Context) : SharedPreferences.
|
|||
callbacks.remove(callback)
|
||||
}
|
||||
|
||||
fun getTheme(): Int {
|
||||
// Turn the string from SharedPreferences into an actual theme value that can
|
||||
// be used, as apparently the preference system provided by androidx doesn't like integers
|
||||
// for some...reason.
|
||||
return when (sharedPrefs.getString(Keys.KEY_THEME, Theme.THEME_AUTO)) {
|
||||
Theme.THEME_AUTO -> AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM
|
||||
Theme.THEME_LIGHT -> AppCompatDelegate.MODE_NIGHT_NO
|
||||
Theme.THEME_DARK -> AppCompatDelegate.MODE_NIGHT_YES
|
||||
|
||||
else -> AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM
|
||||
}
|
||||
}
|
||||
|
||||
fun getAccent(): Pair<Int, Int> {
|
||||
val accentIndex = sharedPrefs.getInt(Keys.KEY_ACCENT, 5)
|
||||
|
||||
return ACCENTS[accentIndex]
|
||||
}
|
||||
|
||||
fun setAccent(accent: Pair<Int, Int>) {
|
||||
val accentIndex = ACCENTS.indexOf(accent)
|
||||
|
||||
check(accentIndex != -1) { "Invalid accent" }
|
||||
|
||||
sharedPrefs.edit()
|
||||
.putInt(Keys.KEY_ACCENT, accentIndex)
|
||||
.apply()
|
||||
|
||||
callbacks.forEach {
|
||||
it.onAccentUpdate(getAccent())
|
||||
}
|
||||
}
|
||||
|
||||
fun setLibrarySortMode(sortMode: SortMode) {
|
||||
sharedPrefs.edit()
|
||||
.putInt(Keys.KEY_LIBRARY_SORT_MODE, sortMode.toConstant())
|
||||
|
@ -45,18 +79,7 @@ class SettingsManager private constructor(context: Context) : SharedPreferences.
|
|||
) ?: SortMode.ALPHA_DOWN
|
||||
}
|
||||
|
||||
fun getTheme(): Int {
|
||||
// Turn the string from SharedPreferences into an actual theme value that can
|
||||
// be used, as apparently the preference system provided by androidx doesn't like integers
|
||||
// for some...reason.
|
||||
return when (sharedPrefs.getString(Keys.KEY_THEME, Theme.THEME_AUTO)) {
|
||||
Theme.THEME_AUTO -> AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM
|
||||
Theme.THEME_LIGHT -> AppCompatDelegate.MODE_NIGHT_NO
|
||||
Theme.THEME_DARK -> AppCompatDelegate.MODE_NIGHT_YES
|
||||
|
||||
else -> AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM
|
||||
}
|
||||
}
|
||||
// --- OVERRIDES ---
|
||||
|
||||
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) {
|
||||
when (key) {
|
||||
|
@ -100,6 +123,7 @@ class SettingsManager private constructor(context: Context) : SharedPreferences.
|
|||
object Keys {
|
||||
const val KEY_LIBRARY_SORT_MODE = "KEY_LIBRARY_SORT_MODE"
|
||||
const val KEY_THEME = "KEY_THEME"
|
||||
const val KEY_ACCENT = "KEY_ACCENT"
|
||||
}
|
||||
|
||||
private object Theme {
|
||||
|
@ -113,6 +137,7 @@ class SettingsManager private constructor(context: Context) : SharedPreferences.
|
|||
* [SharedPreferences.OnSharedPreferenceChangeListener].
|
||||
*/
|
||||
interface Callback {
|
||||
fun onThemeUpdate(value: Int) {}
|
||||
fun onThemeUpdate(newTheme: Int) {}
|
||||
fun onAccentUpdate(newAccent: Pair<Int, Int>) {}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
package org.oxycblt.auxio.settings.adapters
|
||||
|
||||
import android.content.res.ColorStateList
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import org.oxycblt.auxio.R
|
||||
import org.oxycblt.auxio.databinding.ItemAccentBinding
|
||||
import org.oxycblt.auxio.ui.ACCENTS
|
||||
import org.oxycblt.auxio.ui.accent
|
||||
import org.oxycblt.auxio.ui.toColor
|
||||
|
||||
class AccentAdapter(
|
||||
private val doOnAccentConfirm: (accent: Pair<Int, Int>) -> Unit
|
||||
) : RecyclerView.Adapter<AccentAdapter.ViewHolder>() {
|
||||
override fun getItemCount(): Int = ACCENTS.size
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
||||
return ViewHolder(ItemAccentBinding.inflate(LayoutInflater.from(parent.context)))
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||
holder.bind(ACCENTS[position])
|
||||
}
|
||||
|
||||
inner class ViewHolder(
|
||||
private val binding: ItemAccentBinding
|
||||
) : RecyclerView.ViewHolder(binding.root) {
|
||||
|
||||
fun bind(accentData: Pair<Int, Int>) {
|
||||
binding.accent.setOnClickListener {
|
||||
Log.d(this::class.simpleName, accentData.toString())
|
||||
doOnAccentConfirm(accentData)
|
||||
}
|
||||
|
||||
binding.accent.apply {
|
||||
imageTintList = if (accentData.first != accent.first) {
|
||||
ColorStateList.valueOf(
|
||||
android.R.color.transparent.toColor(context)
|
||||
)
|
||||
} else {
|
||||
ColorStateList.valueOf(
|
||||
R.color.background.toColor(context)
|
||||
)
|
||||
}
|
||||
|
||||
backgroundTintList = ColorStateList.valueOf(
|
||||
accentData.first.toColor(context)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -8,11 +8,12 @@ import androidx.annotation.ColorRes
|
|||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.graphics.ColorUtils
|
||||
import org.oxycblt.auxio.R
|
||||
import java.util.Locale
|
||||
|
||||
// Functions for managing colors/accents/whatever.
|
||||
|
||||
// Pairs of the base accent and its theme
|
||||
private val ACCENTS = listOf(
|
||||
val ACCENTS = listOf(
|
||||
Pair(R.color.red, R.style.Theme_Red), // 0
|
||||
Pair(R.color.pink, R.style.Theme_Pink), // 1
|
||||
Pair(R.color.purple, R.style.Theme_Purple), // 2
|
||||
|
@ -34,7 +35,7 @@ private val ACCENTS = listOf(
|
|||
Pair(R.color.blue_grey, R.style.Theme_BlueGrey) // 18
|
||||
)
|
||||
|
||||
val accent = ACCENTS[5]
|
||||
lateinit var accent: Pair<Int, Int>
|
||||
|
||||
/**
|
||||
* Gets the transparent form of a color.
|
||||
|
@ -96,3 +97,8 @@ fun resolveAttr(context: Context, @AttrRes attr: Int): Int {
|
|||
|
||||
return color.toColor(context)
|
||||
}
|
||||
|
||||
fun getAccentItemSummary(context: Context, newAccent: Pair<Int, Int>): String {
|
||||
return context.resources.getResourceEntryName(newAccent.first)
|
||||
.replace("_", " ").capitalize(Locale.getDefault())
|
||||
}
|
||||
|
|
10
app/src/main/res/drawable/ic_accent.xml
Normal file
10
app/src/main/res/drawable/ic_accent.xml
Normal file
|
@ -0,0 +1,10 @@
|
|||
<?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">
|
||||
<path
|
||||
android:fillColor="?attr/colorPrimary"
|
||||
android:pathData="M12,3c-4.97,0 -9,4.03 -9,9s4.03,9 9,9c0.83,0 1.5,-0.67 1.5,-1.5 0,-0.39 -0.15,-0.74 -0.39,-1.01 -0.23,-0.26 -0.38,-0.61 -0.38,-0.99 0,-0.83 0.67,-1.5 1.5,-1.5L16,16c2.76,0 5,-2.24 5,-5 0,-4.42 -4.03,-8 -9,-8zM6.5,12c-0.83,0 -1.5,-0.67 -1.5,-1.5S5.67,9 6.5,9 8,9.67 8,10.5 7.33,12 6.5,12zM9.5,8C8.67,8 8,7.33 8,6.5S8.67,5 9.5,5s1.5,0.67 1.5,1.5S10.33,8 9.5,8zM14.5,8c-0.83,0 -1.5,-0.67 -1.5,-1.5S13.67,5 14.5,5s1.5,0.67 1.5,1.5S15.33,8 14.5,8zM17.5,12c-0.83,0 -1.5,-0.67 -1.5,-1.5S16.67,9 17.5,9s1.5,0.67 1.5,1.5 -0.67,1.5 -1.5,1.5z" />
|
||||
</vector>
|
11
app/src/main/res/drawable/ic_check.xml
Normal file
11
app/src/main/res/drawable/ic_check.xml
Normal 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="@color/background">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M9,16.17L4.83,12l-1.42,1.41L9,19 21,7l-1.41,-1.41z"/>
|
||||
</vector>
|
9
app/src/main/res/drawable/ui_circle.xml
Normal file
9
app/src/main/res/drawable/ui_circle.xml
Normal file
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape android:shape="oval"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<size
|
||||
android:width="48dp"
|
||||
android:height="48dp" />
|
||||
<solid
|
||||
android:color="@android:color/white" />
|
||||
</shape>
|
24
app/src/main/res/layout/item_accent.xml
Normal file
24
app/src/main/res/layout/item_accent.xml
Normal file
|
@ -0,0 +1,24 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
<data>
|
||||
|
||||
</data>
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="@dimen/margin_mid_small">
|
||||
<ImageButton
|
||||
android:id="@+id/accent"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="@dimen/margin_medium"
|
||||
android:scaleType="fitCenter"
|
||||
android:background="@drawable/ui_circular_button"
|
||||
android:src="@drawable/ic_check"
|
||||
tools:backgroundTint="?attr/colorPrimary"
|
||||
tools:ignore="contentDescription" />
|
||||
|
||||
</FrameLayout>
|
||||
</layout>
|
|
@ -5,6 +5,7 @@
|
|||
<item>@string/label_settings_theme_light</item>
|
||||
<item>@string/label_settings_theme_dark</item>
|
||||
</array>
|
||||
|
||||
<array name="theme_values">
|
||||
<item>AUTO</item>
|
||||
<item>LIGHT</item>
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
<string name="label_settings_theme_light">Light</string>
|
||||
<string name="label_settings_theme_dark">Dark</string>
|
||||
<string name="label_settings_theme_choose">Choose theme</string>
|
||||
<string name="label_settings_accent">Accent</string>
|
||||
|
||||
<!-- Debug Namespace | Debug labels -->
|
||||
<string name="debug_state_saved">State saved</string>
|
||||
|
|
|
@ -13,6 +13,12 @@
|
|||
<item name="colorControlNormal">@color/control_color</item>
|
||||
<item name="alertDialogTheme">@style/Theme.CustomDialog</item>
|
||||
<item name="indicatorFastScrollerStyle">@style/FastScrollTheme</item>
|
||||
|
||||
<item name="md_divider_color">@android:color/transparent</item>
|
||||
<item name="md_background_color">@color/background</item>
|
||||
<item name="md_corner_radius">0dp</item>
|
||||
<item name="md_color_button_text">@color/control_color</item>
|
||||
<item name="md_font_title">@font/inter_black</item>
|
||||
</style>
|
||||
|
||||
<!-- Hack to fix the weird icon/underline with LibraryFragment's SearchView -->
|
||||
|
@ -56,6 +62,7 @@
|
|||
<item name="android:windowTitleStyle">@style/TextAppearance.Dialog.Title</item>
|
||||
<item name="colorPrimary">@color/control_color</item>
|
||||
<item name="colorSecondary">@color/control_color</item>
|
||||
<item name="dialogCornerRadius">0dp</item>
|
||||
</style>
|
||||
|
||||
<style name="TextAppearance.Dialog.Title" parent="@android:style/TextAppearance.Material.Title">
|
||||
|
|
|
@ -11,5 +11,12 @@
|
|||
android:entries="@array/theme_entries"
|
||||
android:entryValues="@array/theme_values"
|
||||
app:useSimpleSummaryProvider="true" />
|
||||
|
||||
<Preference
|
||||
app:key="KEY_ACCENT"
|
||||
android:title="@string/label_settings_accent"
|
||||
android:icon="@drawable/ic_accent"
|
||||
app:summary="@string/label_settings_accent"
|
||||
app:enableCopying="true" />
|
||||
</PreferenceCategory>
|
||||
</PreferenceScreen>
|
Loading…
Reference in a new issue