mirror of
https://github.com/gedoor/legado.git
synced 2025-08-10 00:52:30 +00:00
Merge branch 'master' into master
This commit is contained in:
@@ -16,6 +16,7 @@
|
||||
* 校验失效分组具体到搜索发现目录正文
|
||||
* txt文件初次解析目录不选择禁用的正则
|
||||
* txt单章字数超102400均分txt,添加开关
|
||||
* 修复tts被回收后无法继续朗读的bug,重新初始化tts
|
||||
|
||||
**2022/02/03**
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<io.legado.app.ui.widget.prefs.Preference>("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)
|
||||
|
||||
@@ -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<Pattern, Int> = 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
|
||||
}
|
||||
|
||||
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())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(
|
||||
context!!, attrs, defStyleAttr
|
||||
) {
|
||||
initEditorView()
|
||||
}
|
||||
|
||||
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,
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -9,8 +9,6 @@
|
||||
<string name="menu_backup">Inicio</string>
|
||||
<string name="menu_restore">Restaurar</string>
|
||||
<string name="menu_import_old">Importar datos de Legado</string>
|
||||
<string name="mkdirs">Crear carpeta</string>
|
||||
<string name="mkdirs_description">Crea una carpeta de respaldo bajo el nombre de Legado.</string>
|
||||
<string name="webdav_cache_backup">Respaldo de caché de libros sin conexión</string>
|
||||
<string name="webdav_cache_backup_s">Exporta localmente lo respalda para su exportación</string>
|
||||
<string name="backup_path">Respaldar para</string>
|
||||
@@ -929,5 +927,6 @@
|
||||
<string name="less_than">小于</string>
|
||||
<string name="check_source_config_summary">校验超时: %1$s秒\n校验项目:%2$s</string>
|
||||
<string name="record_debug_log">记录调试日志</string>
|
||||
<string name="sub_dir">子文件夹</string>
|
||||
<!-- string end -->
|
||||
</resources>
|
||||
|
||||
@@ -9,8 +9,6 @@
|
||||
<string name="menu_backup">Home</string>
|
||||
<string name="menu_restore">Restore</string>
|
||||
<string name="menu_import_old">Import Legado data</string>
|
||||
<string name="mkdirs">Create a subfolder</string>
|
||||
<string name="mkdirs_description">Create a folder named Legado as the backup folder.</string>
|
||||
<string name="webdav_cache_backup">Offline cache book backup</string>
|
||||
<string name="webdav_cache_backup_s">Export to local and back up to the exports directory under the legado folder</string>
|
||||
<string name="backup_path">Backup to</string>
|
||||
@@ -932,5 +930,6 @@
|
||||
<string name="less_than">小于</string>
|
||||
<string name="check_source_config_summary">校验超时: %1$s秒\n校验项目:%2$s</string>
|
||||
<string name="record_debug_log">记录调试日志</string>
|
||||
<string name="sub_dir">子文件夹</string>
|
||||
<!-- string end -->
|
||||
</resources>
|
||||
|
||||
@@ -9,8 +9,6 @@
|
||||
<string name="menu_backup">Início</string>
|
||||
<string name="menu_restore">Restaurar</string>
|
||||
<string name="menu_import_old">Importar dados ao Legado</string>
|
||||
<string name="mkdirs">Criar uma subpasta</string>
|
||||
<string name="mkdirs_description">Criar uma pasta de Backup com o nome Legado.</string>
|
||||
<string name="webdav_cache_backup">Backup do cache dos livros para leitura off-line</string>
|
||||
<string name="webdav_cache_backup_s">Exportar localmente e fazer Backup à pasta de exportação</string>
|
||||
<string name="backup_path">Backup para</string>
|
||||
@@ -930,5 +928,6 @@
|
||||
<string name="less_than">小于</string>
|
||||
<string name="check_source_config_summary">校验超时: %1$s秒\n校验项目:%2$s</string>
|
||||
<string name="record_debug_log">记录调试日志</string>
|
||||
<string name="sub_dir">子文件夹</string>
|
||||
<!-- string end -->
|
||||
</resources>
|
||||
|
||||
@@ -8,8 +8,6 @@
|
||||
<string name="menu_backup">Home</string>
|
||||
<string name="menu_restore">還原</string>
|
||||
<string name="menu_import_old">導入閲讀數據</string>
|
||||
<string name="mkdirs">創建子文件夾</string>
|
||||
<string name="mkdirs_description">創建 legado 文件夾作爲備份路徑</string>
|
||||
<string name="webdav_cache_backup">離線緩存書籍備份</string>
|
||||
<string name="webdav_cache_backup_s">導出本地同時備份到legado文件夾下exports目錄</string>
|
||||
<string name="backup_path">備份路徑</string>
|
||||
@@ -929,5 +927,6 @@
|
||||
<string name="less_than">小于</string>
|
||||
<string name="check_source_config_summary">校验超时: %1$s秒\n校验项目:%2$s</string>
|
||||
<string name="record_debug_log">记录调试日志</string>
|
||||
<string name="sub_dir">子文件夹</string>
|
||||
<!-- string end -->
|
||||
</resources>
|
||||
|
||||
@@ -8,8 +8,6 @@
|
||||
<string name="menu_backup">備份</string>
|
||||
<string name="menu_restore">復原</string>
|
||||
<string name="menu_import_old">匯入閱讀資料</string>
|
||||
<string name="mkdirs">建立子資料夾</string>
|
||||
<string name="mkdirs_description">建立legado資料夾作為備份資料夾</string>
|
||||
<string name="webdav_cache_backup">離線快取書籍備份</string>
|
||||
<string name="webdav_cache_backup_s">匯出本機同時備份到legado資料夾下exports目錄</string>
|
||||
<string name="backup_path">備份路徑</string>
|
||||
@@ -931,5 +929,6 @@
|
||||
<string name="less_than">小于</string>
|
||||
<string name="check_source_config_summary">校验超时: %1$s秒\n校验项目:%2$s</string>
|
||||
<string name="record_debug_log">记录调试日志</string>
|
||||
<string name="sub_dir">子文件夹</string>
|
||||
<!-- string end -->
|
||||
</resources>
|
||||
|
||||
@@ -8,8 +8,6 @@
|
||||
<string name="menu_backup">Home</string>
|
||||
<string name="menu_restore">恢复</string>
|
||||
<string name="menu_import_old">导入阅读数据</string>
|
||||
<string name="mkdirs">创建子文件夹</string>
|
||||
<string name="mkdirs_description">创建legado文件夹作为备份文件夹</string>
|
||||
<string name="webdav_cache_backup">离线缓存书籍备份</string>
|
||||
<string name="webdav_cache_backup_s">导出本地同时备份到legado文件夹下exports目录</string>
|
||||
<string name="backup_path">备份路径</string>
|
||||
@@ -931,5 +929,6 @@
|
||||
<string name="less_than">小于</string>
|
||||
<string name="check_source_config_summary">校验超时: %1$s秒\n校验项目:%2$s</string>
|
||||
<string name="record_debug_log">记录调试日志</string>
|
||||
<string name="sub_dir">子文件夹</string>
|
||||
<!-- string end -->
|
||||
</resources>
|
||||
|
||||
@@ -9,8 +9,6 @@
|
||||
<string name="menu_backup">Home</string>
|
||||
<string name="menu_restore">Restore</string>
|
||||
<string name="menu_import_old">Import Legado data</string>
|
||||
<string name="mkdirs">Create a subfolder</string>
|
||||
<string name="mkdirs_description">Create a folder named Legado as the backup folder.</string>
|
||||
<string name="webdav_cache_backup">Offline cache book backup</string>
|
||||
<string name="webdav_cache_backup_s">Export to local and back up to the exports directory under the legado folder</string>
|
||||
<string name="backup_path">Backup to</string>
|
||||
@@ -932,5 +930,6 @@
|
||||
<string name="less_than">小于</string>
|
||||
<string name="check_source_config_summary">校验超时: %1$s秒\n校验项目:%2$s</string>
|
||||
<string name="record_debug_log">记录调试日志</string>
|
||||
<string name="sub_dir">Sub dir</string>
|
||||
<!-- string end -->
|
||||
</resources>
|
||||
|
||||
@@ -27,11 +27,9 @@
|
||||
android:summary="@string/web_dav_pw_s"
|
||||
app:iconSpaceReserved="false" />
|
||||
|
||||
<io.legado.app.ui.widget.prefs.SwitchPreference
|
||||
android:key="webDavCreateDir"
|
||||
android:defaultValue="true"
|
||||
android:title="@string/mkdirs"
|
||||
android:summary="@string/mkdirs_description"
|
||||
<io.legado.app.ui.widget.prefs.EditTextPreference
|
||||
android:key="webDavDir"
|
||||
android:title="@string/sub_dir"
|
||||
app:allowDividerAbove="false"
|
||||
app:allowDividerBelow="false"
|
||||
app:iconSpaceReserved="false" />
|
||||
|
||||
@@ -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'
|
||||
|
||||
Reference in New Issue
Block a user