Просмотр исходного кода

Merge branch 'master' into master

kunfei 4 лет назад
Родитель
Сommit
53b9402d4b

+ 10 - 2
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)

+ 10 - 0
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**
 
 * 阅读背景添加透明度调节

+ 7 - 5
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
     }
 

+ 35 - 3
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 =

+ 1 - 1
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()
                     }

+ 4 - 4
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<BookChapter> {
         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)

+ 5 - 5
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()

+ 4 - 4
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}")

+ 9 - 9
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

+ 14 - 2
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()
+                    }
+                }
             }
         }
     }

+ 3 - 1
app/src/main/java/io/legado/app/ui/rss/read/ReadRssActivity.kt

@@ -91,7 +91,9 @@ class ReadRssActivity : VMBaseActivity<ActivityRssReadBinding, ReadRssViewModel>
 
     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)

+ 4 - 4
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() {

+ 1 - 1
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)

+ 32 - 24
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()
     }
 }