diff --git a/app/src/main/assets/help/jsHelp.md b/app/src/main/assets/help/jsHelp.md index 804dd0d87..a23c29fab 100644 --- a/app/src/main/assets/help/jsHelp.md +++ b/app/src/main/assets/help/jsHelp.md @@ -82,8 +82,16 @@ java.base64Encode(str: String, flags: Int) * 文件 > 所有对于文件的读写删操作都是相对路径,只能操作阅读缓存/android/data/{package}/cache/内的文件 ``` -java.readTxtFile(path: String): String -java.deleteFile(path: String) +//文件下载,content为十六进制字符串,url用于生成文件名,返回文件路径 +downloadFile(content: String, url: String): String +//文件解压,zipPath为压缩文件路径,返回解压路径 +unzipFile(zipPath: String): String +//文件夹内所有文件读取 +getTxtInFolder(unzipPath: String): String +//读取文本文件 +readTxtFile(path: String): String +//删除文件 +deleteFile(path: String) ``` **** > [常见加密解密算法介绍](https://www.yijiyong.com/algorithm/encryption/01-intro.html) diff --git a/app/src/main/assets/updateLog.md b/app/src/main/assets/updateLog.md index cfb37bd1c..cbdfd3181 100644 --- a/app/src/main/assets/updateLog.md +++ b/app/src/main/assets/updateLog.md @@ -11,6 +11,16 @@ * 正文出现缺字漏字、内容缺失、排版错乱等情况,有可能是净化规则或简繁转换出现问题。 * 漫画源看书显示乱码,**阅读与其他软件的源并不通用**,请导入阅读的支持的漫画源! +**2022/02/02** + +* 字符串大于1024时禁用代码高亮 +* 修复baseUrl丢失参数的bug +* 添加更新bookUrl和更新tocUrl函数 +* 标题单独净化,防止出现正文标题没了的问题 +* 修复一个null报错 +* 按返回键关闭搜索界面 by Xwite +* 其它一些优化 by Xwite + **2022/01/28** * 阅读背景添加透明度调节 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 1016f7bdb..ba66b900e 100644 --- a/app/src/main/java/io/legado/app/help/JsExtensions.kt +++ b/app/src/main/java/io/legado/app/help/JsExtensions.kt @@ -9,6 +9,7 @@ import cn.hutool.crypto.symmetric.DESede import io.legado.app.BuildConfig import io.legado.app.constant.AppConst import io.legado.app.constant.AppConst.dateFormat +import io.legado.app.constant.AppLog import io.legado.app.data.entities.BaseSource import io.legado.app.help.http.* import io.legado.app.model.Debug @@ -513,13 +514,14 @@ interface JsExtensions { /** * 输出调试日志 */ - fun log(msg: String): String { + fun log(msg: Any?): Any? { getSource()?.let { - Debug.log(it.getKey(), msg) - } ?: Debug.log(msg) + Debug.log(it.getKey(), msg.toString()) + } ?: Debug.log(msg.toString()) if (BuildConfig.DEBUG) { - Timber.d(msg) + Timber.d(msg.toString()) } + AppLog.put(msg.toString()) return msg } @@ -692,7 +694,7 @@ interface JsExtensions { return aesEncodeToBase64ByteArray(data, key, transformation, iv)?.let { String(it) } } - fun android(): String { + fun androidId(): String { return AppConst.androidId } 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 1e1b87915..e1d098517 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 @@ -4,12 +4,11 @@ import android.text.TextUtils import androidx.annotation.Keep import io.legado.app.constant.AppConst.SCRIPT_ENGINE import io.legado.app.constant.AppPattern.JS_PATTERN -import io.legado.app.data.entities.BaseBook -import io.legado.app.data.entities.BaseSource -import io.legado.app.data.entities.BookChapter +import io.legado.app.data.entities.* import io.legado.app.help.CacheManager import io.legado.app.help.JsExtensions import io.legado.app.help.http.CookieStore +import io.legado.app.model.webBook.WebBook import io.legado.app.utils.* import kotlinx.coroutines.runBlocking import org.jsoup.nodes.Entities @@ -684,6 +683,39 @@ class AnalyzeRule( return s } + /** + * 更新BookUrl,如果搜索结果有tocUrl也会更新,有些书源bookUrl定期更新,可以在js内调用更新 + */ + fun refreshBookUrl() { + runBlocking { + val bookSource = source as? BookSource + val book = book as? Book + if (bookSource == null || book == null) return@runBlocking + val books = WebBook.searchBookAwait(this, bookSource, book.name) + books.forEach { + if (it.name == book.name && it.author == book.author) { + book.bookUrl = it.bookUrl + if (it.tocUrl.isNotBlank()) { + book.tocUrl = it.tocUrl + } + return@runBlocking + } + } + } + } + + /** + * 更新tocUrl,有些书源目录url定期更新,可以在js调用更新 + */ + fun refreshTocUrl() { + runBlocking { + val bookSource = source as? BookSource + val book = book as? Book + if (bookSource == null || book == null) return@runBlocking + WebBook.getBookInfoAwait(this, bookSource, book) + } + } + companion object { private val putPattern = Pattern.compile("@put:(\\{[^}]+?\\})", Pattern.CASE_INSENSITIVE) private val evalPattern = diff --git a/app/src/main/java/io/legado/app/model/localBook/TextFile.kt b/app/src/main/java/io/legado/app/model/localBook/TextFile.kt index 1db2093df..6c759e4c0 100644 --- a/app/src/main/java/io/legado/app/model/localBook/TextFile.kt +++ b/app/src/main/java/io/legado/app/model/localBook/TextFile.kt @@ -126,7 +126,7 @@ class TextFile(private val book: Book) { val chapterContent = blockContent.substring(seekPos, chapterStart) val chapterLength = chapterContent.toByteArray(charset).size val lastStart = toc.lastOrNull()?.start ?: curOffset - if (curOffset + chapterLength - lastStart > 50000) { + if (curOffset + chapterLength - lastStart > 102400) { bis.close() return analyze() } 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 f59e62e17..2550a007c 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 @@ -28,8 +28,8 @@ object BookChapterList { scope: CoroutineScope, bookSource: BookSource, book: Book, - redirectUrl: String, baseUrl: String, + redirectUrl: String, body: String? ): List { body ?: throw NoStackTraceException( @@ -39,7 +39,7 @@ object BookChapterList { Debug.log(bookSource.bookSourceUrl, "≡获取成功:${baseUrl}") Debug.log(bookSource.bookSourceUrl, body, state = 30) val tocRule = bookSource.getTocRule() - val nextUrlList = arrayListOf(baseUrl) + val nextUrlList = arrayListOf(redirectUrl) var reverse = false var listRule = tocRule.chapterList ?: "" if (listRule.startsWith("-")) { @@ -157,7 +157,7 @@ object BookChapterList { Debug.log(bookSource.bookSourceUrl, "┌获取目录下一页列表", log) analyzeRule.getStringList(nextTocRule, isUrl = true)?.let { for (item in it) { - if (item != baseUrl) { + if (item != redirectUrl) { nextUrlList.add(item) } } @@ -180,7 +180,7 @@ object BookChapterList { elements.forEachIndexed { index, item -> scope.ensureActive() analyzeRule.setContent(item) - val bookChapter = BookChapter(bookUrl = book.bookUrl, baseUrl = baseUrl) + val bookChapter = BookChapter(bookUrl = book.bookUrl, baseUrl = redirectUrl) analyzeRule.chapter = bookChapter bookChapter.title = analyzeRule.getString(nameRule) bookChapter.url = analyzeRule.getString(urlRule) 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 edcb4c9d2..64b727e2a 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 @@ -32,8 +32,8 @@ object BookContent { bookSource: BookSource, book: Book, bookChapter: BookChapter, - redirectUrl: String, baseUrl: String, + redirectUrl: String, body: String?, nextChapterUrl: String?, needSave: Boolean = true @@ -50,10 +50,10 @@ object BookContent { nextChapterUrl } val content = StringBuilder() - val nextUrlList = arrayListOf(baseUrl) + val nextUrlList = arrayListOf(redirectUrl) val contentRule = bookSource.getContentRule() val analyzeRule = AnalyzeRule(book, bookSource).setContent(body, baseUrl) - analyzeRule.setRedirectUrl(baseUrl) + analyzeRule.setRedirectUrl(redirectUrl) analyzeRule.nextChapterUrl = mNextChapterUrl scope.ensureActive() var contentData = analyzeContent( @@ -64,8 +64,8 @@ object BookContent { var nextUrl = contentData.second[0] while (nextUrl.isNotEmpty() && !nextUrlList.contains(nextUrl)) { if (!mNextChapterUrl.isNullOrEmpty() - && NetworkUtils.getAbsoluteURL(baseUrl, nextUrl) - == NetworkUtils.getAbsoluteURL(baseUrl, mNextChapterUrl) + && NetworkUtils.getAbsoluteURL(redirectUrl, nextUrl) + == NetworkUtils.getAbsoluteURL(redirectUrl, mNextChapterUrl) ) break nextUrlList.add(nextUrl) scope.ensureActive() diff --git a/app/src/main/java/io/legado/app/model/webBook/BookInfo.kt b/app/src/main/java/io/legado/app/model/webBook/BookInfo.kt index a7829d21a..153c8e3e7 100644 --- a/app/src/main/java/io/legado/app/model/webBook/BookInfo.kt +++ b/app/src/main/java/io/legado/app/model/webBook/BookInfo.kt @@ -25,8 +25,8 @@ object BookInfo { scope: CoroutineScope, bookSource: BookSource, book: Book, - redirectUrl: String, baseUrl: String, + redirectUrl: String, body: String?, canReName: Boolean, ) { @@ -126,7 +126,7 @@ object BookInfo { Debug.log(bookSource.bookSourceUrl, "┌获取封面链接") try { analyzeRule.getString(infoRule.coverUrl).let { - if (it.isNotEmpty()) book.coverUrl = NetworkUtils.getAbsoluteURL(baseUrl, it) + if (it.isNotEmpty()) book.coverUrl = NetworkUtils.getAbsoluteURL(redirectUrl, it) Debug.log(bookSource.bookSourceUrl, "└${it}") } } catch (e: Exception) { @@ -136,8 +136,8 @@ object BookInfo { scope.ensureActive() Debug.log(bookSource.bookSourceUrl, "┌获取目录链接") book.tocUrl = analyzeRule.getString(infoRule.tocUrl, isUrl = true) - if (book.tocUrl.isEmpty()) book.tocUrl = redirectUrl - if (book.tocUrl == redirectUrl) { + if (book.tocUrl.isEmpty()) book.tocUrl = baseUrl + if (book.tocUrl == baseUrl) { book.tocHtml = body } Debug.log(bookSource.bookSourceUrl, "└${book.tocUrl}") diff --git a/app/src/main/java/io/legado/app/model/webBook/WebBook.kt b/app/src/main/java/io/legado/app/model/webBook/WebBook.kt index 9cda244cf..f657b4e13 100644 --- a/app/src/main/java/io/legado/app/model/webBook/WebBook.kt +++ b/app/src/main/java/io/legado/app/model/webBook/WebBook.kt @@ -144,8 +144,8 @@ object WebBook { scope = scope, bookSource = bookSource, book = book, - redirectUrl = book.bookUrl, baseUrl = book.bookUrl, + redirectUrl = book.bookUrl, body = book.infoHtml, canReName = canReName ) @@ -168,8 +168,8 @@ object WebBook { scope = scope, bookSource = bookSource, book = book, - redirectUrl = book.bookUrl, - baseUrl = res.url, + baseUrl = book.bookUrl, + redirectUrl = res.url, body = res.body, canReName = canReName ) @@ -202,8 +202,8 @@ object WebBook { scope = scope, bookSource = bookSource, book = book, - redirectUrl = book.tocUrl, baseUrl = book.tocUrl, + redirectUrl = book.tocUrl, body = book.tocHtml ) } else { @@ -225,8 +225,8 @@ object WebBook { scope = scope, bookSource = bookSource, book = book, - redirectUrl = book.tocUrl, - baseUrl = res.url, + baseUrl = book.tocUrl, + redirectUrl = res.url, body = res.body ) } @@ -271,8 +271,8 @@ object WebBook { bookSource = bookSource, book = book, bookChapter = bookChapter, - redirectUrl = bookChapter.getAbsoluteURL(), baseUrl = bookChapter.getAbsoluteURL(), + redirectUrl = bookChapter.getAbsoluteURL(), body = book.tocHtml, nextChapterUrl = nextChapterUrl, needSave = needSave @@ -301,8 +301,8 @@ object WebBook { bookSource = bookSource, book = book, bookChapter = bookChapter, - redirectUrl = bookChapter.getAbsoluteURL(), - baseUrl = res.url, + baseUrl = bookChapter.getAbsoluteURL(), + redirectUrl = res.url, body = res.body, nextChapterUrl = nextChapterUrl, needSave = needSave diff --git a/app/src/main/java/io/legado/app/ui/association/FileAssociationActivity.kt b/app/src/main/java/io/legado/app/ui/association/FileAssociationActivity.kt index 8ee117e3f..133cce8a3 100644 --- a/app/src/main/java/io/legado/app/ui/association/FileAssociationActivity.kt +++ b/app/src/main/java/io/legado/app/ui/association/FileAssociationActivity.kt @@ -18,6 +18,7 @@ import io.legado.app.utils.viewbindingdelegate.viewBinding import kotlinx.coroutines.Dispatchers.IO import kotlinx.coroutines.launch import kotlinx.coroutines.withContext +import timber.log.Timber import java.io.File import java.io.FileOutputStream @@ -108,7 +109,8 @@ class FileAssociationActivity : var doc = treeDoc!!.findFile(name) if (doc == null || bookDoc.lastModified() > doc.lastModified()) { if (doc == null) { - doc = treeDoc.createFile(FileUtils.getMimeType(name), name)!! + doc = treeDoc.createFile(FileUtils.getMimeType(name), name) + ?: throw SecurityException("Permission Denial") } contentResolver.openOutputStream(doc.uri)!!.use { oStream -> contentResolver.openInputStream(bookDoc.uri)!!.use { iStream -> @@ -137,7 +139,17 @@ class FileAssociationActivity : } } }.onFailure { - toastOnUi(it.localizedMessage) + when (it) { + is SecurityException -> localBookTreeSelect.launch { + title = "选择保存书籍的文件夹" + mode = HandleFileContract.DIR_SYS + } + else -> { + Timber.e(it, "导入书籍失败") + toastOnUi(it.localizedMessage) + finish() + } + } } } } 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 7874afcec..24274377a 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 @@ -91,7 +91,9 @@ class ReadRssActivity : VMBaseActivity override fun onCompatOptionsItemSelected(item: MenuItem): Boolean { when (item.itemId) { - R.id.menu_rss_refresh -> viewModel.refresh() + R.id.menu_rss_refresh -> viewModel.refresh { + binding.webView.reload() + } R.id.menu_rss_star -> viewModel.favorite() R.id.menu_share_it -> viewModel.rssArticle?.let { share(it.link) diff --git a/app/src/main/java/io/legado/app/ui/rss/read/ReadRssViewModel.kt b/app/src/main/java/io/legado/app/ui/rss/read/ReadRssViewModel.kt index 1cfbe34fc..41be659fa 100644 --- a/app/src/main/java/io/legado/app/ui/rss/read/ReadRssViewModel.kt +++ b/app/src/main/java/io/legado/app/ui/rss/read/ReadRssViewModel.kt @@ -104,17 +104,17 @@ class ReadRssViewModel(application: Application) : BaseViewModel(application), } } - fun refresh() { + fun refresh(finish: () -> Unit) { rssArticle?.let { rssArticle -> rssSource?.let { val ruleContent = it.ruleContent if (!ruleContent.isNullOrBlank()) { loadContent(rssArticle, ruleContent) } else { - loadUrl(rssArticle.link, rssArticle.origin) + finish.invoke() } - } ?: loadUrl(rssArticle.link, rssArticle.origin) - } + } ?: finish.invoke() + } ?: finish.invoke() } fun favorite() { diff --git a/app/src/main/java/io/legado/app/ui/widget/code/CodeView.kt b/app/src/main/java/io/legado/app/ui/widget/code/CodeView.kt index 82bd4a53d..4f2207d4b 100644 --- a/app/src/main/java/io/legado/app/ui/widget/code/CodeView.kt +++ b/app/src/main/java/io/legado/app/ui/widget/code/CodeView.kt @@ -169,7 +169,7 @@ class CodeView : AppCompatMultiAutoCompleteTextView { } private fun highlight(editable: Editable): Editable { - if (editable.isEmpty()) return editable + if (editable.isEmpty() || editable.length > 1024) return editable try { clearSpans(editable) highlightErrorLines(editable) diff --git a/app/src/main/java/io/legado/app/utils/ToastUtils.kt b/app/src/main/java/io/legado/app/utils/ToastUtils.kt index 7a5f2cc83..a2373f819 100644 --- a/app/src/main/java/io/legado/app/utils/ToastUtils.kt +++ b/app/src/main/java/io/legado/app/utils/ToastUtils.kt @@ -10,49 +10,57 @@ private var toast: Toast? = null fun Context.toastOnUi(message: Int) { runOnUI { - if (toast == null) { - toast = Toast.makeText(this, message, Toast.LENGTH_SHORT) - } else { - toast?.setText(message) - toast?.duration = Toast.LENGTH_SHORT + kotlin.runCatching { + if (toast == null) { + toast = Toast.makeText(this, message, Toast.LENGTH_SHORT) + } else { + toast?.setText(message) + toast?.duration = Toast.LENGTH_SHORT + } + toast?.show() } - toast?.show() } } fun Context.toastOnUi(message: CharSequence?) { runOnUI { - if (toast == null) { - toast = Toast.makeText(this, message, Toast.LENGTH_SHORT) - } else { - toast?.setText(message) - toast?.duration = Toast.LENGTH_SHORT + kotlin.runCatching { + if (toast == null) { + toast = Toast.makeText(this, message, Toast.LENGTH_SHORT) + } else { + toast?.setText(message) + toast?.duration = Toast.LENGTH_SHORT + } + toast?.show() } - toast?.show() } } fun Context.longToastOnUi(message: Int) { runOnUI { - if (toast == null) { - toast = Toast.makeText(this, message, Toast.LENGTH_LONG) - } else { - toast?.setText(message) - toast?.duration = Toast.LENGTH_LONG + kotlin.runCatching { + if (toast == null) { + toast = Toast.makeText(this, message, Toast.LENGTH_LONG) + } else { + toast?.setText(message) + toast?.duration = Toast.LENGTH_LONG + } + toast?.show() } - toast?.show() } } fun Context.longToastOnUi(message: CharSequence?) { runOnUI { - if (toast == null) { - toast = Toast.makeText(this, message, Toast.LENGTH_LONG) - } else { - toast?.setText(message) - toast?.duration = Toast.LENGTH_LONG + kotlin.runCatching { + if (toast == null) { + toast = Toast.makeText(this, message, Toast.LENGTH_LONG) + } else { + toast?.setText(message) + toast?.duration = Toast.LENGTH_LONG + } + toast?.show() } - toast?.show() } }