From 8eb48817678fe83e964b39100135beeda86ed213 Mon Sep 17 00:00:00 2001 From: HapeLee <1321903405@qq.com> Date: Mon, 1 Sep 2025 00:57:17 +0800 Subject: [PATCH] =?UTF-8?q?=E5=90=88=E5=B9=B6=E4=B8=8A=E6=B8=B8=E6=9B=B4?= =?UTF-8?q?=E6=94=B9=20=E6=84=9F=E8=B0=A2=EF=BC=9A@821938089=E3=80=81@zhon?= =?UTF-8?q?gerxll=E3=80=81@youke2580c=E3=80=81@Leroy-X=E3=80=81@Dark-Tide?= =?UTF-8?q?=E3=80=81@lhjgege=E3=80=81@3harmony=E3=80=81@mgz0227?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/assets/web/help/md/jsHelp.md | 15 ++- app/src/main/java/io/legato/kazusa/App.kt | 4 + .../java/io/legato/kazusa/base/BaseService.kt | 6 +- .../io/legato/kazusa/constant/AppPattern.kt | 3 +- .../io/legato/kazusa/constant/EventBus.kt | 1 + .../io/legato/kazusa/constant/PreferKey.kt | 5 +- .../java/io/legato/kazusa/data/dao/BookDao.kt | 7 ++ .../legato/kazusa/data/entities/BaseSource.kt | 3 +- .../io/legato/kazusa/help/DirectLinkUpload.kt | 2 +- .../io/legato/kazusa/help/JsExtensions.kt | 1 + .../io/legato/kazusa/help/book/BookHelp.kt | 10 +- .../io/legato/kazusa/help/config/AppConfig.kt | 3 + .../kazusa/help/config/ReadBookConfig.kt | 109 ++++++++---------- .../legato/kazusa/help/config/ThemeConfig.kt | 63 ++++++---- .../kazusa/help/glide/BlurTransformation.kt | 2 +- .../kazusa/help/http/DecompressInterceptor.kt | 3 +- .../io/legato/kazusa/help/http/HttpHelper.kt | 2 +- .../kazusa/help/http/ObsoleteUrlFactory.kt | 14 ++- .../help/source/SourceVerificationHelp.kt | 7 +- .../io/legato/kazusa/help/storage/Restore.kt | 9 +- .../legato/kazusa/lib/cronet/AbsCallBack.kt | 34 ++++-- .../io/legato/kazusa/lib/mobi/MobiBook.kt | 2 +- .../lib/mobi/utils/BitwiseExtensions.kt | 7 ++ .../lib/mobi/utils/ByteBufferExtensions.kt | 1 - .../io/legato/kazusa/lib/webdav/WebDav.kt | 21 +++- .../java/io/legato/kazusa/model/AudioPlay.kt | 53 ++++++++- .../java/io/legato/kazusa/model/BookCover.kt | 4 +- .../java/io/legato/kazusa/model/CacheBook.kt | 15 +-- .../java/io/legato/kazusa/model/ReadBook.kt | 54 +++++---- .../java/io/legato/kazusa/model/ReadManga.kt | 30 ++--- .../kazusa/model/analyzeRule/AnalyzeUrl.kt | 45 ++++---- .../kazusa/model/localBook/LocalBook.kt | 13 ++- .../legato/kazusa/model/localBook/MobiFile.kt | 2 +- .../legato/kazusa/model/localBook/TextFile.kt | 4 + .../kazusa/model/webBook/SearchModel.kt | 13 ++- .../kazusa/service/BaseReadAloudService.kt | 86 +++++++++++--- .../legato/kazusa/service/CacheBookService.kt | 1 - .../kazusa/service/HttpReadAloudService.kt | 19 +-- .../kazusa/service/TTSReadAloudService.kt | 8 +- .../ui/association/OnLineImportViewModel.kt | 2 +- .../kazusa/ui/book/audio/AudioPlayActivity.kt | 13 +++ .../ui/book/audio/AudioPlayViewModel.kt | 4 +- .../kazusa/ui/book/info/BookInfoActivity.kt | 2 +- .../kazusa/ui/book/info/BookInfoViewModel.kt | 2 +- .../book/info/edit/BookInfoEditViewModel.kt | 8 ++ .../ui/book/manga/ReadMangaViewModel.kt | 2 +- .../kazusa/ui/book/read/ReadBookActivity.kt | 7 +- .../kazusa/ui/book/read/ReadBookViewModel.kt | 2 +- .../ui/book/read/config/BgTextConfigDialog.kt | 15 +-- .../read/config/ClickActionConfigDialog.kt | 3 +- .../ui/book/read/config/MoreConfigDialog.kt | 4 + .../kazusa/ui/book/read/page/AutoPager.kt | 59 +++++++--- .../kazusa/ui/book/read/page/PageView.kt | 38 +++++- .../kazusa/ui/book/read/page/ReadView.kt | 8 ++ .../ui/book/read/page/entities/TextLine.kt | 9 ++ .../read/page/provider/ChapterProvider.kt | 48 +++++--- .../read/page/provider/TextChapterLayout.kt | 21 ++-- .../kazusa/ui/book/search/SearchActivity.kt | 40 ++++--- .../kazusa/ui/book/search/SearchViewModel.kt | 8 ++ .../ui/book/searchContent/SearchResult.kt | 36 ++++-- .../io/legato/kazusa/ui/dict/DictDialog.kt | 11 ++ .../kazusa/ui/login/SourceLoginActivity.kt | 6 +- .../kazusa/ui/login/SourceLoginViewModel.kt | 8 +- .../io/legato/kazusa/ui/main/MainViewModel.kt | 2 +- .../kazusa/ui/replace/ReplaceRuleActivity.kt | 7 +- .../ui/rss/source/manage/RssSourceActivity.kt | 4 +- .../legato/kazusa/utils/ContextExtensions.kt | 6 +- .../legato/kazusa/utils/FragmentExtensions.kt | 3 +- .../io/legato/kazusa/utils/HtmlFormatter.kt | 2 +- .../io/legato/kazusa/utils/RomExtensions.kt | 7 -- .../drawable/ic_play_mode_list_end_stop.xml | 11 ++ .../res/drawable/ic_play_mode_list_loop.xml | 11 ++ .../main/res/drawable/ic_play_mode_random.xml | 11 ++ .../main/res/layout/activity_audio_play.xml | 14 +++ app/src/main/res/layout/dialog_auto_read.xml | 2 +- app/src/main/res/layout/view_book_page.xml | 5 - app/src/main/res/values-es-rES/strings.xml | 2 +- app/src/main/res/values-ja-rJP/strings.xml | 2 +- app/src/main/res/values-pt-rBR/strings.xml | 2 +- app/src/main/res/values-vi/arrays.xml | 2 +- app/src/main/res/values-zh-rHK/strings.xml | 1 + app/src/main/res/values-zh-rTW/strings.xml | 1 + app/src/main/res/values-zh/strings.xml | 7 +- app/src/main/res/values/strings.xml | 6 +- app/src/main/res/xml/pref_config_aloud.xml | 7 ++ app/src/main/res/xml/pref_config_read.xml | 11 +- gradle/libs.versions.toml | 2 +- 87 files changed, 800 insertions(+), 365 deletions(-) create mode 100644 app/src/main/java/io/legato/kazusa/lib/mobi/utils/BitwiseExtensions.kt delete mode 100644 app/src/main/java/io/legato/kazusa/utils/RomExtensions.kt create mode 100644 app/src/main/res/drawable/ic_play_mode_list_end_stop.xml create mode 100644 app/src/main/res/drawable/ic_play_mode_list_loop.xml create mode 100644 app/src/main/res/drawable/ic_play_mode_random.xml diff --git a/app/src/main/assets/web/help/md/jsHelp.md b/app/src/main/assets/web/help/md/jsHelp.md index e4d9219f2..6354b2a1a 100644 --- a/app/src/main/assets/web/help/md/jsHelp.md +++ b/app/src/main/assets/web/help/md/jsHelp.md @@ -45,7 +45,7 @@ * 调用阅读搜索 ```js -java.searchBook(bookName: string) +java.searchBook(bookName: String) ``` * 添加书架 @@ -232,6 +232,13 @@ java.s2t(text: String): String * 时间格式化 ```js java.timeFormatUTC(time: Long, format: String, sh: Int): String? +java.timeFormat(time: Long): String +``` + +* html格式化 + +```js +java.htmlFormat(str: String): String ``` * 文件 > 所有对于文件的读写删操作都是相对路径,只能操作阅读缓存/android/data/{package}/cache/内的文件 @@ -441,9 +448,11 @@ cache.putFile(key: String, value: String, saveTime: Int) 读取文件内容 cache.getFile(key: String): String? 保存到内存 -cache.deleteMemory(key: String) -cache.getFromMemory(key: String): Any? cache.putMemory(key: String, value: Any) +读取内存 +cache.getFromMemory(key: String): Any? +删除内存 +cache.deleteMemory(key: String) ``` diff --git a/app/src/main/java/io/legato/kazusa/App.kt b/app/src/main/java/io/legato/kazusa/App.kt index 19a342257..155e762e0 100644 --- a/app/src/main/java/io/legato/kazusa/App.kt +++ b/app/src/main/java/io/legato/kazusa/App.kt @@ -7,6 +7,7 @@ import android.app.NotificationManager import android.content.Context import android.content.pm.ApplicationInfo import android.content.res.Configuration +import android.os.Build import com.github.liuyueyi.quick.transfer.constants.TransType import com.google.android.material.color.DynamicColors import com.jeremyliao.liveeventbus.LiveEventBus @@ -141,6 +142,9 @@ class App : Application() { * @return */ private fun installGmsTlsProvider(context: Context) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + return + } try { val gmsPackageName = "com.google.android.gms" val appInfo = packageManager.getApplicationInfo(gmsPackageName, 0) diff --git a/app/src/main/java/io/legato/kazusa/base/BaseService.kt b/app/src/main/java/io/legato/kazusa/base/BaseService.kt index 411fd4eb3..8d3bc52d7 100644 --- a/app/src/main/java/io/legato/kazusa/base/BaseService.kt +++ b/app/src/main/java/io/legato/kazusa/base/BaseService.kt @@ -75,9 +75,9 @@ abstract class BaseService : LifecycleService() { } @CallSuper - override fun onTimeout(startId: Int) { - super.onTimeout(startId) - LogUtils.d(simpleName, "onTimeout startId:$startId") + override fun onTimeout(startId: Int, fgsType: Int) { + super.onTimeout(startId, fgsType) + LogUtils.d(simpleName, "onTimeout startId:$startId fgsType:$fgsType") stopSelf() } diff --git a/app/src/main/java/io/legato/kazusa/constant/AppPattern.kt b/app/src/main/java/io/legato/kazusa/constant/AppPattern.kt index 074e545a7..bd73ed6f1 100644 --- a/app/src/main/java/io/legato/kazusa/constant/AppPattern.kt +++ b/app/src/main/java/io/legato/kazusa/constant/AppPattern.kt @@ -9,7 +9,8 @@ object AppPattern { val EXP_PATTERN: Pattern = Pattern.compile("\\{\\{([\\w\\W]*?)\\}\\}") //匹配格式化后的图片格式 - val imgPattern: Pattern = Pattern.compile("]*src=\"([^\"]*(?:\"[^>]+\\})?)\"[^>]*>") + val imgPattern: Pattern = + Pattern.compile("]*src=['\"]([^'\"]*(?:['\"][^>]+\\})?)['\"][^>]*>") //dataURL图片类型 val dataUriRegex = Regex("^data:.*?;base64,(.*)") diff --git a/app/src/main/java/io/legato/kazusa/constant/EventBus.kt b/app/src/main/java/io/legato/kazusa/constant/EventBus.kt index 6b6c4cb89..f23fe405a 100644 --- a/app/src/main/java/io/legato/kazusa/constant/EventBus.kt +++ b/app/src/main/java/io/legato/kazusa/constant/EventBus.kt @@ -32,4 +32,5 @@ object EventBus { const val READ_ALOUD_PLAY = "readAloudPlay" const val EXPORT_BOOK = "exportBook" const val UP_MANGA_CONFIG = "upMangaConfig" + const val PLAY_MODE_CHANGED = "playModeChanged" } \ No newline at end of file diff --git a/app/src/main/java/io/legato/kazusa/constant/PreferKey.kt b/app/src/main/java/io/legato/kazusa/constant/PreferKey.kt index df20ed725..b2e8d8bd4 100644 --- a/app/src/main/java/io/legato/kazusa/constant/PreferKey.kt +++ b/app/src/main/java/io/legato/kazusa/constant/PreferKey.kt @@ -170,14 +170,12 @@ object PreferKey { const val showMangaUi = "showMangaUi" const val disableMangaScale = "disableMangaScale" const val firebaseEnabled = "firebaseEnabled" - const val cPrimary = "colorPrimary" const val cAccent = "colorAccent" const val cBackground = "colorBackground" const val cBBackground = "colorBottomBackground" const val bgImage = "backgroundImage" const val bgImageBlurring = "backgroundImageBlurring" - const val cNPrimary = "colorPrimaryNight" const val cNAccent = "colorAccentNight" const val cNBackground = "colorBackgroundNight" @@ -187,7 +185,6 @@ object PreferKey { const val showReadTitleAddition = "showReadTitleAddition" const val readBarStyleFollowPage = "readBarStyleFollowPage" const val contentSelectSpeakMod = "contentReadAloudMod" - const val themePref = "themePref" const val bookshelfSortOrder = "bookshelfSortOrder" const val showBottomView = "showBottomView" @@ -208,5 +205,5 @@ object PreferKey { const val permissionChecked = "permissionChecked" const val notificationsPost = "notificationsPost" const val ignoreBatteryPermission = "ignoreBatteryPermission" - + const val paddingDisplayCutouts = "paddingDisplayCutouts" } diff --git a/app/src/main/java/io/legato/kazusa/data/dao/BookDao.kt b/app/src/main/java/io/legato/kazusa/data/dao/BookDao.kt index 98e86f182..36029ac27 100644 --- a/app/src/main/java/io/legato/kazusa/data/dao/BookDao.kt +++ b/app/src/main/java/io/legato/kazusa/data/dao/BookDao.kt @@ -5,6 +5,7 @@ import androidx.room.Delete import androidx.room.Insert import androidx.room.OnConflictStrategy import androidx.room.Query +import androidx.room.Transaction import androidx.room.Update import io.legato.kazusa.constant.BookType import io.legato.kazusa.data.entities.Book @@ -150,6 +151,12 @@ interface BookDao { @Delete fun delete(vararg book: Book) + @Transaction + fun replace(oldBook: Book, newBook: Book) { + delete(oldBook) + insert(newBook) + } + @Query("update books set durChapterPos = :pos where bookUrl = :bookUrl") fun upProgress(bookUrl: String, pos: Int) diff --git a/app/src/main/java/io/legato/kazusa/data/entities/BaseSource.kt b/app/src/main/java/io/legato/kazusa/data/entities/BaseSource.kt index 5a3203bf7..baa245b98 100644 --- a/app/src/main/java/io/legato/kazusa/data/entities/BaseSource.kt +++ b/app/src/main/java/io/legato/kazusa/data/entities/BaseSource.kt @@ -13,7 +13,6 @@ import io.legato.kazusa.help.config.AppConfig import io.legato.kazusa.help.crypto.SymmetricCryptoAndroid import io.legato.kazusa.help.http.CookieStore import io.legato.kazusa.help.source.getShareScope -import io.legato.kazusa.model.Debug import io.legato.kazusa.utils.GSON import io.legato.kazusa.utils.GSONStrict import io.legato.kazusa.utils.fromJsonArray @@ -116,7 +115,7 @@ interface BaseSource : JsExtensions { GSONStrict.fromJsonObject>(json).getOrNull()?.let { map -> putAll(map) } ?: GSON.fromJsonObject>(json).getOrNull()?.let { map -> - Debug.log("≡请求头规则 JSON 格式不规范,请改为规范格式") + log("请求头规则 JSON 格式不规范,请改为规范格式") putAll(map) } } catch (e: Exception) { diff --git a/app/src/main/java/io/legato/kazusa/help/DirectLinkUpload.kt b/app/src/main/java/io/legato/kazusa/help/DirectLinkUpload.kt index 53df732e4..02b277618 100644 --- a/app/src/main/java/io/legato/kazusa/help/DirectLinkUpload.kt +++ b/app/src/main/java/io/legato/kazusa/help/DirectLinkUpload.kt @@ -101,7 +101,7 @@ object DirectLinkUpload { @Keep data class Rule( - var uploadUrl: String, //上传url + var uploadUrl: String, //创建分享链接 var downloadUrlRule: String, //下载链接规则 var summary: String, //注释 var compress: Boolean = false, //是否压缩 diff --git a/app/src/main/java/io/legato/kazusa/help/JsExtensions.kt b/app/src/main/java/io/legato/kazusa/help/JsExtensions.kt index 17faefa43..c69a4367e 100644 --- a/app/src/main/java/io/legato/kazusa/help/JsExtensions.kt +++ b/app/src/main/java/io/legato/kazusa/help/JsExtensions.kt @@ -988,6 +988,7 @@ interface JsExtensions : JsEncodeUtils { // 新增 mimeType 参数,默认为 null(保持兼容性) fun openUrl(url: String, mimeType: String? = null) { + require(url.length < 64 * 1024) { "openUrl parameter url too long" } rhinoContext.ensureActive() val source = getSource() ?: throw NoStackTraceException("openUrl source cannot be null") appCtx.startActivity { diff --git a/app/src/main/java/io/legato/kazusa/help/book/BookHelp.kt b/app/src/main/java/io/legato/kazusa/help/book/BookHelp.kt index c1f17e250..85d7a5a63 100644 --- a/app/src/main/java/io/legato/kazusa/help/book/BookHelp.kt +++ b/app/src/main/java/io/legato/kazusa/help/book/BookHelp.kt @@ -3,6 +3,7 @@ package io.legato.kazusa.help.book import android.graphics.BitmapFactory import android.os.ParcelFileDescriptor import androidx.documentfile.provider.DocumentFile +import com.script.rhino.runScriptWithContext import io.legato.kazusa.constant.AppLog import io.legato.kazusa.constant.AppPattern import io.legato.kazusa.constant.EventBus @@ -28,7 +29,6 @@ import io.legato.kazusa.utils.getFile import io.legato.kazusa.utils.isContentScheme import io.legato.kazusa.utils.onEachParallel import io.legato.kazusa.utils.postEvent -import com.script.rhino.runScriptWithContext import kotlinx.coroutines.Dispatchers.IO import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.ensureActive @@ -157,7 +157,7 @@ object BookHelp { } } - suspend fun saveContent( + fun saveContent( bookSource: BookSource, book: Book, bookChapter: BookChapter, @@ -404,7 +404,11 @@ object BookHelp { bookChapter.getFileName() ) if (file.exists()) { - return file.readText() + val string = file.readText() + if (string.isEmpty()) { + return null + } + return string } if (book.isLocal) { val string = LocalBook.getContent(book, bookChapter) diff --git a/app/src/main/java/io/legato/kazusa/help/config/AppConfig.kt b/app/src/main/java/io/legato/kazusa/help/config/AppConfig.kt index dcd67400c..ef3e31b33 100644 --- a/app/src/main/java/io/legato/kazusa/help/config/AppConfig.kt +++ b/app/src/main/java/io/legato/kazusa/help/config/AppConfig.kt @@ -570,6 +570,9 @@ object AppConfig : SharedPreferences.OnSharedPreferenceChangeListener { val mouseWheelPage get() = appCtx.getPrefBoolean(PreferKey.mouseWheelPage, true) + val paddingDisplayCutouts + get() = appCtx.getPrefBoolean(PreferKey.paddingDisplayCutouts, false) + var searchScope: String get() = appCtx.getPrefString("searchScope") ?: "" set(value) { diff --git a/app/src/main/java/io/legato/kazusa/help/config/ReadBookConfig.kt b/app/src/main/java/io/legato/kazusa/help/config/ReadBookConfig.kt index 3d3dba788..f6dff589b 100644 --- a/app/src/main/java/io/legato/kazusa/help/config/ReadBookConfig.kt +++ b/app/src/main/java/io/legato/kazusa/help/config/ReadBookConfig.kt @@ -5,8 +5,8 @@ import android.graphics.drawable.BitmapDrawable import android.graphics.drawable.ColorDrawable import android.graphics.drawable.Drawable import androidx.annotation.Keep +import androidx.core.graphics.toColorInt import com.google.android.material.color.MaterialColors -import io.legato.kazusa.R import io.legato.kazusa.constant.AppLog import io.legato.kazusa.constant.PageAnim import io.legato.kazusa.constant.PreferKey @@ -30,8 +30,6 @@ import io.legato.kazusa.utils.printOnDebug import io.legato.kazusa.utils.putPrefBoolean import io.legato.kazusa.utils.putPrefInt import io.legato.kazusa.utils.resizeAndRecycle -import kotlinx.coroutines.Dispatchers.IO -import kotlinx.coroutines.withContext import splitties.init.appCtx import java.io.File @@ -439,66 +437,55 @@ object ReadBookConfig { return exportConfig } - suspend fun import(byteArray: ByteArray): Result { - return kotlin.runCatching { - withContext(IO) { - val configZipPath = FileUtils.getPath(appCtx.externalCache, "readConfig.zip") - FileUtils.delete(configZipPath) - val zipFile = FileUtils.createFileIfNotExist(configZipPath) - zipFile.writeBytes(byteArray) - val configDir = appCtx.externalCache.getFile("readConfig") - configDir.createFolderReplace() - ZipUtils.unZipToPath(zipFile, configDir) - val configFile = configDir.getFile(configFileName) - val config: Config = GSON.fromJsonObject(configFile.readText()).getOrThrow() - if (config.textFont.isNotEmpty()) { - val fontName = FileUtils.getName(config.textFont) - val fontPath = - FileUtils.getPath(appCtx.externalFiles, "font", fontName) - if (!FileUtils.exist(fontPath)) { - configDir.getFile(fontName).copyTo(File(fontPath)) - } - config.textFont = fontPath - } - if (config.bgType == 2) { - val bgName = FileUtils.getName(config.bgStr) - config.bgStr = bgName - val bgPath = FileUtils.getPath(appCtx.externalFiles, "bg", bgName) - if (!FileUtils.exist(bgPath)) { - val bgFile = configDir.getFile(bgName) - if (bgFile.exists()) { - bgFile.copyTo(File(bgPath)) - } - } - config.bgStr = bgPath - } - if (config.bgTypeNight == 2) { - val bgName = FileUtils.getName(config.bgStrNight) - config.bgStrNight = bgName - val bgPath = FileUtils.getPath(appCtx.externalFiles, "bg", bgName) - if (!FileUtils.exist(bgPath)) { - val bgFile = configDir.getFile(bgName) - if (bgFile.exists()) { - bgFile.copyTo(File(bgPath)) - } - } - config.bgStrNight = bgPath - } - if (config.bgTypeEInk == 2) { - val bgName = FileUtils.getName(config.bgStrEInk) - config.bgStrEInk = bgName - val bgPath = FileUtils.getPath(appCtx.externalFiles, "bg", bgName) - if (!FileUtils.exist(bgPath)) { - val bgFile = configDir.getFile(bgName) - if (bgFile.exists()) { - bgFile.copyTo(File(bgPath)) - } - } - config.bgStrEInk = bgPath - } - return@withContext config + fun import(byteArray: ByteArray): Config { + val configZipPath = FileUtils.getPath(appCtx.externalCache, "readConfig.zip") + FileUtils.delete(configZipPath) + val zipFile = FileUtils.createFileIfNotExist(configZipPath) + zipFile.writeBytes(byteArray) + val configDir = appCtx.externalCache.getFile("readConfig") + configDir.createFolderReplace() + ZipUtils.unZipToPath(zipFile, configDir) + val configFile = configDir.getFile(configFileName) + val config: Config = GSON.fromJsonObject(configFile.readText()).getOrThrow() + if (config.textFont.isNotEmpty()) { + val fontName = FileUtils.getName(config.textFont) + val fontPath = + FileUtils.getPath(appCtx.externalFiles, "font", fontName) + if (!FileUtils.exist(fontPath)) { + configDir.getFile(fontName).copyTo(File(fontPath)) } + config.textFont = fontPath } + if (config.bgType == 2) { + val bgName = FileUtils.getName(config.bgStr) + config.bgStr = bgName + val bgPath = FileUtils.getPath(appCtx.externalFiles, "bg", bgName) + if (!FileUtils.exist(bgPath)) { + val bgFile = configDir.getFile(bgName) + if (bgFile.exists()) { + bgFile.copyTo(File(bgPath)) + } + } + config.bgStrNight = bgPath + } else if (config.bgTypeNight == 0) { + config.bgStrNight.toColorInt() + } + if (config.bgTypeEInk == 2) { + val bgName = FileUtils.getName(config.bgStrEInk) + config.bgStrEInk = bgName + val bgPath = FileUtils.getPath(appCtx.externalFiles, "bg", bgName) + if (!FileUtils.exist(bgPath)) { + val bgFile = configDir.getFile(bgName) + if (bgFile.exists()) { + bgFile.copyTo(File(bgPath)) + } + } + config.bgStrEInk = bgPath + } else if (config.bgTypeEInk == 0) { + config.bgStrEInk.toColorInt() + } + config.curTextColor() + return config } @Keep diff --git a/app/src/main/java/io/legato/kazusa/help/config/ThemeConfig.kt b/app/src/main/java/io/legato/kazusa/help/config/ThemeConfig.kt index 49775e3f8..b52fe8929 100644 --- a/app/src/main/java/io/legato/kazusa/help/config/ThemeConfig.kt +++ b/app/src/main/java/io/legato/kazusa/help/config/ThemeConfig.kt @@ -2,19 +2,18 @@ package io.legato.kazusa.help.config import android.content.Context import android.graphics.Bitmap -import android.graphics.Color import android.util.DisplayMetrics import androidx.annotation.Keep import androidx.appcompat.app.AppCompatDelegate +import androidx.core.graphics.toColorInt import io.legato.kazusa.R +import io.legato.kazusa.constant.AppLog import io.legato.kazusa.constant.EventBus import io.legato.kazusa.constant.PreferKey import io.legato.kazusa.constant.Theme import io.legato.kazusa.help.DefaultData -import io.legato.kazusa.lib.theme.ThemeStore import io.legato.kazusa.model.BookCover import io.legato.kazusa.utils.BitmapUtils -import io.legato.kazusa.utils.ColorUtils import io.legato.kazusa.utils.FileUtils import io.legato.kazusa.utils.GSON import io.legato.kazusa.utils.fromJsonArray @@ -29,7 +28,6 @@ import io.legato.kazusa.utils.putPrefInt import io.legato.kazusa.utils.stackBlur import splitties.init.appCtx import java.io.File -import androidx.core.graphics.toColorInt @Keep object ThemeConfig { @@ -117,13 +115,18 @@ object ThemeConfig { fun addConfig(json: String): Boolean { GSON.fromJsonObject(json.trim { it < ' ' }).getOrNull() ?.let { - addConfig(it) - return true + if (validateConfig(it)) { + addConfig(it) + return true + } } return false } fun addConfig(newConfig: Config) { + if (!validateConfig(newConfig)) { + return + } configList.forEachIndexed { index, config -> if (newConfig.themeName == config.themeName) { configList[index] = newConfig @@ -134,6 +137,18 @@ object ThemeConfig { save() } + private fun validateConfig(config: Config): Boolean { + try { + config.primaryColor.toColorInt() + config.accentColor.toColorInt() + config.backgroundColor.toColorInt() + config.bottomBackground.toColorInt() + return true + } catch (_: Exception) { + return false + } + } + private fun getConfigs(): List? { val configFile = File(configFilePath) if (configFile.exists()) { @@ -148,23 +163,27 @@ object ThemeConfig { } fun applyConfig(context: Context, config: Config) { - val primary = config.primaryColor.toColorInt() - val accent = config.accentColor.toColorInt() - val background = config.backgroundColor.toColorInt() - val bBackground = config.bottomBackground.toColorInt() - if (config.isNightTheme) { - context.putPrefInt(PreferKey.cNPrimary, primary) - context.putPrefInt(PreferKey.cNAccent, accent) - context.putPrefInt(PreferKey.cNBackground, background) - context.putPrefInt(PreferKey.cNBBackground, bBackground) - } else { - context.putPrefInt(PreferKey.cPrimary, primary) - context.putPrefInt(PreferKey.cAccent, accent) - context.putPrefInt(PreferKey.cBackground, background) - context.putPrefInt(PreferKey.cBBackground, bBackground) + try { + val primary = config.primaryColor.toColorInt() + val accent = config.accentColor.toColorInt() + val background = config.backgroundColor.toColorInt() + val bBackground = config.bottomBackground.toColorInt() + if (config.isNightTheme) { + context.putPrefInt(PreferKey.cNPrimary, primary) + context.putPrefInt(PreferKey.cNAccent, accent) + context.putPrefInt(PreferKey.cNBackground, background) + context.putPrefInt(PreferKey.cNBBackground, bBackground) + } else { + context.putPrefInt(PreferKey.cPrimary, primary) + context.putPrefInt(PreferKey.cAccent, accent) + context.putPrefInt(PreferKey.cBackground, background) + context.putPrefInt(PreferKey.cBBackground, bBackground) + } + AppConfig.isNightTheme = config.isNightTheme + applyDayNight(context) + } catch (e: Exception) { + AppLog.put("设置主题出错\n$e", e, true) } - AppConfig.isNightTheme = config.isNightTheme - applyDayNight(context) } fun saveDayTheme(context: Context, name: String) { diff --git a/app/src/main/java/io/legato/kazusa/help/glide/BlurTransformation.kt b/app/src/main/java/io/legato/kazusa/help/glide/BlurTransformation.kt index ff28f0dcb..09a1bff1d 100644 --- a/app/src/main/java/io/legato/kazusa/help/glide/BlurTransformation.kt +++ b/app/src/main/java/io/legato/kazusa/help/glide/BlurTransformation.kt @@ -12,7 +12,7 @@ import java.security.MessageDigest * @radius: 0..25 */ class BlurTransformation( - @IntRange(from = 0, to = 25) private val radius: Int + @param:IntRange(from = 0, to = 25) private val radius: Int ) : BitmapTransformation() { override fun transform( diff --git a/app/src/main/java/io/legato/kazusa/help/http/DecompressInterceptor.kt b/app/src/main/java/io/legato/kazusa/help/http/DecompressInterceptor.kt index 66414e0a2..46e14fcd6 100644 --- a/app/src/main/java/io/legato/kazusa/help/http/DecompressInterceptor.kt +++ b/app/src/main/java/io/legato/kazusa/help/http/DecompressInterceptor.kt @@ -2,6 +2,7 @@ package io.legato.kazusa.help.http import okhttp3.Interceptor import okhttp3.Response +import okhttp3.ResponseBody import okhttp3.ResponseBody.Companion.asResponseBody import okhttp3.internal.http.promisesBody import okio.buffer @@ -24,7 +25,7 @@ object DecompressInterceptor : Interceptor { val response = chain.proceed(requestBuilder.build()) val body = response.body - if (!transparentDecompress || !response.promisesBody() || body == null) { + if (!transparentDecompress || !response.promisesBody() || body == ResponseBody.EMPTY) { return response } diff --git a/app/src/main/java/io/legato/kazusa/help/http/HttpHelper.kt b/app/src/main/java/io/legato/kazusa/help/http/HttpHelper.kt index f4b86aafd..78e0db449 100644 --- a/app/src/main/java/io/legato/kazusa/help/http/HttpHelper.kt +++ b/app/src/main/java/io/legato/kazusa/help/http/HttpHelper.kt @@ -127,7 +127,7 @@ val okHttpClientManga by lazy { val response = chain.proceed(request) val url = request.url.toString() response.newBuilder() - .body(ProgressResponseBody(url, LISTENER, response.body!!)) + .body(ProgressResponseBody(url, LISTENER, response.body)) .build() } interceptors.add(1) { chain -> diff --git a/app/src/main/java/io/legato/kazusa/help/http/ObsoleteUrlFactory.kt b/app/src/main/java/io/legato/kazusa/help/http/ObsoleteUrlFactory.kt index da2864103..f6f57334d 100644 --- a/app/src/main/java/io/legato/kazusa/help/http/ObsoleteUrlFactory.kt +++ b/app/src/main/java/io/legato/kazusa/help/http/ObsoleteUrlFactory.kt @@ -18,8 +18,6 @@ import okhttp3.Protocol import okhttp3.Request import okhttp3.RequestBody import okhttp3.Response -import okhttp3.internal.notifyAll -import okhttp3.internal.wait import okio.Buffer import okio.BufferedSink import okio.Pipe @@ -186,7 +184,7 @@ class ObsoleteUrlFactory(private var client: OkHttpClient) : URLStreamHandlerFac return try { val response = getResponse(true) if (hasBody(response) && response.code >= HTTP_BAD_REQUEST) { - response.body!!.byteStream() + response.body.byteStream() } else null } catch (e: IOException) { null @@ -258,7 +256,7 @@ class ObsoleteUrlFactory(private var client: OkHttpClient) : URLStreamHandlerFac } val response = getResponse(false) if (response.code >= HTTP_BAD_REQUEST) throw FileNotFoundException(url.toString()) - return response.body!!.byteStream() + return response.body.byteStream() } @Throws(IOException::class) @@ -1174,6 +1172,14 @@ class ObsoleteUrlFactory(private var client: OkHttpClient) : URLStreamHandlerFac throw AssertionError() } + @Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN", "NOTHING_TO_INLINE") + private inline fun Any.wait() = (this as Object).wait() + + @Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN", "NOTHING_TO_INLINE") + private inline fun Any.notify() = (this as Object).notify() + + @Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN", "NOTHING_TO_INLINE") + private inline fun Any.notifyAll() = (this as Object).notifyAll() @Throws(Exception::class) @JvmStatic fun main(args: Array) { diff --git a/app/src/main/java/io/legato/kazusa/help/source/SourceVerificationHelp.kt b/app/src/main/java/io/legato/kazusa/help/source/SourceVerificationHelp.kt index 4167c462d..9e26aa522 100644 --- a/app/src/main/java/io/legato/kazusa/help/source/SourceVerificationHelp.kt +++ b/app/src/main/java/io/legato/kazusa/help/source/SourceVerificationHelp.kt @@ -29,6 +29,7 @@ object SourceVerificationHelp { * 获取书源验证结果 * 图片验证码 防爬 滑动验证码 点击字符 等等 */ + @Synchronized fun getVerificationResult( source: BaseSource?, url: String, @@ -38,9 +39,8 @@ object SourceVerificationHelp { ): String { source ?: throw NoStackTraceException("getVerificationResult parameter source cannot be null") - if (isMainThread) { - error("getVerificationResult must be called on a background thread") - } + require(url.length < 64 * 1024) { "getVerificationResult parameter url too long" } + check(!isMainThread) { "getVerificationResult must be called on a background thread" } clearResult(source.getKey()) @@ -84,6 +84,7 @@ object SourceVerificationHelp { refetchAfterSuccess: Boolean? = true ) { source ?: throw NoStackTraceException("startBrowser parameter source cannot be null") + require(url.length < 64 * 1024) { "startBrowser parameter url too long" } appCtx.startActivity { putExtra("title", title) putExtra("url", url) diff --git a/app/src/main/java/io/legato/kazusa/help/storage/Restore.kt b/app/src/main/java/io/legato/kazusa/help/storage/Restore.kt index e8d4727bf..e9dce8d9e 100644 --- a/app/src/main/java/io/legato/kazusa/help/storage/Restore.kt +++ b/app/src/main/java/io/legato/kazusa/help/storage/Restore.kt @@ -1,6 +1,7 @@ package io.legato.kazusa.help.storage import android.content.Context +import android.database.sqlite.SQLiteConstraintException import android.net.Uri import androidx.core.content.edit import androidx.documentfile.provider.DocumentFile @@ -106,7 +107,6 @@ object Restore { .forEach { book -> book.coverUrl = LocalBook.getCoverPath(book) } - val updateBooks = arrayListOf() val newBooks = arrayListOf() val ignoreLocalBook = BackupConfig.ignoreLocalBook it.forEach { book -> @@ -114,12 +114,15 @@ object Restore { return@forEach } if (appDb.bookDao.has(book.bookUrl)) { - updateBooks.add(book) + try { + appDb.bookDao.update(book) + } catch (_: SQLiteConstraintException) { + appDb.bookDao.insert(book) + } } else { newBooks.add(book) } } - appDb.bookDao.update(*updateBooks.toTypedArray()) appDb.bookDao.insert(*newBooks.toTypedArray()) } fileToListT(path, "bookmark.json")?.let { diff --git a/app/src/main/java/io/legato/kazusa/lib/cronet/AbsCallBack.kt b/app/src/main/java/io/legato/kazusa/lib/cronet/AbsCallBack.kt index 2d8bb9a67..1e0ecf9fc 100644 --- a/app/src/main/java/io/legato/kazusa/lib/cronet/AbsCallBack.kt +++ b/app/src/main/java/io/legato/kazusa/lib/cronet/AbsCallBack.kt @@ -9,12 +9,19 @@ import io.legato.kazusa.utils.DebugLog import io.legato.kazusa.utils.asIOException import io.legato.kazusa.utils.splitNotBlank import kotlinx.coroutines.delay -import okhttp3.* +import okhttp3.Call +import okhttp3.Callback import okhttp3.EventListener +import okhttp3.Headers import okhttp3.MediaType.Companion.toMediaTypeOrNull +import okhttp3.Protocol +import okhttp3.Request +import okhttp3.Response +import okhttp3.ResponseBody import okhttp3.ResponseBody.Companion.asResponseBody +import okhttp3.internal.http.HTTP_PERM_REDIRECT +import okhttp3.internal.http.HTTP_TEMP_REDIRECT import okhttp3.internal.http.HttpMethod -import okhttp3.internal.http.StatusLine import okio.Buffer import okio.Source import okio.Timeout @@ -25,7 +32,7 @@ import org.chromium.net.UrlResponseInfo import java.io.IOException import java.net.ProtocolException import java.nio.ByteBuffer -import java.util.* +import java.util.Locale import java.util.concurrent.ArrayBlockingQueue import java.util.concurrent.TimeUnit import java.util.concurrent.atomic.AtomicBoolean @@ -98,9 +105,15 @@ abstract class AbsCallBack( followCount += 1 urlResponseInfoChain.add(info) val client = okHttpClient - if (originalRequest.url.isHttps && newLocationUrl.startsWith("http://") && client.followSslRedirects) { + if (originalRequest.url.isHttps + && newLocationUrl.startsWith("http://") + && client.followSslRedirects + ) { followRedirect = true - } else if (!originalRequest.url.isHttps && newLocationUrl.startsWith("https://") && client.followSslRedirects) { + } else if (!originalRequest.url.isHttps + && newLocationUrl.startsWith("https://") + && client.followSslRedirects + ) { followRedirect = true } else if (okHttpClient.followRedirects) { followRedirect = true @@ -315,7 +328,7 @@ abstract class AbsCallBack( contentLength, bodySource ) - } + } ?: ResponseBody.EMPTY return Response.Builder() .request(request) @@ -381,9 +394,12 @@ abstract class AbsCallBack( if (HttpMethod.permitsRequestBody(method)) { val responseCode = userResponse.code val maintainBody = HttpMethod.redirectsWithBody(method) || - responseCode == StatusLine.HTTP_PERM_REDIRECT || - responseCode == StatusLine.HTTP_TEMP_REDIRECT - if (HttpMethod.redirectsToGet(method) && responseCode != StatusLine.HTTP_PERM_REDIRECT && responseCode != StatusLine.HTTP_TEMP_REDIRECT) { + responseCode == HTTP_PERM_REDIRECT || + responseCode == HTTP_TEMP_REDIRECT + if (HttpMethod.redirectsToGet(method) + && responseCode != HTTP_PERM_REDIRECT + && responseCode != HTTP_TEMP_REDIRECT + ) { requestBuilder.method("GET", null) } else { val requestBody = if (maintainBody) userResponse.request.body else null diff --git a/app/src/main/java/io/legato/kazusa/lib/mobi/MobiBook.kt b/app/src/main/java/io/legato/kazusa/lib/mobi/MobiBook.kt index bcf294671..d4e82686e 100644 --- a/app/src/main/java/io/legato/kazusa/lib/mobi/MobiBook.kt +++ b/app/src/main/java/io/legato/kazusa/lib/mobi/MobiBook.kt @@ -16,11 +16,11 @@ import io.legato.kazusa.lib.mobi.entities.Ptagx import io.legato.kazusa.lib.mobi.entities.TOC import io.legato.kazusa.lib.mobi.entities.TagxHeader import io.legato.kazusa.lib.mobi.entities.TagxTag +import io.legato.kazusa.lib.mobi.utils.and import io.legato.kazusa.lib.mobi.utils.readString import io.legato.kazusa.lib.mobi.utils.readUInt16Array import io.legato.kazusa.lib.mobi.utils.readUInt32 import io.legato.kazusa.lib.mobi.utils.readUInt8 -import okhttp3.internal.and import java.io.ByteArrayInputStream import java.io.InputStream import java.nio.ByteBuffer diff --git a/app/src/main/java/io/legato/kazusa/lib/mobi/utils/BitwiseExtensions.kt b/app/src/main/java/io/legato/kazusa/lib/mobi/utils/BitwiseExtensions.kt new file mode 100644 index 000000000..ea4e1a1f2 --- /dev/null +++ b/app/src/main/java/io/legato/kazusa/lib/mobi/utils/BitwiseExtensions.kt @@ -0,0 +1,7 @@ +package io.legato.kazusa.lib.mobi.utils + +internal infix fun Byte.and(mask: Int): Int = toInt() and mask + +internal infix fun Short.and(mask: Int): Int = toInt() and mask + +internal infix fun Int.and(mask: Long): Long = toLong() and mask \ No newline at end of file diff --git a/app/src/main/java/io/legato/kazusa/lib/mobi/utils/ByteBufferExtensions.kt b/app/src/main/java/io/legato/kazusa/lib/mobi/utils/ByteBufferExtensions.kt index dafeff12b..d706d3711 100644 --- a/app/src/main/java/io/legato/kazusa/lib/mobi/utils/ByteBufferExtensions.kt +++ b/app/src/main/java/io/legato/kazusa/lib/mobi/utils/ByteBufferExtensions.kt @@ -1,6 +1,5 @@ package io.legato.kazusa.lib.mobi.utils -import okhttp3.internal.and import java.nio.ByteBuffer import java.nio.charset.Charset diff --git a/app/src/main/java/io/legato/kazusa/lib/webdav/WebDav.kt b/app/src/main/java/io/legato/kazusa/lib/webdav/WebDav.kt index dbe87f76e..c293314b0 100644 --- a/app/src/main/java/io/legato/kazusa/lib/webdav/WebDav.kt +++ b/app/src/main/java/io/legato/kazusa/lib/webdav/WebDav.kt @@ -18,6 +18,7 @@ import io.legato.kazusa.utils.toRequestBody import kotlinx.coroutines.Dispatchers.IO import kotlinx.coroutines.ensureActive import kotlinx.coroutines.withContext +import okhttp3.HttpUrl.Companion.toHttpUrl import okhttp3.Interceptor import okhttp3.MediaType.Companion.toMediaType import okhttp3.RequestBody.Companion.asRequestBody @@ -92,6 +93,7 @@ open class WebDav( .replace("+", "%20") .replace("%3A", ":") .replace("%2F", "/") + raw.toHttpUrl().toString() }.getOrNull() } private val webDavClient by lazy { @@ -112,7 +114,14 @@ open class WebDav( build() } } - val host: String? get() = url.host + private val host: String? + get() = url.host?.let { + if (it.startsWith("[")) { + it.substring(1, it.lastIndex) + } else { + it + } + } /** * 获取当前url文件信息 @@ -165,7 +174,7 @@ open class WebDav( method("PROPFIND", requestBody) }.apply { checkResult(this) - }.body?.text() + }.body.text() } /** @@ -397,8 +406,8 @@ open class WebDav( url(url) }.apply { checkResult(this) - }.body?.byteStream() - return byteStream ?: throw WebDavException("WebDav下载出错\nNull Exception") + }.body.byteStream() + return byteStream } /** @@ -425,7 +434,7 @@ open class WebDav( */ private fun checkResult(response: Response) { if (!response.isSuccessful) { - val body = response.body?.string() + val body = response.body.string() if (response.code == 401) { val headers = response.headers("WWW-Authenticate") val supportBasicAuth = headers.any { @@ -436,7 +445,7 @@ open class WebDav( } } - if (response.message.isNotBlank() || body.isNullOrBlank()) { + if (response.message.isNotBlank() || body.isBlank()) { throw WebDavException("${url}\n${response.code}:${response.message}") } val document = Jsoup.parse(body) diff --git a/app/src/main/java/io/legato/kazusa/model/AudioPlay.kt b/app/src/main/java/io/legato/kazusa/model/AudioPlay.kt index 807240ddb..03d260251 100644 --- a/app/src/main/java/io/legato/kazusa/model/AudioPlay.kt +++ b/app/src/main/java/io/legato/kazusa/model/AudioPlay.kt @@ -31,6 +31,22 @@ import splitties.init.appCtx @SuppressLint("StaticFieldLeak") @Suppress("unused") object AudioPlay : CoroutineScope by MainScope() { + + enum class PlayMode(val iconRes: Int) { + LIST_END_STOP(R.drawable.ic_play_mode_list_end_stop), + RANDOM(R.drawable.ic_play_mode_random), + LIST_LOOP(R.drawable.ic_play_mode_list_loop); + + fun next(): PlayMode { + return when (this) { + LIST_END_STOP -> RANDOM + RANDOM -> LIST_LOOP + LIST_LOOP -> LIST_END_STOP + } + } + } + + var playMode = PlayMode.LIST_END_STOP var status = Status.STOP private var activityContext: Context? = null private var serviceContext: Context? = null @@ -48,6 +64,11 @@ object AudioPlay : CoroutineScope by MainScope() { var bookSource: BookSource? = null val loadingChapters = arrayListOf() + fun changePlayMode() { + playMode = playMode.next() + postEvent(EventBus.PLAY_MODE_CHANGED, playMode) + } + fun upData(book: Book) { AudioPlay.book = book chapterSize = appDb.bookChapterDao.getChapterCount(book.bookUrl) @@ -259,12 +280,32 @@ object AudioPlay : CoroutineScope by MainScope() { fun next() { stopPlay() - if (durChapterIndex + 1 < simulatedChapterSize) { - durChapterIndex += 1 - durChapterPos = 0 - durPlayUrl = "" - saveRead() - loadPlayUrl() + when (playMode) { + PlayMode.LIST_END_STOP -> { + if (durChapterIndex + 1 < simulatedChapterSize) { + durChapterIndex += 1 + durChapterPos = 0 + durPlayUrl = "" + saveRead() + loadPlayUrl() + } + } + + PlayMode.RANDOM -> { + durChapterIndex = (0 until simulatedChapterSize).random() + durChapterPos = 0 + durPlayUrl = "" + saveRead() + loadPlayUrl() + } + + PlayMode.LIST_LOOP -> { + durChapterIndex = (durChapterIndex + 1) % simulatedChapterSize + durChapterPos = 0 + durPlayUrl = "" + saveRead() + loadPlayUrl() + } } } diff --git a/app/src/main/java/io/legato/kazusa/model/BookCover.kt b/app/src/main/java/io/legato/kazusa/model/BookCover.kt index 2feaca24e..de20def44 100644 --- a/app/src/main/java/io/legato/kazusa/model/BookCover.kt +++ b/app/src/main/java/io/legato/kazusa/model/BookCover.kt @@ -135,7 +135,6 @@ object BookCover { /** * 加载漫画图片 */ - @SuppressLint("CheckResult") fun loadManga( context: Context, path: String?, @@ -155,8 +154,9 @@ object BookCover { .skipMemoryCache(true).let { if (transformation != null) { it.transform(transformation) + } else { + it } - it } .transition(DrawableTransitionOptions.withCrossFade()) } diff --git a/app/src/main/java/io/legato/kazusa/model/CacheBook.kt b/app/src/main/java/io/legato/kazusa/model/CacheBook.kt index 3a215ff0f..4481c2ed3 100644 --- a/app/src/main/java/io/legato/kazusa/model/CacheBook.kt +++ b/app/src/main/java/io/legato/kazusa/model/CacheBook.kt @@ -11,6 +11,7 @@ import io.legato.kazusa.data.entities.BookSource import io.legato.kazusa.exception.ConcurrentException import io.legato.kazusa.help.book.BookHelp import io.legato.kazusa.help.book.isLocal +import io.legato.kazusa.help.coroutine.CompositeCoroutine import io.legato.kazusa.help.coroutine.Coroutine import io.legato.kazusa.model.webBook.WebBook import io.legato.kazusa.service.CacheBookService @@ -96,15 +97,11 @@ object CacheBook { } } - fun clear() { - successDownloadSet.clear() - errorDownloadMap.clear() - } - fun close() { cacheBookMap.forEach { it.value.stop() } cacheBookMap.clear() - clear() + successDownloadSet.clear() + errorDownloadMap.clear() } val downloadSummary: String @@ -146,6 +143,7 @@ object CacheBook { private val waitDownloadSet = linkedSetOf() private val onDownloadSet = linkedSetOf() + private val tasks = CompositeCoroutine() private var isStopped = false private var waitingRetry = false @@ -158,7 +156,7 @@ object CacheBook { @Synchronized fun isRun(): Boolean { - return waitDownloadSet.size > 0 || onDownloadSet.size > 0 + return waitDownloadSet.isNotEmpty() || onDownloadSet.isNotEmpty() } @Synchronized @@ -169,6 +167,7 @@ object CacheBook { @Synchronized fun stop() { waitDownloadSet.clear() + tasks.clear() isStopped = true postEvent(EventBus.UP_DOWNLOAD, book.bookUrl) } @@ -309,6 +308,8 @@ object CacheBook { onCancel(chapterIndex) }.onFinally { onFinally() + }.apply { + tasks.add(this) }.start() } diff --git a/app/src/main/java/io/legato/kazusa/model/ReadBook.kt b/app/src/main/java/io/legato/kazusa/model/ReadBook.kt index a0309b47c..b5c859f04 100644 --- a/app/src/main/java/io/legato/kazusa/model/ReadBook.kt +++ b/app/src/main/java/io/legato/kazusa/model/ReadBook.kt @@ -63,20 +63,8 @@ object ReadBook : CoroutineScope by MainScope() { var isLocalBook = true var chapterChanged = false var prevTextChapter: TextChapter? = null - set(value) { - field?.cancelLayout() - field = value - } var curTextChapter: TextChapter? = null - set(value) { - field?.cancelLayout() - field = value - } var nextTextChapter: TextChapter? = null - set(value) { - field?.cancelLayout() - field = value - } var bookSource: BookSource? = null var msg: String? = null private val loadingChapters = arrayListOf() @@ -222,6 +210,9 @@ object ReadBook : CoroutineScope by MainScope() { } fun clearTextChapter() { + prevTextChapter?.cancelLayout() + curTextChapter?.cancelLayout() + nextTextChapter?.cancelLayout() prevTextChapter = null curTextChapter = null nextTextChapter = null @@ -333,6 +324,7 @@ object ReadBook : CoroutineScope by MainScope() { if (durChapterIndex < simulatedChapterSize - 1) { durChapterPos = 0 durChapterIndex++ + prevTextChapter?.cancelLayout() prevTextChapter = curTextChapter curTextChapter = nextTextChapter nextTextChapter = null @@ -363,6 +355,7 @@ object ReadBook : CoroutineScope by MainScope() { if (durChapterIndex < simulatedChapterSize - 1) { durChapterPos = 0 durChapterIndex++ + prevTextChapter?.cancelLayout() prevTextChapter = curTextChapter curTextChapter = nextTextChapter nextTextChapter = null @@ -394,6 +387,7 @@ object ReadBook : CoroutineScope by MainScope() { if (durChapterIndex > 0) { durChapterPos = if (toLast) prevTextChapter?.lastReadLength ?: Int.MAX_VALUE else 0 durChapterIndex-- + nextTextChapter?.cancelLayout() nextTextChapter = curTextChapter curTextChapter = prevTextChapter prevTextChapter = null @@ -703,6 +697,7 @@ object ReadBook : CoroutineScope by MainScope() { ) when (val offset = chapter.index - durChapterIndex) { 0 -> { + curTextChapter?.cancelLayout() curTextChapter = textChapter callBack?.upMenuView() var available = false @@ -727,12 +722,14 @@ object ReadBook : CoroutineScope by MainScope() { } -1 -> { + prevTextChapter?.cancelLayout() prevTextChapter = textChapter textChapter.layoutChannel.receiveAsFlow().collect() if (upContent) callBack?.upContent(offset, resetPageOffset) } 1 -> { + nextTextChapter?.cancelLayout() nextTextChapter = textChapter for (page in textChapter.layoutChannel) { if (page.index > 1) { @@ -776,6 +773,7 @@ object ReadBook : CoroutineScope by MainScope() { ) when (val offset = chapter.index - durChapterIndex) { 0 -> { + curTextChapter?.cancelLayout() curTextChapter = textChapter callBack?.upMenuView() var available = false @@ -800,12 +798,14 @@ object ReadBook : CoroutineScope by MainScope() { } -1 -> { + prevTextChapter?.cancelLayout() prevTextChapter = textChapter textChapter.layoutChannel.receiveAsFlow().collect() if (upContent) callBack?.upContent(offset, resetPageOffset) } 1 -> { + nextTextChapter?.cancelLayout() nextTextChapter = textChapter for (page in textChapter.layoutChannel) { if (page.index > 1) { @@ -858,21 +858,25 @@ object ReadBook : CoroutineScope by MainScope() { fun saveRead(pageChanged: Boolean = false) { executor.execute { - val book = book ?: return@execute - book.lastCheckCount = 0 - book.durChapterTime = System.currentTimeMillis() - val chapterChanged = book.durChapterIndex != durChapterIndex - book.durChapterIndex = durChapterIndex - book.durChapterPos = durChapterPos - if (!pageChanged || chapterChanged) { - appDb.bookChapterDao.getChapter(book.bookUrl, durChapterIndex)?.let { - book.durChapterTitle = it.getDisplayTitle( - ContentProcessor.get(book.name, book.origin).getTitleReplaceRules(), - book.getUseReplaceRule() - ) + kotlin.runCatching { + val book = book ?: return@execute + book.lastCheckCount = 0 + book.durChapterTime = System.currentTimeMillis() + val chapterChanged = book.durChapterIndex != durChapterIndex + book.durChapterIndex = durChapterIndex + book.durChapterPos = durChapterPos + if (!pageChanged || chapterChanged) { + appDb.bookChapterDao.getChapter(book.bookUrl, durChapterIndex)?.let { + book.durChapterTitle = it.getDisplayTitle( + ContentProcessor.get(book.name, book.origin).getTitleReplaceRules(), + book.getUseReplaceRule() + ) + } } + appDb.bookDao.update(book) + }.onFailure { + AppLog.put("保存书籍阅读进度信息出错\n$it", it) } - appDb.bookDao.update(book) } } diff --git a/app/src/main/java/io/legato/kazusa/model/ReadManga.kt b/app/src/main/java/io/legato/kazusa/model/ReadManga.kt index ab6d8aa4a..c93553ea0 100644 --- a/app/src/main/java/io/legato/kazusa/model/ReadManga.kt +++ b/app/src/main/java/io/legato/kazusa/model/ReadManga.kt @@ -327,21 +327,25 @@ object ReadManga : CoroutineScope by MainScope() { fun saveRead(pageChanged: Boolean = false) { executor.execute { - val book = book ?: return@execute - book.lastCheckCount = 0 - book.durChapterTime = System.currentTimeMillis() - val chapterChanged = book.durChapterIndex != durChapterIndex - book.durChapterIndex = durChapterIndex - book.durChapterPos = durChapterPos - if (!pageChanged || chapterChanged) { - appDb.bookChapterDao.getChapter(book.bookUrl, durChapterIndex)?.let { - book.durChapterTitle = it.getDisplayTitle( - ContentProcessor.get(book.name, book.origin).getTitleReplaceRules(), - book.getUseReplaceRule() - ) + kotlin.runCatching { + val book = book ?: return@execute + book.lastCheckCount = 0 + book.durChapterTime = System.currentTimeMillis() + val chapterChanged = book.durChapterIndex != durChapterIndex + book.durChapterIndex = durChapterIndex + book.durChapterPos = durChapterPos + if (!pageChanged || chapterChanged) { + appDb.bookChapterDao.getChapter(book.bookUrl, durChapterIndex)?.let { + book.durChapterTitle = it.getDisplayTitle( + ContentProcessor.get(book.name, book.origin).getTitleReplaceRules(), + book.getUseReplaceRule() + ) + } } + appDb.bookDao.update(book) + }.onFailure { + AppLog.put("保存漫画阅读进度信息出错\n$it", it) } - appDb.bookDao.update(book) } } diff --git a/app/src/main/java/io/legato/kazusa/model/analyzeRule/AnalyzeUrl.kt b/app/src/main/java/io/legato/kazusa/model/analyzeRule/AnalyzeUrl.kt index 956d7c90a..f9d63cdee 100644 --- a/app/src/main/java/io/legato/kazusa/model/analyzeRule/AnalyzeUrl.kt +++ b/app/src/main/java/io/legato/kazusa/model/analyzeRule/AnalyzeUrl.kt @@ -39,7 +39,6 @@ import io.legato.kazusa.help.http.postForm import io.legato.kazusa.help.http.postJson import io.legato.kazusa.help.http.postMultipart import io.legato.kazusa.help.source.getShareScope -import io.legato.kazusa.model.Debug import io.legato.kazusa.utils.EncoderUtils import io.legato.kazusa.utils.GSON import io.legato.kazusa.utils.GSONStrict @@ -225,32 +224,32 @@ class AnalyzeUrl( if (urlOption == null) { urlOption = GSON.fromJsonObject(urlOptionStr).getOrNull() if (urlOption != null) { - Debug.log("≡链接参数 JSON 格式不规范,请改为规范格式") + log("链接参数 JSON 格式不规范,请改为规范格式") } } urlOption?.let { option -> - option.getMethod()?.let { - if (it.equals("POST", true)) method = RequestMethod.POST - } - option.getHeaderMap()?.forEach { entry -> - headerMap[entry.key.toString()] = entry.value.toString() - } - option.getBody()?.let { - body = it - } - type = option.getType() - charset = option.getCharset() - retry = option.getRetry() - useWebView = option.useWebView() - webJs = option.getWebJs() - option.getJs()?.let { jsStr -> - evalJS(jsStr, url)?.toString()?.let { - url = it - } + option.getMethod()?.let { + if (it.equals("POST", true)) method = RequestMethod.POST + } + option.getHeaderMap()?.forEach { entry -> + headerMap[entry.key.toString()] = entry.value.toString() + } + option.getBody()?.let { + body = it + } + type = option.getType() + charset = option.getCharset() + retry = option.getRetry() + useWebView = option.useWebView() + webJs = option.getWebJs() + option.getJs()?.let { jsStr -> + evalJS(jsStr, url)?.toString()?.let { + url = it } serverID = option.getServerID() webViewDelayTime = max(0, option.getWebViewDelayTime() ?: 0) } + } } urlNoQuery = url when (method) { @@ -458,7 +457,7 @@ class AnalyzeUrl( else -> get(urlNoQuery, encodedQuery) } }.let { - val isXml = it.raw.body?.contentType()?.toString() + val isXml = it.raw.body.contentType()?.toString() ?.matches(AppPattern.xmlContentTypeRegex) == true if (isXml && it.body?.trim()?.startsWith("" + it.body) @@ -553,7 +552,7 @@ class AnalyzeUrl( getByteArrayIfDataUri()?.let { return it } - return getResponseAwait().body!!.bytes() + return getResponseAwait().body.bytes() } fun getByteArray(): ByteArray { @@ -569,7 +568,7 @@ class AnalyzeUrl( getByteArrayIfDataUri()?.let { return ByteArrayInputStream(it) } - return getResponseAwait().body!!.byteStream() + return getResponseAwait().body.byteStream() } fun getInputStream(): InputStream { diff --git a/app/src/main/java/io/legato/kazusa/model/localBook/LocalBook.kt b/app/src/main/java/io/legato/kazusa/model/localBook/LocalBook.kt index 7d8567a7a..a303b8ce0 100644 --- a/app/src/main/java/io/legato/kazusa/model/localBook/LocalBook.kt +++ b/app/src/main/java/io/legato/kazusa/model/localBook/LocalBook.kt @@ -2,6 +2,7 @@ package io.legato.kazusa.model.localBook import android.net.Uri import android.util.Base64 +import androidx.core.net.toUri import androidx.documentfile.provider.DocumentFile import com.script.ScriptBindings import com.script.rhino.RhinoScriptEngine @@ -103,7 +104,7 @@ object LocalBook { fun getLastModified(book: Book): Result { return kotlin.runCatching { - val uri = Uri.parse(book.bookUrl) + val uri = book.bookUrl.toUri() if (uri.isContentScheme()) { return@runCatching DocumentFile.fromSingleUri(appCtx, uri)!!.lastModified() } @@ -144,6 +145,9 @@ object LocalBook { val list = ArrayList(LinkedHashSet(chapters)) list.forEachIndexed { index, bookChapter -> bookChapter.index = index + if (bookChapter.title.isEmpty()) { + bookChapter.title = "无标题章节" + } } val replaceRules = ContentProcessor.get(book).getTitleReplaceRules() book.durChapterTitle = list.getOrElse(book.durChapterIndex) { list.last() } @@ -191,6 +195,9 @@ object LocalBook { return StringEscapeUtils.unescapeHtml4(content) } } + if (content.isNullOrEmpty()) { + return null + } return content } @@ -374,7 +381,7 @@ object LocalBook { } if (deleteOriginal) { if (book.bookUrl.isContentScheme()) { - val uri = Uri.parse(book.bookUrl) + val uri = book.bookUrl.toUri() DocumentFile.fromSingleUri(appCtx, uri)?.delete() } else { FileUtils.delete(book.bookUrl) @@ -419,7 +426,7 @@ object LocalBook { inputStream.use { val defaultBookTreeUri = AppConfig.defaultBookTreeUri if (defaultBookTreeUri.isNullOrBlank()) throw NoBooksDirException() - val treeUri = Uri.parse(defaultBookTreeUri) + val treeUri = defaultBookTreeUri.toUri() return if (treeUri.isContentScheme()) { val treeDoc = DocumentFile.fromTreeUri(appCtx, treeUri) var doc = treeDoc!!.findFile(fileName) diff --git a/app/src/main/java/io/legato/kazusa/model/localBook/MobiFile.kt b/app/src/main/java/io/legato/kazusa/model/localBook/MobiFile.kt index e81259ade..c3408a754 100644 --- a/app/src/main/java/io/legato/kazusa/model/localBook/MobiFile.kt +++ b/app/src/main/java/io/legato/kazusa/model/localBook/MobiFile.kt @@ -229,7 +229,7 @@ class MobiFile(var book: Book) { break } section = section.next ?: break - if (section.linear) { + if (!section.linear) { continue } if (section.href == nextSectionHref) { diff --git a/app/src/main/java/io/legato/kazusa/model/localBook/TextFile.kt b/app/src/main/java/io/legato/kazusa/model/localBook/TextFile.kt index be430ecc5..84bbc21a7 100644 --- a/app/src/main/java/io/legato/kazusa/model/localBook/TextFile.kt +++ b/app/src/main/java/io/legato/kazusa/model/localBook/TextFile.kt @@ -215,6 +215,7 @@ class TextFile(private var book: Book) { val curChapter = BookChapter() curChapter.title = matcher.group() curChapter.start = curOffset + chapterLength + curChapter.end = curChapter.start toc.add(curChapter) lastChapterWordCount = 0 } else if (seekPos == 0 && chapterStart != 0) { @@ -242,6 +243,7 @@ class TextFile(private var book: Book) { val curChapter = BookChapter() curChapter.title = matcher.group() curChapter.start = curOffset + chapterLength + curChapter.end = curChapter.start toc.add(curChapter) } else { //否则就block分割之后,上一个章节的剩余内容 //获取上一章节 @@ -257,6 +259,7 @@ class TextFile(private var book: Book) { val curChapter = BookChapter() curChapter.title = matcher.group() curChapter.start = lastChapter.end + curChapter.end = curChapter.start toc.add(curChapter) } bookWordCount += chapterContent.length @@ -275,6 +278,7 @@ class TextFile(private var book: Book) { val curChapter = BookChapter() curChapter.title = matcher.group() curChapter.start = lastChapter.end + curChapter.end = curChapter.start toc.add(curChapter) } else { //如果章节不存在则创建章节 val curChapter = BookChapter() diff --git a/app/src/main/java/io/legato/kazusa/model/webBook/SearchModel.kt b/app/src/main/java/io/legato/kazusa/model/webBook/SearchModel.kt index 053301050..f4e0c566d 100644 --- a/app/src/main/java/io/legato/kazusa/model/webBook/SearchModel.kt +++ b/app/src/main/java/io/legato/kazusa/model/webBook/SearchModel.kt @@ -17,8 +17,10 @@ import kotlinx.coroutines.Job import kotlinx.coroutines.asCoroutineDispatcher import kotlinx.coroutines.currentCoroutineContext import kotlinx.coroutines.ensureActive +import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.onCompletion import kotlinx.coroutines.flow.onEach @@ -39,7 +41,7 @@ class SearchModel(private val scope: CoroutineScope, private val callBack: CallB private var bookSourceParts = emptyList() private var searchBooks = arrayListOf() private var searchJob: Job? = null - + private var workingState = MutableStateFlow(true) private fun initSearchPool() { searchPool?.close() @@ -82,6 +84,7 @@ class SearchModel(private val scope: CoroutineScope, private val callBack: CallB bs.getBookSource()?.let { emit(it) } + workingState.first { it } } }.onStart { callBack.onSearchStart() @@ -191,6 +194,14 @@ class SearchModel(private val scope: CoroutineScope, private val callBack: CallB mSearchId = 0L } + fun pause() { + workingState.value = false + } + + fun resume() { + workingState.value = true + } + interface CallBack { fun getSearchScope(): SearchScope fun onSearchStart() diff --git a/app/src/main/java/io/legato/kazusa/service/BaseReadAloudService.kt b/app/src/main/java/io/legato/kazusa/service/BaseReadAloudService.kt index 6db557318..5646ee3da 100644 --- a/app/src/main/java/io/legato/kazusa/service/BaseReadAloudService.kt +++ b/app/src/main/java/io/legato/kazusa/service/BaseReadAloudService.kt @@ -27,6 +27,7 @@ import io.legato.kazusa.R import io.legato.kazusa.base.BaseService import io.legato.kazusa.constant.AppConst import io.legato.kazusa.constant.AppLog +import io.legato.kazusa.constant.AppPattern import io.legato.kazusa.constant.EventBus import io.legato.kazusa.constant.IntentAction import io.legato.kazusa.constant.NotificationId @@ -45,9 +46,7 @@ import io.legato.kazusa.ui.book.read.ReadBookActivity import io.legato.kazusa.ui.book.read.page.entities.TextChapter import io.legato.kazusa.utils.LogUtils import io.legato.kazusa.utils.activityPendingIntent -import io.legato.kazusa.utils.broadcastPendingIntent import io.legato.kazusa.utils.getPrefBoolean -import io.legato.kazusa.utils.isVivoDevice import io.legato.kazusa.utils.observeEvent import io.legato.kazusa.utils.observeSharedPreferences import io.legato.kazusa.utils.postEvent @@ -326,9 +325,11 @@ abstract class BaseReadAloudService : BaseService(), private fun prevP() { if (nowSpeak > 0) { playStop() - nowSpeak-- - readAloudNumber -= contentList[nowSpeak].length + 1 + paragraphStartPos - paragraphStartPos = 0 + do { + nowSpeak-- + readAloudNumber -= contentList[nowSpeak].length + 1 + paragraphStartPos + paragraphStartPos = 0 + } while (contentList[nowSpeak].matches(AppPattern.notReadAloudRegex)) textChapter?.let { if (readAloudByPage) { val paragraphs = it.getParagraphs(true) @@ -358,7 +359,9 @@ abstract class BaseReadAloudService : BaseService(), val paragraphs = it.getParagraphs(true) if (!paragraphs[nowSpeak].isParagraphEnd) readAloudNumber-- } - if (readAloudNumber >= it.getReadLength(pageIndex + 1)) { + if (pageIndex + 1 < it.pageSize + && readAloudNumber >= it.getReadLength(pageIndex + 1) + ) { pageIndex++ ReadBook.moveToNextPage() } @@ -443,24 +446,75 @@ abstract class BaseReadAloudService : BaseService(), PlaybackStateCompat.Builder() .setActions(MediaHelp.MEDIA_SESSION_ACTIONS) .setState(state, nowSpeak.toLong(), 1f) + // 为系统媒体控件添加定时按钮 + .addCustomAction( + PlaybackStateCompat.CustomAction.Builder( + "ACTION_ADD_TIMER", + getString(R.string.set_timer), + R.drawable.ic_time_add_24dp + ).build() + ) .build() ) } + /** + * 初始化MediaSession, 注册多媒体按钮 + */ /** * 初始化MediaSession, 注册多媒体按钮 */ @SuppressLint("UnspecifiedImmutableFlag") private fun initMediaSession() { - mediaSessionCompat.setCallback(object : MediaSessionCompat.Callback() { - override fun onMediaButtonEvent(mediaButtonEvent: Intent): Boolean { - return MediaButtonReceiver.handleIntent(this@BaseReadAloudService, mediaButtonEvent) - } - }) - mediaSessionCompat.setMediaButtonReceiver( - broadcastPendingIntent(Intent.ACTION_MEDIA_BUTTON) - ) - mediaSessionCompat.isActive = true + if (getPrefBoolean("systemMediaControlCompatibilityChange")) { + mediaSessionCompat.setCallback(object : MediaSessionCompat.Callback() { + override fun onPlay() { + resumeReadAloud() + } + + override fun onPause() { + pauseReadAloud() + } + + override fun onSkipToNext() { + if (getPrefBoolean("mediaButtonPerNext", false)) { + nextChapter() + } else { + nextP() + } + } + + override fun onSkipToPrevious() { + if (getPrefBoolean("mediaButtonPerNext", false)) { + prevChapter() + } else { + prevP() + } + } + + override fun onStop() { + stopSelf() + } + + override fun onCustomAction(action: String, extras: Bundle?) { + if (action == "ACTION_ADD_TIMER") addTimer() + } + + override fun onMediaButtonEvent(mediaButtonEvent: Intent): Boolean { + return MediaButtonReceiver.handleIntent( + this@BaseReadAloudService, mediaButtonEvent + ) + } + }) + } else { + mediaSessionCompat.setCallback(object : MediaSessionCompat.Callback() { + override fun onMediaButtonEvent(mediaButtonEvent: Intent): Boolean { + return MediaButtonReceiver.handleIntent( + this@BaseReadAloudService, mediaButtonEvent + ) + } + }) + } } /** @@ -523,7 +577,7 @@ abstract class BaseReadAloudService : BaseService(), private fun choiceMediaStyle(): androidx.media.app.NotificationCompat.MediaStyle { val mediaStyle = androidx.media.app.NotificationCompat.MediaStyle() .setShowActionsInCompactView(1, 2, 4) - if (isVivoDevice) { + if (getPrefBoolean("systemMediaControlCompatibilityChange")) { //fix #4090 android 14 can not show play control in lock screen mediaStyle.setMediaSession(mediaSessionCompat.sessionToken) } diff --git a/app/src/main/java/io/legato/kazusa/service/CacheBookService.kt b/app/src/main/java/io/legato/kazusa/service/CacheBookService.kt index 0571fc9ad..6f495fb0f 100644 --- a/app/src/main/java/io/legato/kazusa/service/CacheBookService.kt +++ b/app/src/main/java/io/legato/kazusa/service/CacheBookService.kt @@ -65,7 +65,6 @@ class CacheBookService : BaseService() { override fun onCreate() { super.onCreate() isRun = true - CacheBook.clear() lifecycleScope.launch { while (isActive) { delay(1000) diff --git a/app/src/main/java/io/legato/kazusa/service/HttpReadAloudService.kt b/app/src/main/java/io/legato/kazusa/service/HttpReadAloudService.kt index 0c90927ac..3045f8001 100644 --- a/app/src/main/java/io/legato/kazusa/service/HttpReadAloudService.kt +++ b/app/src/main/java/io/legato/kazusa/service/HttpReadAloudService.kt @@ -3,6 +3,7 @@ package io.legato.kazusa.service import android.annotation.SuppressLint import android.app.PendingIntent import android.net.Uri +import androidx.core.net.toUri import androidx.lifecycle.lifecycleScope import androidx.media3.common.C import androidx.media3.common.MediaItem @@ -306,7 +307,7 @@ class HttpReadAloudService : BaseReadAloudService(), } private fun createDownloader(factory: CacheDataSource.Factory, fileName: String): Downloader { - val uri = Uri.parse(fileName) + val uri = fileName.toUri() val request = DownloadRequest.Builder(fileName, uri).build() return DefaultDownloaderFactory(factory, okHttpClient.dispatcher.executorService) .createDownloader(request) @@ -343,17 +344,17 @@ class HttpReadAloudService : BaseReadAloudService(), val contentType = contentType.substringBefore(";") val ct = httpTts.contentType if (contentType == "application/json" || contentType.startsWith("text/")) { - throw NoStackTraceException(response.body!!.string()) + throw NoStackTraceException(response.body.string()) } else if (ct?.isNotBlank() == true) { if (!contentType.matches(ct.toRegex())) { throw NoStackTraceException( - "TTS服务器返回错误:" + response.body!!.string() + "TTS服务器返回错误:" + response.body.string() ) } } } coroutineContext.ensureActive() - response.body!!.byteStream().let { stream -> + response.body.byteStream().let { stream -> downloadErrorNo = 0 return stream } @@ -477,12 +478,12 @@ class HttpReadAloudService : BaseReadAloudService(), val sleep = exoPlayer.duration / speakTextLength val start = speakTextLength * exoPlayer.currentPosition / exoPlayer.duration for (i in start..contentList[nowSpeak].length) { - if (readAloudNumber + i > textChapter.getReadLength(pageIndex + 1)) { + if (pageIndex + 1 < textChapter.pageSize + && readAloudNumber + i > textChapter.getReadLength(pageIndex + 1) + ) { pageIndex++ - if (pageIndex < textChapter.pageSize) { - ReadBook.moveToNextPage() - upTtsProgress(readAloudNumber + i.toInt()) - } + ReadBook.moveToNextPage() + upTtsProgress(readAloudNumber + i.toInt()) } delay(sleep) } diff --git a/app/src/main/java/io/legato/kazusa/service/TTSReadAloudService.kt b/app/src/main/java/io/legato/kazusa/service/TTSReadAloudService.kt index f261bce45..375d3ffa3 100644 --- a/app/src/main/java/io/legato/kazusa/service/TTSReadAloudService.kt +++ b/app/src/main/java/io/legato/kazusa/service/TTSReadAloudService.kt @@ -195,7 +195,9 @@ class TTSReadAloudService : BaseReadAloudService(), TextToSpeech.OnInitListener if (contentList[nowSpeak].matches(AppPattern.notReadAloudRegex)) { nextParagraph() } - if (readAloudNumber + 1 > it.getReadLength(pageIndex + 1)) { + if (pageIndex + 1 < it.pageSize + && readAloudNumber + 1 > it.getReadLength(pageIndex + 1) + ) { pageIndex++ ReadBook.moveToNextPage() } @@ -214,7 +216,9 @@ class TTSReadAloudService : BaseReadAloudService(), TextToSpeech.OnInitListener "onRangeStart nowSpeak:$nowSpeak pageIndex:$pageIndex utteranceId:$utteranceId start:$start end:$end frame:$frame" LogUtils.d(TAG, msg) textChapter?.let { - if (readAloudNumber + start > it.getReadLength(pageIndex + 1)) { + if (pageIndex + 1 < it.pageSize + && readAloudNumber + start > it.getReadLength(pageIndex + 1) + ) { pageIndex++ ReadBook.moveToNextPage() upTtsProgress(readAloudNumber + start) diff --git a/app/src/main/java/io/legato/kazusa/ui/association/OnLineImportViewModel.kt b/app/src/main/java/io/legato/kazusa/ui/association/OnLineImportViewModel.kt index a5c36d68e..cb3b216e5 100644 --- a/app/src/main/java/io/legato/kazusa/ui/association/OnLineImportViewModel.kt +++ b/app/src/main/java/io/legato/kazusa/ui/association/OnLineImportViewModel.kt @@ -56,7 +56,7 @@ class OnLineImportViewModel(app: Application) : BaseAssociationViewModel(app) { fun importReadConfig(bytes: ByteArray, finally: (title: String, msg: String) -> Unit) { execute { - val config = ReadBookConfig.import(bytes).getOrThrow() + val config = ReadBookConfig.import(bytes) ReadBookConfig.configList.forEachIndexed { index, c -> if (c.name == config.name) { ReadBookConfig.configList[index] = config diff --git a/app/src/main/java/io/legato/kazusa/ui/book/audio/AudioPlayActivity.kt b/app/src/main/java/io/legato/kazusa/ui/book/audio/AudioPlayActivity.kt index 50f18ad95..7e8ffa9f6 100644 --- a/app/src/main/java/io/legato/kazusa/ui/book/audio/AudioPlayActivity.kt +++ b/app/src/main/java/io/legato/kazusa/ui/book/audio/AudioPlayActivity.kt @@ -64,6 +64,7 @@ class AudioPlayActivity : override val viewModel by viewModels() private val timerSliderPopup by lazy { TimerSliderPopup(this) } private var adjustProgress = false + private var playMode = AudioPlay.PlayMode.LIST_END_STOP private val progressTimeFormat by lazy { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { @@ -140,6 +141,14 @@ class AudioPlayActivity : } private fun initView() { + binding.ivPlayMode.setOnClickListener { + AudioPlay.changePlayMode() + } + + observeEventSticky(EventBus.PLAY_MODE_CHANGED) { + playMode = it + updatePlayModeIcon() + } binding.fabPlayStop.setOnClickListener { playButton() } @@ -207,6 +216,10 @@ class AudioPlayActivity : binding.llPlayMenu.applyNavigationBarPadding() } + private fun updatePlayModeIcon() { + binding.ivPlayMode.setImageResource(playMode.iconRes) + } + private fun upCover(path: String?) { BookCover.load(this, path, sourceOrigin = AudioPlay.bookSource?.bookSourceUrl) { BookCover.loadBlur(this, path, sourceOrigin = AudioPlay.bookSource?.bookSourceUrl) diff --git a/app/src/main/java/io/legato/kazusa/ui/book/audio/AudioPlayViewModel.kt b/app/src/main/java/io/legato/kazusa/ui/book/audio/AudioPlayViewModel.kt index d842b2f4c..4ddfbca58 100644 --- a/app/src/main/java/io/legato/kazusa/ui/book/audio/AudioPlayViewModel.kt +++ b/app/src/main/java/io/legato/kazusa/ui/book/audio/AudioPlayViewModel.kt @@ -26,7 +26,7 @@ class AudioPlayViewModel(application: Application) : BaseViewModel(application) fun initData(intent: Intent) = AudioPlay.apply { execute { - val bookUrl = intent.getStringExtra("bookUrl") ?: return@execute + val bookUrl = intent.getStringExtra("bookUrl") ?: book?.bookUrl ?: return@execute val book = appDb.bookDao.getBook(bookUrl) ?: return@execute inBookshelf = intent.getBooleanExtra("inBookshelf", true) initBook(book) @@ -71,7 +71,7 @@ class AudioPlayViewModel(application: Application) : BaseViewModel(application) if (oldBook.bookUrl == book.bookUrl) { appDb.bookDao.update(book) } else { - appDb.bookDao.insert(book) + appDb.bookDao.replace(oldBook, book) } appDb.bookChapterDao.delByBook(book.bookUrl) appDb.bookChapterDao.insert(*cList.toTypedArray()) diff --git a/app/src/main/java/io/legato/kazusa/ui/book/info/BookInfoActivity.kt b/app/src/main/java/io/legato/kazusa/ui/book/info/BookInfoActivity.kt index 72c0ac8be..44898cd77 100644 --- a/app/src/main/java/io/legato/kazusa/ui/book/info/BookInfoActivity.kt +++ b/app/src/main/java/io/legato/kazusa/ui/book/info/BookInfoActivity.kt @@ -739,7 +739,7 @@ class BookInfoActivity : else -> readBookResult.launch( Intent( this, - if (book.isImage && AppConfig.showMangaUi) ReadMangaActivity::class.java + if (!book.isLocal && book.isImage && AppConfig.showMangaUi) ReadMangaActivity::class.java else ReadBookActivity::class.java ) .putExtra("bookUrl", book.bookUrl) diff --git a/app/src/main/java/io/legato/kazusa/ui/book/info/BookInfoViewModel.kt b/app/src/main/java/io/legato/kazusa/ui/book/info/BookInfoViewModel.kt index 7eb6af167..15470fe33 100644 --- a/app/src/main/java/io/legato/kazusa/ui/book/info/BookInfoViewModel.kt +++ b/app/src/main/java/io/legato/kazusa/ui/book/info/BookInfoViewModel.kt @@ -235,7 +235,7 @@ class BookInfoViewModel(application: Application) : BaseViewModel(application) { WebBook.getChapterList(scope, bookSource, book, runPreUpdateJs) .onSuccess(IO) { if (inBookshelf) { - book.save() + appDb.bookDao.replace(oldBook, book) /** * runPreUpdateJs 有可能会修改 book 的 bookUrl */ diff --git a/app/src/main/java/io/legato/kazusa/ui/book/info/edit/BookInfoEditViewModel.kt b/app/src/main/java/io/legato/kazusa/ui/book/info/edit/BookInfoEditViewModel.kt index 34531abd1..6a99ae128 100644 --- a/app/src/main/java/io/legato/kazusa/ui/book/info/edit/BookInfoEditViewModel.kt +++ b/app/src/main/java/io/legato/kazusa/ui/book/info/edit/BookInfoEditViewModel.kt @@ -1,8 +1,10 @@ package io.legato.kazusa.ui.book.info.edit import android.app.Application +import android.database.sqlite.SQLiteConstraintException import androidx.lifecycle.MutableLiveData import io.legato.kazusa.base.BaseViewModel +import io.legato.kazusa.constant.AppLog import io.legato.kazusa.data.appDb import io.legato.kazusa.data.entities.Book import io.legato.kazusa.model.ReadBook @@ -28,6 +30,12 @@ class BookInfoEditViewModel(application: Application) : BaseViewModel(applicatio appDb.bookDao.update(book) }.onSuccess { success?.invoke() + }.onError { + if (it is SQLiteConstraintException) { + AppLog.put("书籍信息保存失败,存在相同书名作者书籍\n$it", it, true) + } else { + AppLog.put("书籍信息保存失败\n$it", it, true) + } } } } \ No newline at end of file diff --git a/app/src/main/java/io/legato/kazusa/ui/book/manga/ReadMangaViewModel.kt b/app/src/main/java/io/legato/kazusa/ui/book/manga/ReadMangaViewModel.kt index 41061e5df..c99f15234 100644 --- a/app/src/main/java/io/legato/kazusa/ui/book/manga/ReadMangaViewModel.kt +++ b/app/src/main/java/io/legato/kazusa/ui/book/manga/ReadMangaViewModel.kt @@ -119,7 +119,7 @@ class ReadMangaViewModel(application: Application) : BaseViewModel(application) if (oldBook.bookUrl == book.bookUrl) { appDb.bookDao.update(book) } else { - appDb.bookDao.insert(book) + appDb.bookDao.replace(oldBook, book) BookHelp.updateCacheFolder(oldBook, book) } appDb.bookChapterDao.delByBook(oldBook.bookUrl) diff --git a/app/src/main/java/io/legato/kazusa/ui/book/read/ReadBookActivity.kt b/app/src/main/java/io/legato/kazusa/ui/book/read/ReadBookActivity.kt index 7ee7ba152..40263de22 100644 --- a/app/src/main/java/io/legato/kazusa/ui/book/read/ReadBookActivity.kt +++ b/app/src/main/java/io/legato/kazusa/ui/book/read/ReadBookActivity.kt @@ -5,7 +5,6 @@ import android.annotation.SuppressLint import android.content.Intent import android.content.res.Configuration import android.graphics.Color -import android.net.Uri import android.os.Bundle import android.view.Gravity import android.view.HapticFeedbackConstants @@ -19,6 +18,7 @@ import androidx.activity.addCallback import androidx.activity.result.contract.ActivityResultContracts import androidx.appcompat.app.AlertDialog import androidx.appcompat.widget.PopupMenu +import androidx.core.net.toUri import androidx.core.view.doOnPreDraw import androidx.core.view.get import androidx.core.view.size @@ -906,10 +906,11 @@ class ReadBookActivity : BaseReadBookActivity(), ReadBook.bookSource?.bookSourceUrl?.let { scopes.add(it) } + val text = selectedText.lineSequence().map { it.trim() }.joinToString("\n") replaceActivity.launch( ReplaceEditActivity.startIntent( this, - pattern = selectedText, + pattern = text, scope = scopes.joinToString(";") ) ) @@ -1421,7 +1422,7 @@ class ReadBookActivity : BaseReadBookActivity(), value = src } } else { - viewModel.saveImage(src, Uri.parse(path)) + viewModel.saveImage(src, path.toUri()) } } diff --git a/app/src/main/java/io/legato/kazusa/ui/book/read/ReadBookViewModel.kt b/app/src/main/java/io/legato/kazusa/ui/book/read/ReadBookViewModel.kt index 9d97ec538..353ca4096 100644 --- a/app/src/main/java/io/legato/kazusa/ui/book/read/ReadBookViewModel.kt +++ b/app/src/main/java/io/legato/kazusa/ui/book/read/ReadBookViewModel.kt @@ -214,7 +214,7 @@ class ReadBookViewModel(application: Application) : BaseViewModel(application) { if (oldBook.bookUrl == book.bookUrl) { appDb.bookDao.update(book) } else { - appDb.bookDao.insert(book) + appDb.bookDao.replace(oldBook, book) BookHelp.updateCacheFolder(oldBook, book) } appDb.bookChapterDao.delByBook(oldBook.bookUrl) diff --git a/app/src/main/java/io/legato/kazusa/ui/book/read/config/BgTextConfigDialog.kt b/app/src/main/java/io/legato/kazusa/ui/book/read/config/BgTextConfigDialog.kt index 607b652a2..1d5154e31 100644 --- a/app/src/main/java/io/legato/kazusa/ui/book/read/config/BgTextConfigDialog.kt +++ b/app/src/main/java/io/legato/kazusa/ui/book/read/config/BgTextConfigDialog.kt @@ -1,16 +1,14 @@ package io.legato.kazusa.ui.book.read.config -//import io.legado.app.lib.theme.bottomBackground -//import io.legado.app.lib.theme.getPrimaryTextColor -//import io.legado.app.lib.theme.getSecondaryTextColor import android.annotation.SuppressLint import android.content.DialogInterface -import android.graphics.Color import android.graphics.PorterDuff import android.net.Uri import android.os.Bundle import android.view.View import androidx.appcompat.widget.TooltipCompat +import androidx.core.graphics.toColorInt +import androidx.core.view.isGone import androidx.documentfile.provider.DocumentFile import com.google.android.material.slider.Slider import com.jaredrummler.android.colorpicker.ColorPickerDialog @@ -22,12 +20,14 @@ import io.legato.kazusa.databinding.DialogEditTextBinding import io.legato.kazusa.databinding.DialogReadBgTextBinding import io.legato.kazusa.databinding.ItemBgImageBinding import io.legato.kazusa.help.DefaultData +import io.legato.kazusa.help.book.isImage import io.legato.kazusa.help.config.ReadBookConfig import io.legato.kazusa.help.http.newCallResponseBody import io.legato.kazusa.help.http.okHttpClient import io.legato.kazusa.lib.dialogs.SelectItem import io.legato.kazusa.lib.dialogs.alert import io.legato.kazusa.lib.dialogs.selector +import io.legato.kazusa.model.ReadBook import io.legato.kazusa.ui.book.read.ReadBookActivity import io.legato.kazusa.ui.file.HandleFileContract import io.legato.kazusa.utils.FileUtils @@ -119,6 +119,7 @@ class BgTextConfigDialog : BaseBottomSheetDialogFragment(R.layout.dialog_read_bg tvName.text = getString(R.string.select_image) ivBg.setImageResource(R.drawable.ic_image) ivBg.setColorFilter(primaryTextColor, PorterDuff.Mode.SRC_IN) + swUnderline.isGone = ReadBook.book?.isImage == true root.setOnClickListener { selectBgImage.launch() } @@ -184,8 +185,8 @@ class BgTextConfigDialog : BaseBottomSheetDialogFragment(R.layout.dialog_read_bg } binding.tvBgColor.setOnClickListener { val bgColor = - if (curBgType() == 0) Color.parseColor(curBgStr()) - else Color.parseColor("#015A86") + if (curBgType() == 0) curBgStr().toColorInt() + else "#015A86".toColorInt() ColorPickerDialog.newBuilder() .setColor(bgColor) .setShowAlphaSlider(false) @@ -355,7 +356,7 @@ class BgTextConfigDialog : BaseBottomSheetDialogFragment(R.layout.dialog_read_bg private fun importConfig(byteArray: ByteArray) { execute { - ReadBookConfig.import(byteArray).getOrThrow() + ReadBookConfig.import(byteArray) }.onSuccess { ReadBookConfig.durConfig = it postEvent(EventBus.UP_CONFIG, arrayListOf(1, 2, 5)) diff --git a/app/src/main/java/io/legato/kazusa/ui/book/read/config/ClickActionConfigDialog.kt b/app/src/main/java/io/legato/kazusa/ui/book/read/config/ClickActionConfigDialog.kt index e8fb8b4e5..822ddce06 100644 --- a/app/src/main/java/io/legato/kazusa/ui/book/read/config/ClickActionConfigDialog.kt +++ b/app/src/main/java/io/legato/kazusa/ui/book/read/config/ClickActionConfigDialog.kt @@ -36,7 +36,8 @@ class ClickActionConfigDialog : BaseDialogFragment(R.layout.dialog_click_action_ Pair(9, getString(R.string.replace_state_change)), Pair(10, getString(R.string.chapter_list)), Pair(11, getString(R.string.search_content)), - Pair(12, getString(R.string.sync_book_progress_t)) + Pair(12, getString(R.string.sync_book_progress_t)), + Pair(13, getString(R.string.read_aloud_pause_resume)) ) } diff --git a/app/src/main/java/io/legato/kazusa/ui/book/read/config/MoreConfigDialog.kt b/app/src/main/java/io/legato/kazusa/ui/book/read/config/MoreConfigDialog.kt index 5979b0dee..4dfe73338 100644 --- a/app/src/main/java/io/legato/kazusa/ui/book/read/config/MoreConfigDialog.kt +++ b/app/src/main/java/io/legato/kazusa/ui/book/read/config/MoreConfigDialog.kt @@ -130,6 +130,10 @@ class MoreConfigDialog : BaseBottomSheetDialogFragment(R.layout.dialog_more_conf ReadBook.callBack?.upPageAnim(true) ReadBook.loadContent(false) } + + PreferKey.paddingDisplayCutouts -> { + postEvent(EventBus.UP_CONFIG, arrayListOf(2)) + } } } diff --git a/app/src/main/java/io/legato/kazusa/ui/book/read/page/AutoPager.kt b/app/src/main/java/io/legato/kazusa/ui/book/read/page/AutoPager.kt index b7bfa2a42..adddea26f 100644 --- a/app/src/main/java/io/legato/kazusa/ui/book/read/page/AutoPager.kt +++ b/app/src/main/java/io/legato/kazusa/ui/book/read/page/AutoPager.kt @@ -14,12 +14,12 @@ import io.legato.kazusa.utils.themeColor /** * 自动翻页 */ -class AutoPager( - private val readView: ReadView) { +class AutoPager(private val readView: ReadView) : Runnable { private var progress = 0 var isRunning = false private set private var isPausing = false + private var isEInkMode = false private var scrollOffsetRemain = 0.0 private var scrollOffset = 0 private var lastTimeMillis = 0L @@ -29,10 +29,15 @@ class AutoPager( fun start() { isRunning = true - paint.color = readView.context.themeColor(androidx.appcompat.R.attr.colorPrimary) - lastTimeMillis = SystemClock.uptimeMillis() + isEInkMode = AppConfig.isEInkMode readView.curPage.upSelectAble(false) - readView.invalidate() + if (isEInkMode) { + readView.postDelayed(this, ReadBookConfig.autoReadSpeed * 1000L) + } else { + paint.color = readView.context.themeColor(androidx.appcompat.R.attr.colorPrimary) + lastTimeMillis = SystemClock.uptimeMillis() + readView.invalidate() + } } fun stop() { @@ -41,6 +46,8 @@ class AutoPager( } isRunning = false isPausing = false + isEInkMode = false + readView.removeCallbacks(this) readView.curPage.upSelectAble(AppConfig.textSelectAble) readView.invalidate() reset() @@ -52,6 +59,7 @@ class AutoPager( return } isPausing = true + readView.removeCallbacks(this) } fun resume() { @@ -59,15 +67,24 @@ class AutoPager( return } isPausing = false - lastTimeMillis = SystemClock.uptimeMillis() - readView.invalidate() + if (isEInkMode) { + readView.postDelayed(this, ReadBookConfig.autoReadSpeed * 1000L) + } else { + lastTimeMillis = SystemClock.uptimeMillis() + readView.invalidate() + } } fun reset() { - progress = 0 - scrollOffsetRemain = 0.0 - scrollOffset = 0 - canvasRecorder.invalidate() + if (isEInkMode) { + readView.removeCallbacks(this) + readView.postDelayed(this, ReadBookConfig.autoReadSpeed * 1000L) + } else { + progress = 0 + scrollOffsetRemain = 0.0 + scrollOffset = 0 + canvasRecorder.invalidate() + } } fun upRecorder() { @@ -76,12 +93,15 @@ class AutoPager( } fun onDraw(canvas: Canvas) { - if (!isRunning) { + if (!isRunning || isEInkMode) { return } if (readView.isScroll) { - if (!isPausing) readView.curPage.scroll(-scrollOffset) + if (!isPausing) { + readView.curPage.scroll(-scrollOffset) + scrollOffset = 0 + } } else { val bottom = progress val width = readView.width @@ -104,7 +124,7 @@ class AutoPager( } fun computeOffset() { - if (!isRunning || isPausing) { + if (!isRunning || isPausing || isEInkMode) { return } @@ -132,4 +152,15 @@ class AutoPager( } } + override fun run() { + if (!isRunning || isPausing) { + return + } + if (!readView.fillPage(PageDirection.NEXT)) { + stop() + } else { + readView.postDelayed(this, ReadBookConfig.autoReadSpeed * 1000L) + } + } + } diff --git a/app/src/main/java/io/legato/kazusa/ui/book/read/page/PageView.kt b/app/src/main/java/io/legato/kazusa/ui/book/read/page/PageView.kt index 59a801ffd..af168514d 100644 --- a/app/src/main/java/io/legato/kazusa/ui/book/read/page/PageView.kt +++ b/app/src/main/java/io/legato/kazusa/ui/book/read/page/PageView.kt @@ -2,15 +2,20 @@ package io.legato.kazusa.ui.book.read.page import android.annotation.SuppressLint import android.content.Context +import android.graphics.drawable.LayerDrawable import android.view.LayoutInflater import android.widget.FrameLayout import androidx.core.content.ContextCompat +import androidx.core.graphics.drawable.toDrawable +import androidx.core.view.ViewCompat +import androidx.core.view.WindowInsetsCompat import androidx.core.view.isGone import androidx.core.view.isInvisible import io.legato.kazusa.R import io.legato.kazusa.constant.AppConst.timeFormat import io.legato.kazusa.data.entities.Bookmark import io.legato.kazusa.databinding.ViewBookPageBinding +import io.legato.kazusa.help.config.AppConfig import io.legato.kazusa.help.config.ReadBookConfig import io.legato.kazusa.help.config.ReadTipConfig import io.legato.kazusa.model.ReadBook @@ -25,6 +30,7 @@ import io.legato.kazusa.utils.applyNavigationBarPadding import io.legato.kazusa.utils.applyStatusBarPadding import io.legato.kazusa.utils.dpToPx import io.legato.kazusa.utils.gone +import io.legato.kazusa.utils.setOnApplyWindowInsetsListenerCompat import io.legato.kazusa.utils.setTextIfNotEqual import io.legato.kazusa.utils.statusBarHeight import splitties.views.backgroundColor @@ -56,7 +62,7 @@ class PageView(context: Context) : FrameLayout(context) { get() { val h1 = if (binding.vwStatusBar.isGone) 0 else binding.vwStatusBar.height val h2 = if (binding.llHeader.isGone) 0 else binding.llHeader.height - return h1 + h2 + return h1 + h2 + binding.vwRoot.paddingTop } init { @@ -94,6 +100,7 @@ class PageView(context: Context) : FrameLayout(context) { vwBottomDivider.backgroundColor = tipDividerColor upStatusBar() upNavigationBar() + upPaddingDisplayCutouts() llHeader.setPadding( it.headerPaddingLeft.dpToPx(), it.headerPaddingTop.dpToPx(), @@ -125,6 +132,24 @@ class PageView(context: Context) : FrameLayout(context) { binding.vwNavigationBar.isGone = ReadBookConfig.hideNavigationBar } + fun upPaddingDisplayCutouts() { + if (AppConfig.paddingDisplayCutouts) { + binding.vwRoot.setOnApplyWindowInsetsListenerCompat { _, windowInsets -> + val insets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout()) + binding.vwRoot.setPadding( + insets.left, + if (binding.vwStatusBar.isGone) insets.top else 0, + insets.right, + insets.bottom + ) + windowInsets + } + } else { + ViewCompat.setOnApplyWindowInsetsListener(binding.vwRoot, null) + binding.vwRoot.setPadding(0, 0, 0, 0) + } + } + /** * 更新阅读信息 */ @@ -239,8 +264,12 @@ class PageView(context: Context) : FrameLayout(context) { * 更新背景 */ fun upBg() { - binding.vwRoot.backgroundColor = ReadBookConfig.bgMeanColor - binding.vwBg.background = ReadBookConfig.bg + binding.vwRoot.background = LayerDrawable( + arrayOf( + ReadBookConfig.bgMeanColor.toDrawable(), + ReadBookConfig.bg + ) + ) upBgAlpha() } @@ -248,7 +277,8 @@ class PageView(context: Context) : FrameLayout(context) { * 更新背景透明度 */ fun upBgAlpha() { - binding.vwBg.alpha = ReadBookConfig.bgAlpha / 100f + ReadBookConfig.bg?.alpha = (ReadBookConfig.bgAlpha / 100f * 255).toInt() + binding.vwRoot.invalidate() } /** diff --git a/app/src/main/java/io/legato/kazusa/ui/book/read/page/ReadView.kt b/app/src/main/java/io/legato/kazusa/ui/book/read/page/ReadView.kt index 72c51a381..261a745b3 100644 --- a/app/src/main/java/io/legato/kazusa/ui/book/read/page/ReadView.kt +++ b/app/src/main/java/io/legato/kazusa/ui/book/read/page/ReadView.kt @@ -17,6 +17,7 @@ import io.legato.kazusa.help.config.AppConfig import io.legato.kazusa.help.config.ReadBookConfig import io.legato.kazusa.model.ReadAloud import io.legato.kazusa.model.ReadBook +import io.legato.kazusa.service.BaseReadAloudService import io.legato.kazusa.ui.book.read.ContentEditDialog import io.legato.kazusa.ui.book.read.page.api.DataSource import io.legato.kazusa.ui.book.read.page.delegate.CoverPageDelegate @@ -456,6 +457,13 @@ class ReadView(context: Context, attrs: AttributeSet) : { progress -> callBack.sureNewProgress(progress) }, { context.longToastOnUi(context.getString(R.string.upload_book_success)) }, { context.longToastOnUi(context.getString(R.string.sync_book_progress_success)) }) + 13 -> { + if (BaseReadAloudService.isPlay()) { + ReadAloud.pause(context) + } else { + ReadAloud.resume(context) + } + } } } diff --git a/app/src/main/java/io/legato/kazusa/ui/book/read/page/entities/TextLine.kt b/app/src/main/java/io/legato/kazusa/ui/book/read/page/entities/TextLine.kt index 18f35f679..5b579da7f 100644 --- a/app/src/main/java/io/legato/kazusa/ui/book/read/page/entities/TextLine.kt +++ b/app/src/main/java/io/legato/kazusa/ui/book/read/page/entities/TextLine.kt @@ -160,6 +160,15 @@ data class TextLine( columns[i].draw(view, canvas) } } + // 墨水屏模式下的朗读和搜索下划线 + if (AppConfig.isEInkMode && (isReadAloud || searchResultColumnCount > 0)) { + val underlinePaint = PaintPool.obtain() + underlinePaint.set(ChapterProvider.contentPaint) + underlinePaint.strokeWidth = 1.dpToPx().toFloat() + val lineY = height - 1.dpToPx() + canvas.drawLine(lineStart + indentWidth, lineY, lineEnd, lineY, underlinePaint) + PaintPool.recycle(underlinePaint) + } if (ReadBookConfig.underline && !isImage && ReadBook.book?.isImage != true) { drawUnderline(canvas) } diff --git a/app/src/main/java/io/legato/kazusa/ui/book/read/page/provider/ChapterProvider.kt b/app/src/main/java/io/legato/kazusa/ui/book/read/page/provider/ChapterProvider.kt index 94640763c..3f1d15ef9 100644 --- a/app/src/main/java/io/legato/kazusa/ui/book/read/page/provider/ChapterProvider.kt +++ b/app/src/main/java/io/legato/kazusa/ui/book/read/page/provider/ChapterProvider.kt @@ -34,6 +34,7 @@ import io.legato.kazusa.utils.postEvent import io.legato.kazusa.utils.spToPx import io.legato.kazusa.utils.splitNotBlank import io.legato.kazusa.utils.textHeight +import io.legato.kazusa.utils.toastOnUi import kotlinx.coroutines.CoroutineScope import splitties.init.appCtx import java.util.LinkedList @@ -1001,20 +1002,8 @@ object ChapterProvider { } } - if (viewWidth > 0 && viewHeight > 0) { - paddingLeft = ReadBookConfig.paddingLeft.dpToPx() - paddingTop = ReadBookConfig.paddingTop.dpToPx() - paddingRight = ReadBookConfig.paddingRight.dpToPx() - paddingBottom = ReadBookConfig.paddingBottom.dpToPx() - visibleWidth = if (doublePage) { - viewWidth / 2 - paddingLeft - paddingRight - } else { - viewWidth - paddingLeft - paddingRight - } - //留1dp画最后一行下划线 - visibleHeight = viewHeight - paddingTop - paddingBottom - visibleRight = viewWidth - paddingRight - visibleBottom = paddingTop + visibleHeight + if (viewWidth <= 0 || viewHeight <= 0) { + return } visibleRect.set( @@ -1024,6 +1013,37 @@ object ChapterProvider { visibleBottom.toFloat() ) + paddingLeft = ReadBookConfig.paddingLeft.dpToPx() + paddingTop = ReadBookConfig.paddingTop.dpToPx() + paddingRight = ReadBookConfig.paddingRight.dpToPx() + paddingBottom = ReadBookConfig.paddingBottom.dpToPx() + visibleWidth = if (doublePage) { + viewWidth / 2 - paddingLeft - paddingRight + } else { + viewWidth - paddingLeft - paddingRight + } + //留1dp画最后一行下划线 + visibleHeight = viewHeight - paddingTop - paddingBottom + visibleRight = viewWidth - paddingRight + visibleBottom = paddingTop + visibleHeight + + if (paddingLeft >= visibleRight || paddingTop >= visibleBottom) { + appCtx.toastOnUi("边距设置过大,请重新设置") + visibleRect.set( + 0f, + 0f, + viewWidth.toFloat(), + viewHeight.toFloat() + ) + } else { + visibleRect.set( + paddingLeft.toFloat(), + paddingTop.toFloat(), + visibleRight.toFloat(), + visibleBottom.toFloat() + ) + } + } } diff --git a/app/src/main/java/io/legato/kazusa/ui/book/read/page/provider/TextChapterLayout.kt b/app/src/main/java/io/legato/kazusa/ui/book/read/page/provider/TextChapterLayout.kt index 1cb14b62b..766922302 100644 --- a/app/src/main/java/io/legato/kazusa/ui/book/read/page/provider/TextChapterLayout.kt +++ b/app/src/main/java/io/legato/kazusa/ui/book/read/page/provider/TextChapterLayout.kt @@ -20,7 +20,6 @@ import io.legato.kazusa.ui.book.read.page.entities.TextChapter import io.legato.kazusa.ui.book.read.page.entities.TextLine import io.legato.kazusa.ui.book.read.page.entities.TextPage import io.legato.kazusa.ui.book.read.page.entities.column.ImageColumn -import io.legato.kazusa.ui.book.read.page.entities.column.ReviewColumn import io.legato.kazusa.ui.book.read.page.entities.column.TextColumn import io.legato.kazusa.utils.dpToPx import io.legato.kazusa.utils.fastSum @@ -214,13 +213,13 @@ class TextChapterLayout( emptyContent = contents.isEmpty(), isVolumeTitle = bookChapter.isVolume ) + pendingTextPage.lines.last().isParagraphEnd = true + stringBuilder.append("\n") } - pendingTextPage.lines.last().isParagraphEnd = true - stringBuilder.append("\n") durY += titleBottomSpacing // 如果是单图模式且当前页有内容,强制分页 - if (isSingleImageStyle && pendingTextPage.lines.isNotEmpty()) { + if (isSingleImageStyle && pendingTextPage.lines.isNotEmpty() && contents.isNotEmpty()) { prepareNextPageIfNeed() } } @@ -708,13 +707,13 @@ class TextChapterLayout( ) } - isLineEnd && char == ChapterProvider.reviewChar -> { - ReviewColumn( - start = absStartX + xStart, - end = absStartX + xEnd, - count = 100 - ) - } +// isLineEnd && char == ChapterProvider.reviewChar -> { +// ReviewColumn( +// start = absStartX + xStart, +// end = absStartX + xEnd, +// count = 100 +// ) +// } else -> { TextColumn( diff --git a/app/src/main/java/io/legato/kazusa/ui/book/search/SearchActivity.kt b/app/src/main/java/io/legato/kazusa/ui/book/search/SearchActivity.kt index a2ea3a020..da2c13d33 100644 --- a/app/src/main/java/io/legato/kazusa/ui/book/search/SearchActivity.kt +++ b/app/src/main/java/io/legato/kazusa/ui/book/search/SearchActivity.kt @@ -12,12 +12,13 @@ import androidx.activity.viewModels import androidx.core.os.bundleOf import androidx.core.view.isVisible import androidx.core.widget.doAfterTextChanged +import androidx.lifecycle.Lifecycle import androidx.lifecycle.lifecycleScope +import androidx.lifecycle.repeatOnLifecycle import androidx.recyclerview.widget.DefaultItemAnimator import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import com.google.android.flexbox.FlexboxLayoutManager -import com.google.android.material.appbar.AppBarLayout import com.google.android.material.search.SearchBar import com.google.android.material.search.SearchView import io.legato.kazusa.R @@ -45,6 +46,7 @@ import io.legato.kazusa.utils.viewbindingdelegate.viewBinding import io.legato.kazusa.utils.visible import kotlinx.coroutines.Dispatchers.IO import kotlinx.coroutines.Job +import kotlinx.coroutines.awaitCancellation import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.conflate import kotlinx.coroutines.flow.flowOn @@ -183,21 +185,19 @@ class SearchActivity : VMBaseActivity - val totalScrollRange = appBarLayout.totalScrollRange - val scrollRatio = - (1f - (abs(verticalOffset).toFloat() / totalScrollRange)).coerceIn(0f, 1f) + binding.appBar.addOnOffsetChangedListener { appBarLayout, verticalOffset -> + val totalScrollRange = appBarLayout.totalScrollRange + val scrollRatio = + (1f - (abs(verticalOffset).toFloat() / totalScrollRange)).coerceIn(0f, 1f) - appBarLayout.alpha = scrollRatio - appBarLayout.setPadding( - appBarLayout.paddingLeft, - (maxPaddingTop * scrollRatio).toInt(), - appBarLayout.paddingRight, - appBarLayout.paddingBottom - ) - } - ) + appBarLayout.alpha = scrollRatio + appBarLayout.setPadding( + appBarLayout.paddingLeft, + (maxPaddingTop * scrollRatio).toInt(), + appBarLayout.paddingRight, + appBarLayout.paddingBottom + ) + } } searchBar.setOnMenuItemClickListener { item -> @@ -343,6 +343,16 @@ class SearchActivity : VMBaseActivity") - append(leftString.colorTextForHtml(textColor)) - append(query.colorTextForHtml(accentColor)) - append(rightString.colorTextForHtml(textColor)) + // 检查是否为墨水屏模式 + val html = if (AppConfig.isEInkMode) { + // 墨水屏模式:使用下划线 + buildString { + append("${chapterTitle}") + append("
") + append(leftString) + append("${query}") + append(rightString) + } + } else { + // 普通模式:使用颜色 + buildString { + append(chapterTitle.colorTextForHtml(accentColor)) + append("
") + append(leftString.colorTextForHtml(textColor)) + append(query.colorTextForHtml(accentColor)) + append(rightString.colorTextForHtml(textColor)) + } } HtmlCompat.fromHtml(html, HtmlCompat.FROM_HTML_MODE_LEGACY) } else { - HtmlCompat.fromHtml( - resultText.colorTextForHtml(textColor), - HtmlCompat.FROM_HTML_MODE_LEGACY - ) + val html = if (AppConfig.isEInkMode) { + resultText + } else { + resultText.colorTextForHtml(textColor) + } + HtmlCompat.fromHtml(html, HtmlCompat.FROM_HTML_MODE_LEGACY) } } diff --git a/app/src/main/java/io/legato/kazusa/ui/dict/DictDialog.kt b/app/src/main/java/io/legato/kazusa/ui/dict/DictDialog.kt index 293d5bf8f..46182e5f5 100644 --- a/app/src/main/java/io/legato/kazusa/ui/dict/DictDialog.kt +++ b/app/src/main/java/io/legato/kazusa/ui/dict/DictDialog.kt @@ -65,8 +65,19 @@ class DictDialog() : BaseBottomSheetDialogFragment(R.layout.dialog_dict) { tag = it }) } + setupTabLayoutMode(it.size) } + } + //根据已启用词典数动态选取布局 + private fun setupTabLayoutMode(dictCount: Int) { + if (dictCount <= 4) { + binding.tabLayout.tabMode = TabLayout.MODE_FIXED + binding.tabLayout.tabGravity = TabLayout.GRAVITY_FILL + } else { + binding.tabLayout.tabMode = TabLayout.MODE_SCROLLABLE + binding.tabLayout.tabGravity = TabLayout.GRAVITY_CENTER + } } } \ No newline at end of file diff --git a/app/src/main/java/io/legato/kazusa/ui/login/SourceLoginActivity.kt b/app/src/main/java/io/legato/kazusa/ui/login/SourceLoginActivity.kt index 996aac0fd..95a94bdc5 100644 --- a/app/src/main/java/io/legato/kazusa/ui/login/SourceLoginActivity.kt +++ b/app/src/main/java/io/legato/kazusa/ui/login/SourceLoginActivity.kt @@ -17,9 +17,11 @@ class SourceLoginActivity : VMBaseActivity + viewModel.initData(intent, success = { source -> initView(source) - } + }, error = { + finish() + }) } private fun initView(source: BaseSource) { diff --git a/app/src/main/java/io/legato/kazusa/ui/login/SourceLoginViewModel.kt b/app/src/main/java/io/legato/kazusa/ui/login/SourceLoginViewModel.kt index 3370dc1ad..26d9f2cbf 100644 --- a/app/src/main/java/io/legato/kazusa/ui/login/SourceLoginViewModel.kt +++ b/app/src/main/java/io/legato/kazusa/ui/login/SourceLoginViewModel.kt @@ -2,11 +2,12 @@ package io.legato.kazusa.ui.login import android.app.Application import android.content.Intent +import com.script.rhino.runScriptWithContext import io.legato.kazusa.base.BaseViewModel +import io.legato.kazusa.constant.AppLog import io.legato.kazusa.data.appDb import io.legato.kazusa.data.entities.BaseSource import io.legato.kazusa.exception.NoStackTraceException -import com.script.rhino.runScriptWithContext import io.legato.kazusa.utils.toastOnUi class SourceLoginViewModel(application: Application) : BaseViewModel(application) { @@ -14,7 +15,7 @@ class SourceLoginViewModel(application: Application) : BaseViewModel(application var source: BaseSource? = null var headerMap: Map = emptyMap() - fun initData(intent: Intent, success: (bookSource: BaseSource) -> Unit) { + fun initData(intent: Intent, success: (bookSource: BaseSource) -> Unit, error: () -> Unit) { execute { val sourceKey = intent.getStringExtra("key") ?: throw NoStackTraceException("没有参数") @@ -33,6 +34,9 @@ class SourceLoginViewModel(application: Application) : BaseViewModel(application } else { context.toastOnUi("未找到书源") } + }.onError { + error.invoke() + AppLog.put("登录 UI 初始化失败\n$it", it, true) } } diff --git a/app/src/main/java/io/legato/kazusa/ui/main/MainViewModel.kt b/app/src/main/java/io/legato/kazusa/ui/main/MainViewModel.kt index 897ec8c3b..05eb3c5e6 100644 --- a/app/src/main/java/io/legato/kazusa/ui/main/MainViewModel.kt +++ b/app/src/main/java/io/legato/kazusa/ui/main/MainViewModel.kt @@ -167,7 +167,7 @@ class MainViewModel(application: Application) : BaseViewModel(application) { if (book.bookUrl == bookUrl) { appDb.bookDao.update(book) } else { - appDb.bookDao.insert(book) + appDb.bookDao.replace(oldBook, book) BookHelp.updateCacheFolder(oldBook, book) } appDb.bookChapterDao.delByBook(bookUrl) diff --git a/app/src/main/java/io/legato/kazusa/ui/replace/ReplaceRuleActivity.kt b/app/src/main/java/io/legato/kazusa/ui/replace/ReplaceRuleActivity.kt index 0a17ca0c8..80e047a8e 100644 --- a/app/src/main/java/io/legato/kazusa/ui/replace/ReplaceRuleActivity.kt +++ b/app/src/main/java/io/legato/kazusa/ui/replace/ReplaceRuleActivity.kt @@ -1,7 +1,6 @@ package io.legato.kazusa.ui.replace import android.annotation.SuppressLint -import android.app.Activity import android.os.Bundle import android.view.Menu import android.view.MenuItem @@ -25,8 +24,6 @@ import io.legato.kazusa.help.DirectLinkUpload import io.legato.kazusa.help.book.ContentProcessor import io.legato.kazusa.help.coroutine.Coroutine import io.legato.kazusa.lib.dialogs.alert -//import io.legado.app.lib.theme.primaryColor -//import io.legado.app.lib.theme.primaryTextColor import io.legato.kazusa.ui.association.ImportReplaceRuleDialog import io.legato.kazusa.ui.file.HandleFileContract import io.legato.kazusa.ui.qrcode.QrCodeResult @@ -72,7 +69,7 @@ class ReplaceRuleActivity : VMBaseActivity() + private var groups = arrayListOf() private var groupMenu: SubMenu? = null private var replaceRuleFlowJob: Job? = null private var dataInit = false @@ -224,7 +221,7 @@ class ReplaceRuleActivity : VMBaseActivity() + private var groups = arrayListOf() private var groupMenu: SubMenu? = null private val qrCodeResult = registerForActivityResult(QrCodeResult()) { it ?: return@registerForActivityResult diff --git a/app/src/main/java/io/legato/kazusa/utils/ContextExtensions.kt b/app/src/main/java/io/legato/kazusa/utils/ContextExtensions.kt index 0c736ebfe..b194bb53e 100644 --- a/app/src/main/java/io/legato/kazusa/utils/ContextExtensions.kt +++ b/app/src/main/java/io/legato/kazusa/utils/ContextExtensions.kt @@ -35,6 +35,7 @@ import androidx.annotation.DrawableRes import androidx.core.content.ContextCompat import androidx.core.content.FileProvider import androidx.core.content.edit +import androidx.core.net.toUri import androidx.datastore.preferences.preferencesDataStore import androidx.preference.PreferenceManager import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel @@ -44,6 +45,7 @@ import io.legato.kazusa.data.entities.Book import io.legato.kazusa.help.IntentHelp import io.legato.kazusa.help.book.isAudio import io.legato.kazusa.help.book.isImage +import io.legato.kazusa.help.book.isLocal import io.legato.kazusa.help.config.AppConfig import io.legato.kazusa.ui.book.audio.AudioPlayActivity import io.legato.kazusa.ui.book.manga.ReadMangaActivity @@ -68,7 +70,7 @@ fun Context.startActivityForBook( ) { val cls = when { book.isAudio -> AudioPlayActivity::class.java - book.isImage && AppConfig.showMangaUi -> ReadMangaActivity::class.java + !book.isLocal && book.isImage && AppConfig.showMangaUi -> ReadMangaActivity::class.java else -> ReadBookActivity::class.java } val intent = Intent(this, cls) @@ -323,7 +325,7 @@ fun Context.getClipText(): String? { fun Context.sendMail(mail: String) { try { val intent = Intent(Intent.ACTION_SENDTO) - intent.data = Uri.parse("mailto:$mail") + intent.data = "mailto:$mail".toUri() intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) startActivity(intent) } catch (e: Exception) { diff --git a/app/src/main/java/io/legato/kazusa/utils/FragmentExtensions.kt b/app/src/main/java/io/legato/kazusa/utils/FragmentExtensions.kt index b1b600ce2..48e05afd9 100644 --- a/app/src/main/java/io/legato/kazusa/utils/FragmentExtensions.kt +++ b/app/src/main/java/io/legato/kazusa/utils/FragmentExtensions.kt @@ -17,6 +17,7 @@ import io.legato.kazusa.R import io.legato.kazusa.data.entities.Book import io.legato.kazusa.help.book.isAudio import io.legato.kazusa.help.book.isImage +import io.legato.kazusa.help.book.isLocal import io.legato.kazusa.help.config.AppConfig import io.legato.kazusa.ui.book.audio.AudioPlayActivity import io.legato.kazusa.ui.book.manga.ReadMangaActivity @@ -93,7 +94,7 @@ fun Fragment.startActivityForBook( ) { val cls = when { book.isAudio -> AudioPlayActivity::class.java - book.isImage && AppConfig.showMangaUi -> ReadMangaActivity::class.java + !book.isLocal && book.isImage && AppConfig.showMangaUi -> ReadMangaActivity::class.java else -> ReadBookActivity::class.java } val intent = Intent(requireActivity(), cls) diff --git a/app/src/main/java/io/legato/kazusa/utils/HtmlFormatter.kt b/app/src/main/java/io/legato/kazusa/utils/HtmlFormatter.kt index 663abde94..14e10c722 100644 --- a/app/src/main/java/io/legato/kazusa/utils/HtmlFormatter.kt +++ b/app/src/main/java/io/legato/kazusa/utils/HtmlFormatter.kt @@ -14,7 +14,7 @@ object HtmlFormatter { private val notImgHtmlRegex = "])[^<>]*>".toRegex() private val otherHtmlRegex = "])[^<>]*>".toRegex() private val formatImagePattern = Pattern.compile( - "]*\\ssrc\\s*=\\s*\"([^\"{>]*\\{(?:[^{}]|\\{[^}>]+\\})+\\})\"[^>]*>|]*\\sdata-[^=>]*=\\s*\"([^\">]*)\"[^>]*>|]*\\ssrc\\s*=\\s*\"([^\">]*)\"[^>]*>", + "]*\\ssrc\\s*=\\s*['\"]([^'\"{>]*\\{(?:[^{}]|\\{[^}>]+\\})+\\})['\"][^>]*>|]*\\s(?:data-src|src)\\s*=\\s*['\"]([^'\">]+)['\"][^>]*>|]*\\sdata-[^=>]*=\\s*['\"]([^'\">]*)['\"][^>]*>", Pattern.CASE_INSENSITIVE ) private val indent1Regex = "\\s*\\n+\\s*".toRegex() diff --git a/app/src/main/java/io/legato/kazusa/utils/RomExtensions.kt b/app/src/main/java/io/legato/kazusa/utils/RomExtensions.kt deleted file mode 100644 index f646f0a32..000000000 --- a/app/src/main/java/io/legato/kazusa/utils/RomExtensions.kt +++ /dev/null @@ -1,7 +0,0 @@ -package io.legato.kazusa.utils - -import android.os.Build - -val isVivoDevice by lazy { - Build.MANUFACTURER.equals("vivo", ignoreCase = true) -} diff --git a/app/src/main/res/drawable/ic_play_mode_list_end_stop.xml b/app/src/main/res/drawable/ic_play_mode_list_end_stop.xml new file mode 100644 index 000000000..f444ad947 --- /dev/null +++ b/app/src/main/res/drawable/ic_play_mode_list_end_stop.xml @@ -0,0 +1,11 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_play_mode_list_loop.xml b/app/src/main/res/drawable/ic_play_mode_list_loop.xml new file mode 100644 index 000000000..cc617d782 --- /dev/null +++ b/app/src/main/res/drawable/ic_play_mode_list_loop.xml @@ -0,0 +1,11 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_play_mode_random.xml b/app/src/main/res/drawable/ic_play_mode_random.xml new file mode 100644 index 000000000..602d8cf15 --- /dev/null +++ b/app/src/main/res/drawable/ic_play_mode_random.xml @@ -0,0 +1,11 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_audio_play.xml b/app/src/main/res/layout/activity_audio_play.xml index 276b00669..e42977a25 100644 --- a/app/src/main/res/layout/activity_audio_play.xml +++ b/app/src/main/res/layout/activity_audio_play.xml @@ -125,6 +125,20 @@ android:paddingHorizontal="6dp" app:layout_constraintBottom_toBottomOf="parent"> + + - - 使用Cronet网络组件 Anti-Aliasing Anti-Aliasing when draw picture - 上传URL + 创建分享链接 downloadUrlRule(downloadUrls) Ordenar por tiempo de respuesta tiempo de respuesta: %1$d ms diff --git a/app/src/main/res/values-ja-rJP/strings.xml b/app/src/main/res/values-ja-rJP/strings.xml index c0011da73..d185acaf0 100644 --- a/app/src/main/res/values-ja-rJP/strings.xml +++ b/app/src/main/res/values-ja-rJP/strings.xml @@ -889,7 +889,7 @@ 使用Cronet网络组件 Anti-Aliasing Anti-Aliasing when draw picture - 上传URL + 创建分享链接 downloadUrlRule(downloadUrls) Sort by respond time respondTime: %1$d ms diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index c7b4dd7a5..e261c367e 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -887,7 +887,7 @@ Usando componentes de rede Cronet Anti-Aliasing Anti-Aliasing when draw picture - 上传URL + 创建分享链接 downloadUrlRule(downloadUrls) Classificar por tempo de resposta tempo de resposta: %1$d ms diff --git a/app/src/main/res/values-vi/arrays.xml b/app/src/main/res/values-vi/arrays.xml index 945aa98d0..f6468fa1e 100644 --- a/app/src/main/res/values-vi/arrays.xml +++ b/app/src/main/res/values-vi/arrays.xml @@ -12,7 +12,7 @@ @string/screen_portrait @string/screen_landscape @string/screen_sensor - Đảo ngược màn hình dọc + @string/screen_portrait_reversed Nhãn diff --git a/app/src/main/res/values-zh-rHK/strings.xml b/app/src/main/res/values-zh-rHK/strings.xml index 6da3a7386..d1de1230a 100644 --- a/app/src/main/res/values-zh-rHK/strings.xml +++ b/app/src/main/res/values-zh-rHK/strings.xml @@ -1210,4 +1210,5 @@ 滤镜 隐藏漫画列表标题 刷新发现 + 填充刘海区域 diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index 88ca1aa3b..a35797f0a 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -1212,4 +1212,5 @@ 滤镜 隐藏漫画列表标题 刷新发现 + 填充刘海区域 diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml index 1a22e77d1..e6497f397 100644 --- a/app/src/main/res/values-zh/strings.xml +++ b/app/src/main/res/values-zh/strings.xml @@ -1234,9 +1234,12 @@ 列表 网格 降序 - 检查更新中... + 检查更新中… 没有内容哦 匿名统计 定位至当前 - + 填充刘海区域 + 系统媒体控件兼容性更改 + 当锁屏不显示系统媒体控件时可以尝试开启,比如 OneUI 7.0或 OriginOS 等 + 朗读暂停/继续 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 5efbf7ee9..73ce4fea7 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1242,5 +1242,9 @@ 匿名统计 允许应用使用Google Firebase进行崩溃和性能统计 定位至当前 - + 填充刘海区域 + 系统媒体控件兼容性更改 + 当锁屏不显示系统媒体控件时可以尝试开启,比如 OneUI 7.0或 OriginOS 等 + 朗读暂停/继续 + 播放模式 diff --git a/app/src/main/res/xml/pref_config_aloud.xml b/app/src/main/res/xml/pref_config_aloud.xml index 36a56572b..d1db44b2f 100644 --- a/app/src/main/res/xml/pref_config_aloud.xml +++ b/app/src/main/res/xml/pref_config_aloud.xml @@ -41,6 +41,13 @@ android:key="readAloudByPage" app:iconSpaceReserved="false" /> + + + + diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 29bbc050c..ac20cd590 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -32,7 +32,7 @@ material = "1.14.0-alpha04" media = "1.7.1" media3 = "1.8.0" nanoHttpd = "2.3.1" -okhttp = "4.12.0" +okhttp = "5.1.0" palette = "1.0.0" preference = "1.2.1" protobufJavalite = "4.26.1"