diff --git a/app/src/main/assets/updateLog.md b/app/src/main/assets/updateLog.md index 84c58d36c..a2d7e9f97 100644 --- a/app/src/main/assets/updateLog.md +++ b/app/src/main/assets/updateLog.md @@ -12,7 +12,13 @@ * 正文出现缺字漏字、内容缺失、排版错乱等情况,有可能是净化规则或简繁转换出现问题。 * 漫画源看书显示乱码,**阅读与其他软件的源并不通用**,请导入阅读的支持的漫画源! +**2023/08/02** + +* 书签导出md格式 +* 优化进度同步,减少webDav下载次数 + **2023/07/30** + * 更新cronet: 115.0.5790.138 * 更新cronet: 115.0.5790.136 diff --git a/app/src/main/java/io/legado/app/help/AppWebDav.kt b/app/src/main/java/io/legado/app/help/AppWebDav.kt index b2b7d9eba..41024e4c4 100644 --- a/app/src/main/java/io/legado/app/help/AppWebDav.kt +++ b/app/src/main/java/io/legado/app/help/AppWebDav.kt @@ -159,7 +159,7 @@ object AppWebDav { /** * 获取云端所有背景名称 */ - suspend fun getAllBgNames(): Result> { + suspend fun getAllBgWebDavFiles(): Result> { return kotlin.runCatching { if (!NetworkUtils.isAvailable()) throw NoStackTraceException("网络未连接") @@ -170,6 +170,21 @@ object AppWebDav { } } + /** + * 上传背景图片 + */ + suspend fun upBgs(files: List) { + val authorization = authorization ?: return + if (!NetworkUtils.isAvailable()) return + } + + /** + * 下载背景图片 + */ + suspend fun downBgs(fileNames: String) { + val authorization = authorization ?: return + if (!NetworkUtils.isAvailable()) return + } suspend fun exportWebDav(byteArray: ByteArray, fileName: String) { if (!NetworkUtils.isAvailable()) return diff --git a/app/src/main/java/io/legado/app/help/coroutine/Coroutine.kt b/app/src/main/java/io/legado/app/help/coroutine/Coroutine.kt index d96322276..4a0965f7c 100644 --- a/app/src/main/java/io/legado/app/help/coroutine/Coroutine.kt +++ b/app/src/main/java/io/legado/app/help/coroutine/Coroutine.kt @@ -113,7 +113,9 @@ class Coroutine( return this@Coroutine } - // 如果协程被取消,有可能会不执行 + /** + * 如果协程被取消,不执行 + */ fun onFinally( context: CoroutineContext? = null, block: suspend CoroutineScope.() -> Unit @@ -187,7 +189,9 @@ class Coroutine( private suspend inline fun dispatchVoidCallback(scope: CoroutineScope, callback: VoidCallback) { if (null == callback.context) { - callback.block.invoke(scope) + withContext(scope.coroutineContext) { + callback.block.invoke(scope) + } } else { withContext(scope.coroutineContext + callback.context) { callback.block.invoke(this) diff --git a/app/src/main/java/io/legado/app/help/storage/Backup.kt b/app/src/main/java/io/legado/app/help/storage/Backup.kt index 70259e32a..067ab6558 100644 --- a/app/src/main/java/io/legado/app/help/storage/Backup.kt +++ b/app/src/main/java/io/legado/app/help/storage/Backup.kt @@ -27,6 +27,7 @@ import java.io.FileOutputStream import java.text.SimpleDateFormat import java.util.* import java.util.concurrent.TimeUnit +import kotlin.coroutines.coroutineContext /** * 备份 @@ -92,113 +93,113 @@ object Backup { suspend fun backup(context: Context, path: String?) { LocalConfig.lastBackup = System.currentTimeMillis() - withContext(IO) { - val aes = BackupAES() - FileUtils.delete(backupPath) - writeListToJson(appDb.bookDao.all, "bookshelf.json", backupPath) - writeListToJson(appDb.bookmarkDao.all, "bookmark.json", backupPath) - writeListToJson(appDb.bookGroupDao.all, "bookGroup.json", backupPath) - writeListToJson(appDb.bookSourceDao.all, "bookSource.json", backupPath) - writeListToJson(appDb.rssSourceDao.all, "rssSources.json", backupPath) - writeListToJson(appDb.rssStarDao.all, "rssStar.json", backupPath) - ensureActive() - writeListToJson(appDb.replaceRuleDao.all, "replaceRule.json", backupPath) - writeListToJson(appDb.readRecordDao.all, "readRecord.json", backupPath) - writeListToJson(appDb.searchKeywordDao.all, "searchHistory.json", backupPath) - writeListToJson(appDb.ruleSubDao.all, "sourceSub.json", backupPath) - writeListToJson(appDb.txtTocRuleDao.all, "txtTocRule.json", backupPath) - writeListToJson(appDb.httpTTSDao.all, "httpTTS.json", backupPath) - writeListToJson(appDb.keyboardAssistsDao.all, "keyboardAssists.json", backupPath) - writeListToJson(appDb.dictRuleDao.all, "dictRule.json", backupPath) - GSON.toJson(appDb.serverDao.all).let { json -> - aes.runCatching { - encryptBase64(json) - }.getOrDefault(json).let { - FileUtils.createFileIfNotExist(backupPath + File.separator + "servers.json") - .writeText(it) - } - } - ensureActive() - GSON.toJson(ReadBookConfig.configList).let { - FileUtils.createFileIfNotExist(backupPath + File.separator + ReadBookConfig.configFileName) + val aes = BackupAES() + FileUtils.delete(backupPath) + writeListToJson(appDb.bookDao.all, "bookshelf.json", backupPath) + writeListToJson(appDb.bookmarkDao.all, "bookmark.json", backupPath) + writeListToJson(appDb.bookGroupDao.all, "bookGroup.json", backupPath) + writeListToJson(appDb.bookSourceDao.all, "bookSource.json", backupPath) + writeListToJson(appDb.rssSourceDao.all, "rssSources.json", backupPath) + writeListToJson(appDb.rssStarDao.all, "rssStar.json", backupPath) + writeListToJson(appDb.replaceRuleDao.all, "replaceRule.json", backupPath) + writeListToJson(appDb.readRecordDao.all, "readRecord.json", backupPath) + writeListToJson(appDb.searchKeywordDao.all, "searchHistory.json", backupPath) + writeListToJson(appDb.ruleSubDao.all, "sourceSub.json", backupPath) + writeListToJson(appDb.txtTocRuleDao.all, "txtTocRule.json", backupPath) + writeListToJson(appDb.httpTTSDao.all, "httpTTS.json", backupPath) + writeListToJson(appDb.keyboardAssistsDao.all, "keyboardAssists.json", backupPath) + writeListToJson(appDb.dictRuleDao.all, "dictRule.json", backupPath) + GSON.toJson(appDb.serverDao.all).let { json -> + aes.runCatching { + encryptBase64(json) + }.getOrDefault(json).let { + FileUtils.createFileIfNotExist(backupPath + File.separator + "servers.json") .writeText(it) } - GSON.toJson(ReadBookConfig.shareConfig).let { - FileUtils.createFileIfNotExist(backupPath + File.separator + ReadBookConfig.shareConfigFileName) - .writeText(it) - } - GSON.toJson(ThemeConfig.configList).let { - FileUtils.createFileIfNotExist(backupPath + File.separator + ThemeConfig.configFileName) - .writeText(it) - } - DirectLinkUpload.getConfig()?.let { - FileUtils.createFileIfNotExist(backupPath + File.separator + DirectLinkUpload.ruleFileName) - .writeText(GSON.toJson(it)) - } - ensureActive() - appCtx.getSharedPreferences(backupPath, "config")?.let { sp -> - val edit = sp.edit() - appCtx.defaultSharedPreferences.all.forEach { (key, value) -> - if (BackupConfig.keyIsNotIgnore(key)) { - when (key) { - PreferKey.webDavPassword -> { - edit.putString(key, aes.runCatching { - encryptBase64(value.toString()) - }.getOrDefault(value.toString())) - } + } + coroutineContext.ensureActive() + GSON.toJson(ReadBookConfig.configList).let { + FileUtils.createFileIfNotExist(backupPath + File.separator + ReadBookConfig.configFileName) + .writeText(it) + } + GSON.toJson(ReadBookConfig.shareConfig).let { + FileUtils.createFileIfNotExist(backupPath + File.separator + ReadBookConfig.shareConfigFileName) + .writeText(it) + } + GSON.toJson(ThemeConfig.configList).let { + FileUtils.createFileIfNotExist(backupPath + File.separator + ThemeConfig.configFileName) + .writeText(it) + } + DirectLinkUpload.getConfig()?.let { + FileUtils.createFileIfNotExist(backupPath + File.separator + DirectLinkUpload.ruleFileName) + .writeText(GSON.toJson(it)) + } + coroutineContext.ensureActive() + appCtx.getSharedPreferences(backupPath, "config")?.let { sp -> + val edit = sp.edit() + appCtx.defaultSharedPreferences.all.forEach { (key, value) -> + if (BackupConfig.keyIsNotIgnore(key)) { + when (key) { + PreferKey.webDavPassword -> { + edit.putString(key, aes.runCatching { + encryptBase64(value.toString()) + }.getOrDefault(value.toString())) + } - else -> when (value) { - is Int -> edit.putInt(key, value) - is Boolean -> edit.putBoolean(key, value) - is Long -> edit.putLong(key, value) - is Float -> edit.putFloat(key, value) - is String -> edit.putString(key, value) - } + else -> when (value) { + is Int -> edit.putInt(key, value) + is Boolean -> edit.putBoolean(key, value) + is Long -> edit.putLong(key, value) + is Float -> edit.putFloat(key, value) + is String -> edit.putString(key, value) } } } - edit.commit() } - ensureActive() - val zipFileName = getNowZipFileName() - val paths = arrayListOf(*backupFileNames) - for (i in 0 until paths.size) { - paths[i] = backupPath + File.separator + paths[i] - } - FileUtils.delete(zipFilePath) - FileUtils.delete(zipFilePath.replace("tmp_", "")) - val backupFileName = if (AppConfig.onlyLatestBackup) { - "backup.zip" - } else { - zipFileName - } - if (ZipUtils.zipFiles(paths, zipFilePath)) { - when { - path.isNullOrBlank() -> { - copyBackup(context.getExternalFilesDir(null)!!, backupFileName) - } - - path.isContentScheme() -> { - copyBackup(context, Uri.parse(path), backupFileName) - } - - else -> { - copyBackup(File(path), backupFileName) - } - } - AppWebDav.backUpWebDav(zipFileName) - } - FileUtils.delete(backupPath) - FileUtils.delete(zipFilePath) + edit.commit() } + coroutineContext.ensureActive() + val zipFileName = getNowZipFileName() + val paths = arrayListOf(*backupFileNames) + for (i in 0 until paths.size) { + paths[i] = backupPath + File.separator + paths[i] + } + FileUtils.delete(zipFilePath) + FileUtils.delete(zipFilePath.replace("tmp_", "")) + val backupFileName = if (AppConfig.onlyLatestBackup) { + "backup.zip" + } else { + zipFileName + } + if (ZipUtils.zipFiles(paths, zipFilePath)) { + when { + path.isNullOrBlank() -> { + copyBackup(context.getExternalFilesDir(null)!!, backupFileName) + } + + path.isContentScheme() -> { + copyBackup(context, Uri.parse(path), backupFileName) + } + + else -> { + copyBackup(File(path), backupFileName) + } + } + AppWebDav.backUpWebDav(zipFileName) + } + FileUtils.delete(backupPath) + FileUtils.delete(zipFilePath) } - private fun writeListToJson(list: List, fileName: String, path: String) { - if (list.isNotEmpty()) { - val file = FileUtils.createFileIfNotExist(path + File.separator + fileName) - FileOutputStream(file).use { fos -> - BufferedOutputStream(fos, 64 * 1024).use { - GSON.writeToOutputStream(it, list) + private suspend fun writeListToJson(list: List, fileName: String, path: String) { + coroutineContext.ensureActive() + withContext(IO) { + if (list.isNotEmpty()) { + val file = FileUtils.createFileIfNotExist(path + File.separator + fileName) + FileOutputStream(file).use { fos -> + BufferedOutputStream(fos, 64 * 1024).use { + GSON.writeToOutputStream(it, list) + } } } } diff --git a/app/src/main/java/io/legado/app/ui/book/search/SearchActivity.kt b/app/src/main/java/io/legado/app/ui/book/search/SearchActivity.kt index d0505c873..89ceb826e 100644 --- a/app/src/main/java/io/legado/app/ui/book/search/SearchActivity.kt +++ b/app/src/main/java/io/legado/app/ui/book/search/SearchActivity.kt @@ -269,7 +269,7 @@ class SearchActivity : VMBaseActivity