优化
This commit is contained in:
@@ -1,14 +1,15 @@
|
|||||||
[
|
[
|
||||||
{
|
{
|
||||||
"articleStyle": 0,
|
|
||||||
"customOrder": 1,
|
"customOrder": 1,
|
||||||
"enableJs": true,
|
"enableJs": true,
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"loadWithBaseUrl": true,
|
"loadWithBaseUrl": true,
|
||||||
"singleUrl": true,
|
"singleUrl": true,
|
||||||
|
"sourceGroup": "legado",
|
||||||
"sourceIcon": "https://funimg.pddpic.com/ktt/762ee1c9-a117-46e4-b0c7-9d820420c08b.png.slim.png",
|
"sourceIcon": "https://funimg.pddpic.com/ktt/762ee1c9-a117-46e4-b0c7-9d820420c08b.png.slim.png",
|
||||||
"sourceName": "我的小店",
|
"sourceName": "我的小店",
|
||||||
"sourceUrl": "https://ktt.pinduoduo.com/t/tlQQsofbQM"
|
"sourceUrl": "https://ktt.pinduoduo.com/t/tlQQsofbQM",
|
||||||
|
"sortUrl": "@js:java.webViewGetOverrideUrl(null, source.sourceUrl, null, \"weixin:.*\")"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"customOrder": 2,
|
"customOrder": 2,
|
||||||
|
|||||||
@@ -104,6 +104,9 @@ interface RssSourceDao {
|
|||||||
@Query("delete from rssSources where sourceUrl = :sourceUrl")
|
@Query("delete from rssSources where sourceUrl = :sourceUrl")
|
||||||
fun delete(sourceUrl: String)
|
fun delete(sourceUrl: String)
|
||||||
|
|
||||||
|
@Query("delete from rssSources where sourceGroup like 'legado'")
|
||||||
|
fun deleteDefault()
|
||||||
|
|
||||||
@get:Query("select * from rssSources where sourceGroup is null or sourceGroup = ''")
|
@get:Query("select * from rssSources where sourceGroup is null or sourceGroup = ''")
|
||||||
val noGroup: List<RssSource>
|
val noGroup: List<RssSource>
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,11 @@ package io.legado.app.help
|
|||||||
|
|
||||||
import io.legado.app.constant.AppConst
|
import io.legado.app.constant.AppConst
|
||||||
import io.legado.app.data.appDb
|
import io.legado.app.data.appDb
|
||||||
import io.legado.app.data.entities.*
|
import io.legado.app.data.entities.DictRule
|
||||||
|
import io.legado.app.data.entities.HttpTTS
|
||||||
|
import io.legado.app.data.entities.KeyboardAssist
|
||||||
|
import io.legado.app.data.entities.RssSource
|
||||||
|
import io.legado.app.data.entities.TxtTocRule
|
||||||
import io.legado.app.help.config.LocalConfig
|
import io.legado.app.help.config.LocalConfig
|
||||||
import io.legado.app.help.config.ReadBookConfig
|
import io.legado.app.help.config.ReadBookConfig
|
||||||
import io.legado.app.help.config.ThemeConfig
|
import io.legado.app.help.config.ThemeConfig
|
||||||
@@ -117,6 +121,7 @@ object DefaultData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun importDefaultRssSources() {
|
fun importDefaultRssSources() {
|
||||||
|
appDb.rssSourceDao.deleteDefault()
|
||||||
appDb.rssSourceDao.insert(*rssSources.toTypedArray())
|
appDb.rssSourceDao.insert(*rssSources.toTypedArray())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -145,6 +145,43 @@ interface JsExtensions : JsEncodeUtils {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 使用webView获取资源url
|
||||||
|
*/
|
||||||
|
fun webViewGetSource(html: String?, url: String?, js: String?, sourceRegex: String): String? {
|
||||||
|
return runBlocking {
|
||||||
|
BackstageWebView(
|
||||||
|
url = url,
|
||||||
|
html = html,
|
||||||
|
javaScript = js,
|
||||||
|
headerMap = getSource()?.getHeaderMap(true),
|
||||||
|
tag = getSource()?.getKey(),
|
||||||
|
sourceRegex = sourceRegex
|
||||||
|
).getStrResponse().body
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 使用webView获取跳转url
|
||||||
|
*/
|
||||||
|
fun webViewGetOverrideUrl(
|
||||||
|
html: String?,
|
||||||
|
url: String?,
|
||||||
|
js: String?,
|
||||||
|
overrideUrlRegex: String
|
||||||
|
): String? {
|
||||||
|
return runBlocking {
|
||||||
|
BackstageWebView(
|
||||||
|
url = url,
|
||||||
|
html = html,
|
||||||
|
javaScript = js,
|
||||||
|
headerMap = getSource()?.getHeaderMap(true),
|
||||||
|
tag = getSource()?.getKey(),
|
||||||
|
overrideUrlRegex = overrideUrlRegex
|
||||||
|
).getStrResponse().body
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 使用内置浏览器打开链接,手动验证网站防爬
|
* 使用内置浏览器打开链接,手动验证网站防爬
|
||||||
* @param url 要打开的链接
|
* @param url 要打开的链接
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import android.os.Looper
|
|||||||
import android.util.AndroidRuntimeException
|
import android.util.AndroidRuntimeException
|
||||||
import android.webkit.CookieManager
|
import android.webkit.CookieManager
|
||||||
import android.webkit.SslErrorHandler
|
import android.webkit.SslErrorHandler
|
||||||
|
import android.webkit.WebResourceRequest
|
||||||
import android.webkit.WebSettings
|
import android.webkit.WebSettings
|
||||||
import android.webkit.WebView
|
import android.webkit.WebView
|
||||||
import android.webkit.WebViewClient
|
import android.webkit.WebViewClient
|
||||||
@@ -33,6 +34,7 @@ class BackstageWebView(
|
|||||||
private val tag: String? = null,
|
private val tag: String? = null,
|
||||||
private val headerMap: Map<String, String>? = null,
|
private val headerMap: Map<String, String>? = null,
|
||||||
private val sourceRegex: String? = null,
|
private val sourceRegex: String? = null,
|
||||||
|
private val overrideUrlRegex: String? = null,
|
||||||
private val javaScript: String? = null,
|
private val javaScript: String? = null,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
@@ -102,7 +104,7 @@ class BackstageWebView(
|
|||||||
settings.blockNetworkImage = true
|
settings.blockNetworkImage = true
|
||||||
settings.userAgentString = headerMap?.get(AppConst.UA_NAME) ?: AppConfig.userAgent
|
settings.userAgentString = headerMap?.get(AppConst.UA_NAME) ?: AppConfig.userAgent
|
||||||
settings.mixedContentMode = WebSettings.MIXED_CONTENT_ALWAYS_ALLOW
|
settings.mixedContentMode = WebSettings.MIXED_CONTENT_ALWAYS_ALLOW
|
||||||
if (sourceRegex.isNullOrEmpty()) {
|
if (sourceRegex.isNullOrBlank() && overrideUrlRegex.isNullOrBlank()) {
|
||||||
webView.webViewClient = HtmlWebViewClient()
|
webView.webViewClient = HtmlWebViewClient()
|
||||||
} else {
|
} else {
|
||||||
webView.webViewClient = SnifferWebClient()
|
webView.webViewClient = SnifferWebClient()
|
||||||
@@ -153,50 +155,84 @@ class BackstageWebView(
|
|||||||
handler?.proceed()
|
handler?.proceed()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
private inner class EvalJsRunnable(
|
||||||
|
webView: WebView,
|
||||||
|
private val url: String,
|
||||||
|
private val mJavaScript: String
|
||||||
|
) : Runnable {
|
||||||
|
var retry = 0
|
||||||
|
private val mWebView: WeakReference<WebView> = WeakReference(webView)
|
||||||
|
override fun run() {
|
||||||
|
mWebView.get()?.evaluateJavascript(mJavaScript) {
|
||||||
|
handleResult(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private inner class EvalJsRunnable(
|
private fun handleResult(result: String) = Coroutine.async {
|
||||||
webView: WebView,
|
if (result.isNotEmpty() && result != "null") {
|
||||||
private val url: String,
|
val content = StringEscapeUtils.unescapeJson(result)
|
||||||
private val mJavaScript: String
|
.replace(quoteRegex, "")
|
||||||
) : Runnable {
|
try {
|
||||||
var retry = 0
|
val response = StrResponse(url, content)
|
||||||
private val mWebView: WeakReference<WebView> = WeakReference(webView)
|
callback?.onResult(response)
|
||||||
override fun run() {
|
} catch (e: Exception) {
|
||||||
mWebView.get()?.evaluateJavascript(mJavaScript) {
|
callback?.onError(e)
|
||||||
handleResult(it)
|
}
|
||||||
|
mHandler.post {
|
||||||
|
destroy()
|
||||||
|
}
|
||||||
|
return@async
|
||||||
|
}
|
||||||
|
if (retry > 30) {
|
||||||
|
callback?.onError(NoStackTraceException("js执行超时"))
|
||||||
|
mHandler.post {
|
||||||
|
destroy()
|
||||||
|
}
|
||||||
|
return@async
|
||||||
|
}
|
||||||
|
retry++
|
||||||
|
mHandler.postDelayed(this@EvalJsRunnable, 1000)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleResult(result: String) = Coroutine.async {
|
|
||||||
if (result.isNotEmpty() && result != "null") {
|
|
||||||
val content = StringEscapeUtils.unescapeJson(result)
|
|
||||||
.replace(quoteRegex, "")
|
|
||||||
try {
|
|
||||||
val response = StrResponse(url, content)
|
|
||||||
callback?.onResult(response)
|
|
||||||
} catch (e: Exception) {
|
|
||||||
callback?.onError(e)
|
|
||||||
}
|
|
||||||
mHandler.post {
|
|
||||||
destroy()
|
|
||||||
}
|
|
||||||
return@async
|
|
||||||
}
|
|
||||||
if (retry > 30) {
|
|
||||||
callback?.onError(NoStackTraceException("js执行超时"))
|
|
||||||
mHandler.post {
|
|
||||||
destroy()
|
|
||||||
}
|
|
||||||
return@async
|
|
||||||
}
|
|
||||||
retry++
|
|
||||||
mHandler.postDelayed(this@EvalJsRunnable, 1000)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private inner class SnifferWebClient : WebViewClient() {
|
private inner class SnifferWebClient : WebViewClient() {
|
||||||
|
|
||||||
|
override fun shouldOverrideUrlLoading(
|
||||||
|
view: WebView,
|
||||||
|
request: WebResourceRequest
|
||||||
|
): Boolean {
|
||||||
|
if (shouldOverrideUrlLoading(request.url.toString())) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return super.shouldOverrideUrlLoading(view, request)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("DEPRECATION", "OVERRIDE_DEPRECATION", "KotlinRedundantDiagnosticSuppress")
|
||||||
|
override fun shouldOverrideUrlLoading(view: WebView, url: String): Boolean {
|
||||||
|
if (shouldOverrideUrlLoading(url)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return super.shouldOverrideUrlLoading(view, url)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun shouldOverrideUrlLoading(requestUrl: String): Boolean {
|
||||||
|
overrideUrlRegex?.let {
|
||||||
|
if (requestUrl.matches(it.toRegex())) {
|
||||||
|
try {
|
||||||
|
val response = StrResponse(url!!, requestUrl)
|
||||||
|
callback?.onResult(response)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
callback?.onError(e)
|
||||||
|
}
|
||||||
|
destroy()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
override fun onLoadResource(view: WebView, resUrl: String) {
|
override fun onLoadResource(view: WebView, resUrl: String) {
|
||||||
sourceRegex?.let {
|
sourceRegex?.let {
|
||||||
if (resUrl.matches(it.toRegex())) {
|
if (resUrl.matches(it.toRegex())) {
|
||||||
@@ -213,8 +249,7 @@ class BackstageWebView(
|
|||||||
|
|
||||||
override fun onPageFinished(webView: WebView, url: String) {
|
override fun onPageFinished(webView: WebView, url: String) {
|
||||||
setCookie(url)
|
setCookie(url)
|
||||||
val js = javaScript
|
if (!javaScript.isNullOrEmpty()) {
|
||||||
if (!js.isNullOrEmpty()) {
|
|
||||||
val runnable = LoadJsRunnable(webView, javaScript)
|
val runnable = LoadJsRunnable(webView, javaScript)
|
||||||
mHandler.postDelayed(runnable, 1000L)
|
mHandler.postDelayed(runnable, 1000L)
|
||||||
}
|
}
|
||||||
@@ -229,16 +264,16 @@ class BackstageWebView(
|
|||||||
handler?.proceed()
|
handler?.proceed()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
private inner class LoadJsRunnable(
|
||||||
|
webView: WebView,
|
||||||
private class LoadJsRunnable(
|
private val mJavaScript: String?
|
||||||
webView: WebView,
|
) : Runnable {
|
||||||
private val mJavaScript: String?
|
private val mWebView: WeakReference<WebView> = WeakReference(webView)
|
||||||
) : Runnable {
|
override fun run() {
|
||||||
private val mWebView: WeakReference<WebView> = WeakReference(webView)
|
mWebView.get()?.loadUrl("javascript:${mJavaScript}")
|
||||||
override fun run() {
|
}
|
||||||
mWebView.get()?.loadUrl("javascript:${mJavaScript}")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|||||||
@@ -24,7 +24,6 @@ import io.legado.app.ui.rss.favorites.RssFavoritesActivity
|
|||||||
import io.legado.app.ui.rss.read.ReadRssActivity
|
import io.legado.app.ui.rss.read.ReadRssActivity
|
||||||
import io.legado.app.ui.rss.source.edit.RssSourceEditActivity
|
import io.legado.app.ui.rss.source.edit.RssSourceEditActivity
|
||||||
import io.legado.app.ui.rss.source.manage.RssSourceActivity
|
import io.legado.app.ui.rss.source.manage.RssSourceActivity
|
||||||
import io.legado.app.ui.rss.source.manage.RssSourceViewModel
|
|
||||||
import io.legado.app.ui.rss.subscription.RuleSubActivity
|
import io.legado.app.ui.rss.subscription.RuleSubActivity
|
||||||
import io.legado.app.utils.applyTint
|
import io.legado.app.utils.applyTint
|
||||||
import io.legado.app.utils.cnCompare
|
import io.legado.app.utils.cnCompare
|
||||||
@@ -42,7 +41,7 @@ import kotlinx.coroutines.launch
|
|||||||
/**
|
/**
|
||||||
* 订阅界面
|
* 订阅界面
|
||||||
*/
|
*/
|
||||||
class RssFragment() : VMBaseFragment<RssSourceViewModel>(R.layout.fragment_rss),
|
class RssFragment() : VMBaseFragment<RssViewModel>(R.layout.fragment_rss),
|
||||||
MainFragmentInterface,
|
MainFragmentInterface,
|
||||||
RssAdapter.CallBack {
|
RssAdapter.CallBack {
|
||||||
|
|
||||||
@@ -55,7 +54,7 @@ class RssFragment() : VMBaseFragment<RssSourceViewModel>(R.layout.fragment_rss),
|
|||||||
override val position: Int? get() = arguments?.getInt("position")
|
override val position: Int? get() = arguments?.getInt("position")
|
||||||
|
|
||||||
private val binding by viewBinding(FragmentRssBinding::bind)
|
private val binding by viewBinding(FragmentRssBinding::bind)
|
||||||
override val viewModel by viewModels<RssSourceViewModel>()
|
override val viewModel by viewModels<RssViewModel>()
|
||||||
private val adapter by lazy { RssAdapter(requireContext(), this) }
|
private val adapter by lazy { RssAdapter(requireContext(), this) }
|
||||||
private val searchView: SearchView by lazy {
|
private val searchView: SearchView by lazy {
|
||||||
binding.titleBar.findViewById(R.id.search_view)
|
binding.titleBar.findViewById(R.id.search_view)
|
||||||
@@ -169,13 +168,15 @@ class RssFragment() : VMBaseFragment<RssSourceViewModel>(R.layout.fragment_rss),
|
|||||||
|
|
||||||
override fun openRss(rssSource: RssSource) {
|
override fun openRss(rssSource: RssSource) {
|
||||||
if (rssSource.singleUrl) {
|
if (rssSource.singleUrl) {
|
||||||
if (rssSource.sourceUrl.startsWith("http", true)) {
|
viewModel.getSingleUrl(rssSource) { url ->
|
||||||
startActivity<ReadRssActivity> {
|
if (url.startsWith("http", true)) {
|
||||||
putExtra("title", rssSource.sourceName)
|
startActivity<ReadRssActivity> {
|
||||||
putExtra("origin", rssSource.sourceUrl)
|
putExtra("title", rssSource.sourceName)
|
||||||
|
putExtra("origin", url)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
context?.openUrl(url)
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
context?.openUrl(rssSource.sourceUrl)
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
startActivity<RssSortActivity> {
|
startActivity<RssSortActivity> {
|
||||||
|
|||||||
@@ -0,0 +1,74 @@
|
|||||||
|
package io.legado.app.ui.main.rss
|
||||||
|
|
||||||
|
import android.app.Application
|
||||||
|
import io.legado.app.base.BaseViewModel
|
||||||
|
import io.legado.app.data.appDb
|
||||||
|
import io.legado.app.data.entities.RssSource
|
||||||
|
import io.legado.app.utils.toastOnUi
|
||||||
|
|
||||||
|
class RssViewModel(application: Application) : BaseViewModel(application) {
|
||||||
|
|
||||||
|
fun topSource(vararg sources: RssSource) {
|
||||||
|
execute {
|
||||||
|
sources.sortBy { it.customOrder }
|
||||||
|
val minOrder = appDb.rssSourceDao.minOrder - 1
|
||||||
|
val array = Array(sources.size) {
|
||||||
|
sources[it].copy(customOrder = minOrder - it)
|
||||||
|
}
|
||||||
|
appDb.rssSourceDao.update(*array)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun bottomSource(vararg sources: RssSource) {
|
||||||
|
execute {
|
||||||
|
sources.sortBy { it.customOrder }
|
||||||
|
val maxOrder = appDb.rssSourceDao.maxOrder + 1
|
||||||
|
val array = Array(sources.size) {
|
||||||
|
sources[it].copy(customOrder = maxOrder + it)
|
||||||
|
}
|
||||||
|
appDb.rssSourceDao.update(*array)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun del(vararg rssSource: RssSource) {
|
||||||
|
execute { appDb.rssSourceDao.delete(*rssSource) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun disable(rssSource: RssSource) {
|
||||||
|
execute {
|
||||||
|
rssSource.enabled = false
|
||||||
|
appDb.rssSourceDao.update(rssSource)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getSingleUrl(rssSource: RssSource, onSuccess: (url: String) -> Unit) {
|
||||||
|
execute {
|
||||||
|
val url = rssSource.sortUrl
|
||||||
|
if (!url.isNullOrBlank()) {
|
||||||
|
if (url.startsWith("<js>", false)
|
||||||
|
|| url.startsWith("@js:", false)
|
||||||
|
) {
|
||||||
|
val jsStr = if (url.startsWith("@")) {
|
||||||
|
url.substring(4)
|
||||||
|
} else {
|
||||||
|
url.substring(4, url.lastIndexOf("<"))
|
||||||
|
}
|
||||||
|
val result = rssSource.evalJS(jsStr)?.toString()
|
||||||
|
if (!result.isNullOrBlank()) {
|
||||||
|
return@execute result
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return@execute url
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rssSource.sourceUrl
|
||||||
|
}.timeout(10000)
|
||||||
|
.onSuccess {
|
||||||
|
onSuccess.invoke(it)
|
||||||
|
}.onError {
|
||||||
|
context.toastOnUi(it.localizedMessage)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user