diff --git a/app/src/main/assets/updateLog.md b/app/src/main/assets/updateLog.md index 9bf7bcd87..b78537486 100644 --- a/app/src/main/assets/updateLog.md +++ b/app/src/main/assets/updateLog.md @@ -1,11 +1,27 @@ ## 更新日志 -* 旧版数据导入教程: -* 先在旧版阅读(2.x)中进行备份,然后在新版阅读(3.x)【我的】->【备份与恢复】,选择【导入旧版本数据】。 +* 旧版数据导入教程:先在旧版阅读(2.x)中进行备份,然后在新版阅读(3.x)【我的】->【备份与恢复】,选择【导入旧版本数据】。 +* 请关注[开源阅读软件]()支持我,同时关注合作公众号[小说拾遗](),阅读公众号小编。 + +**2020/03/02** +* 添加书源登录 +* 替换规则实时生效 +* 页面最后一行计算是否能放下时不计算行距 + +**2020/03/01** +* 修复书源解析的一个bug +* 添加底部操作栏颜色配置 +* 修复滚动点击翻页,修复滚动最后一页显示加载中 +* 去除备份恢复默认路径 +* 尝试修复部分手机一键导入书源报错 +* 翻页还有些bug不用反馈了,我已经知道,会修复的 **2020/02/29** * 添加书源一键导入 * 修复主题模式跟随系统 * 修复书源校验 +* 添加书架排序 +* 添加点击翻页开关 +* 修复共用布局没有记住配置的bug **2020/02/28** * 解决阅读界面部分字体超出范围的问题 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/constant/AppConst.kt b/app/src/main/java/io/legado/app/constant/AppConst.kt index 3a5b42efe..96cc7567f 100644 --- a/app/src/main/java/io/legado/app/constant/AppConst.kt +++ b/app/src/main/java/io/legado/app/constant/AppConst.kt @@ -22,7 +22,7 @@ object AppConst { const val UA_NAME = "User-Agent" val userAgent: String by lazy { - "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36" + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36" } val SCRIPT_ENGINE: ScriptEngine by lazy { diff --git a/app/src/main/java/io/legado/app/constant/PreferKey.kt b/app/src/main/java/io/legado/app/constant/PreferKey.kt index 6be087f05..72d8c6b15 100644 --- a/app/src/main/java/io/legado/app/constant/PreferKey.kt +++ b/app/src/main/java/io/legado/app/constant/PreferKey.kt @@ -5,6 +5,7 @@ object PreferKey { const val themeMode = "themeMode" const val downloadPath = "downloadPath" const val hideStatusBar = "hideStatusBar" + const val clickTurnPage = "clickTurnPage" const val clickAllNext = "clickAllNext" const val hideNavigationBar = "hideNavigationBar" const val precisionSearch = "precisionSearch" @@ -16,6 +17,7 @@ object PreferKey { const val nextKey = "nextKeyCode" const val showRss = "showRss" const val bookshelfLayout = "bookshelfLayout" + const val bookshelfSort = "bookshelfSort" const val recordLog = "recordLog" const val processText = "process_text" const val cleanCache = "cleanCache" 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 57ccd5f14..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,23 +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 var bg: Drawable? = null init { @@ -112,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..9087c3e9f 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.createFileIfNotExist(file, "myBookSource.json") val json = sourceFile.readText() val importCount = importOldSource(json) withContext(Dispatchers.Main) { @@ -53,7 +50,7 @@ object ImportOldData { try {// Replace rules val ruleFile = - FileUtils.createFileIfNotExist(yueDuPath + File.separator + "myBookReplaceRule.json") + FileUtils.createFileIfNotExist(file, "myBookReplaceRule.json") val json = ruleFile.readText() val importCount = importOldReplaceRule(json) withContext(Dispatchers.Main) { @@ -115,13 +112,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/OldRule.kt b/app/src/main/java/io/legado/app/help/storage/OldRule.kt index c8036e559..7696bb383 100644 --- a/app/src/main/java/io/legado/app/help/storage/OldRule.kt +++ b/app/src/main/java/io/legado/app/help/storage/OldRule.kt @@ -100,15 +100,22 @@ object OldRule { if (oldRule.isNullOrBlank()) return null var newRule = oldRule var reverse = false + var allinone = false if (oldRule.startsWith("-")) { reverse = true newRule = oldRule.substring(1) } + if (newRule.startsWith("+")) { + allinone = true + newRule = newRule.substring(1) + } if (!newRule.startsWith("@CSS:", true) && !newRule.startsWith("@XPath:", true) && !newRule.startsWith("//") && !newRule.startsWith("##") && - !newRule.startsWith(":") + !newRule.startsWith(":") && + !newRule.contains("@js:",true) && + !newRule.contains("",true) ) { if (newRule.contains("#") && !newRule.contains("##")) { newRule = oldRule.replace("#", "##") @@ -134,8 +141,11 @@ object OldRule { newRule = newRule.replace("&", "&&") } } + if (allinone) { + newRule = "+" + newRule + } if (reverse) { - newRule += "-" + newRule = "-" + newRule } return newRule } @@ -145,7 +155,7 @@ object OldRule { if (!oldUrls.contains("\n") && !oldUrls.contains("&&")) return toNewUrl(oldUrls) - val urls = oldUrls.split("(&&|\n)+".toRegex()) + val urls = oldUrls.split("(&&|\r?\n)+".toRegex()) return urls.map { toNewUrl(it)?.replace("\n\\s*".toRegex(), "") }.joinToString("\n") 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/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/ui/book/read/ReadBookActivity.kt b/app/src/main/java/io/legado/app/ui/book/read/ReadBookActivity.kt index 48bdd6cf6..09d29a7f3 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 @@ -40,6 +42,7 @@ 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.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 @@ -219,6 +223,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) } @@ -544,14 +554,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 +639,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..7ea2cff28 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 @@ -12,6 +12,7 @@ import io.legado.app.App import io.legado.app.R import io.legado.app.help.AppConfig import io.legado.app.help.ReadBookConfig +import io.legado.app.lib.theme.ATH import io.legado.app.lib.theme.accentColor import io.legado.app.lib.theme.buttonDisabledColor import io.legado.app.service.help.ReadBook @@ -48,6 +49,7 @@ class ReadMenu : FrameLayout { fabNightTheme.setImageResource(R.drawable.ic_brightness) } initAnimation() + ATH.applyBackgroundTint(fl_bottom_bg) 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/ReadStyleDialog.kt b/app/src/main/java/io/legado/app/ui/book/read/config/ReadStyleDialog.kt index b7a0a330b..996eaa25c 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,7 +11,6 @@ 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 @@ -19,6 +18,7 @@ import io.legado.app.lib.theme.primaryColor import io.legado.app.ui.book.read.Help import io.legado.app.ui.book.read.ReadBookActivity import io.legado.app.ui.widget.font.FontSelectDialog +import io.legado.app.utils.getIndexById import io.legado.app.utils.getPrefString import io.legado.app.utils.postEvent import io.legado.app.utils.putPrefString @@ -30,6 +30,8 @@ import org.jetbrains.anko.sdk27.listeners.onLongClick class ReadStyleDialog : DialogFragment(), FontSelectDialog.CallBack { + val callBack get() = activity as? ReadBookActivity + override fun onStart() { super.onStart() val dm = DisplayMetrics() @@ -115,16 +117,13 @@ 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) } } tv_padding.onClick { - val activity = activity dismiss() - if (activity is ReadBookActivity) { - activity.showPaddingConfig() - } + callBack?.showPaddingConfig() } dsb_text_size.onChanged = { ReadBookConfig.textSize = it + 5 @@ -143,15 +142,9 @@ class ReadStyleDialog : DialogFragment(), FontSelectDialog.CallBack { postEvent(EventBus.UP_CONFIG, true) } rg_page_anim.onCheckedChange { _, checkedId -> - for (i in 0 until rg_page_anim.childCount) { - if (checkedId == rg_page_anim[i].id) { - ReadBookConfig.pageAnim = i - val activity = activity - if (activity is ReadBookActivity) { - activity.page_view.upPageAnim() - } - break - } + rg_page_anim.getIndexById(checkedId).let { + ReadBookConfig.pageAnim = it + callBack?.page_view?.upPageAnim() } } cb_share_layout.onCheckedChangeListener = { checkBox, isChecked -> @@ -186,10 +179,7 @@ class ReadStyleDialog : DialogFragment(), FontSelectDialog.CallBack { private fun showBgTextConfig(index: Int): Boolean { dismiss() changeBg(index) - val activity = activity - if (activity is ReadBookActivity) { - activity.showBgTextConfig() - } + callBack?.showBgTextConfig() return 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 1482d309a..c6839b214 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 @@ -120,7 +120,7 @@ class PageView(context: Context, attrs: AttributeSet) : if (noAnim) { fillPage(PageDelegate.Direction.PREV) } else { - pageDelegate?.start(PageDelegate.Direction.PREV) + pageDelegate?.prevPageByAnim() } } @@ -128,7 +128,7 @@ class PageView(context: Context, attrs: AttributeSet) : if (noAnim) { fillPage(PageDelegate.Direction.NEXT) } else { - pageDelegate?.start(PageDelegate.Direction.NEXT) + pageDelegate?.nextPageByAnim() } } 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/HorizontalPageDelegate.kt b/app/src/main/java/io/legado/app/ui/book/read/page/delegate/HorizontalPageDelegate.kt index 586b3d88e..700728b14 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 @@ -52,4 +52,19 @@ abstract class HorizontalPageDelegate(pageView: PageView) : PageDelegate(pageVie } } + override fun nextPageByAnim() { + if (!hasNext()) return + setDirection(Direction.NEXT) + setBitmap() + setTouchPoint(viewWidth.toFloat(), 0f) + onAnimStart() + } + + override fun 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 2b26714c4..76b40fb78 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 @@ -12,6 +12,7 @@ import android.widget.Scroller import androidx.annotation.CallSuper import com.google.android.material.snackbar.Snackbar 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 @@ -157,34 +158,6 @@ abstract class PageDelegate(protected val pageView: PageView) : } } - fun start(direction: Direction) { - if (isStarted) return - if (direction === Direction.NEXT) { - val x = viewWidth.toFloat() - val y = viewHeight.toFloat() - //初始化动画 - setStartPoint(x, y, false) - //设置点击点 - setTouchPoint(x, y, false) - //设置方向 - if (!hasNext()) { - return - } - } else { - val x = 0.toFloat() - val y = viewHeight.toFloat() - //初始化动画 - setStartPoint(x, y, false) - //设置点击点 - setTouchPoint(x, y, false) - //设置方向方向 - if (!hasPrev()) { - return - } - } - onAnimStart() - } - open fun onAnimStart() {}//scroller start open fun onDraw(canvas: Canvas) {}//绘制 @@ -193,6 +166,10 @@ abstract class PageDelegate(protected val pageView: PageView) : open fun onScroll() {}//移动contentView, slidePage + abstract fun nextPageByAnim() + + abstract fun prevPageByAnim() + @CallSuper open fun setDirection(direction: Direction) { mDirection = direction @@ -260,21 +237,14 @@ abstract class PageDelegate(protected val pageView: PageView) : if (centerRectF.contains(x, y)) { pageView.callBack.clickCenter() setTouchPoint(x, y) - } else { + } else if (ReadBookConfig.clickTurnPage) { if (x > viewWidth / 2 || AppConfig.clickAllNext ) { - //设置动画方向 - if (!hasNext()) return true - setDirection(Direction.NEXT) - setBitmap() + nextPageByAnim() } else { - if (!hasPrev()) return true - setDirection(Direction.PREV) - setBitmap() + prevPageByAnim() } - setTouchPoint(x, y) - onAnimStart() } return true } 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 d90ee8649..a3434aeed 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 @@ -58,4 +59,12 @@ class ScrollPageDelegate(pageView: PageView) : PageDelegate(pageView) { super.onDestroy() mVelocity.recycle() } + + override fun nextPageByAnim() { + startScroll(0, 0, 0, -ChapterProvider.visibleHeight) + } + + override fun 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/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..0652350c5 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 @@ -52,7 +52,10 @@ class ThemeConfigFragment : PreferenceFragmentCompat(), SharedPreferences.OnShar "transparentStatusBar" -> { recreateActivities() } - "colorPrimary", "colorAccent", "colorBackground" -> { + "colorPrimary", + "colorAccent", + "colorBackground", + "colorBottomBackground" -> { if (backgroundIsDark(sharedPreferences)) { alert { title = "白天背景太暗" @@ -73,7 +76,10 @@ class ThemeConfigFragment : PreferenceFragmentCompat(), SharedPreferences.OnShar upTheme(false) } } - "colorPrimaryNight", "colorAccentNight", "colorBackgroundNight" -> { + "colorPrimaryNight", + "colorAccentNight", + "colorBackgroundNight", + "colorBottomBackgroundNight" -> { if (backgroundIsLight(sharedPreferences)) { alert { title = "夜间背景太亮" diff --git a/app/src/main/java/io/legado/app/ui/download/DownloadActivity.kt b/app/src/main/java/io/legado/app/ui/download/DownloadActivity.kt index ef1d87e68..cdcbf9746 100644 --- a/app/src/main/java/io/legado/app/ui/download/DownloadActivity.kt +++ b/app/src/main/java/io/legado/app/ui/download/DownloadActivity.kt @@ -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/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 16b7b286f..f49cf2ea6 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 @@ -2,6 +2,7 @@ package io.legado.app.ui.main.bookshelf import android.annotation.SuppressLint import android.os.Bundle +import android.view.LayoutInflater import android.view.Menu import android.view.MenuItem import android.view.View @@ -17,7 +18,10 @@ import io.legado.app.constant.AppConst import io.legado.app.constant.EventBus import io.legado.app.constant.PreferKey import io.legado.app.data.entities.BookGroup -import io.legado.app.lib.dialogs.* +import io.legado.app.lib.dialogs.alert +import io.legado.app.lib.dialogs.customView +import io.legado.app.lib.dialogs.noButton +import io.legado.app.lib.dialogs.okButton import io.legado.app.lib.theme.ATH import io.legado.app.lib.theme.accentColor import io.legado.app.ui.book.arrange.ArrangeBookActivity @@ -27,6 +31,7 @@ import io.legado.app.ui.download.DownloadActivity import io.legado.app.ui.importbook.ImportBookActivity import io.legado.app.ui.widget.text.AutoCompleteTextView import io.legado.app.utils.* +import kotlinx.android.synthetic.main.dialog_bookshelf_config.view.* import kotlinx.android.synthetic.main.dialog_edit_text.view.* import kotlinx.android.synthetic.main.fragment_bookshelf.* import kotlinx.android.synthetic.main.view_tab_layout.* @@ -61,7 +66,7 @@ class BookshelfFragment : VMBaseFragment(R.layout.fragment_b super.onCompatOptionsItemSelected(item) when (item.itemId) { R.id.menu_search -> startActivity() - R.id.menu_bookshelf_layout -> selectBookshelfLayout() + R.id.menu_bookshelf_layout -> configBookshelf() R.id.menu_group_manage -> GroupManageDialog() .show(childFragmentManager, "groupManageDialog") R.id.menu_add_local -> startActivity() @@ -145,14 +150,35 @@ class BookshelfFragment : VMBaseFragment(R.layout.fragment_b } } - private fun selectBookshelfLayout() { - selector( - title = "选择书架布局", - items = resources.getStringArray(R.array.bookshelf_layout).toList() - ) { _, index -> - putPrefInt(PreferKey.bookshelfLayout, index) - activity?.recreate() - } + @SuppressLint("InflateParams") + private fun configBookshelf() { + requireContext().alert(titleResource = R.string.bookshelf_layout) { + val bookshelfLayout = getPrefInt(PreferKey.bookshelfLayout) + val bookshelfSort = getPrefInt(PreferKey.bookshelfSort) + val root = LayoutInflater.from(requireContext()) + .inflate(R.layout.dialog_bookshelf_config, null).apply { + rg_layout.checkByIndex(bookshelfLayout) + rg_sort.checkByIndex(bookshelfSort) + } + customView = root + okButton { + root.apply { + var changed = false + if (bookshelfLayout != rg_layout.getCheckedIndex()) { + putPrefInt(PreferKey.bookshelfLayout, rg_layout.getCheckedIndex()) + changed = true + } + if (bookshelfSort != rg_sort.getCheckedIndex()) { + putPrefInt(PreferKey.bookshelfSort, rg_sort.getCheckedIndex()) + changed = true + } + if (changed) { + activity?.recreate() + } + } + } + noButton() + }.show().applyTint() } @SuppressLint("InflateParams") 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 1a462a294..49cadb56c 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 @@ -103,10 +103,16 @@ class BooksFragment : BaseFragment(R.layout.fragment_books), -3 -> App.db.bookDao().observeAudio() else -> App.db.bookDao().observeByGroup(groupId) } - bookshelfLiveData?.observe(this, Observer { + bookshelfLiveData?.observe(this, Observer { list -> + val books = when (getPrefInt(PreferKey.bookshelfSort)) { + 1 -> list.sortedByDescending { it.latestChapterTime } + 2 -> list.sortedBy { it.name } + 3 -> list.sortedBy { it.order } + else -> list.sortedByDescending { it.durChapterTime } + } val diffResult = DiffUtil - .calculateDiff(BooksDiffCallBack(ArrayList(booksAdapter.getItems()), it)) - booksAdapter.setItems(it, diffResult) + .calculateDiff(BooksDiffCallBack(ArrayList(booksAdapter.getItems()), books)) + booksAdapter.setItems(books, diffResult) }) } 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..e5be539c5 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 @@ -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,25 +273,32 @@ 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()) } 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/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/java/io/legado/app/utils/ViewExtensions.kt b/app/src/main/java/io/legado/app/utils/ViewExtensions.kt index 8eb3cb1f6..b8a457325 100644 --- a/app/src/main/java/io/legado/app/utils/ViewExtensions.kt +++ b/app/src/main/java/io/legado/app/utils/ViewExtensions.kt @@ -7,8 +7,10 @@ import android.os.Build import android.view.View import android.view.View.* import android.view.inputmethod.InputMethodManager +import android.widget.RadioGroup import android.widget.SeekBar import androidx.appcompat.app.AppCompatActivity +import androidx.core.view.get import io.legado.app.App @@ -69,4 +71,26 @@ fun View.screenshot(): Bitmap? { fun SeekBar.progressAdd(int: Int) { progress += int +} + +fun RadioGroup.getIndexById(id: Int): Int { + for (i in 0 until this.childCount) { + if (id == get(i).id) { + return i + } + } + return 0 +} + +fun RadioGroup.getCheckedIndex(): Int { + for (i in 0 until this.childCount) { + if (checkedRadioButtonId == get(i).id) { + return i + } + } + return 0 +} + +fun RadioGroup.checkByIndex(index: Int) { + check(get(index).id) } \ No newline at end of file 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_main.xml b/app/src/main/res/layout/activity_main.xml index a21e6de1c..9f2d2044e 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -8,7 +8,7 @@ android:id="@+id/bottom_navigation_view" android:layout_width="match_parent" android:layout_height="wrap_content" - android:elevation="6dp" + android:elevation="10dp" android:background="@color/background" app:labelVisibilityMode="labeled" app:menu="@menu/main_bnv" diff --git a/app/src/main/res/layout/activity_rss_read.xml b/app/src/main/res/layout/activity_rss_read.xml index e2b27076c..3c1fd1a3c 100644 --- a/app/src/main/res/layout/activity_rss_read.xml +++ b/app/src/main/res/layout/activity_rss_read.xml @@ -10,7 +10,7 @@ android:layout_height="wrap_content" /> 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_bookshelf_config.xml b/app/src/main/res/layout/dialog_bookshelf_config.xml new file mode 100644 index 000000000..944913f4c --- /dev/null +++ b/app/src/main/res/layout/dialog_bookshelf_config.xml @@ -0,0 +1,105 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/dialog_number_picker.xml b/app/src/main/res/layout/dialog_number_picker.xml index 5a471be84..f54fd0a00 100644 --- a/app/src/main/res/layout/dialog_number_picker.xml +++ b/app/src/main/res/layout/dialog_number_picker.xml @@ -7,6 +7,7 @@ android:id="@+id/number_picker" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_gravity="center" /> + android:layout_gravity="center" + android:scrollbars="none" /> \ No newline at end of file diff --git a/app/src/main/res/layout/view_read_menu.xml b/app/src/main/res/layout/view_read_menu.xml index d23bc2248..9344b90f2 100644 --- a/app/src/main/res/layout/view_read_menu.xml +++ b/app/src/main/res/layout/view_read_menu.xml @@ -147,6 +147,7 @@ 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/values/arrays.xml b/app/src/main/res/values/arrays.xml index bdcdf57eb..4cd90b40e 100644 --- a/app/src/main/res/values/arrays.xml +++ b/app/src/main/res/values/arrays.xml @@ -35,14 +35,6 @@ 9 - - @string/layout_list - @string/layout_grid3 - @string/layout_grid4 - @string/layout_grid5 - @string/layout_grid6 - - @string/indent_0 @string/indent_1 @@ -111,18 +103,6 @@ @string/screen_sensor - - @string/bookshelf_px_0 - @string/bookshelf_px_1 - @string/bookshelf_px_2 - - - - 0 - 1 - 2 - - 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 8da65e500..7903cadfb 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -47,12 +47,13 @@ 书架还空着,先去添加吧! 搜索 下载 - 列表视图 - 网格视图三列 - 网格视图四列 - 网格视图五列 - 网格视图六列 + 列表 + 网格三列 + 网格四列 + 网格五列 + 网格六列 书架布局 + 视图 书城 添加本地 书源 @@ -192,7 +193,7 @@ 封面 音量键翻页 - 点击翻页 + 点击翻页 点击总是翻下一页 翻页动画 屏幕超时 @@ -226,11 +227,11 @@ 来源:%s 本地导入 网络导入 - 书架排序 检查更新间隔 - 按阅读时间排序 - 按更新时间排序 - 手动排序 + 按阅读时间 + 按更新时间 + 按书名 + 手动排序 阅读方式 删除所选 是否确认删除? @@ -487,6 +488,7 @@ 三字符缩进 四字符缩进 选择文件夹 + 选择文件 没有发现,可以在书源里添加。 恢复默认 自定义缓存路径需要存储权限 diff --git a/app/src/main/res/xml/pref_config_read.xml b/app/src/main/res/xml/pref_config_read.xml index 37b089a09..a19a8ba4c 100644 --- a/app/src/main/res/xml/pref_config_read.xml +++ b/app/src/main/res/xml/pref_config_read.xml @@ -28,6 +28,12 @@ android:key="volumeKeyPage" app:iconSpaceReserved="false" /> + + + + + + \ No newline at end of file