AgentWeb
AgentWeb copied to clipboard
请问怎么集成到compose中使用
我也是在用compose写应用,交流交流
有办法集成吗 佬
可以这样简单使用
AndroidView(
modifier = Modifier
.padding(padding)
.fillMaxSize(),
factory = {
LinearLayout(it).apply {
layoutParams = ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT
)
}
},
update = {
AgentWeb.with(activity.context as Activity)
.setAgentWebParent(
it,
LinearLayout.LayoutParams(-1, -1)
)
.useDefaultIndicator()
.setWebViewClient(LoginClient())
.createAgentWeb()
.ready()
.go(SSPaiUrl.LOGIN_URL).apply {
agentWebSettings.webSettings.javaScriptEnabled = true
agentWebSettings.webSettings.builtInZoomControls = true
agentWebSettings.webSettings.displayZoomControls = false
agentWebSettings.webSettings.useWideViewPort = true
}
}
)
那个所谓的activity,是LocalView.current,没有试过LocalContext.current。但是要是想深入使用的话,也许还要写很多状态管理之类的。
这是我使用的
@SuppressLint("SetJavaScriptEnabled")
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun WebViewScreen(
url: String = "https://m.jd.com/",
onBackClick: (() -> Unit)? = null
) {
val TAG = "WebViewScreen"
val context = LocalContext.current
var showMenu by remember { mutableStateOf(false) }
var webTitle by remember { mutableStateOf("") }
// 使用 remember 确保 AgentWeb 只创建一次
var agentWeb = remember {
var agent: AgentWeb? = null
agent
}
// 处理返回按钮逻辑
val handleBack = {
when {
agentWeb?.webCreator?.webView?.canGoBack() == true -> {
agentWeb?.back()
true
}
else -> {
onBackClick?.invoke()
true
}
}
}
// 处理系统返回键
BackHandler(enabled = true, onBack = { handleBack() })
Scaffold(
topBar = {
TopAppBar(
title = { Text(webTitle) },
navigationIcon = {
IconButton(onClick = { handleBack() }) {
Icon(Icons.Default.Close, contentDescription = "关闭")
}
},
actions = {
// IconButton(onClick = { onBackClick?.invoke() }) {
// Icon(Icons.Default.Close, contentDescription = "关闭")
// }
IconButton(onClick = { showMenu = true }) {
Icon(Icons.Default.MoreVert, contentDescription = "更多")
}
DropdownMenu(
expanded = showMenu,
onDismissRequest = { showMenu = false }
) {
DropdownMenuItem(
text = { Text("刷新") },
onClick = {
agentWeb?.urlLoader?.reload()
showMenu = false
}
)
DropdownMenuItem(
text = { Text("复制链接") },
onClick = {
agentWeb?.webCreator?.webView?.url?.let { currentUrl ->
val clipboard = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
clipboard.setPrimaryClip(ClipData.newPlainText(null, currentUrl))
Toast.makeText(context, "已复制链接", Toast.LENGTH_SHORT).show()
}
showMenu = false
}
)
DropdownMenuItem(
text = { Text("浏览器打开") },
onClick = {
agentWeb?.webCreator?.webView?.url?.let { currentUrl ->
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(currentUrl))
context.startActivity(intent)
}
showMenu = false
}
)
}
},
colors = TopAppBarDefaults.topAppBarColors(
containerColor = MaterialTheme.colorScheme.primaryContainer,
titleContentColor = MaterialTheme.colorScheme.onPrimaryContainer,
navigationIconContentColor = MaterialTheme.colorScheme.onPrimaryContainer,
actionIconContentColor = MaterialTheme.colorScheme.onPrimaryContainer
)
)
}
) { paddingValues ->
AndroidView(
modifier = Modifier
.fillMaxSize()
.padding(paddingValues),
factory = { ctx ->
LinearLayout(ctx).apply {
layoutParams = ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT
)
}
},
update = { layout ->
if (agentWeb == null) {
agentWeb = AgentWeb.with(context as Activity)
.setAgentWebParent(
layout,
LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT
)
)
.useDefaultIndicator() //设置进度条颜色与高度,-1为默认值,高度为2,单位为dp。
.setAgentWebWebSettings(object : AbsAgentWebSettings() {
var agentWeb: AgentWeb? = null
override fun bindAgentWebSupport(agentWeb: AgentWeb?) {
this.agentWeb = agentWeb
}
override fun setDownloader(
webView: WebView?,
downloadListener: DownloadListener?
): WebListenerManager {
return super.setDownloader(webView, object : DefaultDownloadImpl(context, webView, agentWeb?.permissionInterceptor) {
override fun createResourceRequest(url: String?): ResourceRequest<*> {
return DownloadImpl.getInstance(context)
.url(url ?: "")
.quickProgress()
.setEnableIndicator(true)
.autoOpenIgnoreMD5()
.setRetry(5)
.setBlockMaxTime(100000L)
}
override fun taskEnqueue(resourceRequest: ResourceRequest<*>?) {
resourceRequest?.enqueue(object :
DownloadListenerAdapter() {
override fun onStart(
url: String,
userAgent: String,
contentDisposition: String,
mimetype: String,
contentLength: Long,
extra: Extra
) {
super.onStart(
url,
userAgent,
contentDisposition,
mimetype,
contentLength,
extra
)
}
@DownloadingListener.MainThread
override fun onProgress(
url: String,
downloaded: Long,
length: Long,
usedTime: Long
) {
super.onProgress(url, downloaded, length, usedTime)
}
override fun onResult(
throwable: Throwable,
path: Uri,
url: String,
extra: Extra
): Boolean {
return super.onResult(throwable, path, url, extra)
}
})
}
})
}
}) //设置 IAgentWebSettings
.setWebViewClient(object : WebViewClient() {
private val timer = HashMap<String, Long?>()
override fun onReceivedError(
view: WebView?,
request: WebResourceRequest?,
error: WebResourceError?
) {
super.onReceivedError(view, request, error)
}
override fun shouldOverrideUrlLoading(
view: WebView?,
request: WebResourceRequest?
): Boolean {
return super.shouldOverrideUrlLoading(view, request)
}
override fun shouldInterceptRequest(
view: WebView?,
request: WebResourceRequest?
): WebResourceResponse? {
return super.shouldInterceptRequest(view, request)
}
@Deprecated("Deprecated in Java")
override fun shouldOverrideUrlLoading(
view: WebView,
url: String
): Boolean {
Log.i(
TAG,
"view:" + view.hitTestResult.toString() + " url:" + url
)
Log.i(
TAG,
"mWebViewClient shouldOverrideUrlLoading:$url"
)
//优酷想唤起自己应用播放该视频 , 下面拦截地址返回 true 则会在应用内 H5 播放 ,禁止优酷唤起播放该视频, 如果返回 false , DefaultWebClient 会根据intent 协议处理 该地址 , 首先匹配该应用存不存在 ,如果存在 , 唤起该应用播放 , 如果不存在 , 则跳到应用市场下载该应用 .
if (url.startsWith("intent://") && url.contains("com.youku.phone")) {
return true
}
/*else if (isAlipay(view, mUrl)) //1.2.5开始不用调用该方法了 ,只要引入支付宝sdk即可 , DefaultWebClient 默认会处理相应url调起支付宝
return true;*/
return super.shouldOverrideUrlLoading(view, url)
}
override fun onPageStarted(
view: WebView?,
url: String,
favicon: Bitmap?
) {
super.onPageStarted(view, url, favicon)
timer[url] = System.currentTimeMillis()
}
override fun onPageFinished(view: WebView?, url: String) {
super.onPageFinished(view, url)
if (timer[url] != null) {
val overTime = System.currentTimeMillis()
val startTime = timer[url]
Log.i(
TAG,
" page mUrl:" + url + " used time:" + (overTime - startTime!!)
)
}
}
/*错误页回调该方法 , 如果重写了该方法, 上面传入了布局将不会显示 , 交由开发者实现,注意参数对齐。*/ /* public void onMainFrameError(AbsAgentWebUIController agentWebUIController, WebView view, int errorCode, String description, String failingUrl) {
Log.i(TAG, "AgentWebFragment onMainFrameError");
agentWebUIController.onMainFrameError(view,errorCode,description,failingUrl);
}*/
override fun onReceivedHttpError(
view: WebView?,
request: WebResourceRequest?,
errorResponse: WebResourceResponse?
) {
super.onReceivedHttpError(view, request, errorResponse)
// Log.i(TAG, "onReceivedHttpError:" + 3 + " request:" + mGson.toJson(request) + " errorResponse:" + mGson.toJson(errorResponse));
}
@SuppressLint("WebViewClientOnReceivedSslError")
override fun onReceivedSslError(
view: WebView?,
handler: SslErrorHandler,
error: SslError?
) {
handler.proceed()
super.onReceivedSslError(view, handler, error)
}
@Deprecated("Deprecated in Java")
override fun onReceivedError(
view: WebView?,
errorCode: Int,
description: String?,
failingUrl: String?
) {
super.onReceivedError(view, errorCode, description, failingUrl)
// Log.i(TAG, "onReceivedError:" + errorCode + " description:" + description + " errorResponse:" + failingUrl);
}
}) //WebViewClient , 与 WebView 使用一致 ,但是请勿获取WebView调用setWebViewClient(xx)方法了,会覆盖AgentWeb DefaultWebClient,同时相应的中间件也会失效。
.setWebChromeClient(object : WebChromeClient() {
override fun onProgressChanged(webView: WebView?, newProgress: Int) {
super.onProgressChanged(webView, newProgress)
Log.i(TAG, "onProgressChanged: $newProgress")
}
override fun onReceivedTitle(view: WebView?, title: String?) {
super.onReceivedTitle(view, title)
webTitle = title ?: ""
if (webTitle.length > 10) {
webTitle = webTitle.substring(0, 10) + "..."
}
}
}) //WebChromeClient
.setPermissionInterceptor(object : PermissionInterceptor {
override fun intercept(
url: String?,
permissions: Array<out String>?,
action: String?
): Boolean {
return false //true 该Url对应页面请求权限进行拦截 ,false 表示不拦截。
}
}) //权限拦截 2.0.0 加入。
.setSecurityType(AgentWeb.SecurityType.STRICT_CHECK) //严格模式 Android 4.2.2 以下会放弃注入对象 ,使用AgentWebView没影响。
.setAgentWebUIController(object : AgentWebUIControllerImplBase() {
override fun onShowMessage(message: String?, from: String?) {
super.onShowMessage(message, from)
}
override fun onSelectItemsPrompt(
view: WebView?,
url: String?,
items: Array<String?>?,
callback: Handler.Callback?
) {
super.onSelectItemsPrompt(view, url, items, callback) // 使用默认的UI
}
/**
* 修改文件选择的弹窗
*/
/* @Override
public void onSelectItemsPrompt(WebView view, String mUrl, String[] ways, final Handler.Callback callback) {
//super.onSelectItemsPrompt(view,mUrl,ways,callback); //这行应该注释或者删除掉
final AlertDialog mAlertDialog = new AlertDialog.Builder(mActivity)//
.setSingleChoiceItems(ways, -1, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
if (callback != null) {
Message mMessage = Message.obtain();
mMessage.what = which; //mMessage.what 必须等于ways的index
callback.handleMessage(mMessage); //最后callback一定要回调
}
}
}).setOnCancelListener(new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
dialog.dismiss();
if (callback != null) {
callback.handleMessage(Message.obtain(null, -1)); //-1表示取消 //最后callback一定要回调
}
}
}).create();
mAlertDialog.show();
}*/
}) //自定义UI AgentWeb3.0.0 加入。
// .setMainFrameErrorView(view, -1) //参数1是错误显示的布局,参数2点击刷新控件ID -1表示点击整个布局都刷新, AgentWeb 3.0.0 加入。
.useMiddlewareWebChrome(object : MiddlewareWebChromeBase() {
override fun onJsAlert(
view: WebView?,
url: String,
message: String?,
result: JsResult?
): Boolean {
Log.i("Info", "onJsAlert:$url")
return super.onJsAlert(view, url, message, result)
}
override fun onProgressChanged(view: WebView?, newProgress: Int) {
super.onProgressChanged(view, newProgress)
Log.i("Info", "onProgressChanged:")
}
})//设置WebChromeClient中间件,支持多个WebChromeClient,AgentWeb 3.0.0 加入。
.additionalHttpHeader(url, "cookie", "41bc7ddf04a26b91803f6b11817a5a1c")
.useMiddlewareWebClient(object : MiddlewareWebClientBase() {
var count: Int = 1
override fun shouldOverrideUrlLoading(
view: WebView?,
request: WebResourceRequest
): Boolean {
Log.i(
"Info",
"MiddlewareWebViewClient -- > shouldOverrideUrlLoading:" + request.url.toString() + " c:" + (count++)
)
/*if (url.startsWith("agentweb")) { // 拦截 url,不执行 DefaultWebClient#shouldOverrideUrlLoading
Log.i(TAG, "agentweb scheme ~");
return true;
}*/
if (super.shouldOverrideUrlLoading(
view,
url
)
) { // 执行 DefaultWebClient#shouldOverrideUrlLoading
return true
}
// do you work
return false
}
@Deprecated("Deprecated in Java")
override fun shouldOverrideUrlLoading(
view: WebView?,
url: String
): Boolean {
Log.i(
"Info",
"MiddlewareWebViewClient -- > shouldOverrideUrlLoading:" + url + " c:" + (count++)
)
return super.shouldOverrideUrlLoading(view, url)
}
override fun onReceivedError(
view: WebView?,
request: WebResourceRequest,
error: WebResourceError
) {
if (request.isForMainFrame && error.errorCode != -1) {
super.onReceivedError(view, request, error)
}
}
})//设置WebViewClient中间件,支持多个WebViewClient, AgentWeb 3.0.0 加入。
.setOpenOtherPageWays(DefaultWebClient.OpenOtherPageWays.ASK)//打开其他页面时,弹窗质询用户前往其他应用 AgentWeb 3.0.0 加入。
.interceptUnkownUrl() //拦截找不到相关页面的Url AgentWeb 3.0.0 加入。
.createAgentWeb()
.ready()
.go(url)
agentWeb?.webCreator?.webView?.overScrollMode = WebView.OVER_SCROLL_ALWAYS
// 配置 WebView 设置
agentWeb?.agentWebSettings?.webSettings?.apply {
javaScriptEnabled = true
useWideViewPort = true
loadWithOverviewMode = true
builtInZoomControls = true
displayZoomControls = true
}
}
}
)
}
// 在组件销毁时清理 AgentWeb
DisposableEffect(Unit) {
onDispose {
agentWeb?.webLifeCycle?.onDestroy()
}
}
}