Completely refactor accents

Completely refactor the accent system to be much more understandable.
This commit is contained in:
OxygenCobalt 2021-01-18 09:28:31 -07:00
parent 2889e6fdfd
commit c4bc86cb05
No known key found for this signature in database
GPG key ID: 37DBE3621FE9AD47
15 changed files with 181 additions and 184 deletions

View file

@ -11,7 +11,7 @@ import androidx.databinding.DataBindingUtil
import org.oxycblt.auxio.databinding.ActivityMainBinding import org.oxycblt.auxio.databinding.ActivityMainBinding
import org.oxycblt.auxio.playback.PlaybackService import org.oxycblt.auxio.playback.PlaybackService
import org.oxycblt.auxio.settings.SettingsManager import org.oxycblt.auxio.settings.SettingsManager
import org.oxycblt.auxio.ui.accent import org.oxycblt.auxio.ui.Accent
import org.oxycblt.auxio.ui.isEdgeOn import org.oxycblt.auxio.ui.isEdgeOn
/** /**
@ -30,10 +30,10 @@ class MainActivity : AppCompatActivity() {
AppCompatDelegate.setDefaultNightMode(settingsManager.theme) AppCompatDelegate.setDefaultNightMode(settingsManager.theme)
accent = settingsManager.accent val accent = Accent.set(settingsManager.accent)
// Apply the theme // Apply the theme
setTheme(accent.second) setTheme(accent.theme)
if (isEdgeOn()) { if (isEdgeOn()) {
doEdgeToEdgeSetup(binding) doEdgeToEdgeSetup(binding)

View file

@ -6,6 +6,7 @@ import android.view.LayoutInflater
import android.view.MenuItem import android.view.MenuItem
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.core.graphics.ColorUtils
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels import androidx.fragment.app.activityViewModels
import androidx.navigation.NavController import androidx.navigation.NavController
@ -17,8 +18,7 @@ import org.oxycblt.auxio.detail.DetailViewModel
import org.oxycblt.auxio.music.MusicStore import org.oxycblt.auxio.music.MusicStore
import org.oxycblt.auxio.music.Song import org.oxycblt.auxio.music.Song
import org.oxycblt.auxio.playback.PlaybackViewModel import org.oxycblt.auxio.playback.PlaybackViewModel
import org.oxycblt.auxio.ui.accent import org.oxycblt.auxio.ui.Accent
import org.oxycblt.auxio.ui.getTransparentAccent
import org.oxycblt.auxio.ui.isLandscape import org.oxycblt.auxio.ui.isLandscape
import org.oxycblt.auxio.ui.toColor import org.oxycblt.auxio.ui.toColor
@ -45,10 +45,8 @@ class MainFragment : Fragment() {
return null return null
} }
val colorActive = accent.first.toColor(requireContext()) val colorActive = Accent.get().color.toColor(requireContext())
val colorInactive = getTransparentAccent( val colorInactive = ColorUtils.setAlphaComponent(colorActive, 150)
requireContext(), accent.first, 150
)
// Set up the tints for the navigation icons + text // Set up the tints for the navigation icons + text
val navTints = ColorStateList( val navTints = ColorStateList(

View file

@ -16,7 +16,7 @@ import org.oxycblt.auxio.playback.PlaybackViewModel
import org.oxycblt.auxio.recycler.DiffCallback import org.oxycblt.auxio.recycler.DiffCallback
import org.oxycblt.auxio.recycler.viewholders.BaseViewHolder import org.oxycblt.auxio.recycler.viewholders.BaseViewHolder
import org.oxycblt.auxio.recycler.viewholders.Highlightable import org.oxycblt.auxio.recycler.viewholders.Highlightable
import org.oxycblt.auxio.ui.accent import org.oxycblt.auxio.ui.Accent
import org.oxycblt.auxio.ui.applyAccents import org.oxycblt.auxio.ui.applyAccents
import org.oxycblt.auxio.ui.disable import org.oxycblt.auxio.ui.disable
import org.oxycblt.auxio.ui.setTextColorResource import org.oxycblt.auxio.ui.setTextColorResource
@ -138,8 +138,10 @@ class AlbumDetailAdapter(
override fun setHighlighted(isHighlighted: Boolean) { override fun setHighlighted(isHighlighted: Boolean) {
if (isHighlighted) { if (isHighlighted) {
binding.songName.setTextColorResource(accent.first) val accent = Accent.get()
binding.songTrack.setTextColorResource(accent.first)
binding.songName.setTextColorResource(accent.color)
binding.songTrack.setTextColorResource(accent.color)
} else { } else {
binding.songName.setTextColor(normalTextColor) binding.songName.setTextColor(normalTextColor)
binding.songTrack.setTextColor(inactiveTextColor) binding.songTrack.setTextColor(inactiveTextColor)

View file

@ -16,7 +16,7 @@ import org.oxycblt.auxio.playback.PlaybackViewModel
import org.oxycblt.auxio.recycler.DiffCallback import org.oxycblt.auxio.recycler.DiffCallback
import org.oxycblt.auxio.recycler.viewholders.BaseViewHolder import org.oxycblt.auxio.recycler.viewholders.BaseViewHolder
import org.oxycblt.auxio.recycler.viewholders.Highlightable import org.oxycblt.auxio.recycler.viewholders.Highlightable
import org.oxycblt.auxio.ui.accent import org.oxycblt.auxio.ui.Accent
import org.oxycblt.auxio.ui.applyAccents import org.oxycblt.auxio.ui.applyAccents
import org.oxycblt.auxio.ui.disable import org.oxycblt.auxio.ui.disable
import org.oxycblt.auxio.ui.setTextColorResource import org.oxycblt.auxio.ui.setTextColorResource
@ -137,7 +137,7 @@ class ArtistDetailAdapter(
override fun setHighlighted(isHighlighted: Boolean) { override fun setHighlighted(isHighlighted: Boolean) {
if (isHighlighted) { if (isHighlighted) {
binding.albumName.setTextColorResource(accent.first) binding.albumName.setTextColorResource(Accent.get().color)
} else { } else {
binding.albumName.setTextColor(normalTextColor) binding.albumName.setTextColor(normalTextColor)
} }

View file

@ -16,7 +16,7 @@ import org.oxycblt.auxio.playback.PlaybackViewModel
import org.oxycblt.auxio.recycler.DiffCallback import org.oxycblt.auxio.recycler.DiffCallback
import org.oxycblt.auxio.recycler.viewholders.BaseViewHolder import org.oxycblt.auxio.recycler.viewholders.BaseViewHolder
import org.oxycblt.auxio.recycler.viewholders.Highlightable import org.oxycblt.auxio.recycler.viewholders.Highlightable
import org.oxycblt.auxio.ui.accent import org.oxycblt.auxio.ui.Accent
import org.oxycblt.auxio.ui.applyAccents import org.oxycblt.auxio.ui.applyAccents
import org.oxycblt.auxio.ui.disable import org.oxycblt.auxio.ui.disable
import org.oxycblt.auxio.ui.setTextColorResource import org.oxycblt.auxio.ui.setTextColorResource
@ -138,7 +138,7 @@ class GenreDetailAdapter(
override fun setHighlighted(isHighlighted: Boolean) { override fun setHighlighted(isHighlighted: Boolean) {
if (isHighlighted) { if (isHighlighted) {
binding.songName.setTextColorResource(accent.first) binding.songName.setTextColorResource(Accent.get().color)
} else { } else {
binding.songName.setTextColor(normalTextColor) binding.songName.setTextColor(normalTextColor)
} }

View file

@ -17,7 +17,7 @@ import org.oxycblt.auxio.databinding.FragmentPlaybackBinding
import org.oxycblt.auxio.detail.DetailViewModel import org.oxycblt.auxio.detail.DetailViewModel
import org.oxycblt.auxio.logD import org.oxycblt.auxio.logD
import org.oxycblt.auxio.playback.state.LoopMode import org.oxycblt.auxio.playback.state.LoopMode
import org.oxycblt.auxio.ui.accent import org.oxycblt.auxio.ui.Accent
import org.oxycblt.auxio.ui.memberBinding import org.oxycblt.auxio.ui.memberBinding
import org.oxycblt.auxio.ui.toColor import org.oxycblt.auxio.ui.toColor
@ -36,7 +36,7 @@ class PlaybackFragment : Fragment(), SeekBar.OnSeekBarChangeListener {
// Colors // Colors
private val accentColor: ColorStateList by lazy { private val accentColor: ColorStateList by lazy {
ColorStateList.valueOf(accent.first.toColor(requireContext())) Accent.get().getStateList(requireContext())
} }
private val controlColor: ColorStateList by lazy { private val controlColor: ColorStateList by lazy {

View file

@ -22,7 +22,7 @@ import androidx.dynamicanimation.animation.SpringForce
import com.reddit.indicatorfastscroll.FastScrollItemIndicator import com.reddit.indicatorfastscroll.FastScrollItemIndicator
import com.reddit.indicatorfastscroll.FastScrollerView import com.reddit.indicatorfastscroll.FastScrollerView
import org.oxycblt.auxio.R import org.oxycblt.auxio.R
import org.oxycblt.auxio.ui.accent import org.oxycblt.auxio.ui.Accent
import org.oxycblt.auxio.ui.toColor import org.oxycblt.auxio.ui.toColor
/** /**
@ -40,7 +40,7 @@ class NoLeakThumbView @JvmOverloads constructor(
) : ConstraintLayout(context, attrs, defStyleAttr), ) : ConstraintLayout(context, attrs, defStyleAttr),
FastScrollerView.ItemIndicatorSelectedCallback { FastScrollerView.ItemIndicatorSelectedCallback {
private val thumbColor = ColorStateList.valueOf(accent.first.toColor(context)) private val thumbColor = Accent.get().getStateList(context)
private val iconColor = R.color.background.toColor(context) private val iconColor = R.color.background.toColor(context)
private val textAppearanceRes = R.style.TextAppearance_ThumbIndicator private val textAppearanceRes = R.style.TextAppearance_ThumbIndicator
private val textColor = R.color.background.toColor(context) private val textColor = R.color.background.toColor(context)

View file

@ -5,7 +5,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.graphics.toColor
import androidx.core.widget.addTextChangedListener import androidx.core.widget.addTextChangedListener
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels import androidx.fragment.app.activityViewModels
@ -23,8 +22,8 @@ import org.oxycblt.auxio.music.Genre
import org.oxycblt.auxio.music.Header import org.oxycblt.auxio.music.Header
import org.oxycblt.auxio.music.Song import org.oxycblt.auxio.music.Song
import org.oxycblt.auxio.playback.PlaybackViewModel import org.oxycblt.auxio.playback.PlaybackViewModel
import org.oxycblt.auxio.ui.Accent
import org.oxycblt.auxio.ui.ActionMenu import org.oxycblt.auxio.ui.ActionMenu
import org.oxycblt.auxio.ui.accent
import org.oxycblt.auxio.ui.fixAnimationInfoMemoryLeak import org.oxycblt.auxio.ui.fixAnimationInfoMemoryLeak
import org.oxycblt.auxio.ui.getLandscapeSpans import org.oxycblt.auxio.ui.getLandscapeSpans
import org.oxycblt.auxio.ui.isLandscape import org.oxycblt.auxio.ui.isLandscape
@ -51,7 +50,7 @@ class SearchFragment : Fragment() {
// Apply the accents manually. Not going through the mess of converting my app's // Apply the accents manually. Not going through the mess of converting my app's
// styling to Material given all the second-and-third-order effects it has. // styling to Material given all the second-and-third-order effects it has.
val accent = accent.first.toColor(requireContext()) val accent = Accent.get().color.toColor(requireContext())
val searchAdapter = SearchAdapter(::onItemSelection) { view, data -> val searchAdapter = SearchAdapter(::onItemSelection) { view, data ->
ActionMenu(requireCompatActivity(), view, data, ActionMenu.FLAG_NONE) ActionMenu(requireCompatActivity(), view, data, ActionMenu.FLAG_NONE)

View file

@ -20,9 +20,8 @@ import org.oxycblt.auxio.playback.PlaybackViewModel
import org.oxycblt.auxio.recycler.DisplayMode import org.oxycblt.auxio.recycler.DisplayMode
import org.oxycblt.auxio.settings.ui.AccentAdapter import org.oxycblt.auxio.settings.ui.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.createToast import org.oxycblt.auxio.ui.createToast
import org.oxycblt.auxio.ui.getDetailedAccentSummary
/** /**
* The actual fragment containing the settings menu. Inherits [PreferenceFragmentCompat]. * The actual fragment containing the settings menu. Inherits [PreferenceFragmentCompat].
@ -84,7 +83,7 @@ class SettingsListFragment : PreferenceFragmentCompat() {
true true
} }
summary = getDetailedAccentSummary(requireActivity(), accent) summary = Accent.get().getDetailedSummary(context)
} }
SettingsManager.Keys.KEY_EDGE_TO_EDGE -> { SettingsManager.Keys.KEY_EDGE_TO_EDGE -> {
@ -153,7 +152,7 @@ class SettingsListFragment : PreferenceFragmentCompat() {
// This is why I hate using third party libraries. // This is why I hate using third party libraries.
val recycler = RecyclerView(requireContext()).apply { val recycler = RecyclerView(requireContext()).apply {
adapter = AccentAdapter { adapter = AccentAdapter {
if (it.first != accent.first) { if (it != Accent.get()) {
SettingsManager.getInstance().accent = it SettingsManager.getInstance().accent = it
requireActivity().recreate() requireActivity().recreate()
@ -169,7 +168,7 @@ class SettingsListFragment : PreferenceFragmentCompat() {
(layoutManager as LinearLayoutManager) (layoutManager as LinearLayoutManager)
.scrollToPositionWithOffset( .scrollToPositionWithOffset(
ACCENTS.indexOf(accent), ACCENTS.indexOf(Accent.get()),
(width / 2) - childWidth (width / 2) - childWidth
) )
} }

View file

@ -7,6 +7,7 @@ import org.oxycblt.auxio.playback.state.PlaybackMode
import org.oxycblt.auxio.recycler.DisplayMode import org.oxycblt.auxio.recycler.DisplayMode
import org.oxycblt.auxio.recycler.SortMode import org.oxycblt.auxio.recycler.SortMode
import org.oxycblt.auxio.ui.ACCENTS import org.oxycblt.auxio.ui.ACCENTS
import org.oxycblt.auxio.ui.Accent
/** /**
* Wrapper around the [SharedPreferences] class that writes & reads values without a context. * Wrapper around the [SharedPreferences] class that writes & reads values without a context.
@ -31,12 +32,10 @@ class SettingsManager private constructor(context: Context) :
/** /**
* The current accent. * The current accent.
*/ */
var accent: Pair<Int, Int> var accent: Accent
get() { get() {
val accentIndex = sharedPrefs.getInt(Keys.KEY_ACCENT, 5)
// Accent is stored as an index [to be efficient], so retrieve it when done. // Accent is stored as an index [to be efficient], so retrieve it when done.
return ACCENTS[accentIndex] return ACCENTS[sharedPrefs.getInt(Keys.KEY_ACCENT, 5)]
} }
set(value) { set(value) {
val accentIndex = ACCENTS.indexOf(value) val accentIndex = ACCENTS.indexOf(value)
@ -200,14 +199,14 @@ class SettingsManager private constructor(context: Context) :
companion object { companion object {
@Volatile @Volatile
private lateinit var INSTANCE: SettingsManager private var INSTANCE: SettingsManager? = null
/** /**
* Init the single instance of [SettingsManager]. Done so that every object * Init the single instance of [SettingsManager]. Done so that every object
* can have access to it regardless of if it has a context. * can have access to it regardless of if it has a context.
*/ */
fun init(context: Context): SettingsManager { fun init(context: Context): SettingsManager {
if (!::INSTANCE.isInitialized) { if (INSTANCE == null) {
synchronized(this) { synchronized(this) {
INSTANCE = SettingsManager(context) INSTANCE = SettingsManager(context)
} }
@ -220,10 +219,13 @@ class SettingsManager private constructor(context: Context) :
* Get the single instance of [SettingsManager]. * Get the single instance of [SettingsManager].
*/ */
fun getInstance(): SettingsManager { fun getInstance(): SettingsManager {
check(::INSTANCE.isInitialized) { val instance = INSTANCE
"SettingsManager must be initialized with init() before getting its instance."
if (instance != null) {
return instance
} }
return INSTANCE
error("SettingsManager must be initialized with init() before getting its instance.")
} }
} }

View file

@ -7,8 +7,7 @@ import androidx.recyclerview.widget.RecyclerView
import org.oxycblt.auxio.R import org.oxycblt.auxio.R
import org.oxycblt.auxio.databinding.ItemAccentBinding import org.oxycblt.auxio.databinding.ItemAccentBinding
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.toColor import org.oxycblt.auxio.ui.toColor
/** /**
@ -17,7 +16,7 @@ import org.oxycblt.auxio.ui.toColor
* @param doOnAccentConfirm What to do when an accent is confirmed. * @param doOnAccentConfirm What to do when an accent is confirmed.
*/ */
class AccentAdapter( class AccentAdapter(
private val doOnAccentConfirm: (accent: Pair<Int, Int>) -> Unit private val doOnAccentConfirm: (accent: Accent) -> Unit
) : RecyclerView.Adapter<AccentAdapter.ViewHolder>() { ) : RecyclerView.Adapter<AccentAdapter.ViewHolder>() {
override fun getItemCount(): Int = ACCENTS.size override fun getItemCount(): Int = ACCENTS.size
@ -33,31 +32,25 @@ class AccentAdapter(
private val binding: ItemAccentBinding private val binding: ItemAccentBinding
) : RecyclerView.ViewHolder(binding.root) { ) : RecyclerView.ViewHolder(binding.root) {
fun bind(accentData: Pair<Int, Int>) { fun bind(accent: Accent) {
binding.accent.apply { binding.accent.apply {
contentDescription = getAccentItemSummary(context, accentData) contentDescription = accent.getDetailedSummary(context)
setOnClickListener { setOnClickListener {
doOnAccentConfirm(accentData) doOnAccentConfirm(accent)
} }
imageTintList = if (accentData.first == accent.first) { imageTintList = if (accent == Accent.get()) {
isEnabled = false isEnabled = false
ColorStateList.valueOf( ColorStateList.valueOf(R.color.background.toColor(context))
R.color.background.toColor(context)
)
} else { } else {
isEnabled = true isEnabled = true
ColorStateList.valueOf( ColorStateList.valueOf(android.R.color.transparent.toColor(context))
android.R.color.transparent.toColor(context)
)
} }
backgroundTintList = ColorStateList.valueOf( backgroundTintList = accent.getStateList(context)
accentData.first.toColor(context)
)
} }
} }
} }

View file

@ -1,6 +1,5 @@
package org.oxycblt.auxio.songs package org.oxycblt.auxio.songs
import android.content.res.ColorStateList
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.util.TypedValue import android.util.TypedValue
@ -18,12 +17,11 @@ import org.oxycblt.auxio.databinding.FragmentSongsBinding
import org.oxycblt.auxio.logD import org.oxycblt.auxio.logD
import org.oxycblt.auxio.music.MusicStore import org.oxycblt.auxio.music.MusicStore
import org.oxycblt.auxio.playback.PlaybackViewModel import org.oxycblt.auxio.playback.PlaybackViewModel
import org.oxycblt.auxio.ui.Accent
import org.oxycblt.auxio.ui.ActionMenu import org.oxycblt.auxio.ui.ActionMenu
import org.oxycblt.auxio.ui.accent
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.toColor
import kotlin.math.ceil import kotlin.math.ceil
/** /**
@ -109,7 +107,7 @@ class SongsFragment : Fragment() {
// API 22 and below don't support the state color, so just use the accent. // API 22 and below don't support the state color, so just use the accent.
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.M) { if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.M) {
textColor = ColorStateList.valueOf(accent.first.toColor(requireContext())) textColor = Accent.get().getStateList(requireContext())
} }
setupWithRecyclerView( setupWithRecyclerView(

View file

@ -0,0 +1,111 @@
package org.oxycblt.auxio.ui
import android.annotation.SuppressLint
import android.content.Context
import android.content.res.ColorStateList
import android.text.Spanned
import androidx.annotation.ColorRes
import androidx.annotation.StringRes
import androidx.annotation.StyleRes
import androidx.core.text.toSpanned
import org.oxycblt.auxio.R
import org.oxycblt.auxio.settings.SettingsManager
import java.util.Locale
/**
* A list of all possible accents.
* TODO: Add custom accents
*/
val ACCENTS = arrayOf(
Accent(R.color.red, R.style.Theme_Red, R.string.color_label_red),
Accent(R.color.pink, R.style.Theme_Pink, R.string.color_label_pink),
Accent(R.color.purple, R.style.Theme_Purple, R.string.color_label_purple),
Accent(R.color.deep_purple, R.style.Theme_DeepPurple, R.string.color_label_deep_purple),
Accent(R.color.indigo, R.style.Theme_Indigo, R.string.color_label_indigo),
Accent(R.color.blue, R.style.Theme_Blue, R.string.color_label_blue),
Accent(R.color.light_blue, R.style.Theme_LightBlue, R.string.color_label_light_blue),
Accent(R.color.cyan, R.style.Theme_Cyan, R.string.color_label_cyan),
Accent(R.color.teal, R.style.Theme_Teal, R.string.color_label_teal),
Accent(R.color.green, R.style.Theme_Green, R.string.color_label_green),
Accent(R.color.light_green, R.style.Theme_LightGreen, R.string.color_label_light_green),
Accent(R.color.lime, R.style.Theme_Lime, R.string.color_label_lime),
Accent(R.color.yellow, R.style.Theme_Yellow, R.string.color_label_yellow),
Accent(R.color.amber, R.style.Theme_Amber, R.string.color_label_amber),
Accent(R.color.orange, R.style.Theme_Orange, R.string.color_label_orange),
Accent(R.color.deep_orange, R.style.Theme_DeepOrange, R.string.color_label_deep_orange),
Accent(R.color.brown, R.style.Theme_Brown, R.string.color_label_brown),
Accent(R.color.grey, R.style.Theme_Gray, R.string.color_label_grey),
Accent(R.color.blue_grey, R.style.Theme_BlueGrey, R.string.color_label_blue_grey),
Accent(R.color.control_color, R.style.Theme_Neutral, R.string.color_label_neutral)
)
/**
* The data object for an accent.
* @property color The color resource for this accent
* @property theme The theme resource for this accent
* @property name The name of this accent
*/
data class Accent(
@ColorRes val color: Int,
@StyleRes val theme: Int,
@StringRes val name: Int
) {
/**
* Get a [ColorStateList] of the accent
*/
fun getStateList(context: Context): ColorStateList {
return ColorStateList.valueOf(color.toColor(context))
}
/**
* Get the name (in bold) and the hex value of a accent.
* @param context [Context] required
* @return A rendered span with the name in bold + the hex value of the accent.
*/
@SuppressLint("ResourceType")
fun getDetailedSummary(context: Context): Spanned {
val name = context.getString(name)
val hex = context.getString(color).toUpperCase(Locale.getDefault())
return context.getString(
R.string.format_accent_summary,
name, hex
).toSpanned().render()
}
companion object {
@Volatile
private var current: Accent? = null
/**
* Get the current accent, will default to whatever is stored in [SettingsManager]
* if there isnt one.
* @return The current accent
*/
fun get(): Accent {
val cur = current
if (cur != null) {
return cur
}
synchronized(this) {
val newCur = SettingsManager.getInstance().accent
current = newCur
return newCur
}
}
/**
* Set the current accent.
* @return The new accent
*/
fun set(accent: Accent): Accent {
synchronized(this) {
current = accent
}
return accent
}
}
}

View file

@ -14,9 +14,11 @@ 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
import androidx.annotation.ColorInt
import androidx.annotation.ColorRes import androidx.annotation.ColorRes
import androidx.annotation.PluralsRes import androidx.annotation.PluralsRes
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
import androidx.core.text.HtmlCompat import androidx.core.text.HtmlCompat
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import com.google.android.material.button.MaterialButton import com.google.android.material.button.MaterialButton
@ -51,7 +53,7 @@ fun TextView.setTextColorResource(@ColorRes color: Int) {
* Required because you cant determine a style of a view before API 29 * Required because you cant determine a style of a view before API 29
*/ */
fun MaterialButton.applyAccents(highlighted: Boolean) { fun MaterialButton.applyAccents(highlighted: Boolean) {
val accent = accent.first.toColor(context) val accent = Accent.get().color.toColor(context)
if (highlighted) { if (highlighted) {
backgroundTintList = ColorStateList.valueOf(accent) backgroundTintList = ColorStateList.valueOf(accent)
@ -103,6 +105,23 @@ fun Spanned.render(): Spanned {
) )
} }
/**
* Resolve a color.
* @param context [Context] required
* @return The resolved color, black if the resolving process failed.
*/
@ColorInt
fun Int.toColor(context: Context): Int {
return try {
ContextCompat.getColor(context, this)
} catch (e: Resources.NotFoundException) {
logE("Attempted color load failed.")
// Default to the emergency color [Black] if the loading fails.
ContextCompat.getColor(context, android.R.color.black)
}
}
// --- CONFIGURATION --- // --- CONFIGURATION ---
/** /**
@ -177,8 +196,9 @@ private fun isSystemBarOnBottom(activity: Activity): Boolean {
// --- HACKY NIGHTMARES --- // --- HACKY NIGHTMARES ---
/** /**
* Use R E F L E C T I O N to fix a memory leak where mAnimationInfo will keep a reference to * Use ***R E F L E C T I O N*** to fix a memory leak where mAnimationInfo will keep a reference to
* its focused view. * its focused view.
*
* I can't believe I have to do this. * I can't believe I have to do this.
*/ */
fun Fragment.fixAnimationInfoMemoryLeak() { fun Fragment.fixAnimationInfoMemoryLeak() {

View file

@ -1,125 +0,0 @@
package org.oxycblt.auxio.ui
import android.content.Context
import android.content.res.Resources
import android.text.Spanned
import androidx.annotation.ColorInt
import androidx.annotation.ColorRes
import androidx.core.content.ContextCompat
import androidx.core.graphics.ColorUtils
import androidx.core.text.toSpanned
import org.oxycblt.auxio.R
import org.oxycblt.auxio.logE
import java.util.Locale
// Functions for managing colors/accents.
/**
* An array of the base accents and their respective themes.
*/
val ACCENTS = arrayOf(
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
Pair(R.color.deep_purple, R.style.Theme_DeepPurple), // 3
Pair(R.color.indigo, R.style.Theme_Indigo), // 4
Pair(R.color.blue, R.style.Theme_Blue), // 5 - Default!
Pair(R.color.light_blue, R.style.Theme_LightBlue), // 6
Pair(R.color.cyan, R.style.Theme_Cyan), // 7
Pair(R.color.teal, R.style.Theme_Teal), // 8
Pair(R.color.green, R.style.Theme_Green), // 9
Pair(R.color.light_green, R.style.Theme_LightGreen), // 10
Pair(R.color.lime, R.style.Theme_Lime), // 11
Pair(R.color.yellow, R.style.Theme_Yellow), // 12
Pair(R.color.amber, R.style.Theme_Amber), // 13
Pair(R.color.orange, R.style.Theme_Orange), // 14
Pair(R.color.deep_orange, R.style.Theme_DeepOrange), // 15
Pair(R.color.brown, R.style.Theme_Brown), // 16
Pair(R.color.grey, R.style.Theme_Gray), // 17
Pair(R.color.blue_grey, R.style.Theme_BlueGrey), // 18
Pair(R.color.control_color, R.style.Theme_Neutral)
)
/**
* An array of strings for each accent, use these instead of [Resources.getResourceName] so that
* the accent names are translated.
*/
private val ACCENT_NAMES = arrayOf(
R.string.color_label_red, R.string.color_label_pink,
R.string.color_label_purple, R.string.color_label_deep_purple,
R.string.color_label_indigo, R.string.color_label_blue,
R.string.color_label_light_blue, R.string.color_label_cyan,
R.string.color_label_teal, R.string.color_label_green,
R.string.color_label_light_green, R.string.color_label_lime,
R.string.color_label_yellow, R.string.color_label_amber,
R.string.color_label_orange, R.string.color_label_deep_orange,
R.string.color_label_brown, R.string.color_label_grey,
R.string.color_label_blue_grey, R.string.color_label_neutral
)
/**
* The programmatically accessible accent, reflects the currently set accent.
*/
lateinit var accent: Pair<Int, Int>
/**
* Gets the transparent form of a color.
* @param context [Context] required to create the color
* @param color The RESOURCE ID for the color
* @param alpha The new alpha that wants to be applied
* @return The new, resolved transparent color
*/
@ColorInt
fun getTransparentAccent(context: Context, @ColorRes color: Int, alpha: Int): Int {
return ColorUtils.setAlphaComponent(
color.toColor(context),
alpha
)
}
/**
* Resolve a color.
* @param context [Context] required
* @return The resolved color, black if the resolving process failed.
*/
@ColorInt
fun Int.toColor(context: Context): Int {
return try {
ContextCompat.getColor(context, this)
} catch (e: Resources.NotFoundException) {
logE("Attempted color load failed.")
// Default to the emergency color [Black] if the loading fails.
ContextCompat.getColor(context, android.R.color.black)
}
}
/**
* Get the name of an accent.
* @param context [Context] required
* @param newAccent The accent the name should be given for.
* @return The accent name according to the strings for this specific locale.
*/
fun getAccentItemSummary(context: Context, newAccent: Pair<Int, Int>): String {
val accentIndex = ACCENTS.indexOf(newAccent)
check(accentIndex != -1) { "Invalid accent given" }
return context.getString(ACCENT_NAMES[accentIndex])
}
/**
* Get the name (in bold) and the hex value of a accent.
* @param context [Context] required
* @param newAccent Accent to get the information for
* @return A rendered span with the name in bold + the hex value of the accent.
*/
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()
}