[新增] 正则分段标题以及更多Sigma分支功能(@Luoyacheng)
This commit is contained in:
@@ -76,7 +76,6 @@ import java.util.zip.ZipInputStream
|
|||||||
import kotlin.coroutines.CoroutineContext
|
import kotlin.coroutines.CoroutineContext
|
||||||
import kotlin.coroutines.EmptyCoroutineContext
|
import kotlin.coroutines.EmptyCoroutineContext
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* js扩展类, 在js中通过java变量调用
|
* js扩展类, 在js中通过java变量调用
|
||||||
* 添加方法,请更新文档/legado/app/src/main/assets/help/JsHelp.md
|
* 添加方法,请更新文档/legado/app/src/main/assets/help/JsHelp.md
|
||||||
@@ -128,7 +127,6 @@ interface JsExtensions : JsEncodeUtils {
|
|||||||
fun ajaxAll(urlList: Array<String>): Array<StrResponse> {
|
fun ajaxAll(urlList: Array<String>): Array<StrResponse> {
|
||||||
return ajaxAll(urlList, false)
|
return ajaxAll(urlList, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun ajaxAll(urlList: Array<String>, skipRateLimit: Boolean): Array<StrResponse> {
|
fun ajaxAll(urlList: Array<String>, skipRateLimit: Boolean): Array<StrResponse> {
|
||||||
return runBlocking(context) {
|
return runBlocking(context) {
|
||||||
urlList.asFlow().mapAsync(AppConfig.threadCount) { url ->
|
urlList.asFlow().mapAsync(AppConfig.threadCount) { url ->
|
||||||
@@ -148,7 +146,6 @@ interface JsExtensions : JsEncodeUtils {
|
|||||||
fun ajaxTestAll(urlList: Array<String>, timeout: Int): Array<StrResponse> {
|
fun ajaxTestAll(urlList: Array<String>, timeout: Int): Array<StrResponse> {
|
||||||
return ajaxTestAll(urlList, timeout, false)
|
return ajaxTestAll(urlList, timeout, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun ajaxTestAll(
|
fun ajaxTestAll(
|
||||||
urlList: Array<String>,
|
urlList: Array<String>,
|
||||||
timeout: Int,
|
timeout: Int,
|
||||||
@@ -238,7 +235,17 @@ interface JsExtensions : JsEncodeUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun webViewGetSource(html: String?, url: String?, js: String?, sourceRegex: String): String? {
|
fun webViewGetSource(html: String?, url: String?, js: String?, sourceRegex: String): String? {
|
||||||
return webViewGetSource(html, url, js, sourceRegex, false)
|
return webViewGetSource(html, url, js, sourceRegex, false, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun webViewGetSource(
|
||||||
|
html: String?,
|
||||||
|
url: String?,
|
||||||
|
js: String?,
|
||||||
|
sourceRegex: String,
|
||||||
|
cacheFirst: Boolean
|
||||||
|
): String? {
|
||||||
|
return webViewGetSource(html, url, js, sourceRegex, cacheFirst, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -249,7 +256,8 @@ interface JsExtensions : JsEncodeUtils {
|
|||||||
url: String?,
|
url: String?,
|
||||||
js: String?,
|
js: String?,
|
||||||
sourceRegex: String,
|
sourceRegex: String,
|
||||||
cacheFirst: Boolean
|
cacheFirst: Boolean,
|
||||||
|
delayTime: Long
|
||||||
): String? {
|
): String? {
|
||||||
if (isMainThread) {
|
if (isMainThread) {
|
||||||
error("webViewGetSource must be called on a background thread")
|
error("webViewGetSource must be called on a background thread")
|
||||||
@@ -261,7 +269,8 @@ interface JsExtensions : JsEncodeUtils {
|
|||||||
javaScript = js,
|
javaScript = js,
|
||||||
headerMap = getSource()?.getHeaderMap(true),
|
headerMap = getSource()?.getHeaderMap(true),
|
||||||
tag = getSource()?.getKey(),
|
tag = getSource()?.getKey(),
|
||||||
sourceRegex = sourceRegex
|
sourceRegex = sourceRegex,
|
||||||
|
delayTime = delayTime
|
||||||
).getStrResponse().body
|
).getStrResponse().body
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -272,7 +281,17 @@ interface JsExtensions : JsEncodeUtils {
|
|||||||
js: String?,
|
js: String?,
|
||||||
overrideUrlRegex: String
|
overrideUrlRegex: String
|
||||||
): String? {
|
): String? {
|
||||||
return webViewGetOverrideUrl(html, url, js, overrideUrlRegex, false)
|
return webViewGetOverrideUrl(html, url, js, overrideUrlRegex, false, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun webViewGetOverrideUrl(
|
||||||
|
html: String?,
|
||||||
|
url: String?,
|
||||||
|
js: String?,
|
||||||
|
overrideUrlRegex: String,
|
||||||
|
cacheFirst: Boolean
|
||||||
|
): String? {
|
||||||
|
return webViewGetOverrideUrl(html, url, js, overrideUrlRegex, cacheFirst, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -283,7 +302,8 @@ interface JsExtensions : JsEncodeUtils {
|
|||||||
url: String?,
|
url: String?,
|
||||||
js: String?,
|
js: String?,
|
||||||
overrideUrlRegex: String,
|
overrideUrlRegex: String,
|
||||||
cacheFirst: Boolean
|
cacheFirst: Boolean,
|
||||||
|
delayTime: Long
|
||||||
): String? {
|
): String? {
|
||||||
if (isMainThread) {
|
if (isMainThread) {
|
||||||
error("webViewGetOverrideUrl must be called on a background thread")
|
error("webViewGetOverrideUrl must be called on a background thread")
|
||||||
@@ -295,11 +315,23 @@ interface JsExtensions : JsEncodeUtils {
|
|||||||
javaScript = js,
|
javaScript = js,
|
||||||
headerMap = getSource()?.getHeaderMap(true),
|
headerMap = getSource()?.getHeaderMap(true),
|
||||||
tag = getSource()?.getKey(),
|
tag = getSource()?.getKey(),
|
||||||
overrideUrlRegex = overrideUrlRegex
|
overrideUrlRegex = overrideUrlRegex,
|
||||||
|
delayTime = delayTime
|
||||||
).getStrResponse().body
|
).getStrResponse().body
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 打开内置视频播放器
|
||||||
|
* @param url 视频播放链接
|
||||||
|
* @param title 视频的标题
|
||||||
|
* @param float 是否悬浮窗打开
|
||||||
|
*/
|
||||||
|
@JavascriptInterface
|
||||||
|
fun openVideoPlayer(url: String, title: String, float: Boolean) {
|
||||||
|
//SourceHelp.openVideoPlayer(getSource(), url, title, float)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 使用内置浏览器打开链接,手动验证网站防爬
|
* 使用内置浏览器打开链接,手动验证网站防爬
|
||||||
* @param url 要打开的链接
|
* @param url 要打开的链接
|
||||||
@@ -1164,9 +1196,9 @@ interface JsExtensions : JsEncodeUtils {
|
|||||||
return GSON.toJson(ReadBookConfig.durConfig)
|
return GSON.toJson(ReadBookConfig.durConfig)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* fun getReadBookConfigMap(): Map<String, Any> {
|
//fun getReadBookConfigMap(): Map<String, Any> {
|
||||||
return ReadBookConfig.durConfig.toMap()
|
// return ReadBookConfig.durConfig.toMap()
|
||||||
} */
|
//}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取主题模式
|
* 获取主题模式
|
||||||
@@ -1179,14 +1211,14 @@ interface JsExtensions : JsEncodeUtils {
|
|||||||
/**
|
/**
|
||||||
* 获取主题配置
|
* 获取主题配置
|
||||||
*/
|
*/
|
||||||
//@JavascriptInterface
|
// @JavascriptInterface
|
||||||
/*fun getThemeConfig(): String {
|
//fun getThemeConfig(): String {
|
||||||
val themeConfig = ThemeConfig.getDurConfig(appCtx)
|
// val themeConfig = ThemeConfig.getDurConfig(appCtx)
|
||||||
return GSON.toJson(themeConfig)
|
// return GSON.toJson(themeConfig)
|
||||||
}*/
|
//}
|
||||||
|
|
||||||
/* fun getThemeConfigMap(): Map<String, Any?> {
|
//fun getThemeConfigMap(): Map<String, Any?> {
|
||||||
return ThemeConfig.getDurConfig(appCtx).toMap()
|
// return ThemeConfig.getDurConfig(appCtx).toMap()
|
||||||
} */
|
//}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -19,15 +19,27 @@ import io.legado.app.ui.rss.read.RssJsExtensions
|
|||||||
import io.legado.app.utils.GSON
|
import io.legado.app.utils.GSON
|
||||||
import io.legado.app.utils.escapeForJs
|
import io.legado.app.utils.escapeForJs
|
||||||
import io.legado.app.utils.fromJsonObject
|
import io.legado.app.utils.fromJsonObject
|
||||||
|
import java.lang.ref.WeakReference
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
|
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
class WebJsExtensions(
|
class WebJsExtensions(
|
||||||
source: BaseSource,
|
source: BaseSource, activity: AppCompatActivity,
|
||||||
activity: AppCompatActivity,
|
|
||||||
private val webView: WebView,
|
private val webView: WebView,
|
||||||
private val bookType: Int = 0
|
private val bookType: Int = 0,
|
||||||
|
callback: Callback? = null
|
||||||
) : RssJsExtensions(activity, source) {
|
) : RssJsExtensions(activity, source) {
|
||||||
|
private val callbackRef: WeakReference<Callback> = WeakReference(callback)
|
||||||
|
|
||||||
|
interface Callback {
|
||||||
|
fun upConfig(config: String)
|
||||||
|
}
|
||||||
|
|
||||||
|
@JavascriptInterface
|
||||||
|
fun upConfig(config: String) {
|
||||||
|
callbackRef.get()?.upConfig(config)
|
||||||
|
}
|
||||||
|
|
||||||
private val bookAndChapter by lazy {
|
private val bookAndChapter by lazy {
|
||||||
var book: Book? = null
|
var book: Book? = null
|
||||||
var chapter: BookChapter? = null
|
var chapter: BookChapter? = null
|
||||||
@@ -64,35 +76,37 @@ class WebJsExtensions(
|
|||||||
setChapter(chapter)
|
setChapter(chapter)
|
||||||
evalJS(jsParam[0]).toString()
|
evalJS(jsParam[0]).toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
"ajaxAwait" -> {
|
"ajaxAwait" -> {
|
||||||
ajax(jsParam[0], jsParam[1].toIntOrNull()).toString()
|
ajax(jsParam[0], jsParam[1].toIntOrNull()).toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
"connectAwait" -> {
|
"connectAwait" -> {
|
||||||
connect(jsParam[0], jsParam[1], jsParam[2].toIntOrNull())
|
connect(jsParam[0], jsParam[1], jsParam[2].toIntOrNull())
|
||||||
}
|
}
|
||||||
|
|
||||||
"getAwait" -> {
|
"getAwait" -> {
|
||||||
get(jsParam[0], jsParam[1], jsParam[2].toIntOrNull())
|
get(jsParam[0], jsParam[1], jsParam[2].toIntOrNull())
|
||||||
}
|
}
|
||||||
|
|
||||||
"headAwait" -> {
|
"headAwait" -> {
|
||||||
head(jsParam[0], jsParam[1], jsParam[2].toIntOrNull())
|
head(jsParam[0], jsParam[1], jsParam[2].toIntOrNull())
|
||||||
}
|
}
|
||||||
|
|
||||||
"postAwait" -> {
|
"postAwait" -> {
|
||||||
post(jsParam[0], jsParam[1], jsParam[2], jsParam[3].toIntOrNull())
|
post(jsParam[0], jsParam[1], jsParam[2], jsParam[3].toIntOrNull())
|
||||||
}
|
}
|
||||||
|
|
||||||
"webViewAwait" -> {
|
"webViewAwait" -> {
|
||||||
webView(jsParam[0], jsParam[1], jsParam[2], jsParam[3].toBoolean()).toString()
|
webView(jsParam[0], jsParam[1], jsParam[2], jsParam[3].toBoolean()).toString()
|
||||||
}
|
}
|
||||||
|
"webViewGetSourceAwait" -> {
|
||||||
|
webViewGetSource(
|
||||||
|
jsParam[0],
|
||||||
|
jsParam[1],
|
||||||
|
jsParam[2],
|
||||||
|
jsParam[3],
|
||||||
|
jsParam[4].toBoolean(),
|
||||||
|
jsParam[5].toLongOrNull() ?: 0
|
||||||
|
).toString()
|
||||||
|
}
|
||||||
"decryptStrAwait" -> {
|
"decryptStrAwait" -> {
|
||||||
createSymmetricCrypto(jsParam[0], jsParam[1], jsParam[2]).decryptStr(jsParam[3])
|
createSymmetricCrypto(jsParam[0], jsParam[1], jsParam[2]).decryptStr(jsParam[3])
|
||||||
}
|
}
|
||||||
|
|
||||||
"encryptBase64Await" -> {
|
"encryptBase64Await" -> {
|
||||||
createSymmetricCrypto(
|
createSymmetricCrypto(
|
||||||
jsParam[0],
|
jsParam[0],
|
||||||
@@ -100,34 +114,27 @@ class WebJsExtensions(
|
|||||||
jsParam[2]
|
jsParam[2]
|
||||||
).encryptBase64(jsParam[3])
|
).encryptBase64(jsParam[3])
|
||||||
}
|
}
|
||||||
|
|
||||||
"encryptHexAwait" -> {
|
"encryptHexAwait" -> {
|
||||||
createSymmetricCrypto(jsParam[0], jsParam[1], jsParam[2]).encryptHex(jsParam[3])
|
createSymmetricCrypto(jsParam[0], jsParam[1], jsParam[2]).encryptHex(jsParam[3])
|
||||||
}
|
}
|
||||||
|
|
||||||
"createSignHexAwait" -> {
|
"createSignHexAwait" -> {
|
||||||
createSign(jsParam[0]).setPublicKey(jsParam[1]).setPrivateKey(jsParam[2])
|
createSign(jsParam[0]).setPublicKey(jsParam[1]).setPrivateKey(jsParam[2])
|
||||||
.signHex(jsParam[3])
|
.signHex(jsParam[3])
|
||||||
}
|
}
|
||||||
|
|
||||||
"downloadFileAwait" -> {
|
"downloadFileAwait" -> {
|
||||||
downloadFile(jsParam[0])
|
downloadFile(jsParam[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
"readTxtFileAwait" -> {
|
"readTxtFileAwait" -> {
|
||||||
readTxtFile(jsParam[0])
|
readTxtFile(jsParam[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
"importScriptAwait" -> {
|
"importScriptAwait" -> {
|
||||||
importScript(jsParam[0])
|
importScript(jsParam[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
"getStringAwait" -> AnalyzeRule(book, getSource()).run {
|
"getStringAwait" -> AnalyzeRule(book, getSource()).run {
|
||||||
setCoroutineContext(coroutineContext)
|
setCoroutineContext(coroutineContext)
|
||||||
setChapter(chapter)
|
setChapter(chapter)
|
||||||
getString(jsParam[0], jsParam[1])
|
getString(jsParam[0], jsParam[1])
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> "error funName"
|
else -> "error funName"
|
||||||
}
|
}
|
||||||
}.onSuccess { data ->
|
}.onSuccess { data ->
|
||||||
@@ -142,78 +149,64 @@ class WebJsExtensions(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@JavascriptInterface
|
@JavascriptInterface
|
||||||
fun toast(msg: String?) {
|
fun toast(msg: String?) {
|
||||||
super.toast(msg)
|
super.toast(msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
@JavascriptInterface
|
@JavascriptInterface
|
||||||
fun longToast(msg: String?) {
|
fun longToast(msg: String?) {
|
||||||
super.longToast(msg)
|
super.longToast(msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
@JavascriptInterface
|
@JavascriptInterface
|
||||||
fun log(msg: String?): String {
|
fun log(msg: String?): String {
|
||||||
return super.log(msg).toString()
|
return super.log(msg).toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
@JavascriptInterface
|
@JavascriptInterface
|
||||||
fun ajax(url: String): String? {
|
fun ajax(url: String): String? {
|
||||||
return super.ajax(url, 9000)
|
return super.ajax(url, 9000)
|
||||||
}
|
}
|
||||||
|
|
||||||
@JavascriptInterface
|
@JavascriptInterface
|
||||||
fun ajax(url: String, callTimeout: Int?): String? {
|
fun ajax(url: String, callTimeout: Int?): String? {
|
||||||
return super.ajax(url, callTimeout?.toLong())
|
return super.ajax(url, callTimeout?.toLong())
|
||||||
}
|
}
|
||||||
|
|
||||||
@JavascriptInterface
|
@JavascriptInterface
|
||||||
fun connect(urlStr: String?): String {
|
fun connect(urlStr: String?): String {
|
||||||
if (urlStr.isNullOrEmpty()) return "error empty url"
|
if (urlStr.isNullOrEmpty()) return "error empty url"
|
||||||
return super.connect(urlStr, null, 9000).toString()
|
return super.connect(urlStr, null, 9000).toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
@JavascriptInterface
|
@JavascriptInterface
|
||||||
fun connect(urlStr: String, header: String): String {
|
fun connect(urlStr: String, header: String): String {
|
||||||
return super.connect(urlStr, header, 9000).toString()
|
return super.connect(urlStr, header, 9000).toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
@JavascriptInterface
|
@JavascriptInterface
|
||||||
fun connect(urlStr: String, header: String, callTimeout: Int?): String {
|
fun connect(urlStr: String, header: String, callTimeout: Int?): String {
|
||||||
return super.connect(urlStr, header, callTimeout?.toLong()).toString()
|
return super.connect(urlStr, header, callTimeout?.toLong()).toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
@JavascriptInterface
|
@JavascriptInterface
|
||||||
fun get(urlStr: String, headers: String): String {
|
fun get(urlStr: String, headers: String): String {
|
||||||
val headerMap = GSON.fromJsonObject<Map<String, String>>(headers).getOrNull() ?: emptyMap()
|
val headerMap = GSON.fromJsonObject<Map<String, String>>(headers).getOrNull() ?: emptyMap()
|
||||||
return super.get(urlStr, headerMap, 9000).body()
|
return super.get(urlStr, headerMap, 9000).body()
|
||||||
}
|
}
|
||||||
|
|
||||||
@JavascriptInterface
|
@JavascriptInterface
|
||||||
fun get(urlStr: String, headers: String, timeout: Int?): String {
|
fun get(urlStr: String, headers: String, timeout: Int?): String {
|
||||||
val headerMap = GSON.fromJsonObject<Map<String, String>>(headers).getOrNull() ?: emptyMap()
|
val headerMap = GSON.fromJsonObject<Map<String, String>>(headers).getOrNull() ?: emptyMap()
|
||||||
return super.get(urlStr, headerMap, timeout).body()
|
return super.get(urlStr, headerMap, timeout).body()
|
||||||
}
|
}
|
||||||
|
|
||||||
@JavascriptInterface
|
@JavascriptInterface
|
||||||
fun post(urlStr: String, body: String, headers: String): String {
|
fun post(urlStr: String, body: String, headers: String): String {
|
||||||
val headerMap = GSON.fromJsonObject<Map<String, String>>(headers).getOrNull() ?: emptyMap()
|
val headerMap = GSON.fromJsonObject<Map<String, String>>(headers).getOrNull() ?: emptyMap()
|
||||||
return super.post(urlStr, body, headerMap, 9000).body()
|
return super.post(urlStr, body, headerMap, 9000).body()
|
||||||
}
|
}
|
||||||
|
|
||||||
@JavascriptInterface
|
@JavascriptInterface
|
||||||
fun post(urlStr: String, body: String, headers: String, timeout: Int?): String {
|
fun post(urlStr: String, body: String, headers: String, timeout: Int?): String {
|
||||||
val headerMap = GSON.fromJsonObject<Map<String, String>>(headers).getOrNull() ?: emptyMap()
|
val headerMap = GSON.fromJsonObject<Map<String, String>>(headers).getOrNull() ?: emptyMap()
|
||||||
return super.post(urlStr, body, headerMap, timeout).body()
|
return super.post(urlStr, body, headerMap, timeout).body()
|
||||||
}
|
}
|
||||||
|
|
||||||
@JavascriptInterface
|
@JavascriptInterface
|
||||||
fun head(urlStr: String, headers: String): String {
|
fun head(urlStr: String, headers: String): String {
|
||||||
val headerMap = GSON.fromJsonObject<Map<String, String>>(headers).getOrNull() ?: emptyMap()
|
val headerMap = GSON.fromJsonObject<Map<String, String>>(headers).getOrNull() ?: emptyMap()
|
||||||
return GSON.toJson(super.head(urlStr, headerMap, 9000).headers())
|
return GSON.toJson(super.head(urlStr, headerMap, 9000).headers())
|
||||||
}
|
}
|
||||||
|
|
||||||
@JavascriptInterface
|
@JavascriptInterface
|
||||||
fun head(urlStr: String, headers: String, timeout: Int?): String {
|
fun head(urlStr: String, headers: String, timeout: Int?): String {
|
||||||
val headerMap = GSON.fromJsonObject<Map<String, String>>(headers).getOrNull() ?: emptyMap()
|
val headerMap = GSON.fromJsonObject<Map<String, String>>(headers).getOrNull() ?: emptyMap()
|
||||||
@@ -222,127 +215,146 @@ class WebJsExtensions(
|
|||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private fun getRandomLetter(): Char {
|
private fun getRandomLetter(): Char {
|
||||||
val letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_"
|
val letters = "abcdefghijklmnopqrstuvwxyz"
|
||||||
return letters.random()
|
return letters.random()
|
||||||
}
|
}
|
||||||
|
|
||||||
val uuid by lazy { UUID.randomUUID().toString().split("-") }
|
val uuid by lazy {
|
||||||
val uuid2 by lazy { UUID.randomUUID().toString().split("-") }
|
UUID.randomUUID().toString().replace('-', getRandomLetter()).chunked(6)
|
||||||
val nameJava by lazy { getRandomLetter() + uuid[0] + uuid[1] }
|
}
|
||||||
val nameCache by lazy { getRandomLetter() + uuid[2] + uuid[3] }
|
val uuid2 by lazy {
|
||||||
val nameSource by lazy { getRandomLetter() + uuid[4] }
|
UUID.randomUUID().toString().replace('-', getRandomLetter()).chunked(6)
|
||||||
val nameBasic by lazy { getRandomLetter() + uuid2[1] + uuid2[2] }
|
}
|
||||||
val JSBridgeResult by lazy { getRandomLetter() + uuid2[3] + uuid2[4] }
|
val nameUrl by lazy { "https://" + uuid[0] + ".com/" + uuid2[0] + ".js" }
|
||||||
|
val nameJava by lazy { getRandomLetter() + uuid[1] + uuid2[1] }
|
||||||
|
val nameCache by lazy { getRandomLetter() + uuid[2] + uuid2[2] }
|
||||||
|
val nameSource by lazy { getRandomLetter() + uuid[3] + uuid2[3] }
|
||||||
|
val nameBasic by lazy { getRandomLetter() + uuid[4] + uuid2[4] }
|
||||||
|
val JSBridgeResult by lazy { getRandomLetter() + uuid[5] + uuid2[5] }
|
||||||
|
val JS_URL by lazy {
|
||||||
|
"<script src=\"$nameUrl\"></script>"
|
||||||
|
}
|
||||||
|
|
||||||
val JS_INJECTION by lazy {
|
val JS_INJECTION by lazy {
|
||||||
"""
|
"""
|
||||||
const requestId = n => 'req_' + n + '_' + Date.now() + '_' + Math.random().toString(36).slice(-3);
|
const requestId = n => 'req_' + n + '_' + Date.now() + '_' + Math.random().toString(36).slice(-3);
|
||||||
const JSBridgeCallbacks = {};
|
const JSBridgeCallbacks = {};
|
||||||
const java = window.$nameJava;
|
const java = window.$nameJava;
|
||||||
|
delete window.$nameJava;
|
||||||
const source = window.$nameSource;
|
const source = window.$nameSource;
|
||||||
|
delete window.$nameSource;
|
||||||
const cache = window.$nameCache;
|
const cache = window.$nameCache;
|
||||||
|
delete window.$nameCache;
|
||||||
function run(jsCode) {
|
function run(jsCode) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const id = requestId("run");
|
const id = requestId("run");
|
||||||
JSBridgeCallbacks[id] = { resolve, reject };
|
JSBridgeCallbacks[id] = { resolve, reject };
|
||||||
window.$nameJava?.request("run", [String(jsCode)], id);
|
java?.request("run", [String(jsCode)], id);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
function ajaxAwait(url, callTimeout) {
|
function ajaxAwait(url, callTimeout) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const id = requestId("ajaxAwait");
|
const id = requestId("ajaxAwait");
|
||||||
JSBridgeCallbacks[id] = { resolve, reject };
|
JSBridgeCallbacks[id] = { resolve, reject };
|
||||||
window.$nameJava?.request("ajaxAwait", [String(url), String(callTimeout)], id);
|
java?.request("ajaxAwait", [String(url), String(callTimeout)], id);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
function connectAwait(url, header, callTimeout) {
|
function connectAwait(url, header, callTimeout) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const id = requestId("connectAwait");
|
const id = requestId("connectAwait");
|
||||||
JSBridgeCallbacks[id] = { resolve, reject };
|
JSBridgeCallbacks[id] = { resolve, reject };
|
||||||
window.$nameJava?.request("connectAwait", [String(url), String(header), String(callTimeout)], id);
|
java?.request("connectAwait", [String(url), String(header), String(callTimeout)], id);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
function getAwait(url, header, callTimeout) {
|
function getAwait(url, header, callTimeout) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const id = requestId("getAwait");
|
const id = requestId("getAwait");
|
||||||
JSBridgeCallbacks[id] = { resolve, reject };
|
JSBridgeCallbacks[id] = { resolve, reject };
|
||||||
window.$nameJava?.request("getAwait", [String(url), String(header), String(callTimeout)], id);
|
java?.request("getAwait", [String(url), String(header), String(callTimeout)], id);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
function headAwait(url, header, callTimeout) {
|
function headAwait(url, header, callTimeout) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const id = requestId("headAwait");
|
const id = requestId("headAwait");
|
||||||
JSBridgeCallbacks[id] = { resolve, reject };
|
JSBridgeCallbacks[id] = { resolve, reject };
|
||||||
window.$nameJava?.request("headAwait", [String(url), String(header), String(callTimeout)], id);
|
java?.request("headAwait", [String(url), String(header), String(callTimeout)], id);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
function postAwait(url, body, header, callTimeout) {
|
function postAwait(url, body, header, callTimeout) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const id = requestId("postAwait");
|
const id = requestId("postAwait");
|
||||||
JSBridgeCallbacks[id] = { resolve, reject };
|
JSBridgeCallbacks[id] = { resolve, reject };
|
||||||
window.$nameJava?.request("postAwait", [String(url), String(body), String(header), String(callTimeout)], id);
|
java?.request("postAwait", [String(url), String(body), String(header), String(callTimeout)], id);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
function webViewAwait(html, url, js, cacheFirst) {
|
function webViewAwait(html, url, js, cacheFirst) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const id = requestId("webViewAwait");
|
const id = requestId("webViewAwait");
|
||||||
JSBridgeCallbacks[id] = { resolve, reject };
|
JSBridgeCallbacks[id] = { resolve, reject };
|
||||||
window.$nameJava?.request("webViewAwait", [String(html), String(url), String(js), String(cacheFirst)], id);
|
java?.request("webViewAwait", [String(html), String(url), String(js), String(cacheFirst)], id);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
function webViewGetSourceAwait(html, url, js, sourceRegex, cacheFirst, delayTime) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const id = requestId("webViewGetSourceAwait");
|
||||||
|
JSBridgeCallbacks[id] = { resolve, reject };
|
||||||
|
java?.request("webViewGetSourceAwait", [String(html), String(url), String(js), String(sourceRegex), String(cacheFirst), String(delayTime)], id);
|
||||||
|
});
|
||||||
|
}
|
||||||
function decryptStrAwait(transformation, key, iv, data) {
|
function decryptStrAwait(transformation, key, iv, data) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const id = requestId("decryptStrAwait");
|
const id = requestId("decryptStrAwait");
|
||||||
JSBridgeCallbacks[id] = { resolve, reject };
|
JSBridgeCallbacks[id] = { resolve, reject };
|
||||||
window.$nameJava?.request("decryptStrAwait", [String(transformation), String(key), String(iv), String(data)], id);
|
java?.request("decryptStrAwait", [String(transformation), String(key), String(iv), String(data)], id);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
function encryptBase64Await(transformation, key, iv, data) {
|
function encryptBase64Await(transformation, key, iv, data) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const id = requestId("encryptBase64Await");
|
const id = requestId("encryptBase64Await");
|
||||||
JSBridgeCallbacks[id] = { resolve, reject };
|
JSBridgeCallbacks[id] = { resolve, reject };
|
||||||
window.$nameJava?.request("encryptBase64Await", [String(transformation), String(key), String(iv), String(data)], id);
|
java?.request("encryptBase64Await", [String(transformation), String(key), String(iv), String(data)], id);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
function encryptHexAwait(transformation, key, iv, data) {
|
function encryptHexAwait(transformation, key, iv, data) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const id = requestId("encryptHexAwait");
|
const id = requestId("encryptHexAwait");
|
||||||
JSBridgeCallbacks[id] = { resolve, reject };
|
JSBridgeCallbacks[id] = { resolve, reject };
|
||||||
window.$nameJava?.request("encryptHexAwait", [String(transformation), String(key), String(iv), String(data)], id);
|
java?.request("encryptHexAwait", [String(transformation), String(key), String(iv), String(data)], id);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
function createSignHexAwait(algorithm, publicKey, privateKey, data) {
|
function createSignHexAwait(algorithm, publicKey, privateKey, data) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const id = requestId("createSignHexAwait");
|
const id = requestId("createSignHexAwait");
|
||||||
JSBridgeCallbacks[id] = { resolve, reject };
|
JSBridgeCallbacks[id] = { resolve, reject };
|
||||||
window.$nameJava?.request("createSignHexAwait", [String(algorithm), String(publicKey), String(privateKey), String(data)], id);
|
java?.request("createSignHexAwait", [String(algorithm), String(publicKey), String(privateKey), String(data)], id);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
function downloadFileAwait(url) {
|
function downloadFileAwait(url) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const id = requestId("downloadFileAwait");
|
const id = requestId("downloadFileAwait");
|
||||||
JSBridgeCallbacks[id] = { resolve, reject };
|
JSBridgeCallbacks[id] = { resolve, reject };
|
||||||
window.$nameJava?.request("downloadFileAwait", [String(url)], id);
|
java?.request("downloadFileAwait", [String(url)], id);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
function readTxtFileAwait(path) {
|
function readTxtFileAwait(path) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const id = requestId("readTxtFileAwait");
|
const id = requestId("readTxtFileAwait");
|
||||||
JSBridgeCallbacks[id] = { resolve, reject };
|
JSBridgeCallbacks[id] = { resolve, reject };
|
||||||
window.$nameJava?.request("readTxtFileAwait", [String(path)], id);
|
java?.request("readTxtFileAwait", [String(path)], id);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
function importScriptAwait(url) {
|
function importScriptAwait(url) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const id = requestId("importScriptAwait");
|
const id = requestId("importScriptAwait");
|
||||||
JSBridgeCallbacks[id] = { resolve, reject };
|
JSBridgeCallbacks[id] = { resolve, reject };
|
||||||
window.$nameJava?.request("importScriptAwait", [String(url)], id);
|
java?.request("importScriptAwait", [String(url)], id);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
function getStringAwait(ruleStr, mContent) {
|
function getStringAwait(ruleStr, mContent) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const id = requestId("getStringAwait");
|
const id = requestId("getStringAwait");
|
||||||
JSBridgeCallbacks[id] = { resolve, reject };
|
JSBridgeCallbacks[id] = { resolve, reject };
|
||||||
window.$nameJava?.request("getStringAwait", [String(ruleStr), String(mContent)], id);
|
java?.request("getStringAwait", [String(ruleStr), String(mContent)], id);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
window.$JSBridgeResult = function(requestId, result, error) {
|
window.$JSBridgeResult = function(requestId, result, error) {
|
||||||
@@ -354,31 +366,55 @@ class WebJsExtensions(
|
|||||||
}
|
}
|
||||||
delete JSBridgeCallbacks[requestId];
|
delete JSBridgeCallbacks[requestId];
|
||||||
}
|
}
|
||||||
};"""
|
};""".trimIndent()
|
||||||
|
}
|
||||||
|
|
||||||
|
val JS_INJECTION2 by lazy {
|
||||||
|
"""
|
||||||
|
const requestId = n => 'req_' + n + '_' + Date.now() + '_' + Math.random().toString(36).slice(-3);
|
||||||
|
const JSBridgeCallbacks = {};
|
||||||
|
const java = window.$nameJava;
|
||||||
|
delete window.$nameJava;
|
||||||
|
function run(jsCode) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const id = requestId("run");
|
||||||
|
JSBridgeCallbacks[id] = { resolve, reject };
|
||||||
|
java?.request("run", [String(jsCode)], id);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
window.$JSBridgeResult = function(requestId, result, error) {
|
||||||
|
if (JSBridgeCallbacks[requestId]) {
|
||||||
|
if (error) {
|
||||||
|
JSBridgeCallbacks[requestId].reject(error);
|
||||||
|
} else {
|
||||||
|
JSBridgeCallbacks[requestId].resolve(result);
|
||||||
|
}
|
||||||
|
delete JSBridgeCallbacks[requestId];
|
||||||
|
}
|
||||||
|
};""".trimIndent()
|
||||||
}
|
}
|
||||||
|
|
||||||
val basicJs by lazy {
|
val basicJs by lazy {
|
||||||
"""
|
"""
|
||||||
(function() {
|
(function() {
|
||||||
if (screen.orientation && !screen.orientation.__patched) {
|
if (screen.orientation) {
|
||||||
screen.orientation.lock = function(orientation) {
|
screen.orientation.lock = function(orientation) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
window.$nameBasic?.lockOrientation(orientation)
|
window.$nameBasic?.lockOrientation(orientation);
|
||||||
resolve()
|
resolve()
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
screen.orientation.unlock = function() {
|
screen.orientation.unlock = function() {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
window.$nameBasic?.lockOrientation('unlock')
|
window.$nameBasic?.lockOrientation('unlock');
|
||||||
resolve()
|
resolve()
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
screen.orientation.__patched = true;
|
|
||||||
};
|
};
|
||||||
window.close = function() {
|
window.close = function() {
|
||||||
window.$nameBasic?.onCloseRequested();
|
window.$nameBasic?.onCloseRequested();
|
||||||
};
|
};
|
||||||
})();"""
|
})();""".trimIndent()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -255,6 +255,7 @@ class ReadBookActivity : BaseReadBookActivity(),
|
|||||||
override val pageFactory get() = binding.readView.pageFactory
|
override val pageFactory get() = binding.readView.pageFactory
|
||||||
override val pageDelegate get() = binding.readView.pageDelegate
|
override val pageDelegate get() = binding.readView.pageDelegate
|
||||||
override val headerHeight: Int get() = binding.readView.curPage.headerHeight
|
override val headerHeight: Int get() = binding.readView.curPage.headerHeight
|
||||||
|
override val imgBgPaddingStart: Int get() = binding.readView.curPage.imgBgPaddingStart
|
||||||
private val nextPageDebounce by lazy { Debounce { keyPage(PageDirection.NEXT) } }
|
private val nextPageDebounce by lazy { Debounce { keyPage(PageDirection.NEXT) } }
|
||||||
private val prevPageDebounce by lazy { Debounce { keyPage(PageDirection.PREV) } }
|
private val prevPageDebounce by lazy { Debounce { keyPage(PageDirection.PREV) } }
|
||||||
private var bookChanged = false
|
private var bookChanged = false
|
||||||
|
|||||||
@@ -165,6 +165,36 @@ class TipConfigDialog : BaseBottomSheetDialogFragment(R.layout.dialog_tip_config
|
|||||||
}.requestInputMethod()
|
}.requestInputMethod()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
3 -> { // 正则表达式分段
|
||||||
|
alert(title = "设置正则分段规则") {
|
||||||
|
val alertBinding = DialogEditTextBinding.inflate(layoutInflater).apply {
|
||||||
|
editView.inputType = InputType.TYPE_CLASS_TEXT
|
||||||
|
editView.setText(ReadBookConfig.titleSegFlag)
|
||||||
|
editLayout.hint = "例如: [章回篇] 或 (第.{1,3}章)"
|
||||||
|
editView.isSingleLine = true
|
||||||
|
}
|
||||||
|
|
||||||
|
customView { alertBinding.root }
|
||||||
|
|
||||||
|
okButton {
|
||||||
|
val value = alertBinding.editView.text?.toString()?.trim()
|
||||||
|
if (!value.isNullOrEmpty()) {
|
||||||
|
try {
|
||||||
|
Regex(value)
|
||||||
|
ReadBookConfig.titleSegFlag = value
|
||||||
|
toastOnUi("正则规则已保存")
|
||||||
|
postEvent(EventBus.UP_CONFIG, arrayListOf(5))
|
||||||
|
} catch (e: Exception) {
|
||||||
|
toastOnUi("正则表达式格式错误")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
toastOnUi("规则不能为空")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cancelButton()
|
||||||
|
}.requestInputMethod()
|
||||||
|
}
|
||||||
|
|
||||||
else -> {
|
else -> {
|
||||||
toastOnUi("当前分段模式无需配置参数")
|
toastOnUi("当前分段模式无需配置参数")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -64,6 +64,8 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at
|
|||||||
private var autoPager: AutoPager? = null
|
private var autoPager: AutoPager? = null
|
||||||
private var isScroll = false
|
private var isScroll = false
|
||||||
private val renderRunnable by lazy { Runnable { preRenderPage() } }
|
private val renderRunnable by lazy { Runnable { preRenderPage() } }
|
||||||
|
private var lastClickTime = 0L
|
||||||
|
private var doubleClick = false
|
||||||
|
|
||||||
//绘制图片的paint
|
//绘制图片的paint
|
||||||
val imagePaint by lazy {
|
val imagePaint by lazy {
|
||||||
@@ -226,6 +228,11 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at
|
|||||||
column.selected = true
|
column.selected = true
|
||||||
select(textPos)
|
select(textPos)
|
||||||
}
|
}
|
||||||
|
is TextHtmlColumn -> {
|
||||||
|
if (!selectAble) return@touch
|
||||||
|
column.selected = true
|
||||||
|
select(textPos)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -236,6 +243,14 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at
|
|||||||
*/
|
*/
|
||||||
@Suppress("UNUSED_ANONYMOUS_PARAMETER")
|
@Suppress("UNUSED_ANONYMOUS_PARAMETER")
|
||||||
fun click(x: Float, y: Float): Boolean {
|
fun click(x: Float, y: Float): Boolean {
|
||||||
|
val currentTime = System.currentTimeMillis()
|
||||||
|
val debounceClick = currentTime - lastClickTime < 300L //300毫秒防抖和双击
|
||||||
|
lastClickTime = currentTime
|
||||||
|
doubleClick = if (debounceClick) {
|
||||||
|
!doubleClick
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
var handled = false
|
var handled = false
|
||||||
touch(x, y) { _, textPos, textPage, textLine, column ->
|
touch(x, y) { _, textPos, textPage, textLine, column ->
|
||||||
when (column) {
|
when (column) {
|
||||||
@@ -254,45 +269,51 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at
|
|||||||
activity?.showDialogFragment(PhotoDialog(column.src))
|
activity?.showDialogFragment(PhotoDialog(column.src))
|
||||||
handled = true
|
handled = true
|
||||||
}
|
}
|
||||||
|
|
||||||
"2" -> { //兼容处理
|
"2" -> { //兼容处理
|
||||||
if (ReadBook.book?.isOnLineTxt == true) {
|
if (!debounceClick) {
|
||||||
val click = column.click
|
if (ReadBook.book?.isOnLineTxt == true) {
|
||||||
val src = column.src
|
val click = column.click
|
||||||
if (!click.isNullOrBlank()) {
|
val src = column.src
|
||||||
callBack.clickImg(click, src)
|
if (!click.isNullOrBlank()) {
|
||||||
handled = true
|
callBack.clickImg(click, src)
|
||||||
} else {
|
handled = true
|
||||||
handled = callBack.oldClickImg(src)
|
} else {
|
||||||
|
handled = callBack.oldClickImg(src)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
"3" -> { //关闭
|
"3" -> { //关闭
|
||||||
handled = false
|
handled = false
|
||||||
}
|
}
|
||||||
|
"4" -> { //双击
|
||||||
else -> { //默认点击
|
if (doubleClick) {
|
||||||
val click = column.click
|
val click = column.click
|
||||||
if (!click.isNullOrBlank()) {
|
if (!click.isNullOrBlank()) {
|
||||||
callBack.clickImg(click, column.src)
|
callBack.clickImg(click, column.src)
|
||||||
|
handled = true
|
||||||
|
}
|
||||||
|
} else {
|
||||||
handled = true
|
handled = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else -> { //默认点击
|
||||||
|
if (!debounceClick) {
|
||||||
|
val click = column.click
|
||||||
|
if (!click.isNullOrBlank()) {
|
||||||
|
callBack.clickImg(click, column.src)
|
||||||
|
handled = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
is TextHtmlColumn -> {
|
is TextHtmlColumn -> {
|
||||||
column.linkUrl?.let {
|
column.linkUrl?.let {
|
||||||
activity?.startActivity<OpenUrlConfirmActivity> {
|
activity?.startActivity<OpenUrlConfirmActivity> {
|
||||||
putExtra("uri", it)
|
putExtra("uri", it)
|
||||||
// putExtra("mimeType", mimeType)
|
|
||||||
// putExtra("sourceOrigin", source.getKey())
|
|
||||||
// putExtra("sourceName", source.getTag())
|
|
||||||
// putExtra("sourceType", source.getSourceType())
|
|
||||||
}
|
}
|
||||||
handled = true
|
handled = true
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -308,7 +329,7 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at
|
|||||||
select: (textPos: TextPos) -> Unit,
|
select: (textPos: TextPos) -> Unit,
|
||||||
) {
|
) {
|
||||||
touchRough(x, y) { _, textPos, _, _, column ->
|
touchRough(x, y) { _, textPos, _, _, column ->
|
||||||
if (column is TextColumn) {
|
if (column is TextBaseColumn) {
|
||||||
column.selected = true
|
column.selected = true
|
||||||
select(textPos)
|
select(textPos)
|
||||||
}
|
}
|
||||||
@@ -582,7 +603,7 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at
|
|||||||
textPos.lineIndex = lineIndex
|
textPos.lineIndex = lineIndex
|
||||||
for ((charIndex, column) in textLine.columns.withIndex()) {
|
for ((charIndex, column) in textLine.columns.withIndex()) {
|
||||||
textPos.columnIndex = charIndex
|
textPos.columnIndex = charIndex
|
||||||
if (column is TextColumn) {
|
if (column is TextBaseColumn) {
|
||||||
val compareStart = textPos.compare(selectStart)
|
val compareStart = textPos.compare(selectStart)
|
||||||
val compareEnd = textPos.compare(selectEnd)
|
val compareEnd = textPos.compare(selectEnd)
|
||||||
column.selected = compareStart >= 0 && compareEnd <= 0
|
column.selected = compareStart >= 0 && compareEnd <= 0
|
||||||
@@ -600,13 +621,13 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at
|
|||||||
|
|
||||||
private fun upSelectedStart(x: Float, y: Float, top: Float) {
|
private fun upSelectedStart(x: Float, y: Float, top: Float) {
|
||||||
callBack.run {
|
callBack.run {
|
||||||
upSelectedStart(x, y + headerHeight, top + headerHeight)
|
upSelectedStart(x + imgBgPaddingStart, y + headerHeight, top + headerHeight)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun upSelectedEnd(x: Float, y: Float) {
|
private fun upSelectedEnd(x: Float, y: Float) {
|
||||||
callBack.run {
|
callBack.run {
|
||||||
upSelectedEnd(x, y + headerHeight)
|
upSelectedEnd(x + imgBgPaddingStart, y + headerHeight)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -621,7 +642,7 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at
|
|||||||
val textPage = relativePage(relativePos)
|
val textPage = relativePage(relativePos)
|
||||||
textPage.lines.forEach { textLine ->
|
textPage.lines.forEach { textLine ->
|
||||||
textLine.columns.forEach {
|
textLine.columns.forEach {
|
||||||
if (it is TextColumn) {
|
if (it is TextBaseColumn) {
|
||||||
it.selected = false
|
it.selected = false
|
||||||
if (clearSearchResult) {
|
if (clearSearchResult) {
|
||||||
it.isSearchResult = false
|
it.isSearchResult = false
|
||||||
@@ -649,7 +670,7 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at
|
|||||||
textPos.columnIndex = charIndex
|
textPos.columnIndex = charIndex
|
||||||
val compareStart = textPos.compare(selectStart)
|
val compareStart = textPos.compare(selectStart)
|
||||||
val compareEnd = textPos.compare(selectEnd)
|
val compareEnd = textPos.compare(selectEnd)
|
||||||
if (column is TextColumn) {
|
if (column is TextBaseColumn) {
|
||||||
when {
|
when {
|
||||||
compareStart == -1 -> if (
|
compareStart == -1 -> if (
|
||||||
selectStart.columnIndex == textLine.columns.size
|
selectStart.columnIndex == textLine.columns.size
|
||||||
@@ -750,6 +771,7 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at
|
|||||||
|
|
||||||
interface CallBack {
|
interface CallBack {
|
||||||
val headerHeight: Int
|
val headerHeight: Int
|
||||||
|
val imgBgPaddingStart: Int
|
||||||
val pageFactory: TextPageFactory
|
val pageFactory: TextPageFactory
|
||||||
val pageDelegate: PageDelegate?
|
val pageDelegate: PageDelegate?
|
||||||
val isScroll: Boolean
|
val isScroll: Boolean
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import androidx.core.graphics.drawable.toDrawable
|
|||||||
import androidx.core.view.ViewCompat
|
import androidx.core.view.ViewCompat
|
||||||
import androidx.core.view.WindowInsetsCompat
|
import androidx.core.view.WindowInsetsCompat
|
||||||
import androidx.core.view.isGone
|
import androidx.core.view.isGone
|
||||||
import androidx.core.view.isInvisible
|
|
||||||
import io.legado.app.R
|
import io.legado.app.R
|
||||||
import io.legado.app.constant.AppConst.timeFormat
|
import io.legado.app.constant.AppConst.timeFormat
|
||||||
import io.legado.app.data.entities.Bookmark
|
import io.legado.app.data.entities.Bookmark
|
||||||
@@ -72,6 +71,11 @@ class PageView(context: Context) : FrameLayout(context) {
|
|||||||
return h1 + h2 + binding.vwRoot.paddingTop
|
return h1 + h2 + binding.vwRoot.paddingTop
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val imgBgPaddingStart: Int
|
||||||
|
get() {
|
||||||
|
return binding.vwRoot.paddingStart
|
||||||
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
upStyle()
|
upStyle()
|
||||||
binding.vwStatusBar.applyStatusBarPadding()
|
binding.vwStatusBar.applyStatusBarPadding()
|
||||||
@@ -132,7 +136,7 @@ class PageView(context: Context) : FrameLayout(context) {
|
|||||||
*/
|
*/
|
||||||
fun upStatusBar() = with(binding.vwStatusBar) {
|
fun upStatusBar() = with(binding.vwStatusBar) {
|
||||||
setPadding(paddingLeft, context.statusBarHeight, paddingRight, paddingBottom)
|
setPadding(paddingLeft, context.statusBarHeight, paddingRight, paddingBottom)
|
||||||
isGone = ReadBookConfig.hideStatusBar
|
isGone = ReadBookConfig.hideStatusBar || readBookActivity?.isInMultiWindow == true
|
||||||
}
|
}
|
||||||
|
|
||||||
fun upNavigationBar() {
|
fun upNavigationBar() {
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import io.legado.app.help.PaintPool
|
|||||||
import io.legado.app.help.config.AppConfig
|
import io.legado.app.help.config.AppConfig
|
||||||
import io.legado.app.help.config.ReadBookConfig
|
import io.legado.app.help.config.ReadBookConfig
|
||||||
import io.legado.app.ui.book.read.page.ContentTextView
|
import io.legado.app.ui.book.read.page.ContentTextView
|
||||||
|
import io.legado.app.ui.book.read.page.TextBaseColumn
|
||||||
import io.legado.app.ui.book.read.page.entities.TextChapter.Companion.emptyTextChapter
|
import io.legado.app.ui.book.read.page.entities.TextChapter.Companion.emptyTextChapter
|
||||||
import io.legado.app.ui.book.read.page.entities.column.TextColumn
|
import io.legado.app.ui.book.read.page.entities.column.TextColumn
|
||||||
import io.legado.app.ui.book.read.page.provider.ChapterProvider
|
import io.legado.app.ui.book.read.page.provider.ChapterProvider
|
||||||
@@ -49,9 +50,8 @@ data class TextPage(
|
|||||||
val lines: List<TextLine> get() = textLines
|
val lines: List<TextLine> get() = textLines
|
||||||
val lineSize: Int get() = textLines.size
|
val lineSize: Int get() = textLines.size
|
||||||
val charSize: Int get() = text.length.coerceAtLeast(1)
|
val charSize: Int get() = text.length.coerceAtLeast(1)
|
||||||
val chapterPosition: Int
|
val chapterPosition: Int get() = textLines.first().chapterPosition
|
||||||
get() = textLines.firstOrNull()?.chapterPosition ?: 0
|
val searchResult = hashSetOf<TextBaseColumn>()
|
||||||
val searchResult = hashSetOf<TextColumn>()
|
|
||||||
var isMsgPage: Boolean = false
|
var isMsgPage: Boolean = false
|
||||||
var canvasRecorder = CanvasRecorderFactory.create(true)
|
var canvasRecorder = CanvasRecorderFactory.create(true)
|
||||||
var doublePage = false
|
var doublePage = false
|
||||||
@@ -271,7 +271,7 @@ data class TextPage(
|
|||||||
val columns = textLines[maxIndex].columns
|
val columns = textLines[maxIndex].columns
|
||||||
for (index in 0 until columnIndex) {
|
for (index in 0 until columnIndex) {
|
||||||
val column = columns[index]
|
val column = columns[index]
|
||||||
if (column is TextColumn) {
|
if (column is TextBaseColumn) {
|
||||||
length += column.charData.length
|
length += column.charData.length
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -292,10 +292,10 @@ data class TextPage(
|
|||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
fun containPos(chapterPos: Int): Boolean {
|
fun containPos(chapterPos: Int): Boolean {
|
||||||
val line = lines.firstOrNull() ?: return false // lines 为空时直接返回 false
|
val line = lines.first()
|
||||||
val startPos = line.chapterPosition
|
val startPos = line.chapterPosition
|
||||||
val endPos = startPos + charSize
|
val endPos = startPos + charSize
|
||||||
return chapterPos in startPos until endPos
|
return chapterPos in startPos..<endPos
|
||||||
}
|
}
|
||||||
|
|
||||||
fun draw(view: ContentTextView, canvas: Canvas, relativeOffset: Float) {
|
fun draw(view: ContentTextView, canvas: Canvas, relativeOffset: Float) {
|
||||||
@@ -337,7 +337,10 @@ data class TextPage(
|
|||||||
|
|
||||||
fun render(view: ContentTextView): Boolean {
|
fun render(view: ContentTextView): Boolean {
|
||||||
if (!isCompleted) return false
|
if (!isCompleted) return false
|
||||||
return canvasRecorder.recordIfNeeded(view.width, renderHeight) {
|
return canvasRecorder.recordIfNeeded(
|
||||||
|
view.width,
|
||||||
|
renderHeight + 10.dpToPx()
|
||||||
|
) { //高度留余,避免图片过高时被截断 下划线最远10dp
|
||||||
drawPage(view, this)
|
drawPage(view, this)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -365,15 +368,10 @@ data class TextPage(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun upRenderHeight() {
|
fun upRenderHeight() {
|
||||||
if (lines.isEmpty()) {
|
|
||||||
renderHeight = 0
|
|
||||||
return
|
|
||||||
}
|
|
||||||
renderHeight = ceil(lines.last().lineBottom).toInt()
|
renderHeight = ceil(lines.last().lineBottom).toInt()
|
||||||
if (leftLineSize > 0 && leftLineSize <= lines.size) {
|
if (leftLineSize > 0 && leftLineSize != lines.size) {
|
||||||
val leftHeight = ceil(lines[leftLineSize - 1].lineBottom).toInt()
|
val leftHeight = ceil(lines[leftLineSize - 1].lineBottom).toInt()
|
||||||
renderHeight = max(renderHeight, leftHeight)
|
renderHeight = max(renderHeight, leftHeight)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,13 +20,21 @@ object TitleStyleParser {
|
|||||||
listOf(rawTitle.take(segDistance), rawTitle.substring(segDistance))
|
listOf(rawTitle.take(segDistance), rawTitle.substring(segDistance))
|
||||||
}
|
}
|
||||||
|
|
||||||
2 -> {
|
2, 3 -> {
|
||||||
val flags = segFlag.split(",").map { it.trim() }.filter { it.isNotEmpty() }
|
if (segFlag.isEmpty()) {
|
||||||
if (flags.isEmpty()) listOf(rawTitle)
|
listOf(rawTitle)
|
||||||
else {
|
} else {
|
||||||
val pattern = flags.joinToString("|") { Regex.escape(it) }
|
val regex = if (segType == 3) {
|
||||||
val regex = Regex("(?<=$pattern)")
|
Regex("(?<=$segFlag)")
|
||||||
rawTitle.split(regex).map { it.trim() }.filter { it.isNotEmpty() }
|
} else {
|
||||||
|
val flags = segFlag.split(",").map { it.trim() }.filter { it.isNotEmpty() }
|
||||||
|
val pattern = flags.joinToString("|") { Regex.escape(it) }
|
||||||
|
Regex("(?<=$pattern)")
|
||||||
|
}
|
||||||
|
|
||||||
|
rawTitle.split(regex)
|
||||||
|
.map { it.trim() }
|
||||||
|
.filter { it.isNotEmpty() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,29 +17,30 @@ import io.legado.app.utils.toastOnUi
|
|||||||
import kotlinx.coroutines.Dispatchers.IO
|
import kotlinx.coroutines.Dispatchers.IO
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
import java.lang.ref.WeakReference
|
||||||
|
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
class SourceLoginJsExtensions(
|
class SourceLoginJsExtensions(
|
||||||
activity: AppCompatActivity?, source: BaseSource?,
|
activity: AppCompatActivity?, source: BaseSource?,
|
||||||
private val bookType: Int = 0,
|
private val bookType: Int = 0,
|
||||||
private val callback: Callback? = null
|
callback: Callback? = null
|
||||||
) : RssJsExtensions(activity, source) {
|
) : RssJsExtensions(activity, source) {
|
||||||
|
private val callbackRef: WeakReference<Callback> = WeakReference(callback)
|
||||||
interface Callback {
|
interface Callback {
|
||||||
fun upUiData(data: Map<String, String?>?)
|
fun upUiData(data: Map<String, String?>?)
|
||||||
fun reUiView()
|
fun reUiView()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun upLoginData(data: Map<String, String?>?) {
|
fun upLoginData(data: Map<String, String?>?) {
|
||||||
callback?.upUiData(data)
|
callbackRef.get()?.upUiData(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun reLoginView() {
|
fun reLoginView() {
|
||||||
callback?.reUiView()
|
callbackRef.get()?.reUiView()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun refreshExplore() {
|
fun refreshExplore() {
|
||||||
callback?.reUiView()
|
callbackRef.get()?.reUiView()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun refreshBookInfo() {
|
fun refreshBookInfo() {
|
||||||
@@ -65,7 +66,12 @@ class SourceLoginJsExtensions(
|
|||||||
}
|
}
|
||||||
|
|
||||||
@JvmOverloads
|
@JvmOverloads
|
||||||
fun showBrowser(url: String, html: String, preloadJs: String? = null) {
|
fun showBrowser(
|
||||||
|
url: String,
|
||||||
|
html: String? = null,
|
||||||
|
preloadJs: String? = null,
|
||||||
|
config: String? = null
|
||||||
|
) {
|
||||||
val activity = activityRef.get() ?: return
|
val activity = activityRef.get() ?: return
|
||||||
val source = getSource() ?: return
|
val source = getSource() ?: return
|
||||||
activity.showDialogFragment(
|
activity.showDialogFragment(
|
||||||
@@ -74,7 +80,8 @@ class SourceLoginJsExtensions(
|
|||||||
bookType,
|
bookType,
|
||||||
url,
|
url,
|
||||||
html,
|
html,
|
||||||
preloadJs
|
preloadJs,
|
||||||
|
config
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import io.legado.app.ui.book.explore.ExploreShowActivity
|
|||||||
import io.legado.app.ui.book.search.SearchActivity
|
import io.legado.app.ui.book.search.SearchActivity
|
||||||
import io.legado.app.ui.login.SourceLoginActivity
|
import io.legado.app.ui.login.SourceLoginActivity
|
||||||
import io.legado.app.ui.rss.article.RssSortActivity
|
import io.legado.app.ui.rss.article.RssSortActivity
|
||||||
|
import io.legado.app.ui.widget.dialog.PhotoDialog
|
||||||
import io.legado.app.utils.isJsonObject
|
import io.legado.app.utils.isJsonObject
|
||||||
import io.legado.app.utils.showDialogFragment
|
import io.legado.app.utils.showDialogFragment
|
||||||
import io.legado.app.utils.startActivity
|
import io.legado.app.utils.startActivity
|
||||||
@@ -25,6 +26,7 @@ import kotlinx.coroutines.withContext
|
|||||||
import org.json.JSONObject
|
import org.json.JSONObject
|
||||||
import java.lang.ref.WeakReference
|
import java.lang.ref.WeakReference
|
||||||
|
|
||||||
|
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
open class RssJsExtensions(activity: AppCompatActivity?, source: BaseSource?) : JsExtensions {
|
open class RssJsExtensions(activity: AppCompatActivity?, source: BaseSource?) : JsExtensions {
|
||||||
|
|
||||||
@@ -36,7 +38,7 @@ open class RssJsExtensions(activity: AppCompatActivity?, source: BaseSource?) :
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun getTag(): String? {
|
override fun getTag(): String? {
|
||||||
return sourceRef.get()?.getTag()
|
return getSource()?.getTag()
|
||||||
}
|
}
|
||||||
|
|
||||||
@JavascriptInterface
|
@JavascriptInterface
|
||||||
@@ -67,6 +69,11 @@ open class RssJsExtensions(activity: AppCompatActivity?, source: BaseSource?) :
|
|||||||
activityRef.get()?.showDialogFragment(AddToBookshelfDialog(bookUrl))
|
activityRef.get()?.showDialogFragment(AddToBookshelfDialog(bookUrl))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun showPhoto(src: String) {
|
||||||
|
activityRef.get()?.showDialogFragment(PhotoDialog(src, getSource()?.getKey()))
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@JavascriptInterface
|
@JavascriptInterface
|
||||||
@JvmOverloads
|
@JvmOverloads
|
||||||
fun open(name: String, url: String? = null, title: String? = null, origin: String? = null) {
|
fun open(name: String, url: String? = null, title: String? = null, origin: String? = null) {
|
||||||
|
|||||||
@@ -59,8 +59,9 @@ class BottomWebViewDialog() :
|
|||||||
sourceKey: String,
|
sourceKey: String,
|
||||||
bookType: Int,
|
bookType: Int,
|
||||||
url: String,
|
url: String,
|
||||||
html: String,
|
html: String? = null,
|
||||||
preloadJs: String? = null
|
preloadJs: String? = null,
|
||||||
|
config: String? = null
|
||||||
) : this() {
|
) : this() {
|
||||||
arguments = Bundle().apply {
|
arguments = Bundle().apply {
|
||||||
putString("sourceKey", sourceKey)
|
putString("sourceKey", sourceKey)
|
||||||
@@ -68,6 +69,7 @@ class BottomWebViewDialog() :
|
|||||||
putString("url", url)
|
putString("url", url)
|
||||||
putString("html", html)
|
putString("html", html)
|
||||||
putString("preloadJs", preloadJs)
|
putString("preloadJs", preloadJs)
|
||||||
|
putString("config", config)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package com.script.rhino
|
package com.script.rhino
|
||||||
|
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.currentCoroutineContext
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import kotlinx.coroutines.supervisorScope
|
import kotlinx.coroutines.supervisorScope
|
||||||
import org.mozilla.javascript.Context
|
import org.mozilla.javascript.Context
|
||||||
@@ -9,7 +10,6 @@ import kotlin.contracts.InvocationKind
|
|||||||
import kotlin.contracts.contract
|
import kotlin.contracts.contract
|
||||||
import kotlin.coroutines.ContinuationInterceptor
|
import kotlin.coroutines.ContinuationInterceptor
|
||||||
import kotlin.coroutines.CoroutineContext
|
import kotlin.coroutines.CoroutineContext
|
||||||
import kotlin.coroutines.coroutineContext
|
|
||||||
|
|
||||||
val rhinoContext: RhinoContext
|
val rhinoContext: RhinoContext
|
||||||
get() = Context.getCurrentContext() as RhinoContext
|
get() = Context.getCurrentContext() as RhinoContext
|
||||||
@@ -55,7 +55,7 @@ inline fun <T> runScriptWithContext(context: CoroutineContext, block: () -> T):
|
|||||||
suspend inline fun <T> runScriptWithContext(block: () -> T): T {
|
suspend inline fun <T> runScriptWithContext(block: () -> T): T {
|
||||||
val rhinoContext = Context.enter() as RhinoContext
|
val rhinoContext = Context.enter() as RhinoContext
|
||||||
val previousCoroutineContext = rhinoContext.coroutineContext
|
val previousCoroutineContext = rhinoContext.coroutineContext
|
||||||
rhinoContext.coroutineContext = coroutineContext.minusKey(ContinuationInterceptor)
|
rhinoContext.coroutineContext = currentCoroutineContext().minusKey(ContinuationInterceptor)
|
||||||
try {
|
try {
|
||||||
return block()
|
return block()
|
||||||
} finally {
|
} finally {
|
||||||
|
|||||||
Reference in New Issue
Block a user