up Theme lib

pull/32/head
GKF 6 years ago
parent e29e6fcbaa
commit 9cd970ccc1
  1. 9
      app/src/main/java/io/legado/app/base/BaseActivity.kt
  2. 118
      app/src/main/java/io/legado/app/lib/theme/ATH.java
  3. 116
      app/src/main/java/io/legado/app/lib/theme/ATH.kt
  4. 31
      app/src/main/java/io/legado/app/lib/theme/ATHUtil.java
  5. 24
      app/src/main/java/io/legado/app/lib/theme/ATHUtil.kt
  6. 80
      app/src/main/java/io/legado/app/lib/theme/ColorUtil.java
  7. 79
      app/src/main/java/io/legado/app/lib/theme/ColorUtil.kt
  8. 28
      app/src/main/java/io/legado/app/lib/theme/DrawableUtil.java
  9. 25
      app/src/main/java/io/legado/app/lib/theme/DrawableUtil.kt
  10. 52
      app/src/main/java/io/legado/app/lib/theme/MaterialValueHelper.java
  11. 45
      app/src/main/java/io/legado/app/lib/theme/MaterialValueHelper.kt
  12. 55
      app/src/main/java/io/legado/app/lib/theme/NavigationViewUtil.java
  13. 38
      app/src/main/java/io/legado/app/lib/theme/NavigationViewUtil.kt
  14. 430
      app/src/main/java/io/legado/app/lib/theme/Selector.java
  15. 443
      app/src/main/java/io/legado/app/lib/theme/Selector.kt
  16. 318
      app/src/main/java/io/legado/app/lib/theme/ThemeStore.java
  17. 309
      app/src/main/java/io/legado/app/lib/theme/ThemeStore.kt
  18. 92
      app/src/main/java/io/legado/app/lib/theme/ThemeStoreInterface.java
  19. 92
      app/src/main/java/io/legado/app/lib/theme/ThemeStoreInterface.kt
  20. 29
      app/src/main/java/io/legado/app/lib/theme/ThemeStorePrefKeys.java
  21. 31
      app/src/main/java/io/legado/app/lib/theme/ThemeStorePrefKeys.kt
  22. 384
      app/src/main/java/io/legado/app/lib/theme/TintHelper.java
  23. 455
      app/src/main/java/io/legado/app/lib/theme/TintHelper.kt
  24. 48
      app/src/main/java/io/legado/app/lib/theme/ViewUtil.java
  25. 41
      app/src/main/java/io/legado/app/lib/theme/ViewUtil.kt

@ -20,6 +20,7 @@ abstract class BaseActivity<VM : ViewModel> : AppCompatActivity() {
protected abstract val layoutID: Int
override fun onCreate(savedInstanceState: Bundle?) {
initTheme()
super.onCreate(savedInstanceState)
setContentView(layoutID)
onViewModelCreated(viewModel, savedInstanceState)
@ -88,4 +89,12 @@ abstract class BaseActivity<VM : ViewModel> : AppCompatActivity() {
open fun onCompatOptionsItemSelected(item: MenuItem): Boolean {
return true
}
protected fun initTheme() {
if (ColorUtil.isColorLight(ThemeStore.primaryColor(this))) {
setTheme(R.style.CAppTheme)
} else {
setTheme(R.style.CAppThemeBarDark)
}
}
}

@ -1,118 +0,0 @@
package io.legado.app.lib.theme;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.ActivityManager;
import android.content.Context;
import android.content.res.ColorStateList;
import android.os.Build;
import android.view.View;
import androidx.annotation.ColorInt;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import static android.view.View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR;
/**
* @author Karim Abou Zeid (kabouzeid)
*/
public final class ATH {
@SuppressLint("CommitPrefEdits")
public static boolean didThemeValuesChange(@NonNull Context context, long since) {
return ThemeStore.isConfigured(context) && ThemeStore.prefs(context).getLong(ThemeStore.VALUES_CHANGED, -1) > since;
}
public static void setStatusbarColorAuto(Activity activity) {
setStatusbarColor(activity, ThemeStore.statusBarColor(activity));
}
public static void setStatusbarColor(Activity activity, int color) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
activity.getWindow().setStatusBarColor(color);
setLightStatusbarAuto(activity, color);
}
}
public static void setLightStatusbarAuto(Activity activity, int bgColor) {
setLightStatusbar(activity, ColorUtil.isColorLight(bgColor));
}
public static void setLightStatusbar(Activity activity, boolean enabled) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
final View decorView = activity.getWindow().getDecorView();
final int systemUiVisibility = decorView.getSystemUiVisibility();
if (enabled) {
decorView.setSystemUiVisibility(systemUiVisibility | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
} else {
decorView.setSystemUiVisibility(systemUiVisibility & ~View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
}
}
}
public static void setLightNavigationbar(Activity activity, boolean enabled) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
final View decorView = activity.getWindow().getDecorView();
int systemUiVisibility = decorView.getSystemUiVisibility();
if (enabled) {
systemUiVisibility |= SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR;
} else {
systemUiVisibility &= ~SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR;
}
decorView.setSystemUiVisibility(systemUiVisibility);
}
}
public static void setLightNavigationbarAuto(Activity activity, int bgColor) {
setLightNavigationbar(activity, ColorUtil.isColorLight(bgColor));
}
public static void setNavigationbarColorAuto(Activity activity) {
setNavigationbarColor(activity, ThemeStore.navigationBarColor(activity));
}
public static void setNavigationbarColor(Activity activity, int color) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
activity.getWindow().setNavigationBarColor(color);
setLightNavigationbarAuto(activity, color);
}
}
public static void setTaskDescriptionColorAuto(@NonNull Activity activity) {
setTaskDescriptionColor(activity, ThemeStore.primaryColor(activity));
}
public static void setTaskDescriptionColor(@NonNull Activity activity, @ColorInt int color) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
// Task description requires fully opaque color
color = ColorUtil.stripAlpha(color);
// Sets color of entry in the system recents page
activity.setTaskDescription(new ActivityManager.TaskDescription((String) activity.getTitle(), null, color));
}
}
public static void setTint(@NonNull View view, @ColorInt int color) {
TintHelper.setTintAuto(view, color, false);
}
public static void setBackgroundTint(@NonNull View view, @ColorInt int color) {
TintHelper.setTintAuto(view, color, true);
}
public static AlertDialog setAlertDialogTint(@NonNull AlertDialog dialog) {
ColorStateList colorStateList = Selector.colorBuild()
.setDefaultColor(ThemeStore.accentColor(dialog.getContext()))
.setPressedColor(ColorUtil.darkenColor(ThemeStore.accentColor(dialog.getContext())))
.create();
if (dialog.getButton(androidx.appcompat.app.AlertDialog.BUTTON_NEGATIVE) != null) {
dialog.getButton(androidx.appcompat.app.AlertDialog.BUTTON_NEGATIVE).setTextColor(colorStateList);
}
if (dialog.getButton(androidx.appcompat.app.AlertDialog.BUTTON_POSITIVE) != null) {
dialog.getButton(androidx.appcompat.app.AlertDialog.BUTTON_POSITIVE).setTextColor(colorStateList);
}
return dialog;
}
private ATH() {
}
}

@ -0,0 +1,116 @@
package io.legado.app.lib.theme
import android.annotation.SuppressLint
import android.app.Activity
import android.app.ActivityManager
import android.content.Context
import android.os.Build
import android.view.View
import android.view.View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR
import androidx.annotation.ColorInt
import androidx.appcompat.app.AlertDialog
/**
* @author Karim Abou Zeid (kabouzeid)
*/
object ATH {
@SuppressLint("CommitPrefEdits")
fun didThemeValuesChange(context: Context, since: Long): Boolean {
return ThemeStore.isConfigured(context) && ThemeStore.prefs(context).getLong(
ThemeStore.VALUES_CHANGED,
-1
) > since
}
fun setStatusbarColorAuto(activity: Activity) {
setStatusbarColor(activity, ThemeStore.statusBarColor(activity))
}
fun setStatusbarColor(activity: Activity, color: Int) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
activity.window.statusBarColor = color
setLightStatusbarAuto(activity, color)
}
}
fun setLightStatusbarAuto(activity: Activity, bgColor: Int) {
setLightStatusbar(activity, ColorUtil.isColorLight(bgColor))
}
fun setLightStatusbar(activity: Activity, enabled: Boolean) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
val decorView = activity.window.decorView
val systemUiVisibility = decorView.systemUiVisibility
if (enabled) {
decorView.systemUiVisibility = systemUiVisibility or View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
} else {
decorView.systemUiVisibility = systemUiVisibility and View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR.inv()
}
}
}
fun setLightNavigationbar(activity: Activity, enabled: Boolean) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val decorView = activity.window.decorView
var systemUiVisibility = decorView.systemUiVisibility
if (enabled) {
systemUiVisibility = systemUiVisibility or SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR
} else {
systemUiVisibility = systemUiVisibility and SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR.inv()
}
decorView.systemUiVisibility = systemUiVisibility
}
}
fun setLightNavigationbarAuto(activity: Activity, bgColor: Int) {
setLightNavigationbar(activity, ColorUtil.isColorLight(bgColor))
}
fun setNavigationbarColorAuto(activity: Activity) {
setNavigationbarColor(activity, ThemeStore.navigationBarColor(activity))
}
fun setNavigationbarColor(activity: Activity, color: Int) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
activity.window.navigationBarColor = color
setLightNavigationbarAuto(activity, color)
}
}
fun setTaskDescriptionColorAuto(activity: Activity) {
setTaskDescriptionColor(activity, ThemeStore.primaryColor(activity))
}
fun setTaskDescriptionColor(activity: Activity, @ColorInt color: Int) {
val color1: Int
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
// Task description requires fully opaque color
color1 = ColorUtil.stripAlpha(color)
// Sets color of entry in the system recents page
activity.setTaskDescription(ActivityManager.TaskDescription(activity.title as String, null, color1))
}
}
fun setTint(view: View, @ColorInt color: Int) {
TintHelper.setTintAuto(view, color, false)
}
fun setBackgroundTint(view: View, @ColorInt color: Int) {
TintHelper.setTintAuto(view, color, true)
}
fun setAlertDialogTint(dialog: AlertDialog): AlertDialog {
val colorStateList = Selector.colorBuild()
.setDefaultColor(ThemeStore.accentColor(dialog.context))
.setPressedColor(ColorUtil.darkenColor(ThemeStore.accentColor(dialog.context)))
.create()
if (dialog.getButton(AlertDialog.BUTTON_NEGATIVE) != null) {
dialog.getButton(AlertDialog.BUTTON_NEGATIVE).setTextColor(colorStateList)
}
if (dialog.getButton(AlertDialog.BUTTON_POSITIVE) != null) {
dialog.getButton(AlertDialog.BUTTON_POSITIVE).setTextColor(colorStateList)
}
return dialog
}
}

@ -1,31 +0,0 @@
package io.legado.app.lib.theme;
import android.content.Context;
import android.content.res.TypedArray;
import androidx.annotation.AttrRes;
/**
* @author Aidan Follestad (afollestad)
*/
public final class ATHUtil {
public static boolean isWindowBackgroundDark(Context context) {
return !ColorUtil.isColorLight(ATHUtil.resolveColor(context, android.R.attr.windowBackground));
}
public static int resolveColor(Context context, @AttrRes int attr) {
return resolveColor(context, attr, 0);
}
public static int resolveColor(Context context, @AttrRes int attr, int fallback) {
TypedArray a = context.getTheme().obtainStyledAttributes(new int[]{attr});
try {
return a.getColor(0, fallback);
} finally {
a.recycle();
}
}
private ATHUtil() {
}
}

@ -0,0 +1,24 @@
package io.legado.app.lib.theme
import android.content.Context
import androidx.annotation.AttrRes
/**
* @author Aidan Follestad (afollestad)
*/
object ATHUtil {
fun isWindowBackgroundDark(context: Context): Boolean {
return !ColorUtil.isColorLight(ATHUtil.resolveColor(context, android.R.attr.windowBackground))
}
@JvmOverloads
fun resolveColor(context: Context, @AttrRes attr: Int, fallback: Int = 0): Int {
val a = context.theme.obtainStyledAttributes(intArrayOf(attr))
try {
return a.getColor(0, fallback)
} finally {
a.recycle()
}
}
}

@ -1,80 +0,0 @@
package io.legado.app.lib.theme;
import android.graphics.Color;
import androidx.annotation.ColorInt;
import androidx.annotation.FloatRange;
@SuppressWarnings({"unused", "WeakerAccess"})
public class ColorUtil {
public static String intToString(int intColor) {
return String.format("#%06X", 0xFFFFFF & intColor);
}
public static int stripAlpha(@ColorInt int color) {
return 0xff000000 | color;
}
@ColorInt
public static int shiftColor(@ColorInt int color, @FloatRange(from = 0.0f, to = 2.0f) float by) {
if (by == 1f) return color;
int alpha = Color.alpha(color);
float[] hsv = new float[3];
Color.colorToHSV(color, hsv);
hsv[2] *= by; // value component
return (alpha << 24) + (0x00ffffff & Color.HSVToColor(hsv));
}
@ColorInt
public static int darkenColor(@ColorInt int color) {
return shiftColor(color, 0.9f);
}
@ColorInt
public static int lightenColor(@ColorInt int color) {
return shiftColor(color, 1.1f);
}
public static boolean isColorLight(@ColorInt int color) {
final double darkness = 1 - (0.299 * Color.red(color) + 0.587 * Color.green(color) + 0.114 * Color.blue(color)) / 255;
return darkness < 0.4;
}
@ColorInt
public static int invertColor(@ColorInt int color) {
final int r = 255 - Color.red(color);
final int g = 255 - Color.green(color);
final int b = 255 - Color.blue(color);
return Color.argb(Color.alpha(color), r, g, b);
}
@ColorInt
public static int adjustAlpha(@ColorInt int color, @FloatRange(from = 0.0, to = 1.0) float factor) {
int alpha = Math.round(Color.alpha(color) * factor);
int red = Color.red(color);
int green = Color.green(color);
int blue = Color.blue(color);
return Color.argb(alpha, red, green, blue);
}
@ColorInt
public static int withAlpha(@ColorInt int baseColor, @FloatRange(from = 0.0, to = 1.0) float alpha) {
int a = Math.min(255, Math.max(0, (int) (alpha * 255))) << 24;
int rgb = 0x00ffffff & baseColor;
return a + rgb;
}
/**
* Taken from CollapsingToolbarLayout's CollapsingTextHelper class.
*/
public static int blendColors(int color1, int color2, @FloatRange(from = 0.0, to = 1.0) float ratio) {
final float inverseRatio = 1f - ratio;
float a = (Color.alpha(color1) * inverseRatio) + (Color.alpha(color2) * ratio);
float r = (Color.red(color1) * inverseRatio) + (Color.red(color2) * ratio);
float g = (Color.green(color1) * inverseRatio) + (Color.green(color2) * ratio);
float b = (Color.blue(color1) * inverseRatio) + (Color.blue(color2) * ratio);
return Color.argb((int) a, (int) r, (int) g, (int) b);
}
}

@ -0,0 +1,79 @@
package io.legado.app.lib.theme
import android.graphics.Color
import androidx.annotation.ColorInt
import androidx.annotation.FloatRange
object ColorUtil {
fun intToString(intColor: Int): String {
return String.format("#%06X", 0xFFFFFF and intColor)
}
fun stripAlpha(@ColorInt color: Int): Int {
return -0x1000000 or color
}
@ColorInt
fun shiftColor(@ColorInt color: Int, @FloatRange(from = 0.0, to = 2.0) by: Float): Int {
if (by == 1f) return color
val alpha = Color.alpha(color)
val hsv = FloatArray(3)
Color.colorToHSV(color, hsv)
hsv[2] *= by // value component
return (alpha shl 24) + (0x00ffffff and Color.HSVToColor(hsv))
}
@ColorInt
fun darkenColor(@ColorInt color: Int): Int {
return shiftColor(color, 0.9f)
}
@ColorInt
fun lightenColor(@ColorInt color: Int): Int {
return shiftColor(color, 1.1f)
}
fun isColorLight(@ColorInt color: Int): Boolean {
val darkness = 1 - (0.299 * Color.red(color) + 0.587 * Color.green(color) + 0.114 * Color.blue(color)) / 255
return darkness < 0.4
}
@ColorInt
fun invertColor(@ColorInt color: Int): Int {
val r = 255 - Color.red(color)
val g = 255 - Color.green(color)
val b = 255 - Color.blue(color)
return Color.argb(Color.alpha(color), r, g, b)
}
@ColorInt
fun adjustAlpha(@ColorInt color: Int, @FloatRange(from = 0.0, to = 1.0) factor: Float): Int {
val alpha = Math.round(Color.alpha(color) * factor)
val red = Color.red(color)
val green = Color.green(color)
val blue = Color.blue(color)
return Color.argb(alpha, red, green, blue)
}
@ColorInt
fun withAlpha(@ColorInt baseColor: Int, @FloatRange(from = 0.0, to = 1.0) alpha: Float): Int {
val a = Math.min(255, Math.max(0, (alpha * 255).toInt())) shl 24
val rgb = 0x00ffffff and baseColor
return a + rgb
}
/**
* Taken from CollapsingToolbarLayout's CollapsingTextHelper class.
*/
fun blendColors(color1: Int, color2: Int, @FloatRange(from = 0.0, to = 1.0) ratio: Float): Int {
val inverseRatio = 1f - ratio
val a = Color.alpha(color1) * inverseRatio + Color.alpha(color2) * ratio
val r = Color.red(color1) * inverseRatio + Color.red(color2) * ratio
val g = Color.green(color1) * inverseRatio + Color.green(color2) * ratio
val b = Color.blue(color1) * inverseRatio + Color.blue(color2) * ratio
return Color.argb(a.toInt(), r.toInt(), g.toInt(), b.toInt())
}
}

@ -1,28 +0,0 @@
package io.legado.app.lib.theme;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.TransitionDrawable;
import androidx.annotation.ColorInt;
/**
* @author Karim Abou Zeid (kabouzeid)
*/
public final class DrawableUtil {
public static TransitionDrawable createTransitionDrawable(@ColorInt int startColor, @ColorInt int endColor) {
return createTransitionDrawable(new ColorDrawable(startColor), new ColorDrawable(endColor));
}
public static TransitionDrawable createTransitionDrawable(Drawable start, Drawable end) {
final Drawable[] drawables = new Drawable[2];
drawables[0] = start;
drawables[1] = end;
return new TransitionDrawable(drawables);
}
private DrawableUtil() {
}
}

@ -0,0 +1,25 @@
package io.legado.app.lib.theme
import android.graphics.drawable.ColorDrawable
import android.graphics.drawable.Drawable
import android.graphics.drawable.TransitionDrawable
import androidx.annotation.ColorInt
/**
* @author Karim Abou Zeid (kabouzeid)
*/
object DrawableUtil {
fun createTransitionDrawable(@ColorInt startColor: Int, @ColorInt endColor: Int): TransitionDrawable {
return createTransitionDrawable(ColorDrawable(startColor), ColorDrawable(endColor))
}
fun createTransitionDrawable(start: Drawable, end: Drawable): TransitionDrawable {
val drawables = arrayOfNulls<Drawable>(2)
drawables[0] = start
drawables[1] = end
return TransitionDrawable(drawables)
}
}

@ -1,52 +0,0 @@
package io.legado.app.lib.theme;
import android.annotation.SuppressLint;
import android.content.Context;
import androidx.annotation.ColorInt;
import androidx.core.content.ContextCompat;
import io.legado.app.R;
/**
* @author Karim Abou Zeid (kabouzeid)
*/
public final class MaterialValueHelper {
@SuppressLint("PrivateResource")
@ColorInt
public static int getPrimaryTextColor(final Context context, boolean dark) {
if (dark) {
return ContextCompat.getColor(context, R.color.primary_text_default_material_light);
}
return ContextCompat.getColor(context, R.color.primary_text_default_material_dark);
}
@SuppressLint("PrivateResource")
@ColorInt
public static int getSecondaryTextColor(final Context context, boolean dark) {
if (dark) {
return ContextCompat.getColor(context, R.color.secondary_text_default_material_light);
}
return ContextCompat.getColor(context, R.color.secondary_text_default_material_dark);
}
@SuppressLint("PrivateResource")
@ColorInt
public static int getPrimaryDisabledTextColor(final Context context, boolean dark) {
if (dark) {
return ContextCompat.getColor(context, R.color.primary_text_disabled_material_light);
}
return ContextCompat.getColor(context, R.color.primary_text_disabled_material_dark);
}
@SuppressLint("PrivateResource")
@ColorInt
public static int getSecondaryDisabledTextColor(final Context context, boolean dark) {
if (dark) {
return ContextCompat.getColor(context, R.color.secondary_text_disabled_material_light);
}
return ContextCompat.getColor(context, R.color.secondary_text_disabled_material_dark);
}
private MaterialValueHelper() {
}
}

@ -0,0 +1,45 @@
package io.legado.app.lib.theme
import android.annotation.SuppressLint
import android.content.Context
import androidx.annotation.ColorInt
import androidx.core.content.ContextCompat
import io.legado.app.R
/**
* @author Karim Abou Zeid (kabouzeid)
*/
object MaterialValueHelper {
@SuppressLint("PrivateResource")
@ColorInt
fun getPrimaryTextColor(context: Context, dark: Boolean): Int {
return if (dark) {
ContextCompat.getColor(context, R.color.primary_text_default_material_light)
} else ContextCompat.getColor(context, R.color.primary_text_default_material_dark)
}
@SuppressLint("PrivateResource")
@ColorInt
fun getSecondaryTextColor(context: Context, dark: Boolean): Int {
return if (dark) {
ContextCompat.getColor(context, R.color.secondary_text_default_material_light)
} else ContextCompat.getColor(context, R.color.secondary_text_default_material_dark)
}
@SuppressLint("PrivateResource")
@ColorInt
fun getPrimaryDisabledTextColor(context: Context, dark: Boolean): Int {
return if (dark) {
ContextCompat.getColor(context, R.color.primary_text_disabled_material_light)
} else ContextCompat.getColor(context, R.color.primary_text_disabled_material_dark)
}
@SuppressLint("PrivateResource")
@ColorInt
fun getSecondaryDisabledTextColor(context: Context, dark: Boolean): Int {
return if (dark) {
ContextCompat.getColor(context, R.color.secondary_text_disabled_material_light)
} else ContextCompat.getColor(context, R.color.secondary_text_disabled_material_dark)
}
}

@ -1,55 +0,0 @@
package io.legado.app.lib.theme;
import android.content.res.ColorStateList;
import androidx.annotation.ColorInt;
import androidx.annotation.NonNull;
import com.google.android.material.internal.NavigationMenuView;
import com.google.android.material.navigation.NavigationView;
/**
* @author Karim Abou Zeid (kabouzeid)
*/
public final class NavigationViewUtil {
public static void setItemIconColors(@NonNull NavigationView navigationView, @ColorInt int normalColor, @ColorInt int selectedColor) {
final ColorStateList iconSl = new ColorStateList(
new int[][]{
new int[]{-android.R.attr.state_checked},
new int[]{android.R.attr.state_checked}
},
new int[]{
normalColor,
selectedColor
});
navigationView.setItemIconTintList(iconSl);
}
public static void setItemTextColors(@NonNull NavigationView navigationView, @ColorInt int normalColor, @ColorInt int selectedColor) {
final ColorStateList textSl = new ColorStateList(
new int[][]{
new int[]{-android.R.attr.state_checked},
new int[]{android.R.attr.state_checked}
},
new int[]{
normalColor,
selectedColor
});
navigationView.setItemTextColor(textSl);
}
/**
* 去掉navigationView的滚动条
* @param navigationView NavigationView
*/
public static void disableScrollbar(NavigationView navigationView) {
if (navigationView != null) {
NavigationMenuView navigationMenuView = (NavigationMenuView) navigationView.getChildAt(0);
if (navigationMenuView != null) {
navigationMenuView.setVerticalScrollBarEnabled(false);
}
}
}
private NavigationViewUtil() {
}
}

@ -0,0 +1,38 @@
package io.legado.app.lib.theme
import android.content.res.ColorStateList
import androidx.annotation.ColorInt
import com.google.android.material.internal.NavigationMenuView
import com.google.android.material.navigation.NavigationView
/**
* @author Karim Abou Zeid (kabouzeid)
*/
object NavigationViewUtil {
fun setItemIconColors(navigationView: NavigationView, @ColorInt normalColor: Int, @ColorInt selectedColor: Int) {
val iconSl = ColorStateList(
arrayOf(intArrayOf(-android.R.attr.state_checked), intArrayOf(android.R.attr.state_checked)),
intArrayOf(normalColor, selectedColor)
)
navigationView.itemIconTintList = iconSl
}
fun setItemTextColors(navigationView: NavigationView, @ColorInt normalColor: Int, @ColorInt selectedColor: Int) {
val textSl = ColorStateList(
arrayOf(intArrayOf(-android.R.attr.state_checked), intArrayOf(android.R.attr.state_checked)),
intArrayOf(normalColor, selectedColor)
)
navigationView.itemTextColor = textSl
}
/**
* 去掉navigationView的滚动条
* @param navigationView NavigationView
*/
fun disableScrollbar(navigationView: NavigationView?) {
navigationView ?: return
val navigationMenuView = navigationView.getChildAt(0) as? NavigationMenuView
navigationMenuView?.isVerticalScrollBarEnabled = false
}
}

@ -1,430 +0,0 @@
package io.legado.app.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;
public class Selector {
public static ShapeSelector shapeBuild() {
return new ShapeSelector();
}
public static ColorSelector colorBuild() {
return new ColorSelector();
}
public static DrawableSelector drawableBuild() {
return new DrawableSelector();
}
/**
* 形状ShapeSelector
*
* @author hjy
* created at 2017/12/11 22:26
*/
public static final class ShapeSelector {
@IntDef({GradientDrawable.RECTANGLE, GradientDrawable.OVAL,
GradientDrawable.LINE, GradientDrawable.RING})
private @interface Shape {
}
private int mShape; //the shape of background
private int mDefaultBgColor; //default background color
private int mDisabledBgColor; //state_enabled = false
private int mPressedBgColor; //state_pressed = true
private int mSelectedBgColor; //state_selected = true
private int mFocusedBgColor; //state_focused = true
private int mCheckedBgColor; //state_checked = true
private int mStrokeWidth; //stroke width in pixel
private int mDefaultStrokeColor; //default stroke color
private int mDisabledStrokeColor; //state_enabled = false
private int mPressedStrokeColor; //state_pressed = true
private int mSelectedStrokeColor; //state_selected = true
private int mFocusedStrokeColor; //state_focused = true
private int mCheckedStrokeColor; //state_checked = true
private int mCornerRadius; //corner radius
private boolean hasSetDisabledBgColor = false;
private boolean hasSetPressedBgColor = false;
private boolean hasSetSelectedBgColor = false;
private boolean hasSetFocusedBgColor = false;
private boolean hasSetCheckedBgColor = false;
private boolean hasSetDisabledStrokeColor = false;
private boolean hasSetPressedStrokeColor = false;
private boolean hasSetSelectedStrokeColor = false;
private boolean hasSetFocusedStrokeColor = false;
private boolean hasSetCheckedStrokeColor = false;
public ShapeSelector() {
//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;
}
public ShapeSelector setShape(@Shape int shape) {
mShape = shape;
return this;
}
public ShapeSelector setDefaultBgColor(@ColorInt int color) {
mDefaultBgColor = color;
if (!hasSetDisabledBgColor)
mDisabledBgColor = color;
if (!hasSetPressedBgColor)
mPressedBgColor = color;
if (!hasSetSelectedBgColor)
mSelectedBgColor = color;
if (!hasSetFocusedBgColor)
mFocusedBgColor = color;
return this;
}
public ShapeSelector setDisabledBgColor(@ColorInt int color) {
mDisabledBgColor = color;
hasSetDisabledBgColor = true;
return this;
}
public ShapeSelector setPressedBgColor(@ColorInt int color) {
mPressedBgColor = color;
hasSetPressedBgColor = true;
return this;
}
public ShapeSelector setSelectedBgColor(@ColorInt int color) {
mSelectedBgColor = color;
hasSetSelectedBgColor = true;
return this;
}
public ShapeSelector setFocusedBgColor(@ColorInt int color) {
mFocusedBgColor = color;
hasSetPressedBgColor = true;
return this;
}
public ShapeSelector setCheckedBgColor(@ColorInt int color) {
mCheckedBgColor = color;
hasSetCheckedBgColor = true;
return this;
}
public ShapeSelector setStrokeWidth(@Dimension int width) {
mStrokeWidth = width;
return this;
}
public ShapeSelector setDefaultStrokeColor(@ColorInt int color) {
mDefaultStrokeColor = color;
if (!hasSetDisabledStrokeColor)
mDisabledStrokeColor = color;
if (!hasSetPressedStrokeColor)
mPressedStrokeColor = color;
if (!hasSetSelectedStrokeColor)
mSelectedStrokeColor = color;
if (!hasSetFocusedStrokeColor)
mFocusedStrokeColor = color;
return this;
}
public ShapeSelector setDisabledStrokeColor(@ColorInt int color) {
mDisabledStrokeColor = color;
hasSetDisabledStrokeColor = true;
return this;
}
public ShapeSelector setPressedStrokeColor(@ColorInt int color) {
mPressedStrokeColor = color;
hasSetPressedStrokeColor = true;
return this;
}
public ShapeSelector setSelectedStrokeColor(@ColorInt int color) {
mSelectedStrokeColor = color;
hasSetSelectedStrokeColor = true;
return this;
}
public ShapeSelector setCheckedStrokeColor(@ColorInt int color) {
mCheckedStrokeColor = color;
hasSetCheckedStrokeColor = true;
return this;
}
public ShapeSelector setFocusedStrokeColor(@ColorInt int color) {
mFocusedStrokeColor = color;
hasSetFocusedStrokeColor = true;
return this;
}
public ShapeSelector setCornerRadius(@Dimension int radius) {
mCornerRadius = radius;
return this;
}
public StateListDrawable create() {
StateListDrawable selector = new StateListDrawable();
//enabled = false
if (hasSetDisabledBgColor || hasSetDisabledStrokeColor) {
GradientDrawable disabledShape = getItemShape(mShape, mCornerRadius,
mDisabledBgColor, mStrokeWidth, mDisabledStrokeColor);
selector.addState(new int[]{-android.R.attr.state_enabled}, disabledShape);
}
//pressed = true
if (hasSetPressedBgColor || hasSetPressedStrokeColor) {
GradientDrawable pressedShape = getItemShape(mShape, mCornerRadius,
mPressedBgColor, mStrokeWidth, mPressedStrokeColor);
selector.addState(new int[]{android.R.attr.state_pressed}, pressedShape);
}
//selected = true
if (hasSetSelectedBgColor || hasSetSelectedStrokeColor) {
GradientDrawable selectedShape = getItemShape(mShape, mCornerRadius,
mSelectedBgColor, mStrokeWidth, mSelectedStrokeColor);
selector.addState(new int[]{android.R.attr.state_selected}, selectedShape);
}
//focused = true
if (hasSetFocusedBgColor || hasSetFocusedStrokeColor) {
GradientDrawable focusedShape = getItemShape(mShape, mCornerRadius,
mFocusedBgColor, mStrokeWidth, mFocusedStrokeColor);
selector.addState(new int[]{android.R.attr.state_focused}, focusedShape);
}
//checked = true
if (hasSetCheckedBgColor || hasSetCheckedStrokeColor) {
GradientDrawable checkedShape = getItemShape(mShape, mCornerRadius,
mCheckedBgColor, mStrokeWidth, mCheckedStrokeColor);
selector.addState(new int[]{android.R.attr.state_checked}, checkedShape);
}
//default
GradientDrawable defaultShape = getItemShape(mShape, mCornerRadius,
mDefaultBgColor, mStrokeWidth, mDefaultStrokeColor);
selector.addState(new int[]{}, defaultShape);
return selector;
}
private GradientDrawable getItemShape(int shape, int cornerRadius,
int solidColor, int strokeWidth, int strokeColor) {
GradientDrawable drawable = new GradientDrawable();
drawable.setShape(shape);
drawable.setStroke(strokeWidth, strokeColor);
drawable.setCornerRadius(cornerRadius);
drawable.setColor(solidColor);
return drawable;
}
}
/**
* 资源DrawableSelector
*
* @author hjy
* created at 2017/12/11 22:34
*/
public static final class DrawableSelector {
private Drawable mDefaultDrawable;
private Drawable mDisabledDrawable;
private Drawable mPressedDrawable;
private Drawable mSelectedDrawable;
private Drawable mFocusedDrawable;
private boolean hasSetDisabledDrawable = false;
private boolean hasSetPressedDrawable = false;
private boolean hasSetSelectedDrawable = false;
private boolean hasSetFocusedDrawable = false;
private DrawableSelector() {
mDefaultDrawable = new ColorDrawable(Color.TRANSPARENT);
}
public DrawableSelector setDefaultDrawable(Drawable drawable) {
mDefaultDrawable = drawable;
if (!hasSetDisabledDrawable)
mDisabledDrawable = drawable;
if (!hasSetPressedDrawable)
mPressedDrawable = drawable;
if (!hasSetSelectedDrawable)
mSelectedDrawable = drawable;
if (!hasSetFocusedDrawable)
mFocusedDrawable = drawable;
return this;
}
public DrawableSelector setDisabledDrawable(Drawable drawable) {
mDisabledDrawable = drawable;
hasSetDisabledDrawable = true;
return this;
}
public DrawableSelector setPressedDrawable(Drawable drawable) {
mPressedDrawable = drawable;
hasSetPressedDrawable = true;
return this;
}
public DrawableSelector setSelectedDrawable(Drawable drawable) {
mSelectedDrawable = drawable;
hasSetSelectedDrawable = true;
return this;
}
public DrawableSelector setFocusedDrawable(Drawable drawable) {
mFocusedDrawable = drawable;
hasSetFocusedDrawable = true;
return this;
}
public StateListDrawable create() {
StateListDrawable selector = new StateListDrawable();
if (hasSetDisabledDrawable)
selector.addState(new int[]{-android.R.attr.state_enabled}, mDisabledDrawable);
if (hasSetPressedDrawable)
selector.addState(new int[]{android.R.attr.state_pressed}, mPressedDrawable);
if (hasSetSelectedDrawable)
selector.addState(new int[]{android.R.attr.state_selected}, mSelectedDrawable);
if (hasSetFocusedDrawable)
selector.addState(new int[]{android.R.attr.state_focused}, mFocusedDrawable);
selector.addState(new int[]{}, mDefaultDrawable);
return selector;
}
public DrawableSelector setDefaultDrawable(Context context, @DrawableRes int drawableRes) {
return setDefaultDrawable(ContextCompat.getDrawable(context, drawableRes));
}
public DrawableSelector setDisabledDrawable(Context context, @DrawableRes int drawableRes) {
return setDisabledDrawable(ContextCompat.getDrawable(context, drawableRes));
}
public DrawableSelector setPressedDrawable(Context context, @DrawableRes int drawableRes) {
return setPressedDrawable(ContextCompat.getDrawable(context, drawableRes));
}
public DrawableSelector setSelectedDrawable(Context context, @DrawableRes int drawableRes) {
return setSelectedDrawable(ContextCompat.getDrawable(context, drawableRes));
}
public DrawableSelector setFocusedDrawable(Context context, @DrawableRes int drawableRes) {
return setFocusedDrawable(ContextCompat.getDrawable(context, drawableRes));
}
}
/**
* 颜色ColorSelector
*
* @author hjy
* created at 2017/12/11 22:26
*/
public static final class ColorSelector {
private int mDefaultColor;
private int mDisabledColor;
private int mPressedColor;
private int mSelectedColor;
private int mFocusedColor;
private int mCheckedColor;
private boolean hasSetDisabledColor = false;
private boolean hasSetPressedColor = false;
private boolean hasSetSelectedColor = false;
private boolean hasSetFocusedColor = false;
private boolean hasSetCheckedColor = false;
private ColorSelector() {
mDefaultColor = Color.BLACK;
mDisabledColor = Color.GRAY;
mPressedColor = Color.BLACK;
mSelectedColor = Color.BLACK;
mFocusedColor = Color.BLACK;
}
public ColorSelector setDefaultColor(@ColorInt int color) {
mDefaultColor = color;
if (!hasSetDisabledColor)
mDisabledColor = color;
if (!hasSetPressedColor)
mPressedColor = color;
if (!hasSetSelectedColor)
mSelectedColor = color;
if (!hasSetFocusedColor)
mFocusedColor = color;
return this;
}
public ColorSelector setDisabledColor(@ColorInt int color) {
mDisabledColor = color;
hasSetDisabledColor = true;
return this;
}
public ColorSelector setPressedColor(@ColorInt int color) {
mPressedColor = color;
hasSetPressedColor = true;
return this;
}
public ColorSelector setSelectedColor(@ColorInt int color) {
mSelectedColor = color;
hasSetSelectedColor = true;
return this;
}
public ColorSelector setFocusedColor(@ColorInt int color) {
mFocusedColor = color;
hasSetFocusedColor = true;
return this;
}
public ColorSelector setCheckedColor(@ColorInt int color) {
mCheckedColor = color;
hasSetCheckedColor = true;
return this;
}
public ColorStateList create() {
int[] colors = new int[]{
hasSetDisabledColor ? mDisabledColor : mDefaultColor,
hasSetPressedColor ? mPressedColor : mDefaultColor,
hasSetSelectedColor ? mSelectedColor : mDefaultColor,
hasSetFocusedColor ? mFocusedColor : mDefaultColor,
hasSetCheckedColor ? mCheckedColor : mDefaultColor,
mDefaultColor
};
int[][] states = new int[6][];
states[0] = new int[]{-android.R.attr.state_enabled};
states[1] = new int[]{android.R.attr.state_pressed};
states[2] = new int[]{android.R.attr.state_selected};
states[3] = new int[]{android.R.attr.state_focused};
states[4] = new int[]{android.R.attr.state_checked};
states[5] = new int[]{};
return new ColorStateList(states, colors);
}
}
}

@ -0,0 +1,443 @@
package io.legado.app.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
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
*/
class DrawableSelector constructor() {
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 constructor() {
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)
}
}
}

@ -1,318 +0,0 @@
package io.legado.app.lib.theme;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.SharedPreferences;
import android.graphics.Color;
import androidx.annotation.*;
import androidx.core.content.ContextCompat;
import io.legado.app.R;
/**
* @author Aidan Follestad (afollestad), Karim Abou Zeid (kabouzeid)
*/
public final class ThemeStore implements ThemeStorePrefKeys, ThemeStoreInterface {
private final Context mContext;
private final SharedPreferences.Editor mEditor;
public static ThemeStore editTheme(@NonNull Context context) {
return new ThemeStore(context);
}
@SuppressLint("CommitPrefEdits")
private ThemeStore(@NonNull Context context) {
mContext = context;
mEditor = prefs(context).edit();
}
@Override
public ThemeStore primaryColor(@ColorInt int color) {
mEditor.putInt(KEY_PRIMARY_COLOR, color);
if (autoGeneratePrimaryDark(mContext))
primaryColorDark(ColorUtil.darkenColor(color));
return this;
}
@Override
public ThemeStore primaryColorRes(@ColorRes int colorRes) {
return primaryColor(ContextCompat.getColor(mContext, colorRes));
}
@Override
public ThemeStore primaryColorAttr(@AttrRes int colorAttr) {
return primaryColor(ATHUtil.resolveColor(mContext, colorAttr));
}
@Override
public ThemeStore primaryColorDark(@ColorInt int color) {
mEditor.putInt(KEY_PRIMARY_COLOR_DARK, color);
return this;
}
@Override
public ThemeStore primaryColorDarkRes(@ColorRes int colorRes) {
return primaryColorDark(ContextCompat.getColor(mContext, colorRes));
}
@Override
public ThemeStore primaryColorDarkAttr(@AttrRes int colorAttr) {
return primaryColorDark(ATHUtil.resolveColor(mContext, colorAttr));
}
@Override
public ThemeStore accentColor(@ColorInt int color) {
mEditor.putInt(KEY_ACCENT_COLOR, color);
return this;
}
@Override
public ThemeStore accentColorRes(@ColorRes int colorRes) {
return accentColor(ContextCompat.getColor(mContext, colorRes));
}
@Override
public ThemeStore accentColorAttr(@AttrRes int colorAttr) {
return accentColor(ATHUtil.resolveColor(mContext, colorAttr));
}
@Override
public ThemeStore statusBarColor(@ColorInt int color) {
mEditor.putInt(KEY_STATUS_BAR_COLOR, color);
return this;
}
@Override
public ThemeStore statusBarColorRes(@ColorRes int colorRes) {
return statusBarColor(ContextCompat.getColor(mContext, colorRes));
}
@Override
public ThemeStore statusBarColorAttr(@AttrRes int colorAttr) {
return statusBarColor(ATHUtil.resolveColor(mContext, colorAttr));
}
@Override
public ThemeStore navigationBarColor(@ColorInt int color) {
mEditor.putInt(KEY_NAVIGATION_BAR_COLOR, color);
return this;
}
@Override
public ThemeStore navigationBarColorRes(@ColorRes int colorRes) {
return navigationBarColor(ContextCompat.getColor(mContext, colorRes));
}
@Override
public ThemeStore navigationBarColorAttr(@AttrRes int colorAttr) {
return navigationBarColor(ATHUtil.resolveColor(mContext, colorAttr));
}
@Override
public ThemeStore textColorPrimary(@ColorInt int color) {
mEditor.putInt(KEY_TEXT_COLOR_PRIMARY, color);
return this;
}
@Override
public ThemeStore textColorPrimaryRes(@ColorRes int colorRes) {
return textColorPrimary(ContextCompat.getColor(mContext, colorRes));
}
@Override
public ThemeStore textColorPrimaryAttr(@AttrRes int colorAttr) {
return textColorPrimary(ATHUtil.resolveColor(mContext, colorAttr));
}
@Override
public ThemeStore textColorPrimaryInverse(@ColorInt int color) {
mEditor.putInt(KEY_TEXT_COLOR_PRIMARY_INVERSE, color);
return this;
}
@Override
public ThemeStore textColorPrimaryInverseRes(@ColorRes int colorRes) {
return textColorPrimaryInverse(ContextCompat.getColor(mContext, colorRes));
}
@Override
public ThemeStore textColorPrimaryInverseAttr(@AttrRes int colorAttr) {
return textColorPrimaryInverse(ATHUtil.resolveColor(mContext, colorAttr));
}
@Override
public ThemeStore textColorSecondary(@ColorInt int color) {
mEditor.putInt(KEY_TEXT_COLOR_SECONDARY, color);
return this;
}
@Override
public ThemeStore textColorSecondaryRes(@ColorRes int colorRes) {
return textColorSecondary(ContextCompat.getColor(mContext, colorRes));
}
@Override
public ThemeStore textColorSecondaryAttr(@AttrRes int colorAttr) {
return textColorSecondary(ATHUtil.resolveColor(mContext, colorAttr));
}
@Override
public ThemeStore textColorSecondaryInverse(@ColorInt int color) {
mEditor.putInt(KEY_TEXT_COLOR_SECONDARY_INVERSE, color);
return this;
}
@Override
public ThemeStore textColorSecondaryInverseRes(@ColorRes int colorRes) {
return textColorSecondaryInverse(ContextCompat.getColor(mContext, colorRes));
}
@Override
public ThemeStore textColorSecondaryInverseAttr(@AttrRes int colorAttr) {
return textColorSecondaryInverse(ATHUtil.resolveColor(mContext, colorAttr));
}
@Override
public ThemeStore backgroundColor(int color) {
mEditor.putInt(KEY_BACKGROUND_COLOR, color);
return this;
}
@Override
public ThemeStore coloredStatusBar(boolean colored) {
mEditor.putBoolean(KEY_APPLY_PRIMARYDARK_STATUSBAR, colored);
return this;
}
@Override
public ThemeStore coloredNavigationBar(boolean applyToNavBar) {
mEditor.putBoolean(KEY_APPLY_PRIMARY_NAVBAR, applyToNavBar);
return this;
}
@Override
public ThemeStore autoGeneratePrimaryDark(boolean autoGenerate) {
mEditor.putBoolean(KEY_AUTO_GENERATE_PRIMARYDARK, autoGenerate);
return this;
}
// Commit method
@SuppressWarnings("unchecked")
@Override
public void apply() {
mEditor.putLong(VALUES_CHANGED, System.currentTimeMillis())
.putBoolean(IS_CONFIGURED_KEY, true)
.apply();
}
// Static getters
@CheckResult
@NonNull
protected static SharedPreferences prefs(@NonNull Context context) {
return context.getSharedPreferences(CONFIG_PREFS_KEY_DEFAULT, Context.MODE_PRIVATE);
}
public static void markChanged(@NonNull Context context) {
new ThemeStore(context).apply();
}
@CheckResult
@ColorInt
public static int primaryColor(@NonNull Context context) {
return prefs(context).getInt(KEY_PRIMARY_COLOR, ATHUtil.resolveColor(context, R.attr.colorPrimary, Color.parseColor("#455A64")));
}
@CheckResult
@ColorInt
public static int primaryColorDark(@NonNull Context context) {
return prefs(context).getInt(KEY_PRIMARY_COLOR_DARK, ATHUtil.resolveColor(context, R.attr.colorPrimaryDark, Color.parseColor("#37474F")));
}
@CheckResult
@ColorInt
public static int accentColor(@NonNull Context context) {
return prefs(context).getInt(KEY_ACCENT_COLOR, ATHUtil.resolveColor(context, R.attr.colorAccent, Color.parseColor("#263238")));
}
@CheckResult
@ColorInt
public static int statusBarColor(@NonNull Context context) {
if (!coloredStatusBar(context)) {
return Color.BLACK;
}
return prefs(context).getInt(KEY_STATUS_BAR_COLOR, primaryColorDark(context));
}
@CheckResult
@ColorInt
public static int navigationBarColor(@NonNull Context context) {
if (!coloredNavigationBar(context)) {
return Color.BLACK;
}
return prefs(context).getInt(KEY_NAVIGATION_BAR_COLOR, primaryColor(context));
}
@CheckResult
@ColorInt
public static int textColorPrimary(@NonNull Context context) {
return prefs(context).getInt(KEY_TEXT_COLOR_PRIMARY, ATHUtil.resolveColor(context, android.R.attr.textColorPrimary));
}
@CheckResult
@ColorInt
public static int textColorPrimaryInverse(@NonNull Context context) {
return prefs(context).getInt(KEY_TEXT_COLOR_PRIMARY_INVERSE, ATHUtil.resolveColor(context, android.R.attr.textColorPrimaryInverse));
}
@CheckResult
@ColorInt
public static int textColorSecondary(@NonNull Context context) {
return prefs(context).getInt(KEY_TEXT_COLOR_SECONDARY, ATHUtil.resolveColor(context, android.R.attr.textColorSecondary));
}
@CheckResult
@ColorInt
public static int textColorSecondaryInverse(@NonNull Context context) {
return prefs(context).getInt(KEY_TEXT_COLOR_SECONDARY_INVERSE, ATHUtil.resolveColor(context, android.R.attr.textColorSecondaryInverse));
}
@CheckResult
@ColorInt
public static int backgroundColor(@NonNull Context context) {
return prefs(context).getInt(KEY_BACKGROUND_COLOR, ATHUtil.resolveColor(context, android.R.attr.colorBackground));
}
@CheckResult
public static boolean coloredStatusBar(@NonNull Context context) {
return prefs(context).getBoolean(KEY_APPLY_PRIMARYDARK_STATUSBAR, true);
}
@CheckResult
public static boolean coloredNavigationBar(@NonNull Context context) {
return prefs(context).getBoolean(KEY_APPLY_PRIMARY_NAVBAR, false);
}
@CheckResult
public static boolean autoGeneratePrimaryDark(@NonNull Context context) {
return prefs(context).getBoolean(KEY_AUTO_GENERATE_PRIMARYDARK, true);
}
@CheckResult
public static boolean isConfigured(Context context) {
return prefs(context).getBoolean(IS_CONFIGURED_KEY, false);
}
@SuppressLint("CommitPrefEdits")
public static boolean isConfigured(Context context, @IntRange(from = 0, to = Integer.MAX_VALUE) int version) {
final SharedPreferences prefs = prefs(context);
final int lastVersion = prefs.getInt(IS_CONFIGURED_VERSION_KEY, -1);
if (version > lastVersion) {
prefs.edit().putInt(IS_CONFIGURED_VERSION_KEY, version).apply();
return false;
}
return true;
}
}

@ -0,0 +1,309 @@
package io.legado.app.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.legado.app.R
/**
* @author Aidan Follestad (afollestad), Karim Abou Zeid (kabouzeid)
*/
class ThemeStore @SuppressLint("CommitPrefEdits")
private constructor(private val mContext: Context) : ThemeStorePrefKeys, ThemeStoreInterface {
private val mEditor: SharedPreferences.Editor
init {
mEditor = prefs(mContext).edit()
}
override fun primaryColor(@ColorInt color: Int): ThemeStore {
mEditor.putInt(ThemeStorePrefKeys.KEY_PRIMARY_COLOR, color)
if (autoGeneratePrimaryDark(mContext))
primaryColorDark(ColorUtil.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(ATHUtil.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(ATHUtil.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(ATHUtil.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(ATHUtil.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(ATHUtil.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(ATHUtil.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(ATHUtil.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(ATHUtil.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(ATHUtil.resolveColor(mContext, colorAttr))
}
override fun backgroundColor(color: Int): ThemeStore {
mEditor.putInt(ThemeStorePrefKeys.KEY_BACKGROUND_COLOR, color)
return this
}
override fun coloredStatusBar(colored: Boolean): ThemeStore {
mEditor.putBoolean(ThemeStorePrefKeys.KEY_APPLY_PRIMARYDARK_STATUSBAR, colored)
return this
}
override fun coloredNavigationBar(applyToNavBar: Boolean): ThemeStore {
mEditor.putBoolean(ThemeStorePrefKeys.KEY_APPLY_PRIMARY_NAVBAR, applyToNavBar)
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()
}
companion object {
fun editTheme(context: Context): ThemeStore {
return ThemeStore(context)
}
// Static getters
@CheckResult
protected 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): Int {
return prefs(context).getInt(
ThemeStorePrefKeys.KEY_PRIMARY_COLOR,
ATHUtil.resolveColor(context, R.attr.colorPrimary, Color.parseColor("#455A64"))
)
}
@CheckResult
@ColorInt
fun primaryColorDark(context: Context): Int {
return prefs(context).getInt(
ThemeStorePrefKeys.KEY_PRIMARY_COLOR_DARK,
ATHUtil.resolveColor(context, R.attr.colorPrimaryDark, Color.parseColor("#37474F"))
)
}
@CheckResult
@ColorInt
fun accentColor(context: Context): Int {
return prefs(context).getInt(
ThemeStorePrefKeys.KEY_ACCENT_COLOR,
ATHUtil.resolveColor(context, R.attr.colorAccent, Color.parseColor("#263238"))
)
}
@CheckResult
@ColorInt
fun statusBarColor(context: Context): Int {
return if (!coloredStatusBar(context)) {
Color.BLACK
} else prefs(context).getInt(ThemeStorePrefKeys.KEY_STATUS_BAR_COLOR, primaryColorDark(context))
}
@CheckResult
@ColorInt
fun navigationBarColor(context: Context): Int {
return if (!coloredNavigationBar(context)) {
Color.BLACK
} else prefs(context).getInt(ThemeStorePrefKeys.KEY_NAVIGATION_BAR_COLOR, primaryColor(context))
}
@CheckResult
@ColorInt
fun textColorPrimary(context: Context): Int {
return prefs(context).getInt(
ThemeStorePrefKeys.KEY_TEXT_COLOR_PRIMARY,
ATHUtil.resolveColor(context, android.R.attr.textColorPrimary)
)
}
@CheckResult
@ColorInt
fun textColorPrimaryInverse(context: Context): Int {
return prefs(context).getInt(
ThemeStorePrefKeys.KEY_TEXT_COLOR_PRIMARY_INVERSE,
ATHUtil.resolveColor(context, android.R.attr.textColorPrimaryInverse)
)
}
@CheckResult
@ColorInt
fun textColorSecondary(context: Context): Int {
return prefs(context).getInt(
ThemeStorePrefKeys.KEY_TEXT_COLOR_SECONDARY,
ATHUtil.resolveColor(context, android.R.attr.textColorSecondary)
)
}
@CheckResult
@ColorInt
fun textColorSecondaryInverse(context: Context): Int {
return prefs(context).getInt(
ThemeStorePrefKeys.KEY_TEXT_COLOR_SECONDARY_INVERSE,
ATHUtil.resolveColor(context, android.R.attr.textColorSecondaryInverse)
)
}
@CheckResult
@ColorInt
fun backgroundColor(context: Context): Int {
return prefs(context).getInt(
ThemeStorePrefKeys.KEY_BACKGROUND_COLOR,
ATHUtil.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
}
}
}

@ -1,92 +0,0 @@
package io.legado.app.lib.theme;
import androidx.annotation.AttrRes;
import androidx.annotation.ColorInt;
import androidx.annotation.ColorRes;
/**
* @author Aidan Follestad (afollestad), Karim Abou Zeid (kabouzeid)
*/
interface ThemeStoreInterface {
// Primary colors
ThemeStore primaryColor(@ColorInt int color);
ThemeStore primaryColorRes(@ColorRes int colorRes);
ThemeStore primaryColorAttr(@AttrRes int colorAttr);
ThemeStore autoGeneratePrimaryDark(boolean autoGenerate);
ThemeStore primaryColorDark(@ColorInt int color);
ThemeStore primaryColorDarkRes(@ColorRes int colorRes);
ThemeStore primaryColorDarkAttr(@AttrRes int colorAttr);
// Accent colors
ThemeStore accentColor(@ColorInt int color);
ThemeStore accentColorRes(@ColorRes int colorRes);
ThemeStore accentColorAttr(@AttrRes int colorAttr);
// Status bar color
ThemeStore statusBarColor(@ColorInt int color);
ThemeStore statusBarColorRes(@ColorRes int colorRes);
ThemeStore statusBarColorAttr(@AttrRes int colorAttr);
// Navigation bar color
ThemeStore navigationBarColor(@ColorInt int color);
ThemeStore navigationBarColorRes(@ColorRes int colorRes);
ThemeStore navigationBarColorAttr(@AttrRes int colorAttr);
// Primary text color
ThemeStore textColorPrimary(@ColorInt int color);
ThemeStore textColorPrimaryRes(@ColorRes int colorRes);
ThemeStore textColorPrimaryAttr(@AttrRes int colorAttr);
ThemeStore textColorPrimaryInverse(@ColorInt int color);
ThemeStore textColorPrimaryInverseRes(@ColorRes int colorRes);
ThemeStore textColorPrimaryInverseAttr(@AttrRes int colorAttr);
// Secondary text color
ThemeStore textColorSecondary(@ColorInt int color);
ThemeStore textColorSecondaryRes(@ColorRes int colorRes);
ThemeStore textColorSecondaryAttr(@AttrRes int colorAttr);
ThemeStore textColorSecondaryInverse(@ColorInt int color);
ThemeStore textColorSecondaryInverseRes(@ColorRes int colorRes);
ThemeStore textColorSecondaryInverseAttr(@AttrRes int colorAttr);
ThemeStore backgroundColor(@ColorInt int color);
// Toggle configurations
ThemeStore coloredStatusBar(boolean colored);
ThemeStore coloredNavigationBar(boolean applyToNavBar);
// Commit/apply
void apply();
}

@ -0,0 +1,92 @@
package io.legado.app.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
fun backgroundColor(@ColorInt color: Int): ThemeStore
// Toggle configurations
fun coloredStatusBar(colored: Boolean): ThemeStore
fun coloredNavigationBar(applyToNavBar: Boolean): ThemeStore
// Commit/apply
fun apply()
}

@ -1,29 +0,0 @@
package io.legado.app.lib.theme;
/**
* @author Aidan Follestad (afollestad), Karim Abou Zeid (kabouzeid)
*/
interface ThemeStorePrefKeys {
String CONFIG_PREFS_KEY_DEFAULT = "app_themes";
String IS_CONFIGURED_KEY = "is_configured";
String IS_CONFIGURED_VERSION_KEY = "is_configured_version";
String VALUES_CHANGED = "values_changed";
String KEY_PRIMARY_COLOR = "primary_color";
String KEY_PRIMARY_COLOR_DARK = "primary_color_dark";
String KEY_ACCENT_COLOR = "accent_color";
String KEY_STATUS_BAR_COLOR = "status_bar_color";
String KEY_NAVIGATION_BAR_COLOR = "navigation_bar_color";
String KEY_TEXT_COLOR_PRIMARY = "text_color_primary";
String KEY_TEXT_COLOR_PRIMARY_INVERSE = "text_color_primary_inverse";
String KEY_TEXT_COLOR_SECONDARY = "text_color_secondary";
String KEY_TEXT_COLOR_SECONDARY_INVERSE = "text_color_secondary_inverse";
String KEY_BACKGROUND_COLOR = "backgroundColor";
String KEY_APPLY_PRIMARYDARK_STATUSBAR = "apply_primarydark_statusbar";
String KEY_APPLY_PRIMARY_NAVBAR = "apply_primary_navbar";
String KEY_AUTO_GENERATE_PRIMARYDARK = "auto_generate_primarydark";
}

@ -0,0 +1,31 @@
package io.legado.app.lib.theme
/**
* @author Aidan Follestad (afollestad), Karim Abou Zeid (kabouzeid)
*/
internal interface ThemeStorePrefKeys {
companion object {
val CONFIG_PREFS_KEY_DEFAULT = "app_themes"
val IS_CONFIGURED_KEY = "is_configured"
val IS_CONFIGURED_VERSION_KEY = "is_configured_version"
val VALUES_CHANGED = "values_changed"
val KEY_PRIMARY_COLOR = "primary_color"
val KEY_PRIMARY_COLOR_DARK = "primary_color_dark"
val KEY_ACCENT_COLOR = "accent_color"
val KEY_STATUS_BAR_COLOR = "status_bar_color"
val KEY_NAVIGATION_BAR_COLOR = "navigation_bar_color"
val KEY_TEXT_COLOR_PRIMARY = "text_color_primary"
val KEY_TEXT_COLOR_PRIMARY_INVERSE = "text_color_primary_inverse"
val KEY_TEXT_COLOR_SECONDARY = "text_color_secondary"
val KEY_TEXT_COLOR_SECONDARY_INVERSE = "text_color_secondary_inverse"
val KEY_BACKGROUND_COLOR = "backgroundColor"
val KEY_APPLY_PRIMARYDARK_STATUSBAR = "apply_primarydark_statusbar"
val KEY_APPLY_PRIMARY_NAVBAR = "apply_primary_navbar"
val KEY_AUTO_GENERATE_PRIMARYDARK = "auto_generate_primarydark"
}
}

@ -1,384 +0,0 @@
package io.legado.app.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.os.Build;
import android.view.View;
import android.widget.*;
import androidx.annotation.CheckResult;
import androidx.annotation.ColorInt;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
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 com.google.android.material.floatingactionbutton.FloatingActionButton;
import io.legado.app.R;
import java.lang.reflect.Field;
/**
* @author afollestad, plusCubed
*/
public final class TintHelper {
@SuppressLint("PrivateResource")
@ColorInt
private static int getDefaultRippleColor(@NonNull Context context, boolean useDarkRipple) {
// Light ripple is actually translucent black, and vice versa
return ContextCompat.getColor(context, useDarkRipple ?
R.color.ripple_material_light : R.color.ripple_material_dark);
}
@NonNull
private static ColorStateList getDisabledColorStateList(@ColorInt int normal, @ColorInt int disabled) {
return new ColorStateList(new int[][]{
new int[]{-android.R.attr.state_enabled},
new int[]{android.R.attr.state_enabled}
}, new int[]{
disabled,
normal
});
}
@SuppressWarnings("deprecation")
public static void setTintSelector(@NonNull View view, @ColorInt final int color, final boolean darker, final boolean useDarkTheme) {
final boolean isColorLight = ColorUtil.isColorLight(color);
final int disabled = ContextCompat.getColor(view.getContext(), useDarkTheme ? R.color.ate_button_disabled_dark : R.color.ate_button_disabled_light);
final int pressed = ColorUtil.shiftColor(color, darker ? 0.9f : 1.1f);
final int activated = ColorUtil.shiftColor(color, darker ? 1.1f : 0.9f);
final int rippleColor = getDefaultRippleColor(view.getContext(), isColorLight);
final int textColor = ContextCompat.getColor(view.getContext(), isColorLight ? R.color.ate_primary_text_light : R.color.ate_primary_text_dark);
final ColorStateList sl;
if (view instanceof Button) {
sl = getDisabledColorStateList(color, disabled);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP &&
view.getBackground() instanceof RippleDrawable) {
RippleDrawable rd = (RippleDrawable) view.getBackground();
rd.setColor(ColorStateList.valueOf(rippleColor));
}
// Disabled text color state for buttons, may get overridden later by ATE tags
final Button button = (Button) view;
button.setTextColor(getDisabledColorStateList(textColor, ContextCompat.getColor(view.getContext(), useDarkTheme ? R.color.ate_button_text_disabled_dark : R.color.ate_button_text_disabled_light)));
} else if (view instanceof FloatingActionButton) {
// FloatingActionButton doesn't support disabled state?
sl = new ColorStateList(new int[][]{
new int[]{-android.R.attr.state_pressed},
new int[]{android.R.attr.state_pressed}
}, new int[]{
color,
pressed
});
final FloatingActionButton fab = (FloatingActionButton) view;
fab.setRippleColor(rippleColor);
fab.setBackgroundTintList(sl);
if (fab.getDrawable() != null)
fab.setImageDrawable(createTintedDrawable(fab.getDrawable(), textColor));
return;
} else {
sl = new ColorStateList(
new int[][]{
new int[]{-android.R.attr.state_enabled},
new int[]{android.R.attr.state_enabled},
new int[]{android.R.attr.state_enabled, android.R.attr.state_pressed},
new int[]{android.R.attr.state_enabled, android.R.attr.state_activated},
new int[]{android.R.attr.state_enabled, android.R.attr.state_checked}
},
new int[]{
disabled,
color,
pressed,
activated,
activated
}
);
}
Drawable drawable = view.getBackground();
if (drawable != null) {
drawable = createTintedDrawable(drawable, sl);
ViewUtil.setBackgroundCompat(view, drawable);
}
if (view instanceof TextView && !(view instanceof Button)) {
final TextView tv = (TextView) view;
tv.setTextColor(getDisabledColorStateList(textColor, ContextCompat.getColor(view.getContext(), isColorLight ? R.color.ate_text_disabled_light : R.color.ate_text_disabled_dark)));
}
}
public static void setTintAuto(final @NonNull View view, final @ColorInt int color,
boolean background) {
setTintAuto(view, color, background, ATHUtil.isWindowBackgroundDark(view.getContext()));
}
@SuppressWarnings("deprecation")
public static void setTintAuto(final @NonNull View view, final @ColorInt int color,
boolean background, final boolean isDark) {
if (!background) {
if (view instanceof RadioButton)
setTint((RadioButton) view, color, isDark);
else if (view instanceof SeekBar)
setTint((SeekBar) view, color, isDark);
else if (view instanceof ProgressBar)
setTint((ProgressBar) view, color);
else if (view instanceof AppCompatEditText)
setTint((AppCompatEditText) view, color, isDark);
else if (view instanceof CheckBox)
setTint((CheckBox) view, color, isDark);
else if (view instanceof ImageView)
setTint((ImageView) view, color);
else if (view instanceof Switch)
setTint((Switch) view, color, isDark);
else if (view instanceof SwitchCompat)
setTint((SwitchCompat) view, color, isDark);
else if (view instanceof SearchView) {
int iconIdS[] = new int[]{androidx.appcompat.R.id.search_button, androidx.appcompat.R.id.search_close_btn,};
for (int iconId : iconIdS) {
ImageView icon = view.findViewById(iconId);
if (icon != null) {
setTint(icon, color);
}
}
} else {
background = true;
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP &&
!background && view.getBackground() instanceof RippleDrawable) {
// Ripples for the above views (e.g. when you tap and hold a switch or checkbox)
RippleDrawable rd = (RippleDrawable) view.getBackground();
@SuppressLint("PrivateResource") final int unchecked = ContextCompat.getColor(view.getContext(),
isDark ? R.color.ripple_material_dark : R.color.ripple_material_light);
final int checked = ColorUtil.adjustAlpha(color, 0.4f);
final ColorStateList sl = new ColorStateList(
new int[][]{
new int[]{-android.R.attr.state_activated, -android.R.attr.state_checked},
new int[]{android.R.attr.state_activated},
new int[]{android.R.attr.state_checked}
},
new int[]{
unchecked,
checked,
checked
}
);
rd.setColor(sl);
}
}
if (background) {
// Need to tint the background of a view
if (view instanceof FloatingActionButton || view instanceof Button) {
setTintSelector(view, color, false, isDark);
} else if (view.getBackground() != null) {
Drawable drawable = view.getBackground();
if (drawable != null) {
drawable = createTintedDrawable(drawable, color);
ViewUtil.setBackgroundCompat(view, drawable);
}
}
}
}
public static void setTint(@NonNull RadioButton radioButton, @ColorInt int color, boolean useDarker) {
ColorStateList sl = new ColorStateList(new int[][]{
new int[]{-android.R.attr.state_enabled},
new int[]{android.R.attr.state_enabled, -android.R.attr.state_checked},
new int[]{android.R.attr.state_enabled, android.R.attr.state_checked}
}, new int[]{
// Rdio button includes own alpha for disabled state
ColorUtil.stripAlpha(ContextCompat.getColor(radioButton.getContext(), useDarker ? R.color.ate_control_disabled_dark : R.color.ate_control_disabled_light)),
ContextCompat.getColor(radioButton.getContext(), useDarker ? R.color.ate_control_normal_dark : R.color.ate_control_normal_light),
color
});
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
radioButton.setButtonTintList(sl);
} else {
Drawable d = createTintedDrawable(ContextCompat.getDrawable(radioButton.getContext(), R.drawable.abc_btn_radio_material), sl);
radioButton.setButtonDrawable(d);
}
}
public static void setTint(@NonNull SeekBar seekBar, @ColorInt int color, boolean useDarker) {
final ColorStateList s1 = getDisabledColorStateList(color,
ContextCompat.getColor(seekBar.getContext(), useDarker ? R.color.ate_control_disabled_dark : R.color.ate_control_disabled_light));
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
seekBar.setThumbTintList(s1);
seekBar.setProgressTintList(s1);
} else {
Drawable progressDrawable = createTintedDrawable(seekBar.getProgressDrawable(), s1);
seekBar.setProgressDrawable(progressDrawable);
Drawable thumbDrawable = createTintedDrawable(seekBar.getThumb(), s1);
seekBar.setThumb(thumbDrawable);
}
}
public static void setTint(@NonNull ProgressBar progressBar, @ColorInt int color) {
setTint(progressBar, color, false);
}
public static void setTint(@NonNull ProgressBar progressBar, @ColorInt int color, boolean skipIndeterminate) {
ColorStateList sl = ColorStateList.valueOf(color);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
progressBar.setProgressTintList(sl);
progressBar.setSecondaryProgressTintList(sl);
if (!skipIndeterminate)
progressBar.setIndeterminateTintList(sl);
} else {
PorterDuff.Mode mode = PorterDuff.Mode.SRC_IN;
if (!skipIndeterminate && progressBar.getIndeterminateDrawable() != null)
progressBar.getIndeterminateDrawable().setColorFilter(color, mode);
if (progressBar.getProgressDrawable() != null)
progressBar.getProgressDrawable().setColorFilter(color, mode);
}
}
@SuppressLint("RestrictedApi")
public static void setTint(@NonNull AppCompatEditText editText, @ColorInt int color, boolean useDarker) {
final ColorStateList editTextColorStateList = new ColorStateList(new int[][]{
new int[]{-android.R.attr.state_enabled},
new int[]{android.R.attr.state_enabled, -android.R.attr.state_pressed, -android.R.attr.state_focused},
new int[]{}
}, new int[]{
ContextCompat.getColor(editText.getContext(), useDarker ? R.color.ate_text_disabled_dark : R.color.ate_text_disabled_light),
ContextCompat.getColor(editText.getContext(), useDarker ? R.color.ate_control_normal_dark : R.color.ate_control_normal_light),
color
});
editText.setSupportBackgroundTintList(editTextColorStateList);
setCursorTint(editText, color);
}
public static void setTint(@NonNull CheckBox box, @ColorInt int color, boolean useDarker) {
ColorStateList sl = new ColorStateList(new int[][]{
new int[]{-android.R.attr.state_enabled},
new int[]{android.R.attr.state_enabled, -android.R.attr.state_checked},
new int[]{android.R.attr.state_enabled, android.R.attr.state_checked}
}, new int[]{
ContextCompat.getColor(box.getContext(), useDarker ? R.color.ate_control_disabled_dark : R.color.ate_control_disabled_light),
ContextCompat.getColor(box.getContext(), useDarker ? R.color.ate_control_normal_dark : R.color.ate_control_normal_light),
color
});
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
box.setButtonTintList(sl);
} else {
Drawable drawable = createTintedDrawable(ContextCompat.getDrawable(box.getContext(), R.drawable.abc_btn_check_material), sl);
box.setButtonDrawable(drawable);
}
}
public static void setTint(@NonNull ImageView image, @ColorInt int color) {
image.setColorFilter(color, PorterDuff.Mode.SRC_ATOP);
}
private static Drawable modifySwitchDrawable(@NonNull Context context, @NonNull Drawable from, @ColorInt int tint, boolean thumb, boolean compatSwitch, boolean useDarker) {
if (useDarker) {
tint = ColorUtil.shiftColor(tint, 1.1f);
}
tint = ColorUtil.adjustAlpha(tint, (compatSwitch && !thumb) ? 0.5f : 1.0f);
int disabled;
int normal;
if (thumb) {
disabled = ContextCompat.getColor(context, useDarker ? R.color.ate_switch_thumb_disabled_dark : R.color.ate_switch_thumb_disabled_light);
normal = ContextCompat.getColor(context, useDarker ? R.color.ate_switch_thumb_normal_dark : R.color.ate_switch_thumb_normal_light);
} else {
disabled = ContextCompat.getColor(context, useDarker ? R.color.ate_switch_track_disabled_dark : R.color.ate_switch_track_disabled_light);
normal = ContextCompat.getColor(context, useDarker ? R.color.ate_switch_track_normal_dark : R.color.ate_switch_track_normal_light);
}
// Stock switch includes its own alpha
if (!compatSwitch) {
normal = ColorUtil.stripAlpha(normal);
}
final ColorStateList sl = new ColorStateList(
new int[][]{
new int[]{-android.R.attr.state_enabled},
new int[]{android.R.attr.state_enabled, -android.R.attr.state_activated, -android.R.attr.state_checked},
new int[]{android.R.attr.state_enabled, android.R.attr.state_activated},
new int[]{android.R.attr.state_enabled, android.R.attr.state_checked}
},
new int[]{
disabled,
normal,
tint,
tint
}
);
return createTintedDrawable(from, sl);
}
public static void setTint(@NonNull Switch switchView, @ColorInt int color, boolean useDarker) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) return;
if (switchView.getTrackDrawable() != null) {
switchView.setTrackDrawable(modifySwitchDrawable(switchView.getContext(),
switchView.getTrackDrawable(), color, false, false, useDarker));
}
if (switchView.getThumbDrawable() != null) {
switchView.setThumbDrawable(modifySwitchDrawable(switchView.getContext(),
switchView.getThumbDrawable(), color, true, false, useDarker));
}
}
public static void setTint(@NonNull SwitchCompat switchView, @ColorInt int color, boolean useDarker) {
if (switchView.getTrackDrawable() != null) {
switchView.setTrackDrawable(modifySwitchDrawable(switchView.getContext(),
switchView.getTrackDrawable(), color, false, true, useDarker));
}
if (switchView.getThumbDrawable() != null) {
switchView.setThumbDrawable(modifySwitchDrawable(switchView.getContext(),
switchView.getThumbDrawable(), color, true, true, 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
@Nullable
public static Drawable createTintedDrawable(@Nullable Drawable drawable, @ColorInt int color) {
if (drawable == null) return null;
drawable = DrawableCompat.wrap(drawable.mutate());
DrawableCompat.setTintMode(drawable, PorterDuff.Mode.SRC_IN);
DrawableCompat.setTint(drawable, color);
return drawable;
}
// 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
@Nullable
public static Drawable createTintedDrawable(@Nullable Drawable drawable, @NonNull ColorStateList sl) {
if (drawable == null) return null;
drawable = DrawableCompat.wrap(drawable.mutate());
DrawableCompat.setTintList(drawable, sl);
return drawable;
}
public static void setCursorTint(@NonNull EditText editText, @ColorInt int color) {
try {
Field fCursorDrawableRes = TextView.class.getDeclaredField("mCursorDrawableRes");
fCursorDrawableRes.setAccessible(true);
int mCursorDrawableRes = fCursorDrawableRes.getInt(editText);
Field fEditor = TextView.class.getDeclaredField("mEditor");
fEditor.setAccessible(true);
Object editor = fEditor.get(editText);
Class<?> clazz = editor.getClass();
Field fCursorDrawable = clazz.getDeclaredField("mCursorDrawable");
fCursorDrawable.setAccessible(true);
Drawable[] drawables = new Drawable[2];
drawables[0] = ContextCompat.getDrawable(editText.getContext(), mCursorDrawableRes);
drawables[0] = createTintedDrawable(drawables[0], color);
drawables[1] = ContextCompat.getDrawable(editText.getContext(), mCursorDrawableRes);
drawables[1] = createTintedDrawable(drawables[1], color);
fCursorDrawable.set(editor, drawables);
} catch (Exception ignored) {
}
}
}

@ -0,0 +1,455 @@
package io.legado.app.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.os.Build
import android.view.View
import android.widget.*
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 com.google.android.material.floatingactionbutton.FloatingActionButton
import io.legado.app.R
/**
* @author afollestad, plusCubed
*/
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)
R.color.ripple_material_light
else
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 = ColorUtil.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 = ColorUtil.shiftColor(color, if (darker) 0.9f else 1.1f)
val activated = ColorUtil.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
if (view is Button) {
sl = getDisabledColorStateList(color, disabled)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && 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
)
)
)
} else if (view 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)
ViewUtil.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,
background: Boolean
) {
setTintAuto(view, color, background, ATHUtil.isWindowBackgroundDark(view.context))
}
fun setTintAuto(
view: View, @ColorInt color: Int,
background: Boolean, isDark: Boolean
) {
var background = background
if (!background) {
if (view is RadioButton)
setTint(view, color, isDark)
else if (view is SeekBar)
setTint(view, color, isDark)
else if (view is ProgressBar)
setTint(view, color)
else if (view is AppCompatEditText)
setTint(view, color, isDark)
else if (view is CheckBox)
setTint(view, color, isDark)
else if (view is ImageView)
setTint(view, color)
else if (view is Switch)
setTint(view, color, isDark)
else if (view is SwitchCompat)
setTint(view, color, isDark)
else if (view is SearchView) {
val iconIdS =
intArrayOf(androidx.appcompat.R.id.search_button, androidx.appcompat.R.id.search_close_btn)
for (iconId in iconIdS) {
val icon = view.findViewById<ImageView>(iconId)
if (icon != null) {
setTint(icon, color)
}
}
} else {
background = true
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP &&
!background && 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) R.color.ripple_material_dark else R.color.ripple_material_light
)
val checked = ColorUtil.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 (background) {
// Need to tint the background 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)
ViewUtil.setBackgroundCompat(view, drawable)
}
}
}
}
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(
// Rdio button includes own alpha for disabled state
ColorUtil.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
)
)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
radioButton.buttonTintList = sl
} else {
val d = createTintedDrawable(
ContextCompat.getDrawable(radioButton.context, R.drawable.abc_btn_radio_material),
sl
)
radioButton.buttonDrawable = d
}
}
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
)
)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
seekBar.thumbTintList = s1
seekBar.progressTintList = s1
} else {
val progressDrawable = createTintedDrawable(seekBar.progressDrawable, s1)
seekBar.progressDrawable = progressDrawable
val thumbDrawable = createTintedDrawable(seekBar.thumb, s1)
seekBar.thumb = thumbDrawable
}
}
@JvmOverloads
fun setTint(progressBar: ProgressBar, @ColorInt color: Int, skipIndeterminate: Boolean = false) {
val sl = ColorStateList.valueOf(color)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
progressBar.progressTintList = sl
progressBar.secondaryProgressTintList = sl
if (!skipIndeterminate)
progressBar.indeterminateTintList = sl
} else {
val mode = PorterDuff.Mode.SRC_IN
if (!skipIndeterminate && progressBar.indeterminateDrawable != null)
progressBar.indeterminateDrawable.setColorFilter(color, mode)
if (progressBar.progressDrawable != null)
progressBar.progressDrawable.setColorFilter(color, mode)
}
}
@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)
}
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
)
)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
box.buttonTintList = sl
} else {
val drawable =
createTintedDrawable(ContextCompat.getDrawable(box.context, R.drawable.abc_btn_check_material), sl)
box.buttonDrawable = drawable
}
}
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 tint = tint
if (useDarker) {
tint = ColorUtil.shiftColor(tint, 1.1f)
}
tint = ColorUtil.adjustAlpha(tint, 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 = ColorUtil.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, tint, tint)
)
return createTintedDrawable(from, sl)
}
fun setTint(switchView: Switch, @ColorInt color: Int, useDarker: Boolean) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) return
if (switchView.trackDrawable != null) {
switchView.trackDrawable = modifySwitchDrawable(
switchView.context,
switchView.trackDrawable, color, false, false, useDarker
)
}
if (switchView.thumbDrawable != null) {
switchView.thumbDrawable = modifySwitchDrawable(
switchView.context,
switchView.thumbDrawable, color, true, false, useDarker
)
}
}
fun setTint(switchView: SwitchCompat, @ColorInt color: Int, useDarker: Boolean) {
if (switchView.trackDrawable != null) {
switchView.trackDrawable = modifySwitchDrawable(
switchView.context,
switchView.trackDrawable, color, false, true, useDarker
)
}
if (switchView.thumbDrawable != null) {
switchView.thumbDrawable = modifySwitchDrawable(
switchView.context,
switchView.thumbDrawable, color, true, true, 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 drawable: Drawable? = drawable ?: return null
drawable = DrawableCompat.wrap(drawable!!.mutate())
DrawableCompat.setTintMode(drawable!!, PorterDuff.Mode.SRC_IN)
DrawableCompat.setTint(drawable, color)
return drawable
}
// 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 drawable: Drawable? = drawable ?: return null
drawable = DrawableCompat.wrap(drawable!!.mutate())
DrawableCompat.setTintList(drawable!!, sl)
return drawable
}
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) {
}
}
}

@ -1,48 +0,0 @@
package io.legado.app.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 androidx.annotation.NonNull;
import androidx.annotation.Nullable;
/**
* @author Karim Abou Zeid (kabouzeid)
*/
public final class ViewUtil {
@SuppressWarnings("deprecation")
public static void removeOnGlobalLayoutListener(View v, ViewTreeObserver.OnGlobalLayoutListener listener) {
v.getViewTreeObserver().removeOnGlobalLayoutListener(listener);
}
@SuppressWarnings("deprecation")
public static void setBackgroundCompat(@NonNull View view, @Nullable Drawable drawable) {
view.setBackground(drawable);
}
public static TransitionDrawable setBackgroundTransition(@NonNull View view, @NonNull Drawable newDrawable) {
TransitionDrawable transition = DrawableUtil.createTransitionDrawable(view.getBackground(), newDrawable);
setBackgroundCompat(view, transition);
return transition;
}
public static TransitionDrawable setBackgroundColorTransition(@NonNull View view, @ColorInt int newColor) {
final Drawable oldColor = view.getBackground();
Drawable start = oldColor != null ? oldColor : new ColorDrawable(view.getSolidColor());
Drawable end = new ColorDrawable(newColor);
TransitionDrawable transition = DrawableUtil.createTransitionDrawable(start, end);
setBackgroundCompat(view, transition);
return transition;
}
private ViewUtil() {
}
}

@ -0,0 +1,41 @@
package io.legado.app.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
/**
* @author Karim Abou Zeid (kabouzeid)
*/
object ViewUtil {
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 = DrawableUtil.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 = DrawableUtil.createTransitionDrawable(start, end)
setBackgroundCompat(view, transition)
return transition
}
}
Loading…
Cancel
Save