AgentWeb icon indicating copy to clipboard operation
AgentWeb copied to clipboard

请问怎么集成到compose中使用

Open ping-ter opened this issue 1 year ago • 4 comments

ping-ter avatar Jun 07 '24 02:06 ping-ter

我也是在用compose写应用,交流交流

AdminQQZ avatar Jun 13 '24 09:06 AdminQQZ

有办法集成吗 佬

xuehao0217 avatar Jul 08 '24 10:07 xuehao0217

可以这样简单使用

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。但是要是想深入使用的话,也许还要写很多状态管理之类的。

shizheng233 avatar Nov 16 '24 11:11 shizheng233

这是我使用的

@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()
        }
    }
} 

hanklee9820 avatar Apr 14 '25 09:04 hanklee9820