Show help banner when Cloudflare captcha page is shown in WebView

This commit is contained in:
arkon 2023-07-28 23:09:52 -04:00
parent cdc1c5efa3
commit 6d69caf59e
4 changed files with 92 additions and 56 deletions

View File

@ -4,8 +4,9 @@ import android.content.pm.ApplicationInfo
import android.graphics.Bitmap
import android.webkit.WebResourceRequest
import android.webkit.WebView
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
@ -17,9 +18,11 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalUriHandler
import androidx.compose.ui.res.stringResource
import com.google.accompanist.web.AccompanistWebViewClient
import com.google.accompanist.web.LoadingState
@ -28,9 +31,12 @@ import com.google.accompanist.web.rememberWebViewNavigator
import com.google.accompanist.web.rememberWebViewState
import eu.kanade.presentation.components.AppBar
import eu.kanade.presentation.components.AppBarActions
import eu.kanade.presentation.components.WarningBanner
import eu.kanade.tachiyomi.BuildConfig
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.util.system.getHtml
import eu.kanade.tachiyomi.util.system.setDefaultSettings
import kotlinx.coroutines.launch
import tachiyomi.presentation.core.components.material.Scaffold
@Composable
@ -46,7 +52,53 @@ fun WebViewScreenContent(
) {
val state = rememberWebViewState(url = url, additionalHttpHeaders = headers)
val navigator = rememberWebViewNavigator()
val uriHandler = LocalUriHandler.current
val scope = rememberCoroutineScope()
var currentUrl by remember { mutableStateOf(url) }
var showCloudflareHelp by remember { mutableStateOf(false) }
val webClient = remember {
object : AccompanistWebViewClient() {
override fun onPageStarted(view: WebView, url: String?, favicon: Bitmap?) {
super.onPageStarted(view, url, favicon)
url?.let {
currentUrl = it
onUrlChange(it)
}
}
override fun onPageFinished(view: WebView, url: String?) {
super.onPageFinished(view, url)
scope.launch {
val html = view.getHtml()
showCloudflareHelp = "Checking if the site connection is secure" in html
}
}
override fun doUpdateVisitedHistory(
view: WebView,
url: String?,
isReload: Boolean,
) {
super.doUpdateVisitedHistory(view, url, isReload)
url?.let {
currentUrl = it
onUrlChange(it)
}
}
override fun shouldOverrideUrlLoading(
view: WebView?,
request: WebResourceRequest?,
): Boolean {
request?.let {
view?.loadUrl(it.url.toString(), headers)
}
return super.shouldOverrideUrlLoading(view, request)
}
}
}
Scaffold(
topBar = {
@ -116,45 +168,21 @@ fun WebViewScreenContent(
}
},
) { contentPadding ->
val webClient = remember {
object : AccompanistWebViewClient() {
override fun onPageStarted(view: WebView, url: String?, favicon: Bitmap?) {
super.onPageStarted(view, url, favicon)
url?.let {
currentUrl = it
onUrlChange(it)
}
}
override fun doUpdateVisitedHistory(
view: WebView,
url: String?,
isReload: Boolean,
Column(
modifier = Modifier.padding(contentPadding),
) {
super.doUpdateVisitedHistory(view, url, isReload)
url?.let {
currentUrl = it
onUrlChange(it)
}
}
override fun shouldOverrideUrlLoading(
view: WebView?,
request: WebResourceRequest?,
): Boolean {
request?.let {
view?.loadUrl(it.url.toString(), headers)
}
return super.shouldOverrideUrlLoading(view, request)
}
}
if (showCloudflareHelp) {
WarningBanner(
textRes = R.string.information_cloudflare_help,
modifier = Modifier.clickable {
uriHandler.openUri("https://tachiyomi.org/help/guides/troubleshooting/#solving-cloudflare-issues")
},
)
}
WebView(
state = state,
modifier = Modifier
.padding(contentPadding)
.fillMaxSize(),
modifier = Modifier.weight(1f),
navigator = navigator,
onCreated = { webView ->
webView.setDefaultSettings()
@ -173,4 +201,5 @@ fun WebViewScreenContent(
client = webClient,
)
}
}
}

View File

@ -44,7 +44,7 @@ class CloudflareInterceptor(
// Because OkHttp's enqueue only handles IOExceptions, wrap the exception so that
// we don't crash the entire app
catch (e: CloudflareBypassException) {
throw IOException(context.getString(R.string.information_cloudflare_bypass_failure))
throw IOException(context.getString(R.string.information_cloudflare_bypass_failure), e)
} catch (e: Exception) {
throw IOException(e)
}

View File

@ -6,8 +6,10 @@ import android.content.pm.PackageManager
import android.webkit.CookieManager
import android.webkit.WebSettings
import android.webkit.WebView
import kotlinx.coroutines.suspendCancellableCoroutine
import logcat.LogPriority
import tachiyomi.core.util.system.logcat
import kotlin.coroutines.resume
object WebViewUtil {
const val SPOOF_PACKAGE_NAME = "org.chromium.chrome"
@ -32,6 +34,10 @@ fun WebView.isOutdated(): Boolean {
return getWebViewMajorVersion() < WebViewUtil.MINIMUM_WEBVIEW_VERSION
}
suspend fun WebView.getHtml(): String = suspendCancellableCoroutine {
evaluateJavascript("document.documentElement.outerHTML") { html -> it.resume(html) }
}
@SuppressLint("SetJavaScriptEnabled")
fun WebView.setDefaultSettings() {
with(settings) {

View File

@ -923,6 +923,7 @@
<string name="information_empty_category">You have no categories. Tap the plus button to create one for organizing your library.</string>
<string name="information_empty_category_dialog">You don\'t have any categories yet.</string>
<string name="information_cloudflare_bypass_failure">Failed to bypass Cloudflare</string>
<string name="information_cloudflare_help">Tap here for help with Cloudflare</string>
<string name="information_required_plain">*required</string>
<!-- Do not translate "WebView" -->
<string name="information_webview_required">WebView is required for Tachiyomi</string>