This commit is contained in:
HapeLee committed 2025-06-04 13:33:51 +08:00
1 parent b86d90021c
commit 6c0757c91b
972 files changed
+6885 -7321

No files matched your search

@@ -0,0 +1,7 @@
@file:Suppress("unused")
package io.legato.kazusa.lib.theme
/**
* @author Karim Abou Zeid (kabouzeid)
*/
@@ -0,0 +1,450 @@
package io.legato.kazusa.lib.theme
import android.content.Context
import android.content.res.ColorStateList
import android.graphics.Color
import android.graphics.drawable.ColorDrawable
import android.graphics.drawable.Drawable
import android.graphics.drawable.GradientDrawable
import android.graphics.drawable.StateListDrawable
import androidx.annotation.ColorInt
import androidx.annotation.Dimension
import androidx.annotation.DrawableRes
import androidx.annotation.IntDef
import androidx.core.content.ContextCompat
@Suppress("unused")
object Selector {
fun shapeBuild(): ShapeSelector {
return ShapeSelector()
}
fun colorBuild(): ColorSelector {
return ColorSelector()
}
fun drawableBuild(): DrawableSelector {
return DrawableSelector()
}
/**
* 形状ShapeSelector
*
* @author hjy
* created at 2017/12/11 22:26
*/
class ShapeSelector {
private var mShape: Int = 0 //the shape of background
private var mDefaultBgColor: Int = 0 //default background color
private var mDisabledBgColor: Int = 0 //state_enabled = false
private var mPressedBgColor: Int = 0 //state_pressed = true
private var mSelectedBgColor: Int = 0 //state_selected = true
private var mFocusedBgColor: Int = 0 //state_focused = true
private var mCheckedBgColor: Int = 0 //state_checked = true
private var mStrokeWidth: Int = 0 //stroke width in pixel
private var mDefaultStrokeColor: Int = 0 //default stroke color
private var mDisabledStrokeColor: Int = 0 //state_enabled = false
private var mPressedStrokeColor: Int = 0 //state_pressed = true
private var mSelectedStrokeColor: Int = 0 //state_selected = true
private var mFocusedStrokeColor: Int = 0 //state_focused = true
private var mCheckedStrokeColor: Int = 0 //state_checked = true
private var mCornerRadius: Int = 0 //corner radius
private var hasSetDisabledBgColor = false
private var hasSetPressedBgColor = false
private var hasSetSelectedBgColor = false
private val hasSetFocusedBgColor = false
private var hasSetCheckedBgColor = false
private var hasSetDisabledStrokeColor = false
private var hasSetPressedStrokeColor = false
private var hasSetSelectedStrokeColor = false
private var hasSetFocusedStrokeColor = false
private var hasSetCheckedStrokeColor = false
@IntDef(
GradientDrawable.RECTANGLE,
GradientDrawable.OVAL,
GradientDrawable.LINE,
GradientDrawable.RING
)
private annotation class Shape
init {
//initialize default values
mShape = GradientDrawable.RECTANGLE
mDefaultBgColor = Color.TRANSPARENT
mDisabledBgColor = Color.TRANSPARENT
mPressedBgColor = Color.TRANSPARENT
mSelectedBgColor = Color.TRANSPARENT
mFocusedBgColor = Color.TRANSPARENT
mStrokeWidth = 0
mDefaultStrokeColor = Color.TRANSPARENT
mDisabledStrokeColor = Color.TRANSPARENT
mPressedStrokeColor = Color.TRANSPARENT
mSelectedStrokeColor = Color.TRANSPARENT
mFocusedStrokeColor = Color.TRANSPARENT
mCornerRadius = 0
}
fun setShape(@Shape shape: Int): ShapeSelector {
mShape = shape
return this
}
fun setDefaultBgColor(@ColorInt color: Int): ShapeSelector {
mDefaultBgColor = color
if (!hasSetDisabledBgColor)
mDisabledBgColor = color
if (!hasSetPressedBgColor)
mPressedBgColor = color
if (!hasSetSelectedBgColor)
mSelectedBgColor = color
if (!hasSetFocusedBgColor)
mFocusedBgColor = color
return this
}
fun setDisabledBgColor(@ColorInt color: Int): ShapeSelector {
mDisabledBgColor = color
hasSetDisabledBgColor = true
return this
}
fun setPressedBgColor(@ColorInt color: Int): ShapeSelector {
mPressedBgColor = color
hasSetPressedBgColor = true
return this
}
fun setSelectedBgColor(@ColorInt color: Int): ShapeSelector {
mSelectedBgColor = color
hasSetSelectedBgColor = true
return this
}
fun setFocusedBgColor(@ColorInt color: Int): ShapeSelector {
mFocusedBgColor = color
hasSetPressedBgColor = true
return this
}
fun setCheckedBgColor(@ColorInt color: Int): ShapeSelector {
mCheckedBgColor = color
hasSetCheckedBgColor = true
return this
}
fun setStrokeWidth(@Dimension width: Int): ShapeSelector {
mStrokeWidth = width
return this
}
fun setDefaultStrokeColor(@ColorInt color: Int): ShapeSelector {
mDefaultStrokeColor = color
if (!hasSetDisabledStrokeColor)
mDisabledStrokeColor = color
if (!hasSetPressedStrokeColor)
mPressedStrokeColor = color
if (!hasSetSelectedStrokeColor)
mSelectedStrokeColor = color
if (!hasSetFocusedStrokeColor)
mFocusedStrokeColor = color
return this
}
fun setDisabledStrokeColor(@ColorInt color: Int): ShapeSelector {
mDisabledStrokeColor = color
hasSetDisabledStrokeColor = true
return this
}
fun setPressedStrokeColor(@ColorInt color: Int): ShapeSelector {
mPressedStrokeColor = color
hasSetPressedStrokeColor = true
return this
}
fun setSelectedStrokeColor(@ColorInt color: Int): ShapeSelector {
mSelectedStrokeColor = color
hasSetSelectedStrokeColor = true
return this
}
fun setCheckedStrokeColor(@ColorInt color: Int): ShapeSelector {
mCheckedStrokeColor = color
hasSetCheckedStrokeColor = true
return this
}
fun setFocusedStrokeColor(@ColorInt color: Int): ShapeSelector {
mFocusedStrokeColor = color
hasSetFocusedStrokeColor = true
return this
}
fun setCornerRadius(@Dimension radius: Int): ShapeSelector {
mCornerRadius = radius
return this
}
fun create(): StateListDrawable {
val selector = StateListDrawable()
//enabled = false
if (hasSetDisabledBgColor || hasSetDisabledStrokeColor) {
val disabledShape = getItemShape(
mShape, mCornerRadius,
mDisabledBgColor, mStrokeWidth, mDisabledStrokeColor
)
selector.addState(intArrayOf(-android.R.attr.state_enabled), disabledShape)
}
//pressed = true
if (hasSetPressedBgColor || hasSetPressedStrokeColor) {
val pressedShape = getItemShape(
mShape, mCornerRadius,
mPressedBgColor, mStrokeWidth, mPressedStrokeColor
)
selector.addState(intArrayOf(android.R.attr.state_pressed), pressedShape)
}
//selected = true
if (hasSetSelectedBgColor || hasSetSelectedStrokeColor) {
val selectedShape = getItemShape(
mShape, mCornerRadius,
mSelectedBgColor, mStrokeWidth, mSelectedStrokeColor
)
selector.addState(intArrayOf(android.R.attr.state_selected), selectedShape)
}
//focused = true
if (hasSetFocusedBgColor || hasSetFocusedStrokeColor) {
val focusedShape = getItemShape(
mShape, mCornerRadius,
mFocusedBgColor, mStrokeWidth, mFocusedStrokeColor
)
selector.addState(intArrayOf(android.R.attr.state_focused), focusedShape)
}
//checked = true
if (hasSetCheckedBgColor || hasSetCheckedStrokeColor) {
val checkedShape = getItemShape(
mShape, mCornerRadius,
mCheckedBgColor, mStrokeWidth, mCheckedStrokeColor
)
selector.addState(intArrayOf(android.R.attr.state_checked), checkedShape)
}
//default
val defaultShape = getItemShape(
mShape, mCornerRadius,
mDefaultBgColor, mStrokeWidth, mDefaultStrokeColor
)
selector.addState(intArrayOf(), defaultShape)
return selector
}
private fun getItemShape(
shape: Int, cornerRadius: Int,
solidColor: Int, strokeWidth: Int, strokeColor: Int
): GradientDrawable {
val drawable = GradientDrawable()
drawable.shape = shape
drawable.setStroke(strokeWidth, strokeColor)
drawable.cornerRadius = cornerRadius.toFloat()
drawable.setColor(solidColor)
return drawable
}
}
/**
* 资源DrawableSelector
*
* @author hjy
* created at 2017/12/11 22:34
*/
@Suppress("MemberVisibilityCanBePrivate")
class DrawableSelector {
private var mDefaultDrawable: Drawable? = null
private var mDisabledDrawable: Drawable? = null
private var mPressedDrawable: Drawable? = null
private var mSelectedDrawable: Drawable? = null
private var mFocusedDrawable: Drawable? = null
private var hasSetDisabledDrawable = false
private var hasSetPressedDrawable = false
private var hasSetSelectedDrawable = false
private var hasSetFocusedDrawable = false
init {
mDefaultDrawable = ColorDrawable(Color.TRANSPARENT)
}
fun setDefaultDrawable(drawable: Drawable?): DrawableSelector {
mDefaultDrawable = drawable
if (!hasSetDisabledDrawable)
mDisabledDrawable = drawable
if (!hasSetPressedDrawable)
mPressedDrawable = drawable
if (!hasSetSelectedDrawable)
mSelectedDrawable = drawable
if (!hasSetFocusedDrawable)
mFocusedDrawable = drawable
return this
}
fun setDisabledDrawable(drawable: Drawable?): DrawableSelector {
mDisabledDrawable = drawable
hasSetDisabledDrawable = true
return this
}
fun setPressedDrawable(drawable: Drawable?): DrawableSelector {
mPressedDrawable = drawable
hasSetPressedDrawable = true
return this
}
fun setSelectedDrawable(drawable: Drawable?): DrawableSelector {
mSelectedDrawable = drawable
hasSetSelectedDrawable = true
return this
}
fun setFocusedDrawable(drawable: Drawable?): DrawableSelector {
mFocusedDrawable = drawable
hasSetFocusedDrawable = true
return this
}
fun create(): StateListDrawable {
val selector = StateListDrawable()
if (hasSetDisabledDrawable)
selector.addState(intArrayOf(-android.R.attr.state_enabled), mDisabledDrawable)
if (hasSetPressedDrawable)
selector.addState(intArrayOf(android.R.attr.state_pressed), mPressedDrawable)
if (hasSetSelectedDrawable)
selector.addState(intArrayOf(android.R.attr.state_selected), mSelectedDrawable)
if (hasSetFocusedDrawable)
selector.addState(intArrayOf(android.R.attr.state_focused), mFocusedDrawable)
selector.addState(intArrayOf(), mDefaultDrawable)
return selector
}
fun setDefaultDrawable(context: Context, @DrawableRes drawableRes: Int): DrawableSelector {
return setDefaultDrawable(ContextCompat.getDrawable(context, drawableRes))
}
fun setDisabledDrawable(context: Context, @DrawableRes drawableRes: Int): DrawableSelector {
return setDisabledDrawable(ContextCompat.getDrawable(context, drawableRes))
}
fun setPressedDrawable(context: Context, @DrawableRes drawableRes: Int): DrawableSelector {
return setPressedDrawable(ContextCompat.getDrawable(context, drawableRes))
}
fun setSelectedDrawable(context: Context, @DrawableRes drawableRes: Int): DrawableSelector {
return setSelectedDrawable(ContextCompat.getDrawable(context, drawableRes))
}
fun setFocusedDrawable(context: Context, @DrawableRes drawableRes: Int): DrawableSelector {
return setFocusedDrawable(ContextCompat.getDrawable(context, drawableRes))
}
}
/**
* 颜色ColorSelector
*
* @author hjy
* created at 2017/12/11 22:26
*/
class ColorSelector {
private var mDefaultColor: Int = 0
private var mDisabledColor: Int = 0
private var mPressedColor: Int = 0
private var mSelectedColor: Int = 0
private var mFocusedColor: Int = 0
private var mCheckedColor: Int = 0
private var hasSetDisabledColor = false
private var hasSetPressedColor = false
private var hasSetSelectedColor = false
private var hasSetFocusedColor = false
private var hasSetCheckedColor = false
init {
mDefaultColor = Color.BLACK
mDisabledColor = Color.GRAY
mPressedColor = Color.BLACK
mSelectedColor = Color.BLACK
mFocusedColor = Color.BLACK
}
fun setDefaultColor(@ColorInt color: Int): ColorSelector {
mDefaultColor = color
if (!hasSetDisabledColor)
mDisabledColor = color
if (!hasSetPressedColor)
mPressedColor = color
if (!hasSetSelectedColor)
mSelectedColor = color
if (!hasSetFocusedColor)
mFocusedColor = color
return this
}
fun setDisabledColor(@ColorInt color: Int): ColorSelector {
mDisabledColor = color
hasSetDisabledColor = true
return this
}
fun setPressedColor(@ColorInt color: Int): ColorSelector {
mPressedColor = color
hasSetPressedColor = true
return this
}
fun setSelectedColor(@ColorInt color: Int): ColorSelector {
mSelectedColor = color
hasSetSelectedColor = true
return this
}
fun setFocusedColor(@ColorInt color: Int): ColorSelector {
mFocusedColor = color
hasSetFocusedColor = true
return this
}
fun setCheckedColor(@ColorInt color: Int): ColorSelector {
mCheckedColor = color
hasSetCheckedColor = true
return this
}
fun create(): ColorStateList {
val colors = intArrayOf(
if (hasSetDisabledColor) mDisabledColor else mDefaultColor,
if (hasSetPressedColor) mPressedColor else mDefaultColor,
if (hasSetSelectedColor) mSelectedColor else mDefaultColor,
if (hasSetFocusedColor) mFocusedColor else mDefaultColor,
if (hasSetCheckedColor) mCheckedColor else mDefaultColor,
mDefaultColor
)
val states = arrayOfNulls<IntArray>(6)
states[0] = intArrayOf(-android.R.attr.state_enabled)
states[1] = intArrayOf(android.R.attr.state_pressed)
states[2] = intArrayOf(android.R.attr.state_selected)
states[3] = intArrayOf(android.R.attr.state_focused)
states[4] = intArrayOf(android.R.attr.state_checked)
states[5] = intArrayOf()
return ColorStateList(states, colors)
}
}
}
@@ -0,0 +1,354 @@
package io.legato.kazusa.lib.theme
import android.annotation.SuppressLint
import android.content.Context
import android.content.SharedPreferences
import android.graphics.Color
import androidx.annotation.AttrRes
import androidx.annotation.CheckResult
import androidx.annotation.ColorInt
import androidx.annotation.ColorRes
import androidx.core.content.ContextCompat
import io.legato.kazusa.utils.ColorUtils
import splitties.init.appCtx
import androidx.core.graphics.toColorInt
/**
* @author Aidan Follestad (afollestad), Karim Abou Zeid (kabouzeid)
*/
@Suppress("unused")
class ThemeStore @SuppressLint("CommitPrefEdits")
private constructor(private val mContext: Context) : ThemeStoreInterface {
private val mEditor = prefs(mContext).edit()
// override fun enableDynamicColors(context: Context, enable: Boolean): ThemeStore {
// mEditor.putBoolean(ThemeStorePrefKeys.KEY_DYNAMIC_COLORS_ENABLED, enable)
// if (enable && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
// // 使用系统动态颜色
// val dynamicColors = DynamicColorsOptions.Builder()
// .setThemeOverlay(R.style.Theme_App_Dynamic)
// .build()
// DynamicColors.applyToActivitiesIfAvailable(context.applicationContext, dynamicColors)
// }
// return this
// }
override fun primaryColor(@ColorInt color: Int): ThemeStore {
mEditor.putInt(ThemeStorePrefKeys.KEY_PRIMARY_COLOR, color)
if (autoGeneratePrimaryDark(mContext))
primaryColorDark(ColorUtils.darkenColor(color))
return this
}
override fun primaryColorRes(@ColorRes colorRes: Int): ThemeStore {
return primaryColor(ContextCompat.getColor(mContext, colorRes))
}
override fun primaryColorAttr(@AttrRes colorAttr: Int): ThemeStore {
return primaryColor(ThemeUtils.resolveColor(mContext, colorAttr))
}
override fun primaryColorDark(@ColorInt color: Int): ThemeStore {
mEditor.putInt(ThemeStorePrefKeys.KEY_PRIMARY_COLOR_DARK, color)
return this
}
override fun primaryColorDarkRes(@ColorRes colorRes: Int): ThemeStore {
return primaryColorDark(ContextCompat.getColor(mContext, colorRes))
}
override fun primaryColorDarkAttr(@AttrRes colorAttr: Int): ThemeStore {
return primaryColorDark(ThemeUtils.resolveColor(mContext, colorAttr))
}
override fun accentColor(@ColorInt color: Int): ThemeStore {
mEditor.putInt(ThemeStorePrefKeys.KEY_ACCENT_COLOR, color)
return this
}
override fun accentColorRes(@ColorRes colorRes: Int): ThemeStore {
return accentColor(ContextCompat.getColor(mContext, colorRes))
}
override fun accentColorAttr(@AttrRes colorAttr: Int): ThemeStore {
return accentColor(ThemeUtils.resolveColor(mContext, colorAttr))
}
override fun statusBarColor(@ColorInt color: Int): ThemeStore {
mEditor.putInt(ThemeStorePrefKeys.KEY_STATUS_BAR_COLOR, color)
return this
}
override fun statusBarColorRes(@ColorRes colorRes: Int): ThemeStore {
return statusBarColor(ContextCompat.getColor(mContext, colorRes))
}
override fun statusBarColorAttr(@AttrRes colorAttr: Int): ThemeStore {
return statusBarColor(ThemeUtils.resolveColor(mContext, colorAttr))
}
override fun navigationBarColor(@ColorInt color: Int): ThemeStore {
mEditor.putInt(ThemeStorePrefKeys.KEY_NAVIGATION_BAR_COLOR, color)
return this
}
override fun navigationBarColorRes(@ColorRes colorRes: Int): ThemeStore {
return navigationBarColor(ContextCompat.getColor(mContext, colorRes))
}
override fun navigationBarColorAttr(@AttrRes colorAttr: Int): ThemeStore {
return navigationBarColor(ThemeUtils.resolveColor(mContext, colorAttr))
}
override fun textColorPrimary(@ColorInt color: Int): ThemeStore {
mEditor.putInt(ThemeStorePrefKeys.KEY_TEXT_COLOR_PRIMARY, color)
return this
}
override fun textColorPrimaryRes(@ColorRes colorRes: Int): ThemeStore {
return textColorPrimary(ContextCompat.getColor(mContext, colorRes))
}
override fun textColorPrimaryAttr(@AttrRes colorAttr: Int): ThemeStore {
return textColorPrimary(ThemeUtils.resolveColor(mContext, colorAttr))
}
override fun textColorPrimaryInverse(@ColorInt color: Int): ThemeStore {
mEditor.putInt(ThemeStorePrefKeys.KEY_TEXT_COLOR_PRIMARY_INVERSE, color)
return this
}
override fun textColorPrimaryInverseRes(@ColorRes colorRes: Int): ThemeStore {
return textColorPrimaryInverse(ContextCompat.getColor(mContext, colorRes))
}
override fun textColorPrimaryInverseAttr(@AttrRes colorAttr: Int): ThemeStore {
return textColorPrimaryInverse(ThemeUtils.resolveColor(mContext, colorAttr))
}
override fun textColorSecondary(@ColorInt color: Int): ThemeStore {
mEditor.putInt(ThemeStorePrefKeys.KEY_TEXT_COLOR_SECONDARY, color)
return this
}
override fun textColorSecondaryRes(@ColorRes colorRes: Int): ThemeStore {
return textColorSecondary(ContextCompat.getColor(mContext, colorRes))
}
override fun textColorSecondaryAttr(@AttrRes colorAttr: Int): ThemeStore {
return textColorSecondary(ThemeUtils.resolveColor(mContext, colorAttr))
}
override fun textColorSecondaryInverse(@ColorInt color: Int): ThemeStore {
mEditor.putInt(ThemeStorePrefKeys.KEY_TEXT_COLOR_SECONDARY_INVERSE, color)
return this
}
override fun textColorSecondaryInverseRes(@ColorRes colorRes: Int): ThemeStore {
return textColorSecondaryInverse(ContextCompat.getColor(mContext, colorRes))
}
override fun textColorSecondaryInverseAttr(@AttrRes colorAttr: Int): ThemeStore {
return textColorSecondaryInverse(ThemeUtils.resolveColor(mContext, colorAttr))
}
override fun backgroundColor(color: Int): ThemeStore {
mEditor.putInt(ThemeStorePrefKeys.KEY_BACKGROUND_COLOR, color)
return this
}
override fun bottomBackground(color: Int): ThemeStore {
mEditor.putInt(ThemeStorePrefKeys.KEY_BOTTOM_BACKGROUND, color)
return this
}
override fun autoGeneratePrimaryDark(autoGenerate: Boolean): ThemeStore {
mEditor.putBoolean(ThemeStorePrefKeys.KEY_AUTO_GENERATE_PRIMARYDARK, autoGenerate)
return this
}
// Commit method
override fun apply() {
mEditor.putLong(ThemeStorePrefKeys.VALUES_CHANGED, System.currentTimeMillis())
.putBoolean(ThemeStorePrefKeys.IS_CONFIGURED_KEY, true)
.apply()
accentColor = accentColor()
}
companion object {
var accentColor = accentColor()
fun editTheme(context: Context): ThemeStore {
return ThemeStore(context)
}
// Static getters
@CheckResult
internal fun prefs(context: Context): SharedPreferences {
return context.getSharedPreferences(
ThemeStorePrefKeys.CONFIG_PREFS_KEY_DEFAULT,
Context.MODE_PRIVATE
)
}
fun markChanged(context: Context) {
ThemeStore(context).apply()
}
@CheckResult
@ColorInt
fun primaryColor(context: Context = appCtx): Int {
return prefs(context).getInt(
ThemeStorePrefKeys.KEY_PRIMARY_COLOR,
ThemeUtils.resolveColor(
context,
androidx.appcompat.R.attr.colorPrimary,
"#455A64".toColorInt()
)
)
}
@CheckResult
@ColorInt
fun primaryColorDark(context: Context): Int {
return prefs(context).getInt(
ThemeStorePrefKeys.KEY_PRIMARY_COLOR_DARK,
ThemeUtils.resolveColor(
context,
androidx.appcompat.R.attr.colorPrimaryDark,
Color.parseColor("#37474F")
)
)
}
@CheckResult
@ColorInt
fun accentColor(context: Context = appCtx): Int {
return prefs(context).getInt(
ThemeStorePrefKeys.KEY_ACCENT_COLOR,
ThemeUtils.resolveColor(
context,
androidx.appcompat.R.attr.colorAccent,
Color.parseColor("#263238")
)
)
}
@CheckResult
@ColorInt
fun statusBarColor(context: Context, transparent: Boolean): Int {
return if (transparent) {
prefs(context).getInt(
ThemeStorePrefKeys.KEY_STATUS_BAR_COLOR,
primaryColor(context)
)
} else {
prefs(context).getInt(
ThemeStorePrefKeys.KEY_STATUS_BAR_COLOR,
primaryColorDark(context)
)
}
}
@CheckResult
@ColorInt
fun navigationBarColor(context: Context): Int {
return prefs(context).getInt(
ThemeStorePrefKeys.KEY_NAVIGATION_BAR_COLOR,
bottomBackground(context)
)
}
@CheckResult
@ColorInt
fun textColorPrimary(context: Context): Int {
return prefs(context).getInt(
ThemeStorePrefKeys.KEY_TEXT_COLOR_PRIMARY,
ThemeUtils.resolveColor(context, android.R.attr.textColorPrimary)
)
}
@CheckResult
@ColorInt
fun textColorPrimaryInverse(context: Context): Int {
return prefs(context).getInt(
ThemeStorePrefKeys.KEY_TEXT_COLOR_PRIMARY_INVERSE,
ThemeUtils.resolveColor(context, android.R.attr.textColorPrimaryInverse)
)
}
@CheckResult
@ColorInt
fun textColorSecondary(context: Context): Int {
return prefs(context).getInt(
ThemeStorePrefKeys.KEY_TEXT_COLOR_SECONDARY,
ThemeUtils.resolveColor(context, android.R.attr.textColorSecondary)
)
}
@CheckResult
@ColorInt
fun textColorSecondaryInverse(context: Context): Int {
return prefs(context).getInt(
ThemeStorePrefKeys.KEY_TEXT_COLOR_SECONDARY_INVERSE,
ThemeUtils.resolveColor(context, android.R.attr.textColorSecondaryInverse)
)
}
@CheckResult
@ColorInt
fun backgroundColor(context: Context = appCtx): Int {
return prefs(context).getInt(
ThemeStorePrefKeys.KEY_BACKGROUND_COLOR,
ThemeUtils.resolveColor(context, android.R.attr.colorBackground)
)
}
@CheckResult
@ColorInt
fun bottomBackground(context: Context = appCtx): Int {
return prefs(context).getInt(
ThemeStorePrefKeys.KEY_BOTTOM_BACKGROUND,
ThemeUtils.resolveColor(context, android.R.attr.colorBackground)
)
}
@CheckResult
fun coloredStatusBar(context: Context): Boolean {
return prefs(context).getBoolean(
ThemeStorePrefKeys.KEY_APPLY_PRIMARYDARK_STATUSBAR,
true
)
}
@CheckResult
fun coloredNavigationBar(context: Context): Boolean {
return prefs(context).getBoolean(ThemeStorePrefKeys.KEY_APPLY_PRIMARY_NAVBAR, false)
}
@CheckResult
fun autoGeneratePrimaryDark(context: Context): Boolean {
return prefs(context).getBoolean(ThemeStorePrefKeys.KEY_AUTO_GENERATE_PRIMARYDARK, true)
}
@CheckResult
fun isConfigured(context: Context): Boolean {
return prefs(context).getBoolean(ThemeStorePrefKeys.IS_CONFIGURED_KEY, false)
}
@SuppressLint("CommitPrefEdits")
fun isConfigured(context: Context, version: Int): Boolean {
val prefs = prefs(context)
val lastVersion = prefs.getInt(ThemeStorePrefKeys.IS_CONFIGURED_VERSION_KEY, -1)
if (version > lastVersion) {
prefs.edit().putInt(ThemeStorePrefKeys.IS_CONFIGURED_VERSION_KEY, version).apply()
return false
}
return true
}
}
}
@@ -0,0 +1,90 @@
package io.legato.kazusa.lib.theme
import androidx.annotation.AttrRes
import androidx.annotation.ColorInt
import androidx.annotation.ColorRes
/**
* @author Aidan Follestad (afollestad), Karim Abou Zeid (kabouzeid)
*/
internal interface ThemeStoreInterface {
// Primary colors
fun primaryColor(@ColorInt color: Int): ThemeStore
fun primaryColorRes(@ColorRes colorRes: Int): ThemeStore
fun primaryColorAttr(@AttrRes colorAttr: Int): ThemeStore
fun autoGeneratePrimaryDark(autoGenerate: Boolean): ThemeStore
fun primaryColorDark(@ColorInt color: Int): ThemeStore
fun primaryColorDarkRes(@ColorRes colorRes: Int): ThemeStore
fun primaryColorDarkAttr(@AttrRes colorAttr: Int): ThemeStore
// Accent colors
fun accentColor(@ColorInt color: Int): ThemeStore
fun accentColorRes(@ColorRes colorRes: Int): ThemeStore
fun accentColorAttr(@AttrRes colorAttr: Int): ThemeStore
// Status bar color
fun statusBarColor(@ColorInt color: Int): ThemeStore
fun statusBarColorRes(@ColorRes colorRes: Int): ThemeStore
fun statusBarColorAttr(@AttrRes colorAttr: Int): ThemeStore
// Navigation bar color
fun navigationBarColor(@ColorInt color: Int): ThemeStore
fun navigationBarColorRes(@ColorRes colorRes: Int): ThemeStore
fun navigationBarColorAttr(@AttrRes colorAttr: Int): ThemeStore
// Primary text color
fun textColorPrimary(@ColorInt color: Int): ThemeStore
fun textColorPrimaryRes(@ColorRes colorRes: Int): ThemeStore
fun textColorPrimaryAttr(@AttrRes colorAttr: Int): ThemeStore
fun textColorPrimaryInverse(@ColorInt color: Int): ThemeStore
fun textColorPrimaryInverseRes(@ColorRes colorRes: Int): ThemeStore
fun textColorPrimaryInverseAttr(@AttrRes colorAttr: Int): ThemeStore
// Secondary text color
fun textColorSecondary(@ColorInt color: Int): ThemeStore
fun textColorSecondaryRes(@ColorRes colorRes: Int): ThemeStore
fun textColorSecondaryAttr(@AttrRes colorAttr: Int): ThemeStore
fun textColorSecondaryInverse(@ColorInt color: Int): ThemeStore
fun textColorSecondaryInverseRes(@ColorRes colorRes: Int): ThemeStore
fun textColorSecondaryInverseAttr(@AttrRes colorAttr: Int): ThemeStore
// Background
fun backgroundColor(@ColorInt color: Int): ThemeStore
fun bottomBackground(@ColorInt color: Int): ThemeStore
// Commit/apply
fun apply()
}
@@ -0,0 +1,37 @@
package io.legato.kazusa.lib.theme
/**
* @author Aidan Follestad (afollestad), Karim Abou Zeid (kabouzeid)
*/
object ThemeStorePrefKeys {
const val KEY_DYNAMIC_COLORS_ENABLED = ""
const val CONFIG_PREFS_KEY_DEFAULT = "app_themes"
const val IS_CONFIGURED_KEY = "is_configured"
const val IS_CONFIGURED_VERSION_KEY = "is_configured_version"
const val VALUES_CHANGED = "values_changed"
const val KEY_PRIMARY_COLOR = "primary_color"
const val KEY_PRIMARY_COLOR_DARK = "primary_color_dark"
const val KEY_ACCENT_COLOR = "accent_color"
const val KEY_STATUS_BAR_COLOR = "status_bar_color"
const val KEY_NAVIGATION_BAR_COLOR = "navigation_bar_color"
const val KEY_TEXT_COLOR_PRIMARY = "text_color_primary"
const val KEY_TEXT_COLOR_PRIMARY_INVERSE = "text_color_primary_inverse"
const val KEY_TEXT_COLOR_SECONDARY = "text_color_secondary"
const val KEY_TEXT_COLOR_SECONDARY_INVERSE = "text_color_secondary_inverse"
const val KEY_BACKGROUND_COLOR = "backgroundColor"
const val KEY_BOTTOM_BACKGROUND = "bottomBackground"
const val KEY_APPLY_PRIMARYDARK_STATUSBAR = "apply_primarydark_statusbar"
const val KEY_APPLY_PRIMARY_NAVBAR = "apply_primary_navbar"
const val KEY_AUTO_GENERATE_PRIMARYDARK = "auto_generate_primarydark"
const val KEY_COLOR_PRIMARY = "key_color_primary"
const val KEY_COLOR_ON_PRIMARY = "key_color_on_primary"
const val KEY_COLOR_PRIMARY_CONTAINER = "key_color_primary_container"
const val KEY_COLOR_ON_PRIMARY_CONTAINER = "key_color_on_primary_container"
const val KEY_COLOR_SECONDARY = "key_color_secondary"
}
@@ -0,0 +1,44 @@
package io.legato.kazusa.lib.theme
import android.content.Context
import android.graphics.drawable.Drawable
import androidx.annotation.AttrRes
/**
* @author Aidan Follestad (afollestad)
*/
object ThemeUtils {
@JvmOverloads
fun resolveColor(context: Context, @AttrRes attr: Int, fallback: Int = 0): Int {
val a = context.theme.obtainStyledAttributes(intArrayOf(attr))
return try {
a.getColor(0, fallback)
} catch (e: Exception) {
fallback
} finally {
a.recycle()
}
}
@JvmOverloads
fun resolveFloat(context: Context, @AttrRes attr: Int, fallback: Float = 0.0f): Float {
val a = context.theme.obtainStyledAttributes(intArrayOf(attr))
return try {
a.getFloat(0, fallback)
} catch (e: Exception) {
fallback
} finally {
a.recycle()
}
}
fun resolveDrawable(context: Context, @AttrRes attr: Int): Drawable? {
val a = context.theme.obtainStyledAttributes(intArrayOf(attr))
return try {
a.getDrawable(0)
} finally {
a.recycle()
}
}
}
@@ -0,0 +1,487 @@
package io.legato.kazusa.lib.theme
import android.annotation.SuppressLint
import android.content.Context
import android.content.res.ColorStateList
import android.graphics.PorterDuff
import android.graphics.drawable.Drawable
import android.graphics.drawable.RippleDrawable
import android.view.View
import android.widget.Button
import android.widget.CheckBox
import android.widget.CheckedTextView
import android.widget.EditText
import android.widget.ImageView
import android.widget.ProgressBar
import android.widget.RadioButton
import android.widget.SeekBar
import android.widget.Switch
import android.widget.TextView
import androidx.annotation.CheckResult
import androidx.annotation.ColorInt
import androidx.appcompat.widget.AppCompatEditText
import androidx.appcompat.widget.SearchView
import androidx.appcompat.widget.SwitchCompat
import androidx.core.content.ContextCompat
import androidx.core.graphics.drawable.DrawableCompat
import androidx.core.widget.TextViewCompat
import com.google.android.material.floatingactionbutton.FloatingActionButton
import io.legato.kazusa.R
import io.legato.kazusa.utils.ColorUtils
/**
* @author afollestad, plusCubed
*/
@Suppress("MemberVisibilityCanBePrivate")
object TintHelper {
@SuppressLint("PrivateResource")
@ColorInt
private fun getDefaultRippleColor(context: Context, useDarkRipple: Boolean): Int {
// Light ripple is actually translucent black, and vice versa
return ContextCompat.getColor(
context, if (useDarkRipple)
androidx.appcompat.R.color.ripple_material_light
else
androidx.appcompat.R.color.ripple_material_dark
)
}
private fun getDisabledColorStateList(
@ColorInt normal: Int,
@ColorInt disabled: Int
): ColorStateList {
return ColorStateList(
arrayOf(
intArrayOf(-android.R.attr.state_enabled),
intArrayOf(android.R.attr.state_enabled)
), intArrayOf(disabled, normal)
)
}
fun setTintSelector(view: View, @ColorInt color: Int, darker: Boolean, useDarkTheme: Boolean) {
val isColorLight = ColorUtils.isColorLight(color)
val disabled = ContextCompat.getColor(
view.context,
if (useDarkTheme) R.color.ate_button_disabled_dark else R.color.ate_button_disabled_light
)
val pressed = ColorUtils.shiftColor(color, if (darker) 0.9f else 1.1f)
val activated = ColorUtils.shiftColor(color, if (darker) 1.1f else 0.9f)
val rippleColor = getDefaultRippleColor(view.context, isColorLight)
val textColor = ContextCompat.getColor(
view.context,
if (isColorLight) R.color.ate_primary_text_light else R.color.ate_primary_text_dark
)
val sl: ColorStateList
when (view) {
is Button -> {
sl = getDisabledColorStateList(color, disabled)
if (view.getBackground() is RippleDrawable) {
val rd = view.getBackground() as RippleDrawable
rd.setColor(ColorStateList.valueOf(rippleColor))
}
// Disabled text color state for buttons, may get overridden later by ATE tags
view.setTextColor(
getDisabledColorStateList(
textColor,
ContextCompat.getColor(
view.getContext(),
if (useDarkTheme) R.color.ate_button_text_disabled_dark else R.color.ate_button_text_disabled_light
)
)
)
}
is FloatingActionButton -> {
// FloatingActionButton doesn't support disabled state?
sl = ColorStateList(
arrayOf(
intArrayOf(-android.R.attr.state_pressed),
intArrayOf(android.R.attr.state_pressed)
), intArrayOf(color, pressed)
)
view.rippleColor = rippleColor
view.backgroundTintList = sl
if (view.drawable != null)
view.setImageDrawable(createTintedDrawable(view.drawable, textColor))
return
}
else -> {
sl = ColorStateList(
arrayOf(
intArrayOf(-android.R.attr.state_enabled),
intArrayOf(android.R.attr.state_enabled),
intArrayOf(android.R.attr.state_enabled, android.R.attr.state_pressed),
intArrayOf(android.R.attr.state_enabled, android.R.attr.state_activated),
intArrayOf(android.R.attr.state_enabled, android.R.attr.state_checked)
),
intArrayOf(disabled, color, pressed, activated, activated)
)
}
}
var drawable: Drawable? = view.background
if (drawable != null) {
drawable = createTintedDrawable(drawable, sl)
ViewUtils.setBackgroundCompat(view, drawable)
}
if (view is TextView && view !is Button) {
view.setTextColor(
getDisabledColorStateList(
textColor,
ContextCompat.getColor(
view.getContext(),
if (isColorLight) R.color.ate_text_disabled_light else R.color.ate_text_disabled_dark
)
)
)
}
}
fun setTintAuto(
view: View,
@ColorInt color: Int,
isBackground: Boolean,
isDark: Boolean
) {
var isBg = isBackground
if (!isBg) {
when (view) {
is RadioButton -> setTint(view, color, isDark)
is SeekBar -> setTint(view, color, isDark)
is ProgressBar -> setTint(view, color)
is AppCompatEditText -> setTint(view, color, isDark)
is CheckBox -> setTint(view, color, isDark)
is CheckedTextView -> setTint(view, color, isDark)
is ImageView -> setTint(view, color)
is Switch -> setTint(view, color, isDark)
is SwitchCompat -> setTint(view, color, isDark)
is SearchView -> {
val iconIdS =
intArrayOf(
androidx.appcompat.R.id.search_button,
androidx.appcompat.R.id.search_close_btn,
androidx.appcompat.R.id.search_go_btn,
androidx.appcompat.R.id.search_voice_btn,
androidx.appcompat.R.id.search_mag_icon
)
for (iconId in iconIdS) {
val icon = view.findViewById<ImageView>(iconId)
if (icon != null) {
setTint(icon, color)
}
}
}
else -> isBg = true
}
if (!isBg && view.background is RippleDrawable) {
// Ripples for the above views (e.g. when you tap and hold a switch or checkbox)
val rd = view.background as RippleDrawable
@SuppressLint("PrivateResource") val unchecked = ContextCompat.getColor(
view.context,
if (isDark) androidx.appcompat.R.color.ripple_material_dark else androidx.appcompat.R.color.ripple_material_light
)
val checked = ColorUtils.adjustAlpha(color, 0.4f)
val sl = ColorStateList(
arrayOf(
intArrayOf(-android.R.attr.state_activated, -android.R.attr.state_checked),
intArrayOf(android.R.attr.state_activated),
intArrayOf(android.R.attr.state_checked)
),
intArrayOf(unchecked, checked, checked)
)
rd.setColor(sl)
}
}
if (isBg) {
// Need to tint the isBackground of a view
if (view is FloatingActionButton || view is Button) {
setTintSelector(view, color, false, isDark)
} else if (view.background != null) {
var drawable: Drawable? = view.background
if (drawable != null) {
drawable = createTintedDrawable(drawable, color)
ViewUtils.setBackgroundCompat(view, drawable)
}
}
}
}
@SuppressLint("PrivateResource")
fun setTint(radioButton: RadioButton, @ColorInt color: Int, useDarker: Boolean) {
val sl = ColorStateList(
arrayOf(
intArrayOf(-android.R.attr.state_enabled),
intArrayOf(android.R.attr.state_enabled, -android.R.attr.state_checked),
intArrayOf(android.R.attr.state_enabled, android.R.attr.state_checked)
), intArrayOf(
// Radio button includes own alpha for disabled state
ColorUtils.stripAlpha(
ContextCompat.getColor(
radioButton.context,
if (useDarker) R.color.ate_control_disabled_dark else R.color.ate_control_disabled_light
)
),
ContextCompat.getColor(
radioButton.context,
if (useDarker) R.color.ate_control_normal_dark else R.color.ate_control_normal_light
),
color
)
)
radioButton.buttonTintList = sl
}
fun setTint(seekBar: SeekBar, @ColorInt color: Int, useDarker: Boolean) {
val s1 = getDisabledColorStateList(
color,
ContextCompat.getColor(
seekBar.context,
if (useDarker) R.color.ate_control_disabled_dark else R.color.ate_control_disabled_light
)
)
seekBar.thumbTintList = s1
seekBar.progressTintList = s1
}
@JvmOverloads
fun setTint(
progressBar: ProgressBar, @ColorInt color: Int,
skipIndeterminate: Boolean = false
) {
val sl = ColorStateList.valueOf(color)
progressBar.progressTintList = sl
progressBar.secondaryProgressTintList = sl
if (!skipIndeterminate)
progressBar.indeterminateTintList = sl
}
@SuppressLint("RestrictedApi")
fun setTint(editText: AppCompatEditText, @ColorInt color: Int, useDarker: Boolean) {
val editTextColorStateList = ColorStateList(
arrayOf(
intArrayOf(-android.R.attr.state_enabled),
intArrayOf(
android.R.attr.state_enabled,
-android.R.attr.state_pressed,
-android.R.attr.state_focused
),
intArrayOf()
),
intArrayOf(
ContextCompat.getColor(
editText.context,
if (useDarker) R.color.ate_text_disabled_dark else R.color.ate_text_disabled_light
),
ContextCompat.getColor(
editText.context,
if (useDarker) R.color.ate_control_normal_dark else R.color.ate_control_normal_light
),
color
)
)
editText.supportBackgroundTintList = editTextColorStateList
setCursorTint(editText, color)
}
@SuppressLint("PrivateResource")
fun setTint(box: CheckBox, @ColorInt color: Int, useDarker: Boolean) {
val sl = ColorStateList(
arrayOf(
intArrayOf(-android.R.attr.state_enabled),
intArrayOf(android.R.attr.state_enabled, -android.R.attr.state_checked),
intArrayOf(android.R.attr.state_enabled, android.R.attr.state_checked)
),
intArrayOf(
ContextCompat.getColor(
box.context,
if (useDarker) R.color.ate_control_disabled_dark else R.color.ate_control_disabled_light
),
ContextCompat.getColor(
box.context,
if (useDarker) R.color.ate_control_normal_dark else R.color.ate_control_normal_light
),
color
)
)
box.buttonTintList = sl
}
@SuppressLint("PrivateResource")
fun setTint(checkedTextView: CheckedTextView, @ColorInt color: Int, useDarker: Boolean) {
val sl = ColorStateList(
arrayOf(
intArrayOf(-android.R.attr.state_enabled),
intArrayOf(android.R.attr.state_enabled, -android.R.attr.state_checked),
intArrayOf(android.R.attr.state_enabled, android.R.attr.state_checked)
), intArrayOf(
ContextCompat.getColor(
checkedTextView.context,
if (useDarker) R.color.ate_control_disabled_dark else R.color.ate_control_disabled_light
),
ContextCompat.getColor(
checkedTextView.context,
if (useDarker) R.color.ate_control_normal_dark else R.color.ate_control_normal_light
),
color
)
)
checkedTextView.checkMarkTintList = sl
TextViewCompat.setCompoundDrawableTintList(checkedTextView, sl)
}
fun setTint(image: ImageView, @ColorInt color: Int) {
image.setColorFilter(color, PorterDuff.Mode.SRC_ATOP)
}
private fun modifySwitchDrawable(
context: Context,
from: Drawable,
@ColorInt tint: Int,
thumb: Boolean,
compatSwitch: Boolean,
useDarker: Boolean
): Drawable? {
var tint1 = tint
if (useDarker) {
tint1 = ColorUtils.shiftColor(tint1, 1.1f)
}
tint1 = ColorUtils.adjustAlpha(tint1, if (compatSwitch && !thumb) 0.5f else 1.0f)
val disabled: Int
var normal: Int
if (thumb) {
disabled = ContextCompat.getColor(
context,
if (useDarker) R.color.ate_switch_thumb_disabled_dark else R.color.ate_switch_thumb_disabled_light
)
normal = ContextCompat.getColor(
context,
if (useDarker) R.color.ate_switch_thumb_normal_dark else R.color.ate_switch_thumb_normal_light
)
} else {
disabled = ContextCompat.getColor(
context,
if (useDarker) R.color.ate_switch_track_disabled_dark else R.color.ate_switch_track_disabled_light
)
normal = ContextCompat.getColor(
context,
if (useDarker) R.color.ate_switch_track_normal_dark else R.color.ate_switch_track_normal_light
)
}
// Stock switch includes its own alpha
if (!compatSwitch) {
normal = ColorUtils.stripAlpha(normal)
}
val sl = ColorStateList(
arrayOf(
intArrayOf(-android.R.attr.state_enabled),
intArrayOf(
android.R.attr.state_enabled,
-android.R.attr.state_activated,
-android.R.attr.state_checked
),
intArrayOf(android.R.attr.state_enabled, android.R.attr.state_activated),
intArrayOf(android.R.attr.state_enabled, android.R.attr.state_checked)
),
intArrayOf(disabled, normal, tint1, tint1)
)
return createTintedDrawable(from, sl)
}
fun setTint(
@SuppressLint("UseSwitchCompatOrMaterialCode") switchView: Switch,
@ColorInt color: Int,
useDarker: Boolean
) {
if (switchView.trackDrawable != null) {
switchView.trackDrawable = modifySwitchDrawable(
switchView.context,
switchView.trackDrawable,
color,
thumb = false,
compatSwitch = false,
useDarker = useDarker
)
}
if (switchView.thumbDrawable != null) {
switchView.thumbDrawable = modifySwitchDrawable(
switchView.context,
switchView.thumbDrawable,
color,
thumb = true,
compatSwitch = false,
useDarker = useDarker
)
}
}
fun setTint(switchView: SwitchCompat, @ColorInt color: Int, useDarker: Boolean) {
if (switchView.trackDrawable != null) {
switchView.trackDrawable = modifySwitchDrawable(
switchView.context,
switchView.trackDrawable,
color,
thumb = false,
compatSwitch = true,
useDarker = useDarker
)
}
if (switchView.thumbDrawable != null) {
switchView.thumbDrawable = modifySwitchDrawable(
switchView.context,
switchView.thumbDrawable,
color,
thumb = true,
compatSwitch = true,
useDarker = useDarker
)
}
}
// This returns a NEW Drawable because of the mutate() call. The mutate() call is necessary because Drawables with the same resource have shared states otherwise.
@CheckResult
fun createTintedDrawable(drawable: Drawable?, @ColorInt color: Int): Drawable? {
var drawable1: Drawable? = drawable ?: return null
drawable1 = DrawableCompat.wrap(drawable1!!.mutate())
DrawableCompat.setTintMode(drawable1, PorterDuff.Mode.SRC_IN)
DrawableCompat.setTint(drawable1, color)
return drawable1
}
// This returns a NEW Drawable because of the mutate() call. The mutate() call is necessary because Drawables with the same resource have shared states otherwise.
@CheckResult
fun createTintedDrawable(drawable: Drawable?, sl: ColorStateList): Drawable? {
var drawable1: Drawable? = drawable ?: return null
drawable1 = DrawableCompat.wrap(drawable1!!.mutate())
DrawableCompat.setTintList(drawable1, sl)
return drawable1
}
@SuppressLint("DiscouragedPrivateApi", "SoonBlockedPrivateApi")
fun setCursorTint(editText: EditText, @ColorInt color: Int) {
try {
val fCursorDrawableRes = TextView::class.java.getDeclaredField("mCursorDrawableRes")
fCursorDrawableRes.isAccessible = true
val mCursorDrawableRes = fCursorDrawableRes.getInt(editText)
val fEditor = TextView::class.java.getDeclaredField("mEditor")
fEditor.isAccessible = true
val editor = fEditor.get(editText)
val clazz = editor.javaClass
val fCursorDrawable = clazz.getDeclaredField("mCursorDrawable")
fCursorDrawable.isAccessible = true
val drawables = arrayOfNulls<Drawable>(2)
drawables[0] = ContextCompat.getDrawable(editText.context, mCursorDrawableRes)
drawables[0] = createTintedDrawable(drawables[0], color)
drawables[1] = ContextCompat.getDrawable(editText.context, mCursorDrawableRes)
drawables[1] = createTintedDrawable(drawables[1], color)
fCursorDrawable.set(editor, drawables)
} catch (ignored: Exception) {
}
}
}
@@ -0,0 +1,43 @@
package io.legato.kazusa.lib.theme
import android.graphics.drawable.ColorDrawable
import android.graphics.drawable.Drawable
import android.graphics.drawable.TransitionDrawable
import android.view.View
import android.view.ViewTreeObserver
import androidx.annotation.ColorInt
import io.legato.kazusa.utils.DrawableUtils
/**
* @author Karim Abou Zeid (kabouzeid)
*/
@Suppress("unused")
object ViewUtils {
fun removeOnGlobalLayoutListener(v: View, listener: ViewTreeObserver.OnGlobalLayoutListener) {
v.viewTreeObserver.removeOnGlobalLayoutListener(listener)
}
fun setBackgroundCompat(view: View, drawable: Drawable?) {
view.background = drawable
}
fun setBackgroundTransition(view: View, newDrawable: Drawable): TransitionDrawable {
val transition = DrawableUtils.createTransitionDrawable(view.background, newDrawable)
setBackgroundCompat(view, transition)
return transition
}
fun setBackgroundColorTransition(view: View, @ColorInt newColor: Int): TransitionDrawable {
val oldColor = view.background
val start = oldColor ?: ColorDrawable(view.solidColor)
val end = ColorDrawable(newColor)
val transition = DrawableUtils.createTransitionDrawable(start, end)
setBackgroundCompat(view, transition)
return transition
}
}
@@ -0,0 +1,50 @@
package io.legato.kazusa.lib.theme.view
import android.content.Context
import android.graphics.Color
import android.util.AttributeSet
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.core.view.ViewCompat
import com.google.android.material.bottomnavigation.BottomNavigationView
import io.legato.kazusa.databinding.ViewNavigationBadgeBinding
import io.legato.kazusa.help.config.AppConfig
//import io.legado.app.lib.theme.bottomBackground
//import io.legado.app.lib.theme.getSecondaryTextColor
import io.legato.kazusa.ui.widget.text.BadgeView
import androidx.core.graphics.drawable.toDrawable
class ThemeBottomNavigationVIew(context: Context, attrs: AttributeSet) :
BottomNavigationView(context, attrs) {
init {
// val bgColor = context.bottomBackground
// setBackgroundColor(bgColor)
// val textIsDark = ColorUtils.isColorLight(bgColor)
// val textColor = context.getSecondaryTextColor(textIsDark)
//
// val colorStateList = Selector.colorBuild()
// .setDefaultColor(textColor)
// .setSelectedColor(ThemeStore.accentColor(context)).create()
// itemIconTintList = colorStateList
// itemTextColor = colorStateList
if (AppConfig.isEInkMode) {
isItemHorizontalTranslationEnabled = false
itemBackground = Color.TRANSPARENT.toDrawable()
}
ViewCompat.setOnApplyWindowInsetsListener(this, null)
}
fun addBadgeView(index: Int): BadgeView {
//获取底部菜单view
val menuView = getChildAt(0) as ViewGroup
//获取第index个itemView
val itemView = menuView.getChildAt(index) as ViewGroup
val badgeBinding = ViewNavigationBadgeBinding.inflate(LayoutInflater.from(context))
itemView.addView(badgeBinding.root)
return badgeBinding.viewBadge
}
}
@@ -0,0 +1,15 @@
package io.legato.kazusa.lib.theme.view
import android.content.Context
import android.util.AttributeSet
import com.google.android.material.checkbox.MaterialCheckBox
//import io.legado.app.lib.theme.accentColor
class ThemeCheckBox(context: Context, attrs: AttributeSet) : MaterialCheckBox(context, attrs) {
// init {
// if (!isInEditMode) {
// applyTint(context.accentColor)
// }
// }
}
@@ -0,0 +1,21 @@
package io.legato.kazusa.lib.theme.view
import android.content.Context
import android.util.AttributeSet
import androidx.appcompat.widget.AppCompatEditText
//import io.legado.app.lib.theme.accentColor
class ThemeEditText @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null
) : AppCompatEditText(context, attrs) {
// init {
// if (!isInEditMode) {
// applyTint(context.accentColor)
// }
// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM) {
// isLocalePreferredLineHeightForMinimumUsed = false
// }
// }
}
@@ -0,0 +1,15 @@
package io.legato.kazusa.lib.theme.view
import android.content.Context
import android.util.AttributeSet
import android.widget.ProgressBar
//import io.legado.app.lib.theme.accentColor
class ThemeProgressBar(context: Context, attrs: AttributeSet) : ProgressBar(context, attrs) {
// init {
// if (!isInEditMode) {
// applyTint(context.accentColor)
// }
// }
}
@@ -0,0 +1,16 @@
package io.legato.kazusa.lib.theme.view
import android.content.Context
import android.util.AttributeSet
import com.google.android.material.radiobutton.MaterialRadioButton
//import io.legado.app.lib.theme.accentColor
class ThemeRadioButton(context: Context, attrs: AttributeSet) :
MaterialRadioButton(context, attrs) {
// init {
// if (!isInEditMode) {
// applyTint(context.accentColor)
// }
// }
}
@@ -0,0 +1,78 @@
package io.legato.kazusa.lib.theme.view
import android.content.Context
import android.util.AttributeSet
import androidx.appcompat.widget.TooltipCompat
import com.google.android.material.radiobutton.MaterialRadioButton
import io.legato.kazusa.R
//import io.legado.app.lib.theme.accentColor
//import io.legado.app.lib.theme.bottomBackground
//import io.legado.app.lib.theme.getPrimaryTextColor
class ThemeRadioNoButton(context: Context, attrs: AttributeSet) :
MaterialRadioButton(context, attrs) {
private val isBottomBackground: Boolean
init {
val typedArray = context.obtainStyledAttributes(attrs, R.styleable.ThemeRadioNoButton)
isBottomBackground =
typedArray.getBoolean(R.styleable.ThemeRadioNoButton_isBottomBackground, false)
typedArray.recycle()
initTheme()
TooltipCompat.setTooltipText(this, text)
}
private fun initTheme() {
// when {
// isInEditMode -> Unit
// isBottomBackground -> {
// val accentColor = context.accentColor
// val isLight = ColorUtils.isColorLight(context.bottomBackground)
// val textColor = context.getPrimaryTextColor(isLight)
// val checkedTextColor = if (ColorUtils.isColorLight(accentColor)) {
// Color.BLACK
// } else {
// Color.WHITE
// }
// background = Selector.shapeBuild()
// .setCornerRadius(2.dpToPx())
// .setStrokeWidth(2.dpToPx())
// .setCheckedBgColor(accentColor)
// .setCheckedStrokeColor(accentColor)
// .setDefaultStrokeColor(textColor)
// .create()
// setTextColor(
// Selector.colorBuild()
// .setDefaultColor(textColor)
// .setCheckedColor(checkedTextColor)
// .create()
// )
// }
// else -> {
// val accentColor = context.accentColor
// val defaultTextColor = context.getCompatColor(R.color.primaryText)
// val checkedTextColor = if (ColorUtils.isColorLight(accentColor)) {
// Color.BLACK
// } else {
// Color.WHITE
// }
// background = Selector.shapeBuild()
// .setCornerRadius(2.dpToPx())
// .setStrokeWidth(2.dpToPx())
// .setCheckedBgColor(accentColor)
// .setCheckedStrokeColor(accentColor)
// .setDefaultStrokeColor(defaultTextColor)
// .create()
// setTextColor(
// Selector.colorBuild()
// .setDefaultColor(defaultTextColor)
// .setCheckedColor(checkedTextColor)
// .create()
// )
// }
// }
}
}
@@ -0,0 +1,18 @@
package io.legato.kazusa.lib.theme.view
import android.content.Context
import android.util.AttributeSet
import com.google.android.material.slider.Slider
//import io.legado.app.lib.theme.accentColor
/**
* @author Aidan Follestad (afollestad)
*/
class ThemeSlider(context: Context, attrs: AttributeSet) : Slider(context, attrs) {
// init {
// if (!isInEditMode) {
// applyTint(context.accentColor)
// }
// }
}
@@ -0,0 +1,20 @@
package io.legato.kazusa.lib.theme.view
import android.content.Context
import android.util.AttributeSet
import com.google.android.material.materialswitch.MaterialSwitch
//import io.legado.app.lib.theme.accentColor
/**
* @author Aidan Follestad (afollestad)
*/
class ThemeSwitch(context: Context, attrs: AttributeSet) : MaterialSwitch(context, attrs) {
// init {
// if (!isInEditMode) {
// applyTint(context.accentColor)
// }
//
// }
}