主题导入导出

pull/363/head
gedoor 4 years ago
parent c0be0dba20
commit bc88696004
  1. 46
      app/src/main/assets/themeConfig.json
  2. 4
      app/src/main/assets/updateLog.md
  3. 7
      app/src/main/java/io/legado/app/help/ReadBookConfig.kt
  4. 93
      app/src/main/java/io/legado/app/help/ThemeConfig.kt
  5. 4
      app/src/main/java/io/legado/app/help/storage/Backup.kt
  6. 19
      app/src/main/java/io/legado/app/help/storage/Restore.kt
  7. 37
      app/src/main/java/io/legado/app/ui/config/ThemeConfigFragment.kt
  8. 98
      app/src/main/java/io/legado/app/ui/config/ThemeListDialog.kt
  9. 42
      app/src/main/res/layout/item_theme_config.xml

@ -1,6 +1,6 @@
[
{
"configName": "典雅蓝",
"themeName": "典雅蓝",
"isNightTheme": false,
"primaryColor": "#03A9F4",
"accentColor": "#AD1457",
@ -8,43 +8,19 @@
"bottomBackground": "#EEEEEE"
},
{
"configName": "极简",
"themeName": "黑白",
"isNightTheme": false,
"primaryColor": "#03A9F4",
"accentColor": "#AD1457",
"backgroundColor": "#F5F5F5",
"bottomBackground": "#EEEEEE"
"primaryColor": "#303030",
"accentColor": "#E0E0E0",
"backgroundColor": "#424242",
"bottomBackground": "#424242"
},
{
"configName": "黑",
"themeName": "A屏黑",
"isNightTheme": false,
"primaryColor": "#03A9F4",
"accentColor": "#AD1457",
"backgroundColor": "#F5F5F5",
"bottomBackground": "#EEEEEE"
},
{
"configName": "曜夜",
"isNightTheme": false,
"primaryColor": "#03A9F4",
"accentColor": "#AD1457",
"backgroundColor": "#F5F5F5",
"bottomBackground": "#EEEEEE"
},
{
"configName": "黑白",
"isNightTheme": false,
"primaryColor": "#03A9F4",
"accentColor": "#AD1457",
"backgroundColor": "#F5F5F5",
"bottomBackground": "#EEEEEE"
},
{
"configName": "A屏黑",
"isNightTheme": false,
"primaryColor": "#03A9F4",
"accentColor": "#AD1457",
"backgroundColor": "#F5F5F5",
"bottomBackground": "#EEEEEE"
"primaryColor": "#000000",
"accentColor": "#FFFFFF",
"backgroundColor": "#000000",
"bottomBackground": "#000000"
}
]

@ -3,6 +3,10 @@
* 关注合作公众号 **[小说拾遗]()** 获取好看的小说。
- 旧版数据导入教程:先在旧版阅读(2.x)中进行备份,然后在新版阅读(3.x)【我的】->【备份与恢复】,选择【导入旧版本数据】。
**2020/09/08**
* 页眉页脚跟随背景
* 主题导入导出
**2020/09/07**
* 订阅源和替换规则添加滑动选择
* 修复排版配置导入导出

@ -20,11 +20,11 @@ import java.io.File
*/
@Keep
object ReadBookConfig {
const val readConfigFileName = "readConfig.json"
private val configFilePath = FileUtils.getPath(App.INSTANCE.filesDir, readConfigFileName)
const val configFileName = "readConfig.json"
val configFilePath = FileUtils.getPath(App.INSTANCE.filesDir, configFileName)
val configList: ArrayList<Config> = arrayListOf()
private val defaultConfigs by lazy {
val json = String(App.INSTANCE.assets.open(readConfigFileName).readBytes())
val json = String(App.INSTANCE.assets.open(configFileName).readBytes())
GSON.fromJsonArray<Config>(json)!!
}
var durConfig
@ -95,6 +95,7 @@ object ReadBookConfig {
Coroutine.async {
synchronized(this) {
val json = GSON.toJson(configList)
FileUtils.deleteFile(configFilePath)
FileUtils.createFileIfNotExist(configFilePath).writeText(json)
}
}

@ -6,17 +6,62 @@ import io.legado.app.App
import io.legado.app.R
import io.legado.app.constant.EventBus
import io.legado.app.constant.PreferKey
import io.legado.app.help.coroutine.Coroutine
import io.legado.app.utils.*
import java.io.File
object ThemeConfig {
val configFileName = "themeConfigs.json"
private val configFilePath = FileUtils.getPath(App.INSTANCE.filesDir, configFileName)
val configList = arrayListOf<Config>()
const val configFileName = "themeConfig.json"
val configFilePath = FileUtils.getPath(App.INSTANCE.filesDir, configFileName)
private val defaultConfigs by lazy {
val json = String(App.INSTANCE.assets.open(configFileName).readBytes())
GSON.fromJsonArray<Config>(json)!!
}
val configList = arrayListOf<Config>()
init {
upConfig()
}
fun upConfig() {
(getConfigs() ?: defaultConfigs).let {
configList.clear()
configList.addAll(it)
}
}
fun save() {
Coroutine.async {
synchronized(this) {
val json = GSON.toJson(configList)
FileUtils.deleteFile(configFilePath)
FileUtils.createFileIfNotExist(configFilePath).writeText(json)
}
}
}
fun addConfig(newConfig: Config) {
configList.forEachIndexed { index, config ->
if (newConfig.themeName == config.themeName) {
configList[index] = newConfig
return
}
}
configList.add(newConfig)
}
private fun getConfigs(): List<Config>? {
val configFile = File(configFilePath)
if (configFile.exists()) {
try {
val json = configFile.readText()
return GSON.fromJsonArray(json)
} catch (e: Exception) {
e.printStackTrace()
}
}
return null
}
fun applyConfig(context: Context, config: Config) {
val primary = Color.parseColor(config.primaryColor)
@ -42,8 +87,48 @@ object ThemeConfig {
postEvent(EventBus.RECREATE, "")
}
fun saveDayTheme(context: Context, name: String) {
val primary =
context.getPrefInt(PreferKey.cPrimary, context.getCompatColor(R.color.md_brown_500))
val accent =
context.getPrefInt(PreferKey.cAccent, context.getCompatColor(R.color.md_red_600))
val background =
context.getPrefInt(PreferKey.cBackground, context.getCompatColor(R.color.md_grey_100))
val bBackground =
context.getPrefInt(PreferKey.cBBackground, context.getCompatColor(R.color.md_grey_200))
val config = Config(
themeName = name,
isNightTheme = false,
primaryColor = "#${primary.hexString}",
accentColor = "#${accent.hexString}",
backgroundColor = "#${background.hexString}",
bottomBackground = "#${bBackground.hexString}"
)
addConfig(config)
}
fun saveNightTheme(context: Context, name: String) {
val primary =
context.getPrefInt(PreferKey.cNPrimary, context.getCompatColor(R.color.md_blue_grey_600))
val accent =
context.getPrefInt(PreferKey.cNAccent, context.getCompatColor(R.color.md_deep_orange_800))
val background =
context.getPrefInt(PreferKey.cNBackground, context.getCompatColor(R.color.md_grey_900))
val bBackground =
context.getPrefInt(PreferKey.cNBBackground, context.getCompatColor(R.color.md_grey_850))
val config = Config(
themeName = name,
isNightTheme = true,
primaryColor = "#${primary.hexString}",
accentColor = "#${accent.hexString}",
backgroundColor = "#${background.hexString}",
bottomBackground = "#${bBackground.hexString}"
)
addConfig(config)
}
class Config(
var configName: String = "典雅蓝",
var themeName: String = "典雅蓝",
var isNightTheme: Boolean = false,
var primaryColor: String = "#03A9F4",
var accentColor: String = "#AD1457",

@ -33,7 +33,7 @@ object Backup {
"txtTocRule.json",
"readRecord.json",
"httpTTS.json",
ReadBookConfig.readConfigFileName,
ReadBookConfig.configFileName,
"config.xml"
)
}
@ -62,7 +62,7 @@ object Backup {
writeListToJson(App.db.readRecordDao().all, "readRecord.json", backupPath)
writeListToJson(App.db.httpTTSDao().all, "httpTTS.json", backupPath)
GSON.toJson(ReadBookConfig.configList)?.let {
FileUtils.createFileIfNotExist(backupPath + File.separator + ReadBookConfig.readConfigFileName)
FileUtils.createFileIfNotExist(backupPath + File.separator + ReadBookConfig.configFileName)
.writeText(it)
}
Preferences.getSharedPreferences(App.INSTANCE, backupPath, "config")?.let { sp ->

@ -16,6 +16,7 @@ import io.legado.app.data.entities.*
import io.legado.app.help.AppConfig
import io.legado.app.help.LauncherIconHelp
import io.legado.app.help.ReadBookConfig
import io.legado.app.help.ThemeConfig
import io.legado.app.service.help.ReadBook
import io.legado.app.ui.book.read.page.provider.ChapterProvider
import io.legado.app.utils.*
@ -158,14 +159,24 @@ object Restore {
suspend fun restoreConfig(path: String = Backup.backupPath) {
withContext(IO) {
try {
val file =
FileUtils.createFileIfNotExist(path + File.separator + ThemeConfig.configFileName)
if (file.exists()) {
FileUtils.deleteFile(ThemeConfig.configFilePath)
file.copyTo(File(ThemeConfig.configFilePath))
ThemeConfig.upConfig()
}
} catch (e: Exception) {
e.printStackTrace()
}
if (!ignoreReadConfig) {
try {
val file =
FileUtils.createFileIfNotExist(path + File.separator + ReadBookConfig.readConfigFileName)
val configFile =
FileUtils.getFile(App.INSTANCE.filesDir, ReadBookConfig.readConfigFileName)
FileUtils.createFileIfNotExist(path + File.separator + ReadBookConfig.configFileName)
if (file.exists()) {
file.copyTo(configFile, true)
FileUtils.deleteFile(ReadBookConfig.configFilePath)
file.copyTo(File(ReadBookConfig.configFilePath))
ReadBookConfig.upConfig()
}
} catch (e: Exception) {

@ -16,11 +16,18 @@ import io.legado.app.constant.EventBus
import io.legado.app.constant.PreferKey
import io.legado.app.help.AppConfig
import io.legado.app.help.LauncherIconHelp
import io.legado.app.help.ThemeConfig
import io.legado.app.lib.dialogs.alert
import io.legado.app.lib.dialogs.customView
import io.legado.app.lib.dialogs.noButton
import io.legado.app.lib.dialogs.okButton
import io.legado.app.lib.theme.ATH
import io.legado.app.ui.widget.number.NumberPickerDialog
import io.legado.app.ui.widget.prefs.ColorPreference
import io.legado.app.ui.widget.prefs.IconListPreference
import io.legado.app.ui.widget.text.AutoCompleteTextView
import io.legado.app.utils.*
import kotlinx.android.synthetic.main.dialog_edit_text.view.*
@Suppress("SameParameterValue")
@ -147,7 +154,7 @@ class ThemeConfigFragment : BasePreferenceFragment(),
@SuppressLint("PrivateResource")
override fun onPreferenceTreeClick(preference: Preference?): Boolean {
when (preference?.key) {
when (val key = preference?.key) {
PreferKey.barElevation -> NumberPickerDialog(requireContext())
.setTitle(getString(R.string.bar_elevation))
.setMaxValue(32)
@ -162,17 +169,35 @@ class ThemeConfigFragment : BasePreferenceFragment(),
AppConfig.elevation = it
recreateActivities()
}
"themeList" -> themeListAlert()
"themeList" -> ThemeListDialog().show(childFragmentManager, "themeList")
"saveDayTheme", "saveNightTheme" -> saveThemeAlert(key)
}
return super.onPreferenceTreeClick(preference)
}
@SuppressLint("InflateParams")
private fun saveThemeAlert(key: String) {
alert("主题名称") {
var editText: AutoCompleteTextView? = null
customView {
layoutInflater.inflate(R.layout.dialog_edit_text, null).apply {
editText = edit_view
}
}
okButton {
editText?.text?.toString()?.let { themeName ->
when (key) {
"saveDayTheme" -> {
ThemeConfig.saveDayTheme(requireContext(), themeName)
}
"saveNightTheme" -> {
ThemeConfig.saveNightTheme(requireContext(), themeName)
}
}
return super.onPreferenceTreeClick(preference)
}
private fun themeListAlert() {
}
noButton { }
}.show().applyTint()
}
private fun upTheme(isNightTheme: Boolean) {

@ -0,0 +1,98 @@
package io.legado.app.ui.config
import android.os.Bundle
import android.util.DisplayMetrics
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.LinearLayoutManager
import io.legado.app.R
import io.legado.app.base.BaseDialogFragment
import io.legado.app.base.adapter.ItemViewHolder
import io.legado.app.base.adapter.SimpleRecyclerAdapter
import io.legado.app.help.ThemeConfig
import io.legado.app.lib.dialogs.alert
import io.legado.app.lib.dialogs.noButton
import io.legado.app.lib.dialogs.okButton
import io.legado.app.lib.theme.primaryColor
import io.legado.app.ui.widget.recycler.VerticalDivider
import io.legado.app.utils.GSON
import io.legado.app.utils.applyTint
import kotlinx.android.synthetic.main.dialog_recycler_view.*
import kotlinx.android.synthetic.main.item_theme_config.view.*
import org.jetbrains.anko.sdk27.listeners.onClick
import org.jetbrains.anko.share
class ThemeListDialog : BaseDialogFragment() {
private lateinit var adapter: Adapter
override fun onStart() {
super.onStart()
val dm = DisplayMetrics()
activity?.windowManager?.defaultDisplay?.getMetrics(dm)
dialog?.window?.setLayout((dm.widthPixels * 0.9).toInt(), (dm.heightPixels * 0.9).toInt())
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.dialog_recycler_view, container)
}
override fun onFragmentCreated(view: View, savedInstanceState: Bundle?) {
tool_bar.setBackgroundColor(primaryColor)
tool_bar.setTitle(R.string.theme_list)
initView()
initData()
}
private fun initView() {
adapter = Adapter()
recycler_view.layoutManager = LinearLayoutManager(requireContext())
recycler_view.addItemDecoration(VerticalDivider(requireContext()))
recycler_view.adapter = adapter
}
fun initData() {
adapter.setItems(ThemeConfig.configList)
}
fun delete(index: Int) {
alert(R.string.delete, R.string.sure_del) {
okButton {
ThemeConfig.configList.removeAt(index)
initData()
}
noButton()
}.show().applyTint()
}
fun share(index: Int) {
val json = GSON.toJson(ThemeConfig.configList[index])
requireContext().share(json, "主题分享")
}
inner class Adapter : SimpleRecyclerAdapter<ThemeConfig.Config>(requireContext(), R.layout.item_theme_config) {
override fun convert(holder: ItemViewHolder, item: ThemeConfig.Config, payloads: MutableList<Any>) {
holder.itemView.apply {
tv_name.text = item.themeName
}
}
override fun registerListener(holder: ItemViewHolder) {
holder.itemView.apply {
iv_share.onClick {
share(holder.layoutPosition)
}
iv_delete.onClick {
delete(holder.layoutPosition)
}
}
}
}
}

@ -0,0 +1,42 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="8dp"
android:gravity="center_vertical"
android:orientation="horizontal">
<TextView
android:id="@+id/tv_name"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:maxLines="1" />
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/iv_share"
android:layout_width="36dp"
android:layout_height="36dp"
android:layout_gravity="center"
android:background="?android:attr/selectableItemBackgroundBorderless"
android:contentDescription="@string/edit"
android:padding="6dp"
android:tooltipText="@string/edit"
android:src="@drawable/ic_share"
android:tint="@color/primaryText"
tools:ignore="UnusedAttribute" />
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/iv_delete"
android:layout_width="36dp"
android:layout_height="36dp"
android:background="?attr/selectableItemBackgroundBorderless"
android:contentDescription="@string/more_menu"
android:tooltipText="@string/more_menu"
android:padding="6dp"
android:src="@drawable/ic_clear_all"
android:tint="@color/primaryText"
tools:ignore="UnusedAttribute" />
</LinearLayout>
Loading…
Cancel
Save