diff --git a/app/src/main/java/io/legado/app/lib/theme/ATH.java b/app/src/main/java/io/legado/app/lib/theme/ATH.java
new file mode 100644
index 000000000..d7ee194d1
--- /dev/null
+++ b/app/src/main/java/io/legado/app/lib/theme/ATH.java
@@ -0,0 +1,117 @@
+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 void 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);
+ }
+ }
+
+ private ATH() {
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/io/legado/app/lib/theme/ATHUtil.java b/app/src/main/java/io/legado/app/lib/theme/ATHUtil.java
new file mode 100644
index 000000000..eb84da3e5
--- /dev/null
+++ b/app/src/main/java/io/legado/app/lib/theme/ATHUtil.java
@@ -0,0 +1,31 @@
+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() {
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/io/legado/app/lib/theme/ColorUtil.java b/app/src/main/java/io/legado/app/lib/theme/ColorUtil.java
new file mode 100644
index 000000000..4fc255d07
--- /dev/null
+++ b/app/src/main/java/io/legado/app/lib/theme/ColorUtil.java
@@ -0,0 +1,80 @@
+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);
+ }
+
+}
diff --git a/app/src/main/java/io/legado/app/lib/theme/DrawableUtil.java b/app/src/main/java/io/legado/app/lib/theme/DrawableUtil.java
new file mode 100644
index 000000000..2254798f5
--- /dev/null
+++ b/app/src/main/java/io/legado/app/lib/theme/DrawableUtil.java
@@ -0,0 +1,28 @@
+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() {
+ }
+}
diff --git a/app/src/main/java/io/legado/app/lib/theme/MaterialValueHelper.java b/app/src/main/java/io/legado/app/lib/theme/MaterialValueHelper.java
new file mode 100644
index 000000000..51f9031b4
--- /dev/null
+++ b/app/src/main/java/io/legado/app/lib/theme/MaterialValueHelper.java
@@ -0,0 +1,52 @@
+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() {
+ }
+}
diff --git a/app/src/main/java/io/legado/app/lib/theme/NavigationViewUtil.java b/app/src/main/java/io/legado/app/lib/theme/NavigationViewUtil.java
new file mode 100644
index 000000000..0707cf132
--- /dev/null
+++ b/app/src/main/java/io/legado/app/lib/theme/NavigationViewUtil.java
@@ -0,0 +1,55 @@
+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() {
+ }
+}
diff --git a/app/src/main/java/io/legado/app/lib/theme/Selector.java b/app/src/main/java/io/legado/app/lib/theme/Selector.java
new file mode 100644
index 000000000..df4e44b59
--- /dev/null
+++ b/app/src/main/java/io/legado/app/lib/theme/Selector.java
@@ -0,0 +1,430 @@
+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);
+ }
+ }
+}
diff --git a/app/src/main/java/io/legado/app/lib/theme/ThemeStore.java b/app/src/main/java/io/legado/app/lib/theme/ThemeStore.java
new file mode 100644
index 000000000..52e4544ea
--- /dev/null
+++ b/app/src/main/java/io/legado/app/lib/theme/ThemeStore.java
@@ -0,0 +1,318 @@
+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;
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/io/legado/app/lib/theme/ThemeStoreInterface.java b/app/src/main/java/io/legado/app/lib/theme/ThemeStoreInterface.java
new file mode 100644
index 000000000..4c3214396
--- /dev/null
+++ b/app/src/main/java/io/legado/app/lib/theme/ThemeStoreInterface.java
@@ -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)
+ */
+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();
+}
diff --git a/app/src/main/java/io/legado/app/lib/theme/ThemeStorePrefKeys.java b/app/src/main/java/io/legado/app/lib/theme/ThemeStorePrefKeys.java
new file mode 100644
index 000000000..9a8286f9e
--- /dev/null
+++ b/app/src/main/java/io/legado/app/lib/theme/ThemeStorePrefKeys.java
@@ -0,0 +1,29 @@
+package io.legado.app.lib.theme;
+
+/**
+ * @author Aidan Follestad (afollestad), Karim Abou Zeid (kabouzeid)
+ */
+interface ThemeStorePrefKeys {
+
+ String CONFIG_PREFS_KEY_DEFAULT = "[[kabouzeid_app-theme-helper]]";
+ 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";
+}
\ No newline at end of file
diff --git a/app/src/main/java/io/legado/app/lib/theme/TintHelper.java b/app/src/main/java/io/legado/app/lib/theme/TintHelper.java
new file mode 100644
index 000000000..439190147
--- /dev/null
+++ b/app/src/main/java/io/legado/app/lib/theme/TintHelper.java
@@ -0,0 +1,384 @@
+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) {
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/io/legado/app/lib/theme/ViewUtil.java b/app/src/main/java/io/legado/app/lib/theme/ViewUtil.java
new file mode 100644
index 000000000..7fd0f65e3
--- /dev/null
+++ b/app/src/main/java/io/legado/app/lib/theme/ViewUtil.java
@@ -0,0 +1,48 @@
+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() {
+ }
+}
diff --git a/app/src/main/res/values-night/colors.xml b/app/src/main/res/values-night/colors.xml
new file mode 100644
index 000000000..d53b8cac2
--- /dev/null
+++ b/app/src/main/res/values-night/colors.xml
@@ -0,0 +1,34 @@
+
+
+ @color/md_grey_800
+ #353535
+ #282828
+
+ #69000000
+
+ @color/md_grey_800
+ @color/md_grey_900
+ @color/md_grey_100
+
+ #30ffffff
+
+ #363636
+ #804D4D4D
+ #80686868
+ #88111111
+ #66666666
+
+ #737373
+ #565656
+
+ #ffffffff
+ #e5ffffff
+ #b3ffffff
+ #b7b7b7
+
+
+ #303030
+
+
+ #222222
+
diff --git a/app/src/main/res/values-night/styles.xml b/app/src/main/res/values-night/styles.xml
new file mode 100644
index 000000000..c6d88fbc5
--- /dev/null
+++ b/app/src/main/res/values-night/styles.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml
index 8194c2ece..43af45038 100644
--- a/app/src/main/res/values/colors.xml
+++ b/app/src/main/res/values/colors.xml
@@ -7,4 +7,96 @@
#66666666
+ #eb4333
+ #439b53
+
+ #00000000
+
+ @color/md_grey_100
+ #dedede
+ #fcfcfc
+ #00000000
+
+ #30000000
+
+ #d3321b
+
+ #80ACACAC
+ #80858585
+ #88000000
+
+ #737373
+ #adadad
+
+ #de000000
+ #b2000000
+ #8a000000
+ #dfdfdf
+ #383838
+
+
+ #efefef
+
+ #23000000
+ #EEEEEE
+ #aaaaaaaa
+
+
+ #FFD4D4D4
+
+ #64000000
+ #f4f4f4
+
+
+ #99343434
+ #000000
+ #ffffff
+
+
+
+
+
+ #1F000000
+ #1F000000
+
+ #43000000
+ #43000000
+ #43000000
+
+ #61000000
+
+ #8A000000
+ #8A000000
+ #8A000000
+
+ #DE000000
+
+ #FFFAFAFA
+
+ #FFBDBDBD
+
+ #E8E8E8
+
+
+
+ #1AFFFFFF
+
+ #1F000000
+
+ #4DFFFFFF
+ #4DFFFFFF
+ #4DFFFFFF
+ #4DFFFFFF
+
+ #B3FFFFFF
+ #B3FFFFFF
+ #B3FFFFFF
+
+ #FFFFFFFF
+
+ #FFBDBDBD
+
+ #FF424242
+
+ #202020
diff --git a/app/src/main/res/values/colors_material_design.xml b/app/src/main/res/values/colors_material_design.xml
new file mode 100644
index 000000000..a00ddac9e
--- /dev/null
+++ b/app/src/main/res/values/colors_material_design.xml
@@ -0,0 +1,338 @@
+
+
+
+
+
+
+ @color/md_grey_50
+
+ #1F000000
+
+ #61000000
+
+ #8A000000
+ #8A000000
+
+ #DE000000
+
+ @color/md_grey_300
+ @color/md_grey_100
+ @color/md_white_1000
+ @color/md_white_1000
+
+
+ @color/md_grey_850
+
+ #1FFFFFFF
+
+ #4DFFFFFF
+
+ #B3FFFFFF
+ #B3FFFFFF
+
+ #FFFFFFFF
+
+ @color/md_black_1000
+ @color/md_grey_900
+ @color/md_grey_800
+ @color/md_grey_800
+
+
+ #FFEBEE
+ #FFCDD2
+ #EF9A9A
+ #E57373
+ #EF5350
+ #F44336
+ #E53935
+ #D32F2F
+ #C62828
+ #B71C1C
+ #FF8A80
+ #FF5252
+ #FF1744
+ #D50000
+
+
+ #FCE4EC
+ #F8BBD0
+ #F48FB1
+ #F06292
+ #EC407A
+ #E91E63
+ #D81B60
+ #C2185B
+ #AD1457
+ #880E4F
+ #FF80AB
+ #FF4081
+ #F50057
+ #C51162
+
+
+ #F3E5F5
+ #E1BEE7
+ #CE93D8
+ #BA68C8
+ #AB47BC
+ #9C27B0
+ #8E24AA
+ #7B1FA2
+ #6A1B9A
+ #4A148C
+ #EA80FC
+ #E040FB
+ #D500F9
+ #AA00FF
+
+
+ #EDE7F6
+ #D1C4E9
+ #B39DDB
+ #9575CD
+ #7E57C2
+ #673AB7
+ #5E35B1
+ #512DA8
+ #4527A0
+ #311B92
+ #B388FF
+ #7C4DFF
+ #651FFF
+ #6200EA
+
+
+ #E8EAF6
+ #C5CAE9
+ #9FA8DA
+ #7986CB
+ #5C6BC0
+ #3F51B5
+ #3949AB
+ #303F9F
+ #283593
+ #1A237E
+ #8C9EFF
+ #536DFE
+ #3D5AFE
+ #304FFE
+
+
+ #E3F2FD
+ #BBDEFB
+ #90CAF9
+ #64B5F6
+ #42A5F5
+ #2196F3
+ #1E88E5
+ #1976D2
+ #1565C0
+ #0D47A1
+ #82B1FF
+ #448AFF
+ #2979FF
+ #2962FF
+
+
+ #E1F5FE
+ #B3E5FC
+ #81D4FA
+ #4FC3F7
+ #29B6F6
+ #03A9F4
+ #039BE5
+ #0288D1
+ #0277BD
+ #01579B
+ #80D8FF
+ #40C4FF
+ #00B0FF
+ #0091EA
+
+
+ #E0F7FA
+ #B2EBF2
+ #80DEEA
+ #4DD0E1
+ #26C6DA
+ #00BCD4
+ #00ACC1
+ #0097A7
+ #00838F
+ #006064
+ #84FFFF
+ #18FFFF
+ #00E5FF
+ #00B8D4
+
+
+ #E0F2F1
+ #B2DFDB
+ #80CBC4
+ #4DB6AC
+ #26A69A
+ #009688
+ #00897B
+ #00796B
+ #00695C
+ #004D40
+ #A7FFEB
+ #64FFDA
+ #1DE9B6
+ #00BFA5
+
+
+ #E8F5E9
+ #C8E6C9
+ #A5D6A7
+ #81C784
+ #66BB6A
+ #4CAF50
+ #43A047
+ #388E3C
+ #2E7D32
+ #1B5E20
+ #B9F6CA
+ #69F0AE
+ #00E676
+ #00C853
+
+
+ #F1F8E9
+ #DCEDC8
+ #C5E1A5
+ #AED581
+ #9CCC65
+ #8BC34A
+ #7CB342
+ #689F38
+ #558B2F
+ #33691E
+ #CCFF90
+ #B2FF59
+ #76FF03
+ #64DD17
+
+
+ #F9FBE7
+ #F0F4C3
+ #E6EE9C
+ #DCE775
+ #D4E157
+ #CDDC39
+ #C0CA33
+ #AFB42B
+ #9E9D24
+ #827717
+ #F4FF81
+ #EEFF41
+ #C6FF00
+ #AEEA00
+
+
+ #FFFDE7
+ #FFF9C4
+ #FFF59D
+ #FFF176
+ #FFEE58
+ #FFEB3B
+ #FDD835
+ #FBC02D
+ #F9A825
+ #F57F17
+ #FFFF8D
+ #FFFF00
+ #FFEA00
+ #FFD600
+
+
+ #FFF8E1
+ #FFECB3
+ #FFE082
+ #FFD54F
+ #FFCA28
+ #FFC107
+ #FFB300
+ #FFA000
+ #FF8F00
+ #FF6F00
+ #FFE57F
+ #FFD740
+ #FFC400
+ #FFAB00
+
+
+ #FFF3E0
+ #FFE0B2
+ #FFCC80
+ #FFB74D
+ #FFA726
+ #FF9800
+ #FB8C00
+ #F57C00
+ #EF6C00
+ #E65100
+ #FFD180
+ #FFAB40
+ #FF9100
+ #FF6D00
+
+
+ #FBE9E7
+ #FFCCBC
+ #FFAB91
+ #FF8A65
+ #FF7043
+ #FF5722
+ #F4511E
+ #E64A19
+ #D84315
+ #BF360C
+ #FF9E80
+ #FF6E40
+ #FF3D00
+ #DD2C00
+
+
+ #EFEBE9
+ #D7CCC8
+ #BCAAA4
+ #A1887F
+ #8D6E63
+ #795548
+ #6D4C41
+ #5D4037
+ #4E342E
+ #3E2723
+
+
+ #FAFAFA
+ #F5F5F5
+ #EEEEEE
+ #E0E0E0
+ #BDBDBD
+ #9E9E9E
+ #757575
+ #616161
+ #424242
+ #303030
+ #212121
+
+
+ #ECEFF1
+ #CFD8DC
+ #B0BEC5
+ #90A4AE
+ #78909C
+ #607D8B
+ #546E7A
+ #455A64
+ #37474F
+ #263238
+
+
+ #000000
+ #FFFFFF
+
+