diff --git a/app/src/main/java/io/legado/app/ui/rss/read/ReadRssActivity.kt b/app/src/main/java/io/legado/app/ui/rss/read/ReadRssActivity.kt index 20c77dbcb..56cc5fb8c 100644 --- a/app/src/main/java/io/legado/app/ui/rss/read/ReadRssActivity.kt +++ b/app/src/main/java/io/legado/app/ui/rss/read/ReadRssActivity.kt @@ -109,8 +109,8 @@ class ReadRssActivity : VMBaseActivity @SuppressLint("SetJavaScriptEnabled") private fun initWebView() { - binding.webView.webChromeClient = RssWebChromeClient() - binding.webView.webViewClient = RssWebViewClient() + binding.webView.webChromeClient = CustomWebChromeClient() + binding.webView.webViewClient = CustomWebViewClient() binding.webView.settings.apply { mixedContentMode = WebSettings.MIXED_CONTENT_ALWAYS_ALLOW domStorageEnabled = true @@ -124,8 +124,7 @@ class ReadRssActivity : VMBaseActivity hitTestResult.type == WebView.HitTestResult.SRC_IMAGE_ANCHOR_TYPE ) { hitTestResult.extra?.let { - webPic = it - saveImage() + saveImage(it) return@setOnLongClickListener true } } @@ -140,7 +139,8 @@ class ReadRssActivity : VMBaseActivity } - private fun saveImage() { + private fun saveImage(webPic: String) { + this.webPic = webPic val path = ACache.get(this@ReadRssActivity).getAsString(imagePathKey) if (path.isNullOrEmpty()) { selectSaveFolder() @@ -281,7 +281,7 @@ class ReadRssActivity : VMBaseActivity binding.webView.destroy() } - inner class RssWebChromeClient : WebChromeClient() { + inner class CustomWebChromeClient : WebChromeClient() { override fun onShowCustomView(view: View?, callback: CustomViewCallback?) { requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR binding.llView.invisible() @@ -296,7 +296,7 @@ class ReadRssActivity : VMBaseActivity } } - inner class RssWebViewClient : WebViewClient() { + inner class CustomWebViewClient : WebViewClient() { override fun shouldOverrideUrlLoading( view: WebView?, request: WebResourceRequest? diff --git a/app/src/main/java/io/legado/app/ui/web/WebViewActivity.kt b/app/src/main/java/io/legado/app/ui/web/WebViewActivity.kt new file mode 100644 index 000000000..46248d6d9 --- /dev/null +++ b/app/src/main/java/io/legado/app/ui/web/WebViewActivity.kt @@ -0,0 +1,174 @@ +package io.legado.app.ui.web + +import android.annotation.SuppressLint +import android.content.pm.ActivityInfo +import android.net.Uri +import android.os.Bundle +import android.view.View +import android.webkit.* +import androidx.activity.viewModels +import androidx.webkit.WebSettingsCompat +import androidx.webkit.WebViewFeature +import io.legado.app.R +import io.legado.app.base.VMBaseActivity +import io.legado.app.constant.AppConst +import io.legado.app.databinding.ActivityWebViewBinding +import io.legado.app.help.AppConfig +import io.legado.app.lib.dialogs.SelectItem +import io.legado.app.model.Download +import io.legado.app.ui.association.OnLineImportActivity +import io.legado.app.ui.document.HandleFileContract +import io.legado.app.utils.* +import io.legado.app.utils.viewbindingdelegate.viewBinding + +class WebViewActivity : VMBaseActivity() { + + override val binding by viewBinding(ActivityWebViewBinding::inflate) + override val viewModel by viewModels() + private val imagePathKey = "imagePath" + private var customWebViewCallback: WebChromeClient.CustomViewCallback? = null + private var webPic: String? = null + private val saveImage = registerForActivityResult(HandleFileContract()) { + it ?: return@registerForActivityResult + ACache.get(this).put(imagePathKey, it.toString()) + viewModel.saveImage(webPic, it.toString()) + } + + override fun onActivityCreated(savedInstanceState: Bundle?) { + initWebView() + } + + @SuppressLint("JavascriptInterface") + private fun initWebView() { + binding.webView.webChromeClient = CustomWebChromeClient() + binding.webView.webViewClient = CustomWebViewClient() + binding.webView.settings.apply { + mixedContentMode = WebSettings.MIXED_CONTENT_ALWAYS_ALLOW + domStorageEnabled = true + allowContentAccess = true + } + binding.webView.addJavascriptInterface(this, "app") + upWebViewTheme() + binding.webView.setOnLongClickListener { + val hitTestResult = binding.webView.hitTestResult + if (hitTestResult.type == WebView.HitTestResult.IMAGE_TYPE || + hitTestResult.type == WebView.HitTestResult.SRC_IMAGE_ANCHOR_TYPE + ) { + hitTestResult.extra?.let { + saveImage(it) + return@setOnLongClickListener true + } + } + return@setOnLongClickListener false + } + binding.webView.setDownloadListener { url, _, contentDisposition, _, _ -> + val fileName = URLUtil.guessFileName(url, contentDisposition, null) + binding.llView.longSnackbar(fileName, getString(R.string.action_download)) { + Download.start(this, url, fileName) + } + } + } + + private fun upWebViewTheme() { + if (AppConfig.isNightTheme) { + if (WebViewFeature.isFeatureSupported(WebViewFeature.FORCE_DARK_STRATEGY)) { + WebSettingsCompat.setForceDarkStrategy( + binding.webView.settings, + WebSettingsCompat.DARK_STRATEGY_PREFER_WEB_THEME_OVER_USER_AGENT_DARKENING + ) + } + if (WebViewFeature.isFeatureSupported(WebViewFeature.FORCE_DARK)) { + WebSettingsCompat.setForceDark( + binding.webView.settings, + WebSettingsCompat.FORCE_DARK_ON + ) + } else { + binding.webView + .evaluateJavascript(AppConst.darkWebViewJs, null) + } + } + } + + private fun saveImage(webPic: String) { + this.webPic = webPic + val path = ACache.get(this).getAsString(imagePathKey) + if (path.isNullOrEmpty()) { + selectSaveFolder() + } else { + viewModel.saveImage(webPic, path) + } + } + + private fun selectSaveFolder() { + val default = arrayListOf>() + val path = ACache.get(this).getAsString(imagePathKey) + if (!path.isNullOrEmpty()) { + default.add(SelectItem(path, -1)) + } + saveImage.launch { + otherActions = default + } + } + + inner class CustomWebChromeClient : WebChromeClient() { + override fun onShowCustomView(view: View?, callback: CustomViewCallback?) { + requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR + binding.llView.invisible() + binding.customWebView.addView(view) + customWebViewCallback = callback + } + + override fun onHideCustomView() { + binding.customWebView.removeAllViews() + binding.llView.visible() + requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED + } + } + + inner class CustomWebViewClient : WebViewClient() { + override fun shouldOverrideUrlLoading( + view: WebView?, + request: WebResourceRequest? + ): Boolean { + request?.let { + return shouldOverrideUrlLoading(it.url) + } + return true + } + + @Suppress("DEPRECATION") + override fun shouldOverrideUrlLoading(view: WebView?, url: String?): Boolean { + url?.let { + return shouldOverrideUrlLoading(Uri.parse(it)) + } + return true + } + + override fun onPageFinished(view: WebView?, url: String?) { + super.onPageFinished(view, url) + upWebViewTheme() + } + + private fun shouldOverrideUrlLoading(url: Uri): Boolean { + when (url.scheme) { + "http", "https" -> { + return false + } + "legado", "yuedu" -> { + startActivity { + data = url + } + return true + } + else -> { + binding.root.longSnackbar("跳转其它应用", "确认") { + openUrl(url) + } + return true + } + } + } + + } + +} \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/ui/web/WebViewModel.kt b/app/src/main/java/io/legado/app/ui/web/WebViewModel.kt new file mode 100644 index 000000000..c5b0577f0 --- /dev/null +++ b/app/src/main/java/io/legado/app/ui/web/WebViewModel.kt @@ -0,0 +1,54 @@ +package io.legado.app.ui.web + +import android.app.Application +import android.net.Uri +import android.util.Base64 +import android.webkit.URLUtil +import androidx.documentfile.provider.DocumentFile +import io.legado.app.base.BaseViewModel +import io.legado.app.constant.AppConst +import io.legado.app.help.http.newCall +import io.legado.app.help.http.okHttpClient +import io.legado.app.utils.* +import java.io.File +import java.util.* + +class WebViewModel(application: Application) : BaseViewModel(application) { + + + fun saveImage(webPic: String?, path: String) { + webPic ?: return + execute { + val fileName = "${AppConst.fileNameFormat.format(Date(System.currentTimeMillis()))}.jpg" + webData2bitmap(webPic)?.let { biteArray -> + if (path.isContentScheme()) { + val uri = Uri.parse(path) + DocumentFile.fromTreeUri(context, uri)?.let { doc -> + DocumentUtils.createFileIfNotExist(doc, fileName) + ?.writeBytes(context, biteArray) + } + } else { + val file = FileUtils.createFileIfNotExist(File(path), fileName) + file.writeBytes(biteArray) + } + } ?: throw Throwable("NULL") + }.onError { + context.toastOnUi("保存图片失败:${it.localizedMessage}") + }.onSuccess { + context.toastOnUi("保存成功") + } + } + + private suspend fun webData2bitmap(data: String): ByteArray? { + return if (URLUtil.isValidUrl(data)) { + @Suppress("BlockingMethodInNonBlockingContext") + okHttpClient.newCall { + url(data) + }.bytes() + } else { + Base64.decode(data.split(",").toTypedArray()[1], Base64.DEFAULT) + } + } + + +} \ No newline at end of file diff --git a/app/src/main/res/layout/activity_web_view.xml b/app/src/main/res/layout/activity_web_view.xml new file mode 100644 index 000000000..6eb28b4f8 --- /dev/null +++ b/app/src/main/res/layout/activity_web_view.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + \ No newline at end of file