pull/1352/head
parent
fbee6a9dd3
commit
c2ae61b71c
@ -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<ActivityWebViewBinding, WebViewModel>() { |
||||
|
||||
override val binding by viewBinding(ActivityWebViewBinding::inflate) |
||||
override val viewModel by viewModels<WebViewModel>() |
||||
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<SelectItem<Int>>() |
||||
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<OnLineImportActivity> { |
||||
data = url |
||||
} |
||||
return true |
||||
} |
||||
else -> { |
||||
binding.root.longSnackbar("跳转其它应用", "确认") { |
||||
openUrl(url) |
||||
} |
||||
return true |
||||
} |
||||
} |
||||
} |
||||
|
||||
} |
||||
|
||||
} |
@ -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) |
||||
} |
||||
} |
||||
|
||||
|
||||
} |
@ -0,0 +1,32 @@ |
||||
<?xml version="1.0" encoding="utf-8"?> |
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" |
||||
android:layout_width="match_parent" |
||||
android:layout_height="match_parent" |
||||
xmlns:app="http://schemas.android.com/apk/res-auto" |
||||
android:orientation="vertical"> |
||||
|
||||
<LinearLayout |
||||
android:id="@+id/ll_view" |
||||
android:layout_width="match_parent" |
||||
android:layout_height="match_parent" |
||||
android:orientation="vertical"> |
||||
|
||||
<io.legado.app.ui.widget.TitleBar |
||||
android:id="@+id/title_bar" |
||||
android:layout_width="match_parent" |
||||
android:layout_height="wrap_content" |
||||
app:fitStatusBar="false" /> |
||||
|
||||
<io.legado.app.ui.rss.read.VisibleWebView |
||||
android:id="@+id/web_view" |
||||
android:layout_width="match_parent" |
||||
android:layout_height="match_parent" /> |
||||
|
||||
</LinearLayout> |
||||
|
||||
<FrameLayout |
||||
android:id="@+id/custom_web_view" |
||||
android:layout_width="match_parent" |
||||
android:layout_height="match_parent" /> |
||||
|
||||
</FrameLayout> |
Loading…
Reference in new issue