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. 2
      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. 78
      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() throw new JsonPathException("length operation can not applied to " + (obj != null ? obj.getClass().getName()
: "null"); : "null"));
} }
@Override @Override

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

@ -3,6 +3,8 @@ package io.legado.app.data.dao
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.paging.DataSource import androidx.paging.DataSource
import androidx.room.Dao import androidx.room.Dao
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query import androidx.room.Query
import io.legado.app.data.entities.Book import io.legado.app.data.entities.Book
@ -19,4 +21,13 @@ interface BookDao {
@Query("SELECT * FROM books WHERE `name` in (:names)") @Query("SELECT * FROM books WHERE `name` in (:names)")
fun findByName(vararg names: String): List<Book> 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) @Insert(onConflict = OnConflictStrategy.REPLACE)
fun insert(replaceRule: ReplaceRule): Long fun insert(replaceRule: ReplaceRule): Long
@Update @Update
fun update(vararg replaceRules: ReplaceRule) 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()
}

@ -34,6 +34,8 @@ data class Book(@PrimaryKey
var durChapterPos: Int = 0, // 当前阅读的进度(首行字符的索引位置) var durChapterPos: Int = 0, // 当前阅读的进度(首行字符的索引位置)
var durChapterTime: Long = 0, // 最近一次阅读书籍的时间(打开正文的时间) var durChapterTime: Long = 0, // 最近一次阅读书籍的时间(打开正文的时间)
var canUpdate: Boolean = true, // 刷新书架时更新书籍信息 var canUpdate: Boolean = true, // 刷新书架时更新书籍信息
var order: Int = 0, // 手动排序
var useReplaceRule: Boolean = true, // 正文使用净化替换规则
var variable: String? = null // 自定义书籍变量信息(用于书源规则检索书籍信息) var variable: String? = null // 自定义书籍变量信息(用于书源规则检索书籍信息)
) : Parcelable { ) : 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.R
import io.legado.app.base.BaseActivity import io.legado.app.base.BaseActivity
import io.legado.app.constant.AppConst.APP_TAG 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.data.entities.ReplaceRule
import io.legado.app.help.permission.Permissions import io.legado.app.help.permission.Permissions
import io.legado.app.help.permission.PermissionsCompat import io.legado.app.help.permission.PermissionsCompat
@ -107,30 +108,85 @@ class MainActivity : BaseActivity<MainDataBinding, MainViewModel>(), NavigationV
.build() .build()
) )
// 导入书架
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 // Replace rules
val rFile = File(yuedu, "myBookReplaceRule.json") val ruleFile = File(yuedu, "myBookReplaceRule.json")
val replaceRules = mutableListOf<ReplaceRule>() val replaceRules = mutableListOf<ReplaceRule>()
if (rFile.exists()) try { if (ruleFile.exists()) try {
val items: List<Map<String, Any>> = jsonPath.parse(rFile.readText()).read("$.*") 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) { for (item in items) {
val jsonItem = jsonPath.parse(item) val jsonItem = jsonPath.parse(item)
val rRule = ReplaceRule() val rRule = ReplaceRule()
rRule.name = jsonItem.readString("$.replaceSummary")
rRule.pattern = jsonItem.readString("$.regex") 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.replacement = jsonItem.readString("$.replacement")
rRule.isRegex = jsonItem.readBool("$.isRegex") rRule.isRegex = jsonItem.readBool("$.isRegex") == true
rRule.scope = jsonItem.readString("$.useTo") rRule.scope = jsonItem.readString("$.useTo")
rRule.isEnabled = jsonItem.readBool("$.enable") rRule.isEnabled = jsonItem.readBool("$.enable") == true
rRule.order = jsonItem.readInt("$.serialNumber") rRule.order = jsonItem.readInt("$.serialNumber") ?: 0
replaceRules.add(rRule) replaceRules.add(rRule)
} }
doAsync {
App.db.replaceRuleDao().insert(*replaceRules.toTypedArray()) App.db.replaceRuleDao().insert(*replaceRules.toTypedArray())
val count = items.size val count = replaceRules.size
val maxId = App.db.replaceRuleDao().maxOrder val maxId = App.db.replaceRuleDao().maxOrder
uiThread { uiThread {
toast("成功地导入 $count 条净化替换规则") toast(if (count > 0) "成功地导入 $count净化替换规则" else "没有发现新的净化替换规则")
} }
} }

@ -1,11 +1,14 @@
package io.legado.app.ui.replacerule package io.legado.app.ui.replacerule
import android.os.Bundle import android.os.Bundle
import android.util.Log
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.lifecycle.Observer import androidx.lifecycle.Observer
import androidx.paging.LivePagedListBuilder import androidx.paging.LivePagedListBuilder
import androidx.paging.PagedList import androidx.paging.PagedList
import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import io.legado.app.App import io.legado.app.App
import io.legado.app.R 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 kotlinx.android.synthetic.main.activity_replace_rule.*
import org.jetbrains.anko.doAsync import org.jetbrains.anko.doAsync
import org.jetbrains.anko.toast 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() { class ReplaceRuleActivity : AppCompatActivity() {
@ -23,9 +30,9 @@ class ReplaceRuleActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(R.layout.activity_replace_rule) setContentView(R.layout.activity_replace_rule)
rv_replace_rule.layoutManager = LinearLayoutManager(this)
initRecyclerView() initRecyclerView()
initDataObservers() initDataObservers()
initSwipeToDelete()
} }
private fun initRecyclerView() { private fun initRecyclerView() {
@ -54,6 +61,12 @@ class ReplaceRuleActivity : AppCompatActivity() {
} }
} }
rv_replace_rule.adapter = adapter 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() { 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 package io.legado.app.ui.replacerule
import android.content.Context import android.content.Context
import android.util.Log
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
@ -9,6 +10,7 @@ import androidx.paging.PagedListAdapter
import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import io.legado.app.R import io.legado.app.R
import io.legado.app.constant.AppConst.APP_TAG
import io.legado.app.data.entities.ReplaceRule import io.legado.app.data.entities.ReplaceRule
import kotlinx.android.synthetic.main.item_relace_rule.view.* import kotlinx.android.synthetic.main.item_relace_rule.view.*
import org.jetbrains.anko.sdk27.listeners.onClick import org.jetbrains.anko.sdk27.listeners.onClick
@ -45,18 +47,20 @@ class ReplaceRuleAdapter(context: Context) :
} }
override fun onBindViewHolder(holder: MyViewHolder, pos: Int) { 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) { class MyViewHolder(view: View) : RecyclerView.ViewHolder(view) {
fun bind(rule: ReplaceRule, listener: OnClickListener?, hideDivider: Boolean) = with(itemView) { fun bind(rule: ReplaceRule, listener: OnClickListener?) = with(itemView) {
cb_enable.text = rule.name tv_name.text = rule.name
cb_enable.isChecked = rule.isEnabled swt_enabled.isChecked = rule.isEnabled
divider.isGone = hideDivider // divider.isGone = hideDivider
iv_delete.onClick { listener?.delete(rule) } iv_delete.isGone = true
iv_edit.onClick { listener?.edit(rule) } iv_edit.isGone = true
cb_enable.onClick { // iv_delete.onClick { listener?.delete(rule) }
rule.isEnabled = cb_enable.isChecked // iv_edit.onClick { listener?.edit(rule) }
swt_enabled.onClick {
rule.isEnabled = swt_enabled.isChecked
listener?.update(rule) listener?.update(rule)
} }
} }

@ -2,8 +2,11 @@ package io.legado.app.utils
import com.jayway.jsonpath.ReadContext 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:id="@+id/rv_replace_rule"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:paddingStart="8dp" android:paddingStart="16dp"
android:paddingEnd="8dp" android:paddingEnd="16dp"
android:layout_marginBottom="8dp" android:layout_marginBottom="8dp"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"

@ -2,24 +2,9 @@
<androidx.constraintlayout.widget.ConstraintLayout <androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" 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" />
<androidx.appcompat.widget.AppCompatImageView <androidx.appcompat.widget.AppCompatImageView
android:id="@+id/iv_edit" android:id="@+id/iv_edit"
@ -29,6 +14,7 @@
android:background="@drawable/bg_ib_pre_round" android:background="@drawable/bg_ib_pre_round"
android:contentDescription="@string/edit" android:contentDescription="@string/edit"
android:src="@drawable/ic_edit" android:src="@drawable/ic_edit"
android:visibility="gone"
app:tint="@color/text_default" app:tint="@color/text_default"
app:layout_constraintEnd_toStartOf="@id/iv_delete" app:layout_constraintEnd_toStartOf="@id/iv_delete"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
@ -42,16 +28,61 @@
android:background="@drawable/bg_ib_pre_round" android:background="@drawable/bg_ib_pre_round"
android:contentDescription="@string/delete" android:contentDescription="@string/delete"
android:src="@drawable/ic_clear_all" android:src="@drawable/ic_clear_all"
android:visibility="gone"
app:tint="@color/text_default" app:tint="@color/text_default"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toTopOf="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 <View
android:id="@+id/divider" android:id="@+id/divider"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="0.5dp" android:layout_height="0.5dp"
android:background="@color/divider" android:background="@color/divider"
app:layout_constraintBottom_toBottomOf="parent"/> app:layout_constraintBottom_toBottomOf="parent"/>
-->
</androidx.constraintlayout.widget.ConstraintLayout> </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="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> </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="delete">删除</string>
<string name="replace">净化替换</string> <string name="replace">净化替换</string>
<string name="not_available">暂无</string> <string name="not_available">暂无</string>
<string name="enable">启用</string>
</resources> </resources>

Loading…
Cancel
Save