Merge remote-tracking branch 'origin/master' into desirepath

pull/32/head
Awesome 6 years ago
commit b9766fa94c
  1. 4
      app/src/main/java/com/jayway/jsonpath/spi/json/GsonJsonProvider.java
  2. 3
      app/src/main/java/io/legado/app/base/BaseActivity.kt
  3. 11
      app/src/main/java/io/legado/app/data/dao/BookDao.kt
  4. 71
      app/src/main/java/io/legado/app/data/dao/ExploreSearchUrlDao.kt
  5. 1
      app/src/main/java/io/legado/app/data/dao/ReplaceRuleDao.kt
  6. 32
      app/src/main/java/io/legado/app/data/dao/SearchKeywordDao.kt
  7. 4
      app/src/main/java/io/legado/app/data/entities/Book.kt
  8. 29
      app/src/main/java/io/legado/app/data/entities/ExploreSearchUrl.kt
  9. 17
      app/src/main/java/io/legado/app/data/entities/SearchKeyword.kt
  10. 117
      app/src/main/java/io/legado/app/lib/theme/ATH.java
  11. 31
      app/src/main/java/io/legado/app/lib/theme/ATHUtil.java
  12. 80
      app/src/main/java/io/legado/app/lib/theme/ColorUtil.java
  13. 28
      app/src/main/java/io/legado/app/lib/theme/DrawableUtil.java
  14. 52
      app/src/main/java/io/legado/app/lib/theme/MaterialValueHelper.java
  15. 55
      app/src/main/java/io/legado/app/lib/theme/NavigationViewUtil.java
  16. 430
      app/src/main/java/io/legado/app/lib/theme/Selector.java
  17. 318
      app/src/main/java/io/legado/app/lib/theme/ThemeStore.java
  18. 92
      app/src/main/java/io/legado/app/lib/theme/ThemeStoreInterface.java
  19. 29
      app/src/main/java/io/legado/app/lib/theme/ThemeStorePrefKeys.java
  20. 384
      app/src/main/java/io/legado/app/lib/theme/TintHelper.java
  21. 48
      app/src/main/java/io/legado/app/lib/theme/ViewUtil.java
  22. 92
      app/src/main/java/io/legado/app/ui/main/MainActivity.kt
  23. 39
      app/src/main/java/io/legado/app/ui/replacerule/ReplaceRuleActivity.kt
  24. 22
      app/src/main/java/io/legado/app/ui/replacerule/ReplaceRuleAdapter.kt
  25. 9
      app/src/main/java/io/legado/app/utils/MiscExtensions.kt
  26. 6
      app/src/main/res/drawable/ic_divider.xml
  27. 4
      app/src/main/res/layout/activity_replace_rule.xml
  28. 65
      app/src/main/res/layout/item_relace_rule.xml
  29. 34
      app/src/main/res/values-night/colors.xml
  30. 19
      app/src/main/res/values-night/styles.xml
  31. 92
      app/src/main/res/values/colors.xml
  32. 338
      app/src/main/res/values/colors_material_design.xml
  33. 1
      app/src/main/res/values/strings.xml

@ -247,8 +247,8 @@ public class GsonJsonProvider extends AbstractJsonProvider {
}
}
throw new JsonPathException("length operation can not applied to " + obj != null ? obj.getClass().getName()
: "null");
throw new JsonPathException("length operation can not applied to " + (obj != null ? obj.getClass().getName()
: "null"));
}
@Override

@ -6,6 +6,7 @@ import androidx.appcompat.app.AppCompatActivity
import androidx.databinding.DataBindingUtil
import androidx.databinding.ViewDataBinding
import androidx.lifecycle.ViewModel
import com.google.android.material.bottomnavigation.BottomNavigationView
abstract class BaseActivity<BD : ViewDataBinding, VM : ViewModel> : AppCompatActivity() {
@ -33,7 +34,7 @@ abstract class BaseActivity<BD : ViewDataBinding, VM : ViewModel> : AppCompatAct
return true
}
}
return if (item == null) false else onCompatOptionsItemSelected(item)
return item != null && onCompatOptionsItemSelected(item)
}
open fun onCompatOptionsItemSelected(item: MenuItem): Boolean {

@ -3,6 +3,8 @@ package io.legado.app.data.dao
import androidx.lifecycle.LiveData
import androidx.paging.DataSource
import androidx.room.Dao
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import io.legado.app.data.entities.Book
@ -19,4 +21,13 @@ interface BookDao {
@Query("SELECT * FROM books WHERE `name` in (:names)")
fun findByName(vararg names: String): List<Book>
@get:Query("SELECT descUrl FROM books")
val allBookUrls: List<String>
@get:Query("SELECT COUNT(*) FROM books")
val allBookCount: Int
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insert(vararg books: Book)
}

@ -0,0 +1,71 @@
package io.legado.app.data.dao
import androidx.paging.DataSource
import androidx.room.*
import io.legado.app.data.entities.ExploreSearchUrl
@Dao
interface ExploreSearchUrlDao {
companion object {
private const val ORDER_DEFAULT = "ORDER BY sourceId ASC, defOrder ASC"
private const val ORDER_USAGE = "ORDER BY usage DESC, lastUseTime DESC"
private const val ORDER_TIME = "ORDER BY lastUseTime DESC"
private const val QUERY_NAME = "name LIKE '%' || :name || '%'"
private const val QUERY_ENABLED_EXPLORE = "WHERE type = 0 AND isEnabled = 1"
}
// 用于发现列表,默认排序
@Query("SELECT * FROM explore_search_urls $QUERY_ENABLED_EXPLORE $ORDER_DEFAULT")
fun observeExploreUrls(): DataSource.Factory<Int, ExploreSearchUrl>
// 用于发现列表,按使用次数排序
@Query("SELECT * FROM explore_search_urls $QUERY_ENABLED_EXPLORE $ORDER_USAGE")
fun observeExploreUrlsByUsage(): DataSource.Factory<Int, ExploreSearchUrl>
// 用于发现列表,按使用时间排序
@Query("SELECT * FROM explore_search_urls $QUERY_ENABLED_EXPLORE $ORDER_TIME")
fun observeExploreUrlsByTime(): DataSource.Factory<Int, ExploreSearchUrl>
// 用于搜索时的发现列表,默认排序
@Query("SELECT * FROM explore_search_urls $QUERY_ENABLED_EXPLORE AND $QUERY_NAME $ORDER_DEFAULT")
fun observeFilteredExploreUrls(name: String): DataSource.Factory<Int, ExploreSearchUrl>
// 用于搜索时的发现列表,按使用次数排序
@Query("SELECT * FROM explore_search_urls $QUERY_ENABLED_EXPLORE AND $QUERY_NAME $ORDER_USAGE")
fun observeFilteredExploreUrlsByUsage(): DataSource.Factory<Int, ExploreSearchUrl>
// 用于搜索时的发现列表,按使用时间排序
@Query("SELECT * FROM explore_search_urls $QUERY_ENABLED_EXPLORE AND $QUERY_NAME $ORDER_TIME")
fun observeFilteredExploreUrlsByTime(): DataSource.Factory<Int, ExploreSearchUrl>
// 获取特定书源的发现
@Query("SELECT * FROM explore_search_urls $QUERY_ENABLED_EXPLORE AND sourceId = :sourceId")
fun findExploreUrlsBySourceId(sourceId: Int): List<ExploreSearchUrl>
// 获取特定书源的搜索链接
@Query("SELECT * FROM explore_search_urls WHERE type = 1 AND sourceId = :sourceId")
fun findSearchUrlsBySourceId(sourceId: Int): List<ExploreSearchUrl>
// 所有的搜索链接
@get:Query("SELECT * FROM explore_search_urls WHERE type = 1")
val allSearchUrls: List<ExploreSearchUrl>
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insert(vararg keywords: ExploreSearchUrl)
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insert(keyword: ExploreSearchUrl): Long
@Update
fun update(vararg keywords: ExploreSearchUrl)
@Delete
fun delete(vararg keywords: ExploreSearchUrl)
// 批量删除特定书源的发现和搜索链接,一般用于更新书源时
@Query("DELETE FROM explore_search_urls WHERE sourceId = :sourceId")
fun deleteBySourceId(sourceId: Int)
}

@ -44,6 +44,7 @@ interface ReplaceRuleDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insert(replaceRule: ReplaceRule): Long
@Update
fun update(vararg replaceRules: ReplaceRule)

@ -0,0 +1,32 @@
package io.legado.app.data.dao
import androidx.paging.DataSource
import androidx.room.*
import io.legado.app.data.entities.SearchKeyword
@Dao
interface SearchKeywordDao {
@Query("SELECT * FROM search_keywords ORDER BY usage DESC")
fun observeByUsage(): DataSource.Factory<Int, SearchKeyword>
@Query("SELECT * FROM search_keywords ORDER BY lastUseTime DESC")
fun observeByTime(): DataSource.Factory<Int, SearchKeyword>
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insert(vararg keywords: SearchKeyword)
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insert(keyword: SearchKeyword): Long
@Update
fun update(vararg keywords: SearchKeyword)
@Delete
fun delete(vararg keywords: SearchKeyword)
@Query("DELETE FROM search_keywords")
fun deleteAll()
}

@ -29,11 +29,13 @@ data class Book(@PrimaryKey
var lastCheckTime: Long = 0, // 最近一次更新书籍信息的时间
var lastCheckCount: Int = 0, // 最近一次发现新章节的数量
var totalChapterNum: Int = 0, // 书籍目录总数
var durChapterTitle: String? = null, // 当前章节名称
var durChapterTitle: String? = null, // 当前章节名称
var durChapterIndex: Int = 0, // 当前章节索引
var durChapterPos: Int = 0, // 当前阅读的进度(首行字符的索引位置)
var durChapterTime: Long = 0, // 最近一次阅读书籍的时间(打开正文的时间)
var canUpdate: Boolean = true, // 刷新书架时更新书籍信息
var order: Int = 0, // 手动排序
var useReplaceRule: Boolean = true, // 正文使用净化替换规则
var variable: String? = null // 自定义书籍变量信息(用于书源规则检索书籍信息)
) : Parcelable {

@ -0,0 +1,29 @@
package io.legado.app.data.entities
import android.os.Parcelable
import androidx.room.Entity
import androidx.room.ForeignKey
import androidx.room.Index
import androidx.room.PrimaryKey
import kotlinx.android.parcel.Parcelize
@Parcelize
@Entity(tableName = "explore_search_urls",
indices = [(Index(value = ["sourceId", "url"], unique = true))],
foreignKeys = [(ForeignKey(entity = Source::class,
parentColumns = ["sourceId"],
childColumns = ["sourceId"],
onDelete = ForeignKey.CASCADE))]) // 删除书源时自动删除章节
data class ExploreSearchUrl (
@PrimaryKey(autoGenerate = true)
var esId: Int = 0, // 编号
var sourceId: Int = 0, // 书源Id
var name: String = "", // 发现名称,搜索可以没有
var url: String = "", // 地址
var type: Int = 0, // 类型,0 为发现,1 为搜索
var isEnabled: Boolean = true, // 是否启用
var defOrder: Int = 0, // 默认排序,是在编辑书源的时候的顺序
var usage: Int = 0, // 使用次数,用于按使用次数排序
var lastUseTime: Long = 0L // 最后一次使用的时间
) : Parcelable

@ -0,0 +1,17 @@
package io.legado.app.data.entities
import android.os.Parcelable
import androidx.room.Entity
import androidx.room.Index
import androidx.room.PrimaryKey
import kotlinx.android.parcel.Parcelize
@Parcelize
@Entity(tableName = "search_keywords", indices = [(Index(value = ["word"], unique = true))])
data class SearchKeyword (
@PrimaryKey
var word: String = "", // 搜索关键词
var usage: Int = 1, // 使用次数
var lastUseTime: Long = 0 // 最后一次使用时间
): Parcelable

@ -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() {
}
}

@ -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() {
}
}

@ -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);
}
}

@ -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() {
}
}

@ -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() {
}
}

@ -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() {
}
}

@ -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);
}
}
}

@ -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;
}
}

@ -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();
}

@ -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";
}

@ -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) {
}
}
}

@ -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() {
}
}

@ -16,6 +16,7 @@ import io.legado.app.App
import io.legado.app.R
import io.legado.app.base.BaseActivity
import io.legado.app.constant.AppConst.APP_TAG
import io.legado.app.data.entities.Book
import io.legado.app.data.entities.ReplaceRule
import io.legado.app.help.permission.Permissions
import io.legado.app.help.permission.PermissionsCompat
@ -107,30 +108,85 @@ class MainActivity : BaseActivity<MainDataBinding, MainViewModel>(), NavigationV
.build()
)
// Replace rules
val rFile = File(yuedu, "myBookReplaceRule.json")
val replaceRules = mutableListOf<ReplaceRule>()
if (rFile.exists()) try {
val items: List<Map<String, Any>> = jsonPath.parse(rFile.readText()).read("$.*")
for (item in items) {
val jsonItem = jsonPath.parse(item)
val rRule = ReplaceRule()
rRule.name = jsonItem.readString("$.replaceSummary")
rRule.pattern = jsonItem.readString("$.regex")
rRule.replacement = jsonItem.readString("$.replacement")
rRule.isRegex = jsonItem.readBool("$.isRegex")
rRule.scope = jsonItem.readString("$.useTo")
rRule.isEnabled = jsonItem.readBool("$.enable")
rRule.order = jsonItem.readInt("$.serialNumber")
replaceRules.add(rRule)
// 导入书架
val shelfFile = File(yuedu, "myBookShelf.json")
val books = mutableListOf<Book>()
if (shelfFile.exists()) try {
doAsync {
val items: List<Map<String, Any>> = jsonPath.parse(shelfFile.readText()).read("$")
val existingBooks = App.db.bookDao().allBookUrls.toSet()
for (item in items) {
val jsonItem = jsonPath.parse(item)
val book = Book()
book.descUrl = jsonItem.readString("$.noteUrl") ?: ""
if (book.descUrl.isBlank()) continue
book.name = jsonItem.readString("$.bookInfoBean.name")
if (book.descUrl in existingBooks) {
Log.d(APP_TAG, "Found existing book: ${book.name}")
continue
}
book.author = jsonItem.readString("$.bookInfoBean.author")
book.type = if (jsonItem.readString("$.bookInfoBean.bookSourceType") == "AUDIO") 1 else 0
book.tocUrl = jsonItem.readString("$.bookInfoBean.chapterUrl") ?: book.descUrl
book.coverUrl = jsonItem.readString("$.bookInfoBean.coverUrl")
book.customCoverUrl = jsonItem.readString("$.customCoverPath")
book.lastCheckTime = jsonItem.readLong("$.bookInfoBean.finalRefreshData") ?: 0
book.canUpdate = jsonItem.readBool("$.allowUpdate") == true
book.totalChapterNum = jsonItem.readInt("$.chapterListSize") ?: 0
book.durChapterIndex = jsonItem.readInt("$.durChapter") ?: 0
book.durChapterTitle = jsonItem.readString("$.durChapterName")
book.durChapterPos = jsonItem.readInt("$.durChapterPage") ?: 0
book.durChapterTime = jsonItem.readLong("$.finalDate") ?: 0
book.group = jsonItem.readInt("$.group") ?: 0
// book. = jsonItem.readString("$.hasUpdate")
// book. = jsonItem.readString("$.isLoading")
book.latestChapterTitle = jsonItem.readString("$.lastChapterName")
book.lastCheckCount = jsonItem.readInt("$.newChapters") ?: 0
book.order = jsonItem.readInt("$.serialNumber") ?: 0
book.useReplaceRule = jsonItem.readBool("$.useReplaceRule") == true
book.variable = jsonItem.readString("$.variable")
books.add(book)
Log.d(APP_TAG, "Added ${book.name}")
}
App.db.bookDao().insert(*books.toTypedArray())
val count = books.size
uiThread {
toast(if (count > 0) "成功地导入 $count 本新书和音频" else "没有发现新书或音频")
}
}
} catch (e: Exception) {
Log.e(APP_TAG, "Failed to import book shelf.", e)
toast("Unable to import books:\n${e.localizedMessage}")
}
// Replace rules
val ruleFile = File(yuedu, "myBookReplaceRule.json")
val replaceRules = mutableListOf<ReplaceRule>()
if (ruleFile.exists()) try {
doAsync {
val items: List<Map<String, Any>> = jsonPath.parse(ruleFile.readText()).read("$")
val existingRules = App.db.replaceRuleDao().all.map { it.pattern }.toSet()
for (item in items) {
val jsonItem = jsonPath.parse(item)
val rRule = ReplaceRule()
rRule.pattern = jsonItem.readString("$.regex")
if (rRule.pattern.isNullOrEmpty() || rRule.pattern in existingRules) continue
rRule.name = jsonItem.readString("$.replaceSummary")
rRule.replacement = jsonItem.readString("$.replacement")
rRule.isRegex = jsonItem.readBool("$.isRegex") == true
rRule.scope = jsonItem.readString("$.useTo")
rRule.isEnabled = jsonItem.readBool("$.enable") == true
rRule.order = jsonItem.readInt("$.serialNumber") ?: 0
replaceRules.add(rRule)
}
App.db.replaceRuleDao().insert(*replaceRules.toTypedArray())
val count = items.size
val count = replaceRules.size
val maxId = App.db.replaceRuleDao().maxOrder
uiThread {
toast("成功地导入 $count 条净化替换规则")
toast(if (count > 0) "成功地导入 $count净化替换规则" else "没有发现新的净化替换规则")
}
}

@ -1,11 +1,14 @@
package io.legado.app.ui.replacerule
import android.os.Bundle
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
import androidx.lifecycle.LiveData
import androidx.lifecycle.Observer
import androidx.paging.LivePagedListBuilder
import androidx.paging.PagedList
import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.LinearLayoutManager
import io.legado.app.App
import io.legado.app.R
@ -13,6 +16,10 @@ import io.legado.app.data.entities.ReplaceRule
import kotlinx.android.synthetic.main.activity_replace_rule.*
import org.jetbrains.anko.doAsync
import org.jetbrains.anko.toast
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.ItemTouchHelper
import io.legado.app.constant.AppConst.APP_TAG
import kotlinx.android.synthetic.main.item_relace_rule.*
class ReplaceRuleActivity : AppCompatActivity() {
@ -23,9 +30,9 @@ class ReplaceRuleActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_replace_rule)
rv_replace_rule.layoutManager = LinearLayoutManager(this)
initRecyclerView()
initDataObservers()
initSwipeToDelete()
}
private fun initRecyclerView() {
@ -54,6 +61,12 @@ class ReplaceRuleActivity : AppCompatActivity() {
}
}
rv_replace_rule.adapter = adapter
rv_replace_rule.addItemDecoration(
DividerItemDecoration(this, DividerItemDecoration.VERTICAL).apply {
ContextCompat.getDrawable(baseContext, R.drawable.ic_divider)?.let {
Log.e(APP_TAG, it.toString())
this.setDrawable(it) }
})
}
private fun initDataObservers() {
@ -69,4 +82,28 @@ class ReplaceRuleActivity : AppCompatActivity() {
}
}
}
private fun initSwipeToDelete() {
ItemTouchHelper(object : ItemTouchHelper.Callback() {
override fun getMovementFlags(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder): Int {
return ItemTouchHelper.Callback.makeMovementFlags(0, ItemTouchHelper.LEFT or ItemTouchHelper.RIGHT)
}
override fun onMove(
recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder,
target: RecyclerView.ViewHolder
): Boolean {
return false
}
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
toast("You swiped the item!")
// TODO()
// remove((viewHolder as TodoViewHolder).todo)
}
}).attachToRecyclerView(rv_replace_rule)
}
}

@ -1,6 +1,7 @@
package io.legado.app.ui.replacerule
import android.content.Context
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
@ -9,6 +10,7 @@ import androidx.paging.PagedListAdapter
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView
import io.legado.app.R
import io.legado.app.constant.AppConst.APP_TAG
import io.legado.app.data.entities.ReplaceRule
import kotlinx.android.synthetic.main.item_relace_rule.view.*
import org.jetbrains.anko.sdk27.listeners.onClick
@ -45,18 +47,20 @@ class ReplaceRuleAdapter(context: Context) :
}
override fun onBindViewHolder(holder: MyViewHolder, pos: Int) {
getItem(pos)?.let { holder.bind(it, onClickListener, pos == itemCount - 1) }
getItem(pos)?.let { holder.bind(it, onClickListener) }
}
class MyViewHolder(view: View) : RecyclerView.ViewHolder(view) {
fun bind(rule: ReplaceRule, listener: OnClickListener?, hideDivider: Boolean) = with(itemView) {
cb_enable.text = rule.name
cb_enable.isChecked = rule.isEnabled
divider.isGone = hideDivider
iv_delete.onClick { listener?.delete(rule) }
iv_edit.onClick { listener?.edit(rule) }
cb_enable.onClick {
rule.isEnabled = cb_enable.isChecked
fun bind(rule: ReplaceRule, listener: OnClickListener?) = with(itemView) {
tv_name.text = rule.name
swt_enabled.isChecked = rule.isEnabled
// divider.isGone = hideDivider
iv_delete.isGone = true
iv_edit.isGone = true
// iv_delete.onClick { listener?.delete(rule) }
// iv_edit.onClick { listener?.edit(rule) }
swt_enabled.onClick {
rule.isEnabled = swt_enabled.isChecked
listener?.update(rule)
}
}

@ -2,8 +2,11 @@ package io.legado.app.utils
import com.jayway.jsonpath.ReadContext
fun ReadContext.readString(path: String) = this.read(path, String::class.java)
fun ReadContext.readString(path: String): String? = this.read(path, String::class.java)
fun ReadContext.readBool(path: String) = this.read(path, Boolean::class.java)
fun ReadContext.readBool(path: String): Boolean? = this.read(path, Boolean::class.java)
fun ReadContext.readInt(path: String): Int? = this.read(path, Int::class.java)
fun ReadContext.readLong(path: String): Long? = this.read(path, Long::class.java)
fun ReadContext.readInt(path: String) = this.read(path, Int::class.java)

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<size android:height="0.5dp" />
<solid android:color="@color/divider" />
</shape>

@ -18,8 +18,8 @@
android:id="@+id/rv_replace_rule"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingStart="8dp"
android:paddingEnd="8dp"
android:paddingStart="16dp"
android:paddingEnd="16dp"
android:layout_marginBottom="8dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"

@ -2,24 +2,9 @@
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingStart="8dp"
android:paddingEnd="8dp">
<androidx.appcompat.widget.AppCompatCheckBox
android:id="@+id/cb_enable"
android:text="Rule Name"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:paddingTop="16dp"
android:paddingBottom="16dp"
android:textSize="16sp"
android:textColor="@color/text_default"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="@+id/iv_delete"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent" />
android:layout_height="wrap_content">
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/iv_edit"
@ -29,6 +14,7 @@
android:background="@drawable/bg_ib_pre_round"
android:contentDescription="@string/edit"
android:src="@drawable/ic_edit"
android:visibility="gone"
app:tint="@color/text_default"
app:layout_constraintEnd_toStartOf="@id/iv_delete"
app:layout_constraintBottom_toBottomOf="parent"
@ -42,16 +28,61 @@
android:background="@drawable/bg_ib_pre_round"
android:contentDescription="@string/delete"
android:src="@drawable/ic_clear_all"
android:visibility="gone"
app:tint="@color/text_default"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
<androidx.appcompat.widget.AppCompatCheckBox
android:id="@+id/cb_selected"
android:text=""
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingStart="8dp"
android:paddingTop="16dp"
android:paddingBottom="16dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="@+id/tv_name"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/tv_name"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:paddingTop="16dp"
android:paddingBottom="16dp"
android:textSize="16sp"
android:text="Item Name"
android:textColor="@color/text_default"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="@id/cb_selected"
app:layout_constraintEnd_toStartOf="@id/swt_enabled"/>
<Switch
android:id="@+id/swt_enabled"
android:name="@string/enable"
android:text=""
android:paddingStart="16dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/tv_name"
app:layout_constraintHorizontal_bias="1"/>
<!--
<View
android:id="@+id/divider"
android:layout_width="match_parent"
android:layout_height="0.5dp"
android:background="@color/divider"
app:layout_constraintBottom_toBottomOf="parent"/>
-->
</androidx.constraintlayout.widget.ConstraintLayout>

@ -0,0 +1,34 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="background">@color/md_grey_800</color>
<color name="background_card">#353535</color>
<color name="background_menu">#282828</color>
<color name="night_mask">#69000000</color>
<color name="colorPrimary">@color/md_grey_800</color>
<color name="colorPrimaryDark">@color/md_grey_900</color>
<color name="colorAccent">@color/md_grey_100</color>
<color name="transparent30">#30ffffff</color>
<color name="bg_divider_line">#363636</color>
<color name="btn_bg_press">#804D4D4D</color>
<color name="btn_bg_press_2">#80686868</color>
<color name="btn_bg_press_tp">#88111111</color>
<color name="darker_gray">#66666666</color>
<color name="tv_btn_normal_black">#737373</color>
<color name="tv_btn_press_black">#565656</color>
<color name="tv_text_default">#ffffffff</color>
<color name="tv_text_secondary">#e5ffffff</color>
<color name="tv_text_summary">#b3ffffff</color>
<color name="menu_color_default">#b7b7b7</color>
<color name="tv_text_button_nor">#303030</color>
<!--statusBarBackground-->
<color name="navigation_bar_bag">#222222</color>
</resources>

@ -0,0 +1,19 @@
<resources>
<style name="AppTheme.PopupOverlay" parent="ThemeOverlay.AppCompat.Dark">
<item name="overlapAnchor">false</item>
<item name="colorAccent">@color/md_grey_100</item>
</style>
<style name="Style.Shadow.Top" parent="android:Widget">
<item name="android:layout_width">match_parent</item>
<item name="android:layout_height">@dimen/shadow_height</item>
<item name="android:background">@drawable/bg_shadow_top_night</item>
</style>
<style name="Style.Shadow.Bottom" parent="android:Widget">
<item name="android:layout_width">match_parent</item>
<item name="android:layout_height">@dimen/shadow_height</item>
<item name="android:background">@drawable/bg_shadow_bottom_night</item>
</style>
</resources>

@ -7,4 +7,96 @@
<color name="divider">#66666666</color>
<color name="error">#eb4333</color>
<color name="success">#439b53</color>
<color name="night_mask">#00000000</color>
<color name="background">@color/md_grey_100</color>
<color name="background_card">#dedede</color>
<color name="background_menu">#fcfcfc</color>
<color name="transparent">#00000000</color>
<color name="transparent30">#30000000</color>
<color name="highlight">#d3321b</color>
<color name="btn_bg_press">#80ACACAC</color>
<color name="btn_bg_press_2">#80858585</color>
<color name="btn_bg_press_tp">#88000000</color>
<color name="tv_btn_normal_black">#737373</color>
<color name="tv_btn_press_black">#adadad</color>
<color name="tv_text_default">#de000000</color>
<color name="tv_text_secondary">#b2000000</color>
<color name="tv_text_summary">#8a000000</color>
<color name="tv_text_book_detail">#dfdfdf</color>
<color name="menu_color_default">#383838</color>
<color name="tv_text_button_nor">#efefef</color>
<color name="light_translucent">#23000000</color>
<color name="common_gray">#EEEEEE</color>
<color name="darker_gray">#aaaaaaaa</color>
<!-- 分格线背景色 -->
<color name="bg_divider_line">#FFD4D4D4</color>
<!--statusBarBackground-->
<color name="status_bar_bag">#64000000</color>
<color name="navigation_bar_bag">#f4f4f4</color>
<!--base-color-->
<color name="translucent">#99343434</color>
<color name="black">#000000</color>
<color name="white">#ffffff</color>
<!--"control" means checkbox / radio button-->
<!--LIGHT-->
<!--12%-->
<color name="ate_switch_track_disabled_light">#1F000000</color>
<color name="ate_button_disabled_light">#1F000000</color>
<!-- 26% -->
<color name="ate_control_disabled_light">#43000000</color>
<color name="ate_switch_track_normal_light">#43000000</color>
<color name="ate_button_text_disabled_light">#43000000</color>
<!-- 38% -->
<color name="ate_text_disabled_light">#61000000</color>
<!-- 54% -->
<color name="ate_secondary_text_light">#8A000000</color>
<color name="ate_icon_light">#8A000000</color>
<color name="ate_control_normal_light">#8A000000</color>
<!-- 87% -->
<color name="ate_primary_text_light">#DE000000</color>
<!--Grey 50, #FAFAFA, Opacity 100%-->
<color name="ate_switch_thumb_normal_light">#FFFAFAFA</color>
<!--Grey 400, #BDBDBD, Opacity 100%-->
<color name="ate_switch_thumb_disabled_light">#FFBDBDBD</color>
<!---->
<color name="ate_navigation_drawer_selected_light">#E8E8E8</color>
<!--DARK-->
<!--10%-->
<color name="ate_switch_track_disabled_dark">#1AFFFFFF</color>
<!--12%-->
<color name="ate_button_disabled_dark">#1F000000</color>
<!-- 30% -->
<color name="ate_control_disabled_dark">#4DFFFFFF</color>
<color name="ate_switch_track_normal_dark">#4DFFFFFF</color>
<color name="ate_button_text_disabled_dark">#4DFFFFFF</color>
<color name="ate_text_disabled_dark">#4DFFFFFF</color>
<!-- 70% -->
<color name="ate_secondary_text_dark">#B3FFFFFF</color>
<color name="ate_icon_dark">#B3FFFFFF</color>
<color name="ate_control_normal_dark">#B3FFFFFF</color>
<!-- 100% -->
<color name="ate_primary_text_dark">#FFFFFFFF</color>
<!--Grey 400, #BDBDBD, Opacity 100%-->
<color name="ate_switch_thumb_normal_dark">#FFBDBDBD</color>
<!--Grey 800, #424242, Opacity 100%-->
<color name="ate_switch_thumb_disabled_dark">#FF424242</color>
<!---->
<color name="ate_navigation_drawer_selected_dark">#202020</color>
</resources>

@ -0,0 +1,338 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Google's Material Design colors from
https://www.google.com/design/spec/style/color.html#color-color-palette -->
<!-- Items Light -->
<color name="md_light_background">@color/md_grey_50</color>
<!-- 12% -->
<color name="md_light_dividers">#1F000000</color>
<!-- 38% -->
<color name="md_light_disabled">#61000000</color>
<!-- 54% -->
<color name="md_light_secondary">#8A000000</color>
<color name="md_light_primary_icon">#8A000000</color>
<!-- 87% -->
<color name="md_light_primary_text">#DE000000</color>
<!-- other specifications -->
<color name="md_light_statusbar">@color/md_grey_300</color>
<color name="md_light_appbar">@color/md_grey_100</color>
<color name="md_light_cards">@color/md_white_1000</color>
<color name="md_light_dialogs">@color/md_white_1000</color>
<!-- Items Dark -->
<color name="md_dark_background">@color/md_grey_850</color>
<!-- 12% -->
<color name="md_dark_dividers">#1FFFFFFF</color>
<!-- 30% -->
<color name="md_dark_disabled">#4DFFFFFF</color>
<!-- 70% -->
<color name="md_dark_secondary">#B3FFFFFF</color>
<color name="md_dark_primary_icon">#B3FFFFFF</color>
<!-- 100% -->
<color name="md_dark_primary_text">#FFFFFFFF</color>
<!-- other specifications -->
<color name="md_dark_statusbar">@color/md_black_1000</color>
<color name="md_dark_appbar">@color/md_grey_900</color>
<color name="md_dark_cards">@color/md_grey_800</color>
<color name="md_dark_dialogs">@color/md_grey_800</color>
<!-- Red -->
<color name="md_red_50">#FFEBEE</color>
<color name="md_red_100">#FFCDD2</color>
<color name="md_red_200">#EF9A9A</color>
<color name="md_red_300">#E57373</color>
<color name="md_red_400">#EF5350</color>
<color name="md_red_500">#F44336</color>
<color name="md_red_600">#E53935</color>
<color name="md_red_700">#D32F2F</color>
<color name="md_red_800">#C62828</color>
<color name="md_red_900">#B71C1C</color>
<color name="md_red_A100">#FF8A80</color>
<color name="md_red_A200">#FF5252</color>
<color name="md_red_A400">#FF1744</color>
<color name="md_red_A700">#D50000</color>
<!-- Pink -->
<color name="md_pink_50">#FCE4EC</color>
<color name="md_pink_100">#F8BBD0</color>
<color name="md_pink_200">#F48FB1</color>
<color name="md_pink_300">#F06292</color>
<color name="md_pink_400">#EC407A</color>
<color name="md_pink_500">#E91E63</color>
<color name="md_pink_600">#D81B60</color>
<color name="md_pink_700">#C2185B</color>
<color name="md_pink_800">#AD1457</color>
<color name="md_pink_900">#880E4F</color>
<color name="md_pink_A100">#FF80AB</color>
<color name="md_pink_A200">#FF4081</color>
<color name="md_pink_A400">#F50057</color>
<color name="md_pink_A700">#C51162</color>
<!-- Purple -->
<color name="md_purple_50">#F3E5F5</color>
<color name="md_purple_100">#E1BEE7</color>
<color name="md_purple_200">#CE93D8</color>
<color name="md_purple_300">#BA68C8</color>
<color name="md_purple_400">#AB47BC</color>
<color name="md_purple_500">#9C27B0</color>
<color name="md_purple_600">#8E24AA</color>
<color name="md_purple_700">#7B1FA2</color>
<color name="md_purple_800">#6A1B9A</color>
<color name="md_purple_900">#4A148C</color>
<color name="md_purple_A100">#EA80FC</color>
<color name="md_purple_A200">#E040FB</color>
<color name="md_purple_A400">#D500F9</color>
<color name="md_purple_A700">#AA00FF</color>
<!-- Deep Purple -->
<color name="md_deep_purple_50">#EDE7F6</color>
<color name="md_deep_purple_100">#D1C4E9</color>
<color name="md_deep_purple_200">#B39DDB</color>
<color name="md_deep_purple_300">#9575CD</color>
<color name="md_deep_purple_400">#7E57C2</color>
<color name="md_deep_purple_500">#673AB7</color>
<color name="md_deep_purple_600">#5E35B1</color>
<color name="md_deep_purple_700">#512DA8</color>
<color name="md_deep_purple_800">#4527A0</color>
<color name="md_deep_purple_900">#311B92</color>
<color name="md_deep_purple_A100">#B388FF</color>
<color name="md_deep_purple_A200">#7C4DFF</color>
<color name="md_deep_purple_A400">#651FFF</color>
<color name="md_deep_purple_A700">#6200EA</color>
<!-- Indigo -->
<color name="md_indigo_50">#E8EAF6</color>
<color name="md_indigo_100">#C5CAE9</color>
<color name="md_indigo_200">#9FA8DA</color>
<color name="md_indigo_300">#7986CB</color>
<color name="md_indigo_400">#5C6BC0</color>
<color name="md_indigo_500">#3F51B5</color>
<color name="md_indigo_600">#3949AB</color>
<color name="md_indigo_700">#303F9F</color>
<color name="md_indigo_800">#283593</color>
<color name="md_indigo_900">#1A237E</color>
<color name="md_indigo_A100">#8C9EFF</color>
<color name="md_indigo_A200">#536DFE</color>
<color name="md_indigo_A400">#3D5AFE</color>
<color name="md_indigo_A700">#304FFE</color>
<!-- Blue -->
<color name="md_blue_50">#E3F2FD</color>
<color name="md_blue_100">#BBDEFB</color>
<color name="md_blue_200">#90CAF9</color>
<color name="md_blue_300">#64B5F6</color>
<color name="md_blue_400">#42A5F5</color>
<color name="md_blue_500">#2196F3</color>
<color name="md_blue_600">#1E88E5</color>
<color name="md_blue_700">#1976D2</color>
<color name="md_blue_800">#1565C0</color>
<color name="md_blue_900">#0D47A1</color>
<color name="md_blue_A100">#82B1FF</color>
<color name="md_blue_A200">#448AFF</color>
<color name="md_blue_A400">#2979FF</color>
<color name="md_blue_A700">#2962FF</color>
<!-- Light Blue -->
<color name="md_light_blue_50">#E1F5FE</color>
<color name="md_light_blue_100">#B3E5FC</color>
<color name="md_light_blue_200">#81D4FA</color>
<color name="md_light_blue_300">#4FC3F7</color>
<color name="md_light_blue_400">#29B6F6</color>
<color name="md_light_blue_500">#03A9F4</color>
<color name="md_light_blue_600">#039BE5</color>
<color name="md_light_blue_700">#0288D1</color>
<color name="md_light_blue_800">#0277BD</color>
<color name="md_light_blue_900">#01579B</color>
<color name="md_light_blue_A100">#80D8FF</color>
<color name="md_light_blue_A200">#40C4FF</color>
<color name="md_light_blue_A400">#00B0FF</color>
<color name="md_light_blue_A700">#0091EA</color>
<!-- Cyan -->
<color name="md_cyan_50">#E0F7FA</color>
<color name="md_cyan_100">#B2EBF2</color>
<color name="md_cyan_200">#80DEEA</color>
<color name="md_cyan_300">#4DD0E1</color>
<color name="md_cyan_400">#26C6DA</color>
<color name="md_cyan_500">#00BCD4</color>
<color name="md_cyan_600">#00ACC1</color>
<color name="md_cyan_700">#0097A7</color>
<color name="md_cyan_800">#00838F</color>
<color name="md_cyan_900">#006064</color>
<color name="md_cyan_A100">#84FFFF</color>
<color name="md_cyan_A200">#18FFFF</color>
<color name="md_cyan_A400">#00E5FF</color>
<color name="md_cyan_A700">#00B8D4</color>
<!-- Teal -->
<color name="md_teal_50">#E0F2F1</color>
<color name="md_teal_100">#B2DFDB</color>
<color name="md_teal_200">#80CBC4</color>
<color name="md_teal_300">#4DB6AC</color>
<color name="md_teal_400">#26A69A</color>
<color name="md_teal_500">#009688</color>
<color name="md_teal_600">#00897B</color>
<color name="md_teal_700">#00796B</color>
<color name="md_teal_800">#00695C</color>
<color name="md_teal_900">#004D40</color>
<color name="md_teal_A100">#A7FFEB</color>
<color name="md_teal_A200">#64FFDA</color>
<color name="md_teal_A400">#1DE9B6</color>
<color name="md_teal_A700">#00BFA5</color>
<!-- Green -->
<color name="md_green_50">#E8F5E9</color>
<color name="md_green_100">#C8E6C9</color>
<color name="md_green_200">#A5D6A7</color>
<color name="md_green_300">#81C784</color>
<color name="md_green_400">#66BB6A</color>
<color name="md_green_500">#4CAF50</color>
<color name="md_green_600">#43A047</color>
<color name="md_green_700">#388E3C</color>
<color name="md_green_800">#2E7D32</color>
<color name="md_green_900">#1B5E20</color>
<color name="md_green_A100">#B9F6CA</color>
<color name="md_green_A200">#69F0AE</color>
<color name="md_green_A400">#00E676</color>
<color name="md_green_A700">#00C853</color>
<!-- Light Green -->
<color name="md_light_green_50">#F1F8E9</color>
<color name="md_light_green_100">#DCEDC8</color>
<color name="md_light_green_200">#C5E1A5</color>
<color name="md_light_green_300">#AED581</color>
<color name="md_light_green_400">#9CCC65</color>
<color name="md_light_green_500">#8BC34A</color>
<color name="md_light_green_600">#7CB342</color>
<color name="md_light_green_700">#689F38</color>
<color name="md_light_green_800">#558B2F</color>
<color name="md_light_green_900">#33691E</color>
<color name="md_light_green_A100">#CCFF90</color>
<color name="md_light_green_A200">#B2FF59</color>
<color name="md_light_green_A400">#76FF03</color>
<color name="md_light_green_A700">#64DD17</color>
<!-- Lime -->
<color name="md_lime_50">#F9FBE7</color>
<color name="md_lime_100">#F0F4C3</color>
<color name="md_lime_200">#E6EE9C</color>
<color name="md_lime_300">#DCE775</color>
<color name="md_lime_400">#D4E157</color>
<color name="md_lime_500">#CDDC39</color>
<color name="md_lime_600">#C0CA33</color>
<color name="md_lime_700">#AFB42B</color>
<color name="md_lime_800">#9E9D24</color>
<color name="md_lime_900">#827717</color>
<color name="md_lime_A100">#F4FF81</color>
<color name="md_lime_A200">#EEFF41</color>
<color name="md_lime_A400">#C6FF00</color>
<color name="md_lime_A700">#AEEA00</color>
<!-- Yellow -->
<color name="md_yellow_50">#FFFDE7</color>
<color name="md_yellow_100">#FFF9C4</color>
<color name="md_yellow_200">#FFF59D</color>
<color name="md_yellow_300">#FFF176</color>
<color name="md_yellow_400">#FFEE58</color>
<color name="md_yellow_500">#FFEB3B</color>
<color name="md_yellow_600">#FDD835</color>
<color name="md_yellow_700">#FBC02D</color>
<color name="md_yellow_800">#F9A825</color>
<color name="md_yellow_900">#F57F17</color>
<color name="md_yellow_A100">#FFFF8D</color>
<color name="md_yellow_A200">#FFFF00</color>
<color name="md_yellow_A400">#FFEA00</color>
<color name="md_yellow_A700">#FFD600</color>
<!-- Amber -->
<color name="md_amber_50">#FFF8E1</color>
<color name="md_amber_100">#FFECB3</color>
<color name="md_amber_200">#FFE082</color>
<color name="md_amber_300">#FFD54F</color>
<color name="md_amber_400">#FFCA28</color>
<color name="md_amber_500">#FFC107</color>
<color name="md_amber_600">#FFB300</color>
<color name="md_amber_700">#FFA000</color>
<color name="md_amber_800">#FF8F00</color>
<color name="md_amber_900">#FF6F00</color>
<color name="md_amber_A100">#FFE57F</color>
<color name="md_amber_A200">#FFD740</color>
<color name="md_amber_A400">#FFC400</color>
<color name="md_amber_A700">#FFAB00</color>
<!-- Orange -->
<color name="md_orange_50">#FFF3E0</color>
<color name="md_orange_100">#FFE0B2</color>
<color name="md_orange_200">#FFCC80</color>
<color name="md_orange_300">#FFB74D</color>
<color name="md_orange_400">#FFA726</color>
<color name="md_orange_500">#FF9800</color>
<color name="md_orange_600">#FB8C00</color>
<color name="md_orange_700">#F57C00</color>
<color name="md_orange_800">#EF6C00</color>
<color name="md_orange_900">#E65100</color>
<color name="md_orange_A100">#FFD180</color>
<color name="md_orange_A200">#FFAB40</color>
<color name="md_orange_A400">#FF9100</color>
<color name="md_orange_A700">#FF6D00</color>
<!-- Deep Orange -->
<color name="md_deep_orange_50">#FBE9E7</color>
<color name="md_deep_orange_100">#FFCCBC</color>
<color name="md_deep_orange_200">#FFAB91</color>
<color name="md_deep_orange_300">#FF8A65</color>
<color name="md_deep_orange_400">#FF7043</color>
<color name="md_deep_orange_500">#FF5722</color>
<color name="md_deep_orange_600">#F4511E</color>
<color name="md_deep_orange_700">#E64A19</color>
<color name="md_deep_orange_800">#D84315</color>
<color name="md_deep_orange_900">#BF360C</color>
<color name="md_deep_orange_A100">#FF9E80</color>
<color name="md_deep_orange_A200">#FF6E40</color>
<color name="md_deep_orange_A400">#FF3D00</color>
<color name="md_deep_orange_A700">#DD2C00</color>
<!-- Brown -->
<color name="md_brown_50">#EFEBE9</color>
<color name="md_brown_100">#D7CCC8</color>
<color name="md_brown_200">#BCAAA4</color>
<color name="md_brown_300">#A1887F</color>
<color name="md_brown_400">#8D6E63</color>
<color name="md_brown_500">#795548</color>
<color name="md_brown_600">#6D4C41</color>
<color name="md_brown_700">#5D4037</color>
<color name="md_brown_800">#4E342E</color>
<color name="md_brown_900">#3E2723</color>
<!-- Grey -->
<color name="md_grey_50">#FAFAFA</color>
<color name="md_grey_100">#F5F5F5</color>
<color name="md_grey_200">#EEEEEE</color>
<color name="md_grey_300">#E0E0E0</color>
<color name="md_grey_400">#BDBDBD</color>
<color name="md_grey_500">#9E9E9E</color>
<color name="md_grey_600">#757575</color>
<color name="md_grey_700">#616161</color>
<color name="md_grey_800">#424242</color>
<color name="md_grey_850">#303030</color>
<color name="md_grey_900">#212121</color>
<!-- Blue Grey -->
<color name="md_blue_grey_50">#ECEFF1</color>
<color name="md_blue_grey_100">#CFD8DC</color>
<color name="md_blue_grey_200">#B0BEC5</color>
<color name="md_blue_grey_300">#90A4AE</color>
<color name="md_blue_grey_400">#78909C</color>
<color name="md_blue_grey_500">#607D8B</color>
<color name="md_blue_grey_600">#546E7A</color>
<color name="md_blue_grey_700">#455A64</color>
<color name="md_blue_grey_800">#37474F</color>
<color name="md_blue_grey_900">#263238</color>
<!-- Black & White -->
<color name="md_black_1000">#000000</color>
<color name="md_white_1000">#FFFFFF</color>
</resources>

@ -30,4 +30,5 @@
<string name="delete">删除</string>
<string name="replace">净化替换</string>
<string name="not_available">暂无</string>
<string name="enable">启用</string>
</resources>

Loading…
Cancel
Save