diff --git a/app/build.gradle b/app/build.gradle index 1b0c7d4b0..b2c3924d8 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -75,8 +75,11 @@ android { } compileOptions { - sourceCompatibility = '1.8' - targetCompatibility = '1.8' + // Flag to enable support for the new language APIs + //coreLibraryDesugaringEnabled true + // Sets Java compatibility to Java 8 + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 } kotlinOptions { diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index e4bc75603..f4d3af897 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -133,7 +133,9 @@ android:name=".ui.about.DonateActivity" android:launchMode="singleTask" /> - + @@ -144,7 +146,9 @@ - + @@ -159,7 +163,7 @@ + android:launchMode="singleTop"> @@ -171,16 +175,36 @@ android:scheme="yuedu" /> - - - - - - - - - - + + + + + + + + + + diff --git a/app/src/main/assets/txtTocRule.json b/app/src/main/assets/txtTocRule.json index 1304059d0..fc5469653 100644 --- a/app/src/main/assets/txtTocRule.json +++ b/app/src/main/assets/txtTocRule.json @@ -2,67 +2,73 @@ { "enable": true, "name": "目录", - "rule": "^[ \\t]{0,4}(?:(?:内容|文章)?简介|文案|前言|序章|楔子|正文(?!完)|终章|后记|尾声|番外|第?\\s{0,4}[\\d零一二两三四五六七八九十百千万壹贰叁肆伍陆柒捌玖拾佰仟]+?\\s{0,4}(?:章|节(?!课)|卷|集(?![合和])|部(?!分)|篇(?!张))).{0,30}$", + "rule": "^[  \\t]{0,4}(?:(?:内容|文章)?简介|文案|前言|序章|楔子|正文(?!完)|终章|后记|尾声|番外|第?\\s{0,4}[\\d零一二两三四五六七八九十百千万壹贰叁肆伍陆柒捌玖拾佰仟]+?\\s{0,4}(?:章|节(?!课)|卷|集(?![合和])|部(?!分)|篇(?!张))).{0,30}$", "serialNumber": 0 }, { "enable": false, "name": "目录(不匹配行前空白)", - "rule": "^(?<=\\s)(?:(?:内容|文章)?简介|文案|前言|序章|楔子|正文(?!完)|终章|后记|尾声|番外|第?\\s{0,4}[\\d零一二两三四五六七八九十百千万壹贰叁肆伍陆柒捌玖拾佰仟]+?\\s{0,4}(?:章|节(?!课)|卷|集(?![合和])|部(?!分)|篇(?!张))).{0,30}$", + "rule": "^(?<= |\\s)(?:(?:内容|文章)?简介|文案|前言|序章|楔子|正文(?!完)|终章|后记|尾声|番外|第?\\s{0,4}[\\d零一二两三四五六七八九十百千万壹贰叁肆伍陆柒捌玖拾佰仟]+?\\s{0,4}(?:章|节(?!课)|卷|集(?![合和])|部(?!分)|篇(?!张))).{0,30}$", "serialNumber": 1 }, { "enable": false, "name": "目录(去简介)", - "rule": "^(?<=\\s)(?:前言|序章|楔子|正文(?!完)|终章|后记|尾声|番外|第?\\s{0,4}[\\d零一二两三四五六七八九十百千万壹贰叁肆伍陆柒捌玖拾佰仟]+?\\s{0,4}(?:章|节(?!课)|卷|集(?![合和])|部(?!分)|篇(?!张))).{0,30}$", + "rule": "^(?<= |\\s)(?:前言|序章|楔子|正文(?!完)|终章|后记|尾声|番外|第?\\s{0,4}[\\d零一二两三四五六七八九十百千万壹贰叁肆伍陆柒捌玖拾佰仟]+?\\s{0,4}(?:章|节(?!课)|卷|集(?![合和])|部(?!分)|回(?![合来事去])|场(?![和合比电是])|篇(?!张))).{0,30}$", "serialNumber": 2 }, { "enable": false, "name": "目录(古典、轻小说备用)", - "rule": "^[ \\t]{0,4}(?:前言|序章|楔子|正文(?!完)|终章|后记|尾声|番外|第?\\s{0,4}[\\d零一二两三四五六七八九十百千万壹贰叁肆伍陆柒捌玖拾佰仟]+?\\s{0,4}(?:章|节(?!课)|卷|集(?![合和])|部(?!分)|回(?![合来事去])|场(?![和合比电是])|篇(?!张))).{0,30}$", + "rule": "^[  \\t]{0,4}(?:前言|序章|楔子|正文(?!完)|终章|后记|尾声|番外|第?\\s{0,4}[\\d零一二两三四五六七八九十百千万壹贰叁肆伍陆柒捌玖拾佰仟]+?\\s{0,4}(?:章|节(?!课)|卷|集(?![合和])|部(?!分)|回(?![合来事去])|场(?![和合比电是])|篇(?!张))).{0,30}$", "serialNumber": 3 }, { "enable": true, "name": "数字 分隔符 标题名称", - "rule": "^[ \\t]{0,4}\\d{1,5}[\\,\\., 、\\-].{1,30}$", + "rule": "^[  \\t]{0,4}\\d{1,5}[\\,\\., 、\\-].{1,30}$", "serialNumber": 4 }, { "enable": true, "name": "正文 标题/序号", - "rule": "^[ \\t]{0,4}正文\\s{1,4}.{0,20}$", + "rule": "^[  \\t]{0,4}正文\\s{1,4}.{0,20}$", "serialNumber": 5 }, { "enable": true, - "name": "Chapter/Section/Part 序号 标题", - "rule": "^[ \\t]{0,4}(?:[Cc]hapter|[Ss]ection|[Pp]art)\\s{0,4}\\d{1,4}.{0,30}$", + "name": "Chapter/Section/Part/Episode 序号 标题", + "rule": "^[  \\t]{0,4}(?:[Cc]hapter|[Ss]ection|[Pp]art|PART|[Ee]pisode)\\s{0,4}\\d{1,4}.{0,30}$", "serialNumber": 6 }, { "enable": true, "name": "特殊符号 序号 标题", - "rule": "^[ \\t]{0,4}[〈〖〔【][第卷][\\d零一二两三四五六七八九十百千万壹贰叁肆伍陆柒捌玖拾佰仟]{1,10}[章节][\\.:: \f\t].{0,30}$", + "rule": "^[  \\t]{0,4}[〈〖〔【][第卷][\\d零一二两三四五六七八九十百千万壹贰叁肆伍陆柒捌玖拾佰仟]{1,10}[章节][\\.:: \f\t].{0,30}$", "serialNumber": 7 }, { "enable": true, "name": "特殊符号 标题", - "rule": "^[ \\t]{0,4}[\\[〈「『〖〔《(【\\(☆★].{1,30}[\\)】)》〕〗』」〉\\]]?\\s{0,4}$", + "rule": "^[  \\t]{0,4}[\\[〈「『〖〔《(【\\(☆★].{1,30}[\\)】)》〕〗』」〉\\]]?\\s{0,4}$", "serialNumber": 8 }, { "enable":false, "name": "特殊符号 标题(不匹配空白字符)", - "rule": "^(?<=\\s)[\\[〈「『〖〔《(【\\(☆★].{1,30}[\\)】)》〕〗』」〉\\]]?\\s{0,4}$", + "rule": "^(?<= |\\s)[\\[〈「『〖〔《(【\\(☆★].{1,30}[\\)】)》〕〗』」〉\\]]?\\s{0,4}$", "serialNumber": 9 }, + { + "enable": true, + "name": "章/卷 序号 标题", + "rule": "^[  \\t]{0,4}(?:(?:内容|文章)?简介|文案|前言|序章|楔子|正文(?!完)|终章|后记|尾声|番外|[卷章][\\d零一二两三四五六七八九十百千万壹贰叁肆伍陆柒捌玖拾佰仟]{1,8})\\s{0,4}.{0,30}$", + "serialNumber": 10 + }, { "enable":false, "name": "顶格标题", "rule": "^\\S.{1,20}$", - "serialNumber": 10 + "serialNumber": 11 } ] diff --git a/app/src/main/assets/updateLog.md b/app/src/main/assets/updateLog.md index aabd6c581..630769a92 100644 --- a/app/src/main/assets/updateLog.md +++ b/app/src/main/assets/updateLog.md @@ -1,6 +1,22 @@ ## 更新日志 -* 旧版数据导入教程: -* 先在旧版阅读(2.x)中进行备份,然后在新版阅读(3.x)【我的】->【备份与恢复】,选择【导入旧版本数据】。 +* 旧版数据导入教程:先在旧版阅读(2.x)中进行备份,然后在新版阅读(3.x)【我的】->【备份与恢复】,选择【导入旧版本数据】。 +* 请关注[开源阅读软件]()支持我,同时关注合作公众号[小说拾遗](),阅读公众号小编。 + +**2020/03/02** +* 添加书源登录 +* 替换规则实时生效 +* 页面最后一行计算是否能放下时不计算行距 +* 优化翻页动画 +* 优化书源校验 +* 按键翻页有动画了 + +**2020/03/01** +* 修复书源解析的一个bug +* 添加底部操作栏颜色配置 +* 修复滚动点击翻页,修复滚动最后一页显示加载中 +* 去除备份恢复默认路径 +* 尝试修复部分手机一键导入书源报错 +* 翻页还有些bug不用反馈了,我已经知道,会修复的 **2020/02/29** * 添加书源一键导入 diff --git a/app/src/main/java/io/legado/app/App.kt b/app/src/main/java/io/legado/app/App.kt index cf0f4cf43..95f29cd64 100644 --- a/app/src/main/java/io/legado/app/App.kt +++ b/app/src/main/java/io/legado/app/App.kt @@ -78,26 +78,24 @@ class App : Application() { ThemeStore.editTheme(this) .primaryColor( getPrefInt("colorPrimaryNight", getCompatColor(R.color.md_blue_grey_600)) - ) - .accentColor( + ).accentColor( getPrefInt("colorAccentNight", getCompatColor(R.color.md_brown_800)) - ) - .backgroundColor( + ).backgroundColor( getPrefInt("colorBackgroundNight", getCompatColor(R.color.shine_color)) - ) - .apply() + ).bottomBackground( + getPrefInt("colorBottomBackgroundNight", getCompatColor(R.color.md_grey_850)) + ).apply() } else { ThemeStore.editTheme(this) .primaryColor( getPrefInt("colorPrimary", getCompatColor(R.color.md_indigo_800)) - ) - .accentColor( + ).accentColor( getPrefInt("colorAccent", getCompatColor(R.color.md_red_600)) - ) - .backgroundColor( + ).backgroundColor( getPrefInt("colorBackground", getCompatColor(R.color.md_grey_100)) - ) - .apply() + ).bottomBackground( + getPrefInt("colorBottomBackground", getCompatColor(R.color.md_grey_200)) + ).apply() } } diff --git a/app/src/main/java/io/legado/app/help/BookHelp.kt b/app/src/main/java/io/legado/app/help/BookHelp.kt index c7560b819..7df2540c5 100644 --- a/app/src/main/java/io/legado/app/help/BookHelp.kt +++ b/app/src/main/java/io/legado/app/help/BookHelp.kt @@ -237,14 +237,30 @@ object BookHelp { private var bookName: String? = null private var bookOrigin: String? = null private var replaceRules: List = arrayListOf() - var bodyIndentCount = App.INSTANCE.getPrefInt(PreferKey.bodyIndent, 2) - set(value) { - field = value - App.INSTANCE.putPrefInt(PreferKey.bodyIndent, value) - bodyIndent = " ".repeat(value) - } - var bodyIndent = " ".repeat(bodyIndentCount) + @Synchronized + fun upReplaceRules(name: String? = null, origin: String? = null) { + if (name != null) { + if (bookName != name || bookOrigin != origin) { + replaceRules = if (origin.isNullOrEmpty()) { + App.db.replaceRuleDao().findEnabledByScope(name) + } else { + App.db.replaceRuleDao().findEnabledByScope(name, origin) + } + bookName = name + bookOrigin = origin + } + } else { + val o = bookOrigin + bookName?.let { + replaceRules = if (o.isNullOrEmpty()) { + App.db.replaceRuleDao().findEnabledByScope(it) + } else { + App.db.replaceRuleDao().findEnabledByScope(it, o) + } + } + } + } fun disposeContent( title: String, @@ -253,23 +269,17 @@ object BookHelp { content: String, enableReplace: Boolean ): String { - synchronized(this) { - if (enableReplace && (bookName != name || bookOrigin != origin)) { - replaceRules = if (origin.isNullOrEmpty()) { - App.db.replaceRuleDao().findEnabledByScope(name) - } else { - App.db.replaceRuleDao().findEnabledByScope(name, origin) - } - } - } var c = content - for (item in replaceRules) { - item.pattern.let { - if (it.isNotEmpty()) { - c = if (item.isRegex) { - c.replace(it.toRegex(), item.replacement) - } else { - c.replace(it, item.replacement) + if (enableReplace) { + upReplaceRules(name, origin) + for (item in replaceRules) { + item.pattern.let { + if (it.isNotEmpty()) { + c = if (item.isRegex) { + c.replace(it.toRegex(), item.replacement) + } else { + c.replace(it, item.replacement) + } } } } @@ -281,6 +291,6 @@ object BookHelp { 1 -> c = ZhConvertBootstrap.newInstance().toSimple(c) 2 -> c = ZhConvertBootstrap.newInstance().toTraditional(c) } - return c.replace("\\s*\\n+\\s*".toRegex(), "\n$bodyIndent") + return c.replace("\\s*\\n+\\s*".toRegex(), "\n${ReadBookConfig.bodyIndent}") } } \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/help/JsExtensions.kt b/app/src/main/java/io/legado/app/help/JsExtensions.kt index 0160000f7..32da4bb06 100644 --- a/app/src/main/java/io/legado/app/help/JsExtensions.kt +++ b/app/src/main/java/io/legado/app/help/JsExtensions.kt @@ -17,7 +17,7 @@ interface JsExtensions { fun ajax(urlStr: String): String? { return try { val analyzeUrl = AnalyzeUrl(urlStr, null, null, null, null, null) - val call = analyzeUrl.getResponse() + val call = analyzeUrl.getResponse(urlStr) val response = call.execute() response.body() } catch (e: Exception) { diff --git a/app/src/main/java/io/legado/app/help/ReadBookConfig.kt b/app/src/main/java/io/legado/app/help/ReadBookConfig.kt index 229d5d7e6..a3a6de521 100644 --- a/app/src/main/java/io/legado/app/help/ReadBookConfig.kt +++ b/app/src/main/java/io/legado/app/help/ReadBookConfig.kt @@ -26,24 +26,6 @@ object ReadBookConfig { } val durConfig get() = getConfig(styleSelect) private val shareConfig get() = getConfig(5) - var styleSelect = App.INSTANCE.getPrefInt(PreferKey.readStyleSelect) - set(value) { - field = value - App.INSTANCE.putPrefInt(PreferKey.readStyleSelect, value) - } - var shareLayout = App.INSTANCE.getPrefBoolean(PreferKey.shareLayout) - set(value) { - field = value - App.INSTANCE.putPrefBoolean(PreferKey.shareLayout, value) - } - var pageAnim = App.INSTANCE.getPrefInt(PreferKey.pageAnim) - set(value) { - field = value - isScroll = value == 3 - App.INSTANCE.putPrefInt(PreferKey.pageAnim, value) - } - var isScroll = pageAnim == 3 - val clickTurnPage get() = App.INSTANCE.getPrefBoolean(PreferKey.clickTurnPage, true) var bg: Drawable? = null init { @@ -113,6 +95,39 @@ object ReadBookConfig { } //配置写入读取 + var styleSelect = App.INSTANCE.getPrefInt(PreferKey.readStyleSelect) + set(value) { + field = value + if (App.INSTANCE.getPrefInt(PreferKey.readStyleSelect) != value) { + App.INSTANCE.putPrefInt(PreferKey.readStyleSelect, value) + } + } + var shareLayout = App.INSTANCE.getPrefBoolean(PreferKey.shareLayout) + set(value) { + field = value + if (App.INSTANCE.getPrefBoolean(PreferKey.shareLayout) != value) { + App.INSTANCE.putPrefBoolean(PreferKey.shareLayout, value) + } + } + var pageAnim = App.INSTANCE.getPrefInt(PreferKey.pageAnim) + set(value) { + field = value + isScroll = value == 3 + if (App.INSTANCE.getPrefInt(PreferKey.pageAnim) != value) { + App.INSTANCE.putPrefInt(PreferKey.pageAnim, value) + } + } + var isScroll = pageAnim == 3 + val clickTurnPage get() = App.INSTANCE.getPrefBoolean(PreferKey.clickTurnPage, true) + var bodyIndentCount = App.INSTANCE.getPrefInt(PreferKey.bodyIndent, 2) + set(value) { + field = value + bodyIndent = " ".repeat(value) + if (App.INSTANCE.getPrefInt(PreferKey.bodyIndent, 2) != value) { + App.INSTANCE.putPrefInt(PreferKey.bodyIndent, value) + } + } + var bodyIndent = " ".repeat(bodyIndentCount) var hideStatusBar = App.INSTANCE.getPrefBoolean(PreferKey.hideStatusBar) var hideNavigationBar = App.INSTANCE.getPrefBoolean(PreferKey.hideNavigationBar) var textBold: Boolean diff --git a/app/src/main/java/io/legado/app/help/http/AjaxWebView.kt b/app/src/main/java/io/legado/app/help/http/AjaxWebView.kt index 81d0347b8..f501fcb71 100644 --- a/app/src/main/java/io/legado/app/help/http/AjaxWebView.kt +++ b/app/src/main/java/io/legado/app/help/http/AjaxWebView.kt @@ -1,7 +1,6 @@ package io.legado.app.help.http import android.annotation.SuppressLint -import android.net.http.SslError import android.os.Build import android.os.Handler import android.os.Looper @@ -167,10 +166,6 @@ class AjaxWebView { ).sendToTarget() } } - - override fun onReceivedSslError(view: WebView, handler: SslErrorHandler, error: SslError) { - handler.proceed() - } } private class EvalJsRunnable( @@ -242,10 +237,6 @@ class AjaxWebView { } } - override fun onReceivedSslError(view: WebView, handler: SslErrorHandler, error: SslError) { - handler.proceed() - } - override fun onPageFinished(view: WebView, url: String) { params.setCookie(url) if (params.hasJavaScript()) { diff --git a/app/src/main/java/io/legado/app/help/http/CookieStore.kt b/app/src/main/java/io/legado/app/help/http/CookieStore.kt index d01eb465c..d82a4b175 100644 --- a/app/src/main/java/io/legado/app/help/http/CookieStore.kt +++ b/app/src/main/java/io/legado/app/help/http/CookieStore.kt @@ -3,15 +3,15 @@ package io.legado.app.help.http import android.text.TextUtils import io.legado.app.App import io.legado.app.data.entities.Cookie +import io.legado.app.help.coroutine.Coroutine object CookieStore { fun setCookie(url: String, cookie: String?) { - try { + Coroutine.async { val cookieBean = Cookie(url, cookie ?: "") App.db.cookieDao().insert(cookieBean) - } catch (ignore: Exception) { } } 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 921910f34..8c4fa8fc2 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 @@ -101,9 +101,8 @@ object Backup { val file = File(backupPath + File.separator + fileName) if (file.exists()) { treeDoc.findFile(fileName)?.delete() - treeDoc.createFile("", fileName)?.let { - DocumentUtils.writeBytes(context, file.readBytes(), it.uri) - } + treeDoc.createFile("", fileName) + ?.writeBytes(context, file.readBytes()) } } } diff --git a/app/src/main/java/io/legado/app/help/storage/ImportOldData.kt b/app/src/main/java/io/legado/app/help/storage/ImportOldData.kt index 7329705e9..8d0c9cd2c 100644 --- a/app/src/main/java/io/legado/app/help/storage/ImportOldData.kt +++ b/app/src/main/java/io/legado/app/help/storage/ImportOldData.kt @@ -17,15 +17,12 @@ import org.jetbrains.anko.toast import java.io.File object ImportOldData { - val yueDuPath by lazy { - FileUtils.getSdCardPath() + File.separator + "YueDu" - } - fun import(context: Context) { + fun import(context: Context, file: File) { GlobalScope.launch(Dispatchers.IO) { try {// 导入书架 val shelfFile = - FileUtils.createFileIfNotExist(yueDuPath + File.separator + "myBookShelf.json") + FileUtils.createFileIfNotExist(file, "myBookShelf.json") val json = shelfFile.readText() val importCount = importOldBookshelf(json) withContext(Dispatchers.Main) { @@ -39,7 +36,7 @@ object ImportOldData { try {// Book source val sourceFile = - FileUtils.createFileIfNotExist(yueDuPath + File.separator + "myBookSource.json") + FileUtils.getFile(file, "myBookSource.json") val json = sourceFile.readText() val importCount = importOldSource(json) withContext(Dispatchers.Main) { @@ -52,12 +49,17 @@ object ImportOldData { } try {// Replace rules - val ruleFile = - FileUtils.createFileIfNotExist(yueDuPath + File.separator + "myBookReplaceRule.json") - val json = ruleFile.readText() - val importCount = importOldReplaceRule(json) - withContext(Dispatchers.Main) { - context.toast("成功导入替换规则${importCount}") + val ruleFile = FileUtils.getFile(file, "myBookReplaceRule.json") + if (ruleFile.exists()) { + val json = ruleFile.readText() + val importCount = importOldReplaceRule(json) + withContext(Dispatchers.Main) { + context.toast("成功导入替换规则${importCount}") + } + } else { + withContext(Dispatchers.Main) { + context.toast("未找到替换规则") + } } } catch (e: Exception) { withContext(Dispatchers.Main) { @@ -115,13 +117,13 @@ object ImportOldData { } } - fun importOldBookshelf(json: String): Int { + private fun importOldBookshelf(json: String): Int { val books = OldBook.toNewBook(json) App.db.bookDao().insert(*books.toTypedArray()) return books.size } - fun importOldSource(json: String): Int { + private fun importOldSource(json: String): Int { val bookSources = mutableListOf() val items: List> = Restore.jsonPath.parse(json).read("$") for (item in items) { diff --git a/app/src/main/java/io/legado/app/help/storage/Restore.kt b/app/src/main/java/io/legado/app/help/storage/Restore.kt index dd48d6472..d9be6e9bd 100644 --- a/app/src/main/java/io/legado/app/help/storage/Restore.kt +++ b/app/src/main/java/io/legado/app/help/storage/Restore.kt @@ -114,6 +114,7 @@ object Restore { pageAnim = App.INSTANCE.getPrefInt(PreferKey.pageAnim) hideStatusBar = App.INSTANCE.getPrefBoolean(PreferKey.hideStatusBar) hideNavigationBar = App.INSTANCE.getPrefBoolean(PreferKey.hideNavigationBar) + bodyIndentCount = App.INSTANCE.getPrefInt(PreferKey.bodyIndent, 2) } ChapterProvider.upStyle() ReadBook.loadContent() diff --git a/app/src/main/java/io/legado/app/lib/theme/ATH.kt b/app/src/main/java/io/legado/app/lib/theme/ATH.kt index e20741987..0bcbb114b 100644 --- a/app/src/main/java/io/legado/app/lib/theme/ATH.kt +++ b/app/src/main/java/io/legado/app/lib/theme/ATH.kt @@ -205,7 +205,7 @@ object ATH { .setSelectedColor(ThemeStore.accentColor(bottom_navigation_view.context)).create() itemIconTintList = colorStateList itemTextColor = colorStateList - itemBackgroundResource = R.color.background_menu + setBackgroundColor(ThemeStore.bottomBackground(context)) } } diff --git a/app/src/main/java/io/legado/app/lib/theme/MaterialValueHelper.kt b/app/src/main/java/io/legado/app/lib/theme/MaterialValueHelper.kt index 1618884b5..fab853c6b 100644 --- a/app/src/main/java/io/legado/app/lib/theme/MaterialValueHelper.kt +++ b/app/src/main/java/io/legado/app/lib/theme/MaterialValueHelper.kt @@ -54,6 +54,9 @@ val Context.accentColor: Int val Context.backgroundColor: Int get() = ThemeStore.backgroundColor(this) +val Context.bottomBackground: Int + get() = ThemeStore.bottomBackground(this) + val Context.primaryTextColor: Int get() = getPrimaryTextColor(isDarkTheme) @@ -78,6 +81,9 @@ val Fragment.accentColor: Int val Fragment.backgroundColor: Int get() = ThemeStore.backgroundColor(requireContext()) +val Fragment.bottomBackground: Int + get() = ThemeStore.bottomBackground(requireContext()) + val Fragment.primaryTextColor: Int get() = requireContext().getPrimaryTextColor(isDarkTheme) diff --git a/app/src/main/java/io/legado/app/lib/theme/ThemeStore.kt b/app/src/main/java/io/legado/app/lib/theme/ThemeStore.kt index 5335a6830..927fc2fb0 100644 --- a/app/src/main/java/io/legado/app/lib/theme/ThemeStore.kt +++ b/app/src/main/java/io/legado/app/lib/theme/ThemeStore.kt @@ -147,6 +147,11 @@ private constructor(private val mContext: Context) : ThemeStoreInterface { return this } + override fun bottomBackground(color: Int): ThemeStore { + mEditor.putInt(ThemeStorePrefKeys.KEY_BOTTOM_BACKGROUND, color) + return this + } + override fun coloredStatusBar(colored: Boolean): ThemeStore { mEditor.putBoolean(ThemeStorePrefKeys.KEY_APPLY_PRIMARYDARK_STATUSBAR, colored) return this @@ -279,6 +284,15 @@ private constructor(private val mContext: Context) : ThemeStoreInterface { ) } + @CheckResult + @ColorInt + fun bottomBackground(context: Context): Int { + return prefs(context).getInt( + ThemeStorePrefKeys.KEY_BOTTOM_BACKGROUND, + ATHUtils.resolveColor(context, android.R.attr.colorBackground) + ) + } + @CheckResult fun coloredStatusBar(context: Context): Boolean { return prefs(context).getBoolean(ThemeStorePrefKeys.KEY_APPLY_PRIMARYDARK_STATUSBAR, true) diff --git a/app/src/main/java/io/legado/app/lib/theme/ThemeStoreInterface.kt b/app/src/main/java/io/legado/app/lib/theme/ThemeStoreInterface.kt index 55584b633..521b9d237 100644 --- a/app/src/main/java/io/legado/app/lib/theme/ThemeStoreInterface.kt +++ b/app/src/main/java/io/legado/app/lib/theme/ThemeStoreInterface.kt @@ -78,8 +78,12 @@ internal interface ThemeStoreInterface { fun textColorSecondaryInverseAttr(@AttrRes colorAttr: Int): ThemeStore + // Background + fun backgroundColor(@ColorInt color: Int): ThemeStore + fun bottomBackground(@ColorInt color: Int): ThemeStore + // Toggle configurations fun coloredStatusBar(colored: Boolean): ThemeStore diff --git a/app/src/main/java/io/legado/app/lib/theme/ThemeStorePrefKeys.kt b/app/src/main/java/io/legado/app/lib/theme/ThemeStorePrefKeys.kt index c934ef176..a2ab19d60 100644 --- a/app/src/main/java/io/legado/app/lib/theme/ThemeStorePrefKeys.kt +++ b/app/src/main/java/io/legado/app/lib/theme/ThemeStorePrefKeys.kt @@ -22,6 +22,7 @@ object ThemeStorePrefKeys { const val KEY_TEXT_COLOR_SECONDARY_INVERSE = "text_color_secondary_inverse" const val KEY_BACKGROUND_COLOR = "backgroundColor" + const val KEY_BOTTOM_BACKGROUND = "bottomBackground" const val KEY_APPLY_PRIMARYDARK_STATUSBAR = "apply_primarydark_statusbar" const val KEY_APPLY_PRIMARY_NAVBAR = "apply_primary_navbar" diff --git a/app/src/main/java/io/legado/app/model/Rss.kt b/app/src/main/java/io/legado/app/model/Rss.kt index c2568f458..b3622b40b 100644 --- a/app/src/main/java/io/legado/app/model/Rss.kt +++ b/app/src/main/java/io/legado/app/model/Rss.kt @@ -35,7 +35,7 @@ object Rss { ): Coroutine { return Coroutine.async(scope, context) { val body = AnalyzeUrl(rssArticle.link, baseUrl = rssArticle.origin) - .getResponseAwait() + .getResponseAwait(rssArticle.origin) .body val analyzeRule = AnalyzeRule() analyzeRule.setContent( diff --git a/app/src/main/java/io/legado/app/model/WebBook.kt b/app/src/main/java/io/legado/app/model/WebBook.kt index bb3f80b8d..5a51b486c 100644 --- a/app/src/main/java/io/legado/app/model/WebBook.kt +++ b/app/src/main/java/io/legado/app/model/WebBook.kt @@ -37,7 +37,7 @@ class WebBook(val bookSource: BookSource) { baseUrl = sourceUrl, headerMapF = bookSource.getHeaderMap() ) - val res = analyzeUrl.getResponseAwait() + val res = analyzeUrl.getResponseAwait(bookSource.bookSourceUrl) BookList.analyzeBookList( res.body, bookSource, @@ -65,7 +65,7 @@ class WebBook(val bookSource: BookSource) { baseUrl = sourceUrl, headerMapF = bookSource.getHeaderMap() ) - val res = analyzeUrl.getResponseAwait() + val res = analyzeUrl.getResponseAwait(bookSource.bookSourceUrl) BookList.analyzeBookList( res.body, bookSource, @@ -96,7 +96,7 @@ class WebBook(val bookSource: BookSource) { baseUrl = sourceUrl, headerMapF = bookSource.getHeaderMap() ) - analyzeUrl.getResponseAwait().body + analyzeUrl.getResponseAwait(bookSource.bookSourceUrl).body } BookInfo.analyzeBookInfo(book, body, bookSource, book.bookUrl) book @@ -122,7 +122,7 @@ class WebBook(val bookSource: BookSource) { ruleUrl = book.tocUrl, baseUrl = book.bookUrl, headerMapF = bookSource.getHeaderMap() - ).getResponseAwait().body + ).getResponseAwait(bookSource.bookSourceUrl).body } BookChapterList.analyzeChapterList(this, book, body, bookSource, book.tocUrl) } diff --git a/app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeRule.kt b/app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeRule.kt index f9dbba8ee..f88a5ee85 100644 --- a/app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeRule.kt +++ b/app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeRule.kt @@ -610,7 +610,7 @@ class AnalyzeRule(var book: BaseBook? = null) : JsExtensions { override fun ajax(urlStr: String): String? { return try { val analyzeUrl = AnalyzeUrl(urlStr, null, null, null, baseUrl, book) - val call = analyzeUrl.getResponse() + val call = analyzeUrl.getResponse(urlStr) val response = call.execute() response.body() } catch (e: Exception) { diff --git a/app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeUrl.kt b/app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeUrl.kt index f1c92e798..9ac402267 100644 --- a/app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeUrl.kt +++ b/app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeUrl.kt @@ -8,10 +8,7 @@ import io.legado.app.constant.Pattern.EXP_PATTERN import io.legado.app.constant.Pattern.JS_PATTERN import io.legado.app.data.entities.BaseBook import io.legado.app.help.JsExtensions -import io.legado.app.help.http.AjaxWebView -import io.legado.app.help.http.HttpHelper -import io.legado.app.help.http.RequestMethod -import io.legado.app.help.http.Res +import io.legado.app.help.http.* import io.legado.app.help.http.api.HttpGetApi import io.legado.app.help.http.api.HttpPostApi import io.legado.app.utils.* @@ -243,7 +240,11 @@ class AnalyzeUrl( } @Throws(Exception::class) - fun getResponse(): Call { + fun getResponse(tag: String): Call { + val cookie = CookieStore.getCookie(tag) + if (cookie.isNotEmpty()) { + headerMap["Cookie"] = cookie + } return when { method == RequestMethod.POST -> { if (fieldMap.isNotEmpty()) { @@ -267,7 +268,7 @@ class AnalyzeUrl( @Throws(Exception::class) suspend fun getResponseAwait( - tag: String? = null, + tag: String, jsStr: String? = null, sourceRegex: String? = null ): Res { @@ -278,8 +279,13 @@ class AnalyzeUrl( params.javaScript = jsStr params.sourceRegex = sourceRegex params.postData = bodyTxt?.toByteArray() + params.tag = tag return HttpHelper.ajax(params) } + val cookie = CookieStore.getCookie(tag) + if (cookie.isNotEmpty()) { + headerMap["Cookie"] = cookie + } val res = when { method == RequestMethod.POST -> { if (fieldMap.isNotEmpty()) { diff --git a/app/src/main/java/io/legado/app/model/localBook/AnalyzeTxtFile.kt b/app/src/main/java/io/legado/app/model/localBook/AnalyzeTxtFile.kt index fad747b34..16426a036 100644 --- a/app/src/main/java/io/legado/app/model/localBook/AnalyzeTxtFile.kt +++ b/app/src/main/java/io/legado/app/model/localBook/AnalyzeTxtFile.kt @@ -20,7 +20,7 @@ object AnalyzeTxtFile { private const val BUFFER_SIZE = 512 * 1024 //没有标题的时候,每个章节的最大长度 private const val MAX_LENGTH_WITH_NO_CHAPTER = 10 * 1024 - private val cacheFolder: File by lazy { + val cacheFolder: File by lazy { val rootFile = App.INSTANCE.getExternalFilesDir(null) ?: App.INSTANCE.externalCacheDir ?: App.INSTANCE.cacheDir diff --git a/app/src/main/java/io/legado/app/model/localBook/LocalBook.kt b/app/src/main/java/io/legado/app/model/localBook/LocalBook.kt index 44ee337b2..c58410cd7 100644 --- a/app/src/main/java/io/legado/app/model/localBook/LocalBook.kt +++ b/app/src/main/java/io/legado/app/model/localBook/LocalBook.kt @@ -1,8 +1,10 @@ package io.legado.app.model.localBook +import android.net.Uri import androidx.documentfile.provider.DocumentFile import io.legado.app.App import io.legado.app.data.entities.Book +import io.legado.app.utils.FileUtils object LocalBook { @@ -27,4 +29,16 @@ object LocalBook { } } + fun deleteBook(book: Book, deleteOriginal: Boolean) { + if (book.isTxt()) { + val bookFile = + FileUtils.getFile(AnalyzeTxtFile.cacheFolder, book.originName, subDirs = *arrayOf()) + bookFile.delete() + } + + if (deleteOriginal) { + val uri = Uri.parse(book.bookUrl) + DocumentFile.fromSingleUri(App.INSTANCE, uri)?.delete() + } + } } \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/model/webBook/BookChapterList.kt b/app/src/main/java/io/legado/app/model/webBook/BookChapterList.kt index aa0b7de74..8559351f4 100644 --- a/app/src/main/java/io/legado/app/model/webBook/BookChapterList.kt +++ b/app/src/main/java/io/legado/app/model/webBook/BookChapterList.kt @@ -31,7 +31,7 @@ object BookChapterList { App.INSTANCE.getString(R.string.error_get_web_content, baseUrl) ) Debug.log(bookSource.bookSourceUrl, "≡获取成功:${baseUrl}") - val analyzeRule = AnalyzeRule(book) + val tocRule = bookSource.getTocRule() val nextUrlList = arrayListOf(baseUrl) var reverse = false @@ -45,8 +45,7 @@ object BookChapterList { } var chapterData = analyzeChapterList( - analyzeRule.setContent(body, baseUrl), - book.bookUrl, baseUrl, tocRule, listRule, bookSource, log = true + book, baseUrl, body, tocRule, listRule, bookSource, log = true ) chapterData.chapterList?.let { chapterList.addAll(it) @@ -64,11 +63,10 @@ object BookChapterList { ruleUrl = nextUrl, book = book, headerMapF = bookSource.getHeaderMap() - ).getResponseAwait() + ).getResponseAwait(bookSource.bookSourceUrl) .body?.let { nextBody -> chapterData = analyzeChapterList( - analyzeRule.setContent(nextBody, nextUrl), - book.bookUrl, nextUrl, tocRule, listRule, bookSource + book, nextUrl, nextBody, tocRule, listRule, bookSource ) nextUrl = if (chapterData.nextUrl.isNotEmpty()) { chapterData.nextUrl[0] @@ -95,35 +93,20 @@ object BookChapterList { } Debug.log(bookSource.bookSourceUrl, "◇目录总页数:${nextUrlList.size}") for (item in chapterDataList) { - Coroutine.async(scope = coroutineScope) { - val nextBody = AnalyzeUrl( - ruleUrl = item.nextUrl, - book = book, - headerMapF = bookSource.getHeaderMap() - ).getResponseAwait().body - val nextChapterData = analyzeChapterList( - analyzeRule.setContent(nextBody, item.nextUrl), - book.bookUrl, item.nextUrl, tocRule, listRule, bookSource, - false - ) - synchronized(chapterDataList) { - val isFinished = addChapterListIsFinish( - chapterDataList, - item, - nextChapterData.chapterList - ) - if (isFinished) { - chapterDataList.forEach { item -> - item.chapterList?.let { - chapterList.addAll(it) - } - } - block.resume(finish(book, chapterList, reverse)) - } - } - }.onError { - block.resumeWithException(it) - } + downloadToc( + coroutineScope, + item, + book, + bookSource, + tocRule, + listRule, + chapterList, + chapterDataList, + { + block.resume(finish(book, chapterList, reverse)) + }, { + block.cancel(it) + }) } } } @@ -132,6 +115,49 @@ object BookChapterList { } } + private fun downloadToc( + coroutineScope: CoroutineScope, + chapterData: ChapterData, + book: Book, + bookSource: BookSource, + tocRule: TocRule, + listRule: String, + chapterList: ArrayList, + chapterDataList: ArrayList>, + onFinish: () -> Unit, + onError: (e: Throwable) -> Unit + ) { + Coroutine.async(scope = coroutineScope) { + val nextBody = AnalyzeUrl( + ruleUrl = chapterData.nextUrl, + book = book, + headerMapF = bookSource.getHeaderMap() + ).getResponseAwait(bookSource.bookSourceUrl).body + ?: throw Exception("${chapterData.nextUrl}, 下载失败") + val nextChapterData = analyzeChapterList( + book, chapterData.nextUrl, nextBody, tocRule, listRule, bookSource, + false + ) + synchronized(chapterDataList) { + val isFinished = addChapterListIsFinish( + chapterDataList, + chapterData, + nextChapterData.chapterList + ) + if (isFinished) { + chapterDataList.forEach { item -> + item.chapterList?.let { + chapterList.addAll(it) + } + } + onFinish() + } + } + }.onError { + onError(it) + } + } + private fun addChapterListIsFinish( chapterDataList: ArrayList>, chapterData: ChapterData, @@ -173,15 +199,17 @@ object BookChapterList { } private fun analyzeChapterList( - analyzeRule: AnalyzeRule, - bookUrl: String, + book: Book, baseUrl: String, + body: String, tocRule: TocRule, listRule: String, bookSource: BookSource, getNextUrl: Boolean = true, log: Boolean = false ): ChapterData> { + val analyzeRule = AnalyzeRule(book) + analyzeRule.setContent(body, baseUrl) val chapterList = arrayListOf() val nextUrlList = arrayListOf() val nextTocRule = tocRule.nextTocUrl @@ -212,7 +240,7 @@ object BookChapterList { var isVip: String? for (item in elements) { analyzeRule.setContent(item) - val bookChapter = BookChapter(bookUrl = bookUrl) + val bookChapter = BookChapter(bookUrl = book.bookUrl) analyzeRule.chapter = bookChapter bookChapter.title = analyzeRule.getString(nameRule) bookChapter.url = analyzeRule.getString(urlRule, true) diff --git a/app/src/main/java/io/legado/app/model/webBook/BookContent.kt b/app/src/main/java/io/legado/app/model/webBook/BookContent.kt index d8e874195..14ea49b25 100644 --- a/app/src/main/java/io/legado/app/model/webBook/BookContent.kt +++ b/app/src/main/java/io/legado/app/model/webBook/BookContent.kt @@ -33,13 +33,11 @@ object BookContent { ) ) Debug.log(bookSource.bookSourceUrl, "≡获取成功:${baseUrl}") - val analyzeRule = AnalyzeRule(book) val content = StringBuilder() val nextUrlList = arrayListOf(baseUrl) val contentRule = bookSource.getContentRule() var contentData = analyzeContent( - analyzeRule.setContent(body, baseUrl), - contentRule, bookChapter, bookSource + book, baseUrl, body, contentRule, bookChapter, bookSource ) content.append(contentData.content.replace(bookChapter.title, "")) if (contentData.nextUrl.size == 1) { @@ -58,12 +56,11 @@ object BookContent { ruleUrl = nextUrl, book = book, headerMapF = bookSource.getHeaderMap() - ).getResponseAwait() + ).getResponseAwait(bookSource.bookSourceUrl) .body?.let { nextBody -> contentData = analyzeContent( - analyzeRule.setContent(nextBody, nextUrl), - contentRule, bookChapter, bookSource, false + book, nextUrl, nextBody, contentRule, bookChapter, bookSource, false ) nextUrl = if (contentData.nextUrl.isNotEmpty()) contentData.nextUrl[0] else "" @@ -83,12 +80,11 @@ object BookContent { ruleUrl = item.nextUrl, book = book, headerMapF = bookSource.getHeaderMap() - ).getResponseAwait() + ).getResponseAwait(bookSource.bookSourceUrl) .body?.let { contentData = analyzeContent( - analyzeRule.setContent(it, item.nextUrl), - contentRule, bookChapter, bookSource, false + book, item.nextUrl, it, contentRule, bookChapter, bookSource, false ) item.content = contentData.content } @@ -108,12 +104,16 @@ object BookContent { @Throws(Exception::class) private fun analyzeContent( - analyzeRule: AnalyzeRule, + book: Book, + baseUrl: String, + body: String, contentRule: ContentRule, chapter: BookChapter, bookSource: BookSource, printLog: Boolean = true ): ContentData> { + val analyzeRule = AnalyzeRule(book) + analyzeRule.setContent(body, baseUrl) val nextUrlList = arrayListOf() analyzeRule.chapter = chapter val nextUrlRule = contentRule.nextContentUrl diff --git a/app/src/main/java/io/legado/app/service/CheckSourceService.kt b/app/src/main/java/io/legado/app/service/CheckSourceService.kt index d94b3a6d1..336dfb18d 100644 --- a/app/src/main/java/io/legado/app/service/CheckSourceService.kt +++ b/app/src/main/java/io/legado/app/service/CheckSourceService.kt @@ -18,11 +18,12 @@ import org.jetbrains.anko.toast import java.util.concurrent.Executors class CheckSourceService : BaseService() { - private var searchPool = - Executors.newFixedThreadPool(AppConfig.threadCount).asCoroutineDispatcher() + private val threadCount = AppConfig.threadCount + private var searchPool = Executors.newFixedThreadPool(threadCount).asCoroutineDispatcher() private var task: Coroutine<*>? = null - private val allIds = LinkedHashSet() - private val checkedIds = LinkedHashSet() + private val allIds = ArrayList() + private val checkedIds = ArrayList() + private var processIndex = 0 override fun onCreate() { super.onCreate() @@ -50,30 +51,44 @@ class CheckSourceService : BaseService() { allIds.clear() checkedIds.clear() allIds.addAll(ids) + processIndex = 0 updateNotification(0, getString(R.string.progress_show, 0, allIds.size)) - task = execute(context = searchPool) { - allIds.forEach { sourceUrl -> - App.db.bookSourceDao().getBookSource(sourceUrl)?.let { source -> - val webBook = WebBook(source) - webBook.searchBook("我的", scope = this, context = searchPool) - .onError(IO) { - source.addGroup("失效") - App.db.bookSourceDao().update(source) - }.onFinally { - checkedIds.add(sourceUrl) - updateNotification( - checkedIds.size, - getString(R.string.progress_show, checkedIds.size, allIds.size) - ) - } - } + task = execute { + for (i in 0 until threadCount) { + check() } }.onError { toast("校验书源出错:${it.localizedMessage}") } + } + - task?.invokeOnCompletion { - stopSelf() + private fun check() { + synchronized(this) { + processIndex++ + } + if (processIndex < allIds.size) { + val sourceUrl = allIds[processIndex] + App.db.bookSourceDao().getBookSource(sourceUrl)?.let { source -> + val webBook = WebBook(source) + webBook.searchBook("我的", scope = this, context = searchPool) + .onError(IO) { + source.addGroup("失效") + App.db.bookSourceDao().update(source) + }.onFinally(IO) { + check() + checkedIds.add(sourceUrl) + updateNotification( + checkedIds.size, + getString(R.string.progress_show, checkedIds.size, allIds.size) + ) + synchronized(this) { + if (processIndex >= allIds.size + threadCount - 1) { + stopSelf() + } + } + } + } } } diff --git a/app/src/main/java/io/legado/app/ui/README.md b/app/src/main/java/io/legado/app/ui/README.md index a8bf2b216..b408eb6b9 100644 --- a/app/src/main/java/io/legado/app/ui/README.md +++ b/app/src/main/java/io/legado/app/ui/README.md @@ -7,14 +7,14 @@ * book\read 书籍阅读界面 * book\search 搜索书籍界面 * book\source 搜索书源界面 -* changeCover 封面换源界面 -* changeSource 换源界面 -* chapterList 目录界面 -* config 配置界面 -* download 下载界面 -* explore 发现界面 +* book\changeCover 封面换源界面 +* book\changeSource 换源界面 +* book\chapterList 目录界面 +* book\download 下载界面 +* book\explore 发现界面 +* book\local 书籍导入界面 * fileChooser 文件选择界面 -* importBook 书籍导入界面 +* config 配置界面 * main 主界面 * qrCode 二维码扫描界面 * replaceRule 替换净化界面 diff --git a/app/src/main/java/io/legado/app/ui/audio/AudioPlayActivity.kt b/app/src/main/java/io/legado/app/ui/audio/AudioPlayActivity.kt index dffd6a50f..502e49986 100644 --- a/app/src/main/java/io/legado/app/ui/audio/AudioPlayActivity.kt +++ b/app/src/main/java/io/legado/app/ui/audio/AudioPlayActivity.kt @@ -25,8 +25,8 @@ import io.legado.app.lib.dialogs.noButton import io.legado.app.lib.dialogs.okButton import io.legado.app.service.AudioPlayService import io.legado.app.service.help.AudioPlay -import io.legado.app.ui.changesource.ChangeSourceDialog -import io.legado.app.ui.chapterlist.ChapterListActivity +import io.legado.app.ui.book.changesource.ChangeSourceDialog +import io.legado.app.ui.book.chapterlist.ChapterListActivity import io.legado.app.utils.* import kotlinx.android.synthetic.main.activity_audio_play.* import org.apache.commons.lang3.time.DateFormatUtils @@ -46,8 +46,9 @@ class AudioPlayActivity : private var adjustProgress = false override fun onActivityCreated(savedInstanceState: Bundle?) { - title_bar.background.alpha = 0 - AudioPlay.titleData.observe(this, Observer { title_bar.title = it }) + setSupportActionBar(toolbar) + supportActionBar?.setDisplayHomeAsUpEnabled(true) + AudioPlay.titleData.observe(this, Observer { toolbar.title = it }) AudioPlay.coverData.observe(this, Observer { upCover(it) }) viewModel.initData(intent) initView() diff --git a/app/src/main/java/io/legado/app/ui/changecover/ChangeCoverDialog.kt b/app/src/main/java/io/legado/app/ui/book/changecover/ChangeCoverDialog.kt similarity index 93% rename from app/src/main/java/io/legado/app/ui/changecover/ChangeCoverDialog.kt rename to app/src/main/java/io/legado/app/ui/book/changecover/ChangeCoverDialog.kt index d1c081f76..7a091c145 100644 --- a/app/src/main/java/io/legado/app/ui/changecover/ChangeCoverDialog.kt +++ b/app/src/main/java/io/legado/app/ui/book/changecover/ChangeCoverDialog.kt @@ -1,4 +1,4 @@ -package io.legado.app.ui.changecover +package io.legado.app.ui.book.changecover import android.os.Bundle import android.util.DisplayMetrics @@ -52,12 +52,12 @@ class ChangeCoverDialog : DialogFragment(), callBack = activity as? CallBack viewModel = getViewModel(ChangeCoverViewModel::class.java) viewModel.callBack = this - return inflater.inflate(R.layout.dialog_change_source, container) + return inflater.inflate(R.layout.dialog_change_cover, container) } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - viewModel.searchStateData.observe(this, Observer { + viewModel.searchStateData.observe(viewLifecycleOwner, Observer { refresh_progress_bar.isAutoLoading = it }) tool_bar.setTitle(R.string.change_cover_source) diff --git a/app/src/main/java/io/legado/app/ui/changecover/ChangeCoverViewModel.kt b/app/src/main/java/io/legado/app/ui/book/changecover/ChangeCoverViewModel.kt similarity index 98% rename from app/src/main/java/io/legado/app/ui/changecover/ChangeCoverViewModel.kt rename to app/src/main/java/io/legado/app/ui/book/changecover/ChangeCoverViewModel.kt index d8ed01d9f..3ef77e076 100644 --- a/app/src/main/java/io/legado/app/ui/changecover/ChangeCoverViewModel.kt +++ b/app/src/main/java/io/legado/app/ui/book/changecover/ChangeCoverViewModel.kt @@ -1,4 +1,4 @@ -package io.legado.app.ui.changecover +package io.legado.app.ui.book.changecover import android.app.Application import androidx.lifecycle.MutableLiveData diff --git a/app/src/main/java/io/legado/app/ui/changecover/CoverAdapter.kt b/app/src/main/java/io/legado/app/ui/book/changecover/CoverAdapter.kt similarity index 96% rename from app/src/main/java/io/legado/app/ui/changecover/CoverAdapter.kt rename to app/src/main/java/io/legado/app/ui/book/changecover/CoverAdapter.kt index eae791b2c..55a841fcd 100644 --- a/app/src/main/java/io/legado/app/ui/changecover/CoverAdapter.kt +++ b/app/src/main/java/io/legado/app/ui/book/changecover/CoverAdapter.kt @@ -1,4 +1,4 @@ -package io.legado.app.ui.changecover +package io.legado.app.ui.book.changecover import android.content.Context import io.legado.app.R diff --git a/app/src/main/java/io/legado/app/ui/changesource/ChangeSourceAdapter.kt b/app/src/main/java/io/legado/app/ui/book/changesource/ChangeSourceAdapter.kt similarity index 97% rename from app/src/main/java/io/legado/app/ui/changesource/ChangeSourceAdapter.kt rename to app/src/main/java/io/legado/app/ui/book/changesource/ChangeSourceAdapter.kt index 1fa9284c1..132858e0f 100644 --- a/app/src/main/java/io/legado/app/ui/changesource/ChangeSourceAdapter.kt +++ b/app/src/main/java/io/legado/app/ui/book/changesource/ChangeSourceAdapter.kt @@ -1,4 +1,4 @@ -package io.legado.app.ui.changesource +package io.legado.app.ui.book.changesource import android.content.Context import android.os.Bundle diff --git a/app/src/main/java/io/legado/app/ui/changesource/ChangeSourceDialog.kt b/app/src/main/java/io/legado/app/ui/book/changesource/ChangeSourceDialog.kt similarity index 94% rename from app/src/main/java/io/legado/app/ui/changesource/ChangeSourceDialog.kt rename to app/src/main/java/io/legado/app/ui/book/changesource/ChangeSourceDialog.kt index d1ce27f2e..4ef9d56a3 100644 --- a/app/src/main/java/io/legado/app/ui/changesource/ChangeSourceDialog.kt +++ b/app/src/main/java/io/legado/app/ui/book/changesource/ChangeSourceDialog.kt @@ -1,4 +1,4 @@ -package io.legado.app.ui.changesource +package io.legado.app.ui.book.changesource import android.os.Bundle import android.util.DisplayMetrics @@ -16,9 +16,11 @@ import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import io.legado.app.R import io.legado.app.constant.PreferKey +import io.legado.app.constant.Theme import io.legado.app.data.entities.Book import io.legado.app.data.entities.SearchBook import io.legado.app.ui.widget.recycler.VerticalDivider +import io.legado.app.utils.applyTint import io.legado.app.utils.getPrefBoolean import io.legado.app.utils.getViewModel import io.legado.app.utils.putPrefBoolean @@ -69,10 +71,8 @@ class ChangeSourceDialog : DialogFragment(), super.onViewCreated(view, savedInstanceState) viewModel.initData(arguments) showTitle() - tool_bar.inflateMenu(R.menu.change_source) - tool_bar.setOnMenuItemClickListener(this) - initRecyclerView() initMenu() + initRecyclerView() initSearchView() initLiveData() viewModel.loadDbSearchBook() @@ -85,6 +85,9 @@ class ChangeSourceDialog : DialogFragment(), } private fun initMenu() { + tool_bar.inflateMenu(R.menu.change_source) + tool_bar.menu.applyTint(requireContext(), Theme.getTheme()) + tool_bar.setOnMenuItemClickListener(this) tool_bar.menu.findItem(R.id.menu_load_toc)?.isChecked = getPrefBoolean(PreferKey.changeSourceLoadToc) } @@ -110,16 +113,15 @@ class ChangeSourceDialog : DialogFragment(), } private fun initSearchView() { - val searchView = tool_bar.menu.findItem(R.id.menu_search).actionView as SearchView - searchView.setOnCloseListener { + search_view.setOnCloseListener { showTitle() false } - searchView.setOnSearchClickListener { + search_view.setOnSearchClickListener { tool_bar.title = "" tool_bar.subtitle = "" } - searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener { + search_view.setOnQueryTextListener(object : SearchView.OnQueryTextListener { override fun onQueryTextSubmit(query: String?): Boolean { return false } diff --git a/app/src/main/java/io/legado/app/ui/changesource/ChangeSourceViewModel.kt b/app/src/main/java/io/legado/app/ui/book/changesource/ChangeSourceViewModel.kt similarity index 99% rename from app/src/main/java/io/legado/app/ui/changesource/ChangeSourceViewModel.kt rename to app/src/main/java/io/legado/app/ui/book/changesource/ChangeSourceViewModel.kt index 1a683427f..548038e59 100644 --- a/app/src/main/java/io/legado/app/ui/changesource/ChangeSourceViewModel.kt +++ b/app/src/main/java/io/legado/app/ui/book/changesource/ChangeSourceViewModel.kt @@ -1,4 +1,4 @@ -package io.legado.app.ui.changesource +package io.legado.app.ui.book.changesource import android.app.Application import android.os.Bundle diff --git a/app/src/main/java/io/legado/app/ui/changesource/DiffCallBack.kt b/app/src/main/java/io/legado/app/ui/book/changesource/DiffCallBack.kt similarity index 97% rename from app/src/main/java/io/legado/app/ui/changesource/DiffCallBack.kt rename to app/src/main/java/io/legado/app/ui/book/changesource/DiffCallBack.kt index 91e640ac3..b0a8f19ef 100644 --- a/app/src/main/java/io/legado/app/ui/changesource/DiffCallBack.kt +++ b/app/src/main/java/io/legado/app/ui/book/changesource/DiffCallBack.kt @@ -1,4 +1,4 @@ -package io.legado.app.ui.changesource +package io.legado.app.ui.book.changesource import android.os.Bundle import androidx.recyclerview.widget.DiffUtil diff --git a/app/src/main/java/io/legado/app/ui/chapterlist/BookmarkAdapter.kt b/app/src/main/java/io/legado/app/ui/book/chapterlist/BookmarkAdapter.kt similarity index 98% rename from app/src/main/java/io/legado/app/ui/chapterlist/BookmarkAdapter.kt rename to app/src/main/java/io/legado/app/ui/book/chapterlist/BookmarkAdapter.kt index ea3f41801..05e3751c9 100644 --- a/app/src/main/java/io/legado/app/ui/chapterlist/BookmarkAdapter.kt +++ b/app/src/main/java/io/legado/app/ui/book/chapterlist/BookmarkAdapter.kt @@ -1,4 +1,4 @@ -package io.legado.app.ui.chapterlist +package io.legado.app.ui.book.chapterlist import android.os.AsyncTask.execute import android.view.LayoutInflater diff --git a/app/src/main/java/io/legado/app/ui/chapterlist/BookmarkFragment.kt b/app/src/main/java/io/legado/app/ui/book/chapterlist/BookmarkFragment.kt similarity index 98% rename from app/src/main/java/io/legado/app/ui/chapterlist/BookmarkFragment.kt rename to app/src/main/java/io/legado/app/ui/book/chapterlist/BookmarkFragment.kt index b2337d0d9..6aa36d62e 100644 --- a/app/src/main/java/io/legado/app/ui/chapterlist/BookmarkFragment.kt +++ b/app/src/main/java/io/legado/app/ui/book/chapterlist/BookmarkFragment.kt @@ -1,4 +1,4 @@ -package io.legado.app.ui.chapterlist +package io.legado.app.ui.book.chapterlist import android.app.Activity import android.content.Intent diff --git a/app/src/main/java/io/legado/app/ui/chapterlist/ChapterListActivity.kt b/app/src/main/java/io/legado/app/ui/book/chapterlist/ChapterListActivity.kt similarity index 98% rename from app/src/main/java/io/legado/app/ui/chapterlist/ChapterListActivity.kt rename to app/src/main/java/io/legado/app/ui/book/chapterlist/ChapterListActivity.kt index a0d401a5e..7420fc047 100644 --- a/app/src/main/java/io/legado/app/ui/chapterlist/ChapterListActivity.kt +++ b/app/src/main/java/io/legado/app/ui/book/chapterlist/ChapterListActivity.kt @@ -1,4 +1,4 @@ -package io.legado.app.ui.chapterlist +package io.legado.app.ui.book.chapterlist import android.os.Bundle import android.view.Menu diff --git a/app/src/main/java/io/legado/app/ui/chapterlist/ChapterListAdapter.kt b/app/src/main/java/io/legado/app/ui/book/chapterlist/ChapterListAdapter.kt similarity index 98% rename from app/src/main/java/io/legado/app/ui/chapterlist/ChapterListAdapter.kt rename to app/src/main/java/io/legado/app/ui/book/chapterlist/ChapterListAdapter.kt index 08b222970..b9d20920b 100644 --- a/app/src/main/java/io/legado/app/ui/chapterlist/ChapterListAdapter.kt +++ b/app/src/main/java/io/legado/app/ui/book/chapterlist/ChapterListAdapter.kt @@ -1,4 +1,4 @@ -package io.legado.app.ui.chapterlist +package io.legado.app.ui.book.chapterlist import android.content.Context import android.widget.TextView diff --git a/app/src/main/java/io/legado/app/ui/chapterlist/ChapterListFragment.kt b/app/src/main/java/io/legado/app/ui/book/chapterlist/ChapterListFragment.kt similarity index 99% rename from app/src/main/java/io/legado/app/ui/chapterlist/ChapterListFragment.kt rename to app/src/main/java/io/legado/app/ui/book/chapterlist/ChapterListFragment.kt index 0bfc9a765..9e9b5c813 100644 --- a/app/src/main/java/io/legado/app/ui/chapterlist/ChapterListFragment.kt +++ b/app/src/main/java/io/legado/app/ui/book/chapterlist/ChapterListFragment.kt @@ -1,4 +1,4 @@ -package io.legado.app.ui.chapterlist +package io.legado.app.ui.book.chapterlist import android.app.Activity.RESULT_OK import android.content.Intent diff --git a/app/src/main/java/io/legado/app/ui/chapterlist/ChapterListViewModel.kt b/app/src/main/java/io/legado/app/ui/book/chapterlist/ChapterListViewModel.kt similarity index 94% rename from app/src/main/java/io/legado/app/ui/chapterlist/ChapterListViewModel.kt rename to app/src/main/java/io/legado/app/ui/book/chapterlist/ChapterListViewModel.kt index 4156c4ac9..ad3361fd1 100644 --- a/app/src/main/java/io/legado/app/ui/chapterlist/ChapterListViewModel.kt +++ b/app/src/main/java/io/legado/app/ui/book/chapterlist/ChapterListViewModel.kt @@ -1,4 +1,4 @@ -package io.legado.app.ui.chapterlist +package io.legado.app.ui.book.chapterlist import android.app.Application diff --git a/app/src/main/java/io/legado/app/ui/download/DownloadActivity.kt b/app/src/main/java/io/legado/app/ui/book/download/DownloadActivity.kt similarity index 75% rename from app/src/main/java/io/legado/app/ui/download/DownloadActivity.kt rename to app/src/main/java/io/legado/app/ui/book/download/DownloadActivity.kt index ef1d87e68..f731806a7 100644 --- a/app/src/main/java/io/legado/app/ui/download/DownloadActivity.kt +++ b/app/src/main/java/io/legado/app/ui/book/download/DownloadActivity.kt @@ -1,4 +1,4 @@ -package io.legado.app.ui.download +package io.legado.app.ui.book.download import android.app.Activity import android.content.Intent @@ -16,17 +16,15 @@ import io.legado.app.constant.EventBus import io.legado.app.data.entities.Book import io.legado.app.data.entities.BookChapter import io.legado.app.help.BookHelp -import io.legado.app.help.permission.Permissions -import io.legado.app.help.permission.PermissionsCompat import io.legado.app.service.help.Download import io.legado.app.ui.filechooser.FileChooserDialog +import io.legado.app.ui.filechooser.FilePicker import io.legado.app.utils.* import kotlinx.android.synthetic.main.activity_download.* import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers.IO import kotlinx.coroutines.launch import kotlinx.coroutines.withContext -import org.jetbrains.anko.alert import org.jetbrains.anko.toast @@ -126,44 +124,14 @@ class DownloadActivity : VMBaseActivity(R.layout.activity_dow override fun export(position: Int) { exportPosition = position - alert { - titleResource = R.string.select_folder - items(resources.getStringArray(R.array.select_folder).toList()) { _, index -> - when (index) { - 0 -> { - val path = ACache.get(this@DownloadActivity).getAsString(exportBookPathKey) - if (path.isNullOrEmpty()) { - toast("没有默认路径") - } else { - startExport(path) - } - } - 1 -> { - try { - val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE) - intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) - startActivityForResult(intent, exportRequestCode) - } catch (e: java.lang.Exception) { - e.printStackTrace() - toast(e.localizedMessage ?: "ERROR") - } - } - 2 -> { - PermissionsCompat.Builder(this@DownloadActivity) - .addPermissions(*Permissions.Group.STORAGE) - .rationale(R.string.tip_perm_request_storage) - .onGranted { - FileChooserDialog.show( - supportFragmentManager, - exportRequestCode, - mode = FileChooserDialog.DIRECTORY - ) - } - .request() - } - } + FilePicker.selectFolder(this, exportRequestCode) { + val path = ACache.get(this@DownloadActivity).getAsString(exportBookPathKey) + if (path.isNullOrEmpty()) { + toast("没有默认路径") + } else { + startExport(path) } - }.show() + } } private fun startExport(path: String) { diff --git a/app/src/main/java/io/legado/app/ui/download/DownloadAdapter.kt b/app/src/main/java/io/legado/app/ui/book/download/DownloadAdapter.kt similarity index 98% rename from app/src/main/java/io/legado/app/ui/download/DownloadAdapter.kt rename to app/src/main/java/io/legado/app/ui/book/download/DownloadAdapter.kt index 57cafc78a..082e5aecc 100644 --- a/app/src/main/java/io/legado/app/ui/download/DownloadAdapter.kt +++ b/app/src/main/java/io/legado/app/ui/book/download/DownloadAdapter.kt @@ -1,4 +1,4 @@ -package io.legado.app.ui.download +package io.legado.app.ui.book.download import android.content.Context import android.widget.ImageView diff --git a/app/src/main/java/io/legado/app/ui/download/DownloadViewModel.kt b/app/src/main/java/io/legado/app/ui/book/download/DownloadViewModel.kt similarity index 98% rename from app/src/main/java/io/legado/app/ui/download/DownloadViewModel.kt rename to app/src/main/java/io/legado/app/ui/book/download/DownloadViewModel.kt index da12d3d53..e5886d100 100644 --- a/app/src/main/java/io/legado/app/ui/download/DownloadViewModel.kt +++ b/app/src/main/java/io/legado/app/ui/book/download/DownloadViewModel.kt @@ -1,4 +1,4 @@ -package io.legado.app.ui.download +package io.legado.app.ui.book.download import android.app.Application import android.net.Uri diff --git a/app/src/main/java/io/legado/app/ui/explore/ExploreShowActivity.kt b/app/src/main/java/io/legado/app/ui/book/explore/ExploreShowActivity.kt similarity index 98% rename from app/src/main/java/io/legado/app/ui/explore/ExploreShowActivity.kt rename to app/src/main/java/io/legado/app/ui/book/explore/ExploreShowActivity.kt index 08a96a340..04bf86188 100644 --- a/app/src/main/java/io/legado/app/ui/explore/ExploreShowActivity.kt +++ b/app/src/main/java/io/legado/app/ui/book/explore/ExploreShowActivity.kt @@ -1,4 +1,4 @@ -package io.legado.app.ui.explore +package io.legado.app.ui.book.explore import android.os.Bundle import androidx.lifecycle.Observer diff --git a/app/src/main/java/io/legado/app/ui/explore/ExploreShowAdapter.kt b/app/src/main/java/io/legado/app/ui/book/explore/ExploreShowAdapter.kt similarity index 97% rename from app/src/main/java/io/legado/app/ui/explore/ExploreShowAdapter.kt rename to app/src/main/java/io/legado/app/ui/book/explore/ExploreShowAdapter.kt index 2204dae03..322e4a7a3 100644 --- a/app/src/main/java/io/legado/app/ui/explore/ExploreShowAdapter.kt +++ b/app/src/main/java/io/legado/app/ui/book/explore/ExploreShowAdapter.kt @@ -1,4 +1,4 @@ -package io.legado.app.ui.explore +package io.legado.app.ui.book.explore import android.content.Context import io.legado.app.R diff --git a/app/src/main/java/io/legado/app/ui/explore/ExploreShowViewModel.kt b/app/src/main/java/io/legado/app/ui/book/explore/ExploreShowViewModel.kt similarity index 97% rename from app/src/main/java/io/legado/app/ui/explore/ExploreShowViewModel.kt rename to app/src/main/java/io/legado/app/ui/book/explore/ExploreShowViewModel.kt index 849b1ca87..d34e21916 100644 --- a/app/src/main/java/io/legado/app/ui/explore/ExploreShowViewModel.kt +++ b/app/src/main/java/io/legado/app/ui/book/explore/ExploreShowViewModel.kt @@ -1,4 +1,4 @@ -package io.legado.app.ui.explore +package io.legado.app.ui.book.explore import android.app.Application import android.content.Intent diff --git a/app/src/main/java/io/legado/app/ui/book/info/BookInfoActivity.kt b/app/src/main/java/io/legado/app/ui/book/info/BookInfoActivity.kt index efcddba7c..782b538ee 100644 --- a/app/src/main/java/io/legado/app/ui/book/info/BookInfoActivity.kt +++ b/app/src/main/java/io/legado/app/ui/book/info/BookInfoActivity.kt @@ -20,14 +20,17 @@ import io.legado.app.data.entities.BookChapter import io.legado.app.help.BlurTransformation import io.legado.app.help.ImageLoader import io.legado.app.help.IntentDataHelp +import io.legado.app.lib.dialogs.alert +import io.legado.app.lib.dialogs.noButton +import io.legado.app.lib.dialogs.okButton import io.legado.app.ui.audio.AudioPlayActivity +import io.legado.app.ui.book.changecover.ChangeCoverDialog +import io.legado.app.ui.book.changesource.ChangeSourceDialog +import io.legado.app.ui.book.chapterlist.ChapterListActivity import io.legado.app.ui.book.group.GroupSelectDialog import io.legado.app.ui.book.info.edit.BookInfoEditActivity import io.legado.app.ui.book.read.ReadBookActivity import io.legado.app.ui.book.source.edit.BookSourceEditActivity -import io.legado.app.ui.changecover.ChangeCoverDialog -import io.legado.app.ui.changesource.ChangeSourceDialog -import io.legado.app.ui.chapterlist.ChapterListActivity import io.legado.app.utils.getViewModel import io.legado.app.utils.gone import io.legado.app.utils.visible @@ -52,7 +55,8 @@ class BookInfoActivity : get() = getViewModel(BookInfoViewModel::class.java) override fun onActivityCreated(savedInstanceState: Bundle?) { - title_bar.background.alpha = 0 + setSupportActionBar(toolbar) + supportActionBar?.setDisplayHomeAsUpEnabled(true) tv_intro.movementMethod = ScrollingMovementMethod.getInstance() viewModel.bookData.observe(this, Observer { showBook(it) }) viewModel.chapterListData.observe(this, Observer { upLoading(false, it) }) @@ -186,9 +190,7 @@ class BookInfoActivity : } tv_shelf.onClick { if (viewModel.inBookshelf) { - viewModel.delBook { - upTvBookshelf() - } + deleteBook() } else { viewModel.addToBookshelf { upTvBookshelf() @@ -223,6 +225,32 @@ class BookInfoActivity : } } + private fun deleteBook() { + viewModel.bookData.value?.let { + if (it.isLocalBook()) { + alert( + titleResource = R.string.sure, + messageResource = R.string.sure_delete_book_file + ) { + okButton { + viewModel.delBook(true) { + finish() + } + } + noButton { + viewModel.delBook(false) { + finish() + } + } + } + } else { + viewModel.delBook { + upTvBookshelf() + } + } + } + } + private fun openChapterList() { if (viewModel.chapterListData.value.isNullOrEmpty()) { toast(R.string.chapter_list_empty) @@ -320,7 +348,7 @@ class BookInfoActivity : } } else { if (!viewModel.inBookshelf) { - viewModel.delBook(null) + viewModel.delBook() } } } diff --git a/app/src/main/java/io/legado/app/ui/book/info/BookInfoViewModel.kt b/app/src/main/java/io/legado/app/ui/book/info/BookInfoViewModel.kt index 1a675e21a..0364696a6 100644 --- a/app/src/main/java/io/legado/app/ui/book/info/BookInfoViewModel.kt +++ b/app/src/main/java/io/legado/app/ui/book/info/BookInfoViewModel.kt @@ -11,6 +11,7 @@ import io.legado.app.data.entities.BookChapter import io.legado.app.help.BookHelp import io.legado.app.model.WebBook import io.legado.app.model.localBook.AnalyzeTxtFile +import io.legado.app.model.localBook.LocalBook import kotlinx.coroutines.Dispatchers.IO class BookInfoViewModel(application: Application) : BaseViewModel(application) { @@ -198,9 +199,12 @@ class BookInfoViewModel(application: Application) : BaseViewModel(application) { } } - fun delBook(success: (() -> Unit)?) { + fun delBook(deleteOriginal: Boolean = false, success: (() -> Unit)? = null) { execute { bookData.value?.let { + if (it.isLocalBook()) { + LocalBook.deleteBook(it, deleteOriginal) + } App.db.bookDao().delete(it) } inBookshelf = false diff --git a/app/src/main/java/io/legado/app/ui/book/info/edit/BookInfoEditActivity.kt b/app/src/main/java/io/legado/app/ui/book/info/edit/BookInfoEditActivity.kt index 6bcae9257..969b1ee45 100644 --- a/app/src/main/java/io/legado/app/ui/book/info/edit/BookInfoEditActivity.kt +++ b/app/src/main/java/io/legado/app/ui/book/info/edit/BookInfoEditActivity.kt @@ -8,7 +8,7 @@ import androidx.lifecycle.Observer import io.legado.app.R import io.legado.app.base.VMBaseActivity import io.legado.app.data.entities.Book -import io.legado.app.ui.changecover.ChangeCoverDialog +import io.legado.app.ui.book.changecover.ChangeCoverDialog import io.legado.app.utils.getViewModel import kotlinx.android.synthetic.main.activity_book_info_edit.* import org.jetbrains.anko.sdk27.listeners.onClick diff --git a/app/src/main/java/io/legado/app/ui/importbook/ImportBookActivity.kt b/app/src/main/java/io/legado/app/ui/book/local/ImportBookActivity.kt similarity index 99% rename from app/src/main/java/io/legado/app/ui/importbook/ImportBookActivity.kt rename to app/src/main/java/io/legado/app/ui/book/local/ImportBookActivity.kt index 8afa2793b..ed6bd4c74 100644 --- a/app/src/main/java/io/legado/app/ui/importbook/ImportBookActivity.kt +++ b/app/src/main/java/io/legado/app/ui/book/local/ImportBookActivity.kt @@ -1,4 +1,4 @@ -package io.legado.app.ui.importbook +package io.legado.app.ui.book.local import android.annotation.SuppressLint import android.app.Activity diff --git a/app/src/main/java/io/legado/app/ui/importbook/ImportBookAdapter.kt b/app/src/main/java/io/legado/app/ui/book/local/ImportBookAdapter.kt similarity index 99% rename from app/src/main/java/io/legado/app/ui/importbook/ImportBookAdapter.kt rename to app/src/main/java/io/legado/app/ui/book/local/ImportBookAdapter.kt index 6cb49d10f..acc3b7265 100644 --- a/app/src/main/java/io/legado/app/ui/importbook/ImportBookAdapter.kt +++ b/app/src/main/java/io/legado/app/ui/book/local/ImportBookAdapter.kt @@ -1,4 +1,4 @@ -package io.legado.app.ui.importbook +package io.legado.app.ui.book.local import android.content.Context import androidx.documentfile.provider.DocumentFile diff --git a/app/src/main/java/io/legado/app/ui/importbook/ImportBookViewModel.kt b/app/src/main/java/io/legado/app/ui/book/local/ImportBookViewModel.kt similarity index 96% rename from app/src/main/java/io/legado/app/ui/importbook/ImportBookViewModel.kt rename to app/src/main/java/io/legado/app/ui/book/local/ImportBookViewModel.kt index 1250da682..847a692bd 100644 --- a/app/src/main/java/io/legado/app/ui/importbook/ImportBookViewModel.kt +++ b/app/src/main/java/io/legado/app/ui/book/local/ImportBookViewModel.kt @@ -1,4 +1,4 @@ -package io.legado.app.ui.importbook +package io.legado.app.ui.book.local import android.app.Application import android.net.Uri diff --git a/app/src/main/java/io/legado/app/ui/book/read/ReadBookActivity.kt b/app/src/main/java/io/legado/app/ui/book/read/ReadBookActivity.kt index 48bdd6cf6..447a030b8 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/ReadBookActivity.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/ReadBookActivity.kt @@ -20,7 +20,9 @@ import io.legado.app.constant.PreferKey import io.legado.app.constant.Status import io.legado.app.data.entities.Book import io.legado.app.data.entities.BookChapter +import io.legado.app.help.BookHelp import io.legado.app.help.ReadBookConfig +import io.legado.app.help.coroutine.Coroutine import io.legado.app.lib.dialogs.alert import io.legado.app.lib.dialogs.noButton import io.legado.app.lib.dialogs.okButton @@ -38,8 +40,9 @@ import io.legado.app.ui.book.read.page.PageView import io.legado.app.ui.book.read.page.TextPageFactory import io.legado.app.ui.book.read.page.delegate.PageDelegate import io.legado.app.ui.book.source.edit.BookSourceEditActivity -import io.legado.app.ui.changesource.ChangeSourceDialog -import io.legado.app.ui.chapterlist.ChapterListActivity +import io.legado.app.ui.book.changesource.ChangeSourceDialog +import io.legado.app.ui.book.chapterlist.ChapterListActivity +import io.legado.app.ui.login.SourceLogin import io.legado.app.ui.replacerule.ReplaceRuleActivity import io.legado.app.ui.replacerule.edit.ReplaceEditDialog import io.legado.app.ui.widget.dialog.TextDialog @@ -63,6 +66,7 @@ class ReadBookActivity : VMBaseActivity(R.layout.activity_boo ChangeSourceDialog.CallBack, ReadBook.CallBack, TocRegexDialog.CallBack, + ReplaceEditDialog.CallBack, ColorPickerDialogListener { private val requestCodeChapterList = 568 private val requestCodeEditSource = 111 @@ -211,6 +215,7 @@ class ReadBookActivity : VMBaseActivity(R.layout.activity_boo R.id.menu_enable_replace -> ReadBook.book?.let { it.useReplaceRule = !it.useReplaceRule menu?.findItem(R.id.menu_enable_replace)?.isChecked = it.useReplaceRule + onReplaceRuleSave() } R.id.menu_book_info -> ReadBook.book?.let { startActivity(Pair("bookUrl", it.bookUrl)) @@ -219,6 +224,12 @@ class ReadBookActivity : VMBaseActivity(R.layout.activity_boo supportFragmentManager, ReadBook.book?.tocUrl ) + R.id.menu_login -> ReadBook.webBook?.bookSource?.let { + startActivity( + Pair("sourceUrl", it.bookSourceUrl), + Pair("loginUrl", it.loginUrl) + ) + } } return super.onCompatOptionsItemSelected(item) } @@ -260,18 +271,18 @@ class ReadBookActivity : VMBaseActivity(R.layout.activity_boo } } KeyEvent.KEYCODE_SPACE -> { - page_view.moveToNextPage() + page_view.pageDelegate?.keyTurnPage(PageDelegate.Direction.NEXT) return true } getPrefInt(PreferKey.prevKey) -> { if (keyCode != KeyEvent.KEYCODE_UNKNOWN) { - page_view.moveToPrevPage() + page_view.pageDelegate?.keyTurnPage(PageDelegate.Direction.PREV) return true } } getPrefInt(PreferKey.nextKey) -> { if (keyCode != KeyEvent.KEYCODE_UNKNOWN) { - page_view.moveToNextPage() + page_view.pageDelegate?.keyTurnPage(PageDelegate.Direction.NEXT) return true } } @@ -429,11 +440,7 @@ class ReadBookActivity : VMBaseActivity(R.layout.activity_boo if (getPrefBoolean("volumeKeyPageOnPlay") || BaseReadAloudService.pause ) { - when (direction) { - PageDelegate.Direction.PREV -> page_view.moveToPrevPage() - PageDelegate.Direction.NEXT -> page_view.moveToNextPage() - else -> return true - } + page_view.pageDelegate?.keyTurnPage(direction) return true } } @@ -544,14 +551,33 @@ class ReadBookActivity : VMBaseActivity(R.layout.activity_boo } } + /** + * 替换规则变化 + */ + override fun onReplaceRuleSave() { + Coroutine.async { + BookHelp.upReplaceRules() + ReadBook.loadContent() + } + } + + /** + * 显示阅读样式配置 + */ override fun showReadStyle() { ReadStyleDialog().show(supportFragmentManager, "readStyle") } + /** + * 显示更多设置 + */ override fun showMoreSetting() { MoreConfigDialog().show(supportFragmentManager, "moreConfig") } + /** + * 更新状态栏,导航栏 + */ override fun upSystemUiVisibility() { Help.upSystemUiVisibility(this, !read_menu.isVisible) } @@ -610,7 +636,7 @@ class ReadBookActivity : VMBaseActivity(R.layout.activity_boo viewModel.openChapter(index) } } - requestCodeReplace -> ReadBook.loadContent() + requestCodeReplace -> onReplaceRuleSave() } } } diff --git a/app/src/main/java/io/legado/app/ui/book/read/ReadMenu.kt b/app/src/main/java/io/legado/app/ui/book/read/ReadMenu.kt index 6dec43d12..254d037ad 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/ReadMenu.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/ReadMenu.kt @@ -13,6 +13,7 @@ import io.legado.app.R import io.legado.app.help.AppConfig import io.legado.app.help.ReadBookConfig import io.legado.app.lib.theme.accentColor +import io.legado.app.lib.theme.bottomBackground import io.legado.app.lib.theme.buttonDisabledColor import io.legado.app.service.help.ReadBook import io.legado.app.utils.* @@ -48,6 +49,7 @@ class ReadMenu : FrameLayout { fabNightTheme.setImageResource(R.drawable.ic_brightness) } initAnimation() + ll_bottom_bg.setBackgroundColor(context.bottomBackground) vw_bg.onClick { } vwNavigationBar.onClick { } seek_brightness.progress = context.getPrefInt("brightness", 100) diff --git a/app/src/main/java/io/legado/app/ui/book/read/TextActionMenu.kt b/app/src/main/java/io/legado/app/ui/book/read/TextActionMenu.kt index 4dc9ba7da..7e90c7ad1 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/TextActionMenu.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/TextActionMenu.kt @@ -95,6 +95,7 @@ class TextActionMenu(private val context: Context, private val callBack: CallBac onMenuItemSelected(it) } } + callBack.onMenuActionFinally() } } } @@ -121,7 +122,6 @@ class TextActionMenu(private val context: Context, private val callBack: CallBac } } } - callBack.onMenuActionFinally() } @RequiresApi(Build.VERSION_CODES.M) diff --git a/app/src/main/java/io/legado/app/ui/book/read/config/MoreConfigDialog.kt b/app/src/main/java/io/legado/app/ui/book/read/config/MoreConfigDialog.kt index 9b975e7e8..6f472f270 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/config/MoreConfigDialog.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/config/MoreConfigDialog.kt @@ -16,6 +16,7 @@ import io.legado.app.constant.EventBus import io.legado.app.constant.PreferKey import io.legado.app.help.ReadBookConfig import io.legado.app.lib.theme.ATH +import io.legado.app.lib.theme.bottomBackground import io.legado.app.ui.book.read.Help import io.legado.app.utils.getPrefBoolean import io.legado.app.utils.postEvent @@ -32,7 +33,7 @@ class MoreConfigDialog : DialogFragment() { } dialog?.window?.let { it.setBackgroundDrawableResource(R.color.background) - it.decorView.setPadding(0, 5, 0, 0) + it.decorView.setPadding(0, 0, 0, 0) val attr = it.attributes attr.dimAmount = 0.0f attr.gravity = Gravity.BOTTOM @@ -47,7 +48,7 @@ class MoreConfigDialog : DialogFragment() { savedInstanceState: Bundle? ): View? { val view = LinearLayout(context) - view.setBackgroundResource(R.color.background) + view.setBackgroundColor(requireContext().bottomBackground) view.id = R.id.tag1 container?.addView(view) return view diff --git a/app/src/main/java/io/legado/app/ui/book/read/config/ReadAloudDialog.kt b/app/src/main/java/io/legado/app/ui/book/read/config/ReadAloudDialog.kt index 6040c0880..8cce7d4d2 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/config/ReadAloudDialog.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/config/ReadAloudDialog.kt @@ -11,6 +11,7 @@ import androidx.fragment.app.DialogFragment import io.legado.app.R import io.legado.app.constant.EventBus import io.legado.app.help.AppConfig +import io.legado.app.lib.theme.bottomBackground import io.legado.app.service.BaseReadAloudService import io.legado.app.service.help.ReadAloud import io.legado.app.service.help.ReadBook @@ -54,6 +55,7 @@ class ReadAloudDialog : DialogFragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) + ll_bottom_bg.setBackgroundColor(requireContext().bottomBackground) initData() initOnChange() initOnClick() diff --git a/app/src/main/java/io/legado/app/ui/book/read/config/ReadStyleDialog.kt b/app/src/main/java/io/legado/app/ui/book/read/config/ReadStyleDialog.kt index 40c3c9e94..b51d8c088 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/config/ReadStyleDialog.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/config/ReadStyleDialog.kt @@ -11,10 +11,10 @@ import androidx.fragment.app.DialogFragment import io.legado.app.R import io.legado.app.constant.EventBus import io.legado.app.constant.PreferKey -import io.legado.app.help.BookHelp import io.legado.app.help.ReadBookConfig import io.legado.app.lib.dialogs.selector import io.legado.app.lib.theme.accentColor +import io.legado.app.lib.theme.bottomBackground import io.legado.app.lib.theme.primaryColor import io.legado.app.ui.book.read.Help import io.legado.app.ui.book.read.ReadBookActivity @@ -42,7 +42,7 @@ class ReadStyleDialog : DialogFragment(), FontSelectDialog.CallBack { } dialog?.window?.let { it.setBackgroundDrawableResource(R.color.background) - it.decorView.setPadding(0, 5, 0, 0) + it.decorView.setPadding(0, 0, 0, 0) val attr = it.attributes attr.dimAmount = 0.0f attr.gravity = Gravity.BOTTOM @@ -72,6 +72,7 @@ class ReadStyleDialog : DialogFragment(), FontSelectDialog.CallBack { } private fun initView() { + root_view.setBackgroundColor(requireContext().bottomBackground) dsb_text_size.valueFormat = { (it + 5).toString() } @@ -118,7 +119,7 @@ class ReadStyleDialog : DialogFragment(), FontSelectDialog.CallBack { title = getString(R.string.text_indent), items = resources.getStringArray(R.array.indent).toList() ) { _, index -> - BookHelp.bodyIndentCount = index + ReadBookConfig.bodyIndentCount = index postEvent(EventBus.UP_CONFIG, true) } } diff --git a/app/src/main/java/io/legado/app/ui/book/read/page/ChapterProvider.kt b/app/src/main/java/io/legado/app/ui/book/read/page/ChapterProvider.kt index 2c3996fd8..430db0e53 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/page/ChapterProvider.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/page/ChapterProvider.kt @@ -9,7 +9,6 @@ import io.legado.app.App import io.legado.app.constant.PreferKey import io.legado.app.data.entities.BookChapter import io.legado.app.help.AppConfig -import io.legado.app.help.BookHelp import io.legado.app.help.ReadBookConfig import io.legado.app.ui.book.read.page.entities.TextChapter import io.legado.app.ui.book.read.page.entities.TextLine @@ -34,7 +33,6 @@ object ChapterProvider { var typeface: Typeface = Typeface.SANS_SERIF var titlePaint = TextPaint() var contentPaint = TextPaint() - private var bodyIndent = BookHelp.bodyIndent init { upStyle() @@ -122,10 +120,12 @@ object ChapterProvider { ) for (lineIndex in 0 until layout.lineCount) { textPages.last().height = durY - durY = durY + layout.getLineBottom(lineIndex) - layout.getLineTop(lineIndex) val textLine = TextLine(isTitle = true) - if (durY < visibleHeight) { + if (durY + layout.getLineBaseline(lineIndex) - layout.getLineTop(lineIndex) + + contentPaint.fontMetrics.descent < visibleHeight + ) { textPages.last().textLines.add(textLine) + durY = durY + layout.getLineBottom(lineIndex) - layout.getLineTop(lineIndex) } else { textPages.last().text = stringBuilder.toString() stringBuilder.clear() @@ -140,7 +140,7 @@ object ChapterProvider { (layout.getLineBottom(lineIndex) - layout.getLineTop(lineIndex))).toFloat() textLine.lineBase = (paddingTop + durY - (layout.getLineBottom(lineIndex) - layout.getLineBaseline(lineIndex))).toFloat() - textLine.lineBottom = textLine.lineBase + titlePaint.fontMetrics.descent + textLine.lineBottom = textLine.lineBase + contentPaint.fontMetrics.descent val words = title.substring(layout.getLineStart(lineIndex), layout.getLineEnd(lineIndex)) stringBuilder.append(words) @@ -178,10 +178,12 @@ object ChapterProvider { ) for (lineIndex in 0 until layout.lineCount) { textPages.last().height = durY - durY = durY + layout.getLineBottom(lineIndex) - layout.getLineTop(lineIndex) val textLine = TextLine() - if (durY < visibleHeight) { + if (durY + layout.getLineBaseline(lineIndex) - layout.getLineTop(lineIndex) + + contentPaint.fontMetrics.descent < visibleHeight + ) { textPages.last().textLines.add(textLine) + durY = durY + layout.getLineBottom(lineIndex) - layout.getLineTop(lineIndex) } else { textPages.last().text = stringBuilder.toString() stringBuilder.clear() @@ -227,6 +229,7 @@ object ChapterProvider { desiredWidth: Float ) { var x = 0f + val bodyIndent = ReadBookConfig.bodyIndent val icw = StaticLayout.getDesiredWidth(bodyIndent, textPaint) / bodyIndent.length for (i in 0..bodyIndent.lastIndex) { val x1 = x + icw @@ -348,8 +351,6 @@ object ChapterProvider { titlePaint.textSize = (ReadBookConfig.textSize + 2).dp.toFloat() contentPaint.textSize = ReadBookConfig.textSize.dp.toFloat() - bodyIndent = BookHelp.bodyIndent - upSize() } diff --git a/app/src/main/java/io/legado/app/ui/book/read/page/ContentTextView.kt b/app/src/main/java/io/legado/app/ui/book/read/page/ContentTextView.kt index d17e55a67..e8beebf6a 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/page/ContentTextView.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/page/ContentTextView.kt @@ -92,6 +92,7 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at } if (!ReadBookConfig.isScroll) return //滚动翻页 + if (!pageFactory.hasNext()) return val nextPage = relativePage(1) relativeOffset = relativeOffset(1) nextPage.textLines.forEach { textLine -> @@ -108,6 +109,7 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at textLine.isReadAloud ) } + if (!pageFactory.hasNextPlus()) return relativeOffset = relativeOffset(2) if (relativeOffset < ChapterProvider.visibleHeight) { relativePage(2).textLines.forEach { textLine -> @@ -163,24 +165,20 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at } pageOffset += offset - if (pageOffset > 0) { - if (!pageFactory.hasPrev()) { - pageOffset = 0f - } else { - pageFactory.moveToPrev() - textPage = pageFactory.currentPage - pageOffset -= textPage.height - upView?.invoke(textPage) - } + if (!pageFactory.hasPrev() && pageOffset > 0) { + pageOffset = 0f + } else if (!pageFactory.hasNext() && pageOffset < 0) { + pageOffset = 0f + } else if (pageOffset > 0) { + pageFactory.moveToPrev() + textPage = pageFactory.currentPage + pageOffset -= textPage.height + upView?.invoke(textPage) } else if (pageOffset < -textPage.height) { - if (!pageFactory.hasNext()) { - pageOffset = -textPage.height.toFloat() - } else { - pageOffset += textPage.height - pageFactory.moveToNext() - textPage = pageFactory.currentPage - upView?.invoke(textPage) - } + pageOffset += textPage.height + pageFactory.moveToNext() + textPage = pageFactory.currentPage + upView?.invoke(textPage) } invalidate() } diff --git a/app/src/main/java/io/legado/app/ui/book/read/page/PageFactory.kt b/app/src/main/java/io/legado/app/ui/book/read/page/PageFactory.kt index 85cdecbd5..7a04ae5ec 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/page/PageFactory.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/page/PageFactory.kt @@ -22,4 +22,5 @@ abstract class PageFactory(protected val dataSource: DataSource) { abstract fun hasPrev(): Boolean + abstract fun hasNextPlus(): Boolean } \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/ui/book/read/page/PageView.kt b/app/src/main/java/io/legado/app/ui/book/read/page/PageView.kt index c6839b214..7f89a98a7 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/page/PageView.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/page/PageView.kt @@ -26,12 +26,12 @@ class PageView(context: Context, attrs: AttributeSet) : init { callBack = activity as CallBack - prevPage = ContentView(context) - addView(prevPage) nextPage = ContentView(context) addView(nextPage) curPage = ContentView(context) addView(curPage) + prevPage = ContentView(context) + addView(prevPage) upBg() setWillNotDraw(false) pageFactory = TextPageFactory(this) @@ -40,6 +40,7 @@ class PageView(context: Context, attrs: AttributeSet) : override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) { super.onSizeChanged(w, h, oldw, oldh) + prevPage.x = -w.toFloat() pageDelegate?.setViewSize(w, h) if (oldw != 0 && oldh != 0) { ReadBook.loadContent() @@ -116,22 +117,6 @@ class PageView(context: Context, attrs: AttributeSet) : callBack.screenOffTimerStart() } - fun moveToPrevPage(noAnim: Boolean = true) { - if (noAnim) { - fillPage(PageDelegate.Direction.PREV) - } else { - pageDelegate?.prevPageByAnim() - } - } - - fun moveToNextPage(noAnim: Boolean = true) { - if (noAnim) { - fillPage(PageDelegate.Direction.NEXT) - } else { - pageDelegate?.nextPageByAnim() - } - } - fun upStyle() { ChapterProvider.upStyle() curPage.upStyle() diff --git a/app/src/main/java/io/legado/app/ui/book/read/page/TextPageFactory.kt b/app/src/main/java/io/legado/app/ui/book/read/page/TextPageFactory.kt index fe18f1b26..571f0d68f 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/page/TextPageFactory.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/page/TextPageFactory.kt @@ -13,6 +13,10 @@ class TextPageFactory(dataSource: DataSource) : PageFactory(dataSource return hasNextChapter() || currentChapter?.isLastIndex(pageIndex) != true } + override fun hasNextPlus(): Boolean = with(dataSource) { + return hasNextChapter() || pageIndex < (currentChapter?.pageSize() ?: 1) - 2 + } + override fun moveToFirst() { ReadBook.setPageIndex(0) } @@ -68,6 +72,9 @@ class TextPageFactory(dataSource: DataSource) : PageFactory(dataSource ?: TextPage(title = it.title).format() } } + if (!hasNextChapter()) { + return@with TextPage(text = "") + } nextChapter?.let { return@with it.page(0)?.removePageAloudSpan() ?: TextPage(title = it.title).format() diff --git a/app/src/main/java/io/legado/app/ui/book/read/page/delegate/CoverPageDelegate.kt b/app/src/main/java/io/legado/app/ui/book/read/page/delegate/CoverPageDelegate.kt index 21ffcee77..2f4b2c404 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/page/delegate/CoverPageDelegate.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/page/delegate/CoverPageDelegate.kt @@ -1,14 +1,12 @@ package io.legado.app.ui.book.read.page.delegate import android.graphics.Canvas -import android.graphics.Matrix import android.graphics.drawable.GradientDrawable import io.legado.app.ui.book.read.page.PageView class CoverPageDelegate(pageView: PageView) : HorizontalPageDelegate(pageView) { private val shadowDrawableR: GradientDrawable - private val bitmapMatrix = Matrix() init { val shadowColors = intArrayOf(0x66111111, 0x00000000) @@ -18,35 +16,11 @@ class CoverPageDelegate(pageView: PageView) : HorizontalPageDelegate(pageView) { shadowDrawableR.gradientType = GradientDrawable.LINEAR_GRADIENT } - override fun onAnimStart() { - val distanceX: Float - when (mDirection) { - Direction.NEXT -> distanceX = - if (isCancel) { - var dis = viewWidth - startX + touchX - if (dis > viewWidth) { - dis = viewWidth.toFloat() - } - viewWidth - dis - } else { - -(touchX + (viewWidth - startX)) - } - else -> distanceX = - if (isCancel) { - -(touchX - startX) - } else { - viewWidth - (touchX - startX) - } - } - - startScroll(touchX.toInt(), 0, distanceX.toInt(), 0) - } - - override fun onAnimStop() { + override fun setStartPoint(x: Float, y: Float, invalidate: Boolean) { curPage.x = 0.toFloat() - if (!isCancel) { - pageView.fillPage(mDirection) - } + prevPage.x = -viewWidth.toFloat() + nextPage.x = 0.toFloat() + super.setStartPoint(x, y, invalidate) } override fun onDraw(canvas: Canvas) { @@ -57,13 +31,12 @@ class CoverPageDelegate(pageView: PageView) : HorizontalPageDelegate(pageView) { ) return val distanceX = if (offsetX > 0) offsetX - viewWidth else offsetX + viewWidth - bitmap?.let { - if (mDirection == Direction.PREV) { - bitmapMatrix.setTranslate(distanceX, 0.toFloat()) - canvas.drawBitmap(it, bitmapMatrix, null) - } else if (mDirection == Direction.NEXT) { - curPage.translationX = offsetX - } + if (!isRunning) return + if (mDirection == Direction.PREV) { + prevPage.translationX = offsetX - viewWidth + addShadow(distanceX.toInt(), canvas) + } else if (mDirection == Direction.NEXT) { + curPage.translationX = offsetX addShadow(distanceX.toInt(), canvas) } } @@ -77,4 +50,36 @@ class CoverPageDelegate(pageView: PageView) : HorizontalPageDelegate(pageView) { shadowDrawableR.draw(canvas) } } + + override fun onAnimStop() { + curPage.x = 0.toFloat() + prevPage.x = -viewWidth.toFloat() + if (!isCancel) { + pageView.fillPage(mDirection) + } + } + + override fun onAnimStart() { + val distanceX: Float + when (mDirection) { + Direction.NEXT -> distanceX = + if (isCancel) { + var dis = viewWidth - startX + touchX + if (dis > viewWidth) { + dis = viewWidth.toFloat() + } + viewWidth - dis + } else { + -(touchX + (viewWidth - startX)) + } + else -> distanceX = + if (isCancel) { + -(touchX - startX) + } else { + viewWidth - (touchX - startX) + } + } + startScroll(touchX.toInt(), 0, distanceX.toInt(), 0) + } + } diff --git a/app/src/main/java/io/legado/app/ui/book/read/page/delegate/HorizontalPageDelegate.kt b/app/src/main/java/io/legado/app/ui/book/read/page/delegate/HorizontalPageDelegate.kt index 700728b14..a95d01e2a 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/page/delegate/HorizontalPageDelegate.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/page/delegate/HorizontalPageDelegate.kt @@ -8,6 +8,9 @@ abstract class HorizontalPageDelegate(pageView: PageView) : PageDelegate(pageVie override fun onTouch(event: MotionEvent) { when (event.action) { + MotionEvent.ACTION_DOWN -> { + abort() + } MotionEvent.ACTION_MOVE -> { if (isTextSelected) { selectText(event) @@ -32,7 +35,6 @@ abstract class HorizontalPageDelegate(pageView: PageView) : PageDelegate(pageVie return } setDirection(Direction.PREV) - setBitmap() } else { //如果不存在表示没有下一页了 if (!hasNext()) { @@ -40,7 +42,6 @@ abstract class HorizontalPageDelegate(pageView: PageView) : PageDelegate(pageVie return } setDirection(Direction.NEXT) - setBitmap() } } } @@ -53,18 +54,19 @@ abstract class HorizontalPageDelegate(pageView: PageView) : PageDelegate(pageVie } override fun nextPageByAnim() { + super.nextPageByAnim() if (!hasNext()) return setDirection(Direction.NEXT) - setBitmap() setTouchPoint(viewWidth.toFloat(), 0f) onAnimStart() } override fun prevPageByAnim() { + super.prevPageByAnim() if (!hasPrev()) return setDirection(Direction.PREV) - setBitmap() setTouchPoint(0f, 0f) onAnimStart() } + } \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/ui/book/read/page/delegate/PageDelegate.kt b/app/src/main/java/io/legado/app/ui/book/read/page/delegate/PageDelegate.kt index 76b40fb78..fef7e5b0e 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/page/delegate/PageDelegate.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/page/delegate/PageDelegate.kt @@ -1,7 +1,6 @@ package io.legado.app.ui.book.read.page.delegate import android.content.Context -import android.graphics.Bitmap import android.graphics.Canvas import android.graphics.RectF import android.view.GestureDetector @@ -15,7 +14,6 @@ import io.legado.app.help.AppConfig import io.legado.app.help.ReadBookConfig import io.legado.app.ui.book.read.page.ContentView import io.legado.app.ui.book.read.page.PageView -import io.legado.app.utils.screenshot import kotlin.math.abs abstract class PageDelegate(protected val pageView: PageView) : @@ -40,8 +38,6 @@ abstract class PageDelegate(protected val pageView: PageView) : protected val curPage: ContentView get() = pageView.curPage protected val prevPage: ContentView get() = pageView.prevPage - protected var bitmap: Bitmap? = null - protected var viewWidth: Int = pageView.width protected var viewHeight: Int = pageView.height @@ -126,11 +122,10 @@ abstract class PageDelegate(protected val pageView: PageView) : } private fun stopScroll() { + isMoved = false isRunning = false isStarted = false pageView.invalidate() - bitmap?.recycle() - bitmap = null } open fun setViewSize(width: Int, height: Int) { @@ -166,23 +161,28 @@ abstract class PageDelegate(protected val pageView: PageView) : open fun onScroll() {}//移动contentView, slidePage - abstract fun nextPageByAnim() + open fun nextPageByAnim() { + abort() + } - abstract fun prevPageByAnim() + open fun prevPageByAnim() { + abort() + } + + open fun keyTurnPage(direction: Direction) { + if (isRunning) return + when (direction) { + Direction.NEXT -> nextPageByAnim() + Direction.PREV -> prevPageByAnim() + else -> return + } + } @CallSuper open fun setDirection(direction: Direction) { mDirection = direction } - open fun setBitmap() { - bitmap = when (mDirection) { - Direction.NEXT -> nextPage.screenshot() - Direction.PREV -> prevPage.screenshot() - else -> null - } - } - /** * 触摸事件处理 */ @@ -321,7 +321,7 @@ abstract class PageDelegate(protected val pageView: PageView) : } open fun onDestroy() { - bitmap?.recycle() + } enum class Direction { diff --git a/app/src/main/java/io/legado/app/ui/book/read/page/delegate/ScrollPageDelegate.kt b/app/src/main/java/io/legado/app/ui/book/read/page/delegate/ScrollPageDelegate.kt index 368a9ad8a..0e5fb7dff 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/page/delegate/ScrollPageDelegate.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/page/delegate/ScrollPageDelegate.kt @@ -2,6 +2,7 @@ package io.legado.app.ui.book.read.page.delegate import android.view.MotionEvent import android.view.VelocityTracker +import io.legado.app.ui.book.read.page.ChapterProvider import io.legado.app.ui.book.read.page.PageView import kotlin.math.abs @@ -60,10 +61,12 @@ class ScrollPageDelegate(pageView: PageView) : PageDelegate(pageView) { } override fun nextPageByAnim() { - + super.nextPageByAnim() + startScroll(0, 0, 0, -ChapterProvider.visibleHeight) } override fun prevPageByAnim() { - + super.prevPageByAnim() + startScroll(0, 0, 0, ChapterProvider.visibleHeight) } } \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/ui/book/read/page/delegate/SimulationPageDelegate.kt b/app/src/main/java/io/legado/app/ui/book/read/page/delegate/SimulationPageDelegate.kt index 6d7dd7bf8..b337a17a1 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/page/delegate/SimulationPageDelegate.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/page/delegate/SimulationPageDelegate.kt @@ -118,6 +118,16 @@ class SimulationPageDelegate(pageView: PageView) : HorizontalPageDelegate(pageVi mMaxLength = hypot(viewWidth.toDouble(), viewHeight.toDouble()).toFloat() } + override fun onDestroy() { + super.onDestroy() + prevBitmap?.recycle() + prevBitmap = null + curBitmap?.recycle() + curBitmap = null + nextBitmap?.recycle() + nextBitmap = null + } + override fun setStartPoint(x: Float, y: Float, invalidate: Boolean) { super.setStartPoint(x, y, invalidate) calcCornerXY(x, y) @@ -126,13 +136,13 @@ class SimulationPageDelegate(pageView: PageView) : HorizontalPageDelegate(pageVi override fun setTouchPoint(x: Float, y: Float, invalidate: Boolean) { super.setTouchPoint(x, y, false) //触摸y中间位置吧y变成屏幕高度 - if ((startY > viewHeight * 0.33 && startY < viewHeight * 0.66) + if ((startY > viewHeight / 3.0 && startY < viewHeight * 2 / 3.0) || mDirection == Direction.PREV ) { touchY = viewHeight.toFloat() } - if (startY > viewHeight * 0.33 && startY < viewHeight / 2.0 + if (startY > viewHeight / 3.0 && startY < viewHeight / 2.0 && mDirection == Direction.NEXT ) { touchY = 1f @@ -142,6 +152,7 @@ class SimulationPageDelegate(pageView: PageView) : HorizontalPageDelegate(pageVi override fun setDirection(direction: Direction) { super.setDirection(direction) + setBitmap() when (direction) { Direction.PREV -> //上一页滑动不出现对角 @@ -158,7 +169,7 @@ class SimulationPageDelegate(pageView: PageView) : HorizontalPageDelegate(pageVi } } - override fun setBitmap() { + private fun setBitmap() { when (mDirection) { Direction.PREV -> { prevBitmap = prevPage.screenshot() @@ -209,12 +220,6 @@ class SimulationPageDelegate(pageView: PageView) : HorizontalPageDelegate(pageVi if (!isCancel) { pageView.fillPage(mDirection) } - prevBitmap?.recycle() - prevBitmap = null - nextBitmap?.recycle() - nextBitmap = null - curBitmap?.recycle() - curBitmap = null } override fun onDraw(canvas: Canvas) { diff --git a/app/src/main/java/io/legado/app/ui/book/read/page/delegate/SlidePageDelegate.kt b/app/src/main/java/io/legado/app/ui/book/read/page/delegate/SlidePageDelegate.kt index 0308d965e..850c6b4ea 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/page/delegate/SlidePageDelegate.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/page/delegate/SlidePageDelegate.kt @@ -1,12 +1,16 @@ package io.legado.app.ui.book.read.page.delegate import android.graphics.Canvas -import android.graphics.Matrix import io.legado.app.ui.book.read.page.PageView class SlidePageDelegate(pageView: PageView) : HorizontalPageDelegate(pageView) { - private val bitmapMatrix = Matrix() + override fun setStartPoint(x: Float, y: Float, invalidate: Boolean) { + curPage.x = 0f + prevPage.x = -viewWidth.toFloat() + nextPage.x = viewWidth.toFloat() + super.setStartPoint(x, y, invalidate) + } override fun onAnimStart() { val distanceX: Float @@ -28,7 +32,6 @@ class SlidePageDelegate(pageView: PageView) : HorizontalPageDelegate(pageView) { viewWidth - (touchX - startX) } } - startScroll(touchX.toInt(), 0, distanceX.toInt(), 0) } @@ -39,26 +42,20 @@ class SlidePageDelegate(pageView: PageView) : HorizontalPageDelegate(pageView) { || (mDirection == Direction.PREV && offsetX < 0) ) return - val distanceX = if (offsetX > 0) offsetX - viewWidth else offsetX + viewWidth - bitmap?.let { - bitmapMatrix.setTranslate(distanceX, 0.toFloat()) - canvas.drawBitmap(it, bitmapMatrix, null) + if (!isRunning) return + if (mDirection == Direction.PREV) { + curPage.translationX = offsetX + prevPage.translationX = offsetX - viewWidth + } else if (mDirection == Direction.NEXT) { + curPage.translationX = offsetX + nextPage.translationX = offsetX + viewWidth } } - override fun onScroll() { - val offsetX = touchX - startX - - if ((mDirection == Direction.NEXT && offsetX > 0) - || (mDirection == Direction.PREV && offsetX < 0) - ) return - - curPage.translationX = offsetX - } - override fun onAnimStop() { - curPage.x = 0.toFloat() - + curPage.x = 0f + prevPage.x = -viewWidth.toFloat() + nextPage.x = viewWidth.toFloat() if (!isCancel) { pageView.fillPage(mDirection) } 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 1227f4232..a41fcd656 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 @@ -280,12 +280,16 @@ class SearchActivity : VMBaseActivity(R.layout.activity_book_se */ @Synchronized private fun upSearchItems(items: List, isMandatoryUpdate: Boolean) { - if (!isMandatoryUpdate && System.currentTimeMillis() - refreshTime < 1000) { + if (!isMandatoryUpdate && System.currentTimeMillis() - refreshTime < 500) { return } refreshTime = System.currentTimeMillis() - val diffResult = DiffUtil.calculateDiff(DiffCallBack(ArrayList(adapter.getItems()), items)) - adapter.setItems(items, diffResult) + try { + val diffResult = DiffUtil.calculateDiff(DiffCallBack(adapter.getItems(), items)) + adapter.setItems(items, diffResult) + } catch (e: Exception) { + e.printStackTrace() + } } /** diff --git a/app/src/main/java/io/legado/app/ui/book/source/debug/BookSourceDebugActivity.kt b/app/src/main/java/io/legado/app/ui/book/source/debug/BookSourceDebugActivity.kt index dafeaa218..75ac065e0 100644 --- a/app/src/main/java/io/legado/app/ui/book/source/debug/BookSourceDebugActivity.kt +++ b/app/src/main/java/io/legado/app/ui/book/source/debug/BookSourceDebugActivity.kt @@ -83,7 +83,7 @@ class BookSourceDebugActivity : override fun onCompatOptionsItemSelected(item: MenuItem): Boolean { when (item.itemId) { - R.id.action_scan -> { + R.id.menu_scan -> { startActivityForResult(qrRequestCode) } } diff --git a/app/src/main/java/io/legado/app/ui/book/source/edit/BookSourceEditActivity.kt b/app/src/main/java/io/legado/app/ui/book/source/edit/BookSourceEditActivity.kt index c0eab1b32..46f3eb49f 100644 --- a/app/src/main/java/io/legado/app/ui/book/source/edit/BookSourceEditActivity.kt +++ b/app/src/main/java/io/legado/app/ui/book/source/edit/BookSourceEditActivity.kt @@ -24,6 +24,7 @@ import io.legado.app.data.entities.rule.* import io.legado.app.lib.dialogs.alert import io.legado.app.lib.theme.ATH import io.legado.app.ui.book.source.debug.BookSourceDebugActivity +import io.legado.app.ui.login.SourceLogin import io.legado.app.ui.widget.KeyboardToolPop import io.legado.app.utils.GSON import io.legado.app.utils.applyTint @@ -67,22 +68,20 @@ class BookSourceEditActivity : override fun onCompatOptionsItemSelected(item: MenuItem): Boolean { when (item.itemId) { - R.id.menu_save -> { - val source = getSource() + R.id.menu_save -> getSource().let { source -> if (checkSource(source)) { viewModel.save(source) { setResult(Activity.RESULT_OK); finish() } } } - R.id.menu_debug_source -> { - val source = getSource() + R.id.menu_debug_source -> getSource().let { source -> if (checkSource(source)) { viewModel.save(source) { startActivity(Pair("key", source.bookSourceUrl)) } } } - R.id.menu_copy_source -> { - GSON.toJson(getSource())?.let { sourceStr -> + R.id.menu_copy_source -> getSource().let { source -> + GSON.toJson(source)?.let { sourceStr -> val clipboard = getSystemService(Context.CLIPBOARD_SERVICE) as? ClipboardManager clipboard?.setPrimaryClip(ClipData.newPlainText(null, sourceStr)) } @@ -101,6 +100,18 @@ class BookSourceEditActivity : toast(R.string.can_not_open) } } + R.id.menu_login -> getSource().let { + if (checkSource(it)) { + if (it.loginUrl.isNullOrEmpty()) { + toast(R.string.source_no_login) + } else { + startActivity( + Pair("sourceUrl", it.bookSourceUrl), + Pair("loginUrl", it.loginUrl) + ) + } + } + } } return super.onCompatOptionsItemSelected(item) } diff --git a/app/src/main/java/io/legado/app/ui/book/source/manage/BookSourceActivity.kt b/app/src/main/java/io/legado/app/ui/book/source/manage/BookSourceActivity.kt index c94b59d7c..c05294965 100644 --- a/app/src/main/java/io/legado/app/ui/book/source/manage/BookSourceActivity.kt +++ b/app/src/main/java/io/legado/app/ui/book/source/manage/BookSourceActivity.kt @@ -9,6 +9,7 @@ import android.view.MenuItem import android.view.SubMenu import androidx.appcompat.widget.PopupMenu import androidx.appcompat.widget.SearchView +import androidx.documentfile.provider.DocumentFile import androidx.lifecycle.LiveData import androidx.lifecycle.Observer import androidx.recyclerview.widget.DiffUtil @@ -20,14 +21,13 @@ import io.legado.app.R import io.legado.app.base.VMBaseActivity import io.legado.app.data.entities.BookSource import io.legado.app.help.ItemTouchCallback -import io.legado.app.help.permission.Permissions -import io.legado.app.help.permission.PermissionsCompat import io.legado.app.lib.dialogs.* import io.legado.app.lib.theme.ATH import io.legado.app.lib.theme.primaryTextColor import io.legado.app.service.help.CheckSource import io.legado.app.ui.book.source.edit.BookSourceEditActivity import io.legado.app.ui.filechooser.FileChooserDialog +import io.legado.app.ui.filechooser.FilePicker import io.legado.app.ui.qrcode.QrCodeActivity import io.legado.app.ui.widget.SelectActionBar import io.legado.app.ui.widget.recycler.VerticalDivider @@ -39,7 +39,7 @@ import kotlinx.android.synthetic.main.view_search.* import org.jetbrains.anko.startActivity import org.jetbrains.anko.startActivityForResult import org.jetbrains.anko.toast -import java.io.FileNotFoundException +import java.io.File class BookSourceActivity : VMBaseActivity(R.layout.activity_book_source), PopupMenu.OnMenuItemClickListener, @@ -50,7 +50,8 @@ class BookSourceActivity : VMBaseActivity(R.layout.activity get() = getViewModel(BookSourceViewModel::class.java) private val importRecordKey = "bookSourceRecordKey" private val qrRequestCode = 101 - private val importSource = 132 + private val importRequestCode = 132 + private val exportRequestCode = 65 private lateinit var adapter: BookSourceAdapter private var bookSourceLiveDate: LiveData>? = null private var groups = linkedSetOf() @@ -82,7 +83,8 @@ class BookSourceActivity : VMBaseActivity(R.layout.activity R.id.menu_import_source_qr -> startActivityForResult(qrRequestCode) R.id.menu_group_manage -> GroupManageDialog().show(supportFragmentManager, "groupManage") - R.id.menu_import_source_local -> selectFileSys() + R.id.menu_import_source_local -> FilePicker + .selectFile(this, importRequestCode, "text/*", arrayOf("txt", "json")) R.id.menu_import_source_onLine -> showImportDialog() } if (item.groupId == R.id.source_group) { @@ -187,7 +189,7 @@ class BookSourceActivity : VMBaseActivity(R.layout.activity R.id.menu_disable_selection -> viewModel.disableSelection(adapter.getSelection()) R.id.menu_enable_explore -> viewModel.enableSelectExplore(adapter.getSelection()) R.id.menu_disable_explore -> viewModel.disableSelectExplore(adapter.getSelection()) - R.id.menu_export_selection -> viewModel.exportSelection(adapter.getSelection()) + R.id.menu_export_selection -> FilePicker.selectFolder(this, exportRequestCode) R.id.menu_check_source -> CheckSource.start(this, adapter.getSelection()) } return true @@ -235,47 +237,10 @@ class BookSourceActivity : VMBaseActivity(R.layout.activity }.show().applyTint() } - private fun selectFileSys() { - try { - val intent = Intent(Intent.ACTION_GET_CONTENT) - intent.addCategory(Intent.CATEGORY_OPENABLE) - intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) - intent.type = "text/*"//设置类型 - startActivityForResult(intent, importSource) - } catch (e: Exception) { - PermissionsCompat.Builder(this) - .addPermissions( - Permissions.READ_EXTERNAL_STORAGE, - Permissions.WRITE_EXTERNAL_STORAGE - ) - .rationale(R.string.bg_image_per) - .onGranted { - selectFile() - } - .request() - } - } - - private fun selectFile() { - FileChooserDialog.show( - supportFragmentManager, importSource, - allowExtensions = arrayOf("txt", "json") - ) - } - override fun upCountView() { select_action_bar.upCountView(adapter.getSelection().size, adapter.getActualItemCount()) } - override fun onFilePicked(requestCode: Int, currentPath: String) { - if (requestCode == importSource) { - Snackbar.make(title_bar, R.string.importing, Snackbar.LENGTH_INDEFINITE).show() - viewModel.importSourceFromFilePath(currentPath) { msg -> - title_bar.snackbar(msg) - } - } - } - override fun onQueryTextChange(newText: String?): Boolean { newText?.let { initLiveDataBookSource(it) @@ -307,6 +272,21 @@ class BookSourceActivity : VMBaseActivity(R.layout.activity viewModel.topSource(bookSource) } + override fun onFilePicked(requestCode: Int, currentPath: String) { + when (requestCode) { + exportRequestCode -> viewModel.exportSelection( + adapter.getSelection(), + File(currentPath) + ) + importRequestCode -> { + Snackbar.make(title_bar, R.string.importing, Snackbar.LENGTH_INDEFINITE).show() + viewModel.importSourceFromFilePath(currentPath) { msg -> + title_bar.snackbar(msg) + } + } + } + } + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) when (requestCode) { @@ -318,7 +298,7 @@ class BookSourceActivity : VMBaseActivity(R.layout.activity } } } - importSource -> if (resultCode == Activity.RESULT_OK) { + importRequestCode -> if (resultCode == Activity.RESULT_OK) { data?.data?.let { uri -> try { uri.readText(this)?.let { @@ -328,22 +308,24 @@ class BookSourceActivity : VMBaseActivity(R.layout.activity title_bar.snackbar(msg) } } - } catch (e: FileNotFoundException) { - PermissionsCompat.Builder(this) - .addPermissions( - Permissions.READ_EXTERNAL_STORAGE, - Permissions.WRITE_EXTERNAL_STORAGE - ) - .rationale(R.string.bg_image_per) - .onGranted { - selectFileSys() - } - .request() } catch (e: Exception) { toast(e.localizedMessage ?: "ERROR") } } } + exportRequestCode -> { + data?.data?.let { uri -> + if (uri.toString().isContentPath()) { + DocumentFile.fromTreeUri(this, uri)?.let { + viewModel.exportSelection(adapter.getSelection(), it) + } + } else { + uri.path?.let { + viewModel.exportSelection(adapter.getSelection(), File(it)) + } + } + } + } } } diff --git a/app/src/main/java/io/legado/app/ui/book/source/manage/BookSourceViewModel.kt b/app/src/main/java/io/legado/app/ui/book/source/manage/BookSourceViewModel.kt index ec5c50020..85e3239bc 100644 --- a/app/src/main/java/io/legado/app/ui/book/source/manage/BookSourceViewModel.kt +++ b/app/src/main/java/io/legado/app/ui/book/source/manage/BookSourceViewModel.kt @@ -2,6 +2,7 @@ package io.legado.app.ui.book.source.manage import android.app.Application import android.text.TextUtils +import androidx.documentfile.provider.DocumentFile import com.jayway.jsonpath.JsonPath import io.legado.app.App import io.legado.app.base.BaseViewModel @@ -89,12 +90,24 @@ class BookSourceViewModel(application: Application) : BaseViewModel(application) } } - fun exportSelection(sources: LinkedHashSet) { + fun exportSelection(sources: LinkedHashSet, file: File) { execute { val json = GSON.toJson(sources) - val file = - FileUtils.createFileIfNotExist(Backup.exportPath + File.separator + "exportBookSource.json") - file.writeText(json) + FileUtils.createFileIfNotExist(file, "exportBookSource.json") + .writeText(json) + }.onSuccess { + context.toast("成功导出至\n${Backup.exportPath}") + }.onError { + context.toast("导出失败\n${it.localizedMessage}") + } + } + + fun exportSelection(sources: LinkedHashSet, doc: DocumentFile) { + execute { + val json = GSON.toJson(sources) + doc.findFile("exportBookSource.json")?.delete() + doc.createFile("", "exportBookSource.json") + ?.writeText(context, json) }.onSuccess { context.toast("成功导出至\n${Backup.exportPath}") }.onError { diff --git a/app/src/main/java/io/legado/app/ui/config/BackupRestoreUi.kt b/app/src/main/java/io/legado/app/ui/config/BackupRestoreUi.kt index f314b5f1b..1bec0a184 100644 --- a/app/src/main/java/io/legado/app/ui/config/BackupRestoreUi.kt +++ b/app/src/main/java/io/legado/app/ui/config/BackupRestoreUi.kt @@ -3,7 +3,6 @@ package io.legado.app.ui.config import android.app.Activity.RESULT_OK import android.content.Intent import android.net.Uri -import android.os.Build import androidx.documentfile.provider.DocumentFile import androidx.fragment.app.Fragment import io.legado.app.App @@ -17,13 +16,13 @@ import io.legado.app.help.storage.Backup import io.legado.app.help.storage.ImportOldData import io.legado.app.help.storage.Restore import io.legado.app.help.storage.WebDavHelp -import io.legado.app.lib.dialogs.alert -import io.legado.app.ui.filechooser.FileChooserDialog +import io.legado.app.ui.filechooser.FilePicker import io.legado.app.utils.getPrefString import io.legado.app.utils.isContentPath import io.legado.app.utils.toast import kotlinx.coroutines.Dispatchers.Main import org.jetbrains.anko.toast +import java.io.File object BackupRestoreUi { private const val selectFolderRequestCode = 21 @@ -79,31 +78,7 @@ object BackupRestoreUi { } fun selectBackupFolder(fragment: Fragment, requestCode: Int = selectFolderRequestCode) { - fragment.alert { - titleResource = R.string.select_folder - items(fragment.resources.getStringArray(R.array.select_folder).toList()) { _, index -> - when (index) { - 0 -> backupUsePermission(fragment, requestCode = requestCode) - 1 -> { - try { - val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE) - intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) - fragment.startActivityForResult(intent, requestCode) - } catch (e: java.lang.Exception) { - e.printStackTrace() - fragment.toast(e.localizedMessage ?: "ERROR") - } - } - 2 -> { - FileChooserDialog.show( - fragment.childFragmentManager, - requestCode, - mode = FileChooserDialog.DIRECTORY - ) - } - } - } - }.show() + FilePicker.selectFolder(fragment, requestCode) } fun restore(fragment: Fragment) { @@ -120,13 +95,13 @@ object BackupRestoreUi { Restore.restore(fragment.requireContext(), backupPath) fragment.toast(R.string.restore_success) } else { - selectRestoreFolder(fragment) + selectBackupFolder(fragment, restoreSelectRequestCode) } } else { restoreUsePermission(fragment, backupPath) } } else { - selectRestoreFolder(fragment) + selectBackupFolder(fragment, restoreSelectRequestCode) } } } @@ -147,79 +122,8 @@ object BackupRestoreUi { .request() } - private fun selectRestoreFolder(fragment: Fragment) { - fragment.alert { - titleResource = R.string.select_folder - items(fragment.resources.getStringArray(R.array.select_folder).toList()) { _, index -> - when (index) { - 0 -> restoreUsePermission(fragment) - 1 -> { - try { - val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE) - intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) - fragment.startActivityForResult(intent, restoreSelectRequestCode) - } catch (e: java.lang.Exception) { - e.printStackTrace() - fragment.toast(e.localizedMessage ?: "ERROR") - } - } - 2 -> { - FileChooserDialog.show( - fragment.childFragmentManager, - restoreSelectRequestCode, - mode = FileChooserDialog.DIRECTORY - ) - } - } - } - }.show() - } - fun importOldData(fragment: Fragment) { - fragment.alert { - titleResource = R.string.select_folder - items(fragment.resources.getStringArray(R.array.select_folder).toList()) { _, index -> - when (index) { - 0 -> importOldUsePermission(fragment) - 1 -> { - try { - val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE) - intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) - fragment.startActivityForResult(intent, oldDataRequestCode) - } catch (e: java.lang.Exception) { - e.printStackTrace() - fragment.toast(e.localizedMessage ?: "ERROR") - } - } - 2 -> { - PermissionsCompat.Builder(fragment) - .addPermissions(*Permissions.Group.STORAGE) - .rationale(R.string.tip_perm_request_storage) - .onGranted { - FileChooserDialog.show( - fragment.childFragmentManager, - oldDataRequestCode, - mode = FileChooserDialog.DIRECTORY - ) - } - .request() - } - } - } - }.show() - } - - private fun importOldUsePermission(fragment: Fragment) { - if (Build.VERSION.SDK_INT > Build.VERSION_CODES.P) { - fragment.toast(R.string.a10_permission_toast) - } - PermissionsCompat.Builder(fragment) - .addPermissions(*Permissions.Group.STORAGE) - .rationale(R.string.tip_perm_request_storage) - .onGranted { - ImportOldData.import(fragment.requireContext()) - } - .request() + FilePicker.selectFolder(fragment, oldDataRequestCode) } fun onFilePicked(requestCode: Int, currentPath: String) { @@ -243,6 +147,9 @@ object BackupRestoreUi { selectFolderRequestCode -> { AppConfig.backupPath = currentPath } + oldDataRequestCode -> { + ImportOldData.import(App.INSTANCE, File(currentPath)) + } } } diff --git a/app/src/main/java/io/legado/app/ui/config/OtherConfigFragment.kt b/app/src/main/java/io/legado/app/ui/config/OtherConfigFragment.kt index 31c3d709f..e8ea335c0 100644 --- a/app/src/main/java/io/legado/app/ui/config/OtherConfigFragment.kt +++ b/app/src/main/java/io/legado/app/ui/config/OtherConfigFragment.kt @@ -16,12 +16,10 @@ import io.legado.app.constant.EventBus import io.legado.app.constant.PreferKey import io.legado.app.help.AppConfig import io.legado.app.help.BookHelp -import io.legado.app.help.permission.Permissions -import io.legado.app.help.permission.PermissionsCompat -import io.legado.app.lib.dialogs.alert import io.legado.app.lib.theme.ATH import io.legado.app.receiver.SharedReceiverActivity import io.legado.app.ui.filechooser.FileChooserDialog +import io.legado.app.ui.filechooser.FilePicker import io.legado.app.ui.widget.number.NumberPickerDialog import io.legado.app.utils.* @@ -124,38 +122,9 @@ class OtherConfigFragment : PreferenceFragmentCompat(), } private fun selectDownloadPath() { - alert { - titleResource = R.string.select_folder - items(resources.getStringArray(R.array.select_folder).toList()) { _, i -> - when (i) { - 0 -> { - removePref(PreferKey.downloadPath) - } - 1 -> { - try { - val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE) - intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) - startActivityForResult(intent, requestCodeDownloadPath) - } catch (e: Exception) { - e.printStackTrace() - toast(e.localizedMessage ?: "ERROR") - } - } - 2 -> PermissionsCompat.Builder(this@OtherConfigFragment) - .addPermissions(*Permissions.Group.STORAGE) - .rationale(R.string.tip_perm_request_storage) - .onGranted { - FileChooserDialog.show( - childFragmentManager, - requestCodeDownloadPath, - mode = FileChooserDialog.DIRECTORY, - initPath = BookHelp.downloadPath - ) - } - .request() - } - } - }.show() + FilePicker.selectFolder(this, requestCodeDownloadPath) { + removePref(PreferKey.downloadPath) + } } override fun onFilePicked(requestCode: Int, currentPath: String) { diff --git a/app/src/main/java/io/legado/app/ui/config/ThemeConfigFragment.kt b/app/src/main/java/io/legado/app/ui/config/ThemeConfigFragment.kt index 1a1851ca1..22f5e7f3d 100644 --- a/app/src/main/java/io/legado/app/ui/config/ThemeConfigFragment.kt +++ b/app/src/main/java/io/legado/app/ui/config/ThemeConfigFragment.kt @@ -18,7 +18,6 @@ import io.legado.app.lib.dialogs.yesButton import io.legado.app.lib.theme.ATH import io.legado.app.lib.theme.ColorUtils import io.legado.app.utils.* -import org.jetbrains.anko.defaultSharedPreferences class ThemeConfigFragment : PreferenceFragmentCompat(), SharedPreferences.OnSharedPreferenceChangeListener { @@ -27,7 +26,6 @@ class ThemeConfigFragment : PreferenceFragmentCompat(), SharedPreferences.OnShar override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { addPreferencesFromResource(R.xml.pref_config_theme) - onSharedPreferenceChanged(requireContext().defaultSharedPreferences, "defaultTheme") } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { @@ -49,10 +47,11 @@ class ThemeConfigFragment : PreferenceFragmentCompat(), SharedPreferences.OnShar sharedPreferences ?: return when (key) { PreferKey.launcherIcon -> LauncherIconHelp.changeIcon(getPrefString(key)) - "transparentStatusBar" -> { - recreateActivities() - } - "colorPrimary", "colorAccent", "colorBackground" -> { + "transparentStatusBar" -> recreateActivities() + "colorPrimary", + "colorAccent", + "colorBackground", + "colorBottomBackground" -> { if (backgroundIsDark(sharedPreferences)) { alert { title = "白天背景太暗" @@ -73,7 +72,10 @@ class ThemeConfigFragment : PreferenceFragmentCompat(), SharedPreferences.OnShar upTheme(false) } } - "colorPrimaryNight", "colorAccentNight", "colorBackgroundNight" -> { + "colorPrimaryNight", + "colorAccentNight", + "colorBackgroundNight", + "colorBottomBackgroundNight" -> { if (backgroundIsLight(sharedPreferences)) { alert { title = "夜间背景太亮" @@ -94,7 +96,6 @@ class ThemeConfigFragment : PreferenceFragmentCompat(), SharedPreferences.OnShar upTheme(true) } } - "defaultTheme" -> findPreference(key)?.summary = items[getPrefInt(key)] } } @@ -141,7 +142,6 @@ class ThemeConfigFragment : PreferenceFragmentCompat(), SharedPreferences.OnShar AppConfig.isNightTheme = true } } - putPrefInt("defaultTheme", which) App.INSTANCE.applyDayNight() recreateActivities() } diff --git a/app/src/main/java/io/legado/app/ui/filechooser/FileChooserDialog.kt b/app/src/main/java/io/legado/app/ui/filechooser/FileChooserDialog.kt index af8cf50f4..f1e6a7bc9 100644 --- a/app/src/main/java/io/legado/app/ui/filechooser/FileChooserDialog.kt +++ b/app/src/main/java/io/legado/app/ui/filechooser/FileChooserDialog.kt @@ -42,7 +42,7 @@ class FileChooserDialog : DialogFragment(), isShowHomeDir: Boolean = false, isShowUpDir: Boolean = true, isShowHideDir: Boolean = false, - allowExtensions: Array? = null, + allowExtensions: Array? = null, menus: Array? = null ) { FileChooserDialog().apply { @@ -61,7 +61,7 @@ class FileChooserDialog : DialogFragment(), } } - override var allowExtensions: Array? = null + override var allowExtensions: Array? = null override val isOnlyListDir: Boolean get() = mode == DIRECTORY override var isShowHomeDir: Boolean = false diff --git a/app/src/main/java/io/legado/app/ui/filechooser/FilePicker.kt b/app/src/main/java/io/legado/app/ui/filechooser/FilePicker.kt new file mode 100644 index 000000000..9260fc8bd --- /dev/null +++ b/app/src/main/java/io/legado/app/ui/filechooser/FilePicker.kt @@ -0,0 +1,191 @@ +package io.legado.app.ui.filechooser + +import android.content.Intent +import androidx.appcompat.app.AppCompatActivity +import androidx.fragment.app.Fragment +import io.legado.app.R +import io.legado.app.base.BaseActivity +import io.legado.app.help.permission.Permissions +import io.legado.app.help.permission.PermissionsCompat +import io.legado.app.lib.dialogs.alert +import io.legado.app.utils.toast +import org.jetbrains.anko.toast + +@Suppress("unused") +object FilePicker { + + fun selectFolder(activity: AppCompatActivity, requestCode: Int, default: (() -> Unit)? = null) { + activity.alert(titleResource = R.string.select_folder) { + val selectList = + activity.resources.getStringArray(R.array.select_folder).toMutableList() + default ?: let { + selectList.removeAt(0) + } + items(selectList) { _, index -> + when (if (default == null) index + 1 else index) { + 0 -> default?.invoke() + 1 -> { + try { + val intent = getSelectDirIntent() + activity.startActivityForResult(intent, requestCode) + } catch (e: java.lang.Exception) { + e.printStackTrace() + activity.toast(e.localizedMessage ?: "ERROR") + } + } + 2 -> checkPermissions(activity) { + FileChooserDialog.show( + activity.supportFragmentManager, + requestCode, + mode = FileChooserDialog.DIRECTORY + ) + } + } + } + }.show() + } + + fun selectFolder(fragment: Fragment, requestCode: Int, default: (() -> Unit)? = null) { + fragment.requireContext() + .alert(titleResource = R.string.select_folder) { + val selectList = + fragment.resources.getStringArray(R.array.select_folder).toMutableList() + default ?: let { + selectList.removeAt(0) + } + items(selectList) { _, index -> + when (if (default == null) index + 1 else index) { + 0 -> default?.invoke() + 1 -> { + try { + val intent = getSelectDirIntent() + fragment.startActivityForResult(intent, requestCode) + } catch (e: java.lang.Exception) { + e.printStackTrace() + fragment.toast(e.localizedMessage ?: "ERROR") + } + } + 2 -> checkPermissions(fragment) { + FileChooserDialog.show( + fragment.childFragmentManager, + requestCode, + mode = FileChooserDialog.DIRECTORY + ) + } + } + } + }.show() + } + + fun selectFile( + activity: BaseActivity, + requestCode: Int, + type: String, + allowExtensions: Array?, + default: (() -> Unit)? = null + ) { + activity.alert(titleResource = R.string.select_file) { + val selectList = + activity.resources.getStringArray(R.array.select_folder).toMutableList() + default ?: let { + selectList.removeAt(0) + } + items(selectList) { _, index -> + when (if (default == null) index + 1 else index) { + 0 -> default?.invoke() + 1 -> { + try { + val intent = getSelectFileIntent() + intent.type = type//设置类型 + activity.startActivityForResult(intent, requestCode) + } catch (e: java.lang.Exception) { + e.printStackTrace() + activity.toast(e.localizedMessage ?: "ERROR") + } + } + 2 -> checkPermissions(activity) { + FileChooserDialog.show( + activity.supportFragmentManager, + requestCode, + mode = FileChooserDialog.FILE, + allowExtensions = allowExtensions + ) + } + } + } + }.show() + } + + fun selectFile( + fragment: Fragment, + requestCode: Int, + type: String, + allowExtensions: Array, + default: (() -> Unit)? = null + ) { + fragment.requireContext() + .alert(titleResource = R.string.select_file) { + val selectList = + fragment.resources.getStringArray(R.array.select_folder).toMutableList() + default ?: let { + selectList.removeAt(0) + } + items(selectList) { _, index -> + when (if (default == null) index + 1 else index) { + 0 -> default?.invoke() + 1 -> { + try { + val intent = getSelectFileIntent() + intent.type = type//设置类型 + fragment.startActivityForResult(intent, requestCode) + } catch (e: java.lang.Exception) { + e.printStackTrace() + fragment.toast(e.localizedMessage ?: "ERROR") + } + } + 2 -> checkPermissions(fragment) { + FileChooserDialog.show( + fragment.childFragmentManager, + requestCode, + mode = FileChooserDialog.FILE, + allowExtensions = allowExtensions + ) + } + } + } + }.show() + } + + private fun getSelectFileIntent(): Intent { + val intent = Intent(Intent.ACTION_GET_CONTENT) + intent.addCategory(Intent.CATEGORY_OPENABLE) + intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) + return intent + } + + private fun getSelectDirIntent(): Intent { + val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE) + intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) + return intent + } + + private fun checkPermissions(fragment: Fragment, success: (() -> Unit)? = null) { + PermissionsCompat.Builder(fragment) + .addPermissions(*Permissions.Group.STORAGE) + .rationale(R.string.tip_perm_request_storage) + .onGranted { + success?.invoke() + } + .request() + } + + private fun checkPermissions(activity: AppCompatActivity, success: (() -> Unit)? = null) { + PermissionsCompat.Builder(activity) + .addPermissions(*Permissions.Group.STORAGE) + .rationale(R.string.tip_perm_request_storage) + .onGranted { + success?.invoke() + } + .request() + } +} \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/ui/filechooser/adapter/FileAdapter.kt b/app/src/main/java/io/legado/app/ui/filechooser/adapter/FileAdapter.kt index 4ade6da7f..42e34dfb1 100644 --- a/app/src/main/java/io/legado/app/ui/filechooser/adapter/FileAdapter.kt +++ b/app/src/main/java/io/legado/app/ui/filechooser/adapter/FileAdapter.kt @@ -6,9 +6,9 @@ import android.graphics.drawable.Drawable import io.legado.app.R import io.legado.app.base.adapter.ItemViewHolder import io.legado.app.base.adapter.SimpleRecyclerAdapter -import io.legado.app.ui.filechooser.FilePickerIcon import io.legado.app.ui.filechooser.entity.FileItem import io.legado.app.ui.filechooser.utils.ConvertUtils +import io.legado.app.ui.filechooser.utils.FilePickerIcon import io.legado.app.ui.filechooser.utils.FileUtils import kotlinx.android.synthetic.main.item_path_filepicker.view.* import org.jetbrains.anko.sdk27.listeners.onClick @@ -122,7 +122,7 @@ class FileAdapter(context: Context, val callBack: CallBack) : interface CallBack { fun onFileClick(position: Int) //允许的扩展名 - var allowExtensions: Array? + var allowExtensions: Array? /** * 是否仅仅读取目录 */ diff --git a/app/src/main/java/io/legado/app/ui/filechooser/adapter/PathAdapter.kt b/app/src/main/java/io/legado/app/ui/filechooser/adapter/PathAdapter.kt index 3d2323495..86ccf67c0 100644 --- a/app/src/main/java/io/legado/app/ui/filechooser/adapter/PathAdapter.kt +++ b/app/src/main/java/io/legado/app/ui/filechooser/adapter/PathAdapter.kt @@ -5,8 +5,8 @@ import android.os.Environment import io.legado.app.R import io.legado.app.base.adapter.ItemViewHolder import io.legado.app.base.adapter.SimpleRecyclerAdapter -import io.legado.app.ui.filechooser.FilePickerIcon import io.legado.app.ui.filechooser.utils.ConvertUtils +import io.legado.app.ui.filechooser.utils.FilePickerIcon import kotlinx.android.synthetic.main.item_path_filepicker.view.* import org.jetbrains.anko.sdk27.listeners.onClick import java.util.* diff --git a/app/src/main/java/io/legado/app/ui/filechooser/FilePickerIcon.java b/app/src/main/java/io/legado/app/ui/filechooser/utils/FilePickerIcon.java similarity index 99% rename from app/src/main/java/io/legado/app/ui/filechooser/FilePickerIcon.java rename to app/src/main/java/io/legado/app/ui/filechooser/utils/FilePickerIcon.java index 4ab481938..6732626e4 100644 --- a/app/src/main/java/io/legado/app/ui/filechooser/FilePickerIcon.java +++ b/app/src/main/java/io/legado/app/ui/filechooser/utils/FilePickerIcon.java @@ -1,4 +1,4 @@ -package io.legado.app.ui.filechooser; +package io.legado.app.ui.filechooser.utils; /** * Generated by https://github.com/gzu-liyujiang/Image2ByteVar diff --git a/app/src/main/java/io/legado/app/ui/filechooser/utils/FileUtils.kt b/app/src/main/java/io/legado/app/ui/filechooser/utils/FileUtils.kt index 309dee0a2..e7e2a4805 100644 --- a/app/src/main/java/io/legado/app/ui/filechooser/utils/FileUtils.kt +++ b/app/src/main/java/io/legado/app/ui/filechooser/utils/FileUtils.kt @@ -60,7 +60,7 @@ object FileUtils { @JvmOverloads fun listDirs( startDirPath: String, - excludeDirs: Array? = null, @SortType sortType: Int = BY_NAME_ASC + excludeDirs: Array? = null, @SortType sortType: Int = BY_NAME_ASC ): Array { var excludeDirs1 = excludeDirs val dirList = ArrayList() @@ -76,7 +76,7 @@ object FileUtils { }) ?: return arrayOfNulls(0) if (excludeDirs1 == null) { - excludeDirs1 = arrayOfNulls(0) + excludeDirs1 = arrayOf() } for (dir in dirs) { val file = dir.absoluteFile @@ -115,7 +115,7 @@ object FileUtils { @JvmOverloads fun listDirsAndFiles( startDirPath: String, - allowExtensions: Array? = null + allowExtensions: Array? = null ): Array? { val dirs: Array? val files: Array? = if (allowExtensions == null) { @@ -189,12 +189,13 @@ object FileUtils { /** * 列出指定目录下的所有文件 */ - fun listFiles(startDirPath: String, allowExtensions: Array): Array? { + fun listFiles(startDirPath: String, allowExtensions: Array?): Array? { val file = File(startDirPath) return file.listFiles { _, name -> //返回当前目录所有以某些扩展名结尾的文件 val extension = getExtension(name) - allowExtensions.contentDeepToString().contains(extension) + allowExtensions?.contentDeepToString()?.contains(extension) == true + || allowExtensions == null } } @@ -202,7 +203,10 @@ object FileUtils { * 列出指定目录下的所有文件 */ fun listFiles(startDirPath: String, allowExtension: String?): Array? { - return listFiles(startDirPath, arrayOf(allowExtension)) + return if (allowExtension == null) + listFiles(startDirPath, allowExtension = null) + else + listFiles(startDirPath, arrayOf(allowExtension)) } /** diff --git a/app/src/main/java/io/legado/app/ui/login/SourceLogin.kt b/app/src/main/java/io/legado/app/ui/login/SourceLogin.kt index d4e9c2e4e..35a62200c 100644 --- a/app/src/main/java/io/legado/app/ui/login/SourceLogin.kt +++ b/app/src/main/java/io/legado/app/ui/login/SourceLogin.kt @@ -1,14 +1,79 @@ package io.legado.app.ui.login +import android.annotation.SuppressLint +import android.graphics.Bitmap import android.os.Bundle +import android.view.Menu +import android.view.MenuItem +import android.webkit.CookieManager +import android.webkit.WebView +import android.webkit.WebViewClient import io.legado.app.R import io.legado.app.base.BaseActivity +import io.legado.app.help.http.CookieStore +import io.legado.app.utils.snackbar +import kotlinx.android.synthetic.main.activity_source_login.* class SourceLogin : BaseActivity(R.layout.activity_source_login) { + var sourceUrl: String? = null + var loginUrl: String? = null + var checking = false + override fun onActivityCreated(savedInstanceState: Bundle?) { + sourceUrl = intent.getStringExtra("sourceUrl") + loginUrl = intent.getStringExtra("loginUrl") + title = getString(R.string.login_source, sourceUrl) + initWebView() + } + + @SuppressLint("SetJavaScriptEnabled") + private fun initWebView() { + val settings = web_view.settings + settings.setSupportZoom(true) + settings.builtInZoomControls = true + settings.javaScriptEnabled = true + val cookieManager = CookieManager.getInstance() + web_view.webViewClient = object : WebViewClient() { + override fun onPageStarted(view: WebView?, url: String?, favicon: Bitmap?) { + val cookie = cookieManager.getCookie(url) + sourceUrl?.let { + CookieStore.setCookie(it, cookie) + } + super.onPageStarted(view, url, favicon) + } + + override fun onPageFinished(view: WebView?, url: String?) { + val cookie = cookieManager.getCookie(url) + sourceUrl?.let { + CookieStore.setCookie(it, cookie) + } + if (checking) { + finish() + } + super.onPageFinished(view, url) + } + } + web_view.loadUrl(loginUrl) + } + + override fun onCompatCreateOptionsMenu(menu: Menu): Boolean { + menuInflater.inflate(R.menu.source_login, menu) + return super.onCompatCreateOptionsMenu(menu) + } + override fun onCompatOptionsItemSelected(item: MenuItem): Boolean { + when (item.itemId) { + R.id.menu_success -> { + if (!checking) { + checking = true + title_bar.snackbar(R.string.check_host_cookie) + web_view.loadUrl(sourceUrl) + } + } + } + return super.onCompatOptionsItemSelected(item) } } \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/ui/main/MainActivity.kt b/app/src/main/java/io/legado/app/ui/main/MainActivity.kt index 38569115b..8b4705ae0 100644 --- a/app/src/main/java/io/legado/app/ui/main/MainActivity.kt +++ b/app/src/main/java/io/legado/app/ui/main/MainActivity.kt @@ -7,7 +7,6 @@ import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentManager import androidx.fragment.app.FragmentStatePagerAdapter import androidx.viewpager.widget.ViewPager -import com.github.houbb.opencc4j.util.ZhConverterUtil import com.google.android.material.bottomnavigation.BottomNavigationView import io.legado.app.App import io.legado.app.BuildConfig @@ -16,7 +15,6 @@ import io.legado.app.base.VMBaseActivity import io.legado.app.constant.EventBus import io.legado.app.constant.PreferKey import io.legado.app.help.AppConfig -import io.legado.app.help.coroutine.Coroutine import io.legado.app.help.storage.Backup import io.legado.app.lib.theme.ATH import io.legado.app.service.BaseReadAloudService @@ -53,21 +51,12 @@ class MainActivity : VMBaseActivity(R.layout.activity_main), override fun onPostCreate(savedInstanceState: Bundle?) { super.onPostCreate(savedInstanceState) upVersion() - //初始化简繁转换引擎 - when (AppConfig.chineseConverterType) { - 1 -> Coroutine.async { ZhConverterUtil.toSimple("初始化") } - 2 -> Coroutine.async { ZhConverterUtil.toTraditional("初始化") } - } //自动更新书籍 if (AppConfig.autoRefreshBook) { view_pager_main.postDelayed({ viewModel.upChapterList() }, 1000) } - //清楚过期数据 - view_pager_main.postDelayed({ - viewModel.clearExpiredData() - }, 3000) } override fun onNavigationItemSelected(item: MenuItem): Boolean { diff --git a/app/src/main/java/io/legado/app/ui/main/MainViewModel.kt b/app/src/main/java/io/legado/app/ui/main/MainViewModel.kt index 1a0652bae..4fe57d758 100644 --- a/app/src/main/java/io/legado/app/ui/main/MainViewModel.kt +++ b/app/src/main/java/io/legado/app/ui/main/MainViewModel.kt @@ -13,7 +13,6 @@ import io.legado.app.utils.fromJsonObject import io.legado.app.utils.postEvent import kotlinx.coroutines.Dispatchers.IO import kotlinx.coroutines.delay -import java.util.concurrent.TimeUnit class MainViewModel(application: Application) : BaseViewModel(application) { val updateList = hashSetOf() @@ -54,13 +53,6 @@ class MainViewModel(application: Application) : BaseViewModel(application) { } } - fun clearExpiredData() { - execute { - App.db.searchBookDao() - .clearExpired(System.currentTimeMillis() - TimeUnit.DAYS.toMillis(1)) - } - } - fun initRss() { execute { val url = "https://gitee.com/alanskycn/yuedu/raw/master/JS/RSS/rssSource" diff --git a/app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfFragment.kt b/app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfFragment.kt index f49cf2ea6..a3a228dd7 100644 --- a/app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfFragment.kt +++ b/app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfFragment.kt @@ -27,8 +27,8 @@ import io.legado.app.lib.theme.accentColor import io.legado.app.ui.book.arrange.ArrangeBookActivity import io.legado.app.ui.book.group.GroupManageDialog import io.legado.app.ui.book.search.SearchActivity -import io.legado.app.ui.download.DownloadActivity -import io.legado.app.ui.importbook.ImportBookActivity +import io.legado.app.ui.book.download.DownloadActivity +import io.legado.app.ui.book.local.ImportBookActivity import io.legado.app.ui.widget.text.AutoCompleteTextView import io.legado.app.utils.* import kotlinx.android.synthetic.main.dialog_bookshelf_config.view.* diff --git a/app/src/main/java/io/legado/app/ui/main/bookshelf/books/BooksFragment.kt b/app/src/main/java/io/legado/app/ui/main/bookshelf/books/BooksFragment.kt index 49cadb56c..95b5b1431 100644 --- a/app/src/main/java/io/legado/app/ui/main/bookshelf/books/BooksFragment.kt +++ b/app/src/main/java/io/legado/app/ui/main/bookshelf/books/BooksFragment.kt @@ -111,7 +111,7 @@ class BooksFragment : BaseFragment(R.layout.fragment_books), else -> list.sortedByDescending { it.durChapterTime } } val diffResult = DiffUtil - .calculateDiff(BooksDiffCallBack(ArrayList(booksAdapter.getItems()), books)) + .calculateDiff(BooksDiffCallBack(booksAdapter.getItems(), books)) booksAdapter.setItems(books, diffResult) }) } diff --git a/app/src/main/java/io/legado/app/ui/main/explore/ExploreFragment.kt b/app/src/main/java/io/legado/app/ui/main/explore/ExploreFragment.kt index 78cdd141a..f4257732c 100644 --- a/app/src/main/java/io/legado/app/ui/main/explore/ExploreFragment.kt +++ b/app/src/main/java/io/legado/app/ui/main/explore/ExploreFragment.kt @@ -18,7 +18,7 @@ import io.legado.app.data.entities.BookSource import io.legado.app.lib.theme.ATH import io.legado.app.lib.theme.primaryTextColor import io.legado.app.ui.book.source.edit.BookSourceEditActivity -import io.legado.app.ui.explore.ExploreShowActivity +import io.legado.app.ui.book.explore.ExploreShowActivity import io.legado.app.utils.getViewModel import io.legado.app.utils.splitNotBlank import io.legado.app.utils.startActivity diff --git a/app/src/main/java/io/legado/app/ui/replacerule/ReplaceRuleActivity.kt b/app/src/main/java/io/legado/app/ui/replacerule/ReplaceRuleActivity.kt index 5044e2667..91f7e7b75 100644 --- a/app/src/main/java/io/legado/app/ui/replacerule/ReplaceRuleActivity.kt +++ b/app/src/main/java/io/legado/app/ui/replacerule/ReplaceRuleActivity.kt @@ -9,6 +9,7 @@ import android.view.MenuItem import android.view.SubMenu import androidx.appcompat.widget.PopupMenu import androidx.appcompat.widget.SearchView +import androidx.documentfile.provider.DocumentFile import androidx.lifecycle.LiveData import androidx.lifecycle.Observer import androidx.recyclerview.widget.DiffUtil @@ -19,13 +20,14 @@ import io.legado.app.App import io.legado.app.R import io.legado.app.base.VMBaseActivity import io.legado.app.data.entities.ReplaceRule +import io.legado.app.help.BookHelp import io.legado.app.help.ItemTouchCallback -import io.legado.app.help.permission.Permissions -import io.legado.app.help.permission.PermissionsCompat +import io.legado.app.help.coroutine.Coroutine import io.legado.app.lib.dialogs.* import io.legado.app.lib.theme.ATH import io.legado.app.lib.theme.primaryTextColor import io.legado.app.ui.filechooser.FileChooserDialog +import io.legado.app.ui.filechooser.FilePicker import io.legado.app.ui.replacerule.edit.ReplaceEditDialog import io.legado.app.ui.widget.SelectActionBar import io.legado.app.ui.widget.recycler.VerticalDivider @@ -36,7 +38,6 @@ import kotlinx.android.synthetic.main.dialog_edit_text.view.* import kotlinx.android.synthetic.main.view_search.* import org.jetbrains.anko.toast import java.io.File -import java.io.FileNotFoundException class ReplaceRuleActivity : VMBaseActivity(R.layout.activity_replace_rule), @@ -47,7 +48,8 @@ class ReplaceRuleActivity : VMBaseActivity(R.layout.activi override val viewModel: ReplaceRuleViewModel get() = getViewModel(ReplaceRuleViewModel::class.java) private val importRecordKey = "replaceRuleRecordKey" - private val importSource = 132 + private val importRequestCode = 132 + private val exportRequestCode = 65 private lateinit var adapter: ReplaceRuleAdapter private var groups = hashSetOf() private var groupMenu: SubMenu? = null @@ -139,8 +141,8 @@ class ReplaceRuleActivity : VMBaseActivity(R.layout.activi } private fun observeReplaceRuleData(key: String? = null) { - replaceRuleLiveData?.removeObservers(this) dataInit = false + replaceRuleLiveData?.removeObservers(this) replaceRuleLiveData = if (key.isNullOrEmpty()) { App.db.replaceRuleDao().liveDataAll() } else { @@ -177,7 +179,8 @@ class ReplaceRuleActivity : VMBaseActivity(R.layout.activi R.id.menu_del_selection -> viewModel.delSelection(adapter.getSelection()) R.id.menu_import_source_onLine -> showImportDialog() - R.id.menu_import_source_local -> selectFileSys() + R.id.menu_import_source_local -> FilePicker + .selectFile(this, importRequestCode, "text/*", arrayOf("txt", "json")) } return super.onCompatOptionsItemSelected(item) } @@ -186,7 +189,7 @@ class ReplaceRuleActivity : VMBaseActivity(R.layout.activi when (item?.itemId) { R.id.menu_enable_selection -> viewModel.enableSelection(adapter.getSelection()) R.id.menu_disable_selection -> viewModel.disableSelection(adapter.getSelection()) - R.id.menu_export_selection -> viewModel.exportSelection(adapter.getSelection()) + R.id.menu_export_selection -> FilePicker.selectFolder(this, exportRequestCode) } return false } @@ -233,43 +236,6 @@ class ReplaceRuleActivity : VMBaseActivity(R.layout.activi }.show().applyTint() } - private fun selectFileSys() { - try { - val intent = Intent(Intent.ACTION_GET_CONTENT) - intent.addCategory(Intent.CATEGORY_OPENABLE) - intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) - intent.type = "text/*"//设置类型 - startActivityForResult(intent, importSource) - } catch (e: Exception) { - PermissionsCompat.Builder(this) - .addPermissions( - Permissions.READ_EXTERNAL_STORAGE, - Permissions.WRITE_EXTERNAL_STORAGE - ) - .rationale(R.string.bg_image_per) - .onGranted { - selectFile() - } - .request() - } - } - - private fun selectFile() { - FileChooserDialog.show( - supportFragmentManager, importSource, - allowExtensions = arrayOf("txt", "json") - ) - } - - override fun onFilePicked(requestCode: Int, currentPath: String) { - if (requestCode == importSource) { - Snackbar.make(title_bar, R.string.importing, Snackbar.LENGTH_INDEFINITE).show() - viewModel.importSource(File(currentPath).readText()) { msg -> - title_bar.snackbar(msg) - } - } - } - override fun onQueryTextChange(newText: String?): Boolean { observeReplaceRuleData("%$newText%") return false @@ -279,10 +245,25 @@ class ReplaceRuleActivity : VMBaseActivity(R.layout.activi return false } + override fun onFilePicked(requestCode: Int, currentPath: String) { + when (requestCode) { + importRequestCode -> { + Snackbar.make(title_bar, R.string.importing, Snackbar.LENGTH_INDEFINITE).show() + viewModel.importSource(File(currentPath).readText()) { msg -> + title_bar.snackbar(msg) + } + } + exportRequestCode -> viewModel.exportSelection( + adapter.getSelection(), + File(currentPath) + ) + } + } + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) when (requestCode) { - importSource -> if (resultCode == Activity.RESULT_OK) { + importRequestCode -> if (resultCode == Activity.RESULT_OK) { data?.data?.let { uri -> try { uri.readText(this)?.let { @@ -292,46 +273,58 @@ class ReplaceRuleActivity : VMBaseActivity(R.layout.activi title_bar.snackbar(msg) } } - } catch (e: FileNotFoundException) { - PermissionsCompat.Builder(this) - .addPermissions( - Permissions.READ_EXTERNAL_STORAGE, - Permissions.WRITE_EXTERNAL_STORAGE - ) - .rationale(R.string.bg_image_per) - .onGranted { - selectFileSys() - } - .request() } catch (e: Exception) { toast(e.localizedMessage ?: "ERROR") } } } + exportRequestCode -> if (resultCode == RESULT_OK) { + data?.data?.let { uri -> + if (uri.toString().isContentPath()) { + DocumentFile.fromTreeUri(this, uri)?.let { + viewModel.exportSelection(adapter.getSelection(), it) + } + } else { + uri.path?.let { + viewModel.exportSelection(adapter.getSelection(), File(it)) + } + } + } + } } } + override fun onDestroy() { + super.onDestroy() + Coroutine.async { BookHelp.upReplaceRules() } + } + override fun upCountView() { select_action_bar.upCountView(adapter.getSelection().size, adapter.getActualItemCount()) } override fun update(vararg rule: ReplaceRule) { + setResult(Activity.RESULT_OK) viewModel.update(*rule) } override fun delete(rule: ReplaceRule) { + setResult(Activity.RESULT_OK) viewModel.delete(rule) } override fun edit(rule: ReplaceRule) { + setResult(Activity.RESULT_OK) ReplaceEditDialog.show(supportFragmentManager, rule.id) } override fun toTop(rule: ReplaceRule) { + setResult(Activity.RESULT_OK) viewModel.toTop(rule) } override fun upOrder() { + setResult(Activity.RESULT_OK) viewModel.upOrder() } } \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/ui/replacerule/ReplaceRuleViewModel.kt b/app/src/main/java/io/legado/app/ui/replacerule/ReplaceRuleViewModel.kt index 42217ec18..a63d7ea38 100644 --- a/app/src/main/java/io/legado/app/ui/replacerule/ReplaceRuleViewModel.kt +++ b/app/src/main/java/io/legado/app/ui/replacerule/ReplaceRuleViewModel.kt @@ -2,6 +2,7 @@ package io.legado.app.ui.replacerule import android.app.Application import android.text.TextUtils +import androidx.documentfile.provider.DocumentFile import io.legado.app.App import io.legado.app.R import io.legado.app.base.BaseViewModel @@ -9,10 +10,7 @@ import io.legado.app.data.entities.ReplaceRule import io.legado.app.help.http.HttpHelper import io.legado.app.help.storage.Backup import io.legado.app.help.storage.ImportOldData -import io.legado.app.utils.FileUtils -import io.legado.app.utils.GSON -import io.legado.app.utils.isAbsUrl -import io.legado.app.utils.splitNotBlank +import io.legado.app.utils.* import org.jetbrains.anko.toast import java.io.File @@ -89,12 +87,24 @@ class ReplaceRuleViewModel(application: Application) : BaseViewModel(application } } - fun exportSelection(rules: LinkedHashSet) { + fun exportSelection(sources: LinkedHashSet, file: File) { execute { - val json = GSON.toJson(rules) - val file = - FileUtils.createFileIfNotExist(Backup.exportPath + File.separator + "exportReplaceRule.json") - file.writeText(json) + val json = GSON.toJson(sources) + FileUtils.createFileIfNotExist(file, "exportReplaceRule.json") + .writeText(json) + }.onSuccess { + context.toast("成功导出至\n${Backup.exportPath}") + }.onError { + context.toast("导出失败\n${it.localizedMessage}") + } + } + + fun exportSelection(sources: LinkedHashSet, doc: DocumentFile) { + execute { + val json = GSON.toJson(sources) + doc.findFile("exportReplaceRule.json")?.delete() + doc.createFile("", "exportReplaceRule.json") + ?.writeText(context, json) }.onSuccess { context.toast("成功导出至\n${Backup.exportPath}") }.onError { diff --git a/app/src/main/java/io/legado/app/ui/replacerule/edit/ReplaceEditDialog.kt b/app/src/main/java/io/legado/app/ui/replacerule/edit/ReplaceEditDialog.kt index de5002931..d125674c5 100644 --- a/app/src/main/java/io/legado/app/ui/replacerule/edit/ReplaceEditDialog.kt +++ b/app/src/main/java/io/legado/app/ui/replacerule/edit/ReplaceEditDialog.kt @@ -23,11 +23,17 @@ class ReplaceEditDialog : DialogFragment(), companion object { - fun show(fragmentManager: FragmentManager, id: Long = -1, pattern: String? = null) { + fun show( + fragmentManager: FragmentManager, + id: Long = -1, + pattern: String? = null, + isRegex: Boolean = false + ) { val dialog = ReplaceEditDialog() val bundle = Bundle() bundle.putLong("id", id) bundle.putString("pattern", pattern) + bundle.putBoolean("isRegex", isRegex) dialog.arguments = bundle dialog.show(fragmentManager, "editReplace") } @@ -68,6 +74,7 @@ class ReplaceEditDialog : DialogFragment(), when (item?.itemId) { R.id.menu_save -> { viewModel.save(getReplaceRule()) { + callBack?.onReplaceRuleSave() dismiss() } } @@ -94,4 +101,10 @@ class ReplaceEditDialog : DialogFragment(), replaceRule.scope = et_scope.text.toString() return replaceRule } + + val callBack get() = activity as? CallBack + + interface CallBack { + fun onReplaceRuleSave() + } } diff --git a/app/src/main/java/io/legado/app/ui/replacerule/edit/ReplaceEditViewModel.kt b/app/src/main/java/io/legado/app/ui/replacerule/edit/ReplaceEditViewModel.kt index f326c88c3..722deb738 100644 --- a/app/src/main/java/io/legado/app/ui/replacerule/edit/ReplaceEditViewModel.kt +++ b/app/src/main/java/io/legado/app/ui/replacerule/edit/ReplaceEditViewModel.kt @@ -21,8 +21,9 @@ class ReplaceEditViewModel(application: Application) : BaseViewModel(application } } else { bundle.getString("pattern")?.let { pattern -> + val isRegex = bundle.getBoolean("isRegex") replaceRuleData.postValue( - ReplaceRule(pattern = pattern) + ReplaceRule(pattern = pattern, isRegex = isRegex) ) } } diff --git a/app/src/main/java/io/legado/app/ui/rss/read/ReadRssActivity.kt b/app/src/main/java/io/legado/app/ui/rss/read/ReadRssActivity.kt index 255505178..10504efd7 100644 --- a/app/src/main/java/io/legado/app/ui/rss/read/ReadRssActivity.kt +++ b/app/src/main/java/io/legado/app/ui/rss/read/ReadRssActivity.kt @@ -65,7 +65,7 @@ class ReadRssActivity : VMBaseActivity(R.layout.activity_rss_r } private fun initWebView() { - webView.webViewClient = object : WebViewClient() { + web_view.webViewClient = object : WebViewClient() { override fun shouldOverrideUrlLoading( view: WebView?, request: WebResourceRequest? @@ -78,8 +78,19 @@ class ReadRssActivity : VMBaseActivity(R.layout.activity_rss_r } return true } + + @Suppress("DEPRECATION") + override fun shouldOverrideUrlLoading(view: WebView?, url: String?): Boolean { + if (url?.startsWith("http", true) == true) { + return false + } + url?.let { + openUrl(it) + } + return true + } } - webView.settings.apply { + web_view.settings.apply { mixedContentMode = WebSettings.MIXED_CONTENT_ALWAYS_ALLOW domStorageEnabled = true allowContentAccess = true @@ -94,23 +105,35 @@ class ReadRssActivity : VMBaseActivity(R.layout.activity_rss_r val url = NetworkUtils.getAbsoluteURL(it.origin, it.link) val html = viewModel.clHtml(content) if (viewModel.rssSource?.loadWithBaseUrl == true) { - webView.loadDataWithBaseURL(url, html, "text/html", "utf-8", url)//不想用baseUrl进else + web_view.loadDataWithBaseURL( + url, + html, + "text/html", + "utf-8", + url + )//不想用baseUrl进else } else { //webView.loadData(html, "text/html;charset=utf-8", "utf-8")//经测试可以解决中文乱码 - webView.loadDataWithBaseURL(null, html, "text/html;charset=utf-8", "utf-8", url) + web_view.loadDataWithBaseURL( + null, + html, + "text/html;charset=utf-8", + "utf-8", + url + ) } } }) viewModel.urlLiveData.observe(this, Observer { upJavaScriptEnable() - webView.loadUrl(it.url, it.headerMap) + web_view.loadUrl(it.url, it.headerMap) }) } @SuppressLint("SetJavaScriptEnabled") private fun upJavaScriptEnable() { if (viewModel.rssSource?.enableJs == true) { - webView.settings.javaScriptEnabled = true + web_view.settings.javaScriptEnabled = true } } @@ -151,9 +174,9 @@ class ReadRssActivity : VMBaseActivity(R.layout.activity_rss_r override fun onKeyUp(keyCode: Int, event: KeyEvent?): Boolean { event?.let { when (keyCode) { - KeyEvent.KEYCODE_BACK -> if (event.isTracking && !event.isCanceled && webView.canGoBack()) { - if (webView.copyBackForwardList().size > 1) { - webView.goBack() + KeyEvent.KEYCODE_BACK -> if (event.isTracking && !event.isCanceled && web_view.canGoBack()) { + if (web_view.copyBackForwardList().size > 1) { + web_view.goBack() return true } } @@ -168,8 +191,8 @@ class ReadRssActivity : VMBaseActivity(R.layout.activity_rss_r viewModel.textToSpeech.stop() upTtsMenu(false) } else { - webView.settings.javaScriptEnabled = true - webView.evaluateJavascript("document.documentElement.outerHTML") { + web_view.settings.javaScriptEnabled = true + web_view.evaluateJavascript("document.documentElement.outerHTML") { val html = StringEscapeUtils.unescapeJson(it) val text = Jsoup.clean(html, Whitelist.none()) .replace(Regex("""&\w+;"""), "") diff --git a/app/src/main/java/io/legado/app/ui/rss/source/manage/RssSourceActivity.kt b/app/src/main/java/io/legado/app/ui/rss/source/manage/RssSourceActivity.kt index 22277476e..c3dd008c8 100644 --- a/app/src/main/java/io/legado/app/ui/rss/source/manage/RssSourceActivity.kt +++ b/app/src/main/java/io/legado/app/ui/rss/source/manage/RssSourceActivity.kt @@ -9,6 +9,7 @@ import android.view.MenuItem import android.view.SubMenu import androidx.appcompat.widget.PopupMenu import androidx.appcompat.widget.SearchView +import androidx.documentfile.provider.DocumentFile import androidx.lifecycle.LiveData import androidx.lifecycle.Observer import androidx.recyclerview.widget.DiffUtil @@ -20,12 +21,11 @@ import io.legado.app.R import io.legado.app.base.VMBaseActivity import io.legado.app.data.entities.RssSource import io.legado.app.help.ItemTouchCallback -import io.legado.app.help.permission.Permissions -import io.legado.app.help.permission.PermissionsCompat import io.legado.app.lib.dialogs.* import io.legado.app.lib.theme.ATH import io.legado.app.lib.theme.primaryTextColor import io.legado.app.ui.filechooser.FileChooserDialog +import io.legado.app.ui.filechooser.FilePicker import io.legado.app.ui.qrcode.QrCodeActivity import io.legado.app.ui.rss.source.edit.RssSourceEditActivity import io.legado.app.ui.widget.SelectActionBar @@ -38,7 +38,7 @@ import kotlinx.android.synthetic.main.view_search.* import org.jetbrains.anko.startActivity import org.jetbrains.anko.startActivityForResult import org.jetbrains.anko.toast -import java.io.FileNotFoundException +import java.io.File class RssSourceActivity : VMBaseActivity(R.layout.activity_rss_source), @@ -50,7 +50,8 @@ class RssSourceActivity : VMBaseActivity(R.layout.activity_r get() = getViewModel(RssSourceViewModel::class.java) private val importRecordKey = "rssSourceRecordKey" private val qrRequestCode = 101 - private val importSource = 124 + private val importRequestCode = 124 + private val exportRequestCode = 65 private lateinit var adapter: RssSourceAdapter private var sourceLiveData: LiveData>? = null private var groups = hashSetOf() @@ -79,7 +80,8 @@ class RssSourceActivity : VMBaseActivity(R.layout.activity_r override fun onCompatOptionsItemSelected(item: MenuItem): Boolean { when (item.itemId) { R.id.menu_add -> startActivity() - R.id.menu_import_source_local -> selectFileSys() + R.id.menu_import_source_local -> FilePicker + .selectFile(this, importRequestCode, "text/*", arrayOf("txt", "json")) R.id.menu_import_source_onLine -> showImportDialog() R.id.menu_import_source_qr -> startActivityForResult(qrRequestCode) R.id.menu_group_manage -> GroupManageDialog() @@ -96,7 +98,7 @@ class RssSourceActivity : VMBaseActivity(R.layout.activity_r R.id.menu_enable_selection -> viewModel.enableSelection(adapter.getSelection()) R.id.menu_disable_selection -> viewModel.disableSelection(adapter.getSelection()) R.id.menu_del_selection -> viewModel.delSelection(adapter.getSelection()) - R.id.menu_export_selection -> viewModel.exportSelection(adapter.getSelection()) + R.id.menu_export_selection -> FilePicker.selectFolder(this, exportRequestCode) R.id.menu_check_source -> { } } @@ -248,46 +250,25 @@ class RssSourceActivity : VMBaseActivity(R.layout.activity_r }.show().applyTint() } - private fun selectFileSys() { - try { - val intent = Intent(Intent.ACTION_GET_CONTENT) - intent.addCategory(Intent.CATEGORY_OPENABLE) - intent.type = "text/*"//设置类型 - startActivityForResult(intent, importSource) - } catch (e: Exception) { - PermissionsCompat.Builder(this) - .addPermissions( - Permissions.READ_EXTERNAL_STORAGE, - Permissions.WRITE_EXTERNAL_STORAGE - ) - .rationale(R.string.bg_image_per) - .onGranted { - selectFile() - } - .request() - } - } - - private fun selectFile() { - FileChooserDialog.show( - supportFragmentManager, importSource, - allowExtensions = arrayOf("txt", "json") - ) - } - override fun onFilePicked(requestCode: Int, currentPath: String) { - if (requestCode == importSource) { - Snackbar.make(title_bar, R.string.importing, Snackbar.LENGTH_INDEFINITE).show() - viewModel.importSourceFromFilePath(currentPath) { msg -> - title_bar.snackbar(msg) + when (requestCode) { + importRequestCode -> { + Snackbar.make(title_bar, R.string.importing, Snackbar.LENGTH_INDEFINITE).show() + viewModel.importSourceFromFilePath(currentPath) { msg -> + title_bar.snackbar(msg) + } } + exportRequestCode -> viewModel.exportSelection( + adapter.getSelection(), + File(currentPath) + ) } } override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) when (requestCode) { - importSource -> if (resultCode == Activity.RESULT_OK) { + importRequestCode -> if (resultCode == Activity.RESULT_OK) { data?.data?.let { uri -> try { uri.readText(this)?.let { @@ -297,17 +278,6 @@ class RssSourceActivity : VMBaseActivity(R.layout.activity_r title_bar.snackbar(msg) } } - } catch (e: FileNotFoundException) { - PermissionsCompat.Builder(this) - .addPermissions( - Permissions.READ_EXTERNAL_STORAGE, - Permissions.WRITE_EXTERNAL_STORAGE - ) - .rationale(R.string.bg_image_per) - .onGranted { - selectFileSys() - } - .request() } catch (e: Exception) { toast(e.localizedMessage ?: "ERROR") } @@ -321,6 +291,19 @@ class RssSourceActivity : VMBaseActivity(R.layout.activity_r } } } + exportRequestCode -> if (resultCode == RESULT_OK) { + data?.data?.let { uri -> + if (uri.toString().isContentPath()) { + DocumentFile.fromTreeUri(this, uri)?.let { + viewModel.exportSelection(adapter.getSelection(), it) + } + } else { + uri.path?.let { + viewModel.exportSelection(adapter.getSelection(), File(it)) + } + } + } + } } } diff --git a/app/src/main/java/io/legado/app/ui/rss/source/manage/RssSourceViewModel.kt b/app/src/main/java/io/legado/app/ui/rss/source/manage/RssSourceViewModel.kt index bb9e5f219..9160155b3 100644 --- a/app/src/main/java/io/legado/app/ui/rss/source/manage/RssSourceViewModel.kt +++ b/app/src/main/java/io/legado/app/ui/rss/source/manage/RssSourceViewModel.kt @@ -2,6 +2,7 @@ package io.legado.app.ui.rss.source.manage import android.app.Application import android.text.TextUtils +import androidx.documentfile.provider.DocumentFile import com.jayway.jsonpath.JsonPath import io.legado.app.App import io.legado.app.R @@ -67,12 +68,24 @@ class RssSourceViewModel(application: Application) : BaseViewModel(application) } } - fun exportSelection(sources: LinkedHashSet) { + fun exportSelection(sources: LinkedHashSet, file: File) { execute { val json = GSON.toJson(sources) - val file = - FileUtils.createFileIfNotExist(Backup.exportPath + File.separator + "exportRssSource.json") - file.writeText(json) + FileUtils.createFileIfNotExist(file, "exportRssSource.json") + .writeText(json) + }.onSuccess { + context.toast("成功导出至\n${Backup.exportPath}") + }.onError { + context.toast("导出失败\n${it.localizedMessage}") + } + } + + fun exportSelection(sources: LinkedHashSet, doc: DocumentFile) { + execute { + val json = GSON.toJson(sources) + doc.findFile("exportRssSource.json")?.delete() + doc.createFile("", "exportRssSource.json") + ?.writeText(context, json) }.onSuccess { context.toast("成功导出至\n${Backup.exportPath}") }.onError { diff --git a/app/src/main/java/io/legado/app/ui/welcome/WelcomeActivity.kt b/app/src/main/java/io/legado/app/ui/welcome/WelcomeActivity.kt index 86b326ee1..7e535f009 100644 --- a/app/src/main/java/io/legado/app/ui/welcome/WelcomeActivity.kt +++ b/app/src/main/java/io/legado/app/ui/welcome/WelcomeActivity.kt @@ -1,49 +1,55 @@ package io.legado.app.ui.welcome -import android.animation.Animator -import android.animation.ValueAnimator import android.content.Intent import android.os.Bundle +import com.github.houbb.opencc4j.util.ZhConverterUtil +import io.legado.app.App import io.legado.app.R import io.legado.app.base.BaseActivity +import io.legado.app.help.AppConfig +import io.legado.app.help.coroutine.Coroutine import io.legado.app.lib.theme.accentColor import io.legado.app.ui.book.read.ReadBookActivity import io.legado.app.ui.main.MainActivity import io.legado.app.utils.getPrefBoolean import kotlinx.android.synthetic.main.activity_welcome.* import org.jetbrains.anko.startActivity +import java.util.concurrent.TimeUnit open class WelcomeActivity : BaseActivity(R.layout.activity_welcome) { override fun onActivityCreated(savedInstanceState: Bundle?) { - iv_bg.setColorFilter(accentColor) + iv_book.setColorFilter(accentColor) + vw_title_line.setBackgroundColor(accentColor) // 避免从桌面启动程序后,会重新实例化入口类的activity if (intent.flags and Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT != 0) { finish() - return + } else { + init() } - val welAnimator = ValueAnimator.ofFloat(1f, 0f).setDuration(800) - welAnimator.startDelay = 100 - welAnimator.addUpdateListener { animation -> - val alpha = animation.animatedValue as Float - iv_bg.alpha = alpha - } - welAnimator.addListener(object : Animator.AnimatorListener { - override fun onAnimationStart(animation: Animator) { - startActivity() - if (getPrefBoolean(getString(R.string.pk_default_read))) { - startActivity() - } - finish() - } - - override fun onAnimationEnd(animation: Animator) = Unit + } - override fun onAnimationCancel(animation: Animator) = Unit + private fun init() { + Coroutine.async { + //清楚过期数据 + App.db.searchBookDao() + .clearExpired(System.currentTimeMillis() - TimeUnit.DAYS.toMillis(1)) + //初始化简繁转换引擎 + when (AppConfig.chineseConverterType) { + 1 -> ZhConverterUtil.toSimple("初始化") + 2 -> ZhConverterUtil.toTraditional("初始化") + else -> null + } + } + root_view.postDelayed({ startMainActivity() }, 300) + } - override fun onAnimationRepeat(animation: Animator) = Unit - }) - welAnimator.start() + private fun startMainActivity() { + startActivity() + if (getPrefBoolean(getString(R.string.pk_default_read))) { + startActivity() + } + finish() } } diff --git a/app/src/main/java/io/legado/app/ui/widget/SelectActionBar.kt b/app/src/main/java/io/legado/app/ui/widget/SelectActionBar.kt index 3aab86a50..dc37085b0 100644 --- a/app/src/main/java/io/legado/app/ui/widget/SelectActionBar.kt +++ b/app/src/main/java/io/legado/app/ui/widget/SelectActionBar.kt @@ -9,6 +9,8 @@ import androidx.annotation.MenuRes import androidx.annotation.StringRes import androidx.appcompat.widget.PopupMenu import io.legado.app.R +import io.legado.app.lib.theme.bottomBackground +import io.legado.app.utils.dp import io.legado.app.utils.visible import kotlinx.android.synthetic.main.view_select_action_bar.view.* import org.jetbrains.anko.sdk27.listeners.onClick @@ -18,7 +20,8 @@ class SelectActionBar(context: Context, attrs: AttributeSet?) : FrameLayout(cont private var selMenu: PopupMenu? = null init { - setBackgroundResource(R.color.background_menu) + setBackgroundColor(context.bottomBackground) + elevation = 10.dp.toFloat() View.inflate(context, R.layout.view_select_action_bar, this) cb_selected_all.setOnCheckedChangeListener { buttonView, isChecked -> if (buttonView.isPressed) { diff --git a/app/src/main/java/io/legado/app/ui/widget/font/FontSelectDialog.kt b/app/src/main/java/io/legado/app/ui/widget/font/FontSelectDialog.kt index 87d87e8d6..bbeb7f04e 100644 --- a/app/src/main/java/io/legado/app/ui/widget/font/FontSelectDialog.kt +++ b/app/src/main/java/io/legado/app/ui/widget/font/FontSelectDialog.kt @@ -23,13 +23,13 @@ import io.legado.app.help.permission.Permissions import io.legado.app.help.permission.PermissionsCompat import io.legado.app.lib.dialogs.alert import io.legado.app.ui.filechooser.FileChooserDialog +import io.legado.app.ui.filechooser.FilePicker import io.legado.app.utils.* import kotlinx.android.synthetic.main.dialog_font_select.* import kotlinx.coroutines.Dispatchers.IO import kotlinx.coroutines.Dispatchers.Main import kotlinx.coroutines.launch import kotlinx.coroutines.withContext -import org.jetbrains.anko.toast import java.io.File class FontSelectDialog : BaseDialogFragment(), @@ -107,41 +107,11 @@ class FontSelectDialog : BaseDialogFragment(), private fun openFolder() { launch(Main) { - alert { - titleResource = R.string.select_folder - items(resources.getStringArray(R.array.select_folder).toList()) { _, index -> - when (index) { - 0 -> { - val path = "${FileUtils.getSdCardPath()}${File.separator}Fonts" - putPrefString(PreferKey.fontFolder, path) - getFontFilesByPermission(path) - } - 1 -> { - try { - val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE) - intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) - startActivityForResult(intent, fontFolderRequestCode) - } catch (e: java.lang.Exception) { - e.printStackTrace() - requireContext().toast(e.localizedMessage ?: "ERROR") - } - } - 2 -> { - PermissionsCompat.Builder(this@FontSelectDialog) - .addPermissions(*Permissions.Group.STORAGE) - .rationale(R.string.tip_perm_request_storage) - .onGranted { - FileChooserDialog.show( - childFragmentManager, - fontFolderRequestCode, - mode = FileChooserDialog.DIRECTORY - ) - } - .request() - } - } - } - }.show() + FilePicker.selectFolder(this@FontSelectDialog, fontFolderRequestCode) { + val path = "${FileUtils.getSdCardPath()}${File.separator}Fonts" + putPrefString(PreferKey.fontFolder, path) + getFontFilesByPermission(path) + } } } diff --git a/app/src/main/java/io/legado/app/utils/MenuExtensions.kt b/app/src/main/java/io/legado/app/utils/MenuExtensions.kt index d8ecff3e1..9f13d543d 100644 --- a/app/src/main/java/io/legado/app/utils/MenuExtensions.kt +++ b/app/src/main/java/io/legado/app/utils/MenuExtensions.kt @@ -1,5 +1,6 @@ package io.legado.app.utils +import android.annotation.SuppressLint import android.content.Context import android.view.Menu import android.view.MenuItem @@ -13,6 +14,7 @@ import io.legado.app.lib.theme.primaryTextColor import java.lang.reflect.Method import java.util.* +@SuppressLint("RestrictedApi") fun Menu.applyTint(context: Context, theme: Theme = Theme.Auto): Menu = this.let { menu -> if (menu is MenuBuilder) { menu.setOptionalIconsVisible(true) diff --git a/app/src/main/java/io/legado/app/utils/UriExtensions.kt b/app/src/main/java/io/legado/app/utils/UriExtensions.kt index 672259dc8..583f699f7 100644 --- a/app/src/main/java/io/legado/app/utils/UriExtensions.kt +++ b/app/src/main/java/io/legado/app/utils/UriExtensions.kt @@ -19,19 +19,17 @@ fun Uri.readBytes(context: Context): ByteArray? { @Throws(Exception::class) fun Uri.readText(context: Context): String? { - if (this.toString().isContentPath()) { - return DocumentUtils.readText(context, this) - } else { - val path = RealPathUtil.getPath(context, this) - if (path?.isNotEmpty() == true) { - return File(path).readText() - } + readBytes(context)?.let { + return String(it) } return null } @Throws(Exception::class) -fun Uri.writeBytes(context: Context, byteArray: ByteArray): Boolean { +fun Uri.writeBytes( + context: Context, + byteArray: ByteArray +): Boolean { if (this.toString().isContentPath()) { return DocumentUtils.writeBytes(context, byteArray, this) } else { @@ -46,14 +44,5 @@ fun Uri.writeBytes(context: Context, byteArray: ByteArray): Boolean { @Throws(Exception::class) fun Uri.writeText(context: Context, text: String): Boolean { - if (this.toString().isContentPath()) { - return DocumentUtils.writeText(context, text, this) - } else { - val path = RealPathUtil.getPath(context, this) - if (path?.isNotEmpty() == true) { - File(path).writeText(text) - return true - } - } - return false -} \ No newline at end of file + return writeBytes(context, text.toByteArray()) +} diff --git a/app/src/main/res/drawable/image_welcome.xml b/app/src/main/res/drawable/image_welcome.xml deleted file mode 100644 index d9d1c13a9..000000000 --- a/app/src/main/res/drawable/image_welcome.xml +++ /dev/null @@ -1,75 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - \ 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 71a7edfe8..7869b3b1c 100644 --- a/app/src/main/res/layout/activity_audio_play.xml +++ b/app/src/main/res/layout/activity_audio_play.xml @@ -21,10 +21,13 @@ android:layout_height="match_parent" android:background="#50000000" /> - + app:layout_constraintTop_toBottomOf="@+id/toolbar" /> - @@ -38,7 +41,7 @@ android:scaleType="centerCrop" android:src="@drawable/image_cover_default" app:layout_constraintLeft_toLeftOf="parent" - app:layout_constraintTop_toBottomOf="@+id/title_bar" /> + app:layout_constraintTop_toBottomOf="@+id/toolbar" /> diff --git a/app/src/main/res/layout/activity_source_login.xml b/app/src/main/res/layout/activity_source_login.xml index e2b27076c..ed20ab520 100644 --- a/app/src/main/res/layout/activity_source_login.xml +++ b/app/src/main/res/layout/activity_source_login.xml @@ -2,15 +2,17 @@ + android:layout_height="wrap_content" + app:title="@string/login" /> diff --git a/app/src/main/res/layout/activity_welcome.xml b/app/src/main/res/layout/activity_welcome.xml index e8e320279..6013c90b1 100644 --- a/app/src/main/res/layout/activity_welcome.xml +++ b/app/src/main/res/layout/activity_welcome.xml @@ -1,15 +1,71 @@ - + android:layout_height="match_parent" + android:gravity="center_horizontal"> - + + + + + + + android:contentDescription="@string/welcome" + app:layout_constraintBottom_toTopOf="@+id/tv_gzh" + app:layout_constraintLeft_toLeftOf="parent" + app:layout_constraintRight_toRightOf="parent" /> + + - \ No newline at end of file + \ No newline at end of file diff --git a/app/src/main/res/layout/dialog_change_cover.xml b/app/src/main/res/layout/dialog_change_cover.xml new file mode 100644 index 000000000..cc7486b05 --- /dev/null +++ b/app/src/main/res/layout/dialog_change_cover.xml @@ -0,0 +1,27 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/dialog_change_source.xml b/app/src/main/res/layout/dialog_change_source.xml index 1199d4538..f007fae87 100644 --- a/app/src/main/res/layout/dialog_change_source.xml +++ b/app/src/main/res/layout/dialog_change_source.xml @@ -1,31 +1,39 @@ - - + android:background="@color/background_menu"> + + + + + + + android:layout_height="2dp" /> + android:layout_height="match_parent" /> - \ No newline at end of file + \ No newline at end of file diff --git a/app/src/main/res/layout/dialog_read_aloud.xml b/app/src/main/res/layout/dialog_read_aloud.xml index ea04696d4..627bd9414 100644 --- a/app/src/main/res/layout/dialog_read_aloud.xml +++ b/app/src/main/res/layout/dialog_read_aloud.xml @@ -60,6 +60,7 @@ @@ -90,7 +91,7 @@ android:id="@+id/fabAutoPage" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_margin="10dp" + android:layout_margin="16dp" android:contentDescription="@string/auto_next_page" android:src="@drawable/ic_auto_page" android:tint="@color/tv_text_default" @@ -110,7 +111,7 @@ android:id="@+id/fabReplaceRule" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_margin="10dp" + android:layout_margin="16dp" android:contentDescription="@string/replace_rule_title" android:src="@drawable/ic_find_replace" android:tint="@color/tv_text_default" @@ -130,7 +131,7 @@ android:id="@+id/fabNightTheme" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_margin="10dp" + android:layout_margin="16dp" android:contentDescription="@string/dark_theme" android:src="@drawable/ic_brightness" android:tint="@color/tv_text_default" @@ -143,8 +144,6 @@ - - diff --git a/app/src/main/res/menu/source_debug.xml b/app/src/main/res/menu/source_debug.xml index 5b8b467f2..a55aecf0f 100644 --- a/app/src/main/res/menu/source_debug.xml +++ b/app/src/main/res/menu/source_debug.xml @@ -3,7 +3,7 @@ xmlns:app="http://schemas.android.com/apk/res-auto"> diff --git a/app/src/main/res/menu/source_login.xml b/app/src/main/res/menu/source_login.xml new file mode 100644 index 000000000..c58ada14b --- /dev/null +++ b/app/src/main/res/menu/source_login.xml @@ -0,0 +1,11 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.png b/app/src/main/res/mipmap-hdpi/ic_launcher.png index 80723f59b..a7e087bc8 100644 Binary files a/app/src/main/res/mipmap-hdpi/ic_launcher.png and b/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.png b/app/src/main/res/mipmap-mdpi/ic_launcher.png index 40accb5f5..f0e9ad532 100644 Binary files a/app/src/main/res/mipmap-mdpi/ic_launcher.png and b/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/app/src/main/res/mipmap-xhdpi/ic_launcher.png index afdcc42eb..371e045cd 100644 Binary files a/app/src/main/res/mipmap-xhdpi/ic_launcher.png and b/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png index 3e9fdc29a..feff4e965 100644 Binary files a/app/src/main/res/mipmap-xxhdpi/ic_launcher.png and b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png index a51e958cf..63cb54502 100644 Binary files a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 054222265..d6999a7ae 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -15,7 +15,7 @@ @color/md_grey_50 @color/md_grey_100 - @color/md_grey_300 + @color/md_grey_200 #00000000 #30000000 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 3f54ea194..3c92dae8f 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -488,6 +488,7 @@ 三字符缩进 四字符缩进 选择文件夹 + 选择文件 没有发现,可以在书源里添加。 恢复默认 自定义缓存路径需要存储权限 @@ -495,8 +496,6 @@ 文章内容为空 正在换源请等待… 目录列表为空 - 正文边距 - Tip边距 字距 基本 @@ -629,4 +628,5 @@ 其它朗读设置 系统内置字体样式 + 是否删除源文件 diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index d5c9e7104..4cc3907e0 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -33,7 +33,6 @@ @style/Style.PopupMenu @color/transparent true - 0dp