diff --git a/app/src/main/assets/updateLog.md b/app/src/main/assets/updateLog.md index 19101632f..86dc56b07 100644 --- a/app/src/main/assets/updateLog.md +++ b/app/src/main/assets/updateLog.md @@ -16,6 +16,7 @@ * 校验失效分组具体到搜索发现目录正文 * txt文件初次解析目录不选择禁用的正则 * txt单章字数超102400均分txt,添加开关 +* 修复tts被回收后无法继续朗读的bug,重新初始化tts **2022/02/03** diff --git a/app/src/main/java/io/legado/app/constant/PreferKey.kt b/app/src/main/java/io/legado/app/constant/PreferKey.kt index 62ff58866..c0951a129 100644 --- a/app/src/main/java/io/legado/app/constant/PreferKey.kt +++ b/app/src/main/java/io/legado/app/constant/PreferKey.kt @@ -49,7 +49,7 @@ object PreferKey { const val webDavUrl = "web_dav_url" const val webDavAccount = "web_dav_account" const val webDavPassword = "web_dav_password" - const val webDavCreateDir = "webDavCreateDir" + const val webDavDir = "webDavDir" const val exportToWebDav = "webDavCacheBackup" const val exportNoChapterName = "exportNoChapterName" const val exportType = "exportType" diff --git a/app/src/main/java/io/legado/app/data/entities/BookChapter.kt b/app/src/main/java/io/legado/app/data/entities/BookChapter.kt index 2f46bc002..ac5718872 100644 --- a/app/src/main/java/io/legado/app/data/entities/BookChapter.kt +++ b/app/src/main/java/io/legado/app/data/entities/BookChapter.kt @@ -80,7 +80,6 @@ data class BookChapter( chineseConvert: Boolean = true, ): String { var displayTitle = title.replace(AppPattern.rnRegex, "") - val mDisplayTitle = displayTitle if (chineseConvert) { when (AppConfig.chineseConverterType) { 1 -> displayTitle = ChineseUtils.t2s(displayTitle) @@ -91,18 +90,20 @@ data class BookChapter( replaceRules.forEach { item -> if (item.pattern.isNotEmpty()) { try { - displayTitle = if (item.isRegex) { + val mDisplayTitle = if (item.isRegex) { displayTitle.replace(item.pattern.toRegex(), item.replacement) } else { displayTitle.replace(item.pattern, item.replacement) } + if (mDisplayTitle.isNotBlank()) { + displayTitle = mDisplayTitle + } } catch (e: Exception) { appCtx.toastOnUi("${item.name}替换出错") } } } } - if (displayTitle.isBlank()) displayTitle = mDisplayTitle return when { !isVip -> displayTitle isPay -> appCtx.getString(R.string.payed_title, displayTitle) diff --git a/app/src/main/java/io/legado/app/help/AppConfig.kt b/app/src/main/java/io/legado/app/help/AppConfig.kt index 0f8fa940e..8375e07d6 100644 --- a/app/src/main/java/io/legado/app/help/AppConfig.kt +++ b/app/src/main/java/io/legado/app/help/AppConfig.kt @@ -271,6 +271,8 @@ object AppConfig : SharedPreferences.OnSharedPreferenceChangeListener { val replaceEnableDefault get() = appCtx.getPrefBoolean(PreferKey.replaceEnableDefault, true) + val webDavDir get() = appCtx.getPrefString(PreferKey.webDavDir, "legado") + val recordLog get() = appCtx.getPrefBoolean(PreferKey.recordLog) val doublePageHorizontal: Boolean diff --git a/app/src/main/java/io/legado/app/help/storage/AppWebDav.kt b/app/src/main/java/io/legado/app/help/storage/AppWebDav.kt index 1fc779247..dc6f66aef 100644 --- a/app/src/main/java/io/legado/app/help/storage/AppWebDav.kt +++ b/app/src/main/java/io/legado/app/help/storage/AppWebDav.kt @@ -29,13 +29,13 @@ object AppWebDav { private val rootWebDavUrl: String get() { - var url = appCtx.getPrefString(PreferKey.webDavUrl)?.trim() - if (url.isNullOrEmpty()) { - url = defaultWebDavUrl - } + val configUrl = appCtx.getPrefString(PreferKey.webDavUrl)?.trim() + var url = if (configUrl.isNullOrEmpty()) defaultWebDavUrl else configUrl if (!url.endsWith("/")) url = "${url}/" - if (appCtx.getPrefBoolean(PreferKey.webDavCreateDir, true)) { - url = "${url}legado/" + AppConfig.webDavDir?.trim()?.let { + if (it.isNotEmpty()) { + url = "${url}${it}/" + } } return url } diff --git a/app/src/main/java/io/legado/app/service/HttpReadAloudService.kt b/app/src/main/java/io/legado/app/service/HttpReadAloudService.kt index db06ea216..3b2dbd588 100644 --- a/app/src/main/java/io/legado/app/service/HttpReadAloudService.kt +++ b/app/src/main/java/io/legado/app/service/HttpReadAloudService.kt @@ -68,9 +68,10 @@ class HttpReadAloudService : BaseReadAloudService(), ReadBook.readAloud() } else { super.play() - ReadAloud.httpTTS?.let { + kotlin.runCatching { + val tts = ReadAloud.httpTTS ?: throw NoStackTraceException("httpTts is null") val fileName = - md5SpeakFileName(it.url, AppConfig.ttsSpeechRate.toString(), contentList[nowSpeak]) + md5SpeakFileName(tts.url, AppConfig.ttsSpeechRate.toString(), contentList[nowSpeak]) if (nowSpeak == 0) { downloadAudio() } else { @@ -81,6 +82,8 @@ class HttpReadAloudService : BaseReadAloudService(), downloadAudio() } } + }.onFailure { + toastOnUi("朗读出错:${it.localizedMessage}") } } } diff --git a/app/src/main/java/io/legado/app/service/TTSReadAloudService.kt b/app/src/main/java/io/legado/app/service/TTSReadAloudService.kt index 09f122f18..dac6e3ced 100644 --- a/app/src/main/java/io/legado/app/service/TTSReadAloudService.kt +++ b/app/src/main/java/io/legado/app/service/TTSReadAloudService.kt @@ -11,6 +11,7 @@ import io.legado.app.constant.EventBus import io.legado.app.help.AppConfig import io.legado.app.help.MediaHelp import io.legado.app.lib.dialogs.SelectItem +import io.legado.app.model.NoStackTraceException import io.legado.app.model.ReadBook import io.legado.app.utils.* import java.util.* @@ -73,16 +74,25 @@ class TTSReadAloudService : BaseReadAloudService(), TextToSpeech.OnInitListener ReadBook.readAloud() } else { super.play() - execute { + kotlin.runCatching { MediaHelp.playSilentSound(this@TTSReadAloudService) - }.onFinally { - textToSpeech?.let { - it.speak("", TextToSpeech.QUEUE_FLUSH, null, null) - for (i in nowSpeak until contentList.size) { - val text = contentList[i].replace(AppPattern.notReadAloudRegex, "") - it.speak(text, TextToSpeech.QUEUE_ADD, null, AppConst.APP_TAG + i) + val tts = textToSpeech ?: throw NoStackTraceException("tts is null") + var result = tts.speak("", TextToSpeech.QUEUE_FLUSH, null, null) + if (result == TextToSpeech.ERROR) { + clearTTS() + initTts() + return + } + for (i in nowSpeak until contentList.size) { + val text = contentList[i].replace(AppPattern.notReadAloudRegex, "") + result = tts.speak(text, TextToSpeech.QUEUE_ADD, null, AppConst.APP_TAG + i) + if (result == TextToSpeech.ERROR) { + AppLog.put("tts朗读出错:$text") } } + }.onFailure { + AppLog.put("tts朗读出错", it) + toastOnUi(it.localizedMessage) } } } diff --git a/app/src/main/java/io/legado/app/ui/config/BackupConfigFragment.kt b/app/src/main/java/io/legado/app/ui/config/BackupConfigFragment.kt index 6314e693b..6d5377760 100644 --- a/app/src/main/java/io/legado/app/ui/config/BackupConfigFragment.kt +++ b/app/src/main/java/io/legado/app/ui/config/BackupConfigFragment.kt @@ -121,6 +121,7 @@ class BackupConfigFragment : BasePreferenceFragment(), 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("web_dav_restore") ?.onLongClick { restoreDir.launch(); true } @@ -168,6 +169,7 @@ class BackupConfigFragment : BasePreferenceFragment(), PreferKey.backupPath -> { upPreferenceSummary(key, getPrefString(key)) } + PreferKey.webDavDir -> upPreferenceSummary(key, AppConfig.webDavDir) } } @@ -192,6 +194,11 @@ class BackupConfigFragment : BasePreferenceFragment(), } else { preference.summary = "*".repeat(value.toString().length) } + PreferKey.webDavDir -> if (value.isNullOrBlank()) { + preference.summary = "null" + } else { + preference.summary = value + } else -> { if (preference is ListPreference) { val index = preference.findIndexOfValue(value) diff --git a/app/src/main/java/io/legado/app/ui/widget/code/CodeView.kt b/app/src/main/java/io/legado/app/ui/widget/code/CodeView.kt index 4f2207d4b..c07a0d56f 100644 --- a/app/src/main/java/io/legado/app/ui/widget/code/CodeView.kt +++ b/app/src/main/java/io/legado/app/ui/widget/code/CodeView.kt @@ -20,7 +20,9 @@ import java.util.regex.Pattern import kotlin.math.roundToInt @Suppress("unused") -class CodeView : AppCompatMultiAutoCompleteTextView { +class CodeView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) : + AppCompatMultiAutoCompleteTextView(context, attrs) { + private var tabWidth = 0 private var tabWidthInCharacters = 0 private var mUpdateDelayTime = 500 @@ -35,23 +37,53 @@ class CodeView : AppCompatMultiAutoCompleteTextView { private val mSyntaxPatternMap: MutableMap = HashMap() private var mIndentCharacterList = mutableListOf('{', '+', '-', '*', '/', '=') - constructor(context: Context?) : super(context!!) { - initEditorView() + private val mUpdateRunnable = Runnable { + val source = text + highlightWithoutChange(source) } - constructor(context: Context?, attrs: AttributeSet?) : super( - context!!, attrs - ) { - initEditorView() - } + private val mEditorTextWatcher: TextWatcher = object : TextWatcher { + private var start = 0 + private var count = 0 + override fun beforeTextChanged( + charSequence: CharSequence, + start: Int, + before: Int, + count: Int + ) { + this.start = start + this.count = count + } - constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super( - context!!, attrs, defStyleAttr - ) { - initEditorView() + override fun onTextChanged( + charSequence: CharSequence, + start: Int, + before: Int, + count: Int + ) { + if (!modified) return + if (highlightWhileTextChanging) { + if (mSyntaxPatternMap.isNotEmpty()) { + convertTabs(editableText, start, count) + mUpdateHandler.postDelayed(mUpdateRunnable, mUpdateDelayTime.toLong()) + } + } + if (mRemoveErrorsWhenTextChanged) removeAllErrorLines() + } + + override fun afterTextChanged(editable: Editable) { + if (!highlightWhileTextChanging) { + if (!modified) return + cancelHighlighterRender() + if (mSyntaxPatternMap.isNotEmpty()) { + convertTabs(editableText, start, count) + mUpdateHandler.postDelayed(mUpdateRunnable, mUpdateDelayTime.toLong()) + } + } + } } - private fun initEditorView() { + init { if (mAutoCompleteTokenizer == null) { mAutoCompleteTokenizer = KeywordTokenizer() } @@ -169,7 +201,10 @@ class CodeView : AppCompatMultiAutoCompleteTextView { } private fun highlight(editable: Editable): Editable { - if (editable.isEmpty() || editable.length > 1024) return editable + // if (editable.isEmpty() || editable.length > 1024) return editable + if (editable.length !in 1..1024) { + return editable + } try { clearSpans(editable) highlightErrorLines(editable) @@ -345,51 +380,6 @@ class CodeView : AppCompatMultiAutoCompleteTextView { super.showDropDown() } - private val mUpdateRunnable = Runnable { - val source = text - highlightWithoutChange(source) - } - private val mEditorTextWatcher: TextWatcher = object : TextWatcher { - private var start = 0 - private var count = 0 - override fun beforeTextChanged( - charSequence: CharSequence, - start: Int, - before: Int, - count: Int - ) { - this.start = start - this.count = count - } - - override fun onTextChanged( - charSequence: CharSequence, - start: Int, - before: Int, - count: Int - ) { - if (!modified) return - if (highlightWhileTextChanging) { - if (mSyntaxPatternMap.isNotEmpty()) { - convertTabs(editableText, start, count) - mUpdateHandler.postDelayed(mUpdateRunnable, mUpdateDelayTime.toLong()) - } - } - if (mRemoveErrorsWhenTextChanged) removeAllErrorLines() - } - - override fun afterTextChanged(editable: Editable) { - if (!highlightWhileTextChanging) { - if (!modified) return - cancelHighlighterRender() - if (mSyntaxPatternMap.isNotEmpty()) { - convertTabs(editableText, start, count) - mUpdateHandler.postDelayed(mUpdateRunnable, mUpdateDelayTime.toLong()) - } - } - } - } - private inner class TabWidthSpan : ReplacementSpan() { override fun getSize( paint: Paint, diff --git a/app/src/main/java/io/legado/app/utils/ToastUtils.kt b/app/src/main/java/io/legado/app/utils/ToastUtils.kt index a2373f819..c2a91858f 100644 --- a/app/src/main/java/io/legado/app/utils/ToastUtils.kt +++ b/app/src/main/java/io/legado/app/utils/ToastUtils.kt @@ -26,9 +26,9 @@ fun Context.toastOnUi(message: CharSequence?) { runOnUI { kotlin.runCatching { if (toast == null) { - toast = Toast.makeText(this, message, Toast.LENGTH_SHORT) + toast = Toast.makeText(this, message.toString(), Toast.LENGTH_SHORT) } else { - toast?.setText(message) + toast?.setText(message.toString()) toast?.duration = Toast.LENGTH_SHORT } toast?.show() @@ -54,9 +54,9 @@ fun Context.longToastOnUi(message: CharSequence?) { runOnUI { kotlin.runCatching { if (toast == null) { - toast = Toast.makeText(this, message, Toast.LENGTH_LONG) + toast = Toast.makeText(this, message.toString(), Toast.LENGTH_LONG) } else { - toast?.setText(message) + toast?.setText(message.toString()) toast?.duration = Toast.LENGTH_LONG } toast?.show() diff --git a/app/src/main/res/values-es-rES/strings.xml b/app/src/main/res/values-es-rES/strings.xml index 4b371de7e..1a404a11c 100644 --- a/app/src/main/res/values-es-rES/strings.xml +++ b/app/src/main/res/values-es-rES/strings.xml @@ -9,8 +9,6 @@ Inicio Restaurar Importar datos de Legado - Crear carpeta - Crea una carpeta de respaldo bajo el nombre de Legado. Respaldo de caché de libros sin conexión Exporta localmente lo respalda para su exportación Respaldar para @@ -929,5 +927,6 @@ 小于 校验超时: %1$s秒\n校验项目:%2$s 记录调试日志 + 子文件夹 diff --git a/app/src/main/res/values-ja-rJP/strings.xml b/app/src/main/res/values-ja-rJP/strings.xml index 353806354..118b873f2 100644 --- a/app/src/main/res/values-ja-rJP/strings.xml +++ b/app/src/main/res/values-ja-rJP/strings.xml @@ -9,8 +9,6 @@ Home Restore Import Legado data - Create a subfolder - Create a folder named Legado as the backup folder. Offline cache book backup Export to local and back up to the exports directory under the legado folder Backup to @@ -932,5 +930,6 @@ 小于 校验超时: %1$s秒\n校验项目:%2$s 记录调试日志 + 子文件夹 diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index e4056e388..94949dc6d 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -9,8 +9,6 @@ Início Restaurar Importar dados ao Legado - Criar uma subpasta - Criar uma pasta de Backup com o nome Legado. Backup do cache dos livros para leitura off-line Exportar localmente e fazer Backup à pasta de exportação Backup para @@ -930,5 +928,6 @@ 小于 校验超时: %1$s秒\n校验项目:%2$s 记录调试日志 + 子文件夹 diff --git a/app/src/main/res/values-zh-rHK/strings.xml b/app/src/main/res/values-zh-rHK/strings.xml index ed5ae5cab..2158736f8 100644 --- a/app/src/main/res/values-zh-rHK/strings.xml +++ b/app/src/main/res/values-zh-rHK/strings.xml @@ -8,8 +8,6 @@ Home 還原 導入閲讀數據 - 創建子文件夾 - 創建 legado 文件夾作爲備份路徑 離線緩存書籍備份 導出本地同時備份到legado文件夾下exports目錄 備份路徑 @@ -929,5 +927,6 @@ 小于 校验超时: %1$s秒\n校验项目:%2$s 记录调试日志 + 子文件夹 diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index e754d45f7..c99dc1ecc 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -8,8 +8,6 @@ 備份 復原 匯入閱讀資料 - 建立子資料夾 - 建立legado資料夾作為備份資料夾 離線快取書籍備份 匯出本機同時備份到legado資料夾下exports目錄 備份路徑 @@ -931,5 +929,6 @@ 小于 校验超时: %1$s秒\n校验项目:%2$s 记录调试日志 + 子文件夹 diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml index 219fdff47..26ac234e2 100644 --- a/app/src/main/res/values-zh/strings.xml +++ b/app/src/main/res/values-zh/strings.xml @@ -8,8 +8,6 @@ Home 恢复 导入阅读数据 - 创建子文件夹 - 创建legado文件夹作为备份文件夹 离线缓存书籍备份 导出本地同时备份到legado文件夹下exports目录 备份路径 @@ -931,5 +929,6 @@ 小于 校验超时: %1$s秒\n校验项目:%2$s 记录调试日志 + 子文件夹 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 17148d869..d10a2350d 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -9,8 +9,6 @@ Home Restore Import Legado data - Create a subfolder - Create a folder named Legado as the backup folder. Offline cache book backup Export to local and back up to the exports directory under the legado folder Backup to @@ -932,5 +930,6 @@ 小于 校验超时: %1$s秒\n校验项目:%2$s 记录调试日志 + Sub dir diff --git a/app/src/main/res/xml/pref_config_backup.xml b/app/src/main/res/xml/pref_config_backup.xml index 6a57dcc87..06085208d 100644 --- a/app/src/main/res/xml/pref_config_backup.xml +++ b/app/src/main/res/xml/pref_config_backup.xml @@ -27,11 +27,9 @@ android:summary="@string/web_dav_pw_s" app:iconSpaceReserved="false" /> - diff --git a/build.gradle b/build.gradle index ef8b49bf5..242612614 100644 --- a/build.gradle +++ b/build.gradle @@ -13,7 +13,7 @@ buildscript { //maven { url 'https://maven.aliyun.com/repository/gradle-plugin' } } dependencies { - classpath 'com.android.tools.build:gradle:7.1.0' + classpath 'com.android.tools.build:gradle:7.1.1' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath 'de.timfreiheit.resourceplaceholders:placeholders:0.4' classpath 'de.undercouch:gradle-download-task:4.1.2'