Merge branch 'master' into master

This commit is contained in:
Xwite
2022-05-13 07:09:43 +08:00
committed by GitHub
49 changed files with 435 additions and 351 deletions
@@ -7,6 +7,7 @@ import androidx.room.Ignore
import androidx.room.Index
import com.github.liuyueyi.quick.transfer.ChineseUtils
import io.legado.app.R
import io.legado.app.constant.AppLog
import io.legado.app.constant.AppPattern
import io.legado.app.data.appDb
import io.legado.app.exception.RegexTimeoutException
@@ -15,6 +16,7 @@ import io.legado.app.help.config.AppConfig
import io.legado.app.model.analyzeRule.AnalyzeUrl
import io.legado.app.model.analyzeRule.RuleDataInterface
import io.legado.app.utils.*
import kotlinx.coroutines.CancellationException
import kotlinx.parcelize.IgnoredOnParcel
import kotlinx.parcelize.Parcelize
import splitties.init.appCtx
@@ -93,7 +95,7 @@ data class BookChapter(
2 -> displayTitle = ChineseUtils.s2t(displayTitle)
}
}
if (useReplace && replaceRules != null) {
if (useReplace && replaceRules != null) kotlin.run {
replaceRules.forEach { item ->
if (item.pattern.isNotEmpty()) {
try {
@@ -112,7 +114,10 @@ data class BookChapter(
} catch (e: RegexTimeoutException) {
item.isEnabled = false
appDb.replaceRuleDao.update(item)
} catch (e: CancellationException) {
return@run
} catch (e: Exception) {
AppLog.put("${item.name}替换出错\n替换内容\n${displayTitle}", e)
appCtx.toastOnUi("${item.name}替换出错")
}
}
@@ -12,6 +12,7 @@ import io.legado.app.help.config.ReadBookConfig
import io.legado.app.utils.msg
import io.legado.app.utils.replace
import io.legado.app.utils.toastOnUi
import kotlinx.coroutines.CancellationException
import splitties.init.appCtx
import java.lang.ref.WeakReference
import java.util.concurrent.CopyOnWriteArrayList
@@ -135,7 +136,7 @@ class ContentProcessor private constructor(
var mContent = content
getContentReplaceRules().forEach { item ->
if (item.pattern.isNotEmpty()) {
kotlin.runCatching {
try {
mContent = if (item.isRegex) {
mContent.replace(
item.pattern.toRegex(),
@@ -145,18 +146,15 @@ class ContentProcessor private constructor(
} else {
mContent.replace(item.pattern, item.replacement)
}
}.onFailure {
when (it) {
is RegexTimeoutException -> {
item.isEnabled = false
appDb.replaceRuleDao.update(item)
return item.name + it.msg
}
else -> {
AppLog.put("${item.name}替换出错\n${it.localizedMessage}", it)
appCtx.toastOnUi("${item.name}替换出错")
}
}
} catch (e: RegexTimeoutException) {
item.isEnabled = false
appDb.replaceRuleDao.update(item)
return item.name + e.msg
} catch (e: CancellationException) {
return mContent
} catch (e: Exception) {
AppLog.put("${item.name}替换出错\n替换内容\n${mContent}", e)
appCtx.toastOnUi("${item.name}替换出错")
}
}
}
@@ -1,43 +1,47 @@
package io.legado.app.help
import io.legado.app.data.entities.ReplaceRule
import io.legado.app.exception.NoStackTraceException
import io.legado.app.utils.*
object ReplaceAnalyzer {
fun jsonToReplaceRules(json: String): List<ReplaceRule> {
val replaceRules = mutableListOf<ReplaceRule>()
val items: List<Map<String, Any>> = jsonPath.parse(json).read("$")
for (item in items) {
val jsonItem = jsonPath.parse(item)
jsonToReplaceRule(jsonItem.jsonString())?.let {
if (it.isValid()) {
replaceRules.add(it)
fun jsonToReplaceRules(json: String): Result<MutableList<ReplaceRule>> {
return kotlin.runCatching {
val replaceRules = mutableListOf<ReplaceRule>()
val items: List<Map<String, Any>> = jsonPath.parse(json).read("$")
for (item in items) {
val jsonItem = jsonPath.parse(item)
jsonToReplaceRule(jsonItem.jsonString()).getOrThrow().let {
if (it.isValid()) {
replaceRules.add(it)
}
}
}
replaceRules
}
return replaceRules
}
private fun jsonToReplaceRule(json: String): ReplaceRule? {
val replaceRule: ReplaceRule? = GSON.fromJsonObject<ReplaceRule>(json.trim()).getOrNull()
runCatching {
fun jsonToReplaceRule(json: String): Result<ReplaceRule> {
return runCatching {
val replaceRule: ReplaceRule? =
GSON.fromJsonObject<ReplaceRule>(json.trim()).getOrNull()
if (replaceRule == null || replaceRule.pattern.isBlank()) {
val jsonItem = jsonPath.parse(json.trim())
val rule = ReplaceRule()
rule.id = jsonItem.readLong("$.id") ?: System.currentTimeMillis()
rule.pattern = jsonItem.readString("$.regex") ?: ""
if (rule.pattern.isEmpty()) return null
if (rule.pattern.isEmpty()) throw NoStackTraceException("格式不对")
rule.name = jsonItem.readString("$.replaceSummary") ?: ""
rule.replacement = jsonItem.readString("$.replacement") ?: ""
rule.isRegex = jsonItem.readBool("$.isRegex") == true
rule.scope = jsonItem.readString("$.useTo")
rule.isEnabled = jsonItem.readBool("$.enable") == true
rule.order = jsonItem.readInt("$.serialNumber") ?: 0
return rule
return@runCatching rule
}
return@runCatching replaceRule
}
return replaceRule
}
}
@@ -18,6 +18,7 @@ import org.apache.commons.text.StringEscapeUtils
import splitties.init.appCtx
import java.lang.ref.WeakReference
import kotlin.coroutines.resume
import kotlin.coroutines.resumeWithException
/**
* 后台webView
@@ -50,14 +51,14 @@ class BackstageWebView(
override fun onError(error: Throwable) {
if (!block.isCompleted)
block.cancel(error)
block.resumeWithException(error)
}
}
runOnUI {
try {
load()
} catch (error: Throwable) {
block.cancel(error)
block.resumeWithException(error)
}
}
}
@@ -5,8 +5,8 @@ package io.legado.app.help.http
import android.text.TextUtils
import io.legado.app.data.appDb
import io.legado.app.data.entities.Cookie
import io.legado.app.help.http.api.CookieManager
import io.legado.app.help.CacheManager
import io.legado.app.help.http.api.CookieManager
import io.legado.app.utils.NetworkUtils
object CookieStore : CookieManager {
@@ -44,7 +44,7 @@ object CookieStore : CookieManager {
CacheManager.getFromMemory("${domain}_cookie")?.let { return it }
val cookieBean = appDb.cookieDao.get(domain)
val cookie = cookieBean?.cookie ?: ""
CacheManager.putMemory(url, cookie ?: "")
CacheManager.putMemory(url, cookie)
return cookie
}
@@ -98,9 +98,12 @@ object ImportOldData {
}
private fun importOldReplaceRule(json: String): Int {
val rules = ReplaceAnalyzer.jsonToReplaceRules(json)
appDb.replaceRuleDao.insert(*rules.toTypedArray())
return rules.size
val rules = ReplaceAnalyzer.jsonToReplaceRules(json).getOrNull()
rules?.let {
appDb.replaceRuleDao.insert(*rules.toTypedArray())
return rules.size
}
return 0
}
private fun fromOldBooks(json: String): List<Book> {
@@ -1,4 +1,4 @@
package io.legado.app.ui.widget.prefs
package io.legado.app.lib.prefs
import android.content.Context
import android.content.ContextWrapper
@@ -118,7 +118,7 @@ class ColorPreference(context: Context, attrs: AttributeSet) : Preference(contex
}
override fun onBindViewHolder(holder: PreferenceViewHolder) {
val v = io.legado.app.ui.widget.prefs.Preference.bindView<ColorPanelView>(
val v = io.legado.app.lib.prefs.Preference.bindView<ColorPanelView>(
context, holder, icon, title, summary, widgetLayoutResource,
io.legado.app.R.id.cpv_preference_preview_color_panel, 30, 30
)
@@ -0,0 +1,36 @@
package io.legado.app.lib.prefs
import android.content.Context
import android.util.AttributeSet
import android.widget.TextView
import androidx.preference.EditTextPreference.OnBindEditTextListener
import androidx.preference.PreferenceViewHolder
import io.legado.app.R
import io.legado.app.lib.theme.accentColor
import io.legado.app.utils.applyTint
class EditTextPreference(context: Context, attrs: AttributeSet) :
androidx.preference.EditTextPreference(context, attrs) {
private var mOnBindEditTextListener: OnBindEditTextListener? = null
private val onBindEditTextListener = OnBindEditTextListener { editText ->
editText.applyTint(context.accentColor)
mOnBindEditTextListener?.onBindEditText(editText)
}
init {
// isPersistent = true
layoutResource = R.layout.view_preference
super.setOnBindEditTextListener(onBindEditTextListener)
}
override fun onBindViewHolder(holder: PreferenceViewHolder) {
Preference.bindView<TextView>(context, holder, icon, title, summary, null, null)
super.onBindViewHolder(holder)
}
override fun setOnBindEditTextListener(onBindEditTextListener: OnBindEditTextListener?) {
mOnBindEditTextListener = onBindEditTextListener
}
}
@@ -1,9 +1,11 @@
package io.legado.app.ui.widget.prefs
package io.legado.app.lib.prefs
import android.app.Dialog
import android.os.Bundle
import androidx.appcompat.app.AlertDialog
import androidx.preference.EditTextPreferenceDialogFragmentCompat
import androidx.preference.PreferenceDialogFragmentCompat
import io.legado.app.lib.theme.accentColor
import io.legado.app.lib.theme.filletBackground
class EditTextPreferenceDialog : EditTextPreferenceDialogFragmentCompat() {
@@ -23,6 +25,13 @@ class EditTextPreferenceDialog : EditTextPreferenceDialogFragmentCompat() {
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val dialog = super.onCreateDialog(savedInstanceState)
dialog.window?.setBackgroundDrawable(requireContext().filletBackground)
dialog.window?.decorView?.post {
(dialog as AlertDialog).run {
getButton(AlertDialog.BUTTON_NEGATIVE)?.setTextColor(accentColor)
getButton(AlertDialog.BUTTON_POSITIVE)?.setTextColor(accentColor)
getButton(AlertDialog.BUTTON_NEUTRAL)?.setTextColor(accentColor)
}
}
return dialog
}
@@ -1,4 +1,4 @@
package io.legado.app.ui.widget.prefs
package io.legado.app.lib.prefs
import android.content.Context
import android.content.ContextWrapper
@@ -1,9 +1,11 @@
package io.legado.app.ui.widget.prefs
package io.legado.app.lib.prefs
import android.app.Dialog
import android.os.Bundle
import androidx.appcompat.app.AlertDialog
import androidx.preference.ListPreferenceDialogFragmentCompat
import androidx.preference.PreferenceDialogFragmentCompat
import io.legado.app.lib.theme.accentColor
import io.legado.app.lib.theme.filletBackground
class ListPreferenceDialog : ListPreferenceDialogFragmentCompat() {
@@ -23,6 +25,13 @@ class ListPreferenceDialog : ListPreferenceDialogFragmentCompat() {
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val dialog = super.onCreateDialog(savedInstanceState)
dialog.window?.setBackgroundDrawable(requireContext().filletBackground)
dialog.window?.decorView?.post {
(dialog as AlertDialog).run {
getButton(AlertDialog.BUTTON_NEGATIVE)?.setTextColor(accentColor)
getButton(AlertDialog.BUTTON_POSITIVE)?.setTextColor(accentColor)
getButton(AlertDialog.BUTTON_NEUTRAL)?.setTextColor(accentColor)
}
}
return dialog
}
@@ -1,9 +1,11 @@
package io.legado.app.ui.widget.prefs
package io.legado.app.lib.prefs
import android.app.Dialog
import android.os.Bundle
import androidx.appcompat.app.AlertDialog
import androidx.preference.MultiSelectListPreferenceDialogFragmentCompat
import androidx.preference.PreferenceDialogFragmentCompat
import io.legado.app.lib.theme.accentColor
import io.legado.app.lib.theme.filletBackground
class MultiSelectListPreferenceDialog : MultiSelectListPreferenceDialogFragmentCompat() {
@@ -25,6 +27,13 @@ class MultiSelectListPreferenceDialog : MultiSelectListPreferenceDialogFragmentC
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val dialog = super.onCreateDialog(savedInstanceState)
dialog.window?.setBackgroundDrawable(requireContext().filletBackground)
dialog.window?.decorView?.post {
(dialog as AlertDialog).run {
getButton(AlertDialog.BUTTON_NEGATIVE)?.setTextColor(accentColor)
getButton(AlertDialog.BUTTON_POSITIVE)?.setTextColor(accentColor)
getButton(AlertDialog.BUTTON_NEUTRAL)?.setTextColor(accentColor)
}
}
return dialog
}
@@ -1,4 +1,4 @@
package io.legado.app.ui.widget.prefs
package io.legado.app.lib.prefs
import android.content.Context
import android.util.AttributeSet
@@ -1,4 +1,4 @@
package io.legado.app.ui.widget.prefs
package io.legado.app.lib.prefs
import android.content.Context
import android.graphics.drawable.Drawable
@@ -1,4 +1,4 @@
package io.legado.app.ui.widget.prefs
package io.legado.app.lib.prefs
import android.content.Context
import android.util.AttributeSet
@@ -1,4 +1,4 @@
package io.legado.app.ui.widget.prefs
package io.legado.app.lib.prefs
import android.content.Context
import android.util.AttributeSet
@@ -1,13 +1,13 @@
package io.legado.app.base
package io.legado.app.lib.prefs.fragment
import android.annotation.SuppressLint
import androidx.fragment.app.DialogFragment
import androidx.preference.*
import io.legado.app.ui.widget.prefs.EditTextPreferenceDialog
import io.legado.app.ui.widget.prefs.ListPreferenceDialog
import io.legado.app.ui.widget.prefs.MultiSelectListPreferenceDialog
import io.legado.app.lib.prefs.EditTextPreferenceDialog
import io.legado.app.lib.prefs.ListPreferenceDialog
import io.legado.app.lib.prefs.MultiSelectListPreferenceDialog
abstract class BasePreferenceFragment : PreferenceFragmentCompat() {
abstract class PreferenceFragment : PreferenceFragmentCompat() {
private val dialogFragmentTag = "androidx.preference.PreferenceFragment.DIALOG"
@@ -34,7 +34,7 @@ abstract class BasePreferenceFragment : PreferenceFragmentCompat() {
return
}
val f: DialogFragment = when (preference) {
val dialogFragment: DialogFragment = when (preference) {
is EditTextPreference -> {
EditTextPreferenceDialog.newInstance(preference.getKey())
}
@@ -54,10 +54,9 @@ abstract class BasePreferenceFragment : PreferenceFragmentCompat() {
}
}
@Suppress("DEPRECATION")
f.setTargetFragment(this, 0)
dialogFragment.setTargetFragment(this, 0)
f.show(parentFragmentManager, dialogFragmentTag)
dialogFragment.show(parentFragmentManager, dialogFragmentTag)
}
}
@@ -131,9 +131,9 @@ object CacheBook {
class CacheBookModel(var bookSource: BookSource, var book: Book) {
private val waitDownloadSet = hashSetOf<Int>()
private val onDownloadSet = hashSetOf<Int>()
private val successDownloadSet = hashSetOf<Int>()
private val waitDownloadSet = linkedSetOf<Int>()
private val onDownloadSet = linkedSetOf<Int>()
private val successDownloadSet = linkedSetOf<Int>()
private val errorDownloadMap = hashMapOf<Int, Int>()
val waitCount get() = waitDownloadSet.size
@@ -174,7 +174,7 @@ object CacheBook {
}
onDownloadSet.remove(index)
//重试3次
if (errorDownloadMap[index] ?: 0 < 3) {
if ((errorDownloadMap[index] ?: 0) < 3) {
waitDownloadSet.add(index)
} else {
AppLog.put(
@@ -537,8 +537,7 @@ class AnalyzeUrl(
val cookieMap = CookieStore.cookieToMap(cookie)
val customCookieMap = CookieStore.cookieToMap(headerMap["Cookie"] ?: "")
cookieMap.putAll(customCookieMap)
val newCookie = CookieStore.mapToCookie(cookieMap)
newCookie?.let {
CookieStore.mapToCookie(cookieMap)?.let {
headerMap.put("Cookie", it)
}
}
@@ -61,21 +61,7 @@ class ImportHttpTtsViewModel(app: Application) : BaseViewModel(app) {
fun importSource(text: String) {
execute {
val mText = text.trim()
when {
mText.isJsonObject() -> {
HttpTTS.fromJson(mText).getOrThrow().let {
allSources.add(it)
}
}
mText.isJsonArray() -> HttpTTS.fromJsonArray(mText).getOrThrow().let { items ->
allSources.addAll(items)
}
mText.isAbsUrl() -> {
importSourceUrl(mText)
}
else -> throw NoStackTraceException(context.getString(R.string.wrong_format))
}
importSourceAwait(text.trim())
}.onError {
it.printOnDebug()
errorLiveData.postValue(it.localizedMessage ?: "")
@@ -84,11 +70,28 @@ class ImportHttpTtsViewModel(app: Application) : BaseViewModel(app) {
}
}
private suspend fun importSourceAwait(text: String) {
when {
text.isJsonObject() -> {
HttpTTS.fromJson(text).getOrThrow().let {
allSources.add(it)
}
}
text.isJsonArray() -> HttpTTS.fromJsonArray(text).getOrThrow().let { items ->
allSources.addAll(items)
}
text.isAbsUrl() -> {
importSourceUrl(text)
}
else -> throw NoStackTraceException(context.getString(R.string.wrong_format))
}
}
private suspend fun importSourceUrl(url: String) {
okHttpClient.newCallResponseBody {
url(url)
}.text().let {
allSources.addAll(HttpTTS.fromJsonArray(it).getOrThrow())
importSourceAwait(it)
}
}
@@ -88,14 +88,14 @@ class ImportReplaceRuleDialog() : BaseDialogFragment(R.layout.dialog_recycler_vi
adapter.notifyDataSetChanged()
upSelectText()
}
viewModel.errorLiveData.observe(this, {
viewModel.errorLiveData.observe(this) {
binding.rotateLoading.hide()
binding.tvMsg.apply {
text = it
visible()
}
})
viewModel.successLiveData.observe(this, {
}
viewModel.successLiveData.observe(this) {
binding.rotateLoading.hide()
if (it > 0) {
adapter.setItems(viewModel.allRules)
@@ -106,7 +106,7 @@ class ImportReplaceRuleDialog() : BaseDialogFragment(R.layout.dialog_recycler_vi
visible()
}
}
})
}
val source = arguments?.getString("source")
if (source.isNullOrEmpty()) {
dismiss()
@@ -6,12 +6,15 @@ import io.legado.app.base.BaseViewModel
import io.legado.app.constant.AppPattern
import io.legado.app.data.appDb
import io.legado.app.data.entities.ReplaceRule
import io.legado.app.exception.NoStackTraceException
import io.legado.app.help.ReplaceAnalyzer
import io.legado.app.help.config.AppConfig
import io.legado.app.help.http.newCallResponseBody
import io.legado.app.help.http.okHttpClient
import io.legado.app.help.http.text
import io.legado.app.utils.isAbsUrl
import io.legado.app.utils.isJsonArray
import io.legado.app.utils.isJsonObject
import io.legado.app.utils.splitNotBlank
class ImportReplaceRuleViewModel(app: Application) : BaseViewModel(app) {
@@ -83,17 +86,7 @@ class ImportReplaceRuleViewModel(app: Application) : BaseViewModel(app) {
fun import(text: String) {
execute {
if (text.isAbsUrl()) {
okHttpClient.newCallResponseBody {
url(text)
}.text("utf-8").let {
val rules = ReplaceAnalyzer.jsonToReplaceRules(it)
allRules.addAll(rules)
}
} else {
val rules = ReplaceAnalyzer.jsonToReplaceRules(text)
allRules.addAll(rules)
}
importAwait(text.trim())
}.onError {
errorLiveData.postValue(it.localizedMessage ?: "ERROR")
}.onSuccess {
@@ -101,6 +94,29 @@ class ImportReplaceRuleViewModel(app: Application) : BaseViewModel(app) {
}
}
private suspend fun importAwait(text: String) {
when {
text.isAbsUrl() -> importUrl(text)
text.isJsonArray() -> {
val rules = ReplaceAnalyzer.jsonToReplaceRules(text).getOrThrow()
allRules.addAll(rules)
}
text.isJsonObject() -> {
val rule = ReplaceAnalyzer.jsonToReplaceRule(text).getOrThrow()
allRules.add(rule)
}
else -> throw NoStackTraceException("格式不对")
}
}
private suspend fun importUrl(url: String) {
okHttpClient.newCallResponseBody {
url(url)
}.text("utf-8").let {
importAwait(it)
}
}
private fun comparisonSource() {
execute {
allRules.forEach {
@@ -9,10 +9,10 @@ import android.widget.LinearLayout
import androidx.fragment.app.DialogFragment
import androidx.preference.Preference
import io.legado.app.R
import io.legado.app.base.BasePreferenceFragment
import io.legado.app.constant.EventBus
import io.legado.app.constant.PreferKey
import io.legado.app.help.config.ReadBookConfig
import io.legado.app.lib.prefs.fragment.PreferenceFragment
import io.legado.app.lib.theme.bottomBackground
import io.legado.app.lib.theme.primaryColor
import io.legado.app.model.ReadBook
@@ -67,7 +67,7 @@ class MoreConfigDialog : DialogFragment() {
(activity as ReadBookActivity).bottomDialog--
}
class ReadPreferenceFragment : BasePreferenceFragment(),
class ReadPreferenceFragment : PreferenceFragment(),
SharedPreferences.OnSharedPreferenceChangeListener {
@SuppressLint("RestrictedApi")
@@ -10,12 +10,12 @@ import androidx.fragment.app.DialogFragment
import androidx.preference.ListPreference
import androidx.preference.Preference
import io.legado.app.R
import io.legado.app.base.BasePreferenceFragment
import io.legado.app.constant.EventBus
import io.legado.app.constant.PreferKey
import io.legado.app.data.appDb
import io.legado.app.help.IntentHelp
import io.legado.app.lib.dialogs.SelectItem
import io.legado.app.lib.prefs.fragment.PreferenceFragment
import io.legado.app.lib.theme.backgroundColor
import io.legado.app.lib.theme.primaryColor
import io.legado.app.model.ReadAloud
@@ -54,7 +54,7 @@ class ReadAloudConfigDialog : DialogFragment() {
.commit()
}
class ReadAloudPreferenceFragment : BasePreferenceFragment(),
class ReadAloudPreferenceFragment : PreferenceFragment(),
SpeakEngineDialog.CallBack,
SharedPreferences.OnSharedPreferenceChangeListener {
@@ -19,8 +19,10 @@ import io.legado.app.utils.gone
import io.legado.app.utils.longToastOnUi
import io.legado.app.utils.visible
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.isActive
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.Dispatchers.Main
import kotlinx.coroutines.async
import kotlinx.coroutines.ensureActive
import kotlinx.coroutines.withContext
import java.util.concurrent.ConcurrentHashMap
class ChapterListAdapter(context: Context, val callback: Callback) :
@@ -54,17 +56,10 @@ class ChapterListAdapter(context: Context, val callback: Callback) :
}
private val replaceRules
get() = callback.book?.let {
ContentProcessor.get(it.name, it.origin).getTitleReplaceRules()
}
private val useReplace
get() = AppConfig.tocUiUseReplace && callback.book?.getUseReplaceRule() == true
private var upDisplayTileJob: Coroutine<*>? = null
override fun onCurrentListChanged() {
super.onCurrentListChanged()
upDisplayTitle()
callback.onListChanged()
}
@@ -73,38 +68,52 @@ class ChapterListAdapter(context: Context, val callback: Callback) :
displayTitleMap.clear()
}
fun upDisplayTitle() {
fun upDisplayTitles(startIndex: Int) {
upDisplayTileJob?.cancel()
upDisplayTileJob = Coroutine.async(callback.scope) {
val replaceRules = replaceRules
val useReplace = useReplace
getItems().forEach {
if (!isActive) {
return@async
val book = callback.book ?: return@async
val replaceRules = ContentProcessor.get(book.name, book.origin).getTitleReplaceRules()
val useReplace = AppConfig.tocUiUseReplace && book.getUseReplaceRule()
val items = getItems()
async {
for (i in startIndex until items.size) {
val item = items[i]
if (displayTitleMap[item.title] == null) {
ensureActive()
val displayTitle = item.getDisplayTitle(replaceRules, useReplace)
ensureActive()
displayTitleMap[item.title] = displayTitle
withContext(Main) {
notifyItemChanged(i, true)
}
}
}
if (displayTitleMap[it.title] == null) {
displayTitleMap[it.title] = it.getDisplayTitle(replaceRules, useReplace)
}.start()
async {
for (i in startIndex downTo 0) {
val item = items[i]
if (displayTitleMap[item.title] == null) {
ensureActive()
val displayTitle = item.getDisplayTitle(replaceRules, useReplace)
ensureActive()
displayTitleMap[item.title] = displayTitle
withContext(Main) {
notifyItemChanged(i, true)
}
}
}
}
}.start()
}
}
private fun getDisplayTitle(chapter: BookChapter): String {
return displayTitleMap[chapter.title] ?: chapter.title
}
override fun getViewBinding(parent: ViewGroup): ItemChapterListBinding {
return ItemChapterListBinding.inflate(inflater, parent, false)
}
private fun getDisplayTile(chapter: BookChapter): String {
var displayTitle = displayTitleMap[chapter.title]
if (displayTitle != null) {
return displayTitle
}
displayTitle = runBlocking {
chapter.getDisplayTitle(replaceRules, useReplace)
}
displayTitleMap[chapter.title] = displayTitle
return displayTitle
}
override fun convert(
holder: ItemViewHolder,
binding: ItemChapterListBinding,
@@ -120,7 +129,7 @@ class ChapterListAdapter(context: Context, val callback: Callback) :
} else {
tvChapterName.setTextColor(context.getCompatColor(R.color.primaryText))
}
tvChapterName.text = getDisplayTile(item)
tvChapterName.text = getDisplayTitle(item)
if (item.isVolume) {
//卷名,如第一卷 突出显示
tvChapterItem.setBackgroundColor(context.getCompatColor(R.color.btn_bg_press))
@@ -138,6 +147,7 @@ class ChapterListAdapter(context: Context, val callback: Callback) :
}
upHasCache(binding, isDur, cached)
} else {
tvChapterName.text = getDisplayTitle(item)
upHasCache(binding, isDur, cached)
}
}
@@ -150,8 +160,8 @@ class ChapterListAdapter(context: Context, val callback: Callback) :
}
}
holder.itemView.setOnLongClickListener {
getItem(holder.layoutPosition)?.let {
context.longToastOnUi(getDisplayTile(it))
getItem(holder.layoutPosition)?.let { item ->
context.longToastOnUi(getDisplayTitle(item))
}
true
}
@@ -97,7 +97,15 @@ class ChapterListFragment : VMBaseFragment<TocViewModel>(R.layout.fragment_chapt
viewModel.bookData.value?.bookUrl?.let { bookUrl ->
if (chapter.bookUrl == bookUrl) {
adapter.cacheFileNames.add(chapter.getFileName())
adapter.notifyItemChanged(chapter.index, true)
if (viewModel.searchKey.isNullOrEmpty()) {
adapter.notifyItemChanged(chapter.index, true)
} else {
adapter.getItems().forEachIndexed { index, bookChapter ->
if (bookChapter.index == chapter.index) {
adapter.notifyItemChanged(index, true)
}
}
}
}
}
}
@@ -128,12 +136,13 @@ class ChapterListFragment : VMBaseFragment<TocViewModel>(R.layout.fragment_chapt
}
}
mLayoutManager.scrollToPositionWithOffset(scrollPos, 0)
adapter.upDisplayTitles(scrollPos)
}
}
override fun clearDisplayTitle() {
adapter.clearDisplayTitle()
adapter.upDisplayTitle()
adapter.upDisplayTitles(mLayoutManager.findFirstVisibleItemPosition())
}
override val scope: CoroutineScope
@@ -59,10 +59,12 @@ class TocActivity : VMBaseActivity<ActivityChapterListBinding, TocViewModel>() {
setOnSearchClickListener { tabLayout.gone() }
setOnQueryTextListener(object : SearchView.OnQueryTextListener {
override fun onQueryTextSubmit(query: String): Boolean {
viewModel.searchKey = query
return false
}
override fun onQueryTextChange(newText: String): Boolean {
viewModel.searchKey = newText
if (tabLayout.selectedTabPosition == 1) {
viewModel.startBookmarkSearch(newText)
} else {
@@ -12,6 +12,7 @@ class TocViewModel(application: Application) : BaseViewModel(application) {
var bookData = MutableLiveData<Book>()
var chapterListCallBack: ChapterListCallBack? = null
var bookMarkCallBack: BookmarkCallBack? = null
var searchKey: String? = null
fun initBook(bookUrl: String) {
this.bookUrl = bookUrl
@@ -15,7 +15,6 @@ import androidx.preference.EditTextPreference
import androidx.preference.ListPreference
import androidx.preference.Preference
import io.legado.app.R
import io.legado.app.base.BasePreferenceFragment
import io.legado.app.constant.AppLog
import io.legado.app.constant.PreferKey
import io.legado.app.help.config.AppConfig
@@ -25,7 +24,7 @@ import io.legado.app.help.storage.*
import io.legado.app.lib.dialogs.alert
import io.legado.app.lib.permission.Permissions
import io.legado.app.lib.permission.PermissionsCompat
import io.legado.app.lib.theme.accentColor
import io.legado.app.lib.prefs.fragment.PreferenceFragment
import io.legado.app.lib.theme.primaryColor
import io.legado.app.ui.document.HandleFileContract
import io.legado.app.ui.widget.dialog.TextDialog
@@ -34,7 +33,7 @@ import kotlinx.coroutines.Dispatchers.Main
import kotlinx.coroutines.launch
import splitties.init.appCtx
class BackupConfigFragment : BasePreferenceFragment(),
class BackupConfigFragment : PreferenceFragment(),
SharedPreferences.OnSharedPreferenceChangeListener {
private val viewModel by activityViewModels<ConfigViewModel>()
@@ -100,34 +99,18 @@ class BackupConfigFragment : BasePreferenceFragment(),
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
addPreferencesFromResource(R.xml.pref_config_backup)
findPreference<EditTextPreference>(PreferKey.webDavUrl)?.let {
it.setOnBindEditTextListener { editText ->
editText.applyTint(requireContext().accentColor)
}
}
findPreference<EditTextPreference>(PreferKey.webDavAccount)?.let {
it.setOnBindEditTextListener { editText ->
editText.applyTint(requireContext().accentColor)
}
}
findPreference<EditTextPreference>(PreferKey.webDavPassword)?.let {
it.setOnBindEditTextListener { editText ->
editText.applyTint(requireContext().accentColor)
editText.inputType =
InputType.TYPE_TEXT_VARIATION_PASSWORD or InputType.TYPE_CLASS_TEXT
}
}
findPreference<EditTextPreference>(PreferKey.webDavDir)?.let {
it.setOnBindEditTextListener { editText ->
editText.applyTint(requireContext().accentColor)
}
}
upPreferenceSummary(PreferKey.webDavUrl, getPrefString(PreferKey.webDavUrl))
upPreferenceSummary(PreferKey.webDavAccount, getPrefString(PreferKey.webDavAccount))
upPreferenceSummary(PreferKey.webDavPassword, getPrefString(PreferKey.webDavPassword))
upPreferenceSummary(PreferKey.webDavDir, AppConfig.webDavDir)
upPreferenceSummary(PreferKey.backupPath, getPrefString(PreferKey.backupPath))
findPreference<io.legado.app.ui.widget.prefs.Preference>("web_dav_restore")
findPreference<io.legado.app.lib.prefs.Preference>("web_dav_restore")
?.onLongClick { restoreDir.launch(); true }
}
@@ -7,16 +7,16 @@ import android.os.Bundle
import android.view.View
import androidx.preference.Preference
import io.legado.app.R
import io.legado.app.base.BasePreferenceFragment
import io.legado.app.constant.PreferKey
import io.legado.app.lib.dialogs.selector
import io.legado.app.lib.prefs.SwitchPreference
import io.legado.app.lib.prefs.fragment.PreferenceFragment
import io.legado.app.lib.theme.primaryColor
import io.legado.app.model.BookCover
import io.legado.app.ui.widget.prefs.SwitchPreference
import io.legado.app.utils.*
import java.io.FileOutputStream
class CoverConfigFragment : BasePreferenceFragment(),
class CoverConfigFragment : PreferenceFragment(),
SharedPreferences.OnSharedPreferenceChangeListener {
private val requestCodeCover = 111
@@ -10,12 +10,12 @@ import androidx.fragment.app.activityViewModels
import androidx.preference.ListPreference
import androidx.preference.Preference
import io.legado.app.R
import io.legado.app.base.BasePreferenceFragment
import io.legado.app.constant.EventBus
import io.legado.app.constant.PreferKey
import io.legado.app.databinding.DialogEditTextBinding
import io.legado.app.help.config.AppConfig
import io.legado.app.lib.dialogs.alert
import io.legado.app.lib.prefs.fragment.PreferenceFragment
import io.legado.app.lib.theme.primaryColor
import io.legado.app.model.CheckSource
import io.legado.app.receiver.SharedReceiverActivity
@@ -26,7 +26,7 @@ import io.legado.app.utils.*
import splitties.init.appCtx
class OtherConfigFragment : BasePreferenceFragment(),
class OtherConfigFragment : PreferenceFragment(),
SharedPreferences.OnSharedPreferenceChangeListener {
private val viewModel by activityViewModels<ConfigViewModel>()
@@ -13,7 +13,6 @@ import android.widget.SeekBar
import androidx.preference.Preference
import io.legado.app.R
import io.legado.app.base.AppContextWrapper
import io.legado.app.base.BasePreferenceFragment
import io.legado.app.constant.AppConst
import io.legado.app.constant.EventBus
import io.legado.app.constant.PreferKey
@@ -24,16 +23,17 @@ import io.legado.app.help.config.AppConfig
import io.legado.app.help.config.ThemeConfig
import io.legado.app.lib.dialogs.alert
import io.legado.app.lib.dialogs.selector
import io.legado.app.lib.prefs.ColorPreference
import io.legado.app.lib.prefs.fragment.PreferenceFragment
import io.legado.app.lib.theme.primaryColor
import io.legado.app.ui.widget.number.NumberPickerDialog
import io.legado.app.ui.widget.prefs.ColorPreference
import io.legado.app.ui.widget.seekbar.SeekBarChangeListener
import io.legado.app.utils.*
import java.io.FileOutputStream
@Suppress("SameParameterValue")
class ThemeConfigFragment : BasePreferenceFragment(),
class ThemeConfigFragment : PreferenceFragment(),
SharedPreferences.OnSharedPreferenceChangeListener {
private val requestCodeBgLight = 121
@@ -7,15 +7,15 @@ import android.os.Bundle
import android.view.View
import androidx.preference.Preference
import io.legado.app.R
import io.legado.app.base.BasePreferenceFragment
import io.legado.app.constant.PreferKey
import io.legado.app.lib.dialogs.selector
import io.legado.app.lib.prefs.fragment.PreferenceFragment
import io.legado.app.lib.theme.primaryColor
import io.legado.app.model.BookCover
import io.legado.app.utils.*
import java.io.FileOutputStream
class WelcomeConfigFragment : BasePreferenceFragment(),
class WelcomeConfigFragment : PreferenceFragment(),
SharedPreferences.OnSharedPreferenceChangeListener {
private val requestWelcomeImage = 221
@@ -8,13 +8,16 @@ import android.view.View
import androidx.preference.Preference
import io.legado.app.R
import io.legado.app.base.BaseFragment
import io.legado.app.base.BasePreferenceFragment
import io.legado.app.constant.EventBus
import io.legado.app.constant.PreferKey
import io.legado.app.databinding.FragmentMyConfigBinding
import io.legado.app.help.config.AppConfig
import io.legado.app.help.config.ThemeConfig
import io.legado.app.lib.dialogs.selector
import io.legado.app.lib.prefs.NameListPreference
import io.legado.app.lib.prefs.PreferenceCategory
import io.legado.app.lib.prefs.SwitchPreference
import io.legado.app.lib.prefs.fragment.PreferenceFragment
import io.legado.app.lib.theme.primaryColor
import io.legado.app.service.WebService
import io.legado.app.ui.about.AboutActivity
@@ -26,9 +29,6 @@ import io.legado.app.ui.config.ConfigActivity
import io.legado.app.ui.config.ConfigTag
import io.legado.app.ui.replace.ReplaceRuleActivity
import io.legado.app.ui.widget.dialog.TextDialog
import io.legado.app.ui.widget.prefs.NameListPreference
import io.legado.app.ui.widget.prefs.PreferenceCategory
import io.legado.app.ui.widget.prefs.SwitchPreference
import io.legado.app.utils.*
import io.legado.app.utils.viewbindingdelegate.viewBinding
@@ -40,7 +40,7 @@ class MyFragment : BaseFragment(R.layout.fragment_my_config) {
setSupportToolbar(binding.titleBar.toolbar)
val fragmentTag = "prefFragment"
var preferenceFragment = childFragmentManager.findFragmentByTag(fragmentTag)
if (preferenceFragment == null) preferenceFragment = PreferenceFragment()
if (preferenceFragment == null) preferenceFragment = MyPreferenceFragment()
childFragmentManager.beginTransaction()
.replace(R.id.pre_fragment, preferenceFragment, fragmentTag).commit()
}
@@ -61,7 +61,7 @@ class MyFragment : BaseFragment(R.layout.fragment_my_config) {
/**
* 配置
*/
class PreferenceFragment : BasePreferenceFragment(),
class MyPreferenceFragment : PreferenceFragment(),
SharedPreferences.OnSharedPreferenceChangeListener {
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
@@ -14,6 +14,7 @@ import io.legado.app.base.VMBaseActivity
import io.legado.app.databinding.ActivityRssArtivlesBinding
import io.legado.app.databinding.DialogEditTextBinding
import io.legado.app.lib.dialogs.alert
import io.legado.app.lib.theme.accentColor
import io.legado.app.ui.login.SourceLoginActivity
import io.legado.app.ui.rss.source.edit.RssSourceEditActivity
import io.legado.app.utils.StartActivityContract
@@ -45,9 +46,10 @@ class RssSortActivity : VMBaseActivity<ActivityRssArtivlesBinding, RssSortViewMo
override fun onActivityCreated(savedInstanceState: Bundle?) {
binding.viewPager.adapter = adapter
binding.tabLayout.setupWithViewPager(binding.viewPager)
viewModel.titleLiveData.observe(this, {
binding.tabLayout.setSelectedTabIndicatorColor(accentColor)
viewModel.titleLiveData.observe(this) {
binding.titleBar.title = it
})
}
viewModel.initData(intent) {
upFragments()
}
@@ -1,22 +0,0 @@
package io.legado.app.ui.widget.prefs
import android.content.Context
import android.util.AttributeSet
import android.widget.TextView
import androidx.preference.PreferenceViewHolder
import io.legado.app.R
class EditTextPreference(context: Context, attrs: AttributeSet) :
androidx.preference.EditTextPreference(context, attrs) {
init {
// isPersistent = true
layoutResource = R.layout.view_preference
}
override fun onBindViewHolder(holder: PreferenceViewHolder) {
Preference.bindView<TextView>(context, holder, icon, title, summary, null, null)
super.onBindViewHolder(holder)
}
}
@@ -6,6 +6,7 @@ import kotlinx.coroutines.suspendCancellableCoroutine
import splitties.init.appCtx
import kotlin.concurrent.thread
import kotlin.coroutines.resume
import kotlin.coroutines.resumeWithException
/**
* 带有超时检测的正则替换
@@ -19,7 +20,7 @@ suspend fun CharSequence.replace(regex: Regex, replacement: String, timeout: Lon
val result = regex.replace(charSequence, replacement)
block.resume(result)
} catch (e: Exception) {
block.cancel(e)
block.resumeWithException(e)
}
}
mainHandler.postDelayed({