mirror of
https://github.com/gedoor/legado.git
synced 2025-08-10 00:52:30 +00:00
优化
This commit is contained in:
@@ -12,13 +12,24 @@ import android.os.Build
|
||||
import com.github.liuyueyi.quick.transfer.constants.TransType
|
||||
import com.jeremyliao.liveeventbus.LiveEventBus
|
||||
import com.jeremyliao.liveeventbus.logger.DefaultLogger
|
||||
import com.script.rhino.ReadOnlyJavaObject
|
||||
import com.script.rhino.RhinoScriptEngine
|
||||
import com.script.rhino.RhinoWrapFactory
|
||||
import io.legado.app.base.AppContextWrapper
|
||||
import io.legado.app.constant.AppConst.channelIdDownload
|
||||
import io.legado.app.constant.AppConst.channelIdReadAloud
|
||||
import io.legado.app.constant.AppConst.channelIdWeb
|
||||
import io.legado.app.constant.PreferKey
|
||||
import io.legado.app.data.appDb
|
||||
import io.legado.app.data.entities.Book
|
||||
import io.legado.app.data.entities.BookChapter
|
||||
import io.legado.app.data.entities.BookSource
|
||||
import io.legado.app.data.entities.HttpTTS
|
||||
import io.legado.app.data.entities.RssSource
|
||||
import io.legado.app.data.entities.rule.BookInfoRule
|
||||
import io.legado.app.data.entities.rule.ContentRule
|
||||
import io.legado.app.data.entities.rule.ExploreRule
|
||||
import io.legado.app.data.entities.rule.SearchRule
|
||||
import io.legado.app.help.AppFreezeMonitor
|
||||
import io.legado.app.help.AppWebDav
|
||||
import io.legado.app.help.CrashHandler
|
||||
@@ -33,6 +44,7 @@ import io.legado.app.help.coroutine.Coroutine
|
||||
import io.legado.app.help.http.Cronet
|
||||
import io.legado.app.help.http.ObsoleteUrlFactory
|
||||
import io.legado.app.help.http.okHttpClient
|
||||
import io.legado.app.help.rhino.NativeBaseSource
|
||||
import io.legado.app.help.source.SourceHelp
|
||||
import io.legado.app.help.storage.Backup
|
||||
import io.legado.app.model.BookCover
|
||||
@@ -79,7 +91,7 @@ class App : Application() {
|
||||
AppFreezeMonitor.init(this@App)
|
||||
URL.setURLStreamHandlerFactory(ObsoleteUrlFactory(okHttpClient))
|
||||
launch { installGmsTlsProvider(appCtx) }
|
||||
RhinoScriptEngine
|
||||
initRhino()
|
||||
//初始化封面
|
||||
BookCover.toString()
|
||||
//清除过期数据
|
||||
@@ -200,6 +212,19 @@ class App : Application() {
|
||||
)
|
||||
}
|
||||
|
||||
private fun initRhino() {
|
||||
RhinoScriptEngine
|
||||
RhinoWrapFactory.register(BookSource::class.java, NativeBaseSource.factory)
|
||||
RhinoWrapFactory.register(RssSource::class.java, NativeBaseSource.factory)
|
||||
RhinoWrapFactory.register(HttpTTS::class.java, NativeBaseSource.factory)
|
||||
RhinoWrapFactory.register(ExploreRule::class.java, ReadOnlyJavaObject.factory)
|
||||
RhinoWrapFactory.register(SearchRule::class.java, ReadOnlyJavaObject.factory)
|
||||
RhinoWrapFactory.register(BookInfoRule::class.java, ReadOnlyJavaObject.factory)
|
||||
RhinoWrapFactory.register(ContentRule::class.java, ReadOnlyJavaObject.factory)
|
||||
RhinoWrapFactory.register(BookChapter::class.java, ReadOnlyJavaObject.factory)
|
||||
RhinoWrapFactory.register(Book.ReadConfig::class.java, ReadOnlyJavaObject.factory)
|
||||
}
|
||||
|
||||
class EventLogger : DefaultLogger() {
|
||||
|
||||
override fun log(level: Level, msg: String) {
|
||||
|
||||
@@ -12,7 +12,6 @@ import io.legado.app.help.JsExtensions
|
||||
import io.legado.app.help.config.AppConfig
|
||||
import io.legado.app.help.crypto.SymmetricCryptoAndroid
|
||||
import io.legado.app.help.http.CookieStore
|
||||
import io.legado.app.help.source.copy
|
||||
import io.legado.app.help.source.getShareScope
|
||||
import io.legado.app.model.Debug
|
||||
import io.legado.app.utils.GSON
|
||||
@@ -63,7 +62,7 @@ interface BaseSource : JsExtensions {
|
||||
fun getKey(): String
|
||||
|
||||
override fun getSource(): BaseSource? {
|
||||
return copy()
|
||||
return this
|
||||
}
|
||||
|
||||
fun loginUi(): List<RowUi>? {
|
||||
@@ -240,10 +239,9 @@ interface BaseSource : JsExtensions {
|
||||
*/
|
||||
@Throws(Exception::class)
|
||||
fun evalJS(jsStr: String, bindingsConfig: ScriptBindings.() -> Unit = {}): Any? {
|
||||
val sourceCopy = copy()
|
||||
val bindings = buildScriptBindings { bindings ->
|
||||
bindings["java"] = sourceCopy
|
||||
bindings["source"] = sourceCopy
|
||||
bindings["java"] = this
|
||||
bindings["source"] = this
|
||||
bindings["baseUrl"] = getKey()
|
||||
bindings["cookie"] = CookieStore
|
||||
bindings["cache"] = CacheManager
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
package io.legado.app.help.crypto
|
||||
|
||||
import androidx.annotation.Keep
|
||||
import cn.hutool.core.codec.Base64
|
||||
import cn.hutool.core.util.HexUtil
|
||||
import cn.hutool.crypto.symmetric.SymmetricCrypto
|
||||
import io.legado.app.utils.EncoderUtils
|
||||
import io.legado.app.utils.isHex
|
||||
import java.io.InputStream
|
||||
import java.nio.charset.Charset
|
||||
|
||||
@@ -32,4 +35,13 @@ class SymmetricCryptoAndroid(
|
||||
return EncoderUtils.base64Encode(encrypt(data))
|
||||
}
|
||||
|
||||
override fun decrypt(data: String): ByteArray {
|
||||
val bytes = if (data.isHex()) {
|
||||
HexUtil.decodeHex(data)
|
||||
} else {
|
||||
Base64.decode(data)
|
||||
}
|
||||
return decrypt(bytes)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
package io.legado.app.help.rhino
|
||||
|
||||
import com.script.rhino.JavaObjectWrapFactory
|
||||
import org.mozilla.javascript.NativeJavaObject
|
||||
import org.mozilla.javascript.Scriptable
|
||||
|
||||
class NativeBaseSource(scope: Scriptable?, javaObject: Any, staticType: Class<*>?) :
|
||||
NativeJavaObject(scope, javaObject, staticType) {
|
||||
|
||||
override fun has(name: String, start: Scriptable): Boolean {
|
||||
if (name != "setVariable" && name.length > 3 && name.startsWith("set")) {
|
||||
val name = name.substring(3).replaceFirstChar { it.lowercase() }
|
||||
if (super.has(name, start)) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return super.has(name, start)
|
||||
}
|
||||
|
||||
override fun get(name: String, start: Scriptable): Any? {
|
||||
if (name != "setVariable" && name.length > 3 && name.startsWith("set")) {
|
||||
val name = name.substring(3).replaceFirstChar { it.lowercase() }
|
||||
if (super.has(name, start)) {
|
||||
return NOT_FOUND
|
||||
}
|
||||
}
|
||||
return super.get(name, start)
|
||||
}
|
||||
|
||||
override fun put(
|
||||
name: String,
|
||||
start: Scriptable,
|
||||
value: Any?
|
||||
) {
|
||||
if (name == "variable") {
|
||||
super.put(name, start, value)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
val factory = JavaObjectWrapFactory { scope, javaObject, staticType ->
|
||||
NativeBaseSource(scope, javaObject, staticType)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -3,7 +3,6 @@ package io.legado.app.help.source
|
||||
import io.legado.app.constant.SourceType
|
||||
import io.legado.app.data.entities.BaseSource
|
||||
import io.legado.app.data.entities.BookSource
|
||||
import io.legado.app.data.entities.HttpTTS
|
||||
import io.legado.app.data.entities.RssSource
|
||||
import io.legado.app.model.SharedJsScope
|
||||
import org.mozilla.javascript.Scriptable
|
||||
@@ -20,18 +19,3 @@ fun BaseSource.getSourceType(): Int {
|
||||
else -> error("unknown source type: ${this::class.simpleName}.")
|
||||
}
|
||||
}
|
||||
|
||||
fun BaseSource.copy(): BaseSource {
|
||||
return when (this) {
|
||||
is BookSource -> copy(
|
||||
ruleExplore = ruleExplore?.copy(),
|
||||
ruleSearch = ruleSearch?.copy(),
|
||||
ruleBookInfo = ruleBookInfo?.copy(),
|
||||
ruleToc = ruleToc?.copy()
|
||||
)
|
||||
|
||||
is RssSource -> copy()
|
||||
is HttpTTS -> copy()
|
||||
else -> error("unknown copy source type: ${this::class.simpleName}.")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,6 @@ import io.legado.app.exception.NoStackTraceException
|
||||
import io.legado.app.help.CacheManager
|
||||
import io.legado.app.help.JsExtensions
|
||||
import io.legado.app.help.http.CookieStore
|
||||
import io.legado.app.help.source.copy
|
||||
import io.legado.app.help.source.getShareScope
|
||||
import io.legado.app.model.Debug
|
||||
import io.legado.app.model.webBook.WebBook
|
||||
@@ -30,7 +29,6 @@ import io.legado.app.utils.isJson
|
||||
import io.legado.app.utils.printOnDebug
|
||||
import io.legado.app.utils.splitNotBlank
|
||||
import io.legado.app.utils.stackTraceStr
|
||||
import io.legado.app.utils.updateVariableTo
|
||||
import kotlinx.coroutines.ensureActive
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.coroutines.withTimeout
|
||||
@@ -777,15 +775,15 @@ class AnalyzeRule(
|
||||
bindings["java"] = this
|
||||
bindings["cookie"] = CookieStore
|
||||
bindings["cache"] = CacheManager
|
||||
bindings["source"] = source?.copy()
|
||||
bindings["source"] = source
|
||||
bindings["book"] = book
|
||||
bindings["result"] = result
|
||||
bindings["baseUrl"] = baseUrl
|
||||
bindings["chapter"] = chapterCopy
|
||||
bindings["title"] = chapterCopy?.title
|
||||
bindings["chapter"] = chapter
|
||||
bindings["title"] = chapter?.title
|
||||
bindings["src"] = content
|
||||
bindings["nextChapterUrl"] = nextChapterUrl
|
||||
bindings["rssArticle"] = rssArticleCopy
|
||||
bindings["rssArticle"] = rssArticle
|
||||
}
|
||||
val topScope = source?.getShareScope(coroutineContext) ?: topScopeRef?.get()
|
||||
val scope = if (topScope == null) {
|
||||
@@ -801,19 +799,9 @@ class AnalyzeRule(
|
||||
}
|
||||
val script = compileScriptCache(jsStr)
|
||||
val result = script.eval(scope, coroutineContext)
|
||||
updateVariable(chapterCopy, rssArticleCopy)
|
||||
return result
|
||||
}
|
||||
|
||||
private fun updateVariable(chapterCopy: BookChapter?, rssArticleCopy: RssArticle?) {
|
||||
chapter?.let {
|
||||
chapterCopy?.updateVariableTo(it)
|
||||
}
|
||||
rssArticle?.let {
|
||||
rssArticleCopy?.updateVariableTo(it)
|
||||
}
|
||||
}
|
||||
|
||||
private fun compileScriptCache(jsStr: String): CompiledScript {
|
||||
return scriptCache.getOrPutLimit(jsStr, 16) {
|
||||
RhinoScriptEngine.compile(jsStr)
|
||||
@@ -821,7 +809,7 @@ class AnalyzeRule(
|
||||
}
|
||||
|
||||
override fun getSource(): BaseSource? {
|
||||
return source?.copy()
|
||||
return source
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -38,7 +38,6 @@ import io.legado.app.help.http.newCallStrResponse
|
||||
import io.legado.app.help.http.postForm
|
||||
import io.legado.app.help.http.postJson
|
||||
import io.legado.app.help.http.postMultipart
|
||||
import io.legado.app.help.source.copy
|
||||
import io.legado.app.help.source.getShareScope
|
||||
import io.legado.app.model.Debug
|
||||
import io.legado.app.utils.EncoderUtils
|
||||
@@ -357,7 +356,7 @@ class AnalyzeUrl(
|
||||
bindings["speakText"] = speakText
|
||||
bindings["speakSpeed"] = speakSpeed
|
||||
bindings["book"] = ruleData as? Book
|
||||
bindings["source"] = source?.copy()
|
||||
bindings["source"] = source
|
||||
bindings["result"] = result
|
||||
}
|
||||
val sharedScope = source?.getShareScope(coroutineContext)
|
||||
@@ -660,7 +659,7 @@ class AnalyzeUrl(
|
||||
}
|
||||
|
||||
override fun getSource(): BaseSource? {
|
||||
return source?.copy()
|
||||
return source
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
@@ -10,7 +10,7 @@ import io.legado.app.utils.showDialogFragment
|
||||
class RssJsExtensions(private val activity: ReadRssActivity) : JsExtensions {
|
||||
|
||||
override fun getSource(): BaseSource? {
|
||||
return activity.getSource()?.copy()
|
||||
return activity.getSource()
|
||||
}
|
||||
|
||||
fun searchBook(key: String) {
|
||||
|
||||
@@ -6,10 +6,3 @@ fun BookChapter.internString() {
|
||||
title = title.intern()
|
||||
bookUrl = bookUrl.intern()
|
||||
}
|
||||
|
||||
fun BookChapter.updateVariableTo(chapter: BookChapter) {
|
||||
if (variable != chapter.variable) {
|
||||
chapter.variableMap.clear()
|
||||
chapter.variableMap.putAll(variableMap)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
package io.legado.app.utils
|
||||
|
||||
import io.legado.app.data.entities.RssArticle
|
||||
|
||||
fun RssArticle.updateVariableTo(rssArticle: RssArticle) {
|
||||
if (variable != rssArticle.variable) {
|
||||
rssArticle.variableMap.clear()
|
||||
rssArticle.variableMap.putAll(variableMap)
|
||||
}
|
||||
}
|
||||
@@ -78,6 +78,12 @@ fun String?.isTrue(nullIsTrue: Boolean = false): Boolean {
|
||||
return !this.trim().matches("(?i)^(false|no|not|0)$".toRegex())
|
||||
}
|
||||
|
||||
fun String.isHex(): Boolean {
|
||||
return all {c ->
|
||||
c in '0'..'9' || c in 'A'..'F' || c in 'a'..'f'
|
||||
}
|
||||
}
|
||||
|
||||
fun String.splitNotBlank(vararg delimiter: String, limit: Int = 0): Array<String> = run {
|
||||
this.split(*delimiter, limit = limit).map { it.trim() }.filterNot { it.isBlank() }
|
||||
.toTypedArray()
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.script.rhino
|
||||
|
||||
import org.mozilla.javascript.Scriptable
|
||||
|
||||
fun interface JavaObjectWrapFactory {
|
||||
|
||||
fun wrap(scope: Scriptable?, javaObject: Any, staticType: Class<*>?): Scriptable
|
||||
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package com.script.rhino
|
||||
|
||||
import org.mozilla.javascript.NativeJavaObject
|
||||
import org.mozilla.javascript.Scriptable
|
||||
|
||||
class ReadOnlyJavaObject(scope: Scriptable?, javaObject: Any, staticType: Class<*>?) :
|
||||
NativeJavaObject(scope, javaObject, staticType) {
|
||||
|
||||
override fun has(name: String, start: Scriptable): Boolean {
|
||||
if (name.length > 3 && name.startsWith("set")) {
|
||||
val name = name.substring(3).replaceFirstChar { it.lowercase() }
|
||||
if (super.has(name, start)) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return super.has(name, start)
|
||||
}
|
||||
|
||||
override fun get(name: String, start: Scriptable): Any? {
|
||||
if (name.length > 3 && name.startsWith("set")) {
|
||||
val name = name.substring(3).replaceFirstChar { it.lowercase() }
|
||||
if (super.has(name, start)) {
|
||||
return NOT_FOUND
|
||||
}
|
||||
}
|
||||
return super.get(name, start)
|
||||
}
|
||||
|
||||
override fun put(
|
||||
name: String?,
|
||||
start: Scriptable?,
|
||||
value: Any?
|
||||
) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
companion object {
|
||||
val factory = JavaObjectWrapFactory { scope, javaObject, staticType ->
|
||||
ReadOnlyJavaObject(scope, javaObject, staticType)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -45,6 +45,8 @@ import org.mozilla.javascript.WrapFactory
|
||||
*/
|
||||
object RhinoWrapFactory : WrapFactory() {
|
||||
|
||||
private val factories = hashMapOf<Class<*>, JavaObjectWrapFactory>()
|
||||
|
||||
override fun wrapAsJavaObject(
|
||||
cx: Context,
|
||||
scope: Scriptable?,
|
||||
@@ -54,7 +56,8 @@ object RhinoWrapFactory : WrapFactory() {
|
||||
if (!RhinoClassShutter.visibleToScripts(javaObject)) {
|
||||
return null
|
||||
}
|
||||
return super.wrapAsJavaObject(cx, scope, javaObject, staticType)
|
||||
return wrapOrNull(scope, javaObject, staticType)
|
||||
?: super.wrapAsJavaObject(cx, scope, javaObject, staticType)
|
||||
}
|
||||
|
||||
override fun wrapJavaClass(
|
||||
@@ -71,4 +74,18 @@ object RhinoWrapFactory : WrapFactory() {
|
||||
return RhinoClassShutter.wrapJavaClass(scope, javaClass)
|
||||
}
|
||||
|
||||
private fun wrapOrNull(
|
||||
scope: Scriptable?,
|
||||
javaObject: Any,
|
||||
staticType: Class<*>?
|
||||
): Scriptable? {
|
||||
return factories[javaObject.javaClass]?.wrap(scope, javaObject, staticType)
|
||||
}
|
||||
|
||||
fun register(clazz: Class<*>, factory: JavaObjectWrapFactory) {
|
||||
if (!factories.contains(clazz)) {
|
||||
factories.put(clazz, factory)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user