[
  {
    "path": ".github/FUNDING.yml",
    "content": "# These are supported funding model platforms\n\ngithub: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]\npatreon: # Replace with a single Patreon username\nopen_collective: # Replace with a single Open Collective username\nko_fi: # Replace with a single Ko-fi username\ntidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel\ncustom: # Replace with a single custom sponsorship URL\n"
  },
  {
    "path": ".gitignore",
    "content": "*.iml\n.gradle\n.idea\n/local.properties\n.DS_Store\n/build\n/captures\n.externalNativeBuild\n\n# Built application files\n# *.apk\n*.ap_\n\n# Files for the ART/Dalvik VM\n*.dex\n\n# Java class files\n*.class\n\n# Generated files\nbin/\ngen/\nout/\n\n# Gradle files\n.gradle/\nbuild/\n\n# Local configuration file (sdk path, etc)\nlocal.properties\n\n# Proguard folder generated by Eclipse\nproguard/\n\n# Log Files\n*.log\n\n# Android Studio Navigation editor temp files\n.navigation/\n\n# Android Studio captures folder\ncaptures/\n\n# Intellij\n*.iml\n.idea/workspace.xml\n\nlocal.properties\n# Keystore files\n#*.jks\napp/keystore/\n#sample/keystore/\nlibrary/src/main/res/mipmap-xxhdpi/\nsample/sampledata/\nnode_modules\npackage-lock.json\npackage.json\n\n"
  },
  {
    "path": "LICENSE",
    "content": "                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares,  or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      onResult of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"{}\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright {yyyy} {name of copyright owner}\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n"
  },
  {
    "path": "README.md",
    "content": "\n<div style=\"display: flex;flex-direction: row;justify-content: center\" width=\"100%\">\n      <img src=\"./img/logo.png\"></img>\n</div>\n\n## AgentWeb 介绍\n\nAgentWeb 是一个基于的 Android WebView ，极度容易使用以及功能强大的库，提供了 Android WebView 一系列的问题解决方案 ，并且轻量和极度灵活，详细使用请参照上面的 Sample 。\n\t\n\n## Gradle 引入\n\n```groovy\nallprojects {\n  repositories {\n    mavenCentral()\n    maven { url 'https://jitpack.io' }\n  }\n}\n```\n\n\n\n* Androidx\n\n   ```groovy\n    implementation 'io.github.justson:agentweb-core:v5.1.1-androidx' \n    implementation 'io.github.justson:agentweb-filechooser:v5.1.1-androidx' // (可选)\n    implementation 'com.github.Justson:Downloader:v5.0.4-androidx' // (可选)\n   \n   ```\n\n\n## 相关\n* [flying-pigeon跨进程IPC组件](https://github.com/Justson/flying-pigeon)\n* [AgentWebX5](https://github.com/Justson/AgentWebX5)\n* [WebView 进度条](https://github.com/Justson/CoolIndicator)\n* [Downloader 一个轻量的文件下载器](https://github.com/Justson/Downloader)\n\n\t\n\n\n## 注意事项\n* 支付宝使用需要引入支付宝SDK ，并在项目中依赖 ， 微信支付不需要做任何操作。\n* AgentWeb 内部使用了 `AlertDialog` 需要依赖 `AppCompat` 主题 。 \n* `setAgentWebParent` 不支持  `ConstraintLayout` 。\n* `mAgentWeb.getWebLifeCycle().onPause();`会暂停应用内所有`WebView` 。\n* `minSdkVersion` 低于等于16以下自定义`WebView`请注意与 `JS` 之间通信安全。\n\n\n\n\n## 文档帮助\n* [Wiki](https://github.com/Justson/AgentWeb/wiki)(不全)\n* `Sample`(推荐，详细) \n* [更新日志](./releasenote.md)\n\n\n\n## 有问题或者有更好的建议\n* [![QQ0Group][qq0groupsvg]][qq0group]\n* 欢迎提 [Issues](https://github.com/Justson/AgentWeb/issues)\n\n\n## 赞赏\n开源不易，你的支持是我更新的动力。\n\n<a href=\"img/alipay.jpg\"><img src=\"img/alipay.jpg\" width=\"30%\"/></a> <a href=\"img/wechat_pay.jpg\"><img src=\"img/wechat_pay.jpg\" width=\"30%\"/></a> <a href=\"img/alipay.jpg\"><img src=\"img/alipay.jpg\" width=\"30%\"/></a>\n\n\n[licensesvg]: https://img.shields.io/badge/License-Apache--2.0-brightgreen.svg\n[license]: https://github.com/Justson/AgentWeb/blob/master/LICENSE\n\n[qq0groupsvg]: https://img.shields.io/badge/QQ群-599471474-fba7f9.svg\n[qq0group]: http://qm.qq.com/cgi-bin/qm/qr?k=KpyfInzI2nr-Lh4StG0oh68GpbcD0vMG\n\n \n\n[![License][licensesvg]][license]\n\n## License \n```\nCopyright (C)  Justson(https://github.com/Justson/AgentWeb)\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n     http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n```\n\n​\t\n\n​\t  \n\n\n\n"
  },
  {
    "path": "agentweb-core/.gitignore",
    "content": "/build\n"
  },
  {
    "path": "agentweb-core/build.gradle",
    "content": "apply plugin: 'com.android.library'\napply plugin: 'maven-publish'\n\nandroid {\n    compileSdk COMPILE_SDK_VERSION.toInteger()\n\n\n    defaultConfig {\n        minSdkVersion 14\n        targetSdkVersion TARGET_SDK_VERSION.toInteger()\n        namespace 'com.just.agentweb'\n        versionCode 3\n        versionName VERSION_NAME\n        testInstrumentationRunner \"android.support.test.runner.AndroidJUnitRunner\"\n        testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'\n\n    }\n\n    buildTypes {\n        release {\n            minifyEnabled true\n            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'\n        }\n    }\n    lintOptions {\n        abortOnError false\n    }\n    repositories {\n        flatDir {\n            dirs 'libs', 'providedLibs'\n        }\n    }\n    compileOptions {\n        sourceCompatibility JavaVersion.VERSION_1_8\n        targetCompatibility JavaVersion.VERSION_1_8\n    }\n//   defaultPublishConfig \"debug\"\n}\n\ndependencies {\n    compileOnly fileTree(include: ['*.jar'], dir: 'libs')\n    androidTestImplementation('androidx.test.espresso:espresso-core:3.1.0', {\n        exclude group: 'com.android.support', module: 'support-annotations'\n    })\n    testImplementation 'junit:junit:4.12'\n    compileOnly 'com.github.Justson:Downloader:v5.0.4-androidx'\n    compileOnly 'com.google.android.material:material:1.0.0'\n    compileOnly 'androidx.legacy:legacy-support-v4:1.0.0'\n    compileOnly fileTree(include: ['*.jar'], dir: 'providedLibs')\n}\n\npublishing {\n    publications {\n        // Creates a Maven publication called \"release\".\n        release(MavenPublication) {\n            groupId = 'com.github.Justson.AgentWeb'\n            artifactId = 'agentweb-core'\n            version = 'v5.0.7-androidx'\n        }\n    }\n}"
  },
  {
    "path": "agentweb-core/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# By default, the flags in this file are appended to flags specified\n# in /Users/cenxiaozhong/Library/Android/sdk/tools/proguard/proguard-android.txt\n# You can edit the include path and order by changing the proguardFiles\n# directive in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# Add any project specific keep options here:\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.create.javascript.interface.for.webview {\n#   public *;\n#}\n\n# Uncomment this to preserve the line number information for\n# debugging stack traces.\n#-keepattributes SourceFile,LineNumberTable\n\n# If you keep the line number information, uncomment this to\n# hide the original source file name.\n#-renamesourcefileattribute SourceFile\n-keep class com.just.agentweb.** {\n    *;\n}\n-dontwarn com.just.agentweb.**"
  },
  {
    "path": "agentweb-core/src/androidTest/java/com/just/agentweb/ExampleInstrumentedTest.java",
    "content": "package com.just.agentweb;\n\nimport android.content.Context;\nimport androidx.test.platform.app.InstrumentationRegistry;\nimport androidx.test.ext.junit.runners.AndroidJUnit4;\n\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\n\nimport static org.junit.Assert.*;\n\n/**\n * Instrumentation test, which will execute on an Android device.\n *\n * @see <a href=\"http://d.android.com/tools/testing\">Testing documentation</a>\n */\n@RunWith(AndroidJUnit4.class)\npublic class ExampleInstrumentedTest {\n    @Test\n    public void useAppContext() throws Exception {\n        // Context of the app under test.\n        Context appContext = InstrumentationRegistry.getTargetContext();\n\n        assertEquals(\"com.just.library.test\", appContext.getPackageName());\n    }\n}\n"
  },
  {
    "path": "agentweb-core/src/main/AndroidManifest.xml",
    "content": "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n    <application>\n        <provider\n            android:name=\"com.just.agentweb.AgentWebFileProvider\"\n            android:authorities=\"${applicationId}.AgentWebFileProvider\"\n            android:exported=\"false\"\n            android:grantUriPermissions=\"true\">\n            <meta-data\n                android:name=\"android.support.FILE_PROVIDER_PATHS\"\n                android:resource=\"@xml/web_files_public\"/>\n        </provider>\n\n    </application>\n\n</manifest>\n"
  },
  {
    "path": "agentweb-core/src/main/java/com/just/agentweb/AbsAgentWebSettings.java",
    "content": "/*\r\n * Copyright (C)  Justson(https://github.com/Justson/AgentWeb)\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n *      http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n\r\npackage com.just.agentweb;\r\n\r\n\r\nimport android.os.Build;\r\nimport android.view.View;\r\nimport android.webkit.DownloadListener;\r\nimport android.webkit.WebChromeClient;\r\nimport android.webkit.WebSettings;\r\nimport android.webkit.WebView;\r\nimport android.webkit.WebViewClient;\r\n\r\n/**\r\n * @author cenxiaozhong\r\n * @update 4.0.0 ,WebDefaultSettingsManager rename to AbsAgentWebSettings\r\n * @since 1.0.0\r\n */\r\n\r\npublic abstract class AbsAgentWebSettings implements IAgentWebSettings, WebListenerManager {\r\n    private WebSettings mWebSettings;\r\n    private static final String TAG = AbsAgentWebSettings.class.getSimpleName();\r\n    public static final String USERAGENT_UC = \" UCBrowser/11.6.4.950 \";\r\n    public static final String USERAGENT_QQ_BROWSER = \" MQQBrowser/8.0 \";\r\n    public static final String USERAGENT_AGENTWEB = \" \" + AgentWebConfig.AGENTWEB_VERSION + \" \";\r\n    protected AgentWeb mAgentWeb;\r\n\r\n    public static AbsAgentWebSettings getInstance() {\r\n        return new AgentWebSettingsImpl();\r\n    }\r\n\r\n    public AbsAgentWebSettings() {\r\n    }\r\n\r\n    final void bindAgentWeb(AgentWeb agentWeb) {\r\n        this.mAgentWeb = agentWeb;\r\n        this.bindAgentWebSupport(agentWeb);\r\n    }\r\n\r\n    protected abstract void bindAgentWebSupport(AgentWeb agentWeb);\r\n\r\n    @Override\r\n    public IAgentWebSettings toSetting(WebView webView) {\r\n        settings(webView);\r\n        return this;\r\n    }\r\n\r\n    private void settings(WebView webView) {\r\n        mWebSettings = webView.getSettings();\r\n        mWebSettings.setJavaScriptEnabled(true);\r\n        mWebSettings.setSupportZoom(true);\r\n        mWebSettings.setBuiltInZoomControls(false);\r\n        mWebSettings.setSavePassword(false);\r\n        if (AgentWebUtils.checkNetwork(webView.getContext().getApplicationContext())) {\r\n            //根据cache-control获取数据。\r\n            mWebSettings.setCacheMode(WebSettings.LOAD_DEFAULT);\r\n        } else {\r\n            //没网，则从本地获取，即离线加载\r\n            mWebSettings.setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);\r\n        }\r\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {\r\n            //适配5.0不允许http和https混合使用情况\r\n            mWebSettings.setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);\r\n            webView.setLayerType(View.LAYER_TYPE_HARDWARE, null);\r\n        } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {\r\n            webView.setLayerType(View.LAYER_TYPE_HARDWARE, null);\r\n        } else if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {\r\n            webView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);\r\n        }\r\n        mWebSettings.setTextZoom(100);\r\n        mWebSettings.setDatabaseEnabled(true);\r\n        mWebSettings.setLoadsImagesAutomatically(true);\r\n        mWebSettings.setSupportMultipleWindows(false);\r\n        // 是否阻塞加载网络图片  协议http or https\r\n        mWebSettings.setBlockNetworkImage(false);\r\n        // 允许加载本地文件html  file协议\r\n        mWebSettings.setAllowFileAccess(true);\r\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {\r\n            // 通过 file url 加载的 Javascript 读取其他的本地文件 .建议关闭\r\n            mWebSettings.setAllowFileAccessFromFileURLs(false);\r\n            // 允许通过 file url 加载的 Javascript 可以访问其他的源，包括其他的文件和 http，https 等其他的源\r\n            mWebSettings.setAllowUniversalAccessFromFileURLs(false);\r\n        }\r\n        mWebSettings.setJavaScriptCanOpenWindowsAutomatically(true);\r\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {\r\n\r\n            mWebSettings.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.SINGLE_COLUMN);\r\n        } else {\r\n            mWebSettings.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.NORMAL);\r\n        }\r\n        mWebSettings.setLoadWithOverviewMode(false);\r\n        mWebSettings.setUseWideViewPort(false);\r\n        mWebSettings.setDomStorageEnabled(true);\r\n        mWebSettings.setNeedInitialFocus(true);\r\n        mWebSettings.setDefaultTextEncodingName(\"utf-8\");//设置编码格式\r\n        mWebSettings.setDefaultFontSize(16);\r\n        mWebSettings.setMinimumFontSize(12);//设置 WebView 支持的最小字体大小，默认为 8\r\n        mWebSettings.setGeolocationEnabled(true);\r\n        String dir = AgentWebConfig.getCachePath(webView.getContext());\r\n        LogUtils.i(TAG, \"dir:\" + dir + \"   appcache:\" + AgentWebConfig.getCachePath(webView.getContext()));\r\n        //设置数据库路径  api19 已经废弃,这里只针对 webkit 起作用\r\n        mWebSettings.setGeolocationDatabasePath(dir);\r\n        mWebSettings.setDatabasePath(dir);\r\n        mWebSettings.setUserAgentString(getWebSettings()\r\n                .getUserAgentString()\r\n                .concat(USERAGENT_AGENTWEB)\r\n                .concat(USERAGENT_UC)\r\n        );\r\n        LogUtils.i(TAG, \"UserAgentString : \" + mWebSettings.getUserAgentString());\r\n    }\r\n\r\n    @Override\r\n    public WebSettings getWebSettings() {\r\n        return mWebSettings;\r\n    }\r\n\r\n    @Override\r\n    public WebListenerManager setWebChromeClient(WebView webview, WebChromeClient webChromeClient) {\r\n        webview.setWebChromeClient(webChromeClient);\r\n        return this;\r\n    }\r\n\r\n    @Override\r\n    public WebListenerManager setWebViewClient(WebView webView, WebViewClient webViewClient) {\r\n        webView.setWebViewClient(webViewClient);\r\n        return this;\r\n    }\r\n\r\n    @Override\r\n    public WebListenerManager setDownloader(WebView webView, DownloadListener downloadListener) {\r\n        webView.setDownloadListener(downloadListener);\r\n        return this;\r\n    }\r\n\r\n}\r\n"
  },
  {
    "path": "agentweb-core/src/main/java/com/just/agentweb/AbsAgentWebUIController.java",
    "content": "/*\n * Copyright (C)  Justson(https://github.com/Justson/AgentWeb)\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.just.agentweb;\n\nimport android.app.Activity;\nimport android.app.Dialog;\nimport android.net.http.SslError;\nimport android.os.Handler;\nimport android.webkit.JsPromptResult;\nimport android.webkit.JsResult;\nimport android.webkit.PermissionRequest;\nimport android.webkit.SslErrorHandler;\nimport android.webkit.WebView;\n\n\n/**\n * 该类统一控制了与用户交互的界面\n *\n * @author cenxiaozhong\n * @since 3.0.0\n */\npublic abstract class AbsAgentWebUIController {\n\n    public static boolean HAS_DESIGN_LIB = false;\n    private Activity mActivity;\n    private WebParentLayout mWebParentLayout;\n    private volatile boolean mIsBindWebParent = false;\n    protected AbsAgentWebUIController mAgentWebUIControllerDelegate;\n    protected String TAG = this.getClass().getSimpleName();\n\n    static {\n        try {\n            Class.forName(\"com.google.android.material.snackbar.Snackbar\");\n            Class.forName(\"com.google.android.material.bottomsheet.BottomSheetDialog\");\n            HAS_DESIGN_LIB = true;\n        } catch (Throwable ignore) {\n            HAS_DESIGN_LIB = false;\n            if (LogUtils.isDebug()) {\n                ignore.printStackTrace();\n            }\n        }\n\t}\n\n    protected AbsAgentWebUIController create() {\n        return HAS_DESIGN_LIB ? new DefaultDesignUIController() : new DefaultUIController();\n    }\n\n    protected AbsAgentWebUIController getDelegate() {\n        AbsAgentWebUIController mAgentWebUIController = this.mAgentWebUIControllerDelegate;\n        if (mAgentWebUIController == null) {\n            this.mAgentWebUIControllerDelegate = mAgentWebUIController = create();\n        }\n        return mAgentWebUIController;\n    }\n\n    final synchronized void bindWebParent(WebParentLayout webParentLayout, Activity activity) {\n        if (!mIsBindWebParent) {\n            mIsBindWebParent = true;\n            this.mWebParentLayout = webParentLayout;\n            this.mActivity = activity;\n            bindSupportWebParent(webParentLayout, activity);\n        }\n    }\n\n    protected void toDismissDialog(Dialog dialog) {\n        if (dialog != null && dialog.isShowing()) {\n            dialog.dismiss();\n        }\n    }\n\n    protected void toShowDialog(Dialog dialog) {\n        if (dialog != null && !dialog.isShowing()) {\n            dialog.show();\n        }\n    }\n\n    protected abstract void bindSupportWebParent(WebParentLayout webParentLayout, Activity activity);\n\n    /**\n     * WebChromeClient#onJsAlert\n     *\n     * @param view\n     * @param url\n     * @param message\n     */\n    public abstract void onJsAlert(WebView view, String url, String message);\n\n    /**\n     * 咨询用户是否前往其他页面\n     *\n     * @param view\n     * @param url\n     * @param callback\n     */\n    public abstract void onOpenPagePrompt(WebView view, String url, Handler.Callback callback);\n\n    /**\n     * WebChromeClient#onJsConfirm\n     *\n     * @param view\n     * @param url\n     * @param message\n     * @param jsResult\n     */\n    public abstract void onJsConfirm(WebView view, String url, String message, JsResult jsResult);\n\n    public abstract void onSelectItemsPrompt(WebView view, String url, String[] ways, Handler.Callback callback);\n\n    /**\n     * 强制下载弹窗\n     *\n     * @param url      当前下载地址。\n     * @param callback 用户操作回调回调\n     */\n    public abstract void onForceDownloadAlert(String url, Handler.Callback callback);\n\n    /**\n     * WebChromeClient#onJsPrompt\n     *\n     * @param view\n     * @param url\n     * @param message\n     * @param defaultValue\n     * @param jsPromptResult\n     */\n    public abstract void onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult jsPromptResult);\n\n    /**\n     * 显示错误页\n     *\n     * @param view\n     * @param errorCode\n     * @param description\n     * @param failingUrl\n     */\n    public abstract void onMainFrameError(WebView view, int errorCode, String description, String failingUrl);\n\n    /**\n     * 隐藏错误页\n     */\n    public abstract void onShowMainFrame();\n\n    /**\n     * 正在加载...\n     *\n     * @param msg\n     */\n    public abstract void onLoading(String msg);\n\n    /**\n     * 取消正在加载...\n     */\n    public abstract void onCancelLoading();\n\n    /**\n     * @param message 消息\n     * @param intent  说明message的来源，意图\n     */\n    public abstract void onShowMessage(String message, String intent);\n\n    /**\n     * 当权限被拒回调该方法\n     *\n     * @param permissions\n     * @param permissionType\n     * @param action\n     */\n    public abstract void onPermissionsDeny(String[] permissions, String permissionType, String action);\n\n\t/**\n\t *\n\t * @param view\n\t * @param handler\n\t * @param error\n\t */\n\tpublic abstract void onShowSslCertificateErrorDialog(WebView view, SslErrorHandler handler, SslError error);\n\n\t/**\n\t * 权限请求\n\t * @param request\n\t */\n\tpublic abstract void onPermissionRequest(PermissionRequest request);\n}\n"
  },
  {
    "path": "agentweb-core/src/main/java/com/just/agentweb/Action.java",
    "content": "/*\n * Copyright (C)  Justson(https://github.com/Justson/AgentWeb)\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.just.agentweb;\n\nimport android.content.Intent;\nimport android.net.Uri;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\n\n\n/**\n * @author cenxiaozhong\n * @since 2.0.0\n */\npublic final class Action {\n\n    public transient static final int ACTION_PERMISSION = 1;\n    public transient static final int ACTION_FILE = 2;\n    public transient static final int ACTION_CAMERA = 3;\n    public transient static final int ACTION_VIDEO = 4;\n    private ArrayList<String> mPermissions = new ArrayList<>();\n    private int mAction;\n    private int mFromIntention;\n    private Intent mIntent;\n    private Uri mUri;\n    private AgentActionFragment.RationaleListener mRationaleListener;\n    private AgentActionFragment.PermissionListener mPermissionListener;\n    private AgentActionFragment.ChooserListener mChooserListener;\n\n    public Action() {\n    }\n\n    public ArrayList<String> getPermissions() {\n        return mPermissions;\n    }\n\n    public void setPermissions(ArrayList<String> permissions) {\n        this.mPermissions = permissions;\n    }\n\n    public void setPermissions(String[] permissions) {\n        this.mPermissions = new ArrayList<>(Arrays.asList(permissions));\n    }\n\n    public int getAction() {\n        return mAction;\n    }\n\n    public void setAction(int action) {\n        this.mAction = action;\n    }\n\n\n    public int getFromIntention() {\n        return mFromIntention;\n    }\n\n    public static Action createPermissionsAction(String[] permissions) {\n        Action mAction = new Action();\n        mAction.setAction(Action.ACTION_PERMISSION);\n        List<String> mList = Arrays.asList(permissions);\n        mAction.setPermissions(new ArrayList<String>(mList));\n        return mAction;\n    }\n\n    public Action setFromIntention(int fromIntention) {\n        this.mFromIntention = fromIntention;\n        return this;\n    }\n\n    public AgentActionFragment.RationaleListener getRationaleListener() {\n        return mRationaleListener;\n    }\n\n    public void setRationaleListener(AgentActionFragment.RationaleListener rationaleListener) {\n        mRationaleListener = rationaleListener;\n    }\n\n    public AgentActionFragment.PermissionListener getPermissionListener() {\n        return mPermissionListener;\n    }\n\n    public void setPermissionListener(AgentActionFragment.PermissionListener permissionListener) {\n        mPermissionListener = permissionListener;\n    }\n\n    public AgentActionFragment.ChooserListener getChooserListener() {\n        return mChooserListener;\n    }\n\n    public void setChooserListener(AgentActionFragment.ChooserListener chooserListener) {\n        mChooserListener = chooserListener;\n    }\n\n    public Intent getIntent() {\n        return mIntent;\n    }\n\n    public Uri getUri() {\n        return mUri;\n    }\n\n    public void setUri(Uri uri) {\n        mUri = uri;\n    }\n\n    public void setIntent(Intent intent) {\n        mIntent = intent;\n    }\n}\n"
  },
  {
    "path": "agentweb-core/src/main/java/com/just/agentweb/AgentActionFragment.java",
    "content": "\n/*\n * Copyright (C)  Justson(https://github.com/Justson/AgentWeb)\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.just.agentweb;\n\nimport static android.provider.MediaStore.EXTRA_OUTPUT;\n\nimport android.app.Activity;\nimport android.content.Intent;\nimport android.net.Uri;\nimport android.os.Build;\nimport android.os.Bundle;\nimport android.view.View;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.annotation.RequiresApi;\nimport androidx.fragment.app.Fragment;\nimport androidx.fragment.app.FragmentActivity;\nimport androidx.fragment.app.FragmentManager;\n\nimport java.io.File;\nimport java.util.List;\n\n\n/**\n * @author cenxiaozhong\n * @since 2.0.0\n */\npublic final class AgentActionFragment extends Fragment {\n\n    public static final String KEY_URI = \"KEY_URI\";\n    public static final String KEY_FROM_INTENTION = \"KEY_FROM_INTENTION\";\n    private static final String TAG = AgentActionFragment.class.getSimpleName();\n    private Action mAction;\n    public static final int REQUEST_CODE = 0x254;\n    public static final String FRAGMENT_TAG = \"AgentWebActionFragment\";\n\n    public static void start(Activity activity, Action action) {\n        FragmentActivity fragmentActivity = (FragmentActivity) activity;\n        FragmentManager fragmentManager = fragmentActivity.getSupportFragmentManager();\n        AgentActionFragment fragment = (AgentActionFragment) fragmentManager.findFragmentByTag(FRAGMENT_TAG);\n        if (fragment == null) {\n            fragment = new AgentActionFragment();\n            fragmentManager.beginTransaction().add(fragment, FRAGMENT_TAG).commitAllowingStateLoss();\n        }\n        fragment.mAction = action;\n        if (fragment.isViewCreated) {\n            fragment.runAction();\n        }\n    }\n\n\n    public AgentActionFragment() {\n    }\n\n    private void resetAction() {\n//        mAction = null;\n    }\n\n    private boolean isViewCreated = false;\n\n    @Override\n    public void onCreate(@Nullable Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        if (savedInstanceState != null) {\n            LogUtils.i(TAG, \"savedInstanceState:\" + savedInstanceState);\n            return;\n        }\n        isViewCreated = true;\n        runAction();\n    }\n\n    @Override\n    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {\n        super.onViewCreated(view, savedInstanceState);\n    }\n\n    private void runAction() {\n        if (mAction == null) {\n            resetAction();\n            return;\n        }\n        if (mAction.getAction() == Action.ACTION_PERMISSION) {\n            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {\n                requestPermission(mAction);\n            } else {\n                resetAction();\n            }\n        } else if (mAction.getAction() == Action.ACTION_CAMERA) {\n            captureCamera();\n        } else if (mAction.getAction() == Action.ACTION_VIDEO) {\n            recordVideo();\n        } else {\n            choose();\n        }\n    }\n\n\n\n    private void choose() {\n        try {\n            if (mAction.getChooserListener() == null) {\n                return;\n            }\n            Intent mIntent = mAction.getIntent();\n            if (mIntent == null) {\n                resetAction();\n                return;\n            }\n            this.startActivityForResult(mIntent, REQUEST_CODE);\n        } catch (Throwable throwable) {\n            LogUtils.i(TAG, \"找不到文件选择器\");\n            chooserActionCallback(-1, null);\n            if (LogUtils.isDebug()) {\n                throwable.printStackTrace();\n            }\n        }\n    }\n\n    private void chooserActionCallback(int resultCode, Intent data) {\n        if (mAction.getChooserListener() != null) {\n            mAction.getChooserListener().onChoiceResult(REQUEST_CODE, resultCode, data);\n        }\n        resetAction();\n    }\n\n    @Override\n    public void onActivityResult(int requestCode, int resultCode, Intent data) {\n        if (mAction == null) {\n            return;\n        }\n        if (requestCode == REQUEST_CODE) {\n            if (mAction.getUri() != null) {\n                chooserActionCallback(resultCode, new Intent().putExtra(KEY_URI, mAction.getUri()));\n            } else {\n                chooserActionCallback(resultCode, data);\n            }\n        }\n        resetAction();\n    }\n\n    @RequiresApi(api = Build.VERSION_CODES.M)\n    private void requestPermission(Action action) {\n        List<String> permissions = action.getPermissions();\n        if (AgentWebUtils.isEmptyCollection(permissions)) {\n            resetAction();\n            return;\n        }\n        if (mAction.getRationaleListener() != null) {\n            boolean rationale = false;\n            for (String permission : permissions) {\n                rationale = shouldShowRequestPermissionRationale(permission);\n                if (rationale) {\n                    break;\n                }\n            }\n            mAction.getRationaleListener().onRationaleResult(rationale, new Bundle());\n            resetAction();\n            return;\n        }\n        if (mAction.getPermissionListener() != null) {\n            requestPermissions(permissions.toArray(new String[]{}), 1);\n        }\n    }\n\n\n    private void captureCamera() {\n        try {\n            if (mAction.getChooserListener() == null) {\n                resetAction();\n                return;\n            }\n            File mFile = AgentWebUtils.createImageFile(this.getActivity());\n            if (mFile == null) {\n                mAction.getChooserListener().onChoiceResult(REQUEST_CODE, Activity.RESULT_CANCELED, null);\n            }\n            Intent intent = AgentWebUtils.getIntentCaptureCompat(getActivity(), mFile);\n            // 指定开启系统相机的Action\n            mAction.setUri((Uri) intent.getParcelableExtra(EXTRA_OUTPUT));\n            this.startActivityForResult(intent, REQUEST_CODE);\n        } catch (Throwable ignore) {\n            LogUtils.e(TAG, \"找不到系统相机\");\n            if (mAction.getChooserListener() != null) {\n                mAction.getChooserListener().onChoiceResult(REQUEST_CODE, Activity.RESULT_CANCELED, null);\n            }\n            resetAction();\n            if (LogUtils.isDebug()) {\n                ignore.printStackTrace();\n            }\n        }\n    }\n\n    private void recordVideo() {\n        try {\n            if (mAction.getChooserListener() == null) {\n                resetAction();\n                return;\n            }\n            File mFile = AgentWebUtils.createVideoFile(this.getActivity());\n            if (mFile == null) {\n                mAction.getChooserListener().onChoiceResult(REQUEST_CODE, Activity.RESULT_CANCELED, null);\n                resetAction();\n                return;\n            }\n            Intent intent = AgentWebUtils.getIntentVideoCompat(getActivity(), mFile);\n            // 指定开启系统相机的Action\n            mAction.setUri((Uri) intent.getParcelableExtra(EXTRA_OUTPUT));\n            this.startActivityForResult(intent, REQUEST_CODE);\n        } catch (Throwable ignore) {\n            LogUtils.e(TAG, \"找不到系统相机\");\n            if (mAction.getChooserListener() != null) {\n                mAction.getChooserListener().onChoiceResult(REQUEST_CODE, Activity.RESULT_CANCELED, null);\n            }\n            resetAction();\n            if (LogUtils.isDebug()) {\n                ignore.printStackTrace();\n            }\n        }\n    }\n\n\n    @Override\n    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {\n        if (mAction.getPermissionListener() != null) {\n            Bundle mBundle = new Bundle();\n            mBundle.putInt(KEY_FROM_INTENTION, mAction.getFromIntention());\n            mAction.getPermissionListener().onRequestPermissionsResult(permissions, grantResults, mBundle);\n        }\n        resetAction();\n    }\n\n    public interface RationaleListener {\n        void onRationaleResult(boolean showRationale, Bundle extras);\n    }\n\n    public interface PermissionListener {\n        void onRequestPermissionsResult(@NonNull String[] permissions, @NonNull int[] grantResults, Bundle extras);\n    }\n\n    public interface ChooserListener {\n        void onChoiceResult(int requestCode, int resultCode, Intent data);\n    }\n\n    @Override\n    public void onDestroy() {\n        super.onDestroy();\n    }\n}\n"
  },
  {
    "path": "agentweb-core/src/main/java/com/just/agentweb/AgentWeb.java",
    "content": "/*\r\n * Copyright (C)  Justson(https://github.com/Justson/AgentWeb)\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n *      http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n\r\npackage com.just.agentweb;\r\n\r\nimport android.app.Activity;\r\nimport androidx.annotation.ColorInt;\r\nimport androidx.annotation.IdRes;\r\nimport androidx.annotation.LayoutRes;\r\nimport androidx.annotation.NonNull;\r\nimport androidx.annotation.Nullable;\r\nimport androidx.fragment.app.Fragment;\r\nimport androidx.collection.ArrayMap;\r\nimport android.text.TextUtils;\r\nimport android.view.KeyEvent;\r\nimport android.view.View;\r\nimport android.view.ViewGroup;\r\nimport android.webkit.WebChromeClient;\r\nimport android.webkit.WebView;\r\nimport android.webkit.WebViewClient;\r\n\r\nimport java.lang.ref.WeakReference;\r\nimport java.util.Map;\r\n\r\n/**\r\n * @author cenxiaozhong\r\n * @update 4.0.0\r\n * @since 1.0.0\r\n */\r\npublic final class AgentWeb {\r\n    /**\r\n     * AgentWeb 's TAG\r\n     */\r\n    private static final String TAG = AgentWeb.class.getSimpleName();\r\n    /**\r\n     * Activity\r\n     */\r\n    private Activity mActivity;\r\n    /**\r\n     * 承载 WebParentLayout 的 ViewGroup\r\n     */\r\n    private ViewGroup mViewGroup;\r\n    /**\r\n     * 负责创建布局 WebView ，WebParentLayout  Indicator等。\r\n     */\r\n    private WebCreator mWebCreator;\r\n    /**\r\n     * 管理 WebSettings\r\n     */\r\n    private IAgentWebSettings mAgentWebSettings;\r\n    /**\r\n     * AgentWeb\r\n     */\r\n    private AgentWeb mAgentWeb = null;\r\n    /**\r\n     * IndicatorController 控制Indicator\r\n     */\r\n    private IndicatorController mIndicatorController;\r\n    /**\r\n     * WebChromeClient\r\n     */\r\n    private com.just.agentweb.WebChromeClient mWebChromeClient;\r\n    /**\r\n     * WebViewClient\r\n     */\r\n    private com.just.agentweb.WebViewClient mWebViewClient;\r\n    /**\r\n     * is show indicator\r\n     */\r\n    private boolean mEnableIndicator;\r\n    /**\r\n     * IEventHandler 处理WebView相关返回事件\r\n     */\r\n    private IEventHandler mIEventHandler;\r\n    /**\r\n     * WebView 注入对象\r\n     */\r\n    private ArrayMap<String, Object> mJavaObjects = new ArrayMap<>();\r\n    /**\r\n     * flag\r\n     */\r\n    private int mTagTarget = 0;\r\n    /**\r\n     * WebListenerManager\r\n     */\r\n    private WebListenerManager mWebListenerManager;\r\n    /**\r\n     * 安全 Controller\r\n     */\r\n    private WebSecurityController<WebSecurityCheckLogic> mWebSecurityController = null;\r\n    /**\r\n     * WebSecurityCheckLogic\r\n     */\r\n    private WebSecurityCheckLogic mWebSecurityCheckLogic = null;\r\n    /**\r\n     * WebChromeClient\r\n     */\r\n    private WebChromeClient mTargetChromeClient;\r\n    /**\r\n     * flag security 's mode\r\n     */\r\n    private SecurityType mSecurityType = SecurityType.DEFAULT_CHECK;\r\n    /**\r\n     * Activity\r\n     */\r\n    private static final int ACTIVITY_TAG = 0;\r\n    /**\r\n     * Fragment\r\n     */\r\n    private static final int FRAGMENT_TAG = 1;\r\n    /**\r\n     * AgentWeb 默认注入对像\r\n     */\r\n    private AgentWebJsInterfaceCompat mAgentWebJsInterfaceCompat = null;\r\n    /**\r\n     * JsAccessEntrace 提供快速JS方法调用\r\n     */\r\n    private JsAccessEntrace mJsAccessEntrace = null;\r\n    /**\r\n     * URL Loader ， 提供了 WebView#loadUrl(url) reload() stopLoading（） postUrl()等方法\r\n     */\r\n    private IUrlLoader mIUrlLoader = null;\r\n    /**\r\n     * WebView 生命周期 ， 跟随生命周期释放CPU\r\n     */\r\n    private WebLifeCycle mWebLifeCycle;\r\n    /**\r\n     * Video 视屏播放管理类\r\n     */\r\n    private IVideo mIVideo = null;\r\n    /**\r\n     * WebViewClient 辅助控制开关\r\n     */\r\n    private boolean mWebClientHelper = true;\r\n    /**\r\n     * PermissionInterceptor 权限拦截\r\n     */\r\n    private PermissionInterceptor mPermissionInterceptor;\r\n    /**\r\n     * 是否拦截未知的Url， @link{DefaultWebClient}\r\n     */\r\n    private boolean mIsInterceptUnkownUrl = true;\r\n    /**\r\n     * Url处理方式，是直接跳转还是弹窗让用户去选择\r\n     */\r\n    private int mUrlHandleWays = -1;\r\n    /**\r\n     * MiddlewareWebClientBase WebViewClient 中间件\r\n     */\r\n    private MiddlewareWebClientBase mMiddleWrareWebClientBaseHeader;\r\n    /**\r\n     * MiddlewareWebChromeBase WebChromeClient 中间件\r\n     */\r\n    private MiddlewareWebChromeBase mMiddlewareWebChromeBaseHeader;\r\n    /**\r\n     * 事件拦截\r\n     */\r\n    private EventInterceptor mEventInterceptor;\r\n    /**\r\n     * 注入对象管理类\r\n     */\r\n    private JsInterfaceHolder mJsInterfaceHolder = null;\r\n\r\n\r\n    private AgentWeb(AgentBuilder agentBuilder) {\r\n        mTagTarget = agentBuilder.mTag;\r\n        this.mActivity = agentBuilder.mActivity;\r\n        this.mViewGroup = agentBuilder.mViewGroup;\r\n        this.mIEventHandler = agentBuilder.mIEventHandler;\r\n        this.mEnableIndicator = agentBuilder.mEnableIndicator;\r\n        mWebCreator = agentBuilder.mWebCreator == null ? configWebCreator(agentBuilder.mBaseIndicatorView, agentBuilder.mIndex, agentBuilder.mLayoutParams, agentBuilder.mIndicatorColor, agentBuilder.mHeight, agentBuilder.mWebView, agentBuilder.mWebLayout) : agentBuilder.mWebCreator;\r\n        mIndicatorController = agentBuilder.mIndicatorController;\r\n        this.mWebChromeClient = agentBuilder.mWebChromeClient;\r\n        this.mWebViewClient = agentBuilder.mWebViewClient;\r\n        mAgentWeb = this;\r\n        this.mAgentWebSettings = agentBuilder.mAgentWebSettings;\r\n\r\n        if (agentBuilder.mJavaObject != null && !agentBuilder.mJavaObject.isEmpty()) {\r\n            this.mJavaObjects.putAll((Map<? extends String, ?>) agentBuilder.mJavaObject);\r\n            LogUtils.i(TAG, \"mJavaObject size:\" + this.mJavaObjects.size());\r\n\r\n        }\r\n        this.mPermissionInterceptor = agentBuilder.mPermissionInterceptor == null ? null : new PermissionInterceptorWrapper(agentBuilder.mPermissionInterceptor);\r\n        this.mSecurityType = agentBuilder.mSecurityType;\r\n        this.mIUrlLoader = new UrlLoaderImpl(mWebCreator.create().getWebView(), agentBuilder.mHttpHeaders);\r\n        if (this.mWebCreator.getWebParentLayout() instanceof WebParentLayout) {\r\n            WebParentLayout mWebParentLayout = (WebParentLayout) this.mWebCreator.getWebParentLayout();\r\n            mWebParentLayout.bindController(agentBuilder.mAgentWebUIController == null ? AgentWebUIControllerImplBase.build() : agentBuilder.mAgentWebUIController);\r\n            mWebParentLayout.setErrorLayoutRes(agentBuilder.mErrorLayout, agentBuilder.mReloadId);\r\n            mWebParentLayout.setErrorView(agentBuilder.mErrorView);\r\n        }\r\n        this.mWebLifeCycle = new DefaultWebLifeCycleImpl(mWebCreator.getWebView());\r\n        mWebSecurityController = new WebSecurityControllerImpl(mWebCreator.getWebView(), this.mAgentWeb.mJavaObjects, this.mSecurityType);\r\n        this.mWebClientHelper = agentBuilder.mWebClientHelper;\r\n        this.mIsInterceptUnkownUrl = agentBuilder.mIsInterceptUnkownUrl;\r\n        if (agentBuilder.mOpenOtherPage != null) {\r\n            this.mUrlHandleWays = agentBuilder.mOpenOtherPage.code;\r\n        }\r\n        this.mMiddleWrareWebClientBaseHeader = agentBuilder.mMiddlewareWebClientBaseHeader;\r\n        this.mMiddlewareWebChromeBaseHeader = agentBuilder.mChromeMiddleWareHeader;\r\n        init();\r\n    }\r\n\r\n\r\n    /**\r\n     * @return PermissionInterceptor 权限控制者\r\n     */\r\n    public PermissionInterceptor getPermissionInterceptor() {\r\n        return this.mPermissionInterceptor;\r\n    }\r\n\r\n    public WebLifeCycle getWebLifeCycle() {\r\n        return this.mWebLifeCycle;\r\n    }\r\n\r\n    public JsAccessEntrace getJsAccessEntrace() {\r\n        JsAccessEntrace mJsAccessEntrace = this.mJsAccessEntrace;\r\n        if (mJsAccessEntrace == null) {\r\n            this.mJsAccessEntrace = mJsAccessEntrace = JsAccessEntraceImpl.getInstance(mWebCreator.getWebView());\r\n        }\r\n        return mJsAccessEntrace;\r\n    }\r\n\r\n\r\n    public AgentWeb clearWebCache() {\r\n        if (this.getWebCreator().getWebView() != null) {\r\n            AgentWebUtils.clearWebViewAllCache(mActivity, this.getWebCreator().getWebView());\r\n        } else {\r\n            AgentWebUtils.clearWebViewAllCache(mActivity);\r\n        }\r\n        return this;\r\n    }\r\n\r\n\r\n    public static AgentBuilder with(@NonNull Activity activity) {\r\n        if (activity == null) {\r\n            throw new NullPointerException(\"activity can not be null .\");\r\n        }\r\n        return new AgentBuilder(activity);\r\n    }\r\n\r\n    public static AgentBuilder with(@NonNull Fragment fragment) {\r\n        Activity mActivity = null;\r\n        if ((mActivity = fragment.getActivity()) == null) {\r\n            throw new NullPointerException(\"activity can not be null .\");\r\n        }\r\n        return new AgentBuilder(mActivity, fragment);\r\n    }\r\n\r\n    public boolean handleKeyEvent(int keyCode, KeyEvent keyEvent) {\r\n        if (mIEventHandler == null) {\r\n            mIEventHandler = EventHandlerImpl.getInstantce(mWebCreator.getWebView(), getInterceptor());\r\n        }\r\n        return mIEventHandler.onKeyDown(keyCode, keyEvent);\r\n    }\r\n\r\n    public boolean back() {\r\n        if (mIEventHandler == null) {\r\n            mIEventHandler = EventHandlerImpl.getInstantce(mWebCreator.getWebView(), getInterceptor());\r\n        }\r\n        return mIEventHandler.back();\r\n    }\r\n\r\n\r\n    public WebCreator getWebCreator() {\r\n        return this.mWebCreator;\r\n    }\r\n\r\n    public IEventHandler getIEventHandler() {\r\n        return this.mIEventHandler == null ? (this.mIEventHandler = EventHandlerImpl.getInstantce(mWebCreator.getWebView(), getInterceptor())) : this.mIEventHandler;\r\n    }\r\n\r\n\r\n    public IAgentWebSettings getAgentWebSettings() {\r\n        return this.mAgentWebSettings;\r\n    }\r\n\r\n    public IndicatorController getIndicatorController() {\r\n        return this.mIndicatorController;\r\n    }\r\n\r\n    public JsInterfaceHolder getJsInterfaceHolder() {\r\n        return this.mJsInterfaceHolder;\r\n    }\r\n\r\n    public IUrlLoader getUrlLoader() {\r\n        return this.mIUrlLoader;\r\n    }\r\n\r\n    public void destroy() {\r\n        this.mWebLifeCycle.onDestroy();\r\n    }\r\n\r\n    public static class PreAgentWeb {\r\n        private AgentWeb mAgentWeb;\r\n        private boolean isReady = false;\r\n\r\n        PreAgentWeb(AgentWeb agentWeb) {\r\n            this.mAgentWeb = agentWeb;\r\n        }\r\n\r\n        public PreAgentWeb ready() {\r\n            if (!isReady) {\r\n                mAgentWeb.ready();\r\n                isReady = true;\r\n            }\r\n            return this;\r\n        }\r\n\r\n        public AgentWeb get() {\r\n            ready();\r\n            return mAgentWeb;\r\n        }\r\n\r\n        public AgentWeb go(@Nullable String url) {\r\n            if (!isReady) {\r\n                ready();\r\n            }\r\n            return mAgentWeb.go(url);\r\n        }\r\n    }\r\n\r\n    private void doSafeCheck() {\r\n        WebSecurityCheckLogic mWebSecurityCheckLogic = this.mWebSecurityCheckLogic;\r\n        if (mWebSecurityCheckLogic == null) {\r\n            this.mWebSecurityCheckLogic = mWebSecurityCheckLogic = WebSecurityLogicImpl.getInstance(mWebCreator.getWebViewType());\r\n        }\r\n        mWebSecurityController.check(mWebSecurityCheckLogic);\r\n    }\r\n\r\n    private void doCompat() {\r\n        mJavaObjects.put(\"agentWeb\", mAgentWebJsInterfaceCompat = new AgentWebJsInterfaceCompat(this, mActivity));\r\n    }\r\n\r\n    private WebCreator configWebCreator(BaseIndicatorView progressView, int index, ViewGroup.LayoutParams lp, int indicatorColor, int height_dp, WebView webView, IWebLayout webLayout) {\r\n        if (progressView != null && mEnableIndicator) {\r\n            return new DefaultWebCreator(mActivity, mViewGroup, lp, index, progressView, webView, webLayout);\r\n        } else {\r\n            return mEnableIndicator ?\r\n                    new DefaultWebCreator(mActivity, mViewGroup, lp, index, indicatorColor, height_dp, webView, webLayout)\r\n                    : new DefaultWebCreator(mActivity, mViewGroup, lp, index, webView, webLayout);\r\n        }\r\n    }\r\n\r\n    private AgentWeb go(String url) {\r\n        this.getUrlLoader().loadUrl(url);\r\n        IndicatorController mIndicatorController = null;\r\n        if (!TextUtils.isEmpty(url) && (mIndicatorController = getIndicatorController()) != null && mIndicatorController.offerIndicator() != null) {\r\n            getIndicatorController().offerIndicator().show();\r\n        }\r\n        return this;\r\n    }\r\n\r\n    private EventInterceptor getInterceptor() {\r\n        if (this.mEventInterceptor != null) {\r\n            return this.mEventInterceptor;\r\n        }\r\n        if (mIVideo instanceof VideoImpl) {\r\n            return this.mEventInterceptor = (EventInterceptor) this.mIVideo;\r\n        }\r\n        return null;\r\n    }\r\n\r\n    private void init() {\r\n        doCompat();\r\n        doSafeCheck();\r\n    }\r\n\r\n    private IVideo getIVideo() {\r\n        return mIVideo == null ? new VideoImpl(mActivity, mWebCreator.getWebView()) : mIVideo;\r\n    }\r\n\r\n    private WebViewClient getWebViewClient() {\r\n\r\n        LogUtils.i(TAG, \"getDelegate:\" + this.mMiddleWrareWebClientBaseHeader);\r\n        DefaultWebClient mDefaultWebClient = DefaultWebClient\r\n                .createBuilder()\r\n                .setActivity(this.mActivity)\r\n                .setWebClientHelper(this.mWebClientHelper)\r\n                .setPermissionInterceptor(this.mPermissionInterceptor)\r\n                .setWebView(this.mWebCreator.getWebView())\r\n                .setInterceptUnkownUrl(this.mIsInterceptUnkownUrl)\r\n                .setUrlHandleWays(this.mUrlHandleWays)\r\n                .build();\r\n        MiddlewareWebClientBase header = this.mMiddleWrareWebClientBaseHeader;\r\n        if (this.mWebViewClient != null) {\r\n            this.mWebViewClient.enq(this.mMiddleWrareWebClientBaseHeader);\r\n            header = this.mWebViewClient;\r\n        }\r\n        if (header != null) {\r\n            MiddlewareWebClientBase tail = header;\r\n            int count = 1;\r\n            MiddlewareWebClientBase tmp = header;\r\n            while (tmp.next() != null) {\r\n                tail = tmp = tmp.next();\r\n                count++;\r\n            }\r\n            LogUtils.i(TAG, \"MiddlewareWebClientBase middleware count:\" + count);\r\n            tail.setDelegate(mDefaultWebClient);\r\n            return header;\r\n        } else {\r\n            return mDefaultWebClient;\r\n        }\r\n    }\r\n\r\n    private AgentWeb ready() {\r\n        AgentWebConfig.initCookiesManager(mActivity.getApplicationContext());\r\n        IAgentWebSettings mAgentWebSettings = this.mAgentWebSettings;\r\n        if (mAgentWebSettings == null) {\r\n            this.mAgentWebSettings = mAgentWebSettings = AgentWebSettingsImpl.getInstance();\r\n        }\r\n        if (mAgentWebSettings instanceof AbsAgentWebSettings) {\r\n            ((AbsAgentWebSettings) mAgentWebSettings).bindAgentWeb(this);\r\n        }\r\n        if (mWebListenerManager == null && mAgentWebSettings instanceof AbsAgentWebSettings) {\r\n            mWebListenerManager = (WebListenerManager) mAgentWebSettings;\r\n        }\r\n        mAgentWebSettings.toSetting(mWebCreator.getWebView());\r\n        if (mJsInterfaceHolder == null) {\r\n            mJsInterfaceHolder = JsInterfaceHolderImpl.getJsInterfaceHolder(mWebCreator, this.mSecurityType);\r\n        }\r\n        LogUtils.i(TAG, \"mJavaObjects:\" + mJavaObjects.size());\r\n        if (mJavaObjects != null && !mJavaObjects.isEmpty()) {\r\n            mJsInterfaceHolder.addJavaObjects(mJavaObjects);\r\n        }\r\n        if (mWebListenerManager != null) {\r\n            mWebListenerManager.setDownloader(mWebCreator.getWebView(), null);\r\n            mWebListenerManager.setWebChromeClient(mWebCreator.getWebView(), getChromeClient());\r\n            mWebListenerManager.setWebViewClient(mWebCreator.getWebView(), getWebViewClient());\r\n        }\r\n        return this;\r\n    }\r\n\r\n    private WebChromeClient getChromeClient() {\r\n        IndicatorController mIndicatorController =\r\n                (this.mIndicatorController == null) ?\r\n                        IndicatorHandler.getInstance().inJectIndicator(mWebCreator.offer())\r\n                        : this.mIndicatorController;\r\n\r\n        DefaultChromeClient mDefaultChromeClient =\r\n                new DefaultChromeClient(this.mActivity,\r\n                        this.mIndicatorController = mIndicatorController,\r\n                        null, this.mIVideo = getIVideo(),\r\n                        this.mPermissionInterceptor, mWebCreator.getWebView());\r\n\r\n        LogUtils.i(TAG, \"WebChromeClient:\" + this.mWebChromeClient);\r\n        MiddlewareWebChromeBase header = this.mMiddlewareWebChromeBaseHeader;\r\n        if (this.mWebChromeClient != null) {\r\n            this.mWebChromeClient.enq(header);\r\n            header = this.mWebChromeClient;\r\n        }\r\n        if (header != null) {\r\n            MiddlewareWebChromeBase tail = header;\r\n            int count = 1;\r\n            MiddlewareWebChromeBase tmp = header;\r\n            for (; tmp.next() != null; ) {\r\n                tail = tmp = tmp.next();\r\n                count++;\r\n            }\r\n            LogUtils.i(TAG, \"MiddlewareWebClientBase middleware count:\" + count);\r\n            tail.setDelegate(mDefaultChromeClient);\r\n            return this.mTargetChromeClient = header;\r\n        } else {\r\n            return this.mTargetChromeClient = mDefaultChromeClient;\r\n        }\r\n    }\r\n\r\n    public enum SecurityType {\r\n        DEFAULT_CHECK, STRICT_CHECK;\r\n    }\r\n\r\n    public static final class AgentBuilder {\r\n        private Activity mActivity;\r\n        private Fragment mFragment;\r\n        private ViewGroup mViewGroup;\r\n        private boolean mIsNeedDefaultProgress;\r\n        private int mIndex = -1;\r\n        private BaseIndicatorView mBaseIndicatorView;\r\n        private IndicatorController mIndicatorController = null;\r\n        /*默认进度条是显示的*/\r\n        private boolean mEnableIndicator = true;\r\n        private ViewGroup.LayoutParams mLayoutParams = null;\r\n        private com.just.agentweb.WebViewClient mWebViewClient;\r\n        private com.just.agentweb.WebChromeClient mWebChromeClient;\r\n        private int mIndicatorColor = -1;\r\n        private IAgentWebSettings mAgentWebSettings;\r\n        private WebCreator mWebCreator;\r\n        private HttpHeaders mHttpHeaders = null;\r\n        private IEventHandler mIEventHandler;\r\n        private int mHeight = -1;\r\n        private ArrayMap<String, Object> mJavaObject;\r\n        private SecurityType mSecurityType = SecurityType.DEFAULT_CHECK;\r\n        private WebView mWebView;\r\n        private boolean mWebClientHelper = true;\r\n        private IWebLayout mWebLayout = null;\r\n        private PermissionInterceptor mPermissionInterceptor = null;\r\n        private AbsAgentWebUIController mAgentWebUIController;\r\n        private DefaultWebClient.OpenOtherPageWays mOpenOtherPage = null;\r\n        private boolean mIsInterceptUnkownUrl = true;\r\n        private MiddlewareWebClientBase mMiddlewareWebClientBaseHeader;\r\n        private MiddlewareWebClientBase mMiddlewareWebClientBaseTail;\r\n        private MiddlewareWebChromeBase mChromeMiddleWareHeader = null;\r\n        private MiddlewareWebChromeBase mChromeMiddleWareTail = null;\r\n        private View mErrorView;\r\n        private int mErrorLayout;\r\n        private int mReloadId;\r\n        private int mTag = -1;\r\n\r\n        public AgentBuilder(@NonNull Activity activity, @NonNull Fragment fragment) {\r\n            mActivity = activity;\r\n            mFragment = fragment;\r\n            mTag = AgentWeb.FRAGMENT_TAG;\r\n        }\r\n\r\n        public AgentBuilder(@NonNull Activity activity) {\r\n            mActivity = activity;\r\n            mTag = AgentWeb.ACTIVITY_TAG;\r\n        }\r\n\r\n\r\n        public IndicatorBuilder setAgentWebParent(@NonNull ViewGroup v, @NonNull ViewGroup.LayoutParams lp) {\r\n            this.mViewGroup = v;\r\n            this.mLayoutParams = lp;\r\n            return new IndicatorBuilder(this);\r\n        }\r\n\r\n        public IndicatorBuilder setAgentWebParent(@NonNull ViewGroup v, int index, @NonNull ViewGroup.LayoutParams lp) {\r\n            this.mViewGroup = v;\r\n            this.mLayoutParams = lp;\r\n            this.mIndex = index;\r\n            return new IndicatorBuilder(this);\r\n        }\r\n\r\n        private PreAgentWeb buildAgentWeb() {\r\n            if (mTag == AgentWeb.FRAGMENT_TAG && this.mViewGroup == null) {\r\n                throw new NullPointerException(\"ViewGroup is null,Please check your parameters .\");\r\n            }\r\n            return new PreAgentWeb(HookManager.hookAgentWeb(new AgentWeb(this), this));\r\n        }\r\n\r\n        private void addJavaObject(String key, Object o) {\r\n            if (mJavaObject == null) {\r\n                mJavaObject = new ArrayMap<>();\r\n            }\r\n            mJavaObject.put(key, o);\r\n        }\r\n\r\n        private void addHeader(String baseUrl, String k, String v) {\r\n            if (mHttpHeaders == null) {\r\n                mHttpHeaders = HttpHeaders.create();\r\n            }\r\n            mHttpHeaders.additionalHttpHeader(baseUrl, k, v);\r\n        }\r\n\r\n        private void addHeader(String baseUrl, Map<String, String> headers) {\r\n            if (mHttpHeaders == null) {\r\n                mHttpHeaders = HttpHeaders.create();\r\n            }\r\n            mHttpHeaders.additionalHttpHeaders(baseUrl, headers);\r\n        }\r\n    }\r\n\r\n    public static class IndicatorBuilder {\r\n        private AgentBuilder mAgentBuilder = null;\r\n\r\n        public IndicatorBuilder(AgentBuilder agentBuilder) {\r\n            this.mAgentBuilder = agentBuilder;\r\n        }\r\n\r\n        public CommonBuilder useDefaultIndicator(int color) {\r\n            this.mAgentBuilder.mEnableIndicator = true;\r\n            this.mAgentBuilder.mIndicatorColor = color;\r\n            return new CommonBuilder(mAgentBuilder);\r\n        }\r\n\r\n        public CommonBuilder useDefaultIndicator() {\r\n            this.mAgentBuilder.mEnableIndicator = true;\r\n            return new CommonBuilder(mAgentBuilder);\r\n        }\r\n\r\n        public CommonBuilder closeIndicator() {\r\n            this.mAgentBuilder.mEnableIndicator = false;\r\n            this.mAgentBuilder.mIndicatorColor = -1;\r\n            this.mAgentBuilder.mHeight = -1;\r\n            return new CommonBuilder(mAgentBuilder);\r\n        }\r\n\r\n        public CommonBuilder setCustomIndicator(@NonNull BaseIndicatorView v) {\r\n            if (v != null) {\r\n                this.mAgentBuilder.mEnableIndicator = true;\r\n                this.mAgentBuilder.mBaseIndicatorView = v;\r\n                this.mAgentBuilder.mIsNeedDefaultProgress = false;\r\n            } else {\r\n                this.mAgentBuilder.mEnableIndicator = true;\r\n                this.mAgentBuilder.mIsNeedDefaultProgress = true;\r\n            }\r\n            return new CommonBuilder(mAgentBuilder);\r\n        }\r\n\r\n        public CommonBuilder useDefaultIndicator(@ColorInt int color, int height_dp) {\r\n            this.mAgentBuilder.mIndicatorColor = color;\r\n            this.mAgentBuilder.mHeight = height_dp;\r\n            return new CommonBuilder(this.mAgentBuilder);\r\n        }\r\n    }\r\n\r\n    public static class CommonBuilder {\r\n        private AgentBuilder mAgentBuilder;\r\n\r\n        public CommonBuilder(AgentBuilder agentBuilder) {\r\n            this.mAgentBuilder = agentBuilder;\r\n        }\r\n\r\n        public CommonBuilder setEventHanadler(@Nullable IEventHandler iEventHandler) {\r\n            mAgentBuilder.mIEventHandler = iEventHandler;\r\n            return this;\r\n        }\r\n\r\n        public CommonBuilder closeWebViewClientHelper() {\r\n            mAgentBuilder.mWebClientHelper = false;\r\n            return this;\r\n        }\r\n\r\n        public CommonBuilder setWebChromeClient(@Nullable com.just.agentweb.WebChromeClient webChromeClient) {\r\n            this.mAgentBuilder.mWebChromeClient = webChromeClient;\r\n            return this;\r\n        }\r\n\r\n        public CommonBuilder setWebViewClient(@Nullable com.just.agentweb.WebViewClient webChromeClient) {\r\n            this.mAgentBuilder.mWebViewClient = webChromeClient;\r\n            return this;\r\n        }\r\n\r\n        public CommonBuilder useMiddlewareWebClient(@NonNull MiddlewareWebClientBase middleWrareWebClientBase) {\r\n            if (middleWrareWebClientBase == null) {\r\n                return this;\r\n            }\r\n            if (this.mAgentBuilder.mMiddlewareWebClientBaseHeader == null) {\r\n                this.mAgentBuilder.mMiddlewareWebClientBaseHeader = this.mAgentBuilder.mMiddlewareWebClientBaseTail = middleWrareWebClientBase;\r\n            } else {\r\n                this.mAgentBuilder.mMiddlewareWebClientBaseTail.enq(middleWrareWebClientBase);\r\n                this.mAgentBuilder.mMiddlewareWebClientBaseTail = middleWrareWebClientBase;\r\n            }\r\n            return this;\r\n        }\r\n\r\n        public CommonBuilder useMiddlewareWebChrome(@NonNull MiddlewareWebChromeBase middlewareWebChromeBase) {\r\n            if (middlewareWebChromeBase == null) {\r\n                return this;\r\n            }\r\n            if (this.mAgentBuilder.mChromeMiddleWareHeader == null) {\r\n                this.mAgentBuilder.mChromeMiddleWareHeader = this.mAgentBuilder.mChromeMiddleWareTail = middlewareWebChromeBase;\r\n            } else {\r\n                this.mAgentBuilder.mChromeMiddleWareTail.enq(middlewareWebChromeBase);\r\n                this.mAgentBuilder.mChromeMiddleWareTail = middlewareWebChromeBase;\r\n            }\r\n            return this;\r\n        }\r\n\r\n        public CommonBuilder setMainFrameErrorView(@NonNull View view) {\r\n            this.mAgentBuilder.mErrorView = view;\r\n            return this;\r\n        }\r\n\r\n        public CommonBuilder setMainFrameErrorView(@LayoutRes int errorLayout, @IdRes int clickViewId) {\r\n            this.mAgentBuilder.mErrorLayout = errorLayout;\r\n            this.mAgentBuilder.mReloadId = clickViewId;\r\n            return this;\r\n        }\r\n\r\n        public CommonBuilder setAgentWebWebSettings(@Nullable IAgentWebSettings agentWebSettings) {\r\n            this.mAgentBuilder.mAgentWebSettings = agentWebSettings;\r\n            return this;\r\n        }\r\n\r\n        public PreAgentWeb createAgentWeb() {\r\n            return this.mAgentBuilder.buildAgentWeb();\r\n        }\r\n\r\n\r\n        public CommonBuilder addJavascriptInterface(@NonNull String name, @NonNull Object o) {\r\n            this.mAgentBuilder.addJavaObject(name, o);\r\n            return this;\r\n        }\r\n\r\n        public CommonBuilder setSecurityType(@NonNull SecurityType type) {\r\n            this.mAgentBuilder.mSecurityType = type;\r\n            return this;\r\n        }\r\n\r\n        public CommonBuilder setWebView(@Nullable WebView webView) {\r\n            this.mAgentBuilder.mWebView = webView;\r\n            return this;\r\n        }\r\n\r\n        public CommonBuilder setWebLayout(@Nullable IWebLayout iWebLayout) {\r\n            this.mAgentBuilder.mWebLayout = iWebLayout;\r\n            return this;\r\n        }\r\n\r\n        public CommonBuilder additionalHttpHeader(String baseUrl, String k, String v) {\r\n            this.mAgentBuilder.addHeader(baseUrl, k, v);\r\n            return this;\r\n        }\r\n\r\n        public CommonBuilder additionalHttpHeader(String baseUrl, Map<String, String> headers) {\r\n            this.mAgentBuilder.addHeader(baseUrl, headers);\r\n            return this;\r\n        }\r\n\r\n        public CommonBuilder setPermissionInterceptor(@Nullable PermissionInterceptor permissionInterceptor) {\r\n            this.mAgentBuilder.mPermissionInterceptor = permissionInterceptor;\r\n            return this;\r\n        }\r\n\r\n        public CommonBuilder setAgentWebUIController(@Nullable AgentWebUIControllerImplBase agentWebUIController) {\r\n            this.mAgentBuilder.mAgentWebUIController = agentWebUIController;\r\n            return this;\r\n        }\r\n\r\n        public CommonBuilder setOpenOtherPageWays(@Nullable DefaultWebClient.OpenOtherPageWays openOtherPageWays) {\r\n            this.mAgentBuilder.mOpenOtherPage = openOtherPageWays;\r\n            return this;\r\n        }\r\n\r\n        public CommonBuilder interceptUnkownUrl() {\r\n            this.mAgentBuilder.mIsInterceptUnkownUrl = true;\r\n            return this;\r\n        }\r\n\r\n        public CommonBuilder isInterceptUnkownUrl(boolean isInterceptUnkownUrl) {\r\n            this.mAgentBuilder.mIsInterceptUnkownUrl = isInterceptUnkownUrl;\r\n            return this;\r\n        }\r\n    }\r\n\r\n    Activity getActivity() {\r\n        return this.mActivity;\r\n    }\r\n\r\n    private static final class PermissionInterceptorWrapper implements PermissionInterceptor {\r\n\r\n        private WeakReference<PermissionInterceptor> mWeakReference;\r\n\r\n        private PermissionInterceptorWrapper(PermissionInterceptor permissionInterceptor) {\r\n            this.mWeakReference = new WeakReference<PermissionInterceptor>(permissionInterceptor);\r\n        }\r\n\r\n        @Override\r\n        public boolean intercept(String url, String[] permissions, String a) {\r\n            if (this.mWeakReference.get() == null) {\r\n                return false;\r\n            }\r\n            return mWeakReference.get().intercept(url, permissions, a);\r\n        }\r\n    }\r\n}\r\n"
  },
  {
    "path": "agentweb-core/src/main/java/com/just/agentweb/AgentWebCompat.java",
    "content": "package com.just.agentweb;\n\nimport android.annotation.TargetApi;\nimport android.content.Context;\nimport android.os.Build;\nimport android.text.TextUtils;\nimport android.webkit.WebView;\n\nimport java.io.File;\nimport java.io.RandomAccessFile;\nimport java.nio.channels.FileLock;\nimport java.util.HashSet;\nimport java.util.Set;\n\n/**\n * @author cenxiaozhong\n * @date 2021/11/24\n * @since 1.0.0\n */\npublic class AgentWebCompat {\n\n\n    /**\n     * 来之 https://github.com/Justson/AgentWeb/issues/934 建议\n     * https://juejin.cn/post/6950091477192015902\n     * fix Using WebView from more than one process\n     * @param context\n     */\n    public static void setDataDirectorySuffix(Context context) {\n        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) {\n            return;\n        }\n        try {\n\n            Set<String> pathSet = new HashSet<>();\n            String suffix = \"\";\n            String dataPath = context.getDataDir().getAbsolutePath();\n            String webViewDir = \"/app_webview\";\n            String huaweiWebViewDir = \"/app_hws_webview\";\n            String lockFile = \"/webview_data.lock\";\n            String processName = ProcessUtils.getCurrentProcessName(context);\n            if (!TextUtils.equals(context.getPackageName(), processName)) {//判断不等于默认进程名称\n                suffix = TextUtils.isEmpty(processName) ? context.getPackageName() : processName;\n                WebView.setDataDirectorySuffix(suffix);\n                suffix = \"_\" + suffix;\n                pathSet.add(dataPath + webViewDir + suffix + lockFile);\n                if (RomUtils.isHuawei()) {\n                    pathSet.add(dataPath + huaweiWebViewDir + suffix + lockFile);\n                }\n            }else{\n                //主进程\n                suffix = \"_\" + processName;\n                pathSet.add(dataPath + webViewDir + lockFile);//默认未添加进程名后缀\n                pathSet.add(dataPath + webViewDir + suffix + lockFile);//系统自动添加了进程名后缀\n                if (RomUtils.isHuawei()) {//部分华为手机更改了webview目录名\n                    pathSet.add(dataPath + huaweiWebViewDir + lockFile);\n                    pathSet.add(dataPath + huaweiWebViewDir + suffix + lockFile);\n                }\n            }\n            for (String path : pathSet) {\n                File file = new File(path);\n                if (file.exists()) {\n                    tryLockOrRecreateFile(file);\n                    break;\n                }\n            }\n        } catch (Exception e) {\n            e.printStackTrace();\n        }\n    }\n\n    @TargetApi(Build.VERSION_CODES.P)\n    private static void tryLockOrRecreateFile(File file) {\n        try {\n            FileLock tryLock = new RandomAccessFile(file, \"rw\").getChannel().tryLock();\n            if (tryLock != null) {\n                tryLock.close();\n            } else {\n                createFile(file, file.delete());\n            }\n        } catch (Exception e) {\n            e.printStackTrace();\n            boolean deleted = false;\n            if (file.exists()) {\n                deleted = file.delete();\n            }\n            createFile(file, deleted);\n        }\n    }\n\n    private static void createFile(File file, boolean deleted){\n        try {\n            if (deleted && !file.exists()) {\n                file.createNewFile();\n            }\n        } catch (Exception e) {\n            e.printStackTrace();\n        }\n    }\n\n}\n\n"
  },
  {
    "path": "agentweb-core/src/main/java/com/just/agentweb/AgentWebConfig.java",
    "content": "/*\n * Copyright (C)  Justson(https://github.com/Justson/AgentWeb)\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.just.agentweb;\n\nimport android.content.Context;\nimport android.os.AsyncTask;\nimport android.os.Build;\nimport androidx.annotation.Nullable;\nimport android.text.TextUtils;\nimport android.webkit.CookieManager;\nimport android.webkit.CookieSyncManager;\nimport android.webkit.ValueCallback;\nimport android.webkit.WebView;\n\nimport java.io.File;\n\nimport static com.just.agentweb.AgentWebUtils.getAgentWebFilePath;\n\n/**\n * @author cenxiaozhong\n * @since 1.0.0\n */\npublic class AgentWebConfig {\n\n    static final String FILE_CACHE_PATH = \"agentweb-cache\";\n    static final String AGENTWEB_CACHE_PATCH = File.separator + \"agentweb-cache\";\n    /**\n     * 缓存路径\n     */\n    static String AGENTWEB_FILE_PATH;\n    /**\n     * DEBUG 模式 ， 如果需要查看日志请设置为 true\n     */\n    public static boolean DEBUG = false;\n    /**\n     * 当前操作系统是否低于 KITKAT\n     */\n    static final boolean IS_KITKAT_OR_BELOW_KITKAT = Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT;\n    /**\n     * 默认 WebView  类型 。\n     */\n    public static final int WEBVIEW_DEFAULT_TYPE = 1;\n    /**\n     * 使用 AgentWebView\n     */\n    public static final int WEBVIEW_AGENTWEB_SAFE_TYPE = 2;\n    /**\n     * 自定义 WebView\n     */\n    public static final int WEBVIEW_CUSTOM_TYPE = 3;\n    private static volatile boolean IS_INITIALIZED = false;\n    private static final String TAG = AgentWebConfig.class.getSimpleName();\n    public static final String AGENTWEB_NAME = \"AgentWeb\";\n    /**\n     * AgentWeb 的版本\n     */\n    public static final String AGENTWEB_VERSION = AGENTWEB_NAME + \"/\" + \"5.0.8\";\n    /**\n     * 通过JS获取的文件大小， 这里限制最大为5MB ，太大会抛出 OutOfMemoryError\n     */\n    public static int MAX_FILE_LENGTH = 1024 * 1024 * 5;\n\n    //获取Cookie\n    public static String getCookiesByUrl(String url) {\n        return CookieManager.getInstance() == null ? null : CookieManager.getInstance().getCookie(url);\n    }\n\n    public static void debug() {\n        DEBUG = true;\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {\n            WebView.setWebContentsDebuggingEnabled(true);\n        }\n    }\n\n    /**\n     * 删除所有已经过期的 Cookies\n     */\n    public static void removeExpiredCookies() {\n        CookieManager mCookieManager = null;\n        if ((mCookieManager = CookieManager.getInstance()) != null) { //同步清除\n            mCookieManager.removeExpiredCookie();\n            toSyncCookies();\n        }\n    }\n\n    /**\n     * 删除所有 Cookies\n     */\n    public static void removeAllCookies() {\n        removeAllCookies(null);\n    }\n\n    // 解决兼容 Android 4.4 java.lang.NoSuchMethodError: android.webkit.CookieManager.removeSessionCookies\n    public static void removeSessionCookies() {\n        removeSessionCookies(null);\n    }\n\n    /**\n     * 同步cookie\n     *\n     * @param url\n     * @param cookies\n     */\n    public static void syncCookie(String url, String cookies) {\n        CookieManager mCookieManager = CookieManager.getInstance();\n        if (mCookieManager != null) {\n            mCookieManager.setCookie(url, cookies);\n            toSyncCookies();\n        }\n    }\n\n    public static void removeSessionCookies(ValueCallback<Boolean> callback) {\n        if (callback == null) {\n            callback = getDefaultIgnoreCallback();\n        }\n        if (CookieManager.getInstance() == null) {\n            callback.onReceiveValue(new Boolean(false));\n            return;\n        }\n        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {\n            CookieManager.getInstance().removeSessionCookie();\n            toSyncCookies();\n            callback.onReceiveValue(new Boolean(true));\n            return;\n        }\n        CookieManager.getInstance().removeSessionCookies(callback);\n        toSyncCookies();\n    }\n\n    /**\n     * @param context\n     * @return WebView 的缓存路径\n     */\n    public static String getCachePath(Context context) {\n        return context.getCacheDir().getAbsolutePath() + AGENTWEB_CACHE_PATCH;\n    }\n\n    /**\n     * @param context\n     * @return AgentWeb 缓存路径\n     */\n    public static String getExternalCachePath(Context context) {\n        return getAgentWebFilePath(context);\n    }\n\n\n    //Android  4.4  NoSuchMethodError: android.webkit.CookieManager.removeAllCookies\n    public static void removeAllCookies(@Nullable ValueCallback<Boolean> callback) {\n        if (callback == null) {\n            callback = getDefaultIgnoreCallback();\n        }\n        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {\n            CookieManager.getInstance().removeAllCookie();\n            toSyncCookies();\n            callback.onReceiveValue(!CookieManager.getInstance().hasCookies());\n            return;\n        }\n        CookieManager.getInstance().removeAllCookies(callback);\n        toSyncCookies();\n    }\n\n    /**\n     * 清空缓存\n     *\n     * @param context\n     */\n    public static synchronized void clearDiskCache(Context context) {\n        try {\n            AgentWebUtils.clearCacheFolder(new File(getCachePath(context)), 0);\n            String path = getExternalCachePath(context);\n            if (!TextUtils.isEmpty(path)) {\n                File mFile = new File(path);\n                AgentWebUtils.clearCacheFolder(mFile, 0);\n            }\n        } catch (Throwable throwable) {\n            if (LogUtils.isDebug()) {\n                throwable.printStackTrace();\n            }\n        }\n    }\n\n\n    static synchronized void initCookiesManager(Context context) {\n        if (!IS_INITIALIZED) {\n            createCookiesSyncInstance(context);\n            IS_INITIALIZED = true;\n        }\n    }\n\n    private static void createCookiesSyncInstance(Context context) {\n        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {\n            CookieSyncManager.createInstance(context);\n        }\n    }\n\n    private static void toSyncCookies() {\n        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {\n            CookieSyncManager.getInstance().sync();\n            return;\n        }\n        AsyncTask.THREAD_POOL_EXECUTOR.execute(new Runnable() {\n            @Override\n            public void run() {\n                CookieManager.getInstance().flush();\n            }\n        });\n    }\n\n    static String getDatabasesCachePath(Context context) {\n        return context.getApplicationContext().getDir(\"database\", Context.MODE_PRIVATE).getPath();\n    }\n\n    private static ValueCallback<Boolean> getDefaultIgnoreCallback() {\n        return new ValueCallback<Boolean>() {\n            @Override\n            public void onReceiveValue(Boolean ignore) {\n                LogUtils.i(TAG, \"removeExpiredCookies:\" + ignore);\n            }\n        };\n    }\n}\n"
  },
  {
    "path": "agentweb-core/src/main/java/com/just/agentweb/AgentWebFileProvider.java",
    "content": "/*\n * Copyright (C)  Justson(https://github.com/Justson/AgentWeb)\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.just.agentweb;\n\nimport android.content.Context;\nimport android.content.pm.ProviderInfo;\nimport androidx.annotation.NonNull;\nimport androidx.core.content.FileProvider;\n\n/**\n * @since 2.0.0\n * @author cenxiaozhong\n */\npublic class AgentWebFileProvider extends FileProvider {\n\n    @Override\n    public void attachInfo(@NonNull Context context, @NonNull ProviderInfo info) {\n        super.attachInfo(context, info);\n    }\n}\n"
  },
  {
    "path": "agentweb-core/src/main/java/com/just/agentweb/AgentWebJsInterfaceCompat.java",
    "content": "/*\n * Copyright (C)  Justson(https://github.com/Justson/AgentWeb)\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.just.agentweb;\n\nimport android.app.Activity;\nimport android.os.Handler;\nimport android.os.Message;\nimport android.webkit.JavascriptInterface;\n\nimport java.lang.ref.WeakReference;\n\n/**\n * @author cenxiaozhong\n * @since 1.0.0\n */\npublic class AgentWebJsInterfaceCompat {\n\n\tprivate WeakReference<AgentWeb> mReference = null;\n\tprivate WeakReference<Activity> mActivityWeakReference = null;\n\tprivate String TAG = this.getClass().getSimpleName();\n\n\tAgentWebJsInterfaceCompat(AgentWeb agentWeb, Activity activity) {\n\t\tmReference = new WeakReference<AgentWeb>(agentWeb);\n\t\tmActivityWeakReference = new WeakReference<Activity>(activity);\n\t}\n\n\t@JavascriptInterface\n\tpublic void uploadFile() {\n\t\tuploadFile(\"*/*\");\n\t}\n\n\t@JavascriptInterface\n\tpublic void uploadFile(String acceptType) {\n\t\tLogUtils.i(TAG, acceptType + \"  \" + mActivityWeakReference.get() + \"  \" + mReference.get());\n\t\tif (mActivityWeakReference.get() != null && mReference.get() != null) {\n\t\t\tAgentWebUtils.showFileChooserCompat(mActivityWeakReference.get(),\n\t\t\t\t\tmReference.get().getWebCreator().getWebView(),\n\t\t\t\t\tnull,\n\t\t\t\t\tnull,\n\t\t\t\t\tmReference.get().getPermissionInterceptor(),\n\t\t\t\t\tnull,\n\t\t\t\t\tacceptType,\n\t\t\t\t\tnew Handler.Callback() {\n\t\t\t\t\t\t@Override\n\t\t\t\t\t\tpublic boolean handleMessage(Message msg) {\n\t\t\t\t\t\t\tif (mReference.get() != null) {\n\t\t\t\t\t\t\t\tmReference.get().getJsAccessEntrace()\n\t\t\t\t\t\t\t\t\t\t.quickCallJs(\"uploadFileResult\",\n\t\t\t\t\t\t\t\t\t\t\t\tmsg.obj instanceof String ? (String) msg.obj : null);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "agentweb-core/src/main/java/com/just/agentweb/AgentWebPermissions.java",
    "content": "/*\n * Copyright (C)  Justson(https://github.com/Justson/AgentWeb)\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.just.agentweb;\n\nimport android.Manifest;\nimport android.os.Build;\n\n\n/**\n * @author cenxiaozhong\n * @since 1.0.0\n */\npublic class AgentWebPermissions {\n\tpublic static String[] CAMERA;\n\tpublic static String[] LOCATION;\n\tpublic static String[] MEDIA;\n\tpublic static final String ACTION_CAMERA = \"Camera\";\n\tpublic static final String ACTION_LOCATION = \"Location\";\n\tpublic static final String ACTION_MEDIA = \"Media\";\n\n\tstatic {\n\t\tCAMERA = new String[]{\n\t\t\t\tManifest.permission.CAMERA};\n\n\t\tLOCATION = new String[]{\n\t\t\t\tManifest.permission.ACCESS_FINE_LOCATION,\n\t\t\t\tManifest.permission.ACCESS_COARSE_LOCATION};\n\n\t\tif (Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.TIRAMISU) {\n\t\t\tMEDIA = new String[]{\n\t\t\t\t\tManifest.permission.READ_MEDIA_VIDEO,\n\t\t\t\t\tManifest.permission.READ_MEDIA_AUDIO,\n\t\t\t\t\tManifest.permission.READ_MEDIA_IMAGES,\n\t\t\t};\n\t\t} else {\n\t\t\tMEDIA = new String[]{\n\t\t\t\t\tManifest.permission.READ_EXTERNAL_STORAGE,\n\t\t\t\t\tManifest.permission.WRITE_EXTERNAL_STORAGE\n\t\t\t};\n\t\t}\n\t}\n\n\tprivate static void emptyMediaPermission() {\n\t\tMEDIA = new String[]{};\n\t}\n\n\tprivate static void emptyCameraPermission() {\n\t\tCAMERA = new String[]{};\n\t}\n\n\tpublic static void dontAskUnnecessaryPermissions() {\n\t\tif (Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q) {\n\t\t\temptyMediaPermission();\n\t\t\temptyCameraPermission();\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "agentweb-core/src/main/java/com/just/agentweb/AgentWebSettingsImpl.java",
    "content": "/*\n * Copyright (C)  Justson(https://github.com/Justson/AgentWeb)\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.just.agentweb;\n\nimport android.app.Activity;\nimport android.content.Context;\nimport android.content.ContextWrapper;\nimport android.webkit.DownloadListener;\nimport android.webkit.WebView;\n\n\n/**\n * @author cenxiaozhong\n * @since 1.0.0\n */\npublic class AgentWebSettingsImpl extends AbsAgentWebSettings {\n    private AgentWeb mAgentWeb;\n\n    @Override\n    protected void bindAgentWebSupport(AgentWeb agentWeb) {\n        this.mAgentWeb = agentWeb;\n    }\n\n    @Override\n    public WebListenerManager setDownloader(WebView webView, DownloadListener downloadListener) {\n        if (downloadListener == null) {\n            downloadListener = DefaultDownloadImpl.create(mAgentWeb.getActivity(), webView, mAgentWeb.getPermissionInterceptor());\n        }\n        return super.setDownloader(webView, downloadListener);\n    }\n\n    /**\n     * Copy from com.blankj.utilcode.util.ActivityUtils#getActivityByView\n     */\n    private Activity getActivityByContext(Context context) {\n        if (context instanceof Activity) return (Activity) context;\n        while (context instanceof ContextWrapper) {\n            if (context instanceof Activity) {\n                return (Activity) context;\n            }\n            context = ((ContextWrapper) context).getBaseContext();\n        }\n        return null;\n    }\n\n}\n"
  },
  {
    "path": "agentweb-core/src/main/java/com/just/agentweb/AgentWebUIControllerImplBase.java",
    "content": "/*\n * Copyright (C)  Justson(https://github.com/Justson/AgentWeb)\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.just.agentweb;\n\nimport android.app.Activity;\nimport android.net.http.SslError;\nimport android.os.Handler;\nimport android.webkit.JsPromptResult;\nimport android.webkit.JsResult;\nimport android.webkit.PermissionRequest;\nimport android.webkit.SslErrorHandler;\nimport android.webkit.WebView;\n\n\n/**\n * @author cenxiaozhong\n * @date 2017/12/6\n * @since 3.0.0\n */\npublic class AgentWebUIControllerImplBase extends AbsAgentWebUIController {\n\n    public static AbsAgentWebUIController build() {\n        return new AgentWebUIControllerImplBase();\n    }\n\n    @Override\n    public void onJsAlert(WebView view, String url, String message) {\n        getDelegate().onJsAlert(view, url, message);\n    }\n\n    @Override\n    public void onOpenPagePrompt(WebView view, String url, Handler.Callback callback) {\n        getDelegate().onOpenPagePrompt(view, url, callback);\n    }\n\n    @Override\n    public void onJsConfirm(WebView view, String url, String message, JsResult jsResult) {\n        getDelegate().onJsConfirm(view, url, message, jsResult);\n    }\n\n    @Override\n    public void onSelectItemsPrompt(WebView view, String url, String[] ways, Handler.Callback callback) {\n        getDelegate().onSelectItemsPrompt(view, url, ways, callback);\n    }\n\n    @Override\n    public void onForceDownloadAlert(String url, Handler.Callback callback) {\n        getDelegate().onForceDownloadAlert(url, callback);\n    }\n\n    @Override\n    public void onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult jsPromptResult) {\n        getDelegate().onJsPrompt(view, url, message, defaultValue, jsPromptResult);\n    }\n\n    @Override\n    public void onMainFrameError(WebView view, int errorCode, String description, String failingUrl) {\n        getDelegate().onMainFrameError(view, errorCode, description, failingUrl);\n    }\n\n    @Override\n    public void onShowMainFrame() {\n        getDelegate().onShowMainFrame();\n    }\n\n    @Override\n    public void onLoading(String msg) {\n        getDelegate().onLoading(msg);\n    }\n\n    @Override\n    public void onCancelLoading() {\n        getDelegate().onCancelLoading();\n    }\n\n\n    @Override\n    public void onShowMessage(String message, String from) {\n        getDelegate().onShowMessage(message, from);\n    }\n\n    @Override\n    public void onPermissionsDeny(String[] permissions, String permissionType, String action) {\n        getDelegate().onPermissionsDeny(permissions, permissionType, action);\n    }\n\n    @Override\n    public void onShowSslCertificateErrorDialog(WebView view, SslErrorHandler handler, SslError error) {\n        getDelegate().onShowSslCertificateErrorDialog(view, handler, error);\n    }\n\n    @Override\n    public void onPermissionRequest(PermissionRequest request) {\n        getDelegate().onPermissionRequest(request);\n    }\n\n    @Override\n    protected void bindSupportWebParent(WebParentLayout webParentLayout, Activity activity) {\n        getDelegate().bindSupportWebParent(webParentLayout, activity);\n    }\n\n\n}\n"
  },
  {
    "path": "agentweb-core/src/main/java/com/just/agentweb/AgentWebUtils.java",
    "content": "/*\n * Copyright (C)  Justson(https://github.com/Justson/AgentWeb)\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.just.agentweb;\n\nimport android.annotation.SuppressLint;\nimport android.annotation.TargetApi;\nimport android.app.Activity;\nimport android.content.ContentUris;\nimport android.content.Context;\nimport android.content.Intent;\nimport android.content.pm.ApplicationInfo;\nimport android.content.pm.PackageManager;\nimport android.content.pm.ResolveInfo;\nimport android.database.Cursor;\nimport android.net.ConnectivityManager;\nimport android.net.NetworkInfo;\nimport android.net.Uri;\nimport android.os.Build;\nimport android.os.Environment;\nimport android.os.Handler;\nimport android.os.Looper;\nimport android.os.StatFs;\nimport android.provider.DocumentsContract;\nimport android.provider.MediaStore;\nimport androidx.annotation.ColorInt;\nimport androidx.annotation.NonNull;\nimport com.google.android.material.snackbar.Snackbar;\nimport androidx.core.app.AppOpsManagerCompat;\nimport androidx.core.content.ContextCompat;\nimport androidx.loader.content.CursorLoader;\nimport androidx.core.content.FileProvider;\nimport androidx.core.os.EnvironmentCompat;\nimport android.telephony.TelephonyManager;\nimport android.text.SpannableString;\nimport android.text.Spanned;\nimport android.text.TextUtils;\nimport android.text.format.DateUtils;\nimport android.text.style.ForegroundColorSpan;\nimport android.util.Log;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.view.ViewParent;\nimport android.webkit.ValueCallback;\nimport android.webkit.WebChromeClient;\nimport android.webkit.WebSettings;\nimport android.webkit.WebView;\nimport android.widget.Toast;\n\nimport org.json.JSONArray;\nimport org.json.JSONException;\nimport org.json.JSONObject;\n\nimport java.io.Closeable;\nimport java.io.File;\nimport java.io.IOException;\nimport java.lang.ref.WeakReference;\nimport java.lang.reflect.Method;\nimport java.math.BigInteger;\nimport java.security.MessageDigest;\nimport java.text.SimpleDateFormat;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.Date;\nimport java.util.List;\nimport java.util.Locale;\nimport java.util.Map;\n\nimport static com.just.agentweb.AgentWebConfig.AGENTWEB_FILE_PATH;\nimport static com.just.agentweb.AgentWebConfig.FILE_CACHE_PATH;\n\n\n/**\n * @author cenxiaozhong\n * @since 1.0.0\n */\npublic class AgentWebUtils {\n\n\tprivate static final String TAG = AgentWebUtils.class.getSimpleName();\n\tprivate static Handler mHandler = null;\n\n\tprivate AgentWebUtils() {\n\t\tthrow new UnsupportedOperationException(\"u can't init me\");\n\t}\n\n\tpublic static int dp2px(Context context, float dipValue) {\n\t\tfinal float scale = context.getResources().getDisplayMetrics().density;\n\t\treturn (int) (dipValue * scale + 0.5f);\n\t}\n\n\tstatic final void clearWebView(WebView m) {\n\t\tif (m == null) {\n\t\t\treturn;\n\t\t}\n\t\tif (Looper.myLooper() != Looper.getMainLooper()) {\n\t\t\treturn;\n\t\t}\n\t\tm.loadUrl(\"about:blank\");\n\t\tm.stopLoading();\n\t\tif (m.getHandler() != null) {\n\t\t\tm.getHandler().removeCallbacksAndMessages(null);\n\t\t}\n\t\tm.removeAllViews();\n\t\tViewGroup mViewGroup = null;\n\t\tif ((mViewGroup = ((ViewGroup) m.getParent())) != null) {\n\t\t\tmViewGroup.removeView(m);\n\t\t}\n\t\tm.setWebChromeClient(null);\n\t\tm.setWebViewClient(null);\n\t\tm.setTag(null);\n\t\tm.clearHistory();\n\t\tm.destroy();\n\t\tm = null;\n\t}\n\n\tpublic static String getAgentWebFilePath(Context context) {\n\t\tif (!TextUtils.isEmpty(AGENTWEB_FILE_PATH)) {\n\t\t\treturn AGENTWEB_FILE_PATH;\n\t\t}\n\t\tString dir = getDiskExternalCacheDir(context);\n\t\tFile mFile = new File(dir, FILE_CACHE_PATH);\n\t\ttry {\n\t\t\tif (!mFile.exists()) {\n\t\t\t\tmFile.mkdirs();\n\t\t\t}\n\t\t} catch (Throwable throwable) {\n\t\t\tLogUtils.i(TAG, \"create dir exception\");\n\t\t}\n\t\tLogUtils.i(TAG, \"path:\" + mFile.getAbsolutePath() + \"  path:\" + mFile.getPath());\n\t\treturn AGENTWEB_FILE_PATH = mFile.getAbsolutePath();\n\t}\n\n\n\tpublic static File createFileByName(Context context, String name, boolean cover) throws IOException {\n\t\tString path = getAgentWebFilePath(context);\n\t\tif (TextUtils.isEmpty(path)) {\n\t\t\treturn null;\n\t\t}\n\t\tFile mFile = new File(path, name);\n\t\tif (mFile.exists()) {\n\t\t\tif (cover) {\n\t\t\t\tmFile.delete();\n\t\t\t\tmFile.createNewFile();\n\t\t\t}\n\t\t} else {\n\t\t\tmFile.createNewFile();\n\t\t}\n\t\treturn mFile;\n\t}\n\n\tpublic static int checkNetworkType(Context context) {\n\t\tint netType = 0;\n\t\t//连接管理对象\n\t\tConnectivityManager manager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);\n\t\t//获取NetworkInfo对象\n\t\t@SuppressLint(\"MissingPermission\") NetworkInfo networkInfo = manager.getActiveNetworkInfo();\n\t\tif (networkInfo == null) {\n\t\t\treturn netType;\n\t\t}\n\t\tswitch (networkInfo.getType()) {\n\t\t\tcase ConnectivityManager.TYPE_WIFI:\n\t\t\tcase ConnectivityManager.TYPE_WIMAX:\n\t\t\tcase ConnectivityManager.TYPE_ETHERNET:\n\t\t\t\treturn 1;\n\t\t\tcase ConnectivityManager.TYPE_MOBILE:\n\t\t\t\tswitch (networkInfo.getSubtype()) {\n\t\t\t\t\tcase TelephonyManager.NETWORK_TYPE_LTE:  // 4G\n\t\t\t\t\tcase TelephonyManager.NETWORK_TYPE_HSPAP:\n\t\t\t\t\tcase TelephonyManager.NETWORK_TYPE_EHRPD:\n\t\t\t\t\t\treturn 2;\n\t\t\t\t\tcase TelephonyManager.NETWORK_TYPE_UMTS: // 3G\n\t\t\t\t\tcase TelephonyManager.NETWORK_TYPE_CDMA:\n\t\t\t\t\tcase TelephonyManager.NETWORK_TYPE_EVDO_0:\n\t\t\t\t\tcase TelephonyManager.NETWORK_TYPE_EVDO_A:\n\t\t\t\t\tcase TelephonyManager.NETWORK_TYPE_EVDO_B:\n\t\t\t\t\t\treturn 3;\n\t\t\t\t\tcase TelephonyManager.NETWORK_TYPE_GPRS: // 2G\n\t\t\t\t\tcase TelephonyManager.NETWORK_TYPE_EDGE:\n\t\t\t\t\t\treturn 4;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\treturn netType;\n\t\t\t\t}\n\n\t\t\tdefault:\n\t\t\t\treturn netType;\n\t\t}\n\t}\n\n\tpublic static long getAvailableStorage() {\n\t\ttry {\n\t\t\tStatFs stat = new StatFs(Environment.getExternalStorageDirectory().toString());\n\t\t\tif (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {\n\t\t\t\treturn stat.getAvailableBlocksLong() * stat.getBlockSizeLong();\n\t\t\t} else {\n\t\t\t\treturn (long) stat.getAvailableBlocks() * (long) stat.getBlockSize();\n\t\t\t}\n\t\t} catch (RuntimeException ex) {\n\t\t\treturn 0;\n\t\t}\n\t}\n\n\n\tpublic static Uri getUriFromFile(Context context, File file) {\n\t\tUri uri = null;\n\t\tif (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {\n\t\t\turi = getUriFromFileForN(context, file);\n\t\t} else {\n\t\t\turi = Uri.fromFile(file);\n\t\t}\n\t\treturn uri;\n\t}\n\n\tstatic Uri getUriFromFileForN(Context context, File file) {\n\t\tUri fileUri = FileProvider.getUriForFile(context, context.getPackageName() + \".AgentWebFileProvider\", file);\n\t\treturn fileUri;\n\t}\n\n\n\tstatic void setIntentDataAndType(Context context,\n\t                                 Intent intent,\n\t                                 String type,\n\t                                 File file,\n\t                                 boolean writeAble) {\n\t\tif (Build.VERSION.SDK_INT >= 24) {\n\t\t\tintent.setDataAndType(getUriFromFile(context, file), type);\n\t\t\tintent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);\n\t\t\tif (writeAble) {\n\t\t\t\tintent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);\n\t\t\t}\n\t\t} else {\n\t\t\tintent.setDataAndType(Uri.fromFile(file), type);\n\t\t}\n\t}\n\n\n\tstatic void setIntentData(Context context,\n\t                          Intent intent,\n\t                          File file,\n\t                          boolean writeAble) {\n\t\tif (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {\n\t\t\tintent.setData(getUriFromFile(context, file));\n\t\t\tintent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);\n\t\t\tif (writeAble) {\n\t\t\t\tintent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);\n\t\t\t}\n\t\t} else {\n\t\t\tintent.setData(Uri.fromFile(file));\n\t\t}\n\t}\n\n\tstatic String getDiskExternalCacheDir(Context context) {\n\t\tFile mFile = context.getExternalCacheDir();\n\t\tif (Environment.MEDIA_MOUNTED.equals(EnvironmentCompat.getStorageState(mFile))) {\n\t\t\treturn mFile.getAbsolutePath();\n\t\t}\n\t\treturn null;\n\t}\n\n\tstatic void grantPermissions(Context context, Intent intent, Uri uri, boolean writeAble) {\n\t\tint flag = Intent.FLAG_GRANT_READ_URI_PERMISSION;\n\t\tif (writeAble) {\n\t\t\tflag |= Intent.FLAG_GRANT_WRITE_URI_PERMISSION;\n\t\t}\n\t\tintent.addFlags(flag);\n\t\tList<ResolveInfo> resInfoList = context.getPackageManager()\n\t\t\t\t.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);\n\t\tfor (ResolveInfo resolveInfo : resInfoList) {\n\t\t\tString packageName = resolveInfo.activityInfo.packageName;\n\t\t\tcontext.grantUriPermission(packageName, uri, flag);\n\t\t}\n\t}\n\n\n\tprivate static String getMIMEType(File f) {\n\t\tString type = \"\";\n\t\tString fName = f.getName();\n\t\t/* 取得扩展名 */\n\t\tString end = fName.substring(fName.lastIndexOf(\".\") + 1, fName.length()).toLowerCase();\n\t\t/* 依扩展名的类型决定MimeType */\n\t\tif (end.equals(\"pdf\")) {\n\t\t\ttype = \"application/pdf\";//\n\t\t} else if (end.equals(\"m4a\") || end.equals(\"mp3\") || end.equals(\"mid\") ||\n\t\t\t\tend.equals(\"xmf\") || end.equals(\"ogg\") || end.equals(\"wav\")) {\n\t\t\ttype = \"audio/*\";\n\t\t} else if (end.equals(\"3gp\") || end.equals(\"mp4\")) {\n\t\t\ttype = \"video/*\";\n\t\t} else if (end.equals(\"jpg\") || end.equals(\"gif\") || end.equals(\"png\") ||\n\t\t\t\tend.equals(\"jpeg\") || end.equals(\"bmp\")) {\n\t\t\ttype = \"image/*\";\n\t\t} else if (end.equals(\"apk\")) {\n\t\t\ttype = \"application/vnd.android.package-archive\";\n\t\t} else if (end.equals(\"pptx\") || end.equals(\"ppt\")) {\n\t\t\ttype = \"application/vnd.ms-powerpoint\";\n\t\t} else if (end.equals(\"docx\") || end.equals(\"doc\")) {\n\t\t\ttype = \"application/vnd.ms-word\";\n\t\t} else if (end.equals(\"xlsx\") || end.equals(\"xls\")) {\n\t\t\ttype = \"application/vnd.ms-excel\";\n\t\t} else {\n\t\t\ttype = \"*/*\";\n\t\t}\n\t\treturn type;\n\t}\n\n\n\tprivate static WeakReference<Snackbar> snackbarWeakReference;\n\n\tstatic void show(View parent,\n\t                 CharSequence text,\n\t                 int duration,\n\t                 @ColorInt int textColor,\n\t                 @ColorInt int bgColor,\n\t                 CharSequence actionText,\n\t                 @ColorInt int actionTextColor,\n\t                 View.OnClickListener listener) {\n\t\tSpannableString spannableString = new SpannableString(text);\n\t\tForegroundColorSpan colorSpan = new ForegroundColorSpan(textColor);\n\t\tspannableString.setSpan(colorSpan, 0, spannableString.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);\n\t\tsnackbarWeakReference = new WeakReference<>(Snackbar.make(parent, spannableString, duration));\n\t\tSnackbar snackbar = snackbarWeakReference.get();\n\t\tView view = snackbar.getView();\n\t\tview.setBackgroundColor(bgColor);\n\t\tif (actionText != null && actionText.length() > 0 && listener != null) {\n\t\t\tsnackbar.setActionTextColor(actionTextColor);\n\t\t\tsnackbar.setAction(actionText, listener);\n\t\t}\n\t\tsnackbar.show();\n\t}\n\n\tstatic void dismiss() {\n\t\tif (snackbarWeakReference != null && snackbarWeakReference.get() != null) {\n\t\t\tsnackbarWeakReference.get().dismiss();\n\t\t\tsnackbarWeakReference = null;\n\t\t}\n\t}\n\n\tpublic static boolean checkWifi(Context context) {\n\t\tConnectivityManager connectivity = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);\n\t\tif (connectivity == null) {\n\t\t\treturn false;\n\t\t}\n\t\t@SuppressLint(\"MissingPermission\") NetworkInfo info = connectivity.getActiveNetworkInfo();\n\t\treturn info != null && info.isConnected() && info.getType() == ConnectivityManager.TYPE_WIFI;\n\t}\n\n\tpublic static boolean checkNetwork(Context context) {\n\t\tConnectivityManager connectivity = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);\n\t\tif (connectivity == null) {\n\t\t\treturn false;\n\t\t}\n\t\t@SuppressLint(\"MissingPermission\") NetworkInfo info = connectivity.getActiveNetworkInfo();\n\t\treturn info != null && info.isConnected();\n\t}\n\n\tstatic boolean isOverriedMethod(Object currentObject, String methodName, String method, Class... clazzs) {\n\t\tLogUtils.i(TAG, \"  methodName:\" + methodName + \"   method:\" + method);\n\t\tboolean tag = false;\n\t\tif (currentObject == null) {\n\t\t\treturn tag;\n\t\t}\n\t\ttry {\n\t\t\tClass clazz = currentObject.getClass();\n\t\t\tMethod mMethod = clazz.getMethod(methodName, clazzs);\n\t\t\tString gStr = mMethod.toGenericString();\n\t\t\ttag = !gStr.contains(method);\n\t\t} catch (Exception igonre) {\n\t\t\tif (LogUtils.isDebug()) {\n\t\t\t\tigonre.printStackTrace();\n\t\t\t}\n\t\t}\n\n\t\tLogUtils.i(TAG, \"isOverriedMethod:\" + tag);\n\t\treturn tag;\n\t}\n\n\tstatic Method isExistMethod(Object o, String methodName, Class... clazzs) {\n\t\tif (null == o) {\n\t\t\treturn null;\n\t\t}\n\t\ttry {\n\t\t\tClass clazz = o.getClass();\n\t\t\tMethod mMethod = clazz.getDeclaredMethod(methodName, clazzs);\n\t\t\tmMethod.setAccessible(true);\n\t\t\treturn mMethod;\n\t\t} catch (Throwable ignore) {\n//\t\t\tif (LogUtils.isDebug()) {\n//\t\t\t\tignore.printStackTrace();\n//\t\t\t}\n\t\t}\n\t\treturn null;\n\n\t}\n\n\tstatic void clearAgentWebCache(Context context) {\n\t\ttry {\n\t\t\tclearCacheFolder(new File(getAgentWebFilePath(context)), 0);\n\t\t} catch (Throwable throwable) {\n\t\t\tif (LogUtils.isDebug()) {\n\t\t\t\tthrowable.printStackTrace();\n\t\t\t}\n\t\t}\n\t}\n\n\tstatic void clearWebViewAllCache(Context context, WebView webView) {\n\t\ttry {\n\t\t\tAgentWebConfig.removeAllCookies(null);\n\t\t\twebView.getSettings().setCacheMode(WebSettings.LOAD_NO_CACHE);\n\t\t\tcontext.deleteDatabase(\"webviewCache.db\");\n\t\t\tcontext.deleteDatabase(\"webview.db\");\n\t\t\twebView.clearCache(true);\n\t\t\twebView.clearHistory();\n\t\t\twebView.clearFormData();\n\t\t\tclearCacheFolder(new File(AgentWebConfig.getCachePath(context)), 0);\n\n\t\t} catch (Exception ignore) {\n\t\t\t//ignore.printStackTrace();\n\t\t\tif (AgentWebConfig.DEBUG) {\n\t\t\t\tignore.printStackTrace();\n\t\t\t}\n\t\t}\n\t}\n\n\tstatic void clearWebViewAllCache(Context context) {\n\t\ttry {\n\t\t\tclearWebViewAllCache(context, new LollipopFixedWebView(context.getApplicationContext()));\n\t\t} catch (Exception e) {\n\t\t\te.printStackTrace();\n\t\t}\n\t}\n\n\tstatic int clearCacheFolder(final File dir, final int numDays) {\n\t\tint deletedFiles = 0;\n\t\tif (dir != null) {\n\t\t\tLog.i(\"Info\", \"dir:\" + dir.getAbsolutePath());\n\t\t}\n\t\tif (dir != null && dir.isDirectory()) {\n\t\t\ttry {\n\t\t\t\tfor (File child : dir.listFiles()) {\n\n\t\t\t\t\t//first delete subdirectories recursively\n\t\t\t\t\tif (child.isDirectory()) {\n\t\t\t\t\t\tdeletedFiles += clearCacheFolder(child, numDays);\n\t\t\t\t\t}\n\n\t\t\t\t\t//then delete the files and subdirectories in this dir\n\t\t\t\t\t//only empty directories can be deleted, so subdirs have been done first\n\t\t\t\t\tif (child.lastModified() < new Date().getTime() - numDays * DateUtils.DAY_IN_MILLIS) {\n\t\t\t\t\t\tLog.i(TAG, \"file name:\" + child.getName());\n\t\t\t\t\t\tif (child.delete()) {\n\t\t\t\t\t\t\tdeletedFiles++;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch (Exception e) {\n\t\t\t\tLog.e(\"Info\", String.format(\"Failed to clean the cache, result %s\", e.getMessage()));\n\t\t\t}\n\t\t}\n\t\treturn deletedFiles;\n\t}\n\n\n\tstatic void clearCache(final Context context, final int numDays) {\n\t\tLog.i(\"Info\", String.format(\"Starting cache prune, deleting files older than %d days\", numDays));\n\t\tint numDeletedFiles = clearCacheFolder(context.getCacheDir(), numDays);\n\t\tLog.i(\"Info\", String.format(\"Cache pruning completed, %d files deleted\", numDeletedFiles));\n\t}\n\n\tpublic static String[] uriToPath(Activity activity, Uri[] uris) {\n\t\tif (activity == null || uris == null || uris.length == 0) {\n\t\t\treturn null;\n\t\t}\n\t\ttry {\n\t\t\tString[] paths = new String[uris.length];\n\t\t\tint i = 0;\n\t\t\tfor (Uri mUri : uris) {\n\t\t\t\tpaths[i++] = Build.VERSION.SDK_INT > Build.VERSION_CODES.JELLY_BEAN_MR2 ? getFileAbsolutePath(activity, mUri) : getRealPathBelowVersion(activity, mUri);\n\n\t\t\t}\n\t\t\treturn paths;\n\t\t} catch (Throwable throwable) {\n\t\t\tif (LogUtils.isDebug()) {\n\t\t\t\tthrowable.printStackTrace();\n\t\t\t}\n\t\t}\n\t\treturn null;\n\n\t}\n\n\tprivate static String getRealPathBelowVersion(Context context, Uri uri) {\n\t\tString filePath = null;\n\t\tLogUtils.i(TAG, \"method -> getRealPathBelowVersion \" + uri + \"   path:\" + uri.getPath() + \"    getAuthority:\" + uri.getAuthority());\n\t\tString[] projection = {MediaStore.Images.Media.DATA};\n\t\tCursorLoader loader = new CursorLoader(context, uri, projection, null,\n\t\t\t\tnull, null);\n\t\tCursor cursor = loader.loadInBackground();\n\t\tif (cursor != null) {\n\t\t\tcursor.moveToFirst();\n\t\t\tfilePath = cursor.getString(cursor.getColumnIndex(projection[0]));\n\t\t\tcursor.close();\n\t\t}\n\t\tif (filePath == null) {\n\t\t\tfilePath = uri.getPath();\n\t\t}\n\t\treturn filePath;\n\t}\n\n\tpublic static File createImageFile(Context context) {\n\t\tFile mFile = null;\n\t\ttry {\n\t\t\tString timeStamp =\n\t\t\t\t\tnew SimpleDateFormat(\"yyyyMMddHHmmss\", Locale.getDefault()).format(new Date());\n\t\t\tString imageName = String.format(\"aw_%s.jpg\", timeStamp);\n\t\t\tmFile = createFileByName(context, imageName, true);\n\t\t} catch (Throwable e) {\n\t\t\te.printStackTrace();\n\t\t}\n\t\treturn mFile;\n\t}\n\n\tstatic File createVideoFile(Context context){\n\t\tFile mFile = null;\n\t\ttry {\n\t\t\tString timeStamp =\n\t\t\t\t\tnew SimpleDateFormat(\"yyyyMMddHHmmss\", Locale.getDefault()).format(new Date());\n\t\t\tString imageName = String.format(\"aw_%s.mp4\", timeStamp);  //默认生成mp4\n\t\t\tmFile = createFileByName(context, imageName, true);\n\t\t} catch (Throwable e) {\n\t\t\te.printStackTrace();\n\t\t}\n\t\treturn mFile;\n\t}\n\n\n\tpublic static void closeIO(Closeable closeable) {\n\t\ttry {\n\t\t\tif (closeable != null) {\n\t\t\t\tcloseable.close();\n\t\t\t}\n\t\t} catch (Exception e) {\n\t\t\te.printStackTrace();\n\t\t}\n\t}\n\n\t@TargetApi(19)\n\tstatic String getFileAbsolutePath(Activity context, Uri fileUri) {\n\t\tif (context == null || fileUri == null) {\n\t\t\treturn null;\n\t\t}\n//\t\tLogUtils.i(TAG, \"getAuthority:\" + fileUri.getAuthority() + \"  getHost:\" + fileUri.getHost() + \"   getPath:\" + fileUri.getPath() + \"  getScheme:\" + fileUri.getScheme() + \"  query:\" + fileUri.getQuery());\n\t\tif (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT && DocumentsContract.isDocumentUri(context, fileUri)) {\n\t\t\tif (isExternalStorageDocument(fileUri)) {\n\t\t\t\tString docId = DocumentsContract.getDocumentId(fileUri);\n\t\t\t\tString[] split = docId.split(\":\");\n\t\t\t\tString type = split[0];\n\t\t\t\tif (\"primary\".equalsIgnoreCase(type)) {\n\t\t\t\t\treturn Environment.getExternalStorageDirectory() + \"/\" + split[1];\n\t\t\t\t}\n\t\t\t} else if (isDownloadsDocument(fileUri)) {\n\t\t\t\tString id = DocumentsContract.getDocumentId(fileUri);\n\t\t\t\tUri contentUri = ContentUris.withAppendedId(Uri.parse(\"content://downloads/public_downloads\"), Long.valueOf(id));\n\t\t\t\treturn getDataColumn(context, contentUri, null, null);\n\t\t\t} else if (isMediaDocument(fileUri)) {\n\t\t\t\tString docId = DocumentsContract.getDocumentId(fileUri);\n\t\t\t\tString[] split = docId.split(\":\");\n\t\t\t\tString type = split[0];\n\n\t\t\t\tUri contentUri = null;\n\t\t\t\tif (\"image\".equals(type)) {\n\t\t\t\t\tcontentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;\n\t\t\t\t} else if (\"video\".equals(type)) {\n\t\t\t\t\tcontentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;\n\t\t\t\t} else if (\"audio\".equals(type)) {\n\t\t\t\t\tcontentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;\n\t\t\t\t}\n\t\t\t\tString selection = MediaStore.Images.Media._ID + \"=?\";\n\t\t\t\tString[] selectionArgs = new String[]{split[1]};\n\t\t\t\treturn getDataColumn(context, contentUri, selection, selectionArgs);\n\t\t\t} else {\n\t\t\t}\n\t\t} // MediaStore (and general)\n\t\telse if (fileUri.getAuthority().equalsIgnoreCase(context.getPackageName() + \".AgentWebFileProvider\")) {\n\t\t\tString path = fileUri.getPath();\n\t\t\tint index = path.lastIndexOf(\"/\");\n\t\t\treturn getAgentWebFilePath(context) + File.separator + path.substring(index + 1, path.length());\n\t\t} else if (\"content\".equalsIgnoreCase(fileUri.getScheme())) {\n\t\t\t// Return the remote address\n\t\t\tif (isGooglePhotosUri(fileUri)) {\n\t\t\t\treturn fileUri.getLastPathSegment();\n\t\t\t}\n\t\t\treturn getDataColumn(context, fileUri, null, null);\n\t\t}\n\t\t// File\n\t\telse if (\"file\".equalsIgnoreCase(fileUri.getScheme())) {\n\t\t\treturn fileUri.getPath();\n\t\t}\n\t\treturn null;\n\t}\n\n\tstatic String getDataColumn(Context context, Uri uri, String selection, String[] selectionArgs) {\n\t\tCursor cursor = null;\n\t\tString[] projection = {MediaStore.Images.Media.DATA};\n\t\ttry {\n\t\t\tcursor = context.getContentResolver().query(uri, projection, selection, selectionArgs, null);\n\t\t\tif (cursor != null && cursor.moveToFirst()) {\n\t\t\t\tint index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);\n\t\t\t\treturn cursor.getString(index);\n\t\t\t}\n\t\t} finally {\n\t\t\tif (cursor != null) {\n\t\t\t\tcursor.close();\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\t/**\n\t * @param uri The Uri to check.\n\t * @return Whether the Uri authority is ExternalStorageProvider.\n\t */\n\tstatic boolean isExternalStorageDocument(Uri uri) {\n\t\treturn \"com.android.externalstorage.documents\".equals(uri.getAuthority());\n\t}\n\n\t/**\n\t * @param uri The Uri to check.\n\t * @return Whether the Uri authority is DownloadsProvider.\n\t */\n\tstatic boolean isDownloadsDocument(Uri uri) {\n\t\treturn \"com.android.providers.downloads.documents\".equals(uri.getAuthority());\n\t}\n\n\t/**\n\t * @param uri The Uri to check.\n\t * @return Whether the Uri authority is MediaProvider.\n\t */\n\tstatic boolean isMediaDocument(Uri uri) {\n\t\treturn \"com.android.providers.media.documents\".equals(uri.getAuthority());\n\t}\n\n\t/**\n\t * @param uri The Uri to check.\n\t * @return Whether the Uri authority is Google Photos.\n\t */\n\tstatic boolean isGooglePhotosUri(Uri uri) {\n\t\treturn \"com.google.android.apps.photos.content\".equals(uri.getAuthority());\n\t}\n\n\tstatic Intent getInstallApkIntentCompat(Context context, File file) {\n\t\tIntent mIntent = new Intent().setAction(Intent.ACTION_VIEW);\n\t\tsetIntentDataAndType(context, mIntent, \"application/vnd.android.package-archive\", file, false);\n\t\treturn mIntent;\n\t}\n\n\tpublic static Intent getCommonFileIntentCompat(Context context, File file) {\n\t\tIntent mIntent = new Intent().setAction(Intent.ACTION_VIEW);\n\t\tsetIntentDataAndType(context, mIntent, getMIMEType(file), file, false);\n\t\treturn mIntent;\n\t}\n\n\tstatic Intent getIntentCaptureCompat(Context context, File file) {\n\t\tIntent mIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);\n\t\tUri mUri = getUriFromFile(context, file);\n\t\tmIntent.addCategory(Intent.CATEGORY_DEFAULT);\n\t\tmIntent.putExtra(MediaStore.EXTRA_OUTPUT, mUri);\n\t\treturn mIntent;\n\t}\n\n\tstatic Intent getIntentVideoCompat(Context context, File file){\n\t\tIntent mIntent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);\n\t\tUri mUri = getUriFromFile(context, file);\n\t\tmIntent.addCategory(Intent.CATEGORY_DEFAULT);\n\t\tmIntent.putExtra(MediaStore.EXTRA_OUTPUT, mUri);\n\t\treturn mIntent;\n\t}\n\n\n\tstatic boolean isJson(String target) {\n\t\tif (TextUtils.isEmpty(target)) {\n\t\t\treturn false;\n\t\t}\n\t\tboolean tag = false;\n\t\ttry {\n\t\t\tif (target.startsWith(\"[\")) {\n\t\t\t\tnew JSONArray(target);\n\t\t\t} else {\n\t\t\t\tnew JSONObject(target);\n\t\t\t}\n\t\t\ttag = true;\n\t\t} catch (JSONException ignore) {\n//            ignore.printStackTrace();\n\t\t\ttag = false;\n\t\t}\n\t\treturn tag;\n\t}\n\n\tpublic static boolean isUIThread() {\n\t\treturn Looper.myLooper() == Looper.getMainLooper();\n\t}\n\n\tstatic boolean isEmptyCollection(Collection collection) {\n\t\treturn collection == null || collection.isEmpty();\n\t}\n\n\tstatic boolean isEmptyMap(Map map) {\n\t\treturn map == null || map.isEmpty();\n\t}\n\n\tprivate static Toast mToast = null;\n\tstatic void toastShowShort(Context context, String msg) {\n\t\tif (mToast == null) {\n\t\t\tmToast = Toast.makeText(context.getApplicationContext(), msg, Toast.LENGTH_SHORT);\n\t\t} else {\n\t\t\tmToast.setText(msg);\n\t\t}\n\t\tmToast.show();\n\t}\n\n\t@Deprecated\n\tstatic void getUIControllerAndShowMessage(Activity activity, String message, String from) {\n\t\tif (activity == null || activity.isFinishing()) {\n\t\t\treturn;\n\t\t}\n\t\tWebParentLayout mWebParentLayout = (WebParentLayout) activity.findViewById(R.id.web_parent_layout_id);\n\t\tAbsAgentWebUIController mAgentWebUIController = mWebParentLayout.provide();\n\t\tif (mAgentWebUIController != null) {\n\t\t\tmAgentWebUIController.onShowMessage(message, from);\n\t\t}\n\t}\n\n\tpublic static boolean hasPermission(@NonNull Context context, @NonNull String... permissions) {\n\t\treturn hasPermission(context, Arrays.asList(permissions));\n\t}\n\n\tpublic static boolean hasPermission(@NonNull Context context, @NonNull List<String> permissions) {\n\t\tif (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {\n\t\t\treturn true;\n\t\t}\n\t\tfor (String permission : permissions) {\n\t\t\tint result = ContextCompat.checkSelfPermission(context, permission);\n\t\t\tif (result == PackageManager.PERMISSION_DENIED) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tString op = AppOpsManagerCompat.permissionToOp(permission);\n\t\t\tif (TextUtils.isEmpty(op)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tresult = AppOpsManagerCompat.noteProxyOp(context, op, context.getPackageName());\n\t\t\tif (result != AppOpsManagerCompat.MODE_ALLOWED) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\tpublic static List<String> getDeniedPermissions(Activity activity, String[] permissions) {\n\t\tif (permissions == null || permissions.length == 0) {\n\t\t\treturn Collections.EMPTY_LIST;\n\t\t}\n\t\tList<String> deniedPermissions = new ArrayList<>(permissions.length);\n\t\tfor (int i = 0; i < permissions.length; i++) {\n\t\t\tif (!hasPermission(activity, permissions[i])) {\n\t\t\t\tdeniedPermissions.add(permissions[i]);\n\t\t\t}\n\t\t}\n\t\treturn deniedPermissions;\n\t}\n\n\n\tpublic static AbsAgentWebUIController getAgentWebUIControllerByWebView(WebView webView) {\n\t\tWebParentLayout mWebParentLayout = getWebParentLayoutByWebView(webView);\n\t\treturn mWebParentLayout.provide();\n\t}\n\n\t//获取应用的名称\n\tpublic static String getApplicationName(Context context) {\n\t\tPackageManager packageManager = null;\n\t\tApplicationInfo applicationInfo = null;\n\t\ttry {\n\t\t\tpackageManager = context.getApplicationContext().getPackageManager();\n\t\t\tapplicationInfo = packageManager.getApplicationInfo(context.getPackageName(), 0);\n\t\t} catch (PackageManager.NameNotFoundException e) {\n\t\t\tapplicationInfo = null;\n\t\t}\n\t\tString applicationName =\n\t\t\t\t(String) packageManager.getApplicationLabel(applicationInfo);\n\t\treturn applicationName;\n\t}\n\n\tstatic WebParentLayout getWebParentLayoutByWebView(WebView webView) {\n\t\tViewGroup mViewGroup = null;\n\t\tif (!(webView.getParent() instanceof ViewGroup)) {\n\t\t\tthrow new IllegalStateException(\"please check webcreator's create method was be called ?\");\n\t\t}\n\t\tmViewGroup = (ViewGroup) webView.getParent();\n\t\tAbsAgentWebUIController mAgentWebUIController;\n\t\twhile (mViewGroup != null) {\n\n\t\t\tLogUtils.i(TAG, \"ViewGroup:\" + mViewGroup);\n\t\t\tif (mViewGroup.getId() == R.id.web_parent_layout_id) {\n\t\t\t\tWebParentLayout mWebParentLayout = (WebParentLayout) mViewGroup;\n\t\t\t\tLogUtils.i(TAG, \"found WebParentLayout\");\n\t\t\t\treturn mWebParentLayout;\n\t\t\t} else {\n\t\t\t\tViewParent mViewParent = mViewGroup.getParent();\n\t\t\t\tif (mViewParent instanceof ViewGroup) {\n\t\t\t\t\tmViewGroup = (ViewGroup) mViewParent;\n\t\t\t\t} else {\n\t\t\t\t\tmViewGroup = null;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tthrow new IllegalStateException(\"please check webcreator's create method was be called ?\");\n\t}\n\n\tpublic static void runInUiThread(Runnable runnable) {\n\t\tif (mHandler == null) {\n\t\t\tmHandler = new Handler(Looper.getMainLooper());\n\t\t}\n\t\tmHandler.post(runnable);\n\t}\n\n\tpublic static boolean showFileChooserCompat(Activity activity,\n\t                                     WebView webView,\n\t                                     ValueCallback<Uri[]> valueCallbacks,\n\t                                     WebChromeClient.FileChooserParams fileChooserParams,\n\t                                     PermissionInterceptor permissionInterceptor,\n\t                                     ValueCallback valueCallback,\n\t                                     String mimeType,\n\t                                     Handler.Callback jsChannelCallback\n\t) {\n\t\ttry {\n\t\t\tClass<?> clz = Class.forName(\"com.just.agentweb.filechooser.FileChooser\");\n\t\t\tObject mFileChooser$Builder = clz.getDeclaredMethod(\"newBuilder\",\n\t\t\t\t\tActivity.class, WebView.class)\n\t\t\t\t\t.invoke(null, activity, webView);\n\t\t\tclz = mFileChooser$Builder.getClass();\n\t\t\tMethod mMethod = null;\n\t\t\tif (valueCallbacks != null) {\n\t\t\t\tmMethod = clz.getDeclaredMethod(\"setUriValueCallbacks\", ValueCallback.class);\n\t\t\t\tmMethod.setAccessible(true);\n\t\t\t\tmMethod.invoke(mFileChooser$Builder, valueCallbacks);\n\t\t\t}\n\t\t\tif (fileChooserParams != null) {\n\t\t\t\tmMethod = clz.getDeclaredMethod(\"setFileChooserParams\", WebChromeClient.FileChooserParams.class);\n\t\t\t\tmMethod.setAccessible(true);\n\t\t\t\tmMethod.invoke(mFileChooser$Builder, fileChooserParams);\n\t\t\t}\n\t\t\tif (valueCallback != null) {\n\t\t\t\tmMethod = clz.getDeclaredMethod(\"setUriValueCallback\", ValueCallback.class);\n\t\t\t\tmMethod.setAccessible(true);\n\t\t\t\tmMethod.invoke(mFileChooser$Builder, valueCallback);\n\t\t\t}\n\t\t\tif (!TextUtils.isEmpty(mimeType)) {\n//                LogUtils.i(TAG, Arrays.toString(clz.getDeclaredMethods()));\n\t\t\t\tmMethod = clz.getDeclaredMethod(\"setAcceptType\", String.class);\n\t\t\t\tmMethod.setAccessible(true);\n\t\t\t\tmMethod.invoke(mFileChooser$Builder, mimeType);\n\t\t\t}\n\t\t\tif (jsChannelCallback != null) {\n\t\t\t\tmMethod = clz.getDeclaredMethod(\"setJsChannelCallback\", Handler.Callback.class);\n\t\t\t\tmMethod.setAccessible(true);\n\t\t\t\tmMethod.invoke(mFileChooser$Builder, jsChannelCallback);\n\t\t\t}\n\t\t\tmMethod = clz.getDeclaredMethod(\"setPermissionInterceptor\", PermissionInterceptor.class);\n\t\t\tmMethod.setAccessible(true);\n\t\t\tmMethod.invoke(mFileChooser$Builder, permissionInterceptor);\n\t\t\tmMethod = clz.getDeclaredMethod(\"build\");\n\t\t\tmMethod.setAccessible(true);\n\t\t\tObject mFileChooser = mMethod.invoke(mFileChooser$Builder);\n\t\t\tmMethod = mFileChooser.getClass().getDeclaredMethod(\"openFileChooser\");\n\t\t\tmMethod.setAccessible(true);\n\t\t\tmMethod.invoke(mFileChooser);\n\t\t} catch (Throwable throwable) {\n\t\t\tif (LogUtils.isDebug()) {\n\t\t\t\tthrowable.printStackTrace();\n\t\t\t}\n\t\t\tif (throwable instanceof ClassNotFoundException) {\n\t\t\t\tLogUtils.e(TAG, \"Please check whether compile'com.just.agentweb:filechooser:x.x.x' dependency was added.\");\n\t\t\t}\n\t\t\tif (valueCallbacks != null) {\n\t\t\t\tLogUtils.i(TAG, \"onReceiveValue empty\");\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tif (valueCallback != null) {\n\t\t\t\tvalueCallback.onReceiveValue(null);\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\tpublic static String md5(String str) {\n\t\ttry {\n\t\t\tMessageDigest md = MessageDigest.getInstance(\"MD5\");\n\t\t\tmd.update(str.getBytes());\n\t\t\treturn new BigInteger(1, md.digest()).toString(16);\n\t\t} catch (Exception e) {\n\t\t\tif (LogUtils.isDebug()) {\n\t\t\t\te.printStackTrace();\n\t\t\t}\n\t\t}\n\t\treturn \"\";\n\t}\n}\n"
  },
  {
    "path": "agentweb-core/src/main/java/com/just/agentweb/AgentWebView.java",
    "content": "/*\n * Copyright (C)  Justson(https://github.com/Justson/AgentWeb)\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.just.agentweb;\n\nimport android.annotation.TargetApi;\nimport android.content.Context;\nimport android.graphics.Bitmap;\nimport android.os.Build;\nimport android.util.AttributeSet;\nimport android.util.Log;\nimport android.util.Pair;\nimport android.view.ActionMode;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.view.ViewParent;\nimport android.view.accessibility.AccessibilityManager;\nimport android.webkit.JsPromptResult;\nimport android.webkit.WebBackForwardList;\nimport android.webkit.WebChromeClient;\nimport android.webkit.WebView;\nimport android.webkit.WebViewClient;\nimport android.widget.Toast;\n\nimport org.json.JSONObject;\n\nimport java.lang.reflect.Field;\nimport java.lang.reflect.Method;\nimport java.net.URI;\nimport java.net.URLEncoder;\nimport java.util.HashMap;\nimport java.util.Map;\n\n\n/**\n * @author cenxiaozhong\n * @since 1.0.0\n */\npublic class AgentWebView extends LollipopFixedWebView {\n    private static final String TAG = AgentWebView.class.getSimpleName();\n    private Map<String, JsCallJava> mJsCallJavas;\n    private Map<String, String> mInjectJavaScripts;\n    private FixedOnReceivedTitle mFixedOnReceivedTitle;\n    private boolean mIsInited;\n    private Boolean mIsAccessibilityEnabledOriginal;\n\n    public AgentWebView(Context context) {\n        this(context, null);\n    }\n\n    public AgentWebView(Context context, AttributeSet attrs) {\n        super(context, attrs);\n        removeSearchBoxJavaBridge();\n        mIsInited = true;\n        mFixedOnReceivedTitle = new FixedOnReceivedTitle();\n    }\n\n    /**\n     * 经过大量的测试，按照以下方式才能保证JS脚本100%注入成功：\n     * 1、在第一次loadUrl之前注入JS（在addJavascriptInterface里面注入即可，setWebViewClient和setWebChromeClient要在addJavascriptInterface之前执行）；\n     * 2、在webViewClient.onPageStarted中都注入JS；\n     * 3、在webChromeClient.onProgressChanged中都注入JS，并且不能通过自检查（onJsPrompt里面判断）JS是否注入成功来减少注入JS的次数，因为网页中的JS可以同时打开多个url导致无法控制检查的准确性；\n     *\n     * @deprecated Android 4.2.2及以上版本的 addJavascriptInterface 方法已经解决了安全问题，如果不使用“网页能将JS函数传到Java层”功能，不建议使用该类，毕竟系统的JS注入效率才是最高的；\n     */\n    @Override\n    @Deprecated\n    public final void addJavascriptInterface(Object interfaceObj, String interfaceName) {\n\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {\n            super.addJavascriptInterface(interfaceObj, interfaceName);\n            Log.i(TAG, \"注入\");\n            return;\n        } else {\n            Log.i(TAG, \"use mJsCallJavas:\" + interfaceName);\n        }\n\n        LogUtils.i(TAG, \"addJavascriptInterface:\" + interfaceObj + \"   interfaceName:\" + interfaceName);\n        if (mJsCallJavas == null) {\n            mJsCallJavas = new HashMap<String, JsCallJava>();\n        }\n        mJsCallJavas.put(interfaceName, new JsCallJava(interfaceObj, interfaceName));\n        injectJavaScript();\n        if (LogUtils.isDebug()) {\n            Log.d(TAG, \"injectJavaScript, addJavascriptInterface.interfaceObj = \" + interfaceObj + \", interfaceName = \" + interfaceName);\n        }\n        addJavascriptInterfaceSupport(interfaceObj, interfaceName);\n    }\n\n    protected void addJavascriptInterfaceSupport(Object interfaceObj, String interfaceName) {\n    }\n\n    @Override\n    public final void setWebChromeClient(WebChromeClient client) {\n        AgentWebChrome mAgentWebChrome = new AgentWebChrome(this);\n        mAgentWebChrome.setDelegate(client);\n        mFixedOnReceivedTitle.setWebChromeClient(client);\n        super.setWebChromeClient(mAgentWebChrome);\n        setWebChromeClientSupport(mAgentWebChrome);\n    }\n\n    protected final void setWebChromeClientSupport(WebChromeClient client) {\n    }\n\n    @Override\n    public final void setWebViewClient(WebViewClient client) {\n        AgentWebClient mAgentWebClient = new AgentWebClient(this);\n        mAgentWebClient.setDelegate(client);\n        super.setWebViewClient(mAgentWebClient);\n        setWebViewClientSupport(mAgentWebClient);\n    }\n\n    public final void setWebViewClientSupport(WebViewClient client) {\n    }\n\n    @Override\n    public ActionMode startActionMode(ActionMode.Callback callback) {\n        return super.startActionMode(callback);\n    }\n\n    @Override\n    public void destroy() {\n        setVisibility(View.GONE);\n        if (mJsCallJavas != null) {\n            mJsCallJavas.clear();\n        }\n        if (mInjectJavaScripts != null) {\n            mInjectJavaScripts.clear();\n        }\n        removeAllViewsInLayout();\n        fixedStillAttached();\n        releaseConfigCallback();\n        if (mIsInited) {\n            resetAccessibilityEnabled();\n            LogUtils.i(TAG, \"destroy web\");\n            super.destroy();\n        }\n    }\n\n    @Override\n    public void clearHistory() {\n        if (mIsInited) {\n            super.clearHistory();\n        }\n    }\n\n    public static Pair<Boolean, String> isWebViewPackageException(Throwable e) {\n        String messageCause = e.getCause() == null ? e.toString() : e.getCause().toString();\n        String trace = Log.getStackTraceString(e);\n        if (trace.contains(\"android.content.pm.PackageManager$NameNotFoundException\")\n                || trace.contains(\"java.lang.RuntimeException: Cannot load WebView\")\n                || trace.contains(\"android.webkit.WebViewFactory$MissingWebViewPackageException: Failed to load WebView provider: No WebView installed\")) {\n\n            LogUtils.safeCheckCrash(TAG, \"isWebViewPackageException\", e);\n            return new Pair<Boolean, String>(true, \"WebView load failed, \" + messageCause);\n        }\n        return new Pair<Boolean, String>(false, messageCause);\n    }\n\n    @Override\n    public void setOverScrollMode(int mode) {\n        try {\n            super.setOverScrollMode(mode);\n        } catch (Throwable e) {\n            Pair<Boolean, String> pair = isWebViewPackageException(e);\n            if (pair.first) {\n                Toast.makeText(getContext(), pair.second, Toast.LENGTH_SHORT).show();\n                destroy();\n            } else {\n                throw e;\n            }\n        }\n    }\n\n    @Override\n    public boolean isPrivateBrowsingEnabled() {\n        if (Build.VERSION.SDK_INT == Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1\n                && getSettings() == null) {\n\n            return false; // getSettings().isPrivateBrowsingEnabled()\n        } else {\n            return super.isPrivateBrowsingEnabled();\n        }\n    }\n\n    /**\n     * 添加并注入JavaScript脚本（和“addJavascriptInterface”注入对象的注入时机一致，100%能注入成功）；\n     * 注意：为了做到能100%注入，需要在注入的js中自行判断对象是否已经存在（如：if (typeof(window.Android) = 'undefined')）；\n     *\n     * @param javaScript\n     */\n    public void addInjectJavaScript(String javaScript) {\n        if (mInjectJavaScripts == null) {\n            mInjectJavaScripts = new HashMap<String, String>();\n        }\n        mInjectJavaScripts.put(String.valueOf(javaScript.hashCode()), javaScript);\n        injectExtraJavaScript();\n    }\n\n    private void injectJavaScript() {\n        for (Map.Entry<String, JsCallJava> entry : mJsCallJavas.entrySet()) {\n            this.loadUrl(buildNotRepeatInjectJS(entry.getKey(), entry.getValue().getPreloadInterfaceJs()));\n        }\n    }\n\n    private void injectExtraJavaScript() {\n        for (Map.Entry<String, String> entry : mInjectJavaScripts.entrySet()) {\n            this.loadUrl(buildNotRepeatInjectJS(entry.getKey(), entry.getValue()));\n        }\n    }\n\n    /**\n     * 构建一个“不会重复注入”的js脚本；\n     *\n     * @param key\n     * @param js\n     * @return\n     */\n    public String buildNotRepeatInjectJS(String key, String js) {\n        String obj = String.format(\"__injectFlag_%1$s__\", key);\n        StringBuilder sb = new StringBuilder();\n        sb.append(\"javascript:try{(function(){if(window.\");\n        sb.append(obj);\n        sb.append(\"){console.log('\");\n        sb.append(obj);\n        sb.append(\" has been injected');return;}window.\");\n        sb.append(obj);\n        sb.append(\"=true;\");\n        sb.append(js);\n        sb.append(\"}())}catch(e){console.warn(e)}\");\n        return sb.toString();\n    }\n\n    /**\n     * 构建一个“带try catch”的js脚本；\n     *\n     * @param js\n     * @return\n     */\n    public String buildTryCatchInjectJS(String js) {\n        StringBuilder sb = new StringBuilder();\n        sb.append(\"javascript:try{\");\n        sb.append(js);\n        sb.append(\"}catch(e){console.warn(e)}\");\n        return sb.toString();\n    }\n\n\n    public static class AgentWebClient extends MiddlewareWebClientBase {\n\n        private AgentWebView mAgentWebView;\n\n        private AgentWebClient(AgentWebView agentWebView) {\n            this.mAgentWebView = agentWebView;\n        }\n\n\n        @Override\n        public void onPageStarted(WebView view, String url, Bitmap favicon) {\n            super.onPageStarted(view, url, favicon);\n            if (mAgentWebView.mJsCallJavas != null) {\n                mAgentWebView.injectJavaScript();\n                if (LogUtils.isDebug()) {\n                    Log.d(TAG, \"injectJavaScript, onPageStarted.url = \" + view.getUrl());\n                }\n            }\n            if (mAgentWebView.mInjectJavaScripts != null) {\n                mAgentWebView.injectExtraJavaScript();\n            }\n            mAgentWebView.mFixedOnReceivedTitle.onPageStarted();\n            mAgentWebView.fixedAccessibilityInjectorExceptionForOnPageFinished(url);\n        }\n\n        @Override\n        public void onPageFinished(WebView view, String url) {\n            super.onPageFinished(view, url);\n            mAgentWebView.mFixedOnReceivedTitle.onPageFinished(view);\n            if (LogUtils.isDebug()) {\n                Log.d(TAG, \"onPageFinished.url = \" + view.getUrl());\n            }\n        }\n\n\n    }\n\n    public static class AgentWebChrome extends MiddlewareWebChromeBase {\n\n        private AgentWebView mAgentWebView;\n\n        private AgentWebChrome(AgentWebView agentWebView) {\n            this.mAgentWebView = agentWebView;\n        }\n\n        @Override\n        public void onReceivedTitle(WebView view, String title) {\n            this.mAgentWebView.mFixedOnReceivedTitle.onReceivedTitle();\n            super.onReceivedTitle(view, title);\n        }\n\n        @Override\n        public void onProgressChanged(WebView view, int newProgress) {\n            if (this.mAgentWebView.mJsCallJavas != null) {\n                this.mAgentWebView.injectJavaScript();\n                if (LogUtils.isDebug()) {\n                    Log.d(TAG, \"injectJavaScript, onProgressChanged.newProgress = \" + newProgress + \", url = \" + view.getUrl());\n                }\n            }\n            if (this.mAgentWebView.mInjectJavaScripts != null) {\n                this.mAgentWebView.injectExtraJavaScript();\n            }\n            super.onProgressChanged(view, newProgress);\n\n        }\n\n        @Override\n        public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) {\n            Log.i(TAG, \"onJsPrompt:\" + url + \"  message:\" + message + \"  d:\" + defaultValue + \"  \");\n            if (this.mAgentWebView.mJsCallJavas != null && JsCallJava.isSafeWebViewCallMsg(message)) {\n                JSONObject jsonObject = JsCallJava.getMsgJSONObject(message);\n                String interfacedName = JsCallJava.getInterfacedName(jsonObject);\n                if (interfacedName != null) {\n                    JsCallJava mJsCallJava = this.mAgentWebView.mJsCallJavas.get(interfacedName);\n                    if (mJsCallJava != null) {\n                        result.confirm(mJsCallJava.call(view, jsonObject));\n                    }\n                }\n                return true;\n            } else {\n                return super.onJsPrompt(view, url, message, defaultValue, result);\n            }\n        }\n    }\n\n    /**\n     * 解决部分手机webView返回时不触发onReceivedTitle的问题（如：三星SM-G9008V 4.4.2）；\n     */\n    private static class FixedOnReceivedTitle {\n        private WebChromeClient mWebChromeClient;\n        private boolean mIsOnReceivedTitle;\n\n        public void setWebChromeClient(WebChromeClient webChromeClient) {\n            mWebChromeClient = webChromeClient;\n        }\n\n        public void onPageStarted() {\n            mIsOnReceivedTitle = false;\n        }\n\n        public void onPageFinished(WebView view) {\n            if (!mIsOnReceivedTitle && mWebChromeClient != null) {\n                WebBackForwardList list = null;\n                try {\n                    list = view.copyBackForwardList();\n                } catch (NullPointerException e) {\n                    if (LogUtils.isDebug()) {\n                        e.printStackTrace();\n                    }\n                }\n                if (list != null\n                        && list.getSize() > 0\n                        && list.getCurrentIndex() >= 0\n                        && list.getItemAtIndex(list.getCurrentIndex()) != null) {\n                    String previousTitle = list.getItemAtIndex(list.getCurrentIndex()).getTitle();\n                    mWebChromeClient.onReceivedTitle(view, previousTitle);\n                }\n            }\n        }\n        public void onReceivedTitle() {\n            mIsOnReceivedTitle = true;\n        }\n    }\n\n    // Activity在onDestory时调用webView的destroy，可以停止播放页面中的音频\n    private void fixedStillAttached() {\n        // java.lang.Throwable: Error: WebView.destroy() called while still attached!\n        // at android.webkit.WebViewClassic.destroy(WebViewClassic.java:4142)\n        // at android.webkit.WebView.destroy(WebView.java:707)\n        ViewParent parent = getParent();\n        if (parent instanceof ViewGroup) { // 由于自定义webView构建时传入了该Activity的context对象，因此需要先从父容器中移除webView，然后再销毁webView；\n            ViewGroup mWebViewContainer = (ViewGroup) getParent();\n            mWebViewContainer.removeAllViewsInLayout();\n        }\n    }\n\n    // 解决WebView内存泄漏问题；\n    private void releaseConfigCallback() {\n        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) { // JELLY_BEAN\n            try {\n                Field field = WebView.class.getDeclaredField(\"mWebViewCore\");\n                field = field.getType().getDeclaredField(\"mBrowserFrame\");\n                field = field.getType().getDeclaredField(\"sConfigCallback\");\n                field.setAccessible(true);\n                field.set(null, null);\n            } catch (NoSuchFieldException e) {\n                if (LogUtils.isDebug()) {\n                    e.printStackTrace();\n                }\n            } catch (IllegalAccessException e) {\n                if (LogUtils.isDebug()) {\n                    e.printStackTrace();\n                }\n            }\n        } else if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { // KITKAT\n            try {\n                Field sConfigCallback = Class.forName(\"android.webkit.BrowserFrame\").getDeclaredField(\"sConfigCallback\");\n                if (sConfigCallback != null) {\n                    sConfigCallback.setAccessible(true);\n                    sConfigCallback.set(null, null);\n                }\n            } catch (NoSuchFieldException e) {\n                if (LogUtils.isDebug()) {\n                    e.printStackTrace();\n                }\n            } catch (ClassNotFoundException e) {\n                if (LogUtils.isDebug()) {\n                    e.printStackTrace();\n                }\n            } catch (IllegalAccessException e) {\n                if (LogUtils.isDebug()) {\n                    e.printStackTrace();\n                }\n            }\n        }\n    }\n\n    /**\n     * Android 4.4 KitKat 使用Chrome DevTools 远程调试WebView\n     * WebView.setWebContentsDebuggingEnabled(true);\n     * http://blog.csdn.net/t12x3456/article/details/14225235\n     */\n    @TargetApi(19)\n    protected void trySetWebDebuggEnabled() {\n        if (LogUtils.isDebug() && Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {\n            try {\n                Class<?> clazz = WebView.class;\n                Method method = clazz.getMethod(\"setWebContentsDebuggingEnabled\", boolean.class);\n                method.invoke(null, true);\n            } catch (Throwable e) {\n                if (LogUtils.isDebug()) {\n                    e.printStackTrace();\n                }\n            }\n        }\n    }\n\n    @TargetApi(11)\n    protected boolean removeSearchBoxJavaBridge() {\n        try {\n            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB\n                    && Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) {\n                Method method = this.getClass().getMethod(\"removeJavascriptInterface\", String.class);\n                method.invoke(this, \"searchBoxJavaBridge_\");\n                return true;\n            }\n        } catch (Exception e) {\n            if (LogUtils.isDebug()) {\n                e.printStackTrace();\n            }\n        }\n        return false;\n    }\n\n    protected void fixedAccessibilityInjectorException() {\n        if (Build.VERSION.SDK_INT == Build.VERSION_CODES.JELLY_BEAN_MR1\n                && mIsAccessibilityEnabledOriginal == null\n                && isAccessibilityEnabled()) {\n            mIsAccessibilityEnabledOriginal = true;\n            setAccessibilityEnabled(false);\n        }\n    }\n\n    protected void fixedAccessibilityInjectorExceptionForOnPageFinished(String url) {\n        if (Build.VERSION.SDK_INT == Build.VERSION_CODES.JELLY_BEAN\n                && getSettings().getJavaScriptEnabled()\n                && mIsAccessibilityEnabledOriginal == null\n                && isAccessibilityEnabled()) {\n            try {\n                try {\n                    URLEncoder.encode(String.valueOf(new URI(url)), \"utf-8\");\n//                    URLEncodedUtils.parse(new URI(url), null); // AccessibilityInjector.getAxsUrlParameterValue\n                } catch (IllegalArgumentException e) {\n                    if (\"bad parameter\".equals(e.getMessage())) {\n                        mIsAccessibilityEnabledOriginal = true;\n                        setAccessibilityEnabled(false);\n                        LogUtils.safeCheckCrash(TAG, \"fixedAccessibilityInjectorExceptionForOnPageFinished.url = \" + url, e);\n                    }\n                }\n            } catch (Throwable e) {\n                if (LogUtils.isDebug()) {\n                    LogUtils.e(TAG, \"fixedAccessibilityInjectorExceptionForOnPageFinished\", e);\n                }\n            }\n        }\n    }\n\n    private boolean isAccessibilityEnabled() {\n        AccessibilityManager am = (AccessibilityManager) getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);\n        return am.isEnabled();\n    }\n\n    private void setAccessibilityEnabled(boolean enabled) {\n        AccessibilityManager am = (AccessibilityManager) getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);\n        try {\n            Method setAccessibilityState = am.getClass().getDeclaredMethod(\"setAccessibilityState\", boolean.class);\n            setAccessibilityState.setAccessible(true);\n            setAccessibilityState.invoke(am, enabled);\n            setAccessibilityState.setAccessible(false);\n        } catch (Throwable e) {\n            if (LogUtils.isDebug()) {\n                LogUtils.e(TAG, \"setAccessibilityEnabled\", e);\n            }\n        }\n    }\n\n    private void resetAccessibilityEnabled() {\n        if (mIsAccessibilityEnabledOriginal != null) {\n            setAccessibilityEnabled(mIsAccessibilityEnabledOriginal);\n        }\n    }\n}"
  },
  {
    "path": "agentweb-core/src/main/java/com/just/agentweb/BaseIndicatorSpec.java",
    "content": "/*\r\n * Copyright (C)  Justson(https://github.com/Justson/AgentWeb)\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n *      http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n\r\npackage com.just.agentweb;\r\n\r\n\r\n\r\n/**\r\n * @author cenxiaozhong\r\n * @since 1.0.0\r\n */\r\npublic interface BaseIndicatorSpec {\r\n\r\n    void show();\r\n\r\n    void hide();\r\n\r\n    void reset();\r\n\r\n    void setProgress(int newProgress);\r\n\r\n}\r\n"
  },
  {
    "path": "agentweb-core/src/main/java/com/just/agentweb/BaseIndicatorView.java",
    "content": "/*\n * Copyright (C)  Justson(https://github.com/Justson/AgentWeb)\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.just.agentweb;\n\nimport android.content.Context;\nimport androidx.annotation.Nullable;\nimport android.util.AttributeSet;\nimport android.widget.FrameLayout;\n\n\n/**\n * @author cenxiaozhong\n * @date 2017/5/12\n * @since 1.0.0\n */\npublic abstract class BaseIndicatorView extends FrameLayout implements BaseIndicatorSpec,LayoutParamsOffer{\n    public BaseIndicatorView(Context context) {\n        super(context);\n    }\n\n    public BaseIndicatorView(Context context, @Nullable AttributeSet attrs) {\n        super(context, attrs);\n    }\n\n    public BaseIndicatorView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {\n        super(context, attrs, defStyleAttr);\n    }\n\n    @Override\n    public void reset() {\n    }\n\n    @Override\n    public void setProgress(int newProgress) {\n    }\n\n    @Override\n    public void show() {\n    }\n\n    @Override\n    public void hide() {\n    }\n}\n"
  },
  {
    "path": "agentweb-core/src/main/java/com/just/agentweb/BaseJsAccessEntrace.java",
    "content": "/*\n * Copyright (C)  Justson(https://github.com/Justson/AgentWeb)\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.just.agentweb;\n\nimport android.os.Build;\nimport android.webkit.ValueCallback;\nimport android.webkit.WebView;\n\n/**\n * @author cenxiaozhong\n * @date 2017/5/26\n * @since 1.0.0\n */\npublic abstract class BaseJsAccessEntrace implements JsAccessEntrace {\n    private WebView mWebView;\n    public static final String TAG=BaseJsAccessEntrace.class.getSimpleName();\n    BaseJsAccessEntrace(WebView webView){\n        this.mWebView=webView;\n    }\n    @Override\n    public void callJs(String js, final ValueCallback<String> callback) {\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {\n            this.evaluateJs(js, callback);\n        } else {\n            this.loadJs(js);\n        }\n    }\n    @Override\n    public void callJs(String js) {\n        this.callJs(js,  null);\n    }\n\n    private void loadJs(String js) {\n        mWebView.loadUrl(js);\n    }\n    private void evaluateJs(String js, final ValueCallback<String>callback){\n        mWebView.evaluateJavascript(js, new ValueCallback<String>() {\n            @Override\n            public void onReceiveValue(String value) {\n                if (callback != null){\n                    callback.onReceiveValue(value);\n                }\n            }\n        });\n    }\n\n\n    @Override\n    public void quickCallJs(String method, ValueCallback<String> callback, String... params) {\n        StringBuilder sb=new StringBuilder();\n        sb.append(\"javascript:\"+method);\n        if(params==null||params.length==0){\n            sb.append(\"()\");\n        }else{\n            sb.append(\"(\").append(concat(params)).append(\")\");\n        }\n        callJs(sb.toString(),callback);\n    }\n\n    private String concat(String...params){\n        StringBuilder mStringBuilder=new StringBuilder();\n        for(int i=0;i<params.length;i++){\n            String param=params[i];\n            if(!AgentWebUtils.isJson(param)){\n                mStringBuilder.append(\"\\\"\").append(param).append(\"\\\"\");\n            }else{\n                mStringBuilder.append(param);\n            }\n            if(i!=params.length-1){\n                mStringBuilder.append(\" , \");\n            }\n        }\n        return mStringBuilder.toString();\n    }\n\n    @Override\n    public void quickCallJs(String method, String... params) {\n        this.quickCallJs(method,null,params);\n    }\n\n    @Override\n    public void quickCallJs(String method) {\n        this.quickCallJs(method,(String[])null);\n    }\n}\n"
  },
  {
    "path": "agentweb-core/src/main/java/com/just/agentweb/DefaultChromeClient.java",
    "content": "/*\r\n * Copyright (C)  Justson(https://github.com/Justson/AgentWeb)\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n *      http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n\r\npackage com.just.agentweb;\r\n\r\nimport android.Manifest;\r\nimport android.app.Activity;\r\nimport android.graphics.Bitmap;\r\nimport android.net.Uri;\r\nimport android.os.Build;\r\nimport android.os.Bundle;\r\nimport androidx.annotation.NonNull;\r\nimport androidx.annotation.Nullable;\r\nimport androidx.annotation.RequiresApi;\r\nimport android.util.Log;\r\nimport android.view.View;\r\nimport android.webkit.ConsoleMessage;\r\nimport android.webkit.GeolocationPermissions;\r\nimport android.webkit.JsPromptResult;\r\nimport android.webkit.JsResult;\r\nimport android.webkit.PermissionRequest;\r\nimport android.webkit.ValueCallback;\r\nimport android.webkit.WebChromeClient;\r\nimport android.webkit.WebStorage;\r\nimport android.webkit.WebView;\r\n\r\nimport java.lang.ref.WeakReference;\r\nimport java.util.ArrayList;\r\nimport java.util.Arrays;\r\nimport java.util.HashSet;\r\nimport java.util.List;\r\nimport java.util.Set;\r\n\r\nimport static com.just.agentweb.AgentActionFragment.KEY_FROM_INTENTION;\r\n\r\n\r\n/**\r\n * @author cenxiaozhong\r\n * @since 1.0.0\r\n */\r\npublic class DefaultChromeClient extends MiddlewareWebChromeBase {\r\n    /**\r\n     * Activity\r\n     */\r\n    private WeakReference<Activity> mActivityWeakReference = null;\r\n    /**\r\n     * DefaultChromeClient 's TAG\r\n     */\r\n    private String TAG = DefaultChromeClient.class.getSimpleName();\r\n    /**\r\n     * Android WebChromeClient path ，用于反射，用户是否重写来该方法\r\n     */\r\n    public static final String ANDROID_WEBCHROMECLIENT_PATH = \"android.webkit.WebChromeClient\";\r\n    /**\r\n     * WebChromeClient\r\n     */\r\n    private WebChromeClient mWebChromeClient;\r\n    /**\r\n     * 包装Flag\r\n     */\r\n    private boolean mIsWrapper = false;\r\n    /**\r\n     * Video 处理类\r\n     */\r\n    private IVideo mIVideo;\r\n    /**\r\n     * PermissionInterceptor 权限拦截器\r\n     */\r\n    private PermissionInterceptor mPermissionInterceptor;\r\n    /**\r\n     * 当前 WebView\r\n     */\r\n    private WebView mWebView;\r\n    /**\r\n     * Web端触发的定位 mOrigin\r\n     */\r\n    private String mOrigin = null;\r\n    /**\r\n     * Web 端触发的定位 Callback 回调成功，或者失败\r\n     */\r\n    private GeolocationPermissions.Callback mCallback = null;\r\n    /**\r\n     * 标志位\r\n     */\r\n    public static final int FROM_CODE_INTENTION = 0x18;\r\n    /**\r\n     * 标识当前是获取定位权限\r\n     */\r\n    public static final int FROM_CODE_INTENTION_LOCATION = FROM_CODE_INTENTION << 2;\r\n    /**\r\n     * AbsAgentWebUIController\r\n     */\r\n    private WeakReference<AbsAgentWebUIController> mAgentWebUIController = null;\r\n    /**\r\n     * IndicatorController 进度条控制器\r\n     */\r\n    private IndicatorController mIndicatorController;\r\n    /**\r\n     * 文件选择器\r\n     */\r\n    private Object mFileChooser;\r\n\r\n    DefaultChromeClient(Activity activity,\r\n                        IndicatorController indicatorController,\r\n                        WebChromeClient chromeClient,\r\n                        @Nullable IVideo iVideo,\r\n                        PermissionInterceptor permissionInterceptor, WebView webView) {\r\n        super(chromeClient);\r\n        this.mIndicatorController = indicatorController;\r\n        mIsWrapper = chromeClient != null ? true : false;\r\n        this.mWebChromeClient = chromeClient;\r\n        mActivityWeakReference = new WeakReference<Activity>(activity);\r\n        this.mIVideo = iVideo;\r\n        this.mPermissionInterceptor = permissionInterceptor;\r\n        this.mWebView = webView;\r\n        mAgentWebUIController = new WeakReference<AbsAgentWebUIController>(AgentWebUtils.getAgentWebUIControllerByWebView(webView));\r\n    }\r\n\r\n\r\n    @Override\r\n    public void onProgressChanged(WebView view, int newProgress) {\r\n        super.onProgressChanged(view, newProgress);\r\n        if (mIndicatorController != null) {\r\n            mIndicatorController.progress(view, newProgress);\r\n        }\r\n    }\r\n\r\n    @Override\r\n    public void onReceivedTitle(WebView view, String title) {\r\n        if (mIsWrapper) {\r\n            super.onReceivedTitle(view, title);\r\n        }\r\n    }\r\n\r\n    @Override\r\n    public boolean onJsAlert(WebView view, String url, String message, JsResult result) {\r\n        if (mAgentWebUIController.get() != null) {\r\n            mAgentWebUIController.get().onJsAlert(view, url, message);\r\n        }\r\n        result.confirm();\r\n        return true;\r\n    }\r\n\r\n\r\n    @Override\r\n    public void onReceivedIcon(WebView view, Bitmap icon) {\r\n        super.onReceivedIcon(view, icon);\r\n    }\r\n\r\n    @Override\r\n    public void onGeolocationPermissionsHidePrompt() {\r\n        super.onGeolocationPermissionsHidePrompt();\r\n    }\r\n\r\n    //location\r\n    @Override\r\n    public void onGeolocationPermissionsShowPrompt(String origin, GeolocationPermissions.Callback callback) {\r\n        onGeolocationPermissionsShowPromptInternal(origin, callback);\r\n    }\r\n\r\n    private void onGeolocationPermissionsShowPromptInternal(String origin, GeolocationPermissions.Callback callback) {\r\n        if (mPermissionInterceptor != null) {\r\n            if (mPermissionInterceptor.intercept(this.mWebView.getUrl(), AgentWebPermissions.LOCATION, \"location\")) {\r\n                callback.invoke(origin, false, false);\r\n                return;\r\n            }\r\n        }\r\n        Activity mActivity = mActivityWeakReference.get();\r\n        if (mActivity == null) {\r\n            callback.invoke(origin, false, false);\r\n            return;\r\n        }\r\n        List<String> deniedPermissions = null;\r\n        if ((deniedPermissions = AgentWebUtils.getDeniedPermissions(mActivity, AgentWebPermissions.LOCATION)).isEmpty()) {\r\n            LogUtils.i(TAG, \"onGeolocationPermissionsShowPromptInternal:\" + true);\r\n            callback.invoke(origin, true, false);\r\n        } else {\r\n            Action mAction = Action.createPermissionsAction(deniedPermissions.toArray(new String[]{}));\r\n            mAction.setFromIntention(FROM_CODE_INTENTION_LOCATION);\r\n            mAction.setPermissionListener(mPermissionListener);\r\n            this.mCallback = callback;\r\n            this.mOrigin = origin;\r\n            AgentActionFragment.start(mActivity, mAction);\r\n        }\r\n    }\r\n\r\n    private AgentActionFragment.PermissionListener mPermissionListener = new AgentActionFragment.PermissionListener() {\r\n        @Override\r\n        public void onRequestPermissionsResult(@NonNull String[] permissions, @NonNull int[] grantResults, Bundle extras) {\r\n            if (extras.getInt(KEY_FROM_INTENTION) == FROM_CODE_INTENTION_LOCATION) {\r\n                boolean hasPermission = AgentWebUtils.hasPermission(mActivityWeakReference.get(), permissions);\r\n                if (mCallback != null) {\r\n                    if (hasPermission) {\r\n                        mCallback.invoke(mOrigin, true, false);\r\n                    } else {\r\n                        mCallback.invoke(mOrigin, false, false);\r\n                    }\r\n                    mCallback = null;\r\n                    mOrigin = null;\r\n                }\r\n                if (!hasPermission && null != mAgentWebUIController.get()) {\r\n                    mAgentWebUIController\r\n                            .get()\r\n                            .onPermissionsDeny(\r\n                                    AgentWebPermissions.LOCATION,\r\n                                    AgentWebPermissions.ACTION_LOCATION,\r\n                                    \"Location\");\r\n                }\r\n            }\r\n        }\r\n    };\r\n\r\n    @Override\r\n    public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) {\r\n        try {\r\n            if (this.mAgentWebUIController.get() != null) {\r\n                this.mAgentWebUIController.get().onJsPrompt(mWebView, url, message, defaultValue, result);\r\n            }\r\n        } catch (Exception e) {\r\n            if (LogUtils.isDebug()) {\r\n                e.printStackTrace();\r\n            }\r\n        }\r\n        return true;\r\n    }\r\n\r\n    @Override\r\n    public boolean onJsConfirm(WebView view, String url, String message, JsResult result) {\r\n        if (mAgentWebUIController.get() != null) {\r\n            mAgentWebUIController.get().onJsConfirm(view, url, message, result);\r\n        }\r\n        return true;\r\n    }\r\n\r\n    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)\r\n    @Override\r\n    public void onPermissionRequest(PermissionRequest request) {\r\n        if (request == null) {\r\n            return;\r\n        }\r\n        final String[] resources = request.getResources();\r\n        if (resources == null || resources.length <= 0) {\r\n            request.deny();\r\n            return;\r\n        }\r\n        Set<String> resourcesSet = new HashSet<>(Arrays.asList(resources));\r\n        ArrayList<String> permissions = new ArrayList<>(resourcesSet.size());\r\n        if (resourcesSet.contains(PermissionRequest.RESOURCE_VIDEO_CAPTURE)) {\r\n            permissions.add(Manifest.permission.CAMERA);\r\n        }\r\n        if (resourcesSet.contains(PermissionRequest.RESOURCE_AUDIO_CAPTURE)) {\r\n            permissions.add(Manifest.permission.RECORD_AUDIO);\r\n        }\r\n        if (mPermissionInterceptor != null) {\r\n            boolean intercept = mPermissionInterceptor.intercept(mWebView.getUrl(), permissions.toArray(new String[]{}), \"onPermissionRequest\");\r\n            if (intercept) {\r\n                return;\r\n            }\r\n        }\r\n        if (mAgentWebUIController.get() != null) {\r\n            mAgentWebUIController.get().onPermissionRequest(request);\r\n        }\r\n    }\r\n\r\n    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)\r\n    @Override\r\n    public void onPermissionRequestCanceled(PermissionRequest request) {\r\n        super.onPermissionRequestCanceled(request);\r\n    }\r\n\r\n    @Override\r\n    public void onExceededDatabaseQuota(String url, String databaseIdentifier, long quota, long estimatedDatabaseSize, long totalQuota, WebStorage.QuotaUpdater quotaUpdater) {\r\n        quotaUpdater.updateQuota(totalQuota * 2);\r\n    }\r\n\r\n\r\n    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)\r\n    @Override\r\n    public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback, FileChooserParams fileChooserParams) {\r\n        LogUtils.i(TAG, \"openFileChooser>=5.0\");\r\n        return openFileChooserAboveL(webView, filePathCallback, fileChooserParams);\r\n    }\r\n\r\n    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)\r\n    private boolean openFileChooserAboveL(WebView webView, ValueCallback<Uri[]> valueCallbacks, FileChooserParams fileChooserParams) {\r\n//        LogUtils.i(TAG, \"fileChooserParams:\" + fileChooserParams.getAcceptTypes() + \"  getTitle:\" + fileChooserParams.getTitle() + \" accept:\" + Arrays.toString(fileChooserParams.getAcceptTypes()) + \" length:\" + fileChooserParams.getAcceptTypes().length + \"  isCaptureEnabled:\" + fileChooserParams.isCaptureEnabled() + \"  \" + fileChooserParams.getFilenameHint() + \"  intent:\" + fileChooserParams.createIntent().toString() + \"   mode:\" + fileChooserParams.getMode());\r\n        if (valueCallbacks == null) {\r\n            return false;\r\n        }\r\n        Activity mActivity = this.mActivityWeakReference.get();\r\n        if (mActivity == null || mActivity.isFinishing()) {\r\n            return false;\r\n        }\r\n        return AgentWebUtils.showFileChooserCompat(mActivity,\r\n                mWebView,\r\n                valueCallbacks,\r\n                fileChooserParams,\r\n                this.mPermissionInterceptor,\r\n                null,\r\n                null,\r\n                null\r\n        );\r\n    }\r\n\r\n    /**\r\n     * Android  >= 4.1\r\n     *\r\n     * @param uploadFile ValueCallback ,  File URI callback\r\n     * @param acceptType\r\n     * @param capture\r\n     */\r\n    @Override\r\n    public void openFileChooser(ValueCallback<Uri> uploadFile, String acceptType, String capture) {\r\n        /*believe me , i never want to do this */\r\n        LogUtils.i(TAG, \"openFileChooser>=4.1\");\r\n        createAndOpenCommonFileChooser(uploadFile, acceptType);\r\n    }\r\n\r\n    //  Android < 3.0\r\n    @Override\r\n    public void openFileChooser(ValueCallback<Uri> valueCallback) {\r\n        Log.i(TAG, \"openFileChooser<3.0\");\r\n        createAndOpenCommonFileChooser(valueCallback, \"*/*\");\r\n    }\r\n\r\n    //  Android  >= 3.0\r\n    @Override\r\n    public void openFileChooser(ValueCallback valueCallback, String acceptType) {\r\n        Log.i(TAG, \"openFileChooser>3.0\");\r\n        createAndOpenCommonFileChooser(valueCallback, acceptType);\r\n    }\r\n\r\n\r\n    private void createAndOpenCommonFileChooser(ValueCallback valueCallback, String mimeType) {\r\n        if (valueCallback == null) {\r\n            return;\r\n        }\r\n        Activity mActivity = this.mActivityWeakReference.get();\r\n        if (mActivity == null || mActivity.isFinishing()) {\r\n            valueCallback.onReceiveValue(new Object());\r\n            return;\r\n        }\r\n        AgentWebUtils.showFileChooserCompat(mActivity,\r\n                mWebView,\r\n                null,\r\n                null,\r\n                this.mPermissionInterceptor,\r\n                valueCallback,\r\n                mimeType,\r\n                null\r\n        );\r\n    }\r\n\r\n    @Override\r\n    public boolean onConsoleMessage(ConsoleMessage consoleMessage) {\r\n        super.onConsoleMessage(consoleMessage);\r\n        return true;\r\n    }\r\n\r\n    @Override\r\n    public void onShowCustomView(View view, CustomViewCallback callback) {\r\n        if (mIVideo != null) {\r\n            mIVideo.onShowCustomView(view, callback);\r\n        }\r\n    }\r\n\r\n    @Override\r\n    public void onHideCustomView() {\r\n        if (mIVideo != null) {\r\n            mIVideo.onHideCustomView();\r\n        }\r\n    }\r\n}\r\n"
  },
  {
    "path": "agentweb-core/src/main/java/com/just/agentweb/DefaultDesignUIController.java",
    "content": "/*\n * Copyright (C)  Justson(https://github.com/Justson/AgentWeb)\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.just.agentweb;\n\nimport android.app.Activity;\nimport android.content.DialogInterface;\nimport android.graphics.Color;\nimport android.os.Build;\nimport android.os.Handler;\nimport android.os.Message;\nimport android.text.TextUtils;\nimport android.util.TypedValue;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.webkit.JsPromptResult;\nimport android.webkit.JsResult;\nimport android.webkit.WebView;\nimport android.widget.TextView;\n\nimport com.google.android.material.bottomsheet.BottomSheetDialog;\nimport com.google.android.material.snackbar.Snackbar;\n\nimport androidx.recyclerview.widget.LinearLayoutManager;\nimport androidx.recyclerview.widget.RecyclerView;\n\n/**\n * @author cenxiaozhong\n * @date 2017/12/8\n * @since 3.0.0\n */\npublic class DefaultDesignUIController extends DefaultUIController {\n\n    private BottomSheetDialog mBottomSheetDialog;\n    private static final int RECYCLERVIEW_ID = 0x1001;\n    private Activity mActivity = null;\n    private WebParentLayout mWebParentLayout;\n    private LayoutInflater mLayoutInflater;\n\n    @Override\n    public void onJsAlert(WebView view, String url, String message) {\n        onJsAlertInternal(view, message);\n    }\n\n    private void onJsAlertInternal(WebView view, String message) {\n        Activity mActivity = this.mActivity;\n        if (mActivity == null || mActivity.isFinishing()) {\n            return;\n        }\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {\n            if (mActivity.isDestroyed()) {\n                return;\n            }\n        }\n        try {\n            AgentWebUtils.show(view,\n                    message,\n                    Snackbar.LENGTH_SHORT,\n                    Color.WHITE,\n                    mActivity.getResources().getColor(R.color.black),\n                    null,\n                    -1,\n                    null);\n        } catch (Throwable throwable) {\n            if (LogUtils.isDebug()){\n                throwable.printStackTrace();\n            }\n        }\n    }\n\n    @Override\n    public void onJsConfirm(WebView view, String url, String message, JsResult jsResult) {\n        super.onJsConfirm(view, url, message, jsResult);\n    }\n\n    @Override\n    public void onSelectItemsPrompt(WebView view, String url, String[] ways, Handler.Callback callback) {\n        showChooserInternal(view, url, ways, callback);\n    }\n\n    @Override\n    public void onForceDownloadAlert(String url, final Handler.Callback callback) {\n        super.onForceDownloadAlert(url, callback);\n    }\n\n    private void showChooserInternal(WebView view, String url, final String[] ways, final Handler.Callback callback) {\n        Activity mActivity;\n        if ((mActivity = this.mActivity) == null || mActivity.isFinishing()) {\n            return;\n        }\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {\n            if (mActivity.isDestroyed()) {\n                return;\n            }\n        }\n        LogUtils.i(TAG, \"url:\" + url + \"  ways:\" + ways[0]);\n        RecyclerView mRecyclerView;\n        if (mBottomSheetDialog == null) {\n            mBottomSheetDialog = new BottomSheetDialog(mActivity);\n            mRecyclerView = new RecyclerView(mActivity);\n            mRecyclerView.setLayoutManager(new LinearLayoutManager(mActivity));\n            mRecyclerView.setId(RECYCLERVIEW_ID);\n            mBottomSheetDialog.setContentView(mRecyclerView);\n        }\n        mRecyclerView = (RecyclerView) mBottomSheetDialog.getDelegate().findViewById(RECYCLERVIEW_ID);\n        mRecyclerView.setAdapter(getAdapter(ways, callback));\n        mBottomSheetDialog.setOnCancelListener(new DialogInterface.OnCancelListener() {\n            @Override\n            public void onCancel(DialogInterface dialog) {\n                if (callback != null) {\n                    callback.handleMessage(Message.obtain(null, -1));\n                }\n            }\n        });\n        mBottomSheetDialog.show();\n    }\n\n    private RecyclerView.Adapter getAdapter(final String[] ways, final Handler.Callback callback) {\n        return new RecyclerView.Adapter<BottomSheetHolder>() {\n            @Override\n            public BottomSheetHolder onCreateViewHolder(ViewGroup viewGroup, int i) {\n                return new BottomSheetHolder(mLayoutInflater.inflate(android.R.layout.simple_list_item_1, viewGroup, false));\n            }\n\n            @Override\n            public void onBindViewHolder(BottomSheetHolder bottomSheetHolder, final int i) {\n                TypedValue outValue = new TypedValue();\n                mActivity.getTheme().resolveAttribute(android.R.attr.selectableItemBackground, outValue, true);\n                bottomSheetHolder.mTextView.setBackgroundResource(outValue.resourceId);\n                bottomSheetHolder.mTextView.setText(ways[i]);\n                bottomSheetHolder.mTextView.setOnClickListener(new View.OnClickListener() {\n                    @Override\n                    public void onClick(View v) {\n\n                        if (mBottomSheetDialog != null && mBottomSheetDialog.isShowing()) {\n                            mBottomSheetDialog.dismiss();\n                        }\n                        Message mMessage = Message.obtain();\n                        mMessage.what = i;\n                        callback.handleMessage(mMessage);\n                    }\n                });\n            }\n\n            @Override\n            public int getItemCount() {\n                return ways.length;\n            }\n        };\n    }\n\n    private static class BottomSheetHolder extends RecyclerView.ViewHolder {\n        TextView mTextView;\n        public BottomSheetHolder(View itemView) {\n            super(itemView);\n            mTextView = (TextView) itemView.findViewById(android.R.id.text1);\n        }\n    }\n\n    @Override\n    public void onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult jsPromptResult) {\n        super.onJsPrompt(view, url, message, defaultValue, jsPromptResult);\n    }\n\n    @Override\n    protected void bindSupportWebParent(WebParentLayout webParentLayout, Activity activity) {\n        super.bindSupportWebParent(webParentLayout, activity);\n        this.mActivity = activity;\n        this.mWebParentLayout = webParentLayout;\n        mLayoutInflater = LayoutInflater.from(mActivity);\n    }\n\n    @Override\n    public void onShowMessage(String message, String from) {\n        Activity mActivity;\n        if ((mActivity = this.mActivity) == null || mActivity.isFinishing()) {\n            return;\n        }\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {\n            if (mActivity.isDestroyed()) {\n                return;\n            }\n        }\n        if (!TextUtils.isEmpty(from) && from.contains(\"performDownload\")) {\n            return;\n        }\n        onJsAlertInternal(mWebParentLayout.getWebView(), message);\n    }\n}\n"
  },
  {
    "path": "agentweb-core/src/main/java/com/just/agentweb/DefaultDownloadImpl.java",
    "content": "/*\n * Copyright (C)  Justson(https://github.com/Justson/AgentWeb)\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.just.agentweb;\n\nimport android.app.Activity;\nimport android.content.Context;\nimport android.net.Uri;\nimport android.os.Handler;\nimport android.os.Looper;\nimport android.os.Message;\nimport android.webkit.WebView;\n\nimport com.download.library.DownloadImpl;\nimport com.download.library.DownloadListenerAdapter;\nimport com.download.library.Extra;\nimport com.download.library.ResourceRequest;\n\nimport java.lang.ref.WeakReference;\nimport java.util.concurrent.ConcurrentHashMap;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\n\n/**\n * @author cenxiaozhong\n * @date 2017/5/13\n */\npublic class DefaultDownloadImpl implements android.webkit.DownloadListener {\n    /**\n     * Application Context\n     */\n    protected Context mContext;\n    protected ConcurrentHashMap<String, ResourceRequest> mDownloadTasks = new ConcurrentHashMap<>();\n    /**\n     * Activity\n     */\n    protected WeakReference<Activity> mActivityWeakReference = null;\n    /**\n     * TAG 用于打印，标识\n     */\n    private static final String TAG = DefaultDownloadImpl.class.getSimpleName();\n    /**\n     * 权限拦截\n     */\n    protected PermissionInterceptor mPermissionListener = null;\n    /**\n     * AbsAgentWebUIController\n     */\n    protected WeakReference<AbsAgentWebUIController> mAgentWebUIController;\n\n    private static Handler mHandler = new Handler(Looper.getMainLooper());\n\n    private boolean isInstallDownloader;\n\n    protected DefaultDownloadImpl(Activity activity, WebView webView, PermissionInterceptor permissionInterceptor) {\n        this.mContext = activity.getApplicationContext();\n        this.mActivityWeakReference = new WeakReference<Activity>(activity);\n        this.mPermissionListener = permissionInterceptor;\n        this.mAgentWebUIController = new WeakReference<AbsAgentWebUIController>(AgentWebUtils.getAgentWebUIControllerByWebView(webView));\n        try {\n            DownloadImpl.getInstance(this.mContext);\n            isInstallDownloader = true;\n        } catch (Throwable throwable) {\n            LogUtils.e(TAG, \"implementation 'com.download.library:Downloader:x.x.x'\");\n            if (LogUtils.isDebug()) {\n                throwable.printStackTrace();\n            }\n            isInstallDownloader = false;\n        }\n    }\n\n\n    @Override\n    public void onDownloadStart(final String url, final String userAgent, final String contentDisposition, final String mimetype, final long contentLength) {\n        if (!isInstallDownloader) {\n            LogUtils.e(TAG, \"unable start download \" + url + \"; implementation 'com.download.library:Downloader:x.x.x'\");\n            return;\n        }\n        mHandler.post(new Runnable() {\n            @Override\n            public void run() {\n                onDownloadStartInternal(url, userAgent, contentDisposition, mimetype, contentLength);\n            }\n        });\n    }\n\n    protected void onDownloadStartInternal(String url, String userAgent, String contentDisposition, String mimetype, long contentLength) {\n        if (null == mActivityWeakReference.get() || mActivityWeakReference.get().isFinishing()) {\n            return;\n        }\n        if (null != this.mPermissionListener) {\n            if (this.mPermissionListener.intercept(url, new String[]{}, \"download\")) {\n                return;\n            }\n        }\n        ResourceRequest resourceRequest = createResourceRequest(url);\n        this.mDownloadTasks.put(url, resourceRequest);\n        preDownload(url);\n    }\n\n    protected ResourceRequest createResourceRequest(String url) {\n        return DownloadImpl.getInstance(this.mContext).with(url).setEnableIndicator(true).autoOpenIgnoreMD5();\n    }\n\n\n    protected void preDownload(String url) {\n        // 移动数据\n        if (!isForceRequest(url) &&\n                AgentWebUtils.checkNetworkType(mContext) > 1) {\n            showDialog(url);\n            return;\n        }\n        performDownload(url);\n    }\n\n    protected boolean isForceRequest(String url) {\n        ResourceRequest resourceRequest = mDownloadTasks.get(url);\n        if (null != resourceRequest) {\n            return resourceRequest.getDownloadTask().isForceDownload();\n        }\n        return false;\n    }\n\n    protected void forceDownload(final String url) {\n        ResourceRequest resourceRequest = mDownloadTasks.get(url);\n        resourceRequest.setForceDownload(true);\n        performDownload(url);\n    }\n\n    protected void showDialog(final String url) {\n        Activity mActivity;\n        if (null == (mActivity = mActivityWeakReference.get()) || mActivity.isFinishing()) {\n            return;\n        }\n        AbsAgentWebUIController mAgentWebUIController;\n        if (null != (mAgentWebUIController = this.mAgentWebUIController.get())) {\n            mAgentWebUIController.onForceDownloadAlert(url, createCallback(url));\n        }\n    }\n\n    protected Handler.Callback createCallback(final String url) {\n        return new Handler.Callback() {\n            @Override\n            public boolean handleMessage(Message msg) {\n                forceDownload(url);\n                return true;\n            }\n        };\n    }\n\n    protected void performDownload(String url) {\n        try {\n            LogUtils.e(TAG, \"performDownload:\" + url + \" exist:\" + DownloadImpl.getInstance(this.mContext).exist(url));\n            // 该链接是否正在下载\n            if (DownloadImpl.getInstance(mContext).exist(url)) {\n                if (null != mAgentWebUIController.get()) {\n                    mAgentWebUIController.get().onShowMessage(\n                            mActivityWeakReference.get()\n                                    .getString(R.string.agentweb_download_task_has_been_exist), \"preDownload\");\n                }\n                return;\n            }\n            ResourceRequest resourceRequest = mDownloadTasks.get(url);\n            resourceRequest.addHeader(\"Cookie\", AgentWebConfig.getCookiesByUrl(url));\n            taskEnqueue(resourceRequest);\n        } catch (Throwable ignore) {\n            if (LogUtils.isDebug()) {\n                ignore.printStackTrace();\n            }\n        }\n    }\n\n    protected void taskEnqueue(ResourceRequest resourceRequest) {\n        resourceRequest.enqueue(new DownloadListenerAdapter() {\n            @Override\n            public boolean onResult(Throwable throwable, Uri path, String url, Extra extra) {\n                mDownloadTasks.remove(url);\n                return super.onResult(throwable, path, url, extra);\n            }\n        });\n    }\n\n    public static DefaultDownloadImpl create(@NonNull Activity activity,\n                                             @NonNull WebView webView,\n                                             @Nullable PermissionInterceptor permissionInterceptor) {\n        return new DefaultDownloadImpl(activity, webView, permissionInterceptor);\n    }\n}\n"
  },
  {
    "path": "agentweb-core/src/main/java/com/just/agentweb/DefaultUIController.java",
    "content": "/*\n * Copyright (C)  Justson(https://github.com/Justson/AgentWeb)\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.just.agentweb;\n\nimport android.Manifest;\nimport android.app.Activity;\nimport android.app.ProgressDialog;\nimport android.content.DialogInterface;\nimport android.content.res.Resources;\nimport android.net.http.SslError;\nimport android.os.Build;\nimport android.os.Bundle;\nimport android.os.Handler;\nimport android.os.Message;\nimport android.text.TextUtils;\nimport android.webkit.JsPromptResult;\nimport android.webkit.JsResult;\nimport android.webkit.PermissionRequest;\nimport android.webkit.SslErrorHandler;\nimport android.webkit.WebView;\nimport android.widget.EditText;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.RequiresApi;\nimport androidx.appcompat.app.AlertDialog;\n\n\n/**\n * @author cenxiaozhong\n * @date 2017/12/8\n * @since 3.0.0\n */\npublic class DefaultUIController extends AbsAgentWebUIController {\n\n\tprivate AlertDialog mAlertDialog;\n\tprotected AlertDialog mConfirmDialog;\n\tprivate JsPromptResult mJsPromptResult = null;\n\tprivate JsResult mJsResult = null;\n\tprivate AlertDialog mPromptDialog = null;\n\tprivate Activity mActivity;\n\tprivate WebParentLayout mWebParentLayout;\n\tprivate AlertDialog mAskOpenOtherAppDialog = null;\n\tprivate ProgressDialog mProgressDialog;\n\tprivate Resources mResources = null;\n\n\t@Override\n\tpublic void onJsAlert(WebView view, String url, String message) {\n\t\tAgentWebUtils.toastShowShort(view.getContext().getApplicationContext(), message);\n\t}\n\n\t@Override\n\tpublic void onOpenPagePrompt(WebView view, String url, final Handler.Callback callback) {\n\t\tLogUtils.i(TAG, \"onOpenPagePrompt\");\n\t\tActivity mActivity;\n\t\tif ((mActivity = this.mActivity) == null || mActivity.isFinishing()) {\n\t\t\treturn;\n\t\t}\n\t\tif (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {\n\t\t\tif (mActivity.isDestroyed()) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\tif (mAskOpenOtherAppDialog == null) {\n\t\t\tmAskOpenOtherAppDialog = new AlertDialog\n\t\t\t\t\t.Builder(mActivity)\n\t\t\t\t\t.setMessage(mResources.getString(R.string.agentweb_leave_app_and_go_other_page,\n\t\t\t\t\t\t\tAgentWebUtils.getApplicationName(mActivity)))\n\t\t\t\t\t.setTitle(mResources.getString(R.string.agentweb_tips))\n\t\t\t\t\t.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {\n\t\t\t\t\t\t@Override\n\t\t\t\t\t\tpublic void onClick(DialogInterface dialog, int which) {\n\t\t\t\t\t\t\tif (callback != null) {\n\t\t\t\t\t\t\t\tcallback.handleMessage(Message.obtain(null, -1));\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t})//\n\t\t\t\t\t.setPositiveButton(mResources.getString(R.string.agentweb_leave), new DialogInterface.OnClickListener() {\n\t\t\t\t\t\t@Override\n\t\t\t\t\t\tpublic void onClick(DialogInterface dialog, int which) {\n\t\t\t\t\t\t\tif (callback != null) {\n\t\t\t\t\t\t\t\tcallback.handleMessage(Message.obtain(null, 1));\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\t\t\t\t\t.create();\n\t\t}\n\t\tmAskOpenOtherAppDialog.show();\n\t}\n\n\t@Override\n\tpublic void onJsConfirm(WebView view, String url, String message, JsResult jsResult) {\n\t\tonJsConfirmInternal(message, jsResult);\n\t}\n\n\t@Override\n\tpublic void onSelectItemsPrompt(WebView view, String url, final String[] ways, final Handler.Callback callback) {\n\t\tshowChooserInternal(ways, callback);\n\t}\n\n\t@Override\n\tpublic void onForceDownloadAlert(String url, final Handler.Callback callback) {\n\t\tonForceDownloadAlertInternal(callback);\n\t}\n\n\tprivate void onForceDownloadAlertInternal(final Handler.Callback callback) {\n\t\tActivity mActivity;\n\t\tif ((mActivity = this.mActivity) == null || mActivity.isFinishing()) {\n\t\t\treturn;\n\t\t}\n\t\tif (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {\n\t\t\tif (mActivity.isDestroyed()) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\tAlertDialog mAlertDialog = null;\n\t\tmAlertDialog = new AlertDialog.Builder(mActivity)\n\t\t\t\t.setTitle(mResources.getString(R.string.agentweb_tips))\n\t\t\t\t.setMessage(mResources.getString(R.string.agentweb_honeycomblow))\n\t\t\t\t.setNegativeButton(mResources.getString(R.string.agentweb_download), new DialogInterface.OnClickListener() {\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic void onClick(DialogInterface dialog, int which) {\n\t\t\t\t\t\tif (dialog != null) {\n\t\t\t\t\t\t\tdialog.dismiss();\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (callback != null) {\n\t\t\t\t\t\t\tcallback.handleMessage(Message.obtain());\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t})//\n\t\t\t\t.setPositiveButton(mResources.getString(R.string.agentweb_cancel), new DialogInterface.OnClickListener() {\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic void onClick(DialogInterface dialog, int which) {\n\n\t\t\t\t\t\tif (dialog != null) {\n\t\t\t\t\t\t\tdialog.dismiss();\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}).create();\n\t\tmAlertDialog.show();\n\t}\n\n\tprivate void showChooserInternal(String[] ways, final Handler.Callback callback) {\n\t\tActivity mActivity;\n\t\tif ((mActivity = this.mActivity) == null || mActivity.isFinishing()) {\n\t\t\treturn;\n\t\t}\n\t\tif (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {\n\t\t\tif (mActivity.isDestroyed()) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\tmAlertDialog = new AlertDialog.Builder(mActivity)\n\t\t\t\t.setSingleChoiceItems(ways, -1, new DialogInterface.OnClickListener() {\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic void onClick(DialogInterface dialog, int which) {\n\t\t\t\t\t\tdialog.dismiss();\n\t\t\t\t\t\tLogUtils.i(TAG, \"which:\" + which);\n\t\t\t\t\t\tif (callback != null) {\n\t\t\t\t\t\t\tMessage mMessage = Message.obtain();\n\t\t\t\t\t\t\tmMessage.what = which;\n\t\t\t\t\t\t\tcallback.handleMessage(mMessage);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t}\n\t\t\t\t}).setOnCancelListener(new DialogInterface.OnCancelListener() {\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic void onCancel(DialogInterface dialog) {\n\t\t\t\t\t\tdialog.dismiss();\n\t\t\t\t\t\tif (callback != null) {\n\t\t\t\t\t\t\tcallback.handleMessage(Message.obtain(null, -1));\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}).create();\n\t\tmAlertDialog.show();\n\t}\n\n\tprivate void onJsConfirmInternal(String message, JsResult jsResult) {\n\t\tLogUtils.i(TAG, \"activity:\" + mActivity.hashCode() + \"  \");\n\t\tActivity mActivity = this.mActivity;\n\t\tif (mActivity == null || mActivity.isFinishing()) {\n\t\t\ttoCancelJsresult(jsResult);\n\t\t\treturn;\n\t\t}\n\t\tif (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {\n\t\t\tif (mActivity.isDestroyed()) {\n\t\t\t\ttoCancelJsresult(jsResult);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\tif (mConfirmDialog == null) {\n\t\t\tmConfirmDialog = new AlertDialog.Builder(mActivity)\n\t\t\t\t\t.setMessage(message)\n\t\t\t\t\t.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {\n\t\t\t\t\t\t@Override\n\t\t\t\t\t\tpublic void onClick(DialogInterface dialog, int which) {\n\t\t\t\t\t\t\ttoDismissDialog(mConfirmDialog);\n\t\t\t\t\t\t\ttoCancelJsresult(mJsResult);\n\t\t\t\t\t\t}\n\t\t\t\t\t})//\n\t\t\t\t\t.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {\n\t\t\t\t\t\t@Override\n\t\t\t\t\t\tpublic void onClick(DialogInterface dialog, int which) {\n\t\t\t\t\t\t\ttoDismissDialog(mConfirmDialog);\n\t\t\t\t\t\t\tif (mJsResult != null) {\n\t\t\t\t\t\t\t\tmJsResult.confirm();\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\t\t\t\t\t.setOnCancelListener(new DialogInterface.OnCancelListener() {\n\t\t\t\t\t\t@Override\n\t\t\t\t\t\tpublic void onCancel(DialogInterface dialog) {\n\t\t\t\t\t\t\tdialog.dismiss();\n\t\t\t\t\t\t\ttoCancelJsresult(mJsResult);\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\t\t\t\t\t.create();\n\n\t\t}\n\t\tmConfirmDialog.setMessage(message);\n\t\tthis.mJsResult = jsResult;\n\t\tmConfirmDialog.show();\n\t}\n\n\n\tprivate void onJsPromptInternal(String message, String defaultValue, JsPromptResult jsPromptResult) {\n\t\tActivity mActivity = this.mActivity;\n\t\tif (mActivity == null || mActivity.isFinishing()) {\n\t\t\tjsPromptResult.cancel();\n\t\t\treturn;\n\t\t}\n\t\tif (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {\n\t\t\tif (mActivity.isDestroyed()) {\n\t\t\t\tjsPromptResult.cancel();\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\tif (mPromptDialog == null) {\n\t\t\tfinal EditText et = new EditText(mActivity);\n\t\t\tet.setText(defaultValue);\n\t\t\tmPromptDialog = new AlertDialog.Builder(mActivity)\n\t\t\t\t\t.setView(et)\n\t\t\t\t\t.setTitle(message)\n\t\t\t\t\t.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {\n\t\t\t\t\t\t@Override\n\t\t\t\t\t\tpublic void onClick(DialogInterface dialog, int which) {\n\t\t\t\t\t\t\ttoDismissDialog(mPromptDialog);\n\t\t\t\t\t\t\ttoCancelJsresult(mJsPromptResult);\n\t\t\t\t\t\t}\n\t\t\t\t\t})//\n\t\t\t\t\t.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {\n\t\t\t\t\t\t@Override\n\t\t\t\t\t\tpublic void onClick(DialogInterface dialog, int which) {\n\t\t\t\t\t\t\ttoDismissDialog(mPromptDialog);\n\n\t\t\t\t\t\t\tif (mJsPromptResult != null) {\n\t\t\t\t\t\t\t\tmJsPromptResult.confirm(et.getText().toString());\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\t\t\t\t\t.setOnCancelListener(new DialogInterface.OnCancelListener() {\n\t\t\t\t\t\t@Override\n\t\t\t\t\t\tpublic void onCancel(DialogInterface dialog) {\n\t\t\t\t\t\t\tdialog.dismiss();\n\t\t\t\t\t\t\ttoCancelJsresult(mJsPromptResult);\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\t\t\t\t\t.create();\n\t\t}\n\t\tthis.mJsPromptResult = jsPromptResult;\n\t\tmPromptDialog.show();\n\t}\n\n\t@Override\n\tpublic void onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult jsPromptResult) {\n\t\tonJsPromptInternal(message, defaultValue, jsPromptResult);\n\t}\n\n\t@Override\n\tpublic void onMainFrameError(WebView view, int errorCode, String description, String failingUrl) {\n\n\t\tLogUtils.i(TAG, \"mWebParentLayout onMainFrameError:\" + mWebParentLayout);\n\t\tif (mWebParentLayout != null) {\n\t\t\tmWebParentLayout.showPageMainFrameError();\n\t\t}\n\t}\n\n\t@Override\n\tpublic void onShowMainFrame() {\n\t\tif (mWebParentLayout != null) {\n\t\t\tmWebParentLayout.hideErrorLayout();\n\t\t}\n\t}\n\n\t@Override\n\tpublic void onLoading(String msg) {\n\t\tActivity mActivity;\n\t\tif ((mActivity = this.mActivity) == null || mActivity.isFinishing()) {\n\t\t\treturn;\n\t\t}\n\t\tif (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {\n\t\t\tif (mActivity.isDestroyed()) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\tif (mProgressDialog == null) {\n\t\t\tmProgressDialog = new ProgressDialog(mActivity);\n\t\t}\n\t\tmProgressDialog.setCancelable(false);\n\t\tmProgressDialog.setCanceledOnTouchOutside(false);\n\t\tmProgressDialog.setMessage(msg);\n\t\tmProgressDialog.show();\n\n\t}\n\n\t@Override\n\tpublic void onCancelLoading() {\n\t\tActivity mActivity;\n\t\tif ((mActivity = this.mActivity) == null || mActivity.isFinishing()) {\n\t\t\treturn;\n\t\t}\n\t\tif (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {\n\t\t\tif (mActivity.isDestroyed()) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\tif (mProgressDialog != null && mProgressDialog.isShowing()) {\n\t\t\tmProgressDialog.dismiss();\n\t\t}\n\t\tmProgressDialog = null;\n\t}\n\n\t@Override\n\tpublic void onShowMessage(String message, String from) {\n\t\tif (!TextUtils.isEmpty(from) && from.contains(\"performDownload\")) {\n\t\t\treturn;\n\t\t}\n\t\tAgentWebUtils.toastShowShort(mActivity.getApplicationContext(), message);\n\t}\n\n\t@Override\n\tpublic void onPermissionsDeny(String[] permissions, String permissionType, String action) {\n//\t\tAgentWebUtils.toastShowShort(mActivity.getApplicationContext(), \"权限被冻结\");\n\t}\n\n\t@Override\n\tpublic void onShowSslCertificateErrorDialog(final WebView view, final SslErrorHandler handler, final SslError error) {\n\t\tAlertDialog.Builder alertDialog = new AlertDialog.Builder(mActivity);\n\t\tString sslErrorMessage;\n\t\tswitch (error.getPrimaryError()) {\n\t\t\tcase SslError.SSL_UNTRUSTED:\n\t\t\t\tsslErrorMessage = mActivity.getString(R.string.agentweb_message_show_ssl_untrusted);\n\t\t\t\tbreak;\n\t\t\tcase SslError.SSL_EXPIRED:\n\t\t\t\tsslErrorMessage = mActivity.getString(R.string.agentweb_message_show_ssl_expired);\n\t\t\t\tbreak;\n\t\t\tcase SslError.SSL_IDMISMATCH:\n\t\t\t\tsslErrorMessage = mActivity.getString(R.string.agentweb_message_show_ssl_hostname_mismatch);\n\t\t\t\tbreak;\n\t\t\tcase SslError.SSL_NOTYETVALID:\n\t\t\t\tsslErrorMessage = mActivity.getString(R.string.agentweb_message_show_ssl_not_yet_valid);\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tsslErrorMessage = mActivity.getString(R.string.agentweb_message_show_ssl_error);\n\t\t}\n\t\tsslErrorMessage += mActivity.getString(R.string.agentweb_message_show_continue);\n\t\talertDialog.setTitle(mActivity.getString(R.string.agentweb_title_ssl_error));\n\t\talertDialog.setMessage(sslErrorMessage);\n\t\talertDialog.setPositiveButton(R.string.agentweb_continue, new DialogInterface.OnClickListener() {\n\t\t\t@Override\n\t\t\tpublic void onClick(DialogInterface dialog, int which) {\n\t\t\t\t// Ignore SSL certificate errors\n\t\t\t\thandler.proceed();\n\t\t\t}\n\t\t});\n\n\t\talertDialog.setNegativeButton(R.string.agentweb_cancel, new DialogInterface.OnClickListener() {\n\t\t\t@Override\n\t\t\tpublic void onClick(DialogInterface dialog, int which) {\n\t\t\t\thandler.cancel();\n\t\t\t}\n\t\t});\n\t\talertDialog.show();\n\n\n\t}\n\n\t@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)\n\t@Override\n\tpublic void onPermissionRequest(final PermissionRequest request) {\n\t\tfinal String[] resources = request.getResources();\n\t\tSet<String> resourcesSet = new HashSet<>(Arrays.asList(resources));\n\t\tArrayList<String> permissions = new ArrayList<>(resourcesSet.size());\n\t\tif (resourcesSet.contains(PermissionRequest.RESOURCE_VIDEO_CAPTURE)) {\n\t\t\tpermissions.add(Manifest.permission.CAMERA);\n\t\t}\n\t\tif (resourcesSet.contains(PermissionRequest.RESOURCE_AUDIO_CAPTURE)) {\n\t\t\tpermissions.add(Manifest.permission.RECORD_AUDIO);\n\t\t}\n\t\tif(permissions.isEmpty()){\n\t\t\trequest.grant(resources);\n\t\t\treturn;\n\t\t}\n\n\t\tfinal List<String> denyPermission = AgentWebUtils.getDeniedPermissions(mActivity, permissions.toArray(new String[]{}));\n\t\tif (denyPermission.isEmpty()) {\n\t\t\trequest.grant(resources);\n\t\t} else {\n\t\t\tAction action = Action.createPermissionsAction(denyPermission.toArray(new String[]{}));\n\t\t\taction.setPermissionListener(new AgentActionFragment.PermissionListener() {\n\t\t\t\t@Override\n\t\t\t\tpublic void onRequestPermissionsResult(@NonNull String[] permissions, @NonNull int[] grantResults, Bundle extras) {\n\t\t\t\t\tList<String> deny = AgentWebUtils.getDeniedPermissions(mActivity, denyPermission.toArray(new String[]{}));\n\t\t\t\t\tif (deny.isEmpty()) {\n\t\t\t\t\t\trequest.grant(resources);\n\t\t\t\t\t} else {\n\t\t\t\t\t\trequest.deny();\n\t\t\t\t\t}\n\n\t\t\t\t}\n\t\t\t});\n\t\t\tAgentActionFragment.start(mActivity, action);\n\t\t}\n\n\t}\n\n\tprivate void toCancelJsresult(JsResult result) {\n\t\tif (result != null) {\n\t\t\tresult.cancel();\n\t\t}\n\t}\n\n\n\t@Override\n\tprotected void bindSupportWebParent(WebParentLayout webParentLayout, Activity activity) {\n\t\tthis.mActivity = activity;\n\t\tthis.mWebParentLayout = webParentLayout;\n\t\tmResources = this.mActivity.getResources();\n\n\t}\n}\n"
  },
  {
    "path": "agentweb-core/src/main/java/com/just/agentweb/DefaultWebClient.java",
    "content": "/*\r\n * Copyright (C)  Justson(https://github.com/Justson/AgentWeb)\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n *      http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n\r\npackage com.just.agentweb;\r\n\r\nimport android.annotation.TargetApi;\r\nimport android.app.Activity;\r\nimport android.content.ActivityNotFoundException;\r\nimport android.content.Intent;\r\nimport android.content.pm.ActivityInfo;\r\nimport android.content.pm.PackageManager;\r\nimport android.content.pm.ResolveInfo;\r\nimport android.graphics.Bitmap;\r\nimport android.net.Uri;\r\nimport android.net.http.SslError;\r\nimport android.os.Build;\r\nimport android.os.Handler;\r\nimport android.os.Message;\r\nimport androidx.annotation.RequiresApi;\r\nimport android.text.TextUtils;\r\nimport android.view.KeyEvent;\r\nimport android.view.View;\r\nimport android.webkit.HttpAuthHandler;\r\nimport android.webkit.SslErrorHandler;\r\nimport android.webkit.WebResourceError;\r\nimport android.webkit.WebResourceRequest;\r\nimport android.webkit.WebResourceResponse;\r\nimport android.webkit.WebView;\r\nimport android.webkit.WebViewClient;\r\n\r\nimport com.alipay.sdk.app.H5PayCallback;\r\nimport com.alipay.sdk.app.PayTask;\r\nimport com.alipay.sdk.util.H5PayResultModel;\r\n\r\nimport java.lang.ref.WeakReference;\r\nimport java.lang.reflect.Constructor;\r\nimport java.lang.reflect.Method;\r\nimport java.net.URISyntaxException;\r\nimport java.util.HashSet;\r\nimport java.util.List;\r\nimport java.util.Set;\r\n\r\n/**\r\n * @author cenxiaozhong\r\n * @since 3.0.0\r\n */\r\npublic class DefaultWebClient extends MiddlewareWebClientBase {\r\n\t/**\r\n\t * Activity's WeakReference\r\n\t */\r\n\tprivate WeakReference<Activity> mWeakReference = null;\r\n\t/**\r\n\t * 缩放\r\n\t */\r\n\tprivate static final int CONSTANTS_ABNORMAL_BIG = 7;\r\n\t/**\r\n\t * WebViewClient\r\n\t */\r\n\tprivate WebViewClient mWebViewClient;\r\n\t/**\r\n\t * mWebClientHelper\r\n\t */\r\n\tprivate boolean webClientHelper = true;\r\n\t/**\r\n\t * intent ' s scheme\r\n\t */\r\n\tpublic static final String INTENT_SCHEME = \"intent://\";\r\n\t/**\r\n\t * Wechat pay scheme ，用于唤醒微信支付\r\n\t */\r\n\tpublic static final String WEBCHAT_PAY_SCHEME = \"weixin://wap/pay?\";\r\n\t/**\r\n\t * 支付宝\r\n\t */\r\n\tpublic static final String ALIPAYS_SCHEME = \"alipays://\";\r\n\t/**\r\n\t * http scheme\r\n\t */\r\n\tpublic static final String HTTP_SCHEME = \"http://\";\r\n\t/**\r\n\t * https scheme\r\n\t */\r\n\tpublic static final String HTTPS_SCHEME = \"https://\";\r\n\t/**\r\n\t * true 表示当前应用内依赖了 alipay library , false  反之\r\n\t */\r\n\tprivate static final boolean HAS_ALIPAY_LIB;\r\n\t/**\r\n\t * WebViewClient's tag 用于打印\r\n\t */\r\n\tprivate static final String TAG = DefaultWebClient.class.getSimpleName();\r\n\t/**\r\n\t * 直接打开其他页面\r\n\t */\r\n\tpublic static final int DERECT_OPEN_OTHER_PAGE = 1001;\r\n\t/**\r\n\t * 弹窗咨询用户是否前往其他页面\r\n\t */\r\n\tpublic static final int ASK_USER_OPEN_OTHER_PAGE = DERECT_OPEN_OTHER_PAGE >> 2;\r\n\t/**\r\n\t * 不允许打开其他页面\r\n\t */\r\n\tpublic static final int DISALLOW_OPEN_OTHER_APP = DERECT_OPEN_OTHER_PAGE >> 4;\r\n\t/**\r\n\t * 默认为咨询用户\r\n\t */\r\n\tprivate int mUrlHandleWays = ASK_USER_OPEN_OTHER_PAGE;\r\n\t/**\r\n\t * 是否拦截找不到相应页面的Url，默认拦截\r\n\t */\r\n\tprivate boolean mIsInterceptUnkownUrl = true;\r\n\t/**\r\n\t * AbsAgentWebUIController\r\n\t */\r\n\tprivate WeakReference<AbsAgentWebUIController> mAgentWebUIController = null;\r\n\t/**\r\n\t * WebView\r\n\t */\r\n\tprivate WebView mWebView;\r\n\t/**\r\n\t * 弹窗回调\r\n\t */\r\n\tprivate Handler.Callback mCallback = null;\r\n\t/**\r\n\t * MainFrameErrorMethod\r\n\t */\r\n\tprivate Method onMainFrameErrorMethod = null;\r\n\t/**\r\n\t * Alipay PayTask 对象\r\n\t */\r\n\tprivate Object mPayTask;\r\n\t/**\r\n\t * SMS scheme\r\n\t */\r\n\tpublic static final String SCHEME_SMS = \"sms:\";\r\n\t/**\r\n\t * 缓存当前出现错误的页面\r\n\t */\r\n\tprivate Set<String> mErrorUrlsSet = new HashSet<>();\r\n\t/**\r\n\t * 缓存等待加载完成的页面 onPageStart()执行之后 ，onPageFinished()执行之前\r\n\t */\r\n\tprivate Set<String> mWaittingFinishSet = new HashSet<>();\r\n\r\n\tstatic {\r\n\t\tboolean tag = true;\r\n\t\ttry {\r\n\t\t\tClass.forName(\"com.alipay.sdk.app.PayTask\");\r\n\t\t} catch (Throwable ignore) {\r\n\t\t\ttag = false;\r\n\t\t}\r\n\t\tHAS_ALIPAY_LIB = tag;\r\n\t\tLogUtils.i(TAG, \"HAS_ALIPAY_LIB:\" + HAS_ALIPAY_LIB);\r\n\t}\r\n\r\n\r\n\tDefaultWebClient(Builder builder) {\r\n\t\tsuper(builder.mClient);\r\n\t\tthis.mWebView = builder.mWebView;\r\n\t\tthis.mWebViewClient = builder.mClient;\r\n\t\tmWeakReference = new WeakReference<Activity>(builder.mActivity);\r\n\t\tthis.webClientHelper = builder.mWebClientHelper;\r\n\t\tmAgentWebUIController = new WeakReference<AbsAgentWebUIController>(AgentWebUtils.getAgentWebUIControllerByWebView(builder.mWebView));\r\n\t\tmIsInterceptUnkownUrl = builder.mIsInterceptUnkownScheme;\r\n\t\tif (builder.mUrlHandleWays <= 0) {\r\n\t\t\tmUrlHandleWays = ASK_USER_OPEN_OTHER_PAGE;\r\n\t\t} else {\r\n\t\t\tmUrlHandleWays = builder.mUrlHandleWays;\r\n\t\t}\r\n\t}\r\n\r\n\t@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)\r\n\t@Override\r\n\tpublic boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {\r\n\t\tString url = request.getUrl().toString();\r\n\t\tif (url.startsWith(HTTP_SCHEME) || url.startsWith(HTTPS_SCHEME)) {\r\n\t\t\treturn (webClientHelper && HAS_ALIPAY_LIB && isAlipay(view, url));\r\n\t\t}\r\n\t\tif (!webClientHelper) {\r\n\t\t\treturn super.shouldOverrideUrlLoading(view, request);\r\n\t\t}\r\n\t\tif (handleCommonLink(url)) {\r\n\t\t\treturn true;\r\n\t\t}\r\n\t\t// intent\r\n\t\tif (url.startsWith(INTENT_SCHEME)) {\r\n\t\t\thandleIntentUrl(url);\r\n\t\t\tLogUtils.i(TAG, \"intent url \");\r\n\t\t\treturn true;\r\n\t\t}\r\n\t\t// 微信支付\r\n\t\tif (url.startsWith(WEBCHAT_PAY_SCHEME)) {\r\n\t\t\tLogUtils.i(TAG, \"lookup wechat to pay ~~\");\r\n\t\t\tstartActivity(url);\r\n\t\t\treturn true;\r\n\t\t}\r\n\t\tif (url.startsWith(ALIPAYS_SCHEME) && lookup(url)) {\r\n\t\t\tLogUtils.i(TAG, \"alipays url lookup alipay ~~ \");\r\n\t\t\treturn true;\r\n\t\t}\r\n\t\tif (queryActiviesNumber(url) > 0 && deepLink(url)) {\r\n\t\t\tLogUtils.i(TAG, \"intercept url:\" + url);\r\n\t\t\treturn true;\r\n\t\t}\r\n\t\tif (mIsInterceptUnkownUrl) {\r\n\t\t\tLogUtils.i(TAG, \"intercept UnkownUrl :\" + request.getUrl());\r\n\t\t\treturn true;\r\n\t\t}\r\n\t\treturn super.shouldOverrideUrlLoading(view, request);\r\n\t}\r\n\r\n\t@Override\r\n\tpublic WebResourceResponse shouldInterceptRequest(WebView view, String url) {\r\n\t\treturn super.shouldInterceptRequest(view, url);\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void onReceivedHttpAuthRequest(WebView view, HttpAuthHandler handler, String host, String realm) {\r\n\t\tsuper.onReceivedHttpAuthRequest(view, handler, host, realm);\r\n\t}\r\n\r\n\tprivate boolean deepLink(String url) {\r\n\t\tswitch (mUrlHandleWays) {\r\n\t\t\t// 直接打开其他App\r\n\t\t\tcase DERECT_OPEN_OTHER_PAGE:\r\n\t\t\t\tlookup(url);\r\n\t\t\t\treturn true;\r\n\t\t\t// 咨询用户是否打开其他App\r\n\t\t\tcase ASK_USER_OPEN_OTHER_PAGE:\r\n\t\t\t\tActivity mActivity = null;\r\n\t\t\t\tif ((mActivity = mWeakReference.get()) == null) {\r\n\t\t\t\t\treturn false;\r\n\t\t\t\t}\r\n\t\t\t\tResolveInfo resolveInfo = lookupResolveInfo(url);\r\n\t\t\t\tif (null == resolveInfo) {\r\n\t\t\t\t\treturn false;\r\n\t\t\t\t}\r\n\t\t\t\tActivityInfo activityInfo = resolveInfo.activityInfo;\r\n\t\t\t\tLogUtils.e(TAG, \"resolve package:\" + resolveInfo.activityInfo.packageName + \" app package:\" + mActivity.getPackageName());\r\n\t\t\t\tif (activityInfo != null\r\n\t\t\t\t\t\t&& !TextUtils.isEmpty(activityInfo.packageName)\r\n\t\t\t\t\t\t&& activityInfo.packageName.equals(mActivity.getPackageName())) {\r\n\t\t\t\t\treturn lookup(url);\r\n\t\t\t\t}\r\n\t\t\t\tif (mAgentWebUIController.get() != null) {\r\n\t\t\t\t\tmAgentWebUIController.get()\r\n\t\t\t\t\t\t\t.onOpenPagePrompt(this.mWebView,\r\n\t\t\t\t\t\t\t\t\tmWebView.getUrl(),\r\n\t\t\t\t\t\t\t\t\tgetCallback(url));\r\n\t\t\t\t}\r\n\t\t\t\treturn true;\r\n\t\t\t// 默认不打开\r\n\t\t\tdefault:\r\n\t\t\t\treturn false;\r\n\t\t}\r\n\t}\r\n\r\n\t@Override\r\n\tpublic WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) {\r\n\t\treturn super.shouldInterceptRequest(view, request);\r\n\t}\r\n\r\n\t@Override\r\n\tpublic boolean shouldOverrideUrlLoading(WebView view, String url) {\r\n\t\tif (url.startsWith(HTTP_SCHEME) || url.startsWith(HTTPS_SCHEME)) {\r\n\t\t\treturn (webClientHelper && HAS_ALIPAY_LIB && isAlipay(view, url));\r\n\t\t}\r\n\t\tif (!webClientHelper) {\r\n\t\t\treturn false;\r\n\t\t}\r\n\t\t//电话 ， 邮箱 ， 短信\r\n\t\tif (handleCommonLink(url)) {\r\n\t\t\treturn true;\r\n\t\t}\r\n\t\t//Intent scheme\r\n\t\tif (url.startsWith(INTENT_SCHEME)) {\r\n\t\t\thandleIntentUrl(url);\r\n\t\t\treturn true;\r\n\t\t}\r\n\t\t//微信支付\r\n\t\tif (url.startsWith(WEBCHAT_PAY_SCHEME)) {\r\n\t\t\tstartActivity(url);\r\n\t\t\treturn true;\r\n\t\t}\r\n\t\t//支付宝\r\n\t\tif (url.startsWith(ALIPAYS_SCHEME) && lookup(url)) {\r\n\t\t\treturn true;\r\n\t\t}\r\n\t\t//打开url 相对应的页面\r\n\t\tif (queryActiviesNumber(url) > 0 && deepLink(url)) {\r\n\t\t\tLogUtils.i(TAG, \"intercept OtherAppScheme\");\r\n\t\t\treturn true;\r\n\t\t}\r\n\t\t// 手机里面没有页面能匹配到该链接 ，拦截下来。\r\n\t\tif (mIsInterceptUnkownUrl) {\r\n\t\t\tLogUtils.i(TAG, \"intercept InterceptUnkownScheme : \" + url);\r\n\t\t\treturn true;\r\n\t\t}\r\n\t\treturn super.shouldOverrideUrlLoading(view, url);\r\n\t}\r\n\r\n\r\n\tprivate int queryActiviesNumber(String url) {\r\n\t\ttry {\r\n\t\t\tif (mWeakReference.get() == null) {\r\n\t\t\t\treturn 0;\r\n\t\t\t}\r\n\t\t\tIntent intent = Intent.parseUri(url, Intent.URI_INTENT_SCHEME);\r\n\t\t\tPackageManager mPackageManager = mWeakReference.get().getPackageManager();\r\n\t\t\tList<ResolveInfo> mResolveInfos = mPackageManager.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);\r\n\t\t\treturn mResolveInfos == null ? 0 : mResolveInfos.size();\r\n\t\t} catch (URISyntaxException ignore) {\r\n\t\t\tif (LogUtils.isDebug()) {\r\n\t\t\t\tignore.printStackTrace();\r\n\t\t\t}\r\n\t\t\treturn 0;\r\n\t\t}\r\n\t}\r\n\r\n\tprivate void handleIntentUrl(String intentUrl) {\r\n\t\ttry {\r\n\t\t\tIntent intent = null;\r\n\t\t\tif (TextUtils.isEmpty(intentUrl) || !intentUrl.startsWith(INTENT_SCHEME)) {\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\t\t\tif (lookup(intentUrl)) {\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\t\t} catch (Throwable e) {\r\n\t\t\tif (LogUtils.isDebug()) {\r\n\t\t\t\te.printStackTrace();\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\r\n\tprivate ResolveInfo lookupResolveInfo(String url) {\r\n\t\ttry {\r\n\t\t\tIntent intent;\r\n\t\t\tActivity mActivity = null;\r\n\t\t\tif ((mActivity = mWeakReference.get()) == null) {\r\n\t\t\t\treturn null;\r\n\t\t\t}\r\n\t\t\tPackageManager packageManager = mActivity.getPackageManager();\r\n\t\t\tintent = Intent.parseUri(url, Intent.URI_INTENT_SCHEME);\r\n\t\t\tResolveInfo info = packageManager.resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY);\r\n\t\t\treturn info;\r\n\t\t} catch (Throwable ignore) {\r\n\t\t\tif (LogUtils.isDebug()) {\r\n\t\t\t\tignore.printStackTrace();\r\n\t\t\t}\r\n\t\t}\r\n\t\treturn null;\r\n\t}\r\n\r\n\tprivate boolean lookup(String url) {\r\n\t\ttry {\r\n\t\t\tIntent intent;\r\n\t\t\tActivity mActivity = null;\r\n\t\t\tif ((mActivity = mWeakReference.get()) == null) {\r\n\t\t\t\treturn true;\r\n\t\t\t}\r\n\t\t\tPackageManager packageManager = mActivity.getPackageManager();\r\n\t\t\tintent = Intent.parseUri(url, Intent.URI_INTENT_SCHEME);\r\n\t\t\tResolveInfo info = packageManager.resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY);\r\n\t\t\t// 跳到该应用\r\n\t\t\tif (info != null) {\r\n\t\t\t\tmActivity.startActivity(intent);\r\n\t\t\t\treturn true;\r\n\t\t\t}\r\n\t\t} catch (Throwable ignore) {\r\n\t\t\tif (LogUtils.isDebug()) {\r\n\t\t\t\tignore.printStackTrace();\r\n\t\t\t}\r\n\t\t}\r\n\t\treturn false;\r\n\t}\r\n\r\n\tprivate boolean isAlipay(final WebView view, String url) {\r\n\t\ttry {\r\n\t\t\tActivity mActivity = null;\r\n\t\t\tif ((mActivity = mWeakReference.get()) == null) {\r\n\t\t\t\treturn false;\r\n\t\t\t}\r\n\t\t\t/**\r\n\t\t\t * 推荐采用的新的二合一接口(payInterceptorWithUrl),只需调用一次\r\n\t\t\t */\r\n\t\t\tif (mPayTask == null) {\r\n\t\t\t\tClass clazz = Class.forName(\"com.alipay.sdk.app.PayTask\");\r\n\t\t\t\tConstructor<?> mConstructor = clazz.getConstructor(Activity.class);\r\n\t\t\t\tmPayTask = mConstructor.newInstance(mActivity);\r\n\t\t\t}\r\n\t\t\tfinal PayTask task = (PayTask) mPayTask;\r\n\t\t\tboolean isIntercepted = task.payInterceptorWithUrl(url, true, new H5PayCallback() {\r\n\t\t\t\t@Override\r\n\t\t\t\tpublic void onPayResult(final H5PayResultModel result) {\r\n\t\t\t\t\tfinal String url = result.getReturnUrl();\r\n\t\t\t\t\tif (!TextUtils.isEmpty(url)) {\r\n\t\t\t\t\t\tAgentWebUtils.runInUiThread(new Runnable() {\r\n\t\t\t\t\t\t\t@Override\r\n\t\t\t\t\t\t\tpublic void run() {\r\n\t\t\t\t\t\t\t\tview.loadUrl(url);\r\n\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t});\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t});\r\n\t\t\tif (isIntercepted) {\r\n\t\t\t\tLogUtils.i(TAG, \"alipay-isIntercepted:\" + isIntercepted + \"  url:\" + url);\r\n\t\t\t}\r\n\t\t\treturn isIntercepted;\r\n\t\t} catch (Throwable ignore) {\r\n\t\t\tif (AgentWebConfig.DEBUG) {\r\n//                ignore.printStackTrace();\r\n\t\t\t}\r\n\t\t}\r\n\t\treturn false;\r\n\t}\r\n\r\n\r\n\tprivate boolean handleCommonLink(String url) {\r\n\t\tif (url.startsWith(WebView.SCHEME_TEL)\r\n\t\t\t\t|| url.startsWith(SCHEME_SMS)\r\n\t\t\t\t|| url.startsWith(WebView.SCHEME_MAILTO)\r\n\t\t\t\t|| url.startsWith(WebView.SCHEME_GEO)) {\r\n\t\t\ttry {\r\n\t\t\t\tActivity mActivity = null;\r\n\t\t\t\tif ((mActivity = mWeakReference.get()) == null) {\r\n\t\t\t\t\treturn false;\r\n\t\t\t\t}\r\n\t\t\t\tIntent intent = new Intent(Intent.ACTION_VIEW);\r\n\t\t\t\tintent.setData(Uri.parse(url));\r\n\t\t\t\tmActivity.startActivity(intent);\r\n\t\t\t} catch (ActivityNotFoundException ignored) {\r\n\t\t\t\tif (AgentWebConfig.DEBUG) {\r\n\t\t\t\t\tignored.printStackTrace();\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\treturn true;\r\n\t\t}\r\n\t\treturn false;\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void onPageStarted(WebView view, String url, Bitmap favicon) {\r\n\t\tif (!mWaittingFinishSet.contains(url)) {\r\n\t\t\tmWaittingFinishSet.add(url);\r\n\t\t}\r\n\t\tsuper.onPageStarted(view, url, favicon);\r\n\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {\r\n\t\tif (mAgentWebUIController.get() != null) {\r\n\t\t\tmAgentWebUIController.get().onShowSslCertificateErrorDialog(view, handler, error);\r\n\t\t}\r\n\t}\r\n\r\n\r\n\t/**\r\n\t * MainFrame Error\r\n\t *\r\n\t * @param view\r\n\t * @param errorCode\r\n\t * @param description\r\n\t * @param failingUrl\r\n\t */\r\n\t@Override\r\n\tpublic void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {\r\n\t\tLogUtils.i(TAG, \"onReceivedError：\" + description + \"  CODE:\" + errorCode);\r\n\t\tif (failingUrl == null && errorCode != -12) {\r\n\t\t\treturn;\r\n\t\t}\r\n\t\tif (errorCode == -1) {\r\n\t\t\treturn;\r\n\t\t}\r\n\t\tif (errorCode != ERROR_HOST_LOOKUP && (failingUrl != null && !failingUrl.equals(view.getUrl()) && !failingUrl.equals(view.getOriginalUrl()))) {\r\n\t\t\treturn;\r\n\t\t}\r\n\t\tonMainFrameError(view, errorCode, description, failingUrl);\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void doUpdateVisitedHistory(WebView view, String url, boolean isReload) {\r\n\t\tif (!mWaittingFinishSet.contains(url)) {\r\n\t\t\tmWaittingFinishSet.add(url);\r\n\t\t}\r\n\t\tsuper.doUpdateVisitedHistory(view, url, isReload);\r\n\t}\r\n\r\n\t@TargetApi(Build.VERSION_CODES.M)\r\n\t@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)\r\n\t@Override\r\n\tpublic void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) {\r\n\t\tString failingUrl = request.getUrl().toString();\r\n\t\tint errorCode = error.getErrorCode();\r\n\t\tif (!request.isForMainFrame()) {\r\n\t\t\treturn;\r\n\t\t}\r\n\t\tif (failingUrl == null && errorCode != ERROR_BAD_URL) {\r\n\t\t\treturn;\r\n\t\t}\r\n\t\tif (errorCode == ERROR_UNKNOWN) {\r\n\t\t\treturn;\r\n\t\t}\r\n\t\tLogUtils.i(TAG, \"onReceivedError:\" + error.getDescription() + \" code:\" + error.getErrorCode() + \" failingUrl:\" + failingUrl + \" getUrl:\" + view.getUrl() + \" getOriginalUrl:\" + view.getOriginalUrl());\r\n\t\tif (errorCode != ERROR_HOST_LOOKUP &&\r\n\t\t\t\t(failingUrl != null && !failingUrl.equals(view.getUrl()) && !failingUrl.equals(view.getOriginalUrl()))) {\r\n\t\t\treturn;\r\n\t\t}\r\n\t\tonMainFrameError(view,\r\n\t\t\t\terror.getErrorCode(), error.getDescription().toString(),\r\n\t\t\t\trequest.getUrl().toString());\r\n\r\n\t}\r\n\r\n\tprivate void onMainFrameError(WebView view, int errorCode, String description, String failingUrl) {\r\n\t\tmErrorUrlsSet.add(failingUrl);\r\n\t\t// 下面逻辑判断开发者是否重写了 onMainFrameError 方法 ， 优先交给开发者处理\r\n\t\tif (this.mWebViewClient != null && webClientHelper) {\r\n\t\t\tMethod mMethod = this.onMainFrameErrorMethod;\r\n\t\t\tif (mMethod != null || (this.onMainFrameErrorMethod = mMethod = AgentWebUtils.isExistMethod(mWebViewClient, \"onMainFrameError\", AbsAgentWebUIController.class, WebView.class, int.class, String.class, String.class)) != null) {\r\n\t\t\t\ttry {\r\n\t\t\t\t\tmMethod.invoke(this.mWebViewClient, mAgentWebUIController.get(), view, errorCode, description, failingUrl);\r\n\t\t\t\t} catch (Throwable ignore) {\r\n\t\t\t\t\tif (LogUtils.isDebug()) {\r\n\t\t\t\t\t\tignore.printStackTrace();\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\t\t}\r\n\t\tif (mAgentWebUIController.get() != null) {\r\n\t\t\tmAgentWebUIController.get().onMainFrameError(view, errorCode, description, failingUrl);\r\n\t\t}\r\n//        this.mWebView.setVisibility(View.GONE);\r\n\t}\r\n\r\n\r\n\t@Override\r\n\tpublic void onPageFinished(WebView view, String url) {\r\n\t\tif (!mErrorUrlsSet.contains(url) && mWaittingFinishSet.contains(url)) {\r\n\t\t\tif (mAgentWebUIController.get() != null) {\r\n\t\t\t\tmAgentWebUIController.get().onShowMainFrame();\r\n\t\t\t}\r\n\t\t} else {\r\n\t\t\tview.setVisibility(View.VISIBLE);\r\n\t\t}\r\n\t\tif (mWaittingFinishSet.contains(url)) {\r\n\t\t\tmWaittingFinishSet.remove(url);\r\n\t\t}\r\n\t\tif (!mErrorUrlsSet.isEmpty()) {\r\n\t\t\tmErrorUrlsSet.clear();\r\n\t\t}\r\n\t\tsuper.onPageFinished(view, url);\r\n\t}\r\n\r\n\r\n\t@Override\r\n\tpublic boolean shouldOverrideKeyEvent(WebView view, KeyEvent event) {\r\n\t\treturn super.shouldOverrideKeyEvent(view, event);\r\n\t}\r\n\r\n\r\n\tprivate void startActivity(String url) {\r\n\t\ttry {\r\n\t\t\tif (mWeakReference.get() == null) {\r\n\t\t\t\treturn;\r\n\t\t\t}\r\n\t\t\tIntent intent = new Intent();\r\n\t\t\tintent.setAction(Intent.ACTION_VIEW);\r\n\t\t\tintent.setData(Uri.parse(url));\r\n\t\t\tmWeakReference.get().startActivity(intent);\r\n\r\n\t\t} catch (Exception e) {\r\n\t\t\tif (LogUtils.isDebug()) {\r\n\t\t\t\te.printStackTrace();\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void onReceivedHttpError(WebView view, WebResourceRequest request, WebResourceResponse errorResponse) {\r\n\t\tsuper.onReceivedHttpError(view, request, errorResponse);\r\n\t}\r\n\r\n\r\n\t@Override\r\n\tpublic void onScaleChanged(WebView view, float oldScale, float newScale) {\r\n\t\tLogUtils.i(TAG, \"onScaleChanged:\" + oldScale + \"   n:\" + newScale);\r\n\t\tif (newScale - oldScale > CONSTANTS_ABNORMAL_BIG) {\r\n\t\t\tview.setInitialScale((int) (oldScale / newScale * 100));\r\n\t\t}\r\n\t}\r\n\r\n\tprivate Handler.Callback getCallback(final String url) {\r\n\t\tif (this.mCallback != null) {\r\n\t\t\treturn this.mCallback;\r\n\t\t}\r\n\t\treturn this.mCallback = new Handler.Callback() {\r\n\t\t\t@Override\r\n\t\t\tpublic boolean handleMessage(Message msg) {\r\n\t\t\t\tswitch (msg.what) {\r\n\t\t\t\t\tcase 1:\r\n\t\t\t\t\t\tlookup(url);\r\n\t\t\t\t\t\tbreak;\r\n\t\t\t\t\tdefault:\r\n\t\t\t\t\t\treturn true;\r\n\t\t\t\t}\r\n\t\t\t\treturn true;\r\n\t\t\t}\r\n\t\t};\r\n\t}\r\n\r\n\r\n\tpublic static Builder createBuilder() {\r\n\t\treturn new Builder();\r\n\t}\r\n\r\n\tpublic static class Builder {\r\n\t\tprivate Activity mActivity;\r\n\t\tprivate WebViewClient mClient;\r\n\t\tprivate boolean mWebClientHelper;\r\n\t\tprivate PermissionInterceptor mPermissionInterceptor;\r\n\t\tprivate WebView mWebView;\r\n\t\tprivate boolean mIsInterceptUnkownScheme = true;\r\n\t\tprivate int mUrlHandleWays;\r\n\r\n\t\tpublic Builder setActivity(Activity activity) {\r\n\t\t\tthis.mActivity = activity;\r\n\t\t\treturn this;\r\n\t\t}\r\n\r\n\t\tpublic Builder setClient(WebViewClient client) {\r\n\t\t\tthis.mClient = client;\r\n\t\t\treturn this;\r\n\t\t}\r\n\r\n\t\tpublic Builder setWebClientHelper(boolean webClientHelper) {\r\n\t\t\tthis.mWebClientHelper = webClientHelper;\r\n\t\t\treturn this;\r\n\t\t}\r\n\r\n\t\tpublic Builder setPermissionInterceptor(PermissionInterceptor permissionInterceptor) {\r\n\t\t\tthis.mPermissionInterceptor = permissionInterceptor;\r\n\t\t\treturn this;\r\n\t\t}\r\n\r\n\t\tpublic Builder setWebView(WebView webView) {\r\n\t\t\tthis.mWebView = webView;\r\n\t\t\treturn this;\r\n\t\t}\r\n\r\n\t\tpublic Builder setInterceptUnkownUrl(boolean interceptUnkownScheme) {\r\n\t\t\tthis.mIsInterceptUnkownScheme = interceptUnkownScheme;\r\n\t\t\treturn this;\r\n\t\t}\r\n\r\n\t\tpublic Builder setUrlHandleWays(int urlHandleWays) {\r\n\t\t\tthis.mUrlHandleWays = urlHandleWays;\r\n\t\t\treturn this;\r\n\t\t}\r\n\r\n\t\tpublic DefaultWebClient build() {\r\n\t\t\treturn new DefaultWebClient(this);\r\n\t\t}\r\n\t}\r\n\r\n\tpublic static enum OpenOtherPageWays {\r\n\t\t/**\r\n\t\t * 直接打开跳转页\r\n\t\t */\r\n\t\tDERECT(DefaultWebClient.DERECT_OPEN_OTHER_PAGE),\r\n\t\t/**\r\n\t\t * 咨询用户是否打开\r\n\t\t */\r\n\t\tASK(DefaultWebClient.ASK_USER_OPEN_OTHER_PAGE),\r\n\t\t/**\r\n\t\t * 禁止打开其他页面\r\n\t\t */\r\n\t\tDISALLOW(DefaultWebClient.DISALLOW_OPEN_OTHER_APP);\r\n\t\tint code;\r\n\r\n\t\tOpenOtherPageWays(int code) {\r\n\t\t\tthis.code = code;\r\n\t\t}\r\n\t}\r\n}\r\n"
  },
  {
    "path": "agentweb-core/src/main/java/com/just/agentweb/DefaultWebCreator.java",
    "content": "/*\r\n * Copyright (C)  Justson(https://github.com/Justson/AgentWeb)\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n *      http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n\r\npackage com.just.agentweb;\r\n\r\nimport android.app.Activity;\r\nimport android.graphics.Color;\r\n\r\nimport androidx.annotation.NonNull;\r\nimport androidx.annotation.Nullable;\r\nimport android.view.Gravity;\r\nimport android.view.View;\r\nimport android.view.ViewGroup;\r\nimport android.view.ViewStub;\r\nimport android.webkit.WebView;\r\nimport android.widget.FrameLayout;\r\n\r\nimport static com.just.agentweb.AgentWebConfig.WEBVIEW_DEFAULT_TYPE;\r\n\r\n/**\r\n * @author cenxiaozhong\r\n * @since 1.0.0\r\n */\r\npublic class DefaultWebCreator implements WebCreator {\r\n    private Activity mActivity;\r\n    private ViewGroup mViewGroup;\r\n    private boolean mIsNeedDefaultProgress;\r\n    private int mIndex;\r\n    private BaseIndicatorView mProgressView;\r\n    private ViewGroup.LayoutParams mLayoutParams = null;\r\n    private int mColor = -1;\r\n    /**\r\n     * 单位dp\r\n     */\r\n    private int mHeight;\r\n    private boolean mIsCreated = false;\r\n    private IWebLayout mIWebLayout;\r\n    private BaseIndicatorSpec mBaseIndicatorSpec;\r\n    private WebView mWebView = null;\r\n    private FrameLayout mFrameLayout = null;\r\n    private View mTargetProgress;\r\n    private static final String TAG = DefaultWebCreator.class.getSimpleName();\r\n    private int mWebViewType = WEBVIEW_DEFAULT_TYPE;\r\n\r\n    /**\r\n     * 使用默认的进度条\r\n     *\r\n     * @param activity\r\n     * @param viewGroup\r\n     * @param lp\r\n     * @param index\r\n     * @param color\r\n     * @param mHeight\r\n     * @param webView\r\n     * @param webLayout\r\n     */\r\n    protected DefaultWebCreator(@NonNull Activity activity,\r\n                                @Nullable ViewGroup viewGroup,\r\n                                ViewGroup.LayoutParams lp,\r\n                                int index,\r\n                                int color,\r\n                                int mHeight,\r\n                                WebView webView,\r\n                                IWebLayout webLayout) {\r\n        this.mActivity = activity;\r\n        this.mViewGroup = viewGroup;\r\n        this.mIsNeedDefaultProgress = true;\r\n        this.mIndex = index;\r\n        this.mColor = color;\r\n        this.mLayoutParams = lp;\r\n        this.mHeight = mHeight;\r\n        this.mWebView = webView;\r\n        this.mIWebLayout = webLayout;\r\n    }\r\n\r\n    /**\r\n     * 关闭进度条\r\n     *\r\n     * @param activity\r\n     * @param viewGroup\r\n     * @param lp\r\n     * @param index\r\n     * @param webView\r\n     * @param webLayout\r\n     */\r\n    protected DefaultWebCreator(@NonNull Activity activity, @Nullable ViewGroup viewGroup, ViewGroup.LayoutParams lp, int index, @Nullable WebView webView, IWebLayout webLayout) {\r\n        this.mActivity = activity;\r\n        this.mViewGroup = viewGroup;\r\n        this.mIsNeedDefaultProgress = false;\r\n        this.mIndex = index;\r\n        this.mLayoutParams = lp;\r\n        this.mWebView = webView;\r\n        this.mIWebLayout = webLayout;\r\n    }\r\n\r\n    /**\r\n     * 自定义Indicator\r\n     *\r\n     * @param activity\r\n     * @param viewGroup\r\n     * @param lp\r\n     * @param index\r\n     * @param progressView\r\n     * @param webView\r\n     * @param webLayout\r\n     */\r\n    protected DefaultWebCreator(@NonNull Activity activity, @Nullable ViewGroup viewGroup, ViewGroup.LayoutParams lp, int index, BaseIndicatorView progressView, WebView webView, IWebLayout webLayout) {\r\n        this.mActivity = activity;\r\n        this.mViewGroup = viewGroup;\r\n        this.mIsNeedDefaultProgress = false;\r\n        this.mIndex = index;\r\n        this.mLayoutParams = lp;\r\n        this.mProgressView = progressView;\r\n        this.mWebView = webView;\r\n        this.mIWebLayout = webLayout;\r\n    }\r\n\r\n\r\n    public void setWebView(WebView webView) {\r\n        mWebView = webView;\r\n    }\r\n\r\n    public FrameLayout getFrameLayout() {\r\n        return mFrameLayout;\r\n    }\r\n\r\n\r\n    public View getTargetProgress() {\r\n        return mTargetProgress;\r\n    }\r\n\r\n    public void setTargetProgress(View targetProgress) {\r\n        this.mTargetProgress = targetProgress;\r\n    }\r\n\r\n    @Override\r\n    public DefaultWebCreator create() {\r\n        if (mIsCreated) {\r\n            return this;\r\n        }\r\n        mIsCreated = true;\r\n        ViewGroup mViewGroup = this.mViewGroup;\r\n        if (mViewGroup == null) {\r\n            mViewGroup = this.mFrameLayout = (FrameLayout) createLayout();\r\n            mActivity.setContentView(mViewGroup);\r\n        } else {\r\n            if (mIndex == -1) {\r\n                mViewGroup.addView(this.mFrameLayout = (FrameLayout) createLayout(), mLayoutParams);\r\n            } else {\r\n                mViewGroup.addView(this.mFrameLayout = (FrameLayout) createLayout(), mIndex, mLayoutParams);\r\n            }\r\n        }\r\n        return this;\r\n    }\r\n\r\n    @Override\r\n    public WebView getWebView() {\r\n        return mWebView;\r\n    }\r\n\r\n    @Override\r\n    public FrameLayout getWebParentLayout() {\r\n        return mFrameLayout;\r\n    }\r\n\r\n    @Override\r\n    public int getWebViewType() {\r\n        return this.mWebViewType;\r\n    }\r\n\r\n    private ViewGroup createLayout() {\r\n        Activity mActivity = this.mActivity;\r\n        WebParentLayout mFrameLayout = new WebParentLayout(mActivity);\r\n        mFrameLayout.setId(R.id.web_parent_layout_id);\r\n        mFrameLayout.setBackgroundColor(Color.WHITE);\r\n        View target = mIWebLayout == null ? (this.mWebView = (WebView) createWebView()) : webLayout();\r\n        FrameLayout.LayoutParams mLayoutParams = new FrameLayout.LayoutParams(-1, -1);\r\n        mFrameLayout.addView(target, mLayoutParams);\r\n        mFrameLayout.bindWebView(this.mWebView);\r\n        LogUtils.i(TAG, \"  instanceof  AgentWebView:\" + (this.mWebView instanceof AgentWebView));\r\n        if (this.mWebView instanceof AgentWebView) {\r\n            this.mWebViewType = AgentWebConfig.WEBVIEW_AGENTWEB_SAFE_TYPE;\r\n        }\r\n        ViewStub mViewStub = new ViewStub(mActivity);\r\n        mViewStub.setId(R.id.mainframe_error_viewsub_id);\r\n        mFrameLayout.addView(mViewStub, new FrameLayout.LayoutParams(-1, -1));\r\n        if (mIsNeedDefaultProgress) {\r\n            FrameLayout.LayoutParams lp = null;\r\n            WebIndicator mWebIndicator = new WebIndicator(mActivity);\r\n            if (mHeight > 0) {\r\n                lp = new FrameLayout.LayoutParams(-2, AgentWebUtils.dp2px(mActivity, mHeight));\r\n            } else {\r\n                lp = mWebIndicator.offerLayoutParams();\r\n            }\r\n            if (mColor != -1) {\r\n                mWebIndicator.setColor(mColor);\r\n            }\r\n            lp.gravity = Gravity.TOP;\r\n            mFrameLayout.addView((View) (this.mBaseIndicatorSpec = mWebIndicator), lp);\r\n            mWebIndicator.setVisibility(View.GONE);\r\n        } else if (!mIsNeedDefaultProgress && mProgressView != null) {\r\n            mFrameLayout.addView((View) (this.mBaseIndicatorSpec = (BaseIndicatorSpec) mProgressView), mProgressView.offerLayoutParams());\r\n            mProgressView.setVisibility(View.GONE);\r\n        }\r\n        return mFrameLayout;\r\n    }\r\n\r\n\r\n    private View webLayout() {\r\n        WebView mWebView = null;\r\n        if ((mWebView = mIWebLayout.getWebView()) == null) {\r\n            mWebView = createWebView();\r\n            mIWebLayout.getLayout().addView(mWebView, -1, -1);\r\n            LogUtils.i(TAG, \"add webview\");\r\n        } else {\r\n            this.mWebViewType = AgentWebConfig.WEBVIEW_CUSTOM_TYPE;\r\n        }\r\n        this.mWebView = mWebView;\r\n        return mIWebLayout.getLayout();\r\n    }\r\n\r\n    private WebView createWebView() {\r\n        WebView mWebView = null;\r\n        if (this.mWebView != null) {\r\n            mWebView = this.mWebView;\r\n            this.mWebViewType = AgentWebConfig.WEBVIEW_CUSTOM_TYPE;\r\n        } else if (AgentWebConfig.IS_KITKAT_OR_BELOW_KITKAT) {\r\n            mWebView = new AgentWebView(mActivity);\r\n            this.mWebViewType = AgentWebConfig.WEBVIEW_AGENTWEB_SAFE_TYPE;\r\n        } else {\r\n            mWebView = new LollipopFixedWebView(mActivity);\r\n            this.mWebViewType = WEBVIEW_DEFAULT_TYPE;\r\n        }\r\n        return mWebView;\r\n    }\r\n\r\n    @Override\r\n    public BaseIndicatorSpec offer() {\r\n        return mBaseIndicatorSpec;\r\n    }\r\n}\r\n"
  },
  {
    "path": "agentweb-core/src/main/java/com/just/agentweb/DefaultWebLifeCycleImpl.java",
    "content": "/*\n * Copyright (C)  Justson(https://github.com/Justson/AgentWeb)\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.just.agentweb;\n\nimport android.os.Build;\nimport android.webkit.WebView;\n\n/**\n * @author cenxiaozhong\n * @date 2017/6/3\n * @since 2.0.0\n */\npublic class DefaultWebLifeCycleImpl implements WebLifeCycle {\n    private WebView mWebView;\n    DefaultWebLifeCycleImpl(WebView webView) {\n        this.mWebView = webView;\n    }\n\n    @Override\n    public void onResume() {\n        if (this.mWebView != null) {\n            if (Build.VERSION.SDK_INT >= 11){\n                this.mWebView.onResume();\n            }\n            this.mWebView.resumeTimers();\n        }\n    }\n\n    @Override\n    public void onPause() {\n        if (this.mWebView != null) {\n            if (Build.VERSION.SDK_INT >= 11){\n                this.mWebView.onPause();\n            }\n            this.mWebView.pauseTimers();\n        }\n    }\n\n    @Override\n    public void onDestroy() {\n        if(this.mWebView!=null){\n            this.mWebView.resumeTimers();\n        }\n        AgentWebUtils.clearWebView(this.mWebView);\n    }\n}\n"
  },
  {
    "path": "agentweb-core/src/main/java/com/just/agentweb/EventHandlerImpl.java",
    "content": "/*\r\n * Copyright (C)  Justson(https://github.com/Justson/AgentWeb)\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n *      http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n\r\npackage com.just.agentweb;\r\n\r\nimport android.view.KeyEvent;\r\nimport android.webkit.WebView;\r\n\r\n/**\r\n * IEventHandler 对事件的处理，主要是针对\r\n * 视屏状态进行了处理 ， 如果当前状态为 视频状态\r\n * 则先退出视频。\r\n *\r\n * @author cenxiaozhong\r\n * @date 2017/6/3\r\n * @since 2.0.0\r\n */\r\npublic class EventHandlerImpl implements IEventHandler {\r\n\tprivate WebView mWebView;\r\n\tprivate EventInterceptor mEventInterceptor;\r\n\r\n\tpublic static final EventHandlerImpl getInstantce(WebView view, EventInterceptor eventInterceptor) {\r\n\t\treturn new EventHandlerImpl(view, eventInterceptor);\r\n\t}\r\n\r\n\tpublic EventHandlerImpl(WebView webView, EventInterceptor eventInterceptor) {\r\n\t\tthis.mWebView = webView;\r\n\t\tthis.mEventInterceptor = eventInterceptor;\r\n\t}\r\n\r\n\t@Override\r\n\tpublic boolean onKeyDown(int keyCode, KeyEvent event) {\r\n\t\tif (keyCode == KeyEvent.KEYCODE_BACK) {\r\n\t\t\treturn back();\r\n\t\t}\r\n\t\treturn false;\r\n\t}\r\n\r\n\t@Override\r\n\tpublic boolean back() {\r\n\t\tif (this.mEventInterceptor != null && this.mEventInterceptor.event()) {\r\n\t\t\treturn true;\r\n\t\t}\r\n\t\tif (mWebView != null && mWebView.canGoBack()) {\r\n\t\t\tmWebView.goBack();\r\n\t\t\treturn true;\r\n\t\t}\r\n\t\treturn false;\r\n\t}\r\n\r\n}\r\n"
  },
  {
    "path": "agentweb-core/src/main/java/com/just/agentweb/EventInterceptor.java",
    "content": "/*\n * Copyright (C)  Justson(https://github.com/Justson/AgentWeb)\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.just.agentweb;\n\n/**\n * @author cenxiaozhong\n * @date 2017/6/3\n * @since 1.0.0\n */\npublic interface EventInterceptor {\n    boolean event();\n}\n"
  },
  {
    "path": "agentweb-core/src/main/java/com/just/agentweb/HookManager.java",
    "content": "/*\r\n * Copyright (C)  Justson(https://github.com/Justson/AgentWeb)\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n *      http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n\r\npackage com.just.agentweb;\r\n\r\n\r\n/**\r\n * @author cenxiaozhong\r\n * @since 1.0.0\r\n */\r\npublic class HookManager {\r\n\r\n    public static AgentWeb hookAgentWeb(AgentWeb agentWeb, AgentWeb.AgentBuilder agentBuilder) {\r\n        return agentWeb;\r\n    }\r\n\r\n    public static boolean permissionHook(String url,String[]permissions){\r\n        return true;\r\n    }\r\n}\r\n"
  },
  {
    "path": "agentweb-core/src/main/java/com/just/agentweb/HttpHeaders.java",
    "content": "/*\n * Copyright (C)  Justson(https://github.com/Justson/AgentWeb)\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.just.agentweb;\n\nimport android.net.Uri;\nimport androidx.collection.ArrayMap;\nimport android.text.TextUtils;\n\nimport java.util.Map;\n\n\n/**\n * @author cenxiaozhong\n * @date 2017/7/5\n * @since 2.0.0\n */\npublic class HttpHeaders {\n    public static HttpHeaders create() {\n        return new HttpHeaders();\n    }\n\n    private final Map<String, Map<String, String>> mHeaders;\n\n    HttpHeaders() {\n        mHeaders = new ArrayMap<String, Map<String, String>>();\n    }\n\n    public Map<String, String> getHeaders(String url) {\n        String subUrl = subBaseUrl(url);\n        if (mHeaders.get(subUrl) == null) {\n            Map<String, String> headers = new ArrayMap<>();\n            mHeaders.put(subUrl, headers);\n            return headers;\n        }\n        return mHeaders.get(subUrl);\n    }\n\n    public void additionalHttpHeader(String url, String k, String v) {\n        if (null == url) {\n            return;\n        }\n        url = subBaseUrl(url);\n        Map<String, Map<String, String>> mHeaders = getHeaders();\n        Map<String, String> headersMap = mHeaders.get(subBaseUrl(url));\n        if (null == headersMap) {\n            headersMap = new ArrayMap<>();\n        }\n        headersMap.put(k, v);\n        mHeaders.put(url, headersMap);\n    }\n\n\n    public void additionalHttpHeaders(String url, Map<String, String> headers) {\n        if (null == url) {\n            return;\n        }\n        String subUrl = subBaseUrl(url);\n        Map<String, Map<String, String>> mHeaders = getHeaders();\n        Map<String, String> headersMap = headers;\n        if (null == headersMap) {\n            headersMap = new ArrayMap<>();\n        }\n        mHeaders.put(subUrl, headersMap);\n    }\n\n    public void removeHttpHeader(String url, String k) {\n        if (null == url) {\n            return;\n        }\n        String subUrl = subBaseUrl(url);\n        Map<String, Map<String, String>> mHeaders = getHeaders();\n        Map<String, String> headersMap = mHeaders.get(subUrl);\n        if (null != headersMap) {\n            headersMap.remove(k);\n        }\n    }\n\n    public boolean isEmptyHeaders(String url) {\n        url = subBaseUrl(url);\n        Map<String, String> heads = getHeaders(url);\n        return heads == null || heads.isEmpty();\n    }\n\n    public Map<String, Map<String, String>> getHeaders() {\n        return this.mHeaders;\n    }\n\n    private String subBaseUrl(String originUrl) {\n        if (TextUtils.isEmpty(originUrl)) {\n            return originUrl;\n        }\n        Uri originUri = null;\n        try {\n            originUri = Uri.parse(originUrl);\n        } catch (Throwable throwable) {\n            throwable.printStackTrace();\n            return \"\";\n        }\n        if (TextUtils.isEmpty(originUri.getScheme()) || TextUtils.isEmpty(originUri.getAuthority())) {\n            return \"\";\n        }\n        return originUri.getScheme() + \"://\" + originUri.getAuthority();\n    }\n\n    @Override\n    public String toString() {\n        return \"HttpHeaders{\" +\n                \"mHeaders=\" + mHeaders +\n                '}';\n    }\n}\n"
  },
  {
    "path": "agentweb-core/src/main/java/com/just/agentweb/IAgentWebSettings.java",
    "content": "/*\r\n * Copyright (C)  Justson(https://github.com/Justson/AgentWeb)\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n *      http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n\r\npackage com.just.agentweb;\r\n\r\nimport android.webkit.WebView;\r\n\r\n/**\r\n * @author cenxiaozhong\r\n * @since 1.0.0\r\n */\r\n\r\npublic interface IAgentWebSettings<T extends android.webkit.WebSettings> {\r\n\r\n    IAgentWebSettings toSetting(WebView webView);\r\n\r\n    T getWebSettings();\r\n\r\n}\r\n"
  },
  {
    "path": "agentweb-core/src/main/java/com/just/agentweb/IEventHandler.java",
    "content": "/*\r\n * Copyright (C)  Justson(https://github.com/Justson/AgentWeb)\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n *      http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n\r\npackage com.just.agentweb;\r\n\r\nimport android.view.KeyEvent;\r\n\r\n/**\r\n * @author cenxiaozhong\r\n * @since 1.0.0\r\n */\r\npublic interface IEventHandler {\r\n\r\n    boolean onKeyDown(int keyCode, KeyEvent event);\r\n\r\n    boolean back();\r\n}\r\n"
  },
  {
    "path": "agentweb-core/src/main/java/com/just/agentweb/IUrlLoader.java",
    "content": "/*\n * Copyright (C)  Justson(https://github.com/Justson/AgentWeb)\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.just.agentweb;\n\nimport java.util.Map;\n\n/**\n * @author cenxiaozhong\n * @date 2017/6/3\n * @update 4.0.0\n * @since 2.0.0\n */\npublic interface IUrlLoader {\n\n\n    void loadUrl(String url);\n\n    void loadUrl(String url, Map<String, String> headers);\n\n    void reload();\n\n    void loadData(String data, String mimeType, String encoding);\n\n    void stopLoading();\n\n    void loadDataWithBaseURL(String baseUrl, String data,\n                             String mimeType, String encoding, String historyUrl);\n\n    void postUrl(String url, byte[] params);\n\n    HttpHeaders getHttpHeaders();\n}\n"
  },
  {
    "path": "agentweb-core/src/main/java/com/just/agentweb/IVideo.java",
    "content": "/*\n * Copyright (C)  Justson(https://github.com/Justson/AgentWeb)\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.just.agentweb;\n\nimport android.view.View;\nimport android.webkit.WebChromeClient;\n\n\n/**\n * @author cenxiaozhong\n * @date 2017/6/10\n * @since 2.0.0\n */\npublic interface IVideo {\n\n\n    void onShowCustomView(View view, WebChromeClient.CustomViewCallback callback);\n\n\n    void onHideCustomView();\n\n\n    boolean isVideoState();\n\n}\n"
  },
  {
    "path": "agentweb-core/src/main/java/com/just/agentweb/IWebIndicator.java",
    "content": "/*\r\n * Copyright (C)  Justson(https://github.com/Justson/AgentWeb)\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n *      http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n\r\npackage com.just.agentweb;\r\n\r\n/**\r\n * @author cenxiaozhong\r\n * @since 1.0.0\r\n */\r\n\r\n\r\npublic interface IWebIndicator<T extends BaseIndicatorSpec> {\r\n\r\n\r\n    T offer();\r\n}\r\n"
  },
  {
    "path": "agentweb-core/src/main/java/com/just/agentweb/IWebLayout.java",
    "content": "/*\n * Copyright (C)  Justson(https://github.com/Justson/AgentWeb)\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.just.agentweb;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport android.view.ViewGroup;\nimport android.webkit.WebView;\n\n/**\n * Created by cenxiaozhong on 2017/7/1.\n */\n/**\n * @author cenxiaozhong\n * @date 2017/7/1\n * @update 4.0.0\n * @since 1.0.0\n */\npublic interface IWebLayout<T extends WebView,V extends ViewGroup> {\n\n    /**\n     *\n     * @return WebView 的父控件\n     */\n    @NonNull V getLayout();\n\n    /**\n     *\n     * @return 返回 WebView  或 WebView 的子View ，返回null AgentWeb 内部会创建适当 WebView\n     */\n    @Nullable T getWebView();\n}\n"
  },
  {
    "path": "agentweb-core/src/main/java/com/just/agentweb/IndicatorController.java",
    "content": "/*\r\n * Copyright (C)  Justson(https://github.com/Justson/AgentWeb)\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n *      http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n\r\npackage com.just.agentweb;\r\n\r\nimport android.webkit.WebView;\r\n/**\r\n * @author cenxiaozhong\r\n * @update 4.0.0\r\n * @since 1.0.0\r\n */\r\n\r\npublic interface IndicatorController {\r\n\r\n    void progress(WebView v, int newProgress);\r\n\r\n    BaseIndicatorSpec offerIndicator();\r\n\r\n    void showIndicator();\r\n\r\n    void setProgress(int newProgress);\r\n\r\n    void finish();\r\n}\r\n"
  },
  {
    "path": "agentweb-core/src/main/java/com/just/agentweb/IndicatorHandler.java",
    "content": "/*\r\n * Copyright (C)  Justson(https://github.com/Justson/AgentWeb)\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n *      http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n\r\npackage com.just.agentweb;\r\n\r\nimport android.webkit.WebView;\r\n\r\n\r\n/**\r\n * @author cenxiaozhong\r\n * @since 1.0.0\r\n */\r\npublic class IndicatorHandler implements IndicatorController {\r\n\tprivate BaseIndicatorSpec mBaseIndicatorSpec;\r\n\r\n\t@Override\r\n\tpublic void progress(WebView v, int newProgress) {\r\n\r\n\t\tif (newProgress == 0) {\r\n\t\t\treset();\r\n\t\t} else if (newProgress > 0 && newProgress <= 10) {\r\n\t\t\tshowIndicator();\r\n\t\t} else if (newProgress > 10 && newProgress < 95) {\r\n\t\t\tsetProgress(newProgress);\r\n\t\t} else {\r\n\t\t\tsetProgress(newProgress);\r\n\t\t\tfinish();\r\n\t\t}\r\n\r\n\t}\r\n\r\n\t@Override\r\n\tpublic BaseIndicatorSpec offerIndicator() {\r\n\t\treturn this.mBaseIndicatorSpec;\r\n\t}\r\n\r\n\tpublic void reset() {\r\n\r\n\t\tif (mBaseIndicatorSpec != null) {\r\n\t\t\tmBaseIndicatorSpec.reset();\r\n\t\t}\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void finish() {\r\n\t\tif (mBaseIndicatorSpec != null) {\r\n\t\t\tmBaseIndicatorSpec.hide();\r\n\t\t}\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void setProgress(int n) {\r\n\t\tif (mBaseIndicatorSpec != null) {\r\n\t\t\tmBaseIndicatorSpec.setProgress(n);\r\n\t\t}\r\n\t}\r\n\r\n\t@Override\r\n\tpublic void showIndicator() {\r\n\r\n\t\tif (mBaseIndicatorSpec != null) {\r\n\t\t\tmBaseIndicatorSpec.show();\r\n\t\t}\r\n\t}\r\n\r\n\tstatic IndicatorHandler getInstance() {\r\n\t\treturn new IndicatorHandler();\r\n\t}\r\n\r\n\r\n\tIndicatorHandler inJectIndicator(BaseIndicatorSpec baseIndicatorSpec) {\r\n\t\tthis.mBaseIndicatorSpec = baseIndicatorSpec;\r\n\t\treturn this;\r\n\t}\r\n}\r\n"
  },
  {
    "path": "agentweb-core/src/main/java/com/just/agentweb/JsAccessEntrace.java",
    "content": "/*\n * Copyright (C)  Justson(https://github.com/Justson/AgentWeb)\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.just.agentweb;\n\nimport android.webkit.ValueCallback;\n\n/**\n * @author cenxiaozhong\n * @date 2017/5/14\n * @since 1.0.0\n */\npublic interface JsAccessEntrace extends QuickCallJs {\n\n\n    void callJs(String js, ValueCallback<String> callback);\n\n    void callJs(String js);\n\n\n}\n"
  },
  {
    "path": "agentweb-core/src/main/java/com/just/agentweb/JsAccessEntraceImpl.java",
    "content": "/*\n * Copyright (C)  Justson(https://github.com/Justson/AgentWeb)\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.just.agentweb;\n\nimport android.os.Handler;\nimport android.os.Looper;\nimport android.webkit.ValueCallback;\nimport android.webkit.WebView;\n\n\n/**\n * @author cenxiaozhong\n * @date 2017/6/3\n * @since 1.0.0\n */\npublic class JsAccessEntraceImpl extends BaseJsAccessEntrace {\n\n    private WebView mWebView;\n    private Handler mHandler = new Handler(Looper.getMainLooper());\n\n    public static JsAccessEntraceImpl getInstance(WebView webView) {\n        return new JsAccessEntraceImpl(webView);\n    }\n\n    private JsAccessEntraceImpl(WebView webView) {\n        super(webView);\n        this.mWebView = webView;\n    }\n\n    private void safeCallJs(final String s, final ValueCallback valueCallback) {\n        mHandler.post(new Runnable() {\n            @Override\n            public void run() {\n                callJs(s, valueCallback);\n            }\n        });\n    }\n\n    @Override\n    public void callJs(String params, final ValueCallback<String> callback) {\n        if (Thread.currentThread() != Looper.getMainLooper().getThread()) {\n            safeCallJs(params, callback);\n            return;\n        }\n        super.callJs(params,callback);\n    }\n\n\n}\n"
  },
  {
    "path": "agentweb-core/src/main/java/com/just/agentweb/JsBaseInterfaceHolder.java",
    "content": "/*\n * Copyright (C)  Justson(https://github.com/Justson/AgentWeb)\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.just.agentweb;\n\nimport android.os.Build;\nimport android.webkit.JavascriptInterface;\n\nimport java.lang.annotation.Annotation;\nimport java.lang.reflect.Method;\n\n/**\n * @author cenxiaozhong\n * @date 2017/5/13\n * @since 1.0.0\n */\npublic abstract class JsBaseInterfaceHolder implements JsInterfaceHolder {\n\n    private AgentWeb.SecurityType mSecurityType;\n    private WebCreator mWebCreator;\n\n    protected JsBaseInterfaceHolder(WebCreator webCreator, AgentWeb.SecurityType securityType) {\n        this.mSecurityType = securityType;\n        this.mWebCreator = webCreator;\n    }\n\n    @Override\n    public boolean checkObject(Object v) {\n        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) {\n            return true;\n        }\n        if (mWebCreator.getWebViewType() == AgentWebConfig.WEBVIEW_AGENTWEB_SAFE_TYPE) {\n            return true;\n        }\n        boolean tag = false;\n        Class clazz = v.getClass();\n        Method[] mMethods = clazz.getMethods();\n        for (Method mMethod : mMethods) {\n            Annotation[] mAnnotations = mMethod.getAnnotations();\n            for (Annotation mAnnotation : mAnnotations) {\n                if (mAnnotation instanceof JavascriptInterface) {\n                    tag = true;\n                    break;\n                }\n            }\n            if (tag) {\n                break;\n            }\n        }\n        return tag;\n    }\n\n    protected boolean checkSecurity() {\n        return mSecurityType != AgentWeb.SecurityType.STRICT_CHECK\n                ? true : mWebCreator.getWebViewType() == AgentWebConfig.WEBVIEW_AGENTWEB_SAFE_TYPE\n                ? true : Build.VERSION.SDK_INT > Build.VERSION_CODES.JELLY_BEAN_MR1;\n    }\n\n}\n"
  },
  {
    "path": "agentweb-core/src/main/java/com/just/agentweb/JsCallJava.java",
    "content": "/*\n * Copyright (C)  Justson(https://github.com/Justson/AgentWeb)\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.just.agentweb;\n\nimport android.text.TextUtils;\nimport android.util.Log;\nimport android.webkit.WebView;\n\nimport org.json.JSONArray;\nimport org.json.JSONException;\nimport org.json.JSONObject;\n\nimport java.lang.reflect.Method;\nimport java.util.HashMap;\n\npublic class JsCallJava {\n    private final static String TAG = \"JsCallJava\";\n    private final static String RETURN_RESULT_FORMAT = \"{\\\"CODE\\\": %d, \\\"result\\\": %s}\";\n    private static final String MSG_PROMPT_HEADER = \"AgentWeb:\";\n    private static final String KEY_OBJ = \"obj\";\n    private static final String KEY_METHOD = \"method\";\n    private static final String KEY_TYPES = \"types\";\n    private static final String KEY_ARGS = \"args\";\n    private static final String[] IGNORE_UNSAFE_METHODS = {\"getClass\", \"hashCode\", \"notify\", \"notifyAll\", \"equals\", \"toString\", \"wait\"};\n    private HashMap<String, Method> mMethodsMap;\n    private Object mInterfaceObj;\n    private String mInterfacedName;\n    private String mPreloadInterfaceJs;\n\n    public JsCallJava(Object interfaceObj, String interfaceName) {\n        try {\n            if (TextUtils.isEmpty(interfaceName)) {\n                throw new Exception(\"injected name can not be null\");\n            }\n            mInterfaceObj = interfaceObj;\n            mInterfacedName = interfaceName;\n            mMethodsMap = new HashMap<String, Method>();\n            // getMethods会获得所有继承与非继承的方法\n            Method[] methods = mInterfaceObj.getClass().getMethods();\n            // 拼接的js脚本可参照备份文件：./library/doc/injected.js\n            StringBuilder sb = new StringBuilder(\"javascript:(function(b){console.log(\\\"\");\n            sb.append(mInterfacedName);\n            sb.append(\" init begin\\\");var a={queue:[],callback:function(){var d=Array.prototype.slice.call(arguments,0);var c=d.shift();var e=d.shift();this.queue[c].apply(this,d);if(!e){delete this.queue[c]}}};\");\n            for (Method method : methods) {\n                Log.i(\"Info\",\"method:\"+method);\n                String sign;\n                if ((sign = genJavaMethodSign(method)) == null) {\n                    continue;\n                }\n                mMethodsMap.put(sign, method);\n                sb.append(String.format(\"a.%s=\", method.getName()));\n            }\n            sb.append(\"function(){var f=Array.prototype.slice.call(arguments,0);if(f.length<1){throw\\\"\");\n            sb.append(mInterfacedName);\n            sb.append(\" call result, message:miss method name\\\"}var e=[];for(var h=1;h<f.length;h++){var c=f[h];var j=typeof c;e[e.length]=j;if(j==\\\"function\\\"){var d=a.queue.length;a.queue[d]=c;f[h]=d}}var k = new Date().getTime();var l = f.shift();var m=prompt('\");\n            sb.append(MSG_PROMPT_HEADER);\n            sb.append(\"'+JSON.stringify(\");\n            sb.append(promptMsgFormat(\"'\" + mInterfacedName + \"'\", \"l\", \"e\", \"f\"));\n            sb.append(\"));console.log(\\\"invoke \\\"+l+\\\", time: \\\"+(new Date().getTime()-k));var g=JSON.parse(m);if(g.CODE!=200){throw\\\"\");\n            sb.append(mInterfacedName);\n            sb.append(\" call result, CODE:\\\"+g.CODE+\\\", message:\\\"+g.result}return g.result};Object.getOwnPropertyNames(a).forEach(function(d){var c=a[d];if(typeof c===\\\"function\\\"&&d!==\\\"callback\\\"){a[d]=function(){return c.apply(a,[d].concat(Array.prototype.slice.call(arguments,0)))}}});b.\");\n            sb.append(mInterfacedName);\n            sb.append(\"=a;console.log(\\\"\");\n            sb.append(mInterfacedName);\n            sb.append(\" init end\\\")})(window)\");\n            mPreloadInterfaceJs = sb.toString();\n            sb.setLength(0);\n        } catch (Exception e) {\n            if (LogUtils.isDebug()) {\n                Log.e(TAG, \"init js result:\" + e.getMessage());\n            }\n        }\n    }\n\n    private String genJavaMethodSign(Method method) {\n        String sign = method.getName();\n        Class[] argsTypes = method.getParameterTypes();\n        for (String ignoreMethod : IGNORE_UNSAFE_METHODS) {\n            if (ignoreMethod.equals(sign)) {\n                if (LogUtils.isDebug()) {\n                    Log.w(TAG, \"method(\" + sign + \") is unsafe, will be pass\");\n                }\n                return null;\n            }\n        }\n        int len = argsTypes.length;\n        for (int k = 0; k < len; k++) {\n            Class cls = argsTypes[k];\n            if (cls == String.class) {\n                sign += \"_S\";\n            } else if (cls == int.class ||\n                    cls == long.class ||\n                    cls == float.class ||\n                    cls == double.class) {\n                sign += \"_N\";\n            } else if (cls == boolean.class) {\n                sign += \"_B\";\n            } else if (cls == JSONObject.class) {\n                sign += \"_O\";\n            } else if (cls == JsCallback.class) {\n                sign += \"_F\";\n            } else {\n                sign += \"_P\";\n            }\n        }\n        return sign;\n    }\n\n    public String getPreloadInterfaceJs() {\n        return mPreloadInterfaceJs;\n    }\n\n    public String call(WebView webView, JSONObject jsonObject) {\n        long time = 0;\n        if (LogUtils.isDebug()) {\n            time = android.os.SystemClock.uptimeMillis();\n        }\n        if (jsonObject != null) {\n            try {\n                String methodName = jsonObject.getString(KEY_METHOD);\n                JSONArray argsTypes = jsonObject.getJSONArray(KEY_TYPES);\n                JSONArray argsVals = jsonObject.getJSONArray(KEY_ARGS);\n                String sign = methodName;\n                int len = argsTypes.length();\n                Object[] values = new Object[len];\n                int numIndex = 0;\n                String currType;\n\n                for (int k = 0; k < len; k++) {\n                    currType = argsTypes.optString(k);\n                    if (\"string\".equals(currType)) {\n                        sign += \"_S\";\n                        values[k] = argsVals.isNull(k) ? null : argsVals.getString(k);\n                    } else if (\"number\".equals(currType)) {\n                        sign += \"_N\";\n                        numIndex = numIndex * 10 + k + 1;\n                    } else if (\"boolean\".equals(currType)) {\n                        sign += \"_B\";\n                        values[k] = argsVals.getBoolean(k);\n                    } else if (\"object\".equals(currType)) {\n                        sign += \"_O\";\n                        values[k] = argsVals.isNull(k) ? null : argsVals.getJSONObject(k);\n                    } else if (\"function\".equals(currType)) {\n                        sign += \"_F\";\n                        values[k] = new JsCallback(webView, mInterfacedName, argsVals.getInt(k));\n                    } else {\n                        sign += \"_P\";\n                    }\n                }\n\n                Method currMethod = mMethodsMap.get(sign);\n\n                // 方法匹配失败\n                if (currMethod == null) {\n                    return getReturn(jsonObject, 500, \"not found method(\" + sign + \") with valid parameters\", time);\n                }\n                // 数字类型细分匹配\n                if (numIndex > 0) {\n                    Class[] methodTypes = currMethod.getParameterTypes();\n                    int currIndex;\n                    Class currCls;\n                    while (numIndex > 0) {\n                        currIndex = numIndex - numIndex / 10 * 10 - 1;\n                        currCls = methodTypes[currIndex];\n                        if (currCls == int.class) {\n                            values[currIndex] = argsVals.getInt(currIndex);\n                        } else if (currCls == long.class) {\n                            //WARN: argsJson.getLong(k + defValue) will return a bigger incorrect number\n                            values[currIndex] = Long.parseLong(argsVals.getString(currIndex));\n                        } else {\n                            values[currIndex] = argsVals.getDouble(currIndex);\n                        }\n                        numIndex /= 10;\n                    }\n                }\n\n                return getReturn(jsonObject, 200, currMethod.invoke(mInterfaceObj, values), time);\n            } catch (Exception e) {\n                LogUtils.safeCheckCrash(TAG, \"call\", e);\n                //优先返回详细的错误信息\n                if (e.getCause() != null) {\n                    return getReturn(jsonObject, 500, \"method execute result:\" + e.getCause().getMessage(), time);\n                }\n                return getReturn(jsonObject, 500, \"method execute result:\" + e.getMessage(), time);\n            }\n        } else {\n            return getReturn(jsonObject, 500, \"call data empty\", time);\n        }\n    }\n\n    private String getReturn(JSONObject reqJson, int stateCode, Object result, long time) {\n        String insertRes;\n        if (result == null) {\n            insertRes = \"null\";\n        } else if (result instanceof String) {\n            result = ((String) result).replace(\"\\\"\", \"\\\\\\\"\");\n            insertRes = \"\\\"\".concat(String.valueOf(result)).concat(\"\\\"\");\n        } else { // 其他类型直接转换\n            insertRes = String.valueOf(result);\n\n            // 兼容：如果在解决WebView注入安全漏洞时，js注入采用的是XXX:function(){return prompt(...)}的形式，函数返回类型包括：void、int、boolean、String；\n            // 在返回给网页（onJsPrompt方法中jsPromptResult.confirm）的时候强制返回的是String类型，所以在此将result的值加双引号兼容一下；\n            // insertRes = \"\\\"\".concat(String.valueOf(result)).concat(\"\\\"\");\n        }\n        String resStr = String.format(RETURN_RESULT_FORMAT, stateCode, insertRes);\n        if (LogUtils.isDebug()) {\n            Log.d(TAG, \"call time: \" + (android.os.SystemClock.uptimeMillis() - time) + \", request: \" + reqJson + \", result:\" + resStr);\n        }\n        return resStr;\n    }\n\n    private static String promptMsgFormat(String object, String method, String types, String args) {\n        StringBuilder sb = new StringBuilder();\n        sb.append(\"{\");\n        sb.append(KEY_OBJ).append(\":\").append(object).append(\",\");\n        sb.append(KEY_METHOD).append(\":\").append(method).append(\",\");\n        sb.append(KEY_TYPES).append(\":\").append(types).append(\",\");\n        sb.append(KEY_ARGS).append(\":\").append(args);\n        sb.append(\"}\");\n        return sb.toString();\n    }\n\n    /**\n     * 是否是“Java接口类中方法调用”的内部消息；\n     *\n     * @param message\n     * @return\n     */\n    static boolean isSafeWebViewCallMsg(String message) {\n        return message.startsWith(MSG_PROMPT_HEADER);\n    }\n\n    static JSONObject getMsgJSONObject(String message) {\n        message = message.substring(MSG_PROMPT_HEADER.length());\n        JSONObject jsonObject;\n        try {\n            jsonObject = new JSONObject(message);\n        } catch (JSONException e) {\n            e.printStackTrace();\n            jsonObject = new JSONObject();\n        }\n        return jsonObject;\n    }\n\n    static String getInterfacedName(JSONObject jsonObject) {\n        return jsonObject.optString(KEY_OBJ);\n    }\n}"
  },
  {
    "path": "agentweb-core/src/main/java/com/just/agentweb/JsCallback.java",
    "content": "\n\n/*\n * Copyright (C)  Justson(https://github.com/Justson/AgentWeb)\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.just.agentweb;\n\nimport android.util.Log;\nimport android.webkit.WebView;\n\nimport org.json.JSONArray;\nimport org.json.JSONException;\nimport org.json.JSONObject;\n\nimport java.lang.ref.WeakReference;\npublic class JsCallback {\n    private static final String CALLBACK_JS_FORMAT = \"javascript:%s.callback(%d, %d %s);\";\n    private int mIndex;\n    private boolean mCouldGoOn;\n    private WeakReference<WebView> mWebViewRef;\n    private int mIsPermanent;\n    private String mInjectedName;\n\n    public JsCallback(WebView view, String injectedName, int index) {\n        mCouldGoOn = true;\n        mWebViewRef = new WeakReference<WebView>(view);\n        mInjectedName = injectedName;\n        mIndex = index;\n    }\n\n    /**\n     * 向网页执行js回调；\n     * @param args\n     * @throws JsCallbackException\n     */\n    public void apply (Object... args) throws JsCallbackException {\n        if (mWebViewRef.get() == null) {\n            throw new JsCallbackException(\"the WebView related to the JsCallback has been recycled\");\n        }\n        if (!mCouldGoOn) {\n            throw new JsCallbackException(\"the JsCallback isn't permanent,cannot be called more than once\");\n        }\n        StringBuilder sb = new StringBuilder();\n        for (Object arg : args){\n            sb.append(\",\");\n            boolean isStrArg = arg instanceof String;\n            // 有的接口将Json对象转换成了String返回，这里不能加双引号，否则网页会认为是String而不是JavaScript对象；\n            boolean isObjArg = isJavaScriptObject(arg);\n            if (isStrArg && !isObjArg) {\n                sb.append(\"\\\"\");\n            }\n            sb.append(String.valueOf(arg));\n            if (isStrArg && !isObjArg) {\n                sb.append(\"\\\"\");\n            }\n        }\n        String execJs = String.format(CALLBACK_JS_FORMAT, mInjectedName, mIndex, mIsPermanent, sb.toString());\n        if (LogUtils.isDebug()) {\n            Log.d(\"JsCallBack\", execJs);\n        }\n        mWebViewRef.get().loadUrl(execJs);\n        mCouldGoOn = mIsPermanent > 0;\n    }\n\n    /**\n     * 是否是JSON(JavaScript Object Notation)对象；\n     * @param obj\n     * @return\n     */\n    private boolean isJavaScriptObject(Object obj) {\n        if (obj instanceof JSONObject || obj instanceof JSONArray) {\n            return true;\n        } else {\n            String json = obj.toString();\n            try {\n                new JSONObject(json);\n            } catch (JSONException e) {\n                try {\n                    new JSONArray(json);\n                } catch (JSONException e1) {\n                    return false;\n                }\n            }\n            return true;\n        }\n    }\n\n    /**\n     * 一般传入到Java方法的js function是一次性使用的，即在Java层jsCallback.apply(...)之后不能再发起回调了；\n     * 如果需要传入的function能够在当前页面生命周期内多次使用，请在第一次apply前setPermanent(true)；\n     * @param value\n     */\n    public void setPermanent (boolean value) {\n        mIsPermanent = value ? 1 : 0;\n    }\n\n    public static class JsCallbackException extends Exception {\n        public JsCallbackException (String msg) {\n            super(msg);\n        }\n    }\n}\n"
  },
  {
    "path": "agentweb-core/src/main/java/com/just/agentweb/JsInterfaceHolder.java",
    "content": "/*\n * Copyright (C)  Justson(https://github.com/Justson/AgentWeb)\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.just.agentweb;\n\nimport java.util.Map;\n\n/**\n * @author cenxiaozhong\n * @date 2017/5/13\n * @since 1.0.0\n */\npublic interface JsInterfaceHolder {\n\n    JsInterfaceHolder addJavaObjects(Map<String, Object> maps);\n\n    JsInterfaceHolder addJavaObject(String k, Object v);\n\n    boolean checkObject(Object v);\n\n}\n"
  },
  {
    "path": "agentweb-core/src/main/java/com/just/agentweb/JsInterfaceHolderImpl.java",
    "content": "/*\n * Copyright (C)  Justson(https://github.com/Justson/AgentWeb)\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.just.agentweb;\n\nimport android.webkit.WebView;\n\nimport java.util.Map;\nimport java.util.Set;\n\n/**\n * @author cenxiaozhong\n * @date 2017/5/13\n * @since 1.0.0\n */\npublic class JsInterfaceHolderImpl extends JsBaseInterfaceHolder {\n\n    private static final String TAG = JsInterfaceHolderImpl.class.getSimpleName();\n    private WebCreator mWebCreator;\n    private AgentWeb.SecurityType mSecurityType;\n    private WebView mWebView;\n\n    static JsInterfaceHolderImpl getJsInterfaceHolder(WebCreator webCreator, AgentWeb.SecurityType securityType) {\n        return new JsInterfaceHolderImpl(webCreator, securityType);\n    }\n\n    JsInterfaceHolderImpl(WebCreator webCreator, AgentWeb.SecurityType securityType) {\n        super(webCreator, securityType);\n        this.mWebCreator = webCreator;\n        this.mWebView = mWebCreator.getWebView();\n        this.mSecurityType = securityType;\n    }\n\n    @Override\n    public JsInterfaceHolder addJavaObjects(Map<String, Object> maps) {\n        if (!checkSecurity()) {\n            LogUtils.e(TAG, \"The injected object is not safe, give up injection\");\n            return this;\n        }\n        Set<Map.Entry<String, Object>> sets = maps.entrySet();\n        for (Map.Entry<String, Object> mEntry : sets) {\n            Object v = mEntry.getValue();\n            boolean t = checkObject(v);\n            if (!t) {\n                throw new JsInterfaceObjectException(\"This object has not offer method javascript to call ,please check addJavascriptInterface annotation was be added\");\n            } else {\n                addJavaObjectDirect(mEntry.getKey(), v);\n            }\n        }\n        return this;\n    }\n\n    @Override\n    public JsInterfaceHolder addJavaObject(String k, Object v) {\n        if (!checkSecurity()) {\n            return this;\n        }\n        boolean t = checkObject(v);\n        if (!t) {\n            throw new JsInterfaceObjectException(\"this object has not offer method javascript to call , please check addJavascriptInterface annotation was be added\");\n        } else {\n            addJavaObjectDirect(k, v);\n        }\n        return this;\n    }\n\n    private JsInterfaceHolder addJavaObjectDirect(String k, Object v) {\n        LogUtils.i(TAG, \"k:\" + k + \"  v:\" + v);\n        this.mWebView.addJavascriptInterface(v, k);\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "agentweb-core/src/main/java/com/just/agentweb/JsInterfaceObjectException.java",
    "content": "/*\n * Copyright (C)  Justson(https://github.com/Justson/AgentWeb)\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.just.agentweb;\n\n\n/**\n * @author cenxiaozhong\n * @date 2017/5/13\n * @since 1.0.0\n */\npublic class JsInterfaceObjectException extends RuntimeException {\n    JsInterfaceObjectException(String msg){\n        super(msg);\n    }\n}\n"
  },
  {
    "path": "agentweb-core/src/main/java/com/just/agentweb/LayoutParamsOffer.java",
    "content": "/*\n * Copyright (C)  Justson(https://github.com/Justson/AgentWeb)\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.just.agentweb;\n\nimport android.widget.FrameLayout;\n\n/**\n * @author cenxiaozhong\n * @date 2017/5/12\n * @since 1.0.0\n */\npublic interface LayoutParamsOffer<T extends FrameLayout.LayoutParams> {\n\n    T offerLayoutParams();\n\n}\n"
  },
  {
    "path": "agentweb-core/src/main/java/com/just/agentweb/LogUtils.java",
    "content": "/*\n * Copyright (C)  Justson(https://github.com/Justson/AgentWeb)\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.just.agentweb;\n\nimport android.util.Log;\n\n/**\n * @author cenxiaozhong\n * @date 2017/5/28\n * @since 1.0.0\n */\nclass LogUtils {\n\n    private static final String PREFIX = \"agentweb-\";\n\n    static boolean isDebug() {\n        return AgentWebConfig.DEBUG;\n    }\n\n    static void i(String tag, String message) {\n\n        if (isDebug()) {\n            Log.i(PREFIX.concat(tag), message);\n        }\n    }\n\n    static void v(String tag, String message) {\n\n        if (isDebug()) {\n            Log.v(PREFIX.concat(tag), message);\n        }\n\n    }\n\n    static void safeCheckCrash(String tag, String msg, Throwable tr) {\n        if (isDebug()) {\n            throw new RuntimeException(PREFIX.concat(tag) + \" \" + msg, tr);\n        } else {\n            Log.e(PREFIX.concat(tag), msg, tr);\n        }\n    }\n\n    static void e(String tag, String msg, Throwable tr) {\n        Log.e(tag, msg, tr);\n    }\n\n    static void e(String tag, String message) {\n\n        if (isDebug()) {\n            Log.e(PREFIX.concat(tag), message);\n        }\n    }\n}\n"
  },
  {
    "path": "agentweb-core/src/main/java/com/just/agentweb/LollipopFixedWebView.java",
    "content": "package com.just.agentweb;\n\nimport android.annotation.TargetApi;\nimport android.content.Context;\nimport android.content.res.Configuration;\nimport android.os.Build;\nimport android.util.AttributeSet;\nimport android.webkit.WebView;\n\n/**\n * 修复 Android 5.0 & 5.1 打开 WebView 闪退问题：\n * 参阅 https://stackoverflow.com/questions/41025200/android-view-inflateexception-error-inflating-class-android-webkit-webview\n */\n@SuppressWarnings(\"unused\")\npublic class LollipopFixedWebView extends WebView {\n    public LollipopFixedWebView(Context context) {\n        super(getFixedContext(context));\n    }\n\n    public LollipopFixedWebView(Context context, AttributeSet attrs) {\n        super(getFixedContext(context), attrs);\n    }\n\n    public LollipopFixedWebView(Context context, AttributeSet attrs, int defStyleAttr) {\n        super(getFixedContext(context), attrs, defStyleAttr);\n    }\n\n    @TargetApi(Build.VERSION_CODES.LOLLIPOP)\n    public LollipopFixedWebView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {\n        super(getFixedContext(context), attrs, defStyleAttr, defStyleRes);\n    }\n\n    public LollipopFixedWebView(Context context, AttributeSet attrs, int defStyleAttr, boolean privateBrowsing) {\n        super(getFixedContext(context), attrs, defStyleAttr, privateBrowsing);\n    }\n\n    public static Context getFixedContext(Context context) {\n        if (Build.VERSION.SDK_INT >= 21 && Build.VERSION.SDK_INT < 23) {\n            // Avoid crashing on Android 5 and 6 (API level 21 to 23)\n            return context.createConfigurationContext(new Configuration());\n        }\n        return context;\n    }\n\n}\n"
  },
  {
    "path": "agentweb-core/src/main/java/com/just/agentweb/MiddlewareWebChromeBase.java",
    "content": "/*\n * Copyright (C)  Justson(https://github.com/Justson/AgentWeb)\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.just.agentweb;\n\nimport android.webkit.WebChromeClient;\n\n/**\n * @author cenxiaozhong\n * @date 2017/12/16\n * @since 3.0.0\n */\npublic class MiddlewareWebChromeBase extends WebChromeClientDelegate {\n\n    private MiddlewareWebChromeBase mMiddlewareWebChromeBase;\n\n    protected MiddlewareWebChromeBase(WebChromeClient webChromeClient) {\n        super(webChromeClient);\n    }\n\n    protected MiddlewareWebChromeBase() {\n        super(null);\n    }\n\n    @Override\n    final void setDelegate(WebChromeClient delegate) {\n        super.setDelegate(delegate);\n    }\n\n    final MiddlewareWebChromeBase enq(MiddlewareWebChromeBase middlewareWebChromeBase) {\n        setDelegate(middlewareWebChromeBase);\n        this.mMiddlewareWebChromeBase = middlewareWebChromeBase;\n        return this.mMiddlewareWebChromeBase;\n    }\n\n\n    final MiddlewareWebChromeBase next() {\n        return this.mMiddlewareWebChromeBase;\n    }\n\n}\n"
  },
  {
    "path": "agentweb-core/src/main/java/com/just/agentweb/MiddlewareWebClientBase.java",
    "content": "/*\n * Copyright (C)  Justson(https://github.com/Justson/AgentWeb)\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.just.agentweb;\n\nimport android.webkit.WebViewClient;\n\n/**\n * @author cenxiaozhong\n * @date 2017/12/15\n * @since 3.0.0\n */\npublic class MiddlewareWebClientBase extends WebViewClientDelegate {\n    private MiddlewareWebClientBase mMiddleWrareWebClientBase;\n    private static String TAG = MiddlewareWebClientBase.class.getSimpleName();\n\n    MiddlewareWebClientBase(MiddlewareWebClientBase client) {\n        super(client);\n        this.mMiddleWrareWebClientBase = client;\n    }\n\n    protected MiddlewareWebClientBase(WebViewClient client) {\n        super(client);\n    }\n\n    protected MiddlewareWebClientBase() {\n        super(null);\n    }\n\n    final MiddlewareWebClientBase next() {\n        return this.mMiddleWrareWebClientBase;\n    }\n\n    @Override\n    final void setDelegate(WebViewClient delegate) {\n        super.setDelegate(delegate);\n\n    }\n\n    final MiddlewareWebClientBase enq(MiddlewareWebClientBase middleWrareWebClientBase) {\n        setDelegate(middleWrareWebClientBase);\n        this.mMiddleWrareWebClientBase = middleWrareWebClientBase;\n        return middleWrareWebClientBase;\n    }\n\n}\n"
  },
  {
    "path": "agentweb-core/src/main/java/com/just/agentweb/NestedScrollAgentWebView.java",
    "content": "/*\n * Copyright (C)  LeonDevLifeLog(https://github.com/Justson/AgentWeb)\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage com.just.agentweb;\n\nimport android.content.Context;\nimport androidx.core.view.MotionEventCompat;\nimport androidx.core.view.NestedScrollingChild;\nimport androidx.core.view.NestedScrollingChildHelper;\nimport androidx.core.view.ViewCompat;\nimport android.util.AttributeSet;\nimport android.view.MotionEvent;\n\n/**\n * 结合CoordinatorLayout可以与Toolbar联动的webview\n * @author LeonDevLifeLog\n * @since 4.0.0\n */\n\npublic class NestedScrollAgentWebView extends AgentWebView implements NestedScrollingChild {\n\n    private int mLastMotionY;\n    private final int[] mScrollOffset = new int[2];\n    private final int[] mScrollConsumed = new int[2];\n    private int mNestedYOffset;\n    private NestedScrollingChildHelper mChildHelper;\n\n    public NestedScrollAgentWebView(Context context) {\n        super(context);\n        init();\n    }\n\n    public NestedScrollAgentWebView(Context context, AttributeSet attrs) {\n        super(context, attrs);\n        init();\n    }\n\n    private void init() {\n        mChildHelper = new NestedScrollingChildHelper(this);\n        setNestedScrollingEnabled(true);\n    }\n\n    @Override\n    public boolean onTouchEvent(MotionEvent event) {\n        boolean result = false;\n        MotionEvent trackedEvent = MotionEvent.obtain(event);\n        final int action = MotionEventCompat.getActionMasked(event);\n        if (action == MotionEvent.ACTION_DOWN) {\n            mNestedYOffset = 0;\n        }\n        int y = (int) event.getY();\n        event.offsetLocation(0, mNestedYOffset);\n        switch (action) {\n            case MotionEvent.ACTION_DOWN:\n                mLastMotionY = y;\n                startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL);\n                result = super.onTouchEvent(event);\n                break;\n            case MotionEvent.ACTION_MOVE:\n                int deltaY = mLastMotionY - y;\n\n                if (dispatchNestedPreScroll(0, deltaY, mScrollConsumed, mScrollOffset)) {\n                    deltaY -= mScrollConsumed[1];\n                    trackedEvent.offsetLocation(0, mScrollOffset[1]);\n                    mNestedYOffset += mScrollOffset[1];\n                }\n\n                mLastMotionY = y - mScrollOffset[1];\n\n                int oldY = getScrollY();\n                int newScrollY = Math.max(0, oldY + deltaY);\n                int dyConsumed = newScrollY - oldY;\n                int dyUnconsumed = deltaY - dyConsumed;\n\n                if (dispatchNestedScroll(0, dyConsumed, 0, dyUnconsumed, mScrollOffset)) {\n                    mLastMotionY -= mScrollOffset[1];\n                    trackedEvent.offsetLocation(0, mScrollOffset[1]);\n                    mNestedYOffset += mScrollOffset[1];\n                }\n\n                result = super.onTouchEvent(trackedEvent);\n                trackedEvent.recycle();\n                break;\n            case MotionEvent.ACTION_POINTER_DOWN:\n            case MotionEvent.ACTION_UP:\n            case MotionEvent.ACTION_CANCEL:\n                stopNestedScroll();\n                result = super.onTouchEvent(event);\n                break;\n        }\n        return result;\n    }\n\n    @Override\n    public void setNestedScrollingEnabled(boolean enabled) {\n        mChildHelper.setNestedScrollingEnabled(enabled);\n    }\n\n    @Override\n    public boolean isNestedScrollingEnabled() {\n        return mChildHelper.isNestedScrollingEnabled();\n    }\n\n    @Override\n    public boolean startNestedScroll(int axes) {\n        return mChildHelper.startNestedScroll(axes);\n    }\n\n    @Override\n    public void stopNestedScroll() {\n        mChildHelper.stopNestedScroll();\n    }\n\n    @Override\n    public boolean hasNestedScrollingParent() {\n        return mChildHelper.hasNestedScrollingParent();\n    }\n\n    @Override\n    public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, int[] offsetInWindow) {\n        return mChildHelper.dispatchNestedScroll(dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, offsetInWindow);\n    }\n\n    @Override\n    public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow) {\n        return mChildHelper.dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow);\n    }\n\n    @Override\n    public boolean dispatchNestedFling(float velocityX, float velocityY, boolean consumed) {\n        return mChildHelper.dispatchNestedFling(velocityX, velocityY, consumed);\n    }\n\n    @Override\n    public boolean dispatchNestedPreFling(float velocityX, float velocityY) {\n        return mChildHelper.dispatchNestedPreFling(velocityX, velocityY);\n    }\n}\n"
  },
  {
    "path": "agentweb-core/src/main/java/com/just/agentweb/PermissionInterceptor.java",
    "content": "/*\n * Copyright (C)  Justson(https://github.com/Justson/AgentWeb)\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.just.agentweb;\n\n/**\n * @author cenxiaozhong\n * @since 3.0.0\n */\npublic interface PermissionInterceptor {\n    boolean intercept(String url, String[] permissions, String action);\n}\n"
  },
  {
    "path": "agentweb-core/src/main/java/com/just/agentweb/ProcessUtils.java",
    "content": "package com.just.agentweb;\n\nimport android.app.ActivityManager;\nimport android.app.Application;\nimport android.content.Context;\nimport android.text.TextUtils;\n\nimport java.io.BufferedReader;\nimport java.io.File;\nimport java.io.FileReader;\nimport java.lang.reflect.Field;\nimport java.lang.reflect.Method;\nimport java.util.List;\n\n/**\n * Adapted from com.blankj.utilcode.util.ProcessUtils#getCurrentProcessName\n */\nclass ProcessUtils {\n\n    static String getCurrentProcessName(Context context) {\n        String name = getCurrentProcessNameByFile();\n        if (!TextUtils.isEmpty(name)) return name;\n        name = getCurrentProcessNameByAms(context);\n        if (!TextUtils.isEmpty(name)) return name;\n        name = getCurrentProcessNameByReflect(context);\n        return name;\n    }\n\n    private static String getCurrentProcessNameByFile() {\n        try {\n            File file = new File(\"/proc/\" + android.os.Process.myPid() + \"/\" + \"cmdline\");\n            BufferedReader mBufferedReader = new BufferedReader(new FileReader(file));\n            String processName = mBufferedReader.readLine().trim();\n            mBufferedReader.close();\n            return processName;\n        } catch (Exception e) {\n            e.printStackTrace();\n            return \"\";\n        }\n    }\n\n    private static String getCurrentProcessNameByAms(Context context) {\n        ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);\n        if (am == null) return \"\";\n        List<ActivityManager.RunningAppProcessInfo> info = am.getRunningAppProcesses();\n        if (info == null || info.size() == 0) return \"\";\n        int pid = android.os.Process.myPid();\n        for (ActivityManager.RunningAppProcessInfo aInfo : info) {\n            if (aInfo.pid == pid) {\n                if (aInfo.processName != null) {\n                    return aInfo.processName;\n                }\n            }\n        }\n        return \"\";\n    }\n\n    private static String getCurrentProcessNameByReflect(Context context) {\n        String processName = \"\";\n        try {\n            Application app = (Application) context.getApplicationContext();\n            Field loadedApkField = app.getClass().getField(\"mLoadedApk\");\n            loadedApkField.setAccessible(true);\n            Object loadedApk = loadedApkField.get(app);\n\n            Field activityThreadField = loadedApk.getClass().getDeclaredField(\"mActivityThread\");\n            activityThreadField.setAccessible(true);\n            Object activityThread = activityThreadField.get(loadedApk);\n\n            Method getProcessName = activityThread.getClass().getDeclaredMethod(\"getProcessName\");\n            processName = (String) getProcessName.invoke(activityThread);\n        } catch (Exception e) {\n            e.printStackTrace();\n        }\n        return processName;\n    }\n\n}\n"
  },
  {
    "path": "agentweb-core/src/main/java/com/just/agentweb/Provider.java",
    "content": "/*\n * Copyright (C)  Justson(https://github.com/Justson/AgentWeb)\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.just.agentweb;\n\n/**\n * @author cenxiaozhong\n * @date 2017/7/5\n * @since 1.0.0\n */\npublic interface Provider<T> {\n   T provide();\n}\n"
  },
  {
    "path": "agentweb-core/src/main/java/com/just/agentweb/QuickCallJs.java",
    "content": "/*\n * Copyright (C)  Justson(https://github.com/Justson/AgentWeb)\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.just.agentweb;\n\nimport android.os.Build;\nimport androidx.annotation.RequiresApi;\nimport android.webkit.ValueCallback;\n\n\n/**\n * @author cenxiaozhong\n * @date 2017/5/29\n * @since 1.0.0\n */\npublic interface QuickCallJs {\n    @RequiresApi(Build.VERSION_CODES.KITKAT)\n    void quickCallJs(String method, ValueCallback<String> callback, String... params);\n\n    void quickCallJs(String method, String... params);\n\n    void quickCallJs(String method);\n\n\n}\n"
  },
  {
    "path": "agentweb-core/src/main/java/com/just/agentweb/RomUtils.java",
    "content": "package com.just.agentweb;\n\n\nimport android.annotation.SuppressLint;\nimport android.os.Build;\nimport android.os.Environment;\nimport android.text.TextUtils;\n\nimport java.io.BufferedReader;\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.IOException;\nimport java.io.InputStreamReader;\nimport java.lang.reflect.Method;\nimport java.util.Properties;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2018/07/04\n *     desc  : utils about rom\n * </pre>\n */\npublic final class RomUtils {\n\n    private static final String[] ROM_HUAWEI    = {\"huawei\"};\n    private static final String VERSION_PROPERTY_HUAWEI  = \"ro.build.version.emui\";\n    private final static String UNKNOWN                  = \"unknown\";\n\n    private static RomInfo bean = null;\n\n    private RomUtils() {\n        throw new UnsupportedOperationException(\"u can't instantiate me...\");\n    }\n\n    /**\n     * Return whether the rom is made by huawei.\n     *\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isHuawei() {\n        return ROM_HUAWEI[0].equals(getRomInfo().name);\n    }\n\n    /**\n     * Return the rom's information.\n     *\n     * @return the rom's information\n     */\n    public static RomInfo getRomInfo() {\n        if (bean != null) return bean;\n        bean = new RomInfo();\n        final String brand = getBrand();\n        final String manufacturer = getManufacturer();\n        if (isRightRom(brand, manufacturer, ROM_HUAWEI)) {\n            bean.name = ROM_HUAWEI[0];\n            String version = getRomVersion(VERSION_PROPERTY_HUAWEI);\n            String[] temp = version.split(\"_\");\n            if (temp.length > 1) {\n                bean.version = temp[1];\n            } else {\n                bean.version = version;\n            }\n            return bean;\n        }else {\n            bean.name = manufacturer;\n        }\n        bean.version = getRomVersion(\"\");\n        return bean;\n    }\n\n    private static boolean isRightRom(final String brand, final String manufacturer, final String... names) {\n        for (String name : names) {\n            if (brand.contains(name) || manufacturer.contains(name)) {\n                return true;\n            }\n        }\n        return false;\n    }\n\n    private static String getManufacturer() {\n        try {\n            String manufacturer = Build.MANUFACTURER;\n            if (!TextUtils.isEmpty(manufacturer)) {\n                return manufacturer.toLowerCase();\n            }\n        } catch (Throwable ignore) {/**/}\n        return UNKNOWN;\n    }\n\n    private static String getBrand() {\n        try {\n            String brand = Build.BRAND;\n            if (!TextUtils.isEmpty(brand)) {\n                return brand.toLowerCase();\n            }\n        } catch (Throwable ignore) {/**/}\n        return UNKNOWN;\n    }\n\n    private static String getRomVersion(final String propertyName) {\n        String ret = \"\";\n        if (!TextUtils.isEmpty(propertyName)) {\n            ret = getSystemProperty(propertyName);\n        }\n        if (TextUtils.isEmpty(ret) || ret.equals(UNKNOWN)) {\n            try {\n                String display = Build.DISPLAY;\n                if (!TextUtils.isEmpty(display)) {\n                    ret = display.toLowerCase();\n                }\n            } catch (Throwable ignore) {/**/}\n        }\n        if (TextUtils.isEmpty(ret)) {\n            return UNKNOWN;\n        }\n        return ret;\n    }\n\n    private static String getSystemProperty(final String name) {\n        String prop = getSystemPropertyByShell(name);\n        if (!TextUtils.isEmpty(prop)) return prop;\n        prop = getSystemPropertyByStream(name);\n        if (!TextUtils.isEmpty(prop)) return prop;\n        if (Build.VERSION.SDK_INT < 28) {\n            return getSystemPropertyByReflect(name);\n        }\n        return prop;\n    }\n\n    private static String getSystemPropertyByShell(final String propName) {\n        String line;\n        BufferedReader input = null;\n        try {\n            Process p = Runtime.getRuntime().exec(\"getprop \" + propName);\n            input = new BufferedReader(new InputStreamReader(p.getInputStream()), 1024);\n            String ret = input.readLine();\n            if (ret != null) {\n                return ret;\n            }\n        } catch (IOException ignore) {\n        } finally {\n            if (input != null) {\n                try {\n                    input.close();\n                } catch (IOException ignore) {/**/}\n            }\n        }\n        return \"\";\n    }\n\n    private static String getSystemPropertyByStream(final String key) {\n        try {\n            Properties prop = new Properties();\n            FileInputStream is = new FileInputStream(\n                    new File(Environment.getRootDirectory(), \"build.prop\")\n            );\n            prop.load(is);\n            return prop.getProperty(key, \"\");\n        } catch (Exception ignore) {/**/}\n        return \"\";\n    }\n\n    private static String getSystemPropertyByReflect(String key) {\n        try {\n            @SuppressLint(\"PrivateApi\")\n            Class<?> clz = Class.forName(\"android.os.SystemProperties\");\n            Method getMethod = clz.getMethod(\"get\", String.class, String.class);\n            return (String) getMethod.invoke(clz, key, \"\");\n        } catch (Exception e) {/**/}\n        return \"\";\n    }\n\n    public static class RomInfo {\n        private String name;\n        private String version;\n\n        public String getName() {\n            return name;\n        }\n\n        public String getVersion() {\n            return version;\n        }\n\n        @Override\n        public String toString() {\n            return \"RomInfo{name=\" + name +\n                    \", version=\" + version + \"}\";\n        }\n    }\n}\n\n"
  },
  {
    "path": "agentweb-core/src/main/java/com/just/agentweb/UrlCommonException.java",
    "content": "/*\r\n * Copyright (C)  Justson(https://github.com/Justson/AgentWeb)\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n *      http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n\r\npackage com.just.agentweb;\r\n/**\r\n * @author cenxiaozhong\r\n * @since 1.0.0\r\n */\r\npublic class UrlCommonException extends RuntimeException {\r\n\r\n    public UrlCommonException() {\r\n    }\r\n\r\n    public UrlCommonException(String msg) {\r\n        super(msg);\r\n    }\r\n}\r\n"
  },
  {
    "path": "agentweb-core/src/main/java/com/just/agentweb/UrlLoaderImpl.java",
    "content": "/*\n * Copyright (C)  Justson(https://github.com/Justson/AgentWeb)\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.just.agentweb;\n\nimport android.os.Handler;\nimport android.os.Looper;\nimport android.webkit.WebView;\n\nimport java.util.Map;\n\n/**\n * @author cenxiaozhong\n * @since 2.0.0\n */\npublic class UrlLoaderImpl implements IUrlLoader {\n\tprivate Handler mHandler = null;\n\tprivate WebView mWebView;\n\tprivate HttpHeaders mHttpHeaders;\n\tpublic static final String TAG = UrlLoaderImpl.class.getSimpleName();\n\n\tUrlLoaderImpl(WebView webView, HttpHeaders httpHeaders) {\n\t\tthis.mWebView = webView;\n\t\tif (this.mWebView == null) {\n\t\t\tnew NullPointerException(\"webview cannot be null .\");\n\t\t}\n\t\tthis.mHttpHeaders = httpHeaders;\n\t\tif (this.mHttpHeaders == null) {\n\t\t\tthis.mHttpHeaders = HttpHeaders.create();\n\t\t}\n\t\tmHandler = new Handler(Looper.getMainLooper());\n\t}\n\n\tprivate void safeLoadUrl(final String url) {\n\t\tmHandler.post(new Runnable() {\n\t\t\t@Override\n\t\t\tpublic void run() {\n\t\t\t\tloadUrl(url);\n\t\t\t}\n\t\t});\n\t}\n\n\tprivate void safeReload() {\n\t\tmHandler.post(new Runnable() {\n\t\t\t@Override\n\t\t\tpublic void run() {\n\t\t\t\treload();\n\t\t\t}\n\t\t});\n\t}\n\n\t@Override\n\tpublic void loadUrl(String url) {\n\t\tthis.loadUrl(url, this.mHttpHeaders.getHeaders(url));\n\t}\n\n\t@Override\n\tpublic void loadUrl(final String url, final Map<String, String> headers) {\n\t\tif (!AgentWebUtils.isUIThread()) {\n\t\t\tAgentWebUtils.runInUiThread(new Runnable() {\n\t\t\t\t@Override\n\t\t\t\tpublic void run() {\n\t\t\t\t\tloadUrl(url, headers);\n\t\t\t\t}\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\t\tLogUtils.i(TAG, \"loadUrl:\" + url + \" headers:\" + headers);\n\t\tif (headers == null || headers.isEmpty()) {\n\t\t\tthis.mWebView.loadUrl(url);\n\t\t} else {\n\t\t\tthis.mWebView.loadUrl(url, headers);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void reload() {\n\t\tif (!AgentWebUtils.isUIThread()) {\n\t\t\tmHandler.post(new Runnable() {\n\t\t\t\t@Override\n\t\t\t\tpublic void run() {\n\t\t\t\t\treload();\n\t\t\t\t}\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\t\tthis.mWebView.reload();\n\t}\n\n\t@Override\n\tpublic void loadData(final String data, final String mimeType, final String encoding) {\n\t\tif (!AgentWebUtils.isUIThread()) {\n\t\t\tmHandler.post(new Runnable() {\n\t\t\t\t@Override\n\t\t\t\tpublic void run() {\n\t\t\t\t\tloadData(data, mimeType, encoding);\n\t\t\t\t}\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\t\tthis.mWebView.loadData(data, mimeType, encoding);\n\t}\n\n\t@Override\n\tpublic void stopLoading() {\n\t\tif (!AgentWebUtils.isUIThread()) {\n\t\t\tmHandler.post(new Runnable() {\n\t\t\t\t@Override\n\t\t\t\tpublic void run() {\n\t\t\t\t\tstopLoading();\n\t\t\t\t}\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\t\tthis.mWebView.stopLoading();\n\t}\n\n\t@Override\n\tpublic void loadDataWithBaseURL(final String baseUrl, final String data, final String mimeType, final String encoding, final String historyUrl) {\n\t\tif (!AgentWebUtils.isUIThread()) {\n\t\t\tmHandler.post(new Runnable() {\n\t\t\t\t@Override\n\t\t\t\tpublic void run() {\n\t\t\t\t\tloadDataWithBaseURL(baseUrl, data, mimeType, encoding, historyUrl);\n\t\t\t\t}\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\t\tthis.mWebView.loadDataWithBaseURL(baseUrl, data, mimeType, encoding, historyUrl);\n\t}\n\n\t@Override\n\tpublic void postUrl(final String url, final byte[] postData) {\n\t\tif (!AgentWebUtils.isUIThread()) {\n\t\t\tmHandler.post(new Runnable() {\n\t\t\t\t@Override\n\t\t\t\tpublic void run() {\n\t\t\t\t\tpostUrl(url, postData);\n\t\t\t\t}\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\t\tthis.mWebView.postUrl(url, postData);\n\t}\n\n\t@Override\n\tpublic HttpHeaders getHttpHeaders() {\n\t\treturn this.mHttpHeaders == null ? this.mHttpHeaders = HttpHeaders.create() : this.mHttpHeaders;\n\t}\n}\n"
  },
  {
    "path": "agentweb-core/src/main/java/com/just/agentweb/VideoImpl.java",
    "content": "/*\n * Copyright (C)  Justson(https://github.com/Justson/AgentWeb)\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.just.agentweb;\n\nimport android.app.Activity;\nimport android.content.pm.ActivityInfo;\nimport android.graphics.Color;\nimport android.os.Build;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.view.Window;\nimport android.view.WindowManager;\nimport android.webkit.WebChromeClient;\nimport android.webkit.WebView;\nimport android.widget.FrameLayout;\nimport androidx.core.util.Pair;\nimport java.util.HashSet;\nimport java.util.Set;\n\n/**\n * @author cenxiaozhong\n */\npublic class VideoImpl implements IVideo, EventInterceptor {\n\n    private Activity mActivity;\n    private WebView mWebView;\n    private static final String TAG = VideoImpl.class.getSimpleName();\n    private Set<Pair<Integer, Integer>> mFlags = null;\n    private View mMoiveView = null;\n    private ViewGroup mMoiveParentView = null;\n    private WebChromeClient.CustomViewCallback mCallback;\n    private int mOriginalOrientation;\n\n    public VideoImpl(Activity mActivity, WebView webView) {\n        this.mActivity = mActivity;\n        this.mWebView = webView;\n        mFlags = new HashSet<>();\n    }\n\n    @Override\n    public void onShowCustomView(View view, WebChromeClient.CustomViewCallback callback) {\n        Activity mActivity;\n        if ((mActivity = this.mActivity) == null || mActivity.isFinishing()) {\n            return;\n        }\n        mOriginalOrientation = mActivity.getRequestedOrientation();\n        mActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);\n        Window mWindow = mActivity.getWindow();\n        Pair<Integer, Integer> mPair = null;\n        // 保存当前屏幕的状态\n        if ((mWindow.getAttributes().flags & WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) == 0) {\n            mPair = new Pair<>(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON, 0);\n            mWindow.setFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON,\n                    WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);\n            mFlags.add(mPair);\n        }\n        if ((Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB)\n                && (mWindow.getAttributes().flags\n                & WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED) == 0) {\n            mPair = new Pair<>(WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED, 0);\n            mWindow.setFlags(WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED,\n                    WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);\n            mFlags.add(mPair);\n        }\n        if (mMoiveView != null) {\n            callback.onCustomViewHidden();\n            return;\n        }\n        if (mWebView != null) {\n            mWebView.setVisibility(View.GONE);\n        }\n        if (mMoiveParentView == null) {\n            FrameLayout mContentView = mActivity.findViewById(android.R.id.content);\n            mMoiveParentView = new FrameLayout(mActivity);\n            mMoiveParentView.setBackgroundColor(Color.BLACK);\n            mContentView.addView(mMoiveParentView);\n        }\n        this.mCallback = callback;\n        mMoiveParentView.addView(this.mMoiveView = view);\n        mMoiveParentView.setVisibility(View.VISIBLE);\n    }\n\n    @Override\n    public void onHideCustomView() {\n        if (mMoiveView == null) {\n            return;\n        }\n        if (mActivity != null) {\n            mActivity.setRequestedOrientation(mOriginalOrientation);\n        }\n        if (!mFlags.isEmpty()) {\n            for (Pair<Integer, Integer> mPair : mFlags) {\n                mActivity.getWindow().setFlags(mPair.second, mPair.first);\n            }\n            mFlags.clear();\n        }\n        mMoiveView.setVisibility(View.GONE);\n        if (mMoiveParentView != null && mMoiveView != null) {\n            mMoiveParentView.removeView(mMoiveView);\n        }\n        if (mMoiveParentView != null) {\n            mMoiveParentView.setVisibility(View.GONE);\n        }\n        if (this.mCallback != null) {\n            mCallback.onCustomViewHidden();\n        }\n        this.mMoiveView = null;\n        if (mWebView != null) {\n            mWebView.setVisibility(View.VISIBLE);\n        }\n    }\n\n    @Override\n    public boolean isVideoState() {\n        return mMoiveView != null;\n    }\n\n    @Override\n    public boolean event() {\n        if (isVideoState()) {\n            onHideCustomView();\n            return true;\n        } else {\n            return false;\n        }\n    }\n}\n"
  },
  {
    "path": "agentweb-core/src/main/java/com/just/agentweb/WebChromeClient.java",
    "content": "package com.just.agentweb;\n\n/**\n * @author cenxiaozhong\n * @date 2019/4/13\n * @since 1.0.0\n */\npublic class WebChromeClient extends MiddlewareWebChromeBase{\n\tpublic WebChromeClient() {\n\t}\n}\n"
  },
  {
    "path": "agentweb-core/src/main/java/com/just/agentweb/WebChromeClientDelegate.java",
    "content": "/*\r\n * Copyright (C)  Justson(https://github.com/Justson/AgentWeb)\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n *      http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n\r\npackage com.just.agentweb;\r\n\r\nimport android.graphics.Bitmap;\r\nimport android.net.Uri;\r\nimport android.os.Build;\r\nimport android.os.Message;\r\nimport androidx.annotation.RequiresApi;\r\nimport android.view.View;\r\nimport android.webkit.ConsoleMessage;\r\nimport android.webkit.GeolocationPermissions;\r\nimport android.webkit.JsPromptResult;\r\nimport android.webkit.JsResult;\r\nimport android.webkit.PermissionRequest;\r\nimport android.webkit.ValueCallback;\r\nimport android.webkit.WebChromeClient;\r\nimport android.webkit.WebStorage;\r\nimport android.webkit.WebView;\r\n\r\nimport java.lang.reflect.Method;\r\n\r\n/**\r\n * @author cenxiaozhong\r\n * @update WebChromeClientDelegate\r\n * @since 1.0.0\r\n */\r\npublic class WebChromeClientDelegate extends WebChromeClient {\r\n    private WebChromeClient mDelegate;\r\n\r\n    protected WebChromeClient getDelegate() {\r\n        return mDelegate;\r\n    }\r\n\r\n    public WebChromeClientDelegate(WebChromeClient webChromeClient) {\r\n        this.mDelegate = webChromeClient;\r\n    }\r\n\r\n    void setDelegate(WebChromeClient delegate) {\r\n        this.mDelegate = delegate;\r\n    }\r\n\r\n    @Override\r\n    public void onProgressChanged(WebView view, int newProgress) {\r\n        super.onProgressChanged(view, newProgress);\r\n        if (this.mDelegate != null) {\r\n            this.mDelegate.onProgressChanged(view, newProgress);\r\n            return;\r\n        }\r\n    }\r\n\r\n    @Override\r\n    public void onReceivedTitle(WebView view, String title) {\r\n        if (this.mDelegate != null) {\r\n            this.mDelegate.onReceivedTitle(view, title);\r\n            return;\r\n        }\r\n        super.onReceivedTitle(view, title);\r\n    }\r\n\r\n    @Override\r\n    public void onReceivedIcon(WebView view, Bitmap icon) {\r\n        if (this.mDelegate != null) {\r\n            this.mDelegate.onReceivedIcon(view, icon);\r\n            return;\r\n        }\r\n        super.onReceivedIcon(view, icon);\r\n    }\r\n\r\n    @Override\r\n    public void onReceivedTouchIconUrl(WebView view, String url,\r\n                                       boolean precomposed) {\r\n        if (this.mDelegate != null) {\r\n            this.mDelegate.onReceivedTouchIconUrl(view, url, precomposed);\r\n            return;\r\n        }\r\n        super.onReceivedTouchIconUrl(view, url, precomposed);\r\n    }\r\n\r\n    @Override\r\n    public void onShowCustomView(View view, CustomViewCallback callback) {\r\n        if (this.mDelegate != null) {\r\n            this.mDelegate.onShowCustomView(view, callback);\r\n            return;\r\n        }\r\n        super.onShowCustomView(view, callback);\r\n    }\r\n\r\n\r\n    @Override\r\n    public void onShowCustomView(View view, int requestedOrientation,\r\n                                 CustomViewCallback callback) {\r\n        if (this.mDelegate != null) {\r\n            this.mDelegate.onShowCustomView(view, requestedOrientation, callback);\r\n            return;\r\n        }\r\n        super.onShowCustomView(view, requestedOrientation, callback);\r\n    }\r\n\r\n\r\n    @Override\r\n    public void onHideCustomView() {\r\n        if (this.mDelegate != null) {\r\n            this.mDelegate.onHideCustomView();\r\n            return;\r\n        }\r\n        super.onHideCustomView();\r\n    }\r\n\r\n    @Override\r\n    public boolean onCreateWindow(WebView view, boolean isDialog,\r\n                                  boolean isUserGesture, Message resultMsg) {\r\n        if (this.mDelegate != null) {\r\n            return this.mDelegate.onCreateWindow(view, isDialog, isUserGesture, resultMsg);\r\n        }\r\n        return super.onCreateWindow(view, isDialog, isUserGesture, resultMsg);\r\n    }\r\n\r\n    @Override\r\n    public void onRequestFocus(WebView view) {\r\n        if (this.mDelegate != null) {\r\n            this.mDelegate.onRequestFocus(view);\r\n            return;\r\n        }\r\n        super.onRequestFocus(view);\r\n    }\r\n\r\n    @Override\r\n    public void onCloseWindow(WebView window) {\r\n        if (this.mDelegate != null) {\r\n            this.mDelegate.onCloseWindow(window);\r\n            return;\r\n        }\r\n        super.onCloseWindow(window);\r\n    }\r\n\r\n    @Override\r\n    public boolean onJsAlert(WebView view, String url, String message,\r\n                             JsResult result) {\r\n        if (this.mDelegate != null) {\r\n            return this.mDelegate.onJsAlert(view, url, message, result);\r\n        }\r\n        return super.onJsAlert(view, url, message, result);\r\n    }\r\n\r\n    @Override\r\n    public boolean onJsConfirm(WebView view, String url, String message,\r\n                               JsResult result) {\r\n        if (this.mDelegate != null) {\r\n            return this.mDelegate.onJsConfirm(view, url, message, result);\r\n        }\r\n        return super.onJsConfirm(view, url, message, result);\r\n    }\r\n\r\n    @Override\r\n    public boolean onJsPrompt(WebView view, String url, String message,\r\n                              String defaultValue, JsPromptResult result) {\r\n        if (this.mDelegate != null) {\r\n            return this.mDelegate.onJsPrompt(view, url, message, defaultValue, result);\r\n        }\r\n        return super.onJsPrompt(view, url, message, defaultValue, result);\r\n    }\r\n\r\n    @Override\r\n    public boolean onJsBeforeUnload(WebView view, String url, String message,\r\n                                    JsResult result) {\r\n        if (this.mDelegate != null) {\r\n            return this.mDelegate.onJsBeforeUnload(view, url, message, result);\r\n        }\r\n        return super.onJsBeforeUnload(view, url, message, result);\r\n    }\r\n\r\n    @Override\r\n    @Deprecated\r\n    public void onExceededDatabaseQuota(String url, String databaseIdentifier,\r\n                                        long quota, long estimatedDatabaseSize, long totalQuota,\r\n                                        WebStorage.QuotaUpdater quotaUpdater) {\r\n        // This default implementation passes the current quota back to WebCore.\r\n        // WebCore will interpret this that new quota was declined.\r\n        if (this.mDelegate != null) {\r\n            this.mDelegate.onExceededDatabaseQuota(url, databaseIdentifier, quota, estimatedDatabaseSize, totalQuota, quotaUpdater);\r\n            return;\r\n        }\r\n        super.onExceededDatabaseQuota(url, databaseIdentifier, quota, estimatedDatabaseSize, totalQuota, quotaUpdater);\r\n\r\n    }\r\n\r\n\r\n    @Override\r\n    public void onGeolocationPermissionsShowPrompt(String origin,\r\n                                                   GeolocationPermissions.Callback callback) {\r\n        if (this.mDelegate != null) {\r\n            this.mDelegate.onGeolocationPermissionsShowPrompt(origin, callback);\r\n            return;\r\n        }\r\n        super.onGeolocationPermissionsShowPrompt(origin, callback);\r\n\r\n    }\r\n\r\n    /**\r\n     * notify the host application that a request for Geolocation permissions,\r\n     * made with a previous call to\r\n     * {@link #onGeolocationPermissionsShowPrompt(String, GeolocationPermissions.Callback) onGeolocationPermissionsShowPrompt()}\r\n     * has been canceled. Any related UI should therefore be hidden.\r\n     */\r\n    @Override\r\n    public void onGeolocationPermissionsHidePrompt() {\r\n\r\n        if (this.mDelegate != null) {\r\n            this.mDelegate.onGeolocationPermissionsHidePrompt();\r\n            return;\r\n        }\r\n\r\n        super.onGeolocationPermissionsHidePrompt();\r\n    }\r\n\r\n    @Override\r\n    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)\r\n    public void onPermissionRequest(PermissionRequest request) {\r\n        if (this.mDelegate != null) {\r\n            this.mDelegate.onPermissionRequest(request);\r\n            return;\r\n        }\r\n        super.onPermissionRequest(request);\r\n    }\r\n\r\n    @Override\r\n    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)\r\n    public void onPermissionRequestCanceled(PermissionRequest request) {\r\n\r\n        if (this.mDelegate != null) {\r\n            this.mDelegate.onPermissionRequestCanceled(request);\r\n            return;\r\n        }\r\n        super.onPermissionRequestCanceled(request);\r\n    }\r\n\r\n    @Override\r\n    public boolean onJsTimeout() {\r\n        if (this.mDelegate != null) {\r\n            return this.mDelegate.onJsTimeout();\r\n        }\r\n        return super.onJsTimeout();\r\n    }\r\n\r\n    @Override\r\n    @Deprecated\r\n    public void onConsoleMessage(String message, int lineNumber, String sourceID) {\r\n        if (this.mDelegate != null) {\r\n            this.mDelegate.onConsoleMessage(message, lineNumber, sourceID);\r\n            return;\r\n        }\r\n        super.onConsoleMessage(message, lineNumber, sourceID);\r\n    }\r\n\r\n    @Override\r\n    public boolean onConsoleMessage(ConsoleMessage consoleMessage) {\r\n        /*onConsoleMessage(consoleMessage.message(), consoleMessage.lineNumber(),\r\n                consoleMessage.sourceId());*/\r\n\r\n        if (this.mDelegate != null) {\r\n            return this.mDelegate.onConsoleMessage(consoleMessage);\r\n        }\r\n        return super.onConsoleMessage(consoleMessage);\r\n    }\r\n\r\n    @Override\r\n    public Bitmap getDefaultVideoPoster() {\r\n        if (this.mDelegate != null) {\r\n            return this.mDelegate.getDefaultVideoPoster();\r\n        }\r\n        return super.getDefaultVideoPoster();\r\n    }\r\n\r\n    @Override\r\n    public View getVideoLoadingProgressView() {\r\n        if (this.mDelegate != null) {\r\n            return this.mDelegate.getVideoLoadingProgressView();\r\n        }\r\n        return super.getVideoLoadingProgressView();\r\n    }\r\n\r\n    @Override\r\n    public void getVisitedHistory(ValueCallback<String[]> callback) {\r\n        if (this.mDelegate != null) {\r\n            this.mDelegate.getVisitedHistory(callback);\r\n            return;\r\n        }\r\n        super.getVisitedHistory(callback);\r\n    }\r\n\r\n    @Override\r\n    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)\r\n    public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback,\r\n                                     FileChooserParams fileChooserParams) {\r\n        if (this.mDelegate != null) {\r\n            return this.mDelegate.onShowFileChooser(webView, filePathCallback, fileChooserParams);\r\n        }\r\n        return super.onShowFileChooser(webView, filePathCallback, fileChooserParams);\r\n    }\r\n\r\n\r\n    /**\r\n     * Android  >= 4.1\r\n     *\r\n     * @param uploadFile\r\n     * @param acceptType\r\n     * @param capture\r\n     */\r\n    public void openFileChooser(ValueCallback<Uri> uploadFile, String acceptType, String capture) {\r\n        commonRefect(this.mDelegate, \"openFileChooser\", new Object[]{uploadFile, acceptType, capture}, ValueCallback.class, String.class, String.class);\r\n    }\r\n\r\n    /**\r\n     * Android < 3.0\r\n     *\r\n     * @param valueCallback\r\n     */\r\n    public void openFileChooser(ValueCallback<Uri> valueCallback) {\r\n        commonRefect(this.mDelegate, \"openFileChooser\", new Object[]{valueCallback}, ValueCallback.class);\r\n    }\r\n\r\n    /**\r\n     * Android  >= 3.0\r\n     *\r\n     * @param valueCallback\r\n     * @param acceptType\r\n     */\r\n    public void openFileChooser(ValueCallback valueCallback, String acceptType) {\r\n        commonRefect(this.mDelegate, \"openFileChooser\", new Object[]{valueCallback, acceptType}, ValueCallback.class, String.class);\r\n    }\r\n\r\n\r\n    private void commonRefect(WebChromeClient o, String mothed, Object[] os, Class... clazzs) {\r\n        try {\r\n            if (o == null) {\r\n                return;\r\n            }\r\n            Class<?> clazz = o.getClass();\r\n            Method mMethod = clazz.getMethod(mothed, clazzs);\r\n            mMethod.invoke(o, os);\r\n        } catch (Exception ignore) {\r\n            if (LogUtils.isDebug()) {\r\n                ignore.printStackTrace();\r\n            }\r\n        }\r\n    }\r\n}\r\n"
  },
  {
    "path": "agentweb-core/src/main/java/com/just/agentweb/WebCreator.java",
    "content": "/*\r\n * Copyright (C)  Justson(https://github.com/Justson/AgentWeb)\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n *      http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n\r\npackage com.just.agentweb;\r\n\r\nimport android.webkit.WebView;\r\nimport android.widget.FrameLayout;\r\n/**\r\n * @author cenxiaozhong\r\n * @since 1.0.0\r\n */\r\npublic interface WebCreator extends IWebIndicator {\r\n    WebCreator create();\r\n\r\n    WebView getWebView();\r\n\r\n    FrameLayout getWebParentLayout();\r\n\r\n    int getWebViewType();\r\n}\r\n"
  },
  {
    "path": "agentweb-core/src/main/java/com/just/agentweb/WebIndicator.java",
    "content": "/*\r\n * Copyright (C)  Justson(https://github.com/Justson/AgentWeb)\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n *      http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n\r\npackage com.just.agentweb;\r\n\r\nimport android.animation.Animator;\r\nimport android.animation.AnimatorListenerAdapter;\r\nimport android.animation.AnimatorSet;\r\nimport android.animation.ObjectAnimator;\r\nimport android.animation.ValueAnimator;\r\nimport android.content.Context;\r\nimport android.graphics.Canvas;\r\nimport android.graphics.Color;\r\nimport android.graphics.Paint;\r\nimport androidx.annotation.Nullable;\r\nimport android.util.AttributeSet;\r\nimport android.view.View;\r\nimport android.view.animation.DecelerateInterpolator;\r\nimport android.view.animation.LinearInterpolator;\r\n\r\n/**\r\n * @author cenxiaozhong\r\n * @since 1.0.0\r\n */\r\npublic class WebIndicator extends BaseIndicatorView implements BaseIndicatorSpec {\r\n    /**\r\n     * 进度条颜色\r\n     */\r\n    private int mColor;\r\n    /**\r\n     * 进度条的画笔\r\n     */\r\n    private Paint mPaint;\r\n    /**\r\n     * 进度条动画\r\n     */\r\n    private Animator mAnimator;\r\n    /**\r\n     * 控件的宽度\r\n     */\r\n    private int mTargetWidth = 0;\r\n    /**\r\n     * 默认匀速动画最大的时长\r\n     */\r\n    public static final int MAX_UNIFORM_SPEED_DURATION = 8 * 1000;\r\n    /**\r\n     * 默认加速后减速动画最大时长\r\n     */\r\n    public static final int MAX_DECELERATE_SPEED_DURATION = 450;\r\n    /**\r\n     * 结束动画时长 ， Fade out 。\r\n     */\r\n    public static final int DO_END_ANIMATION_DURATION = 600;\r\n    /**\r\n     * 当前匀速动画最大的时长\r\n     */\r\n    private int mCurrentMaxUniformSpeedDuration = MAX_UNIFORM_SPEED_DURATION;\r\n    /**\r\n     * 当前加速后减速动画最大时长\r\n     */\r\n    private int mCurrentMaxDecelerateSpeedDuration = MAX_DECELERATE_SPEED_DURATION;\r\n    /**\r\n     * 结束动画时长\r\n     */\r\n    private int mCurrentDoEndAnimationDuration = DO_END_ANIMATION_DURATION;\r\n    /**\r\n     * 当前进度条的状态\r\n     */\r\n    private int indicatorStatus = 0;\r\n    public static final int UN_START = 0;\r\n    public static final int STARTED = 1;\r\n    public static final int FINISH = 2;\r\n    private float mCurrentProgress = 0F;\r\n    /**\r\n     * 默认的高度\r\n     */\r\n    public int mWebIndicatorDefaultHeight = 3;\r\n\r\n    public WebIndicator(Context context) {\r\n        this(context, null);\r\n    }\r\n\r\n    public WebIndicator(Context context, @Nullable AttributeSet attrs) {\r\n        this(context, attrs, 0);\r\n    }\r\n\r\n    public WebIndicator(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {\r\n        super(context, attrs, defStyleAttr);\r\n        init(context, attrs, defStyleAttr);\r\n    }\r\n\r\n    private void init(Context context, AttributeSet attrs, int defStyleAttr) {\r\n        mPaint = new Paint();\r\n        mColor = Color.parseColor(\"#1aad19\");\r\n        mPaint.setAntiAlias(true);\r\n        mPaint.setColor(mColor);\r\n        mPaint.setDither(true);\r\n        mPaint.setStrokeCap(Paint.Cap.SQUARE);\r\n        mTargetWidth = context.getResources().getDisplayMetrics().widthPixels;\r\n        mWebIndicatorDefaultHeight = AgentWebUtils.dp2px(context, 3);\r\n    }\r\n\r\n    public void setColor(int color) {\r\n        this.mColor = color;\r\n        mPaint.setColor(color);\r\n    }\r\n\r\n    public void setColor(String color) {\r\n        this.setColor(Color.parseColor(color));\r\n    }\r\n\r\n    @Override\r\n    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {\r\n        int wMode = MeasureSpec.getMode(widthMeasureSpec);\r\n        int w = MeasureSpec.getSize(widthMeasureSpec);\r\n        int hMode = MeasureSpec.getMode(heightMeasureSpec);\r\n        int h = MeasureSpec.getSize(heightMeasureSpec);\r\n\r\n        if (wMode == MeasureSpec.AT_MOST) {\r\n            w = w <= getContext().getResources().getDisplayMetrics().widthPixels ? w : getContext().getResources().getDisplayMetrics().widthPixels;\r\n        }\r\n        if (hMode == MeasureSpec.AT_MOST) {\r\n            h = mWebIndicatorDefaultHeight;\r\n        }\r\n        this.setMeasuredDimension(w, h);\r\n    }\r\n\r\n\r\n    @Override\r\n    protected void onDraw(Canvas canvas) {\r\n    }\r\n\r\n    @Override\r\n    protected void dispatchDraw(Canvas canvas) {\r\n        canvas.drawRect(0, 0, mCurrentProgress / 100 * Float.valueOf(this.getWidth()), this.getHeight(), mPaint);\r\n    }\r\n\r\n    @Override\r\n    public void show() {\r\n        if (getVisibility() == View.GONE) {\r\n            this.setVisibility(View.VISIBLE);\r\n            mCurrentProgress = 0f;\r\n            startAnim(false);\r\n        }\r\n    }\r\n\r\n    @Override\r\n    protected void onSizeChanged(int w, int h, int oldw, int oldh) {\r\n        super.onSizeChanged(w, h, oldw, oldh);\r\n        this.mTargetWidth = getMeasuredWidth();\r\n        int screenWidth = getContext().getResources().getDisplayMetrics().widthPixels;\r\n        if (mTargetWidth >= screenWidth) {\r\n            mCurrentMaxDecelerateSpeedDuration = MAX_DECELERATE_SPEED_DURATION;\r\n            mCurrentMaxUniformSpeedDuration = MAX_UNIFORM_SPEED_DURATION;\r\n            mCurrentDoEndAnimationDuration = MAX_DECELERATE_SPEED_DURATION;\r\n        } else {\r\n            //取比值\r\n            float rate = this.mTargetWidth / Float.valueOf(screenWidth);\r\n            mCurrentMaxUniformSpeedDuration = (int) (MAX_UNIFORM_SPEED_DURATION * rate);\r\n            mCurrentMaxDecelerateSpeedDuration = (int) (MAX_DECELERATE_SPEED_DURATION * rate);\r\n            mCurrentDoEndAnimationDuration = (int) (DO_END_ANIMATION_DURATION * rate);\r\n\r\n        }\r\n        LogUtils.i(\"WebProgress\", \"CURRENT_MAX_UNIFORM_SPEED_DURATION\" + mCurrentMaxUniformSpeedDuration);\r\n    }\r\n\r\n    public void setProgress(float progress) {\r\n        if (getVisibility() == View.GONE) {\r\n            setVisibility(View.VISIBLE);\r\n        }\r\n        if (progress < 95f) {\r\n            return;\r\n        }\r\n        if (indicatorStatus != FINISH) {\r\n            startAnim(true);\r\n        }\r\n    }\r\n\r\n    @Override\r\n    public void hide() {\r\n        indicatorStatus = FINISH;\r\n    }\r\n\r\n    private void startAnim(boolean isFinished) {\r\n        float v = isFinished ? 100 : 95;\r\n        if (mAnimator != null && mAnimator.isStarted()) {\r\n            mAnimator.cancel();\r\n        }\r\n        mCurrentProgress = mCurrentProgress == 0f ? 0.00000001f : mCurrentProgress;\r\n        if (!isFinished) {\r\n            AnimatorSet animatorSet = new AnimatorSet();\r\n\r\n            float p1 = v * 0.60f;\r\n            float p2 = v;\r\n            ValueAnimator animator = ValueAnimator.ofFloat(mCurrentProgress, p1);\r\n            ValueAnimator animator0 = ValueAnimator.ofFloat(p1, p2);\r\n            float residue = 1f - mCurrentProgress / 100 - 0.05f;\r\n            long duration = (long) (residue * mCurrentMaxUniformSpeedDuration);\r\n            long duration6 = (long) (duration * 0.6f);\r\n            long duration4 = (long) (duration * 0.4f);\r\n            animator.setInterpolator(new LinearInterpolator());\r\n            animator.setDuration(duration4);\r\n            animator.addUpdateListener(mAnimatorUpdateListener);\r\n\r\n            animator0.setInterpolator(new LinearInterpolator());\r\n            animator0.setDuration(duration6);\r\n            animator0.addUpdateListener(mAnimatorUpdateListener);\r\n            animatorSet.play(animator0).after(animator);\r\n            animatorSet.start();\r\n            this.mAnimator = animatorSet;\r\n        } else {\r\n            ValueAnimator segment95Animator = null;\r\n            if (mCurrentProgress < 95f) {\r\n                segment95Animator = ValueAnimator.ofFloat(mCurrentProgress, 95);\r\n                float residue = 1f - mCurrentProgress / 100f - 0.05f;\r\n                segment95Animator.setDuration((long) (residue * mCurrentMaxDecelerateSpeedDuration));\r\n                segment95Animator.setInterpolator(new DecelerateInterpolator());\r\n                segment95Animator.addUpdateListener(mAnimatorUpdateListener);\r\n            }\r\n            ObjectAnimator mObjectAnimator = ObjectAnimator.ofFloat(this, \"alpha\", 1f, 0f);\r\n            mObjectAnimator.setDuration(mCurrentDoEndAnimationDuration);\r\n            ValueAnimator mValueAnimatorEnd = ValueAnimator.ofFloat(95f, 100f);\r\n            mValueAnimatorEnd.setDuration(mCurrentDoEndAnimationDuration);\r\n            mValueAnimatorEnd.addUpdateListener(mAnimatorUpdateListener);\r\n            AnimatorSet animatorSet = new AnimatorSet();\r\n            animatorSet.playTogether(mObjectAnimator, mValueAnimatorEnd);\r\n            if (segment95Animator != null) {\r\n                AnimatorSet animatorSet0 = new AnimatorSet();\r\n                animatorSet0.play(animatorSet).after(segment95Animator);\r\n                animatorSet = animatorSet0;\r\n            }\r\n            animatorSet.addListener(mAnimatorListenerAdapter);\r\n            animatorSet.start();\r\n            mAnimator = animatorSet;\r\n        }\r\n        indicatorStatus = STARTED;\r\n    }\r\n\r\n    private ValueAnimator.AnimatorUpdateListener mAnimatorUpdateListener = new ValueAnimator.AnimatorUpdateListener() {\r\n        @Override\r\n        public void onAnimationUpdate(ValueAnimator animation) {\r\n            float t = (float) animation.getAnimatedValue();\r\n            WebIndicator.this.mCurrentProgress = t;\r\n            WebIndicator.this.invalidate();\r\n        }\r\n    };\r\n\r\n    private AnimatorListenerAdapter mAnimatorListenerAdapter = new AnimatorListenerAdapter() {\r\n        @Override\r\n        public void onAnimationEnd(Animator animation) {\r\n            doEnd();\r\n        }\r\n    };\r\n\r\n    @Override\r\n    protected void onDetachedFromWindow() {\r\n        super.onDetachedFromWindow();\r\n        /**\r\n         * animator cause leak , if not cancel;\r\n         */\r\n        if (mAnimator != null && mAnimator.isStarted()) {\r\n            mAnimator.cancel();\r\n            mAnimator = null;\r\n        }\r\n    }\r\n\r\n    private void doEnd() {\r\n        if (indicatorStatus == FINISH && mCurrentProgress == 100f) {\r\n            setVisibility(GONE);\r\n            mCurrentProgress = 0f;\r\n            this.setAlpha(1f);\r\n        }\r\n        indicatorStatus = UN_START;\r\n    }\r\n\r\n    @Override\r\n    public void reset() {\r\n        mCurrentProgress = 0;\r\n        if (mAnimator != null && mAnimator.isStarted()) {\r\n            mAnimator.cancel();\r\n        }\r\n    }\r\n\r\n    @Override\r\n    public void setProgress(int newProgress) {\r\n        setProgress(Float.valueOf(newProgress));\r\n    }\r\n\r\n\r\n    @Override\r\n    public LayoutParams offerLayoutParams() {\r\n        return new LayoutParams(-1, mWebIndicatorDefaultHeight);\r\n    }\r\n}\r\n"
  },
  {
    "path": "agentweb-core/src/main/java/com/just/agentweb/WebLifeCycle.java",
    "content": "/*\n * Copyright (C)  Justson(https://github.com/Justson/AgentWeb)\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.just.agentweb;\n\n/**\n * @author cenxiaozhong\n * @date 2017/5/30\n * @since 1.0.0\n */\npublic interface WebLifeCycle {\n    void onResume();\n    void onPause();\n    void onDestroy();\n}\n"
  },
  {
    "path": "agentweb-core/src/main/java/com/just/agentweb/WebListenerManager.java",
    "content": "/*\n * Copyright (C)  Justson(https://github.com/Justson/AgentWeb)\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.just.agentweb;\n\nimport android.webkit.DownloadListener;\nimport android.webkit.WebChromeClient;\nimport android.webkit.WebView;\nimport android.webkit.WebViewClient;\n\n/**\n * @author cenxiaozhong\n * @date 2017/5/13\n * @since 1.0.0\n */\npublic interface WebListenerManager {\n    WebListenerManager setWebChromeClient(WebView webview, WebChromeClient webChromeClient);\n    WebListenerManager setWebViewClient(WebView webView, WebViewClient webViewClient);\n    WebListenerManager setDownloader(WebView webView, DownloadListener downloadListener);\n}\n"
  },
  {
    "path": "agentweb-core/src/main/java/com/just/agentweb/WebParentLayout.java",
    "content": "/*\n * Copyright (C)  Justson(https://github.com/Justson/AgentWeb)\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.just.agentweb;\n\nimport android.app.Activity;\nimport android.content.Context;\nimport android.graphics.Color;\nimport androidx.annotation.IdRes;\nimport androidx.annotation.LayoutRes;\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport android.util.AttributeSet;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.view.ViewStub;\nimport android.webkit.WebView;\nimport android.widget.FrameLayout;\n\n/**\n * @author cenxiaozhong\n * @date 2017/12/8\n * @since 3.0.0\n */\npublic class WebParentLayout extends FrameLayout implements Provider<AbsAgentWebUIController> {\n\tprivate AbsAgentWebUIController mAgentWebUIController = null;\n\tprivate static final String TAG = WebParentLayout.class.getSimpleName();\n\t@LayoutRes\n\tprivate int mErrorLayoutRes;\n\t@IdRes\n\tprivate int mClickId = -1;\n\tprivate View mErrorView;\n\tprivate WebView mWebView;\n\tprivate FrameLayout mErrorLayout = null;\n\n\tWebParentLayout(@NonNull Context context) {\n\t\tthis(context, null);\n\t\tLogUtils.i(TAG, \"WebParentLayout\");\n\t}\n\n\tWebParentLayout(@NonNull Context context, @Nullable AttributeSet attrs) {\n\t\tthis(context, attrs, -1);\n\t}\n\n\tWebParentLayout(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {\n\t\tsuper(context, attrs, defStyleAttr);\n\t\tif (!(context instanceof Activity)) {\n\t\t\tthrow new IllegalArgumentException(\"WebParentLayout context must be activity or activity sub class .\");\n\t\t}\n\t\tthis.mErrorLayoutRes = R.layout.agentweb_error_page;\n\t}\n\n\tvoid bindController(AbsAgentWebUIController agentWebUIController) {\n\t\tthis.mAgentWebUIController = agentWebUIController;\n\t\tthis.mAgentWebUIController.bindWebParent(this, (Activity) getContext());\n\t}\n\n\tvoid showPageMainFrameError() {\n\t\tView container = this.mErrorLayout;\n\t\tif (container != null) {\n\t\t\tcontainer.setVisibility(View.VISIBLE);\n\t\t} else {\n\t\t\tcreateErrorLayout();\n\t\t\tcontainer = this.mErrorLayout;\n\t\t}\n\t\tView clickView = null;\n\t\tif (mClickId != -1 && (clickView = container.findViewById(mClickId)) != null) {\n\t\t\tclickView.setClickable(true);\n\t\t} else {\n\t\t\tcontainer.setClickable(true);\n\t\t}\n\t}\n\n\tprivate void createErrorLayout() {\n\t\tfinal FrameLayout mFrameLayout = new FrameLayout(getContext());\n\t\tmFrameLayout.setBackgroundColor(Color.WHITE);\n\t\tmFrameLayout.setId(R.id.mainframe_error_container_id);\n\t\tif (this.mErrorView == null) {\n\t\t\tLayoutInflater mLayoutInflater = LayoutInflater.from(getContext());\n\t\t\tLogUtils.i(TAG, \"mErrorLayoutRes:\" + mErrorLayoutRes);\n\t\t\tmLayoutInflater.inflate(mErrorLayoutRes, mFrameLayout, true);\n\t\t} else {\n\t\t\tmFrameLayout.addView(mErrorView);\n\t\t}\n\t\tViewStub mViewStub = (ViewStub) this.findViewById(R.id.mainframe_error_viewsub_id);\n\t\tfinal int index = this.indexOfChild(mViewStub);\n\t\tthis.removeViewInLayout(mViewStub);\n\t\tfinal ViewGroup.LayoutParams layoutParams = getLayoutParams();\n\t\tif (layoutParams != null) {\n\t\t\tthis.addView(this.mErrorLayout = mFrameLayout, index, layoutParams);\n\t\t} else {\n\t\t\tthis.addView(this.mErrorLayout = mFrameLayout, index);\n\t\t}\n\t\tmFrameLayout.setVisibility(View.VISIBLE);\n\t\tif (mClickId != -1) {\n\t\t\tfinal View clickView = mFrameLayout.findViewById(mClickId);\n\t\t\tif (clickView != null) {\n\t\t\t\tclickView.setOnClickListener(new OnClickListener() {\n\t\t\t\t\t@Override\n\t\t\t\t\tpublic void onClick(View v) {\n\t\t\t\t\t\tif (getWebView() != null) {\n\t\t\t\t\t\t\tclickView.setClickable(false);\n\t\t\t\t\t\t\tgetWebView().reload();\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t\treturn;\n\t\t\t} else {\n\t\t\t\tif (LogUtils.isDebug()) {\n\t\t\t\t\tLogUtils.e(TAG, \"ClickView is null , cannot bind accurate view to refresh or reload .\");\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tmFrameLayout.setOnClickListener(new OnClickListener() {\n\t\t\t@Override\n\t\t\tpublic void onClick(View v) {\n\t\t\t\tif (getWebView() != null) {\n\t\t\t\t\tmFrameLayout.setClickable(false);\n\t\t\t\t\tgetWebView().reload();\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t}\n\n\tvoid hideErrorLayout() {\n\t\tView mView = null;\n\t\tif ((mView = this.findViewById(R.id.mainframe_error_container_id)) != null) {\n\t\t\tmView.setVisibility(View.GONE);\n\t\t}\n\t}\n\n\tvoid setErrorView(@NonNull View errorView) {\n\t\tthis.mErrorView = errorView;\n\t}\n\n\tvoid setErrorLayoutRes(@LayoutRes int resLayout, @IdRes int id) {\n\t\tthis.mClickId = id;\n\t\tif (this.mClickId <= 0) {\n\t\t\tthis.mClickId = -1;\n\t\t}\n\t\tthis.mErrorLayoutRes = resLayout;\n\t\tif (this.mErrorLayoutRes <= 0) {\n\t\t\tthis.mErrorLayoutRes = R.layout.agentweb_error_page;\n\t\t}\n\t}\n\n\t@Override\n\tpublic AbsAgentWebUIController provide() {\n\t\treturn this.mAgentWebUIController;\n\t}\n\n\n\tvoid bindWebView(WebView view) {\n\t\tif (this.mWebView == null) {\n\t\t\tthis.mWebView = view;\n\t\t}\n\t}\n\n\tWebView getWebView() {\n\t\treturn this.mWebView;\n\t}\n\n}\n"
  },
  {
    "path": "agentweb-core/src/main/java/com/just/agentweb/WebSecurityCheckLogic.java",
    "content": "/*\n * Copyright (C)  Justson(https://github.com/Justson/AgentWeb)\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.just.agentweb;\n\nimport androidx.collection.ArrayMap;\nimport android.webkit.WebView;\n\n\n/**\n * @author cenxiaozhong\n */\npublic interface WebSecurityCheckLogic {\n    void dealHoneyComb(WebView view);\n    void dealJsInterface(ArrayMap<String, Object> objects,AgentWeb.SecurityType securityType);\n}\n"
  },
  {
    "path": "agentweb-core/src/main/java/com/just/agentweb/WebSecurityController.java",
    "content": "/*\n * Copyright (C)  Justson(https://github.com/Justson/AgentWeb)\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.just.agentweb;\n\n/**\n * @author cenxiaozhong\n */\npublic interface WebSecurityController<T> {\n    void check(T t);\n}\n"
  },
  {
    "path": "agentweb-core/src/main/java/com/just/agentweb/WebSecurityControllerImpl.java",
    "content": "/*\n * Copyright (C)  Justson(https://github.com/Justson/AgentWeb)\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.just.agentweb;\n\nimport android.os.Build;\nimport androidx.collection.ArrayMap;\nimport android.webkit.WebView;\n\n\n/**\n * @author cenxiaozhong\n */\npublic class WebSecurityControllerImpl implements WebSecurityController<WebSecurityCheckLogic> {\n\n\tprivate WebView mWebView;\n\tprivate ArrayMap<String, Object> mMap;\n\tprivate AgentWeb.SecurityType mSecurityType;\n\n\tpublic WebSecurityControllerImpl(WebView view, ArrayMap<String, Object> map, AgentWeb.SecurityType securityType) {\n\t\tthis.mWebView = view;\n\t\tthis.mMap = map;\n\t\tthis.mSecurityType = securityType;\n\t}\n\n\t@Override\n\tpublic void check(WebSecurityCheckLogic webSecurityCheckLogic) {\n\t\tif (Build.VERSION.SDK_INT > Build.VERSION_CODES.HONEYCOMB) {\n\t\t\twebSecurityCheckLogic.dealHoneyComb(mWebView);\n\t\t}\n\t\tif (mMap != null && mSecurityType == AgentWeb.SecurityType.STRICT_CHECK && !mMap.isEmpty()) {\n\t\t\twebSecurityCheckLogic.dealJsInterface(mMap, mSecurityType);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "agentweb-core/src/main/java/com/just/agentweb/WebSecurityLogicImpl.java",
    "content": "/*\n * Copyright (C)  Justson(https://github.com/Justson/AgentWeb)\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.just.agentweb;\n\nimport android.annotation.TargetApi;\nimport android.os.Build;\nimport androidx.collection.ArrayMap;\nimport android.webkit.WebView;\n\n\n/**\n * @author cenxiaozhong\n */\npublic class WebSecurityLogicImpl implements WebSecurityCheckLogic {\n    private String TAG = this.getClass().getSimpleName();\n    private int webviewType;\n\n    public static WebSecurityLogicImpl getInstance(int webViewType) {\n        return new WebSecurityLogicImpl(webViewType);\n    }\n\n    public WebSecurityLogicImpl(int webViewType) {\n        this.webviewType = webViewType;\n    }\n\n    @TargetApi(Build.VERSION_CODES.HONEYCOMB)\n    @Override\n    public void dealHoneyComb(WebView view) {\n        if (Build.VERSION_CODES.HONEYCOMB > Build.VERSION.SDK_INT || Build.VERSION.SDK_INT > Build.VERSION_CODES.JELLY_BEAN_MR1) {\n            return;\n        }\n        view.removeJavascriptInterface(\"searchBoxJavaBridge_\");\n        view.removeJavascriptInterface(\"accessibility\");\n        view.removeJavascriptInterface(\"accessibilityTraversal\");\n    }\n\n    @Override\n    public void dealJsInterface(ArrayMap<String, Object> objects, AgentWeb.SecurityType securityType) {\n        if (securityType == AgentWeb.SecurityType.STRICT_CHECK\n                && this.webviewType != AgentWebConfig.WEBVIEW_AGENTWEB_SAFE_TYPE\n                && Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) {\n            LogUtils.e(TAG, \"Give up all inject objects\");\n            objects.clear();\n            objects = null;\n            System.gc();\n        }\n    }\n}\n"
  },
  {
    "path": "agentweb-core/src/main/java/com/just/agentweb/WebViewClient.java",
    "content": "package com.just.agentweb;\n\n/**\n * @author cenxiaozhong\n * @date 2019/4/13\n * @since 1.0.0\n */\npublic class WebViewClient extends MiddlewareWebClientBase {\n\tpublic WebViewClient() {\n\t}\n\n}\n"
  },
  {
    "path": "agentweb-core/src/main/java/com/just/agentweb/WebViewClientDelegate.java",
    "content": "/*\n * Copyright (C)  Justson(https://github.com/Justson/AgentWeb)\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.just.agentweb;\n\nimport android.graphics.Bitmap;\nimport android.net.http.SslError;\nimport android.os.Message;\nimport android.view.KeyEvent;\nimport android.webkit.ClientCertRequest;\nimport android.webkit.HttpAuthHandler;\nimport android.webkit.SslErrorHandler;\nimport android.webkit.WebResourceError;\nimport android.webkit.WebResourceRequest;\nimport android.webkit.WebResourceResponse;\nimport android.webkit.WebView;\nimport android.webkit.WebViewClient;\n\n/**\n * @author cenxiaozhong\n * @update WebViewClientDelegate\n * @date 2017/5/28\n */\npublic class WebViewClientDelegate extends WebViewClient {\n\n    private WebViewClient mDelegate;\n    private static final String TAG = WebViewClientDelegate.class.getSimpleName();\n\n    WebViewClientDelegate(WebViewClient client) {\n        this.mDelegate = client;\n    }\n\n    protected WebViewClient getDelegate() {\n        return mDelegate;\n    }\n\n    void setDelegate(WebViewClient delegate) {\n        this.mDelegate = delegate;\n    }\n\n    @Deprecated\n    @Override\n    public boolean shouldOverrideUrlLoading(WebView view, String url) {\n        if (mDelegate != null) {\n            return mDelegate.shouldOverrideUrlLoading(view, url);\n        }\n        return super.shouldOverrideUrlLoading(view, url);\n    }\n\n    @Override\n    public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {\n        if (mDelegate != null) {\n            return mDelegate.shouldOverrideUrlLoading(view, request);\n        }\n        return super.shouldOverrideUrlLoading(view, request);\n    }\n\n    @Override\n    public void onPageStarted(WebView view, String url, Bitmap favicon) {\n        if (mDelegate != null) {\n            mDelegate.onPageStarted(view, url, favicon);\n            return;\n        }\n        super.onPageStarted(view, url, favicon);\n    }\n\n    @Override\n    public void onPageFinished(WebView view, String url) {\n        if (mDelegate != null) {\n            mDelegate.onPageFinished(view, url);\n            return;\n        }\n        super.onPageFinished(view, url);\n    }\n\n    @Override\n    public void onLoadResource(WebView view, String url) {\n        if (mDelegate != null) {\n            mDelegate.onLoadResource(view, url);\n            return;\n        }\n        super.onLoadResource(view, url);\n    }\n\n    @Override\n    public void onPageCommitVisible(WebView view, String url) {\n        if (mDelegate != null) {\n            mDelegate.onPageCommitVisible(view, url);\n            return;\n        }\n        super.onPageCommitVisible(view, url);\n    }\n\n    @Override\n    @Deprecated\n    public WebResourceResponse shouldInterceptRequest(WebView view,\n                                                      String url) {\n        if (mDelegate != null) {\n            return mDelegate.shouldInterceptRequest(view, url);\n        }\n        return super.shouldInterceptRequest(view, url);\n    }\n\n    @Override\n    public WebResourceResponse shouldInterceptRequest(WebView view,\n                                                      WebResourceRequest request) {\n        if (mDelegate != null) {\n            return mDelegate.shouldInterceptRequest(view, request);\n        }\n        return super.shouldInterceptRequest(view, request);\n    }\n\n    @Override\n    @Deprecated\n    public void onTooManyRedirects(WebView view, Message cancelMsg,\n                                   Message continueMsg) {\n        if (mDelegate != null) {\n            mDelegate.onTooManyRedirects(view, cancelMsg, continueMsg);\n            return;\n        }\n        super.onTooManyRedirects(view, cancelMsg, continueMsg);\n    }\n\n    @Override\n    @Deprecated\n    public void onReceivedError(WebView view, int errorCode,\n                                String description, String failingUrl) {\n        if (mDelegate != null) {\n            mDelegate.onReceivedError(view, errorCode, description, failingUrl);\n            return;\n        }\n        super.onReceivedError(view, errorCode, description, failingUrl);\n    }\n\n    @Override\n    public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) {\n        if (mDelegate != null) {\n            mDelegate.onReceivedError(view, request, error);\n            return;\n        }\n        super.onReceivedError(view, request, error);\n    }\n\n    @Override\n    public void onReceivedHttpError(\n            WebView view, WebResourceRequest request, WebResourceResponse errorResponse) {\n        if (mDelegate != null) {\n            mDelegate.onReceivedHttpError(view, request, errorResponse);\n            return;\n        }\n        super.onReceivedHttpError(view, request, errorResponse);\n    }\n\n    @Override\n    public void onFormResubmission(WebView view, Message dontResend,\n                                   Message resend) {\n        if (mDelegate != null) {\n            mDelegate.onFormResubmission(view, dontResend, resend);\n            return;\n        }\n        super.onFormResubmission(view, dontResend, resend);\n    }\n\n\n    @Override\n    public void doUpdateVisitedHistory(WebView view, String url,\n                                       boolean isReload) {\n        if (mDelegate != null) {\n            mDelegate.doUpdateVisitedHistory(view, url, isReload);\n            return;\n        }\n        super.doUpdateVisitedHistory(view, url, isReload);\n    }\n\n    @Override\n    public void onReceivedSslError(WebView view, SslErrorHandler handler,\n                                   SslError error) {\n        if (mDelegate != null) {\n            mDelegate.onReceivedSslError(view, handler, error);\n            return;\n        }\n        super.onReceivedSslError(view, handler, error);\n    }\n\n    @Override\n    public void onReceivedClientCertRequest(WebView view, ClientCertRequest request) {\n        if (mDelegate != null) {\n            mDelegate.onReceivedClientCertRequest(view, request);\n            return;\n        }\n        super.onReceivedClientCertRequest(view, request);\n    }\n\n    @Override\n    public void onReceivedHttpAuthRequest(WebView view,\n                                          HttpAuthHandler handler, String host, String realm) {\n        if (mDelegate != null) {\n            mDelegate.onReceivedHttpAuthRequest(view, handler, host, realm);\n            return;\n        }\n        super.onReceivedHttpAuthRequest(view, handler, host, realm);\n    }\n\n    @Override\n    public boolean shouldOverrideKeyEvent(WebView view, KeyEvent event) {\n        if (mDelegate != null) {\n            return mDelegate.shouldOverrideKeyEvent(view, event);\n        }\n        return super.shouldOverrideKeyEvent(view, event);\n    }\n\n    @Override\n    public void onUnhandledKeyEvent(WebView view, KeyEvent event) {\n        if (mDelegate != null) {\n            mDelegate.onUnhandledKeyEvent(view, event);\n            return;\n        }\n        super.onUnhandledKeyEvent(view, event);\n    }\n\n\n    @Override\n    public void onScaleChanged(WebView view, float oldScale, float newScale) {\n        if (mDelegate != null) {\n            mDelegate.onScaleChanged(view, oldScale, newScale);\n            return;\n        }\n        super.onScaleChanged(view, oldScale, newScale);\n    }\n\n    @Override\n    public void onReceivedLoginRequest(WebView view, String realm,\n                                       String account, String args) {\n        if (mDelegate != null) {\n            mDelegate.onReceivedLoginRequest(view, realm, account, args);\n            return;\n        }\n        super.onReceivedLoginRequest(view, realm, account, args);\n    }\n}\n"
  },
  {
    "path": "agentweb-core/src/main/res/layout/agentweb_error_page.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:background=\"@color/white\"\n    >\n\n\n    <TextView\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:text=\"@string/agentweb_default_page_error\"\n        android:textSize=\"22sp\"\n        android:gravity=\"center\"\n        android:textColor=\"#696969\"\n        />\n\n</LinearLayout>\n"
  },
  {
    "path": "agentweb-core/src/main/res/values/colors.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <color name=\"black\">#000000</color>\n    <color name=\"white\">#ffffff</color>\n    <color name=\"select_color\">#2e2e32</color>\n</resources>"
  },
  {
    "path": "agentweb-core/src/main/res/values/ids.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <item name=\"web_parent_layout_id\" type=\"id\"></item>\n    <item name=\"agentweb_webview_id\" type=\"id\"></item>\n    <item name=\"mainframe_error_viewsub_id\" type=\"id\"></item>\n    <item name=\"mainframe_error_container_id\" type=\"id\"></item>\n</resources>"
  },
  {
    "path": "agentweb-core/src/main/res/values/strings.xml",
    "content": "<resources>\n    <string name=\"agentweb_download_task_has_been_exist\">The task already exists, do not repeat click to download!</string>\n    <string name=\"agentweb_tips\">Note</string>\n    <string name=\"agentweb_honeycomblow\">Wi-Fi disconnected. Continue the download via mobile data network?</string>\n    <string name=\"agentweb_download\">Download</string>\n    <string name=\"agentweb_cancel\">Cancel</string>\n    <string name=\"agentweb_download_fail\">Download failed!</string>\n    <string name=\"agentweb_current_downloading_progress\">Downloading:%s</string>\n    <string name=\"agentweb_current_downloaded_length\">downloaded:%s</string>\n    <string name=\"agentweb_trickter\">You have a new notice</string>\n    <string name=\"agentweb_file_download\">Download</string>\n    <string name=\"agentweb_click_open\">Tap to continue</string>\n    <string name=\"agentweb_coming_soon_download\">Coming soon to download the file</string>\n    <string name=\"agentweb_camera\">Camera</string>\n    <string name=\"agentweb_file_chooser\">Files</string>\n    <string name=\"agentweb_loading\">Loading ...</string>\n    <string name=\"agentweb_leave_app_and_go_other_page\">leaving %s and opening another app?</string>\n    <string name=\"agentweb_leave\">Go away</string>\n    <string name=\"agentweb_max_file_length_limit\">The selected file can not be larger than %s MB</string>\n    <string name=\"agentweb_default_page_error\">error~</string>\n    <string name=\"agentweb_message_show_ssl_untrusted\">The certificate authority is not trusted.</string>\n    <string name=\"agentweb_message_show_ssl_expired\">The certificate has expired.</string>\n    <string name=\"agentweb_message_show_ssl_hostname_mismatch\">The certificate Hostname mismatch.</string>\n    <string name=\"agentweb_message_show_ssl_not_yet_valid\">The certificate is not yet valid.</string>\n    <string name=\"agentweb_message_show_ssl_error\">SSL Certificate error.</string>\n    <string name=\"agentweb_message_show_continue\">Do you want to continue anyway?</string>\n    <string name=\"agentweb_title_ssl_error\">SSL Certificate Error</string>\n    <string name=\"agentweb_continue\">Continue</string>\n</resources>\n"
  },
  {
    "path": "agentweb-core/src/main/res/values/style.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<resources xmlns:tools=\"http://schemas.android.com/tools\">\n\n    <style name=\"actionActivity\" parent=\"@android:style/Theme.Translucent.NoTitleBar\">\n        <item name=\"android:statusBarColor\" tools:targetApi=\"lollipop\">@android:color/transparent</item>\n    </style>\n\n</resources>"
  },
  {
    "path": "agentweb-core/src/main/res/values-zh/strings.xml",
    "content": "<resources>\n    <string name=\"agentweb_download_task_has_been_exist\">该任务已经存在 ， 请勿重复点击下载!</string>\n    <string name=\"agentweb_tips\">提示</string>\n    <string name=\"agentweb_honeycomblow\">您正在使用手机流量 ， 继续下载该文件吗?</string>\n    <string name=\"agentweb_download\">下载</string>\n    <string name=\"agentweb_cancel\">取消</string>\n    <string name=\"agentweb_download_fail\">下载失败!</string>\n    <string name=\"agentweb_current_downloading_progress\">当前进度:%s</string>\n    <string name=\"agentweb_current_downloaded_length\">已下载:%s</string>\n    <string name=\"agentweb_trickter\">您有一条新通知</string>\n    <string name=\"agentweb_file_download\">文件下载</string>\n    <string name=\"agentweb_click_open\">点击打开</string>\n    <string name=\"agentweb_coming_soon_download\">即将开始下载文件</string>\n    <string name=\"agentweb_camera\">相机</string>\n    <string name=\"agentweb_file_chooser\">文件</string>\n    <string name=\"agentweb_loading\">加载中 ...</string>\n    <string name=\"agentweb_leave_app_and_go_other_page\">您需要离开%s前往其他应用吗？</string>\n    <string name=\"agentweb_leave\">离开</string>\n    <string name=\"agentweb_message_show_ssl_untrusted\">证书颁发机构不受信任，</string>\n    <string name=\"agentweb_max_file_length_limit\">选择的文件不能大于%sMB</string>\n    <string name=\"agentweb_default_page_error\">出错啦! 点击空白处刷新 ~</string>\n    <string name=\"agentweb_message_show_ssl_expired\">证书已过期，</string>\n    <string name=\"agentweb_message_show_ssl_hostname_mismatch\">证书主机名不匹配，</string>\n    <string name=\"agentweb_message_show_ssl_not_yet_valid\">该证书尚未生效，</string>\n    <string name=\"agentweb_message_show_ssl_error\">SSL 认证错误，</string>\n    <string name=\"agentweb_message_show_continue\">你确定要访问该网页吗？</string>\n    <string name=\"agentweb_title_ssl_error\">SSL 认证错误</string>\n    <string name=\"agentweb_continue\">确定</string>\n</resources>\n"
  },
  {
    "path": "agentweb-core/src/main/res/xml/web_files_public.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<paths>\n\n    <external-cache-path\n        name=\"agenweb_files\"\n        path=\"agentweb-cache\"/>\n</paths>"
  },
  {
    "path": "agentweb-core/src/test/java/com/just/agentweb/ExampleUnitTest.java",
    "content": "package com.just.agentweb;\n\nimport org.junit.Test;\n\nimport static org.junit.Assert.*;\n\n/**\n * Example local unit test, which will execute on the development machine (host).\n *\n * @see <a href=\"http://d.android.com/tools/testing\">Testing documentation</a>\n */\npublic class ExampleUnitTest {\n    @Test\n    public void addition_isCorrect() throws Exception {\n        assertEquals(4, 2 + 2);\n    }\n}"
  },
  {
    "path": "agentweb-filechooser/.gitignore",
    "content": "/build\n"
  },
  {
    "path": "agentweb-filechooser/build.gradle",
    "content": "apply plugin: 'com.android.library'\napply plugin: 'maven-publish'\n\nandroid {\n    compileSdk COMPILE_SDK_VERSION.toInteger()\n\n    defaultConfig {\n        minSdkVersion 14\n        namespace 'com.just.agentweb.filechooser'\n        targetSdkVersion TARGET_SDK_VERSION.toInteger()\n        versionCode 2\n        versionName VERSION_NAME\n\n        testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'\n\n    }\n\n    compileOptions {\n        sourceCompatibility JavaVersion.VERSION_1_8\n        targetCompatibility JavaVersion.VERSION_1_8\n    }\n\n    buildTypes {\n        release {\n            minifyEnabled false\n            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'\n        }\n    }\n    lintOptions{\n        abortOnError false\n    }\n\n}\n\ndependencies {\n    implementation fileTree(include: ['*.jar'], dir: 'libs')\n    testImplementation 'junit:junit:4.12'\n    androidTestImplementation('androidx.test.espresso:espresso-core:3.1.0', {\n        exclude group: 'com.android.support', module: 'support-annotations'\n    })\n    testImplementation 'junit:junit:4.12'\n    compileOnly 'com.google.android.material:material:1.0.0'\n    compileOnly 'androidx.legacy:legacy-support-v4:1.0.0'\n    implementation project(':agentweb-core')\n}\n\nafterEvaluate {\n    publishing {\n        publications {\n            // Creates a Maven publication called \"release\".\n            release(MavenPublication) {\n                groupId = 'com.github.Justson.AgentWeb'\n                artifactId = 'agentweb-filechooser'\n                version = 'v5.0.7-androidx'\n            }\n        }\n    }\n}"
  },
  {
    "path": "agentweb-filechooser/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguardFiles setting in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n\n# Uncomment this to preserve the line number information for\n# debugging stack traces.\n#-keepattributes SourceFile,LineNumberTable\n\n# If you keep the line number information, uncomment this to\n# hide the original source file name.\n#-renamesourcefileattribute SourceFile\n-keep class com.just.agentweb.** {\n    *;\n}\n-dontwarn com.just.agentweb.**"
  },
  {
    "path": "agentweb-filechooser/src/androidTest/java/com/just/agentweb/filechooser/ExampleInstrumentedTest.java",
    "content": "package com.just.agentweb.filechooser;\n\nimport android.content.Context;\nimport androidx.test.platform.app.InstrumentationRegistry;\nimport androidx.test.ext.junit.runners.AndroidJUnit4;\n\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\n\nimport static org.junit.Assert.*;\n\n/**\n * Instrumented test, which will execute on an Android device.\n *\n * @see <a href=\"http://d.android.com/tools/testing\">Testing documentation</a>\n */\n@RunWith(AndroidJUnit4.class)\npublic class ExampleInstrumentedTest {\n    @Test\n    public void useAppContext() throws Exception {\n        // Context of the app under test.\n        Context appContext = InstrumentationRegistry.getTargetContext();\n\n        assertEquals(\"com.just.agentweb.filechooser.test\", appContext.getPackageName());\n    }\n}\n"
  },
  {
    "path": "agentweb-filechooser/src/main/AndroidManifest.xml",
    "content": "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\" >\n    <!-- To handle the reselection within the app on Android 14 (API level 34) -->\n    <uses-permission android:name=\"android.permission.READ_MEDIA_VISUAL_USER_SELECTED\" />\n    <!-- Devices running Android 13 (API level 33) or higher -->\n    <uses-permission android:name=\"android.permission.READ_MEDIA_IMAGES\" />\n    <uses-permission android:name=\"android.permission.READ_MEDIA_VIDEO\" />\n    <uses-permission android:name=\"android.permission.READ_MEDIA_AUDIO\" />\n</manifest>\n"
  },
  {
    "path": "agentweb-filechooser/src/main/java/com/just/agentweb/filechooser/FileChooser.java",
    "content": "/*\n * Copyright (C)  Justson(https://github.com/Justson/AgentWeb)\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.just.agentweb.filechooser;\n\nimport static com.just.agentweb.AgentActionFragment.KEY_FROM_INTENTION;\nimport static com.just.agentweb.AgentActionFragment.KEY_URI;\nimport static com.just.agentweb.AgentActionFragment.start;\n\nimport android.app.Activity;\nimport android.content.ClipData;\nimport android.content.ContentResolver;\nimport android.content.Intent;\nimport android.net.Uri;\nimport android.os.AsyncTask;\nimport android.os.Build;\nimport android.os.Bundle;\nimport android.os.Handler;\nimport android.os.Message;\nimport android.os.SystemClock;\nimport android.text.TextUtils;\nimport android.util.Base64;\nimport android.util.Log;\nimport android.webkit.ValueCallback;\nimport android.webkit.WebChromeClient;\nimport android.webkit.WebView;\n\nimport androidx.annotation.NonNull;\n\nimport com.just.agentweb.AbsAgentWebUIController;\nimport com.just.agentweb.Action;\nimport com.just.agentweb.AgentActionFragment;\nimport com.just.agentweb.AgentWebConfig;\nimport com.just.agentweb.AgentWebPermissions;\nimport com.just.agentweb.AgentWebUtils;\nimport com.just.agentweb.PermissionInterceptor;\n\nimport org.json.JSONArray;\nimport org.json.JSONObject;\n\nimport java.io.ByteArrayOutputStream;\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.InputStream;\nimport java.lang.ref.WeakReference;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Objects;\nimport java.util.Queue;\nimport java.util.concurrent.CountDownLatch;\nimport java.util.concurrent.Executor;\nimport java.util.concurrent.Executors;\nimport java.util.concurrent.LinkedBlockingQueue;\nimport java.util.concurrent.ThreadPoolExecutor;\n\n/**\n * @author cenxiaozhong\n * @date 2017/5/22\n * @update 4.0.0\n */\npublic class FileChooser {\n    /**\n     * Activity\n     */\n    private final Activity mActivity;\n    /**\n     * ValueCallback\n     */\n    private ValueCallback<Uri> mUriValueCallback;\n    /**\n     * ValueCallback<Uri[]> After LOLLIPOP\n     */\n    private ValueCallback<Uri[]> mUriValueCallbacks;\n    /**\n     * Activity Request Code\n     */\n    public static final int REQUEST_CODE = 0x254;\n    /**\n     * WebChromeClient.FileChooserParams 封装了 Intent ，mAcceptType  等参数\n     */\n    private final WebChromeClient.FileChooserParams mFileChooserParams;\n    /**\n     * 如果是通过 JavaScript 打开文件选择器 ，那么 mJsChannelCallback 不能为空\n     */\n    private JsChannelCallback mJsChannelCallback;\n    /**\n     * 是否为Js Channel\n     */\n    private boolean mJsChannel = false;\n    /**\n     * TAG\n     */\n    private static final String TAG = FileChooser.class.getSimpleName();\n    /**\n     * 当前 WebView\n     */\n    private final WebView mWebView;\n    /**\n     * 是否为 Camera State\n     */\n    private boolean mCameraState = false;\n    /**\n     * 是否调用摄像头后  调用的是摄像模式  默认是拍照\n     */\n    private boolean mVideoState = false;\n    /**\n     * 权限拦截\n     */\n    private final PermissionInterceptor mPermissionInterceptor;\n    /**\n     * FROM_INTENTION_CODE 用于表示当前Action\n     */\n    private final int FROM_INTENTION_CODE = 21;\n    /**\n     * 当前 AbsAgentWebUIController\n     */\n    private WeakReference<AbsAgentWebUIController> mAgentWebUIController = null;\n    /**\n     * 选择文件类型\n     */\n    private String mAcceptType = \"*/*\";\n    /**\n     * 修复某些特定手机拍照后，立刻获取照片为空的情况\n     */\n    public static int MAX_WAIT_PHOTO_MS = 8 * 1000;\n\n\n    public FileChooser(Builder builder) {\n\n        this.mActivity = builder.mActivity;\n        this.mUriValueCallback = builder.mUriValueCallback;\n        this.mUriValueCallbacks = builder.mUriValueCallbacks;\n        this.mJsChannel = builder.mJsChannel;\n        this.mFileChooserParams = builder.mFileChooserParams;\n        if (this.mJsChannel) {\n            this.mJsChannelCallback = JsChannelCallback.create(builder.mJsChannelCallback);\n        }\n        this.mWebView = builder.mWebView;\n        this.mPermissionInterceptor = builder.mPermissionInterceptor;\n        this.mAcceptType = builder.mAcceptType;\n        this.mAgentWebUIController = new WeakReference<AbsAgentWebUIController>(AgentWebUtils.getAgentWebUIControllerByWebView(this.mWebView));\n\n    }\n\n\n    public void openFileChooser() {\n        if (!AgentWebUtils.isUIThread()) {\n            AgentWebUtils.runInUiThread(new Runnable() {\n                @Override\n                public void run() {\n                    openFileChooser();\n                }\n            });\n            return;\n        }\n\n        openFileChooserInternal();\n    }\n\n    private void fileChooser() {\n\n        List<String> permission = null;\n        if (AgentWebUtils.getDeniedPermissions(mActivity, AgentWebPermissions.MEDIA).isEmpty()) {\n            chooserAction();\n        } else {\n            Action mAction = Action.createPermissionsAction(AgentWebPermissions.MEDIA);\n            mAction.setFromIntention(FROM_INTENTION_CODE >> 2);\n            mAction.setPermissionListener(mPermissionListener);\n            AgentActionFragment.start(mActivity, mAction);\n        }\n\n\n    }\n\n    private void chooserAction() {\n        Action mAction = new Action();\n        mAction.setAction(Action.ACTION_FILE);\n        mAction.setChooserListener(getChooserListener());\n        try {\n            mAction.setIntent(getFileChooserIntent());\n            AgentActionFragment.start(mActivity, mAction);\n        } catch (Throwable throwable) {\n            if (AgentWebConfig.DEBUG) {\n                throwable.printStackTrace();\n            }\n        }\n\n    }\n\n    private Intent getFileChooserIntent() {\n        Intent mIntent = null;\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && mFileChooserParams != null && (mIntent = mFileChooserParams.createIntent()) != null) {\n            // 多选\n            if (mFileChooserParams.getMode() == WebChromeClient.FileChooserParams.MODE_OPEN_MULTIPLE) {\n                mIntent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);\n}\n            //\t\t\tmIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);\n\n            if (mFileChooserParams.getAcceptTypes() != null && mFileChooserParams.getAcceptTypes().length > 1) {\n                mIntent.putExtra(Intent.EXTRA_MIME_TYPES, mFileChooserParams.getAcceptTypes());\n            }\n            if (Objects.equals(mIntent.getAction(), Intent.ACTION_GET_CONTENT)) {\n                mIntent.setAction(Intent.ACTION_OPEN_DOCUMENT);\n            }\n            return mIntent;\n        }\n\n        Intent i = new Intent();\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {\n            i.setAction(Intent.ACTION_OPEN_DOCUMENT);\n        } else {\n            i.setAction(Intent.ACTION_GET_CONTENT);\n        }\n        i.addCategory(Intent.CATEGORY_OPENABLE);\n        if (TextUtils.isEmpty(this.mAcceptType)) {\n            i.setType(\"*/*\");\n        } else {\n            i.setType(this.mAcceptType);\n        }\n        i.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);\n        return mIntent = Intent.createChooser(i, \"\");\n    }\n\n    private AgentActionFragment.ChooserListener getChooserListener() {\n        return new AgentActionFragment.ChooserListener() {\n            @Override\n            public void onChoiceResult(int requestCode, int resultCode, Intent data) {\n\n                onIntentResult(requestCode, resultCode, data);\n            }\n        };\n    }\n\n\n    private void openFileChooserInternal() {\n        boolean needVideo = false;\n        // 在此支持视频拍摄\n        // 是否直接打开文件选择器\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && this.mFileChooserParams != null && this.mFileChooserParams.getAcceptTypes() != null) {\n            boolean needCamera = false;\n            String[] types = this.mFileChooserParams.getAcceptTypes();\n            for (String typeTmp : types) {\n                if (TextUtils.isEmpty(typeTmp)) {\n                    continue;\n                }\n                if (typeTmp.contains(\"*/\") || typeTmp.contains(\"image/\")) {  //这是拍照模式\n                    needCamera = true;\n                    break;\n                }\n\n                if (typeTmp.contains(\"video/\")) {  //调用摄像机拍摄  这是录像模式\n                    needCamera = true;\n                    mVideoState = true;\n                }\n            }\n            if (!needCamera && !needVideo) {\n                chooserAction();\n                return;\n            }\n        }\n        if (!TextUtils.isEmpty(this.mAcceptType) && !this.mAcceptType.contains(\"*/\") && !this.mAcceptType.contains(\"image/\")) {\n            chooserAction();\n            return;\n        }\n\n        if (this.mAgentWebUIController.get() != null) {\n            this.mAgentWebUIController\n                    .get()\n                    .onSelectItemsPrompt(this.mWebView, mWebView.getUrl(),\n                            new String[]{mActivity.getString(com.just.agentweb.R.string.agentweb_camera),\n                                    mActivity.getString(com.just.agentweb.R.string.agentweb_file_chooser)}, getCallBack());\n        }\n\n    }\n\n\n    private Handler.Callback getCallBack() {\n        return new Handler.Callback() {\n            @Override\n            public boolean handleMessage(Message msg) {\n                switch (msg.what) {\n                    case 0:\n                        mCameraState = true;\n                        onCameraAction();\n                        break;\n                    case 1:\n                        mCameraState = false;\n                        fileChooser();\n                        break;\n                    default:\n                        cancel();\n                        break;\n                }\n                return true;\n            }\n        };\n    }\n\n\n    private void onCameraAction() {\n        if (mActivity == null) {\n            return;\n        }\n\n        if (mPermissionInterceptor != null) {\n            if (mPermissionInterceptor.intercept(FileChooser.this.mWebView.getUrl(), AgentWebPermissions.CAMERA, \"camera\")) {\n                cancel();\n                return;\n            }\n        }\n        Action mAction = new Action();\n        List<String> deniedPermissions = null;\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !(deniedPermissions = checkNeedPermission()).isEmpty()) {\n            mAction.setAction(Action.ACTION_PERMISSION);\n            mAction.setPermissions(deniedPermissions.toArray(new String[]{}));\n            mAction.setFromIntention(FROM_INTENTION_CODE >> 3);\n            mAction.setPermissionListener(this.mPermissionListener);\n            start(mActivity, mAction);\n        } else {\n            openCameraAction();\n        }\n\n    }\n\n    private List<String> checkNeedPermission() {\n\n        List<String> deniedPermissions = new ArrayList<>();\n\n        if (!AgentWebUtils.hasPermission(mActivity, AgentWebPermissions.CAMERA)) {\n            deniedPermissions.add(AgentWebPermissions.CAMERA[0]);\n        }\n        if (!AgentWebUtils.hasPermission(mActivity, AgentWebPermissions.MEDIA)) {\n            deniedPermissions.addAll(Arrays.asList(AgentWebPermissions.MEDIA));\n        }\n        return deniedPermissions;\n    }\n\n    private void openCameraAction() {\n        Action mAction = new Action();\n        if (mVideoState) {  //调用摄像\n            mAction.setAction(Action.ACTION_VIDEO);\n        } else {\n            mAction.setAction(Action.ACTION_CAMERA);\n        }\n        mAction.setChooserListener(this.getChooserListener());\n        AgentActionFragment.start(mActivity, mAction);\n    }\n\n    private AgentActionFragment.PermissionListener mPermissionListener = new AgentActionFragment.PermissionListener() {\n\n        @Override\n        public void onRequestPermissionsResult(@NonNull String[] permissions, @NonNull int[] grantResults, Bundle extras) {\n\n            boolean tag = true;\n            tag = AgentWebUtils.hasPermission(mActivity, Arrays.asList(permissions)) ? true : false;\n            permissionResult(tag, extras.getInt(KEY_FROM_INTENTION));\n\n        }\n    };\n\n    private void permissionResult(boolean grant, int fromIntention) {\n        if (fromIntention == FROM_INTENTION_CODE >> 2) {\n            if (grant) {\n                chooserAction();\n            } else {\n                cancel();\n\n                if (null != mAgentWebUIController.get()) {\n                    mAgentWebUIController\n                            .get()\n                            .onPermissionsDeny(\n                                    AgentWebPermissions.MEDIA,\n                                    AgentWebPermissions.ACTION_MEDIA,\n                                    \"Open file chooser\");\n                }\n            }\n        } else if (fromIntention == FROM_INTENTION_CODE >> 3) {\n            if (grant) {\n                openCameraAction();\n            } else {\n                cancel();\n                if (null != mAgentWebUIController.get()) {\n                    mAgentWebUIController\n                            .get()\n                            .onPermissionsDeny(\n                                    AgentWebPermissions.CAMERA,\n                                    AgentWebPermissions.ACTION_CAMERA,\n                                    \"Take photo\");\n                }\n            }\n        }\n\n\n    }\n\n    public void onIntentResult(int requestCode, int resultCode, Intent data) {\n\n        if (REQUEST_CODE != requestCode) {\n            return;\n        }\n\n        //用户已经取消\n        if (resultCode == Activity.RESULT_CANCELED || data == null) {\n            cancel();\n            return;\n        }\n\n        if (resultCode != Activity.RESULT_OK) {\n            cancel();\n            return;\n        }\n\n        //通过Js获取文件\n        if (mJsChannel) {\n            convertFileAndCallback(mCameraState ? new Uri[]{data.getParcelableExtra(KEY_URI)} : processData(data));\n            return;\n        }\n\n        //5.0以上系统通过input标签获取文件\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {\n            aboveLollipopCheckFilesAndCallback(mCameraState ? new Uri[]{data.getParcelableExtra(KEY_URI)} : processData(data), mCameraState);\n            return;\n        }\n\n\n        //4.4以下系统通过input标签获取文件\n        if (mUriValueCallback == null) {\n            cancel();\n            return;\n        }\n\n        if (mCameraState) {\n//            mUriValueCallback.onReceiveValue((Uri) data.getParcelableExtra(KEY_URI));\n            fileCompressAndValuesCallback((Uri) data.getParcelableExtra(KEY_URI), mUriValueCallback);\n            mUriValueCallback = null;\n        } else {\n            belowLollipopUriCallback(data);\n        }\n\n        /*if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)\n            aboveLollipopCheckFilesAndCallback(mCameraState ? new Uri[]{data.getParcelableExtra(KEY_URI)} : processData(data));\n        else if (mJsChannel)\n            convertFileAndCallback(mCameraState ? new Uri[]{data.getParcelableExtra(KEY_URI)} : processData(data));\n        else {\n            if (mCameraState && mUriValueCallback != null)\n                mUriValueCallback.onReceiveValue((Uri) data.getParcelableExtra(KEY_URI));\n            else\n                belowLollipopUriCallback(data);\n        }*/\n\n\n    }\n\n    private void cancel() {\n        if (mJsChannel) {\n            mJsChannelCallback.call(null);\n            return;\n        }\n        if (mUriValueCallback != null) {\n            try {\n                mUriValueCallback.onReceiveValue(null);\n                mUriValueCallback = null;\n            } catch (Throwable ignored) {\n                if (AgentWebConfig.DEBUG) {\n                    ignored.printStackTrace();\n                }\n            }\n        }\n        if (mUriValueCallbacks != null) {\n            try {\n                mUriValueCallbacks.onReceiveValue(null);\n                mUriValueCallbacks = null;\n            } catch (Throwable ignored) {\n                if (AgentWebConfig.DEBUG) {\n                    ignored.printStackTrace();\n                }\n            }\n\n        }\n        return;\n    }\n\n\n    private void belowLollipopUriCallback(Intent data) {\n\n\n        if (data == null) {\n            if (mUriValueCallback != null) {\n                mUriValueCallback.onReceiveValue(Uri.EMPTY);\n                mUriValueCallback = null;\n            }\n            return;\n        }\n        Uri mUri = data.getData();\n        if (mUriValueCallback != null) {\n//            mUriValueCallback.onReceiveValue(mUri);\n            fileCompressAndValuesCallback(mUri, mUriValueCallback);\n            mUriValueCallback = null;\n        }\n\n    }\n\n    private Uri[] processData(Intent data) {\n\n        Uri[] datas = null;\n        if (data == null) {\n            return datas;\n        }\n        String target = data.getDataString();\n        if (!TextUtils.isEmpty(target)) {\n            return datas = new Uri[]{Uri.parse(target)};\n        }\n        ClipData mClipData = null;\n        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN) {\n            mClipData = data.getClipData();\n        }\n        if (mClipData != null && mClipData.getItemCount() > 0) {\n            datas = new Uri[mClipData.getItemCount()];\n            for (int i = 0; i < mClipData.getItemCount(); i++) {\n\n                ClipData.Item mItem = mClipData.getItemAt(i);\n                datas[i] = mItem.getUri();\n\n            }\n        }\n        return datas;\n\n\n    }\n\n    private void convertFileAndCallback(final Uri[] uris) {\n\n        String[] paths = null;\n        if (uris == null || uris.length == 0 || (paths = AgentWebUtils.uriToPath(mActivity, uris)) == null || paths.length == 0) {\n            mJsChannelCallback.call(null);\n            return;\n        }\n        FileCompressor.getInstance().fileCompress(\"customize\", uris, new ValueCallback<Uri[]>() {\n            @Override\n            public void onReceiveValue(Uri[] value) {\n                String[] compressFilePath = AgentWebUtils.uriToPath(mActivity, value);\n                if (compressFilePath == null || compressFilePath.length == 0) {\n                    mJsChannelCallback.call(null);\n                    return;\n                }\n                int sum = 0;\n                for (String path : compressFilePath) {\n                    if (TextUtils.isEmpty(path)) {\n                        continue;\n                    }\n                    File mFile = new File(path);\n                    if (!mFile.exists()) {\n                        continue;\n                    }\n                    sum += mFile.length();\n                }\n\n                if (sum > AgentWebConfig.MAX_FILE_LENGTH) {\n                    if (mAgentWebUIController.get() != null) {\n                        mAgentWebUIController.get().onShowMessage(mActivity.getString(com.just.agentweb.R.string.agentweb_max_file_length_limit, (AgentWebConfig.MAX_FILE_LENGTH / 1024 / 1024) + \"\"), \"convertFileAndCallback\");\n                    }\n                    mJsChannelCallback.call(null);\n                    return;\n                }\n\n                AsyncTask.THREAD_POOL_EXECUTOR.execute(new CovertFileThread(mJsChannelCallback, compressFilePath));\n            }\n        });\n\n\n    }\n\n    private static void fileCompressAndValuesCallback(final Uri[] datas, final ValueCallback<Uri[]> valueCallback) {\n        FileCompressor.getInstance().fileCompress(\"system\", datas, new ValueCallback<Uri[]>() {\n\n            @Override\n            public void onReceiveValue(Uri[] value) {\n                if (valueCallback != null) {\n                    valueCallback.onReceiveValue(value);\n                }\n            }\n        });\n    }\n\n    private static void fileCompressAndValuesCallback(final Uri datas, final ValueCallback<Uri> valueCallback) {\n        FileCompressor.getInstance().fileCompress(\"system\", new Uri[]{datas}, new ValueCallback<Uri[]>() {\n\n            @Override\n            public void onReceiveValue(Uri[] value) {\n                if (valueCallback != null) {\n                    if (value != null && value.length > 0) {\n                        valueCallback.onReceiveValue(value[0]);\n                    } else {\n                        valueCallback.onReceiveValue(Uri.EMPTY);\n                    }\n                }\n            }\n        });\n    }\n\n    /**\n     * 经过多次的测试，在小米 MIUI ， 华为 ，多部分为 Android 6.0 左右系统相机获取到的文件\n     * length为0 ，导致前端 ，获取到的文件， 作预览的时候不正常 ，等待5S左右文件又正常了 ， 所以这里做了阻塞等待处理，\n     *\n     * @param datas\n     * @param isCamera\n     */\n    private void aboveLollipopCheckFilesAndCallback(final Uri[] datas, boolean isCamera) {\n        if (mUriValueCallbacks == null) {\n            return;\n        }\n        if (null != datas && datas.length > 0 && Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {\n            ContentResolver contentResolver = mActivity.getContentResolver();\n            final int takeFlags = (Intent.FLAG_GRANT_READ_URI_PERMISSION\n                    | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);\n            for (int i = 0; i < datas.length; i++) {\n                try {\n                    contentResolver.takePersistableUriPermission(datas[i], takeFlags);\n                } catch (Throwable throwable) {\n                    if (AgentWebConfig.DEBUG) {\n                        throwable.printStackTrace();\n                    }\n                }\n            }\n        }\n        if (!isCamera) {\n            fileCompressAndValuesCallback(datas == null ? new Uri[]{} : datas, mUriValueCallbacks);\n//            mUriValueCallbacks.onReceiveValue(datas == null ? new Uri[]{} : datas);\n            mUriValueCallbacks = null;\n            return;\n        }\n\n        if (mAgentWebUIController.get() == null) {\n            mUriValueCallbacks.onReceiveValue(null);\n            mUriValueCallbacks = null;\n            return;\n        }\n        String[] paths = AgentWebUtils.uriToPath(mActivity, datas);\n        if (paths == null || paths.length == 0) {\n            mUriValueCallbacks.onReceiveValue(null);\n            mUriValueCallbacks = null;\n            return;\n        }\n        final String path = paths[0];\n        mAgentWebUIController.get().onLoading(mActivity.getString(com.just.agentweb.R.string.agentweb_loading));\n        AsyncTask.THREAD_POOL_EXECUTOR.execute(new WaitPhotoRunnable(path, new AboveLCallback(mUriValueCallbacks, datas, mAgentWebUIController)));\n        mUriValueCallbacks = null;\n\n    }\n\n    private static final class AboveLCallback implements Handler.Callback {\n        private ValueCallback<Uri[]> mValueCallback;\n        private Uri[] mUris;\n        private WeakReference<AbsAgentWebUIController> controller;\n\n        private AboveLCallback(ValueCallback<Uri[]> valueCallbacks, Uri[] uris, WeakReference<AbsAgentWebUIController> controller) {\n            this.mValueCallback = valueCallbacks;\n            this.mUris = uris;\n            this.controller = controller;\n        }\n\n        @Override\n        public boolean handleMessage(final Message msg) {\n\n            AgentWebUtils.runInUiThread(new Runnable() {\n                @Override\n                public void run() {\n                    FileChooser.AboveLCallback.this.safeHandleMessage(msg);\n                }\n            });\n            return false;\n        }\n\n        private void safeHandleMessage(Message msg) {\n            if (mValueCallback != null) {\n                fileCompressAndValuesCallback(mUris, mValueCallback);\n//                mValueCallback.onReceiveValue(mUris);\n            }\n            if (controller != null && controller.get() != null) {\n                controller.get().onCancelLoading();\n            }\n        }\n    }\n\n    private static final class WaitPhotoRunnable implements Runnable {\n        private String path;\n        private Handler.Callback mCallback;\n\n        private WaitPhotoRunnable(String path, Handler.Callback callback) {\n            this.path = path;\n            this.mCallback = callback;\n        }\n\n        @Override\n        public void run() {\n\n\n            if (TextUtils.isEmpty(path) || !new File(path).exists()) {\n                if (mCallback != null) {\n                    mCallback.handleMessage(Message.obtain(null, -1));\n                }\n                return;\n            }\n            int ms = 0;\n\n            while (ms <= MAX_WAIT_PHOTO_MS) {\n\n                ms += 300;\n                SystemClock.sleep(300);\n                File mFile = new File(path);\n                if (mFile.length() > 0) {\n\n                    if (mCallback != null) {\n                        mCallback.handleMessage(Message.obtain(null, 1));\n                        mCallback = null;\n                    }\n                    break;\n                }\n\n            }\n\n            if (ms > MAX_WAIT_PHOTO_MS) {\n                if (mCallback != null) {\n                    mCallback.handleMessage(Message.obtain(null, -1));\n                }\n            }\n            mCallback = null;\n            path = null;\n\n        }\n    }\n\n    // 必须执行在子线程, 会阻塞直到文件转换完成;\n    public static Queue<FileParcel> convertFile(String[] paths) throws Exception {\n\n        if (paths == null || paths.length == 0) {\n            return null;\n        }\n        int tmp = Runtime.getRuntime().availableProcessors() + 1;\n        int result = paths.length > tmp ? tmp : paths.length;\n        Executor mExecutor = Executors.newFixedThreadPool(result);\n        final Queue<FileParcel> mQueue = new LinkedBlockingQueue<>();\n        CountDownLatch mCountDownLatch = new CountDownLatch(paths.length);\n\n        int i = 1;\n        for (String path : paths) {\n\n            if (TextUtils.isEmpty(path)) {\n                mCountDownLatch.countDown();\n                continue;\n            }\n\n            mExecutor.execute(new EncodeFileRunnable(path, mQueue, mCountDownLatch, i++));\n\n        }\n        mCountDownLatch.await();\n\n        if (!((ThreadPoolExecutor) mExecutor).isShutdown()) {\n            ((ThreadPoolExecutor) mExecutor).shutdownNow();\n        }\n        return mQueue;\n    }\n\n\n    static class EncodeFileRunnable implements Runnable {\n\n        private String filePath;\n        private Queue<FileParcel> mQueue;\n        private CountDownLatch mCountDownLatch;\n        private int id;\n\n        public EncodeFileRunnable(String filePath, Queue<FileParcel> queue, CountDownLatch countDownLatch, int id) {\n            this.filePath = filePath;\n            this.mQueue = queue;\n            this.mCountDownLatch = countDownLatch;\n            this.id = id;\n        }\n\n\n        @Override\n        public void run() {\n            InputStream is = null;\n            ByteArrayOutputStream os = null;\n            try {\n                File mFile = new File(filePath);\n                Log.e(TAG, \"encode file:\" + mFile.length());\n                if (mFile.exists()) {\n\n                    is = new FileInputStream(mFile);\n                    if (is == null) {\n                        return;\n                    }\n                    os = new ByteArrayOutputStream();\n                    byte[] b = new byte[1024];\n                    int len;\n                    while ((len = is.read(b, 0, 1024)) != -1) {\n                        os.write(b, 0, len);\n                    }\n                    mQueue.offer(new FileParcel(id, mFile.getAbsolutePath(), Base64.encodeToString(os.toByteArray(), Base64.DEFAULT)));\n                } else {\n                }\n\n            } catch (Throwable e) {\n                e.printStackTrace();\n            } finally {\n                AgentWebUtils.closeIO(is);\n                AgentWebUtils.closeIO(os);\n                mCountDownLatch.countDown();\n            }\n\n\n        }\n    }\n\n    static String convertFileParcelObjectsToJson(Collection<FileParcel> collection) {\n\n        if (collection == null || collection.size() == 0) {\n            return null;\n        }\n        Iterator<FileParcel> mFileParcels = collection.iterator();\n        JSONArray mJSONArray = new JSONArray();\n        try {\n            while (mFileParcels.hasNext()) {\n                JSONObject jo = new JSONObject();\n                FileParcel mFileParcel = mFileParcels.next();\n                jo.put(\"contentPath\", mFileParcel.getContentPath());\n                jo.put(\"fileBase64\", mFileParcel.getFileBase64());\n                jo.put(\"mId\", mFileParcel.getId());\n                mJSONArray.put(jo);\n            }\n        } catch (Throwable throwable) {\n            throwable.printStackTrace();\n        }\n        return mJSONArray + \"\";\n    }\n\n    static class CovertFileThread implements Runnable {\n\n        private WeakReference<JsChannelCallback> mJsChannelCallback;\n        private String[] paths;\n\n        private CovertFileThread(JsChannelCallback JsChannelCallback, String[] paths) {\n            this.mJsChannelCallback = new WeakReference<JsChannelCallback>(JsChannelCallback);\n            this.paths = paths;\n        }\n\n        @Override\n        public void run() {\n\n            String name = Thread.currentThread().getName();\n            Thread.currentThread().setName(\"agentweb-thread\");\n            try {\n                Queue<FileParcel> mQueue = convertFile(paths);\n                String result = convertFileParcelObjectsToJson(mQueue);\n                if (mJsChannelCallback != null && mJsChannelCallback.get() != null) {\n                    mJsChannelCallback.get().call(result);\n                }\n\n            } catch (Exception e) {\n                e.printStackTrace();\n            } finally {\n                Thread.currentThread().setName(name);\n            }\n        }\n    }\n\n    static class JsChannelCallback {\n        WeakReference<Handler.Callback> callback = null;\n\n        JsChannelCallback(Handler.Callback callback) {\n            this.callback = new WeakReference<Handler.Callback>(callback);\n        }\n\n        public static JsChannelCallback create(Handler.Callback callback) {\n            return new JsChannelCallback(callback);\n        }\n\n        void call(String value) {\n            if (this.callback != null && this.callback.get() != null) {\n                this.callback.get().handleMessage(Message.obtain(null, \"JsChannelCallback\".hashCode(), value));\n            }\n        }\n    }\n\n    public static Builder newBuilder(Activity activity, WebView webView) {\n        return new Builder().setActivity(activity).setWebView(webView);\n    }\n\n    public static final class Builder {\n\n        private Activity mActivity;\n        private ValueCallback<Uri> mUriValueCallback;\n        private ValueCallback<Uri[]> mUriValueCallbacks;\n        private WebChromeClient.FileChooserParams mFileChooserParams;\n        private boolean mJsChannel = false;\n        private WebView mWebView;\n        private PermissionInterceptor mPermissionInterceptor;\n        private String mAcceptType = \"*/*\";\n        private Handler.Callback mJsChannelCallback;\n\n        public Builder setAcceptType(String acceptType) {\n            this.mAcceptType = acceptType;\n            return this;\n        }\n\n        public Builder setPermissionInterceptor(PermissionInterceptor permissionInterceptor) {\n            mPermissionInterceptor = permissionInterceptor;\n            return this;\n        }\n\n        public Builder setActivity(Activity activity) {\n            mActivity = activity;\n            return this;\n        }\n\n        public Builder setUriValueCallback(ValueCallback<Uri> uriValueCallback) {\n            mUriValueCallback = uriValueCallback;\n            mJsChannel = false;\n            mUriValueCallbacks = null;\n            return this;\n        }\n\n        public Builder setUriValueCallbacks(ValueCallback<Uri[]> uriValueCallbacks) {\n            mUriValueCallbacks = uriValueCallbacks;\n            mUriValueCallback = null;\n            mJsChannel = false;\n            return this;\n        }\n\n\n        public Builder setFileChooserParams(WebChromeClient.FileChooserParams fileChooserParams) {\n            mFileChooserParams = fileChooserParams;\n            return this;\n        }\n\n        public Builder setJsChannelCallback(Handler.Callback jsChannelCallback) {\n            this.mJsChannelCallback = jsChannelCallback;\n            mJsChannel = true;\n            mUriValueCallback = null;\n            mUriValueCallbacks = null;\n            return this;\n        }\n\n\n        public Builder setWebView(WebView webView) {\n            mWebView = webView;\n            return this;\n        }\n\n\n        public FileChooser build() {\n            return new FileChooser(this);\n        }\n    }\n\n\n}\n"
  },
  {
    "path": "agentweb-filechooser/src/main/java/com/just/agentweb/filechooser/FileCompressor.java",
    "content": "package com.just.agentweb.filechooser;\n\nimport android.net.Uri;\nimport android.webkit.ValueCallback;\n\nimport java.io.Serializable;\n\n/**\n * @author cenxiaozhong\n * @date 2021/11/26\n * @since 1.0.0\n */\npublic class FileCompressor implements Serializable {\n\n    private static FileCompressor sInstance = null;\n    private FileCompressEngine mFileCompressEngine;\n\n    FileCompressor() {\n    }\n\n    public static final FileCompressor getInstance() {\n        if (sInstance == null) {\n            synchronized (FileCompressor.class) {\n                if (sInstance == null) {\n                    sInstance = new FileCompressor();\n                }\n            }\n        }\n        return sInstance;\n    }\n\n\n    public void registerFileCompressEngine(FileCompressEngine valueCallback) {\n        this.mFileCompressEngine = valueCallback;\n    }\n\n    public void unregisterFileCompressEngine(FileCompressEngine valueCallback) {\n        this.mFileCompressEngine = null;\n    }\n\n    void fileCompress(String type, Uri[] uri, ValueCallback<Uri[]> callback) {\n        if (mFileCompressEngine == null) {\n            callback.onReceiveValue(uri);\n        } else {\n            mFileCompressEngine.compressFile(type, uri, callback);\n        }\n    }\n\n    public interface FileCompressEngine {\n        void compressFile(String type, Uri[] uri, ValueCallback<Uri[]> callback);\n    }\n\n\n}\n\n"
  },
  {
    "path": "agentweb-filechooser/src/main/java/com/just/agentweb/filechooser/FileParcel.java",
    "content": "/*\n * Copyright (C)  Justson(https://github.com/Justson/AgentWeb)\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.just.agentweb.filechooser;\n\nimport android.os.Parcel;\nimport android.os.Parcelable;\n\n/**\n * @author cenxiaozhong\n * @date 2017/5/24\n */\npublic class FileParcel implements Parcelable {\n\n    private int mId;\n    private String mContentPath;\n    private String mFileBase64;\n\n    protected FileParcel(Parcel in) {\n        mId = in.readInt();\n        mContentPath = in.readString();\n        mFileBase64 = in.readString();\n    }\n\n    public FileParcel(int id, String contentPath, String fileBase64) {\n        this.mId = id;\n        this.mContentPath = contentPath;\n        this.mFileBase64 = fileBase64;\n\n    }\n\n    public static final Creator<FileParcel> CREATOR = new Creator<FileParcel>() {\n        @Override\n        public FileParcel createFromParcel(Parcel in) {\n            return new FileParcel(in);\n        }\n\n        @Override\n        public FileParcel[] newArray(int size) {\n            return new FileParcel[size];\n        }\n    };\n\n    public int getId() {\n        return mId;\n    }\n\n    public void setId(int id) {\n        this.mId = id;\n    }\n\n    public String getContentPath() {\n        return mContentPath;\n    }\n\n    public void setContentPath(String contentPath) {\n        this.mContentPath = contentPath;\n    }\n\n    public String getFileBase64() {\n        return mFileBase64;\n    }\n\n    public void setFileBase64(String fileBase64) {\n        this.mFileBase64 = fileBase64;\n    }\n\n    @Override\n    public int describeContents() {\n        return 0;\n    }\n\n    @Override\n    public void writeToParcel(Parcel dest, int flags) {\n        dest.writeInt(mId);\n        dest.writeString(mContentPath);\n        dest.writeString(mFileBase64);\n    }\n\n    @Override\n    public String toString() {\n        return \"FileParcel{\" +\n                \"mId=\" + mId +\n                \", mContentPath='\" + mContentPath + '\\'' +\n                \", mFileBase64='\" + mFileBase64 + '\\'' +\n                '}';\n    }\n}\n"
  },
  {
    "path": "agentweb-filechooser/src/main/res/values/strings.xml",
    "content": "<resources>\n</resources>\n"
  },
  {
    "path": "agentweb-filechooser/src/test/java/com/just/agentweb/filechooser/ExampleUnitTest.java",
    "content": "package com.just.agentweb.filechooser;\n\nimport org.junit.Test;\n\nimport static org.junit.Assert.*;\n\n/**\n * Example local unit test, which will execute on the development machine (host).\n *\n * @see <a href=\"http://d.android.com/tools/testing\">Testing documentation</a>\n */\npublic class ExampleUnitTest {\n    @Test\n    public void addition_isCorrect() throws Exception {\n        assertEquals(4, 2 + 2);\n    }\n}"
  },
  {
    "path": "build.gradle",
    "content": "// Top-level build file where you can add configuration options common to all sub-projects/modules.\n\nbuildscript {\n    repositories {\n        mavenCentral()\n        jcenter()\n        google()\n        maven { url \"https://jitpack.io\" }\n        maven {\n            url 'https://maven.google.com/'\n            name 'Google'\n        }\n    }\n    dependencies {\n        classpath \"com.android.tools.build:gradle:8.1.4\"\n        classpath \"org.jetbrains.kotlin:kotlin-gradle-plugin:1.5.20\"\n    }\n}\n\nallprojects {\n    repositories {\n        mavenCentral()\n        maven { url \"https://jitpack.io\" }\n        jcenter()\n        google()\n        maven {\n            url 'https://maven.google.com/'\n            name 'Google'\n        }\n    }\n\n\n}\ntask clean(type: Delete) {\n    delete rootProject.buildDir\n}\n//tasks.getByPath(\":agentweb-core:mavenAndroidJavadocs\").enabled = false\n//tasks.getByPath(\":agentweb-download:mavenAndroidJavadocs\").enabled = false\n//tasks.getByPath(\":agentweb-filechooser:mavenAndroidJavadocs\").enabled = false\n"
  },
  {
    "path": "gradle/wrapper/gradle-wrapper.properties",
    "content": "distributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-8.0-bin.zip\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\n"
  },
  {
    "path": "gradle.properties",
    "content": "# Project-wide Gradle settings.\n\n# IDE (e.g. Android Studio) users:\n# Gradle settings configured through the IDE *will override*\n# any settings specified in this file.\n\n# For more details on how to configure your build environment visit\n# http://www.gradle.org/docs/current/userguide/build_environment.html\n\n# Specifies the JVM arguments used for the daemon process.\n# The setting is particularly useful for tweaking memory settings.\norg.gradle.jvmargs=-Xmx1536m\nCOMPILE_SDK_VERSION=34\nBUILD_TOOL_VERSION=34.0.0\nSUPPORT_LIB_VERSION=34.0.0\nTARGET_SDK_VERSION=34\nVERSION_NAME=\"5.0.8\"\nandroid.useAndroidX=true\nandroid.enableJetifier=true\n# When configured, Gradle will run in incubating parallel mode.\n# This option should only be used with decoupled projects. More details, visit\n# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects\n# org.gradle.parallel=true\n#systemProp.http.proxyHost=127.0.0.1\n#systemProp.http.proxyPort=1087\n#systemProp.https.proxyHost=127.0.0.1\n#systemProp.https.proxyPort=1087\nandroid.injected.testOnly=false\nandroid.nonFinalResIds=false\n#org.gradle.java.home=/Applications/Android\\ Studio.app/Contents/jbr/Contents/Home/"
  },
  {
    "path": "gradlew",
    "content": "#!/usr/bin/env bash\n\n##############################################################################\n##\n##  Gradle onStart up script for UN*X\n##\n##############################################################################\n\n# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nDEFAULT_JVM_OPTS=\"\"\n\nAPP_NAME=\"Gradle\"\nAPP_BASE_NAME=`basename \"$0\"`\n\n# Use the maximum available, or set MAX_FD != -1 to use that value.\nMAX_FD=\"maximum\"\n\nwarn ( ) {\n    echo \"$*\"\n}\n\ndie ( ) {\n    echo\n    echo \"$*\"\n    echo\n    exit 1\n}\n\n# OS specific support (must be 'true' or 'false').\ncygwin=false\nmsys=false\ndarwin=false\ncase \"`uname`\" in\n  CYGWIN* )\n    cygwin=true\n    ;;\n  Darwin* )\n    darwin=true\n    ;;\n  MINGW* )\n    msys=true\n    ;;\nesac\n\n# Attempt to set APP_HOME\n# Resolve links: $0 may be a link\nPRG=\"$0\"\n# Need this for relative symlinks.\nwhile [ -h \"$PRG\" ] ; do\n    ls=`ls -ld \"$PRG\"`\n    link=`expr \"$ls\" : '.*-> \\(.*\\)$'`\n    if expr \"$link\" : '/.*' > /dev/null; then\n        PRG=\"$link\"\n    else\n        PRG=`dirname \"$PRG\"`\"/$link\"\n    fi\ndone\nSAVED=\"`pwd`\"\ncd \"`dirname \\\"$PRG\\\"`/\" >/dev/null\nAPP_HOME=\"`pwd -P`\"\ncd \"$SAVED\" >/dev/null\n\nCLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar\n\n# Determine the Java command to use to onStart the JVM.\nif [ -n \"$JAVA_HOME\" ] ; then\n    if [ -x \"$JAVA_HOME/jre/sh/java\" ] ; then\n        # IBM's JDK on AIX uses strange locations for the executables\n        JAVACMD=\"$JAVA_HOME/jre/sh/java\"\n    else\n        JAVACMD=\"$JAVA_HOME/bin/java\"\n    fi\n    if [ ! -x \"$JAVACMD\" ] ; then\n        die \"ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nelse\n    JAVACMD=\"java\"\n    which java >/dev/null 2>&1 || die \"ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\nfi\n\n# Increase the maximum file descriptors if we can.\nif [ \"$cygwin\" = \"false\" -a \"$darwin\" = \"false\" ] ; then\n    MAX_FD_LIMIT=`ulimit -H -n`\n    if [ $? -eq 0 ] ; then\n        if [ \"$MAX_FD\" = \"maximum\" -o \"$MAX_FD\" = \"max\" ] ; then\n            MAX_FD=\"$MAX_FD_LIMIT\"\n        fi\n        ulimit -n $MAX_FD\n        if [ $? -ne 0 ] ; then\n            warn \"Could not set maximum file descriptor limit: $MAX_FD\"\n        fi\n    else\n        warn \"Could not query maximum file descriptor limit: $MAX_FD_LIMIT\"\n    fi\nfi\n\n# For Darwin, add options to specify how the application appears in the dock\nif $darwin; then\n    GRADLE_OPTS=\"$GRADLE_OPTS \\\"-Xdock:name=$APP_NAME\\\" \\\"-Xdock:icon=$APP_HOME/media/gradle.icns\\\"\"\nfi\n\n# For Cygwin, switch paths to Windows format before running java\nif $cygwin ; then\n    APP_HOME=`cygpath --path --mixed \"$APP_HOME\"`\n    CLASSPATH=`cygpath --path --mixed \"$CLASSPATH\"`\n    JAVACMD=`cygpath --unix \"$JAVACMD\"`\n\n    # We build the pattern for arguments to be converted via cygpath\n    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`\n    SEP=\"\"\n    for dir in $ROOTDIRSRAW ; do\n        ROOTDIRS=\"$ROOTDIRS$SEP$dir\"\n        SEP=\"|\"\n    done\n    OURCYGPATTERN=\"(^($ROOTDIRS))\"\n    # Add a user-defined pattern to the cygpath arguments\n    if [ \"$GRADLE_CYGPATTERN\" != \"\" ] ; then\n        OURCYGPATTERN=\"$OURCYGPATTERN|($GRADLE_CYGPATTERN)\"\n    fi\n    # Now convert the arguments - kludge to limit ourselves to /bin/sh\n    i=0\n    for arg in \"$@\" ; do\n        CHECK=`echo \"$arg\"|egrep -c \"$OURCYGPATTERN\" -`\n        CHECK2=`echo \"$arg\"|egrep -c \"^-\"`                                 ### Determine if an option\n\n        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition\n            eval `echo args$i`=`cygpath --path --ignore --mixed \"$arg\"`\n        else\n            eval `echo args$i`=\"\\\"$arg\\\"\"\n        fi\n        i=$((i+1))\n    done\n    case $i in\n        (0) set -- ;;\n        (1) set -- \"$args0\" ;;\n        (2) set -- \"$args0\" \"$args1\" ;;\n        (3) set -- \"$args0\" \"$args1\" \"$args2\" ;;\n        (4) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" ;;\n        (5) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" ;;\n        (6) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" ;;\n        (7) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" ;;\n        (8) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" ;;\n        (9) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" \"$args8\" ;;\n    esac\nfi\n\n# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules\nfunction splitJvmOpts() {\n    JVM_OPTS=(\"$@\")\n}\neval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS\nJVM_OPTS[${#JVM_OPTS[*]}]=\"-Dorg.gradle.appname=$APP_BASE_NAME\"\n\nexec \"$JAVACMD\" \"${JVM_OPTS[@]}\" -classpath \"$CLASSPATH\" org.gradle.wrapper.GradleWrapperMain \"$@\"\n"
  },
  {
    "path": "gradlew.bat",
    "content": "@if \"%DEBUG%\" == \"\" @echo off\r\n@rem ##########################################################################\r\n@rem\r\n@rem  Gradle startup script for Windows\r\n@rem\r\n@rem ##########################################################################\r\n\r\n@rem Set local scope for the variables with windows NT shell\r\nif \"%OS%\"==\"Windows_NT\" setlocal\r\n\r\n@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\r\nset DEFAULT_JVM_OPTS=\r\n\r\nset DIRNAME=%~dp0\r\nif \"%DIRNAME%\" == \"\" set DIRNAME=.\r\nset APP_BASE_NAME=%~n0\r\nset APP_HOME=%DIRNAME%\r\n\r\n@rem Find java.exe\r\nif defined JAVA_HOME goto findJavaFromJavaHome\r\n\r\nset JAVA_EXE=java.exe\r\n%JAVA_EXE% -version >NUL 2>&1\r\nif \"%ERRORLEVEL%\" == \"0\" goto init\r\n\r\necho.\r\necho ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\r\necho.\r\necho Please set the JAVA_HOME variable in your environment to match the\r\necho location of your Java installation.\r\n\r\ngoto fail\r\n\r\n:findJavaFromJavaHome\r\nset JAVA_HOME=%JAVA_HOME:\"=%\r\nset JAVA_EXE=%JAVA_HOME%/bin/java.exe\r\n\r\nif exist \"%JAVA_EXE%\" goto init\r\n\r\necho.\r\necho ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%\r\necho.\r\necho Please set the JAVA_HOME variable in your environment to match the\r\necho location of your Java installation.\r\n\r\ngoto fail\r\n\r\n:init\r\n@rem Get command-line arguments, handling Windowz variants\r\n\r\nif not \"%OS%\" == \"Windows_NT\" goto win9xME_args\r\nif \"%@eval[2+2]\" == \"4\" goto 4NT_args\r\n\r\n:win9xME_args\r\n@rem Slurp the command line arguments.\r\nset CMD_LINE_ARGS=\r\nset _SKIP=2\r\n\r\n:win9xME_args_slurp\r\nif \"x%~1\" == \"x\" goto execute\r\n\r\nset CMD_LINE_ARGS=%*\r\ngoto execute\r\n\r\n:4NT_args\r\n@rem Get arguments from the 4NT Shell from JP Software\r\nset CMD_LINE_ARGS=%$\r\n\r\n:execute\r\n@rem Setup the command line\r\n\r\nset CLASSPATH=%APP_HOME%\\gradle\\wrapper\\gradle-wrapper.jar\r\n\r\n@rem Execute Gradle\r\n\"%JAVA_EXE%\" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% \"-Dorg.gradle.appname=%APP_BASE_NAME%\" -classpath \"%CLASSPATH%\" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%\r\n\r\n:end\r\n@rem End local scope for the variables with windows NT shell\r\nif \"%ERRORLEVEL%\"==\"0\" goto mainEnd\r\n\r\n:fail\r\nrem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of\r\nrem the _cmd.exe /c_ return code!\r\nif  not \"\" == \"%GRADLE_EXIT_CONSOLE%\" exit 1\r\nexit /b 1\r\n\r\n:mainEnd\r\nif \"%OS%\"==\"Windows_NT\" endlocal\r\n\r\n:omega\r\n"
  },
  {
    "path": "jitpack.yml",
    "content": "jdk:\n  - openjdk17"
  },
  {
    "path": "releasenote.md",
    "content": "* v_5.0.0 更新\n\t* ActionActivity 重构， 使用Fragment 替代 Activity，解决多进程使用问题\n\t* 新增 WebRTC Sample\n\t* 新增 FileCompressor ，允许选择文件后对文件进行操作，如文件压缩，图片方向调整等\n\t* DefaultWebClient#onReceivedSslError 添加默认处理\n\t* 文件选择器开放多选\n\t* fix #777 ，FileChooserParams.createIntent() 导致AcceptTypes丢失问题\n\t* androidx Grade version upgrade to 7.0.2\n\t* 新增 AgentWebCompat.setDataDirectorySuffix(context) 修复 Using WebView from more than one process 崩溃\n\n* v_4.1.1 更新\n    * [#587](https://github.com/Justson/AgentWeb/pull/587) input 支持视屏拍摄\n    * [#614](https://github.com/Justson/AgentWeb/pull/614)修复上传文件选择的兼容性bug\n    * 重构了Download\n    * 最小SDK提升到了 14\n    \n* v_4.0.3 更新\n\t* 部分手机下载过程中～声音一直响 [#523](https://github.com/Justson/AgentWeb/issues/523)\n\t* 抽离[Downloader](https://github.com/Justson/Downloader)\n\t* 放弃反射回调WebViewClient#methods，使用洋葱模型的Middleware代替\n\n* v_4.0.2 更新\n\t* 修复断点续传时进度计算错误\n\t* 修复无法通过`Extra`关闭进度通知\n\n* v_4.0.0 更新\n\t* `AgentWeb` 拆分出 `AgentWeb-Download` 、 `AgentWeb-FileChooser` 、`AgentWeb-core` 三个库，用户可以按需选择\n\t* 重新设计了 `AgentWeb-Download` \n\t* 删除了 `DownloadListener` 、`DefaultMsgConfig` 以及相关API\n\t* 旧废弃的API，4.0.0 直接删除，不在提供兼容\n\t* 部分类和API重命名 \n\t* `Fragment`和`Activity`构建一致。[#227](https://github.com/Justson/AgentWeb/issues/227)\n\t* 从AgentWeb-core删除 `BaseAgentWebFragment`和`BaseAgentWebActivity` ，于Sample形式提供参考\n* v_3.1.0 更新\n\t* `WebProgress` 进度条动画更细腻\n\t* 修复部分机型拍照文件大小为0情况\n\t* 更新了`FileUpLoadChooserImpl`\n* v_3.0.0 更新\n\t* 加入 `MiddlewareWebChromeBase` 中间件 ，支持多个 `WebChromeClient` \n\t* 加入 `MiddlewareWebClientBase`中间件 ， 支持多个 `WebViewClient` \n\t* 加入了默认的错误页，并支持自定义错误页 \n\t* 加入 `AgentWebUIController` ，统一控制UI \n\t* 支持拦截未知的页面 \n\t* 支持调起其他应用 \n* v_2.0.1 更新\n\t* 支持并行下载 ， 修复 #114 #109 \n* v_2.0.0 更新\n\t* 加入动态权限 \n\t* 拍照\n* v_1.2.6 更新\n\t* 修复Android 4.4以下布局错乱 \n* v_1.2.5 提示信息支持配置 \n\t* 提示信息支持配置 \n* v_1.2.4 更新\n\t* 支持传入 IWebLayout ，支持下拉回弹，下拉刷新效果 \n* v_1.2.3 更新\n\t* 新增下载结果回调 \n* v_1.2.2 更新\n\t* 修复已知 Bug \n* v_1.2.1 更新 \n\t* 支持调起支付宝 ， 微信支付 \n* v_1.2.0 更新\n\t* 全面支持全屏视频 \n* v_1.1.2 更新\n\t* 完善功能 \n\n"
  },
  {
    "path": "sample/.gitignore",
    "content": "/build\nmap.txt"
  },
  {
    "path": "sample/build.gradle",
    "content": "\napply plugin: 'com.android.application'\n\nandroid {\n    compileSdk COMPILE_SDK_VERSION.toInteger()\n    buildToolsVersion BUILD_TOOL_VERSION\n\n    defaultConfig {\n        applicationId \"com.just.agentweb.sample\"\n        namespace 'com.just.agentweb.sample'\n        minSdkVersion 19\n        targetSdkVersion TARGET_SDK_VERSION.toInteger()\n        multiDexEnabled true\n        versionCode 5\n        versionName VERSION_NAME\n        testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'\n    }\n    signingConfigs {\n        release {\n            storeFile file(\"./keystore/keystore.jks\")\n            storePassword \"admin123\"\n            keyAlias \"agentweb\"\n            keyPassword \"admin123\"\n        }\n    }\n    buildTypes {\n        release {\n            signingConfig signingConfigs.release\n            minifyEnabled true\n            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'\n        }\n        debug {\n            signingConfig signingConfigs.release\n            minifyEnabled false\n            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'\n        }\n    }\n    sourceSets { main { assets.srcDirs = ['src/main/assets', 'src/main/assets/'] } }\n    lintOptions {\n        abortOnError false\n        checkReleaseBuilds false\n    }\n\n    lintOptions {\n        abortOnError false\n    }\n    compileOptions {\n        sourceCompatibility JavaVersion.VERSION_1_8\n        targetCompatibility JavaVersion.VERSION_1_8\n    }\n\n    sourceSets {\n        main {\n            jniLibs.srcDirs = ['libs']\n        }\n    }\n}\n\n\n\n\ndependencies {\n    api fileTree(include: ['*.jar'], dir: 'libs')\n    androidTestImplementation('androidx.test.espresso:espresso-core:3.1.0', {\n        exclude group: 'com.android.support', module: 'support-annotations'\n    })\n    api 'androidx.appcompat:appcompat:1.5.0'\n    api 'com.google.android.material:material:1.6.1'\n    //    compile \"com.android.support:support-v4:${SUPPORT_LIB_VERSION}\"\n    testImplementation 'junit:junit:4.12'\n    implementation 'com.github.Justson:Downloader:v5.0.4-androidx'\n//    api project(':agentweb-core')\n//    api project(':agentweb-filechooser')\n    implementation 'io.github.justson:agentweb-core:v5.1.1-androidx'\n    implementation 'io.github.justson:agentweb-filechooser:v5.1.1-androidx'\n//    debugImplementation 'com.squareup.leakcanary:leakcanary-android:1.5.1'\n//    releaseImplementation 'com.squareup.leakcanary:leakcanary-android-no-op:1.5.1'\n//    testImplementation 'com.squareup.leakcanary:leakcanary-android-no-op:1.5.1'\n//    compile files('libs/alipaysdk-20170922.jar')\n    implementation 'us.feras.mdv:markdownview:1.1.0'\n    implementation 'com.lcodecorex:tkrefreshlayout:1.0.7'\n    implementation 'com.github.lzyzsd:jsbridge:1.0.4'\n    implementation 'com.google.code.gson:gson:2.8.6'\n    implementation 'com.scwang.smartrefresh:SmartRefreshLayout:1.0.3'\n    implementation 'com.scwang.smartrefresh:SmartRefreshHeader:1.0.3'\n    implementation 'com.tencent.sonic:sdk:2.0.0'\n    implementation 'com.coolindicator.sdk:coolindicator:1.0.0-beta'\n    implementation 'com.squareup.picasso:picasso:2.71828'\n    implementation 'com.github.Justson:flying-pigeon:v1.0.7'\n    implementation 'com.github.Justson:dispatch-queue:v1.0.5'\n    implementation('com.github.Ferfalk:SimpleSearchView:0.2.0', {\n        exclude group: 'com.android.support'\n    })\n    implementation 'top.zibin:Luban:1.1.8'\n}\n"
  },
  {
    "path": "sample/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# By default, the flags in this file are appended to flags specified\n# in /Users/cenxiaozhong/Library/Android/sdk/tools/proguard/proguard-android.txt\n# You can edit the include path and order by changing the proguardFiles\n# directive in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# Add any project specific keep options here:\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.create.javascript.interface.for.webview {\n#   public *;\n#}\n\n# Uncomment this to preserve the line number information for\n# debugging stack traces.\n#-keepattributes SourceFile,LineNumberTable\n\n# If you keep the line number information, uncomment this to\n# hide the original source file name.\n#-renamesourcefileattribute SourceFile\n#-keep public class * extends android.webkit.WebChromeClient{\n#*;\n#}\n-keep class com.just.agentweb.** {\n    *;\n}\n-dontwarn com.just.agentweb.**\n-keepclassmembers class com.just.agentweb.sample.common.AndroidInterface{ *; }\n\n\n-keepclassmembers class com.just.agentweb.sample.common.SonicJavaScriptInterface{ *; }\n\n-dontshrink\n-dontpreverify\n-dontoptimize\n-dontusemixedcaseclassnames\n\n-flattenpackagehierarchy\n-allowaccessmodification\n-printmapping map.txt\n\n-optimizationpasses 7\n-verbose\n-keepattributes Exceptions,InnerClasses\n-dontskipnonpubliclibraryclasses\n-dontskipnonpubliclibraryclassmembers\n-ignorewarnings\n\n-keep public class * extends android.app.Activity\n-keep public class * extends android.app.Application\n-keep public class * extends android.app.Service\n-keep public class * extends android.content.BroadcastReceiver\n-keep public class * extends android.content.ContentProvider\n-keep public class * extends java.lang.Throwable {*;}\n-keep public class * extends java.lang.Exception {*;}\n\n#-libraryjars\n\n-keep class com.alipay.android.app.IAlixPay{*;}\n-keep class com.alipay.android.app.IAlixPay$Stub{*;}\n-keep class com.alipay.android.app.IRemoteServiceCallback{*;}\n-keep class com.alipay.android.app.IRemoteServiceCallback$Stub{*;}\n-keep class com.alipay.sdk.app.PayTask{ public *;}\n-keep class com.alipay.sdk.app.AuthTask{ public *;}\n-keep class com.alipay.sdk.app.H5PayCallback {\n    <fields>;\n    <methods>;\n}\n-keep class com.alipay.android.phone.mrpc.core.** { *; }\n-keep class com.alipay.apmobilesecuritysdk.** { *; }\n-keep class com.alipay.mobile.framework.service.annotation.** { *; }\n-keep class com.alipay.mobilesecuritysdk.face.** { *; }\n-keep class com.alipay.tscenter.biz.rpc.** { *; }\n-keep class org.json.alipay.** { *; }\n-keep class com.alipay.tscenter.** { *; }\n-keep class com.ta.utdid2.** { *;}\n-keep class com.ut.device.** { *;}\n\n\n-keepclasseswithmembernames class * {\n    native <methods>;\n}\n\n-keepclasseswithmembers class * {\n    public <init>(android.content.Context, android.util.AttributeSet);\n}\n\n-keepclasseswithmembers class * {\n    public <init>(android.content.Context, android.util.AttributeSet, int);\n}\n\n-keepclassmembers class * extends android.app.Activity {\n   public void *(android.view.View);\n}\n\n-keepclassmembers enum * {\n    public static **[] values();\n    public static ** valueOf(java.lang.String);\n}\n\n-keep class * implements android.os.Parcelable {\n  public static final android.os.Parcelable$Creator *;\n}\n\n# adding this in to preserve line numbers so that the stack traces\n# can be remapped\n-renamesourcefileattribute SourceFile\n-keepattributes SourceFile,LineNumberTable"
  },
  {
    "path": "sample/src/androidTest/java/com/just/agentweb/sample/ExampleInstrumentedTest.java",
    "content": "package com.just.agentweb.sample;\n\nimport android.content.Context;\nimport androidx.test.platform.app.InstrumentationRegistry;\nimport androidx.test.ext.junit.runners.AndroidJUnit4;\n\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\n\nimport static org.junit.Assert.*;\n\n/**\n * Instrumentation test, which will execute on an Android device.\n *\n * @see <a href=\"http://d.android.com/tools/testing\">Testing documentation</a>\n */\n@RunWith(AndroidJUnit4.class)\npublic class ExampleInstrumentedTest {\n    @Test\n    public void useAppContext() throws Exception {\n        // Context of the app under test.\n        Context appContext = InstrumentationRegistry.getTargetContext();\n\n        assertEquals(\"com.just.library.agentweb\", appContext.getPackageName());\n    }\n}\n"
  },
  {
    "path": "sample/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    package=\"com.just.agentweb.sample\">\n\n    <uses-feature\n        android:name=\"android.hardware.camera\"\n        android:required=\"false\" />\n\n\n    <!-- Devices running Android 12L (API level 32) or lower  -->\n    <uses-permission android:name=\"android.permission.READ_EXTERNAL_STORAGE\" android:maxSdkVersion=\"32\" />\n    <uses-permission android:name=\"android.permission.WRITE_EXTERNAL_STORAGE\" android:maxSdkVersion=\"32\" />\n\n    <uses-permission android:name=\"android.permission.INTERNET\"/>\n    <uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>\n    <uses-permission android:name=\"android.permission.ACCESS_FINE_LOCATION\"/>\n    <uses-permission android:name=\"android.permission.ACCESS_COARSE_LOCATION\"/>\n    <uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>\n    <uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>\n    <uses-permission android:name=\"android.permission.CAMERA\"/>\n    <uses-permission android:name=\"android.permission.REQUEST_INSTALL_PACKAGES\"/>\n    <uses-permission android:name=\"android.permission.MODIFY_AUDIO_SETTINGS\"/>\n\n    <application\n        android:name=\"com.just.agentweb.sample.app.App\"\n        android:allowBackup=\"true\"\n        android:icon=\"@mipmap/app_logo\"\n        android:label=\"@string/app_name\"\n        android:logo=\"@mipmap/app_logo\"\n        android:networkSecurityConfig=\"@xml/network_security_config\"\n        android:roundIcon=\"@mipmap/app_logo\"\n        android:supportsRtl=\"true\"\n        android:theme=\"@style/BaseAppTheme\">\n        <activity\n            android:exported=\"true\"\n            android:name=\"com.just.agentweb.sample.activity.MainActivity\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n\n                <category android:name=\"android.intent.category.LAUNCHER\" />\n            </intent-filter>\n        </activity>\n        <activity\n            android:name=\"com.just.agentweb.sample.activity.WebActivity\"\n            android:launchMode=\"singleTop\" />\n\n        <activity\n            android:name=\"com.just.agentweb.sample.activity.RemoteWebViewlActivity\"\n            android:launchMode=\"singleTop\"\n            android:process=\":web\" />\n\n        <service\n            android:name=\".service.WebService\"\n            android:process=\":web\" />\n\n        <activity\n            android:name=\"com.just.agentweb.sample.activity.CommonActivity\"\n            android:configChanges=\"orientation|screenSize\"\n            android:hardwareAccelerated=\"true\"\n            android:launchMode=\"singleTop\" />\n        <activity\n            android:name=\"com.just.agentweb.sample.activity.EasyWebActivity\"\n            android:configChanges=\"orientation|screenSize\"\n            android:launchMode=\"singleTop\" />\n\n        <activity\n            android:name=\"com.just.agentweb.sample.activity.ContainerActivity\"\n            android:launchMode=\"singleTop\"\n            android:screenOrientation=\"fullSensor\" />\n        <activity android:name=\".activity.AutoHidenToolbarActivity\" />\n\n        <activity\n            android:name=\".activity.NativeDownloadActivity\"\n            android:launchMode=\"singleTop\"\n            android:screenOrientation=\"fullSensor\" />\n\n        <provider\n            android:name=\".provider.ServiceProvider\"\n            android:authorities=\"com.just.agentweb.sample\" />\n\n        <provider\n            android:name=\".provider.WebServiceProvider\"\n            android:authorities=\"com.just.agentweb.sample.web\"\n            android:process=\":web\" />\n\n        <activity\n            android:exported=\"true\"\n            android:name=\".activity.ExternalActivity\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.VIEW\" />\n                <category android:name=\"android.intent.category.DEFAULT\" />\n                <!--BROWSABLE指定该Activity能被浏览器安全调用-->\n                <category android:name=\"android.intent.category.BROWSABLE\" />\n                <data\n                    android:host=\"sample.just.com\"\n                    android:scheme=\"agentweb\" />\n            </intent-filter>\n        </activity>\n    </application>\n\n</manifest>"
  },
  {
    "path": "sample/src/main/assets/js_interaction/button.css",
    "content": "\n.button {\n  color: #666;\n  background-color: #EEE;\n  border-color: #EEE;\n  font-weight: 300;\n  font-size: 16px;\n  font-family: \"Helvetica Neue Light\", \"Helvetica Neue\", Helvetica, Arial, \"Lucida Grande\", sans-serif;\n  text-decoration: none;\n  text-align: center;\n  line-height: 40px;\n  height: 40px;\n  padding: 0 40px;\n  margin: 0;\n  display: inline-block;\n  appearance: none;\n  cursor: pointer;\n  border: none;\n  -webkit-box-sizing: border-box;\n     -moz-box-sizing: border-box;\n          box-sizing: border-box;\n  -webkit-transition-property: all;\n          transition-property: all;\n  -webkit-transition-duration: .3s;\n          transition-duration: .3s;\n  /*\n  * Disabled State\n  *\n  * The disabled state uses the class .disabled, is-disabled,\n  * and the form attribute disabled=\"disabled\".\n  * The use of !important is only added because this is a state\n  * that must be applied to all buttons when in a disabled state.\n  */ }\n\n  .button-glow {\n  -webkit-animation-duration: 3s;\n          animation-duration: 3s;\n  -webkit-animation-iteration-count: infinite;\n          animation-iteration-count: infinite;\n  -webkit-animation-name: glowing;\n          animation-name: glowing; }\n\n.button-rounded {\n  border-radius: 4px; }\n\n  /*\n* Base Colors\n*\n* Create colors for buttons\n* (.button-primary, .button-secondary, etc.)\n*/\n.button-primary,\n.button-primary-flat {\n  background-color: #1B9AF7;\n  border-color: #1B9AF7;\n  color: #FFF; }\n\n/*\n* Border Buttons\n*\n* These buttons have no fill they only have a\n* border to define their hit target.\n*/\n.button-border, .button-border-thin, .button-border-thick {\n  background: none;\n  border-width: 2px;\n  border-style: solid;\n  line-height: 36px; }\n\n"
  },
  {
    "path": "sample/src/main/assets/js_interaction/hello.html",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n    <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">\n    <meta http-equiv=\"Content-Style-Type\" content=\"text/css\">\n    <title></title>\n    <meta name=\"Generator\" content=\"Cocoa HTML Writer\">\n    <meta name=\"CocoaVersion\" content=\"1504.76\">\n    <style type=\"text/css\">\n    p.p1 {margin: 0.0px 0.0px 0.0px 0.0px; font: 12.0px Helvetica}\n    body{\n        padding:0;\n        margin:0;\n    }\n\n    </style>\n    <link rel=\"stylesheet\" type=\"text/css\" href=\"./button.css\">\n    <script type=\"text/javascript\">\n\n  function sendHelloToAndroid() {\n      // body...\n      //console.log(\"call android\")\n      if(window.android!=null&&typeof(window.android)!=\"undefined\"){\n        window.android.callAndroid(\"你好，Android! \");\n      }else{\n         alert(typeof(window.android));\n      }\n     \n  }\n\n  function callByAndroid(){\n      console.log(\"callByAndroid\")\n      alert(\"Js收到消息\");\n      showElement(\"Js收到消息-->无参方法callByAndroid被调用\");\n  }\n\n  function callByAndroidParam(msg1){\n     console.log(\"callByAndroid_param\")\n    alert(\"Js收到消息：\"+msg1);\n    showElement(\"Js收到消息-->方法callByAndroidParam被调用,参数:\"+msg1);\n\n  }\n  function callByAndroidMoreParams(objs,msg2,msg3){\n     \n     alert(\"Js收到消息：\"+\"id:\"+objs.id.toString()+\" name:\"+objs.name+\" age:\"+objs.age.toString()+msg2+msg3);\n    showElement(\"Js收到消息-->方法callByAndroidMoreParam被调用 , 参数1:\"+objs+\"  参数2:\"+msg2+\"  参数3:\"+msg3);\n     return \"ok\";\n  }\n\n  function callByAndroidInteraction(msg){\n\n    showElement(msg)\n\n    window.setTimeout(sendHelloToAndroid,1000);\n\n  }\n\n\n  function showElement(msg){\n      var div   =document.getElementById(\"div_box\");  //获取div\n      var ele=document.createElement('h6');    //创建h2元素节点\n      ele.innerHTML=msg;                //设置h2节点的内容\n      div.appendChild(ele);                    //添加子节点ele\n\n\n}\n\n\n    </script>\n</head>\n<body style=\"width: 100%;height: 100%;padding: 0px\">\n\n<div id=\"div_box\"\n     style=\"width: 100%;min-height: 100%;position: absolute;background: #0ff;margin-top: -50px;margin: 0px\">\n\n\n</div>\n</body>\n</html>"
  },
  {
    "path": "sample/src/main/assets/jsbridge/demo.html",
    "content": "<html>\n    <head>\n        <meta content=\"text/html; charset=utf-8\" http-equiv=\"content-type\">\n        <title>\n            js调用java\n        </title>\n    </head>\n    \n    <body>\n\n        <p>\n            <xmp id=\"show\">\n            </xmp>\n        </p>\n\n        <p>\n            <xmp id=\"init\"></xmp>\n        </p>\n\n        <p>\n            <input type=\"text\" id=\"text1\" value=\"用户名(username)\" />\n        </p>\n\n        <p>\n           <input type=\"text\" id=\"text2\" value=\"password\" />\n        </p>\n\n        <p>\n            <input type=\"button\" id=\"enter\" value=\"发消息给Native\" onclick=\"testClick();\"/>\n        </p>\n\n        <p>\n          <input type=\"button\" id=\"enter1\" value=\"调用Native方法\" onclick=\"testClick1();\"/>\n        </p>\n\n        <p>\n            <input type=\"button\" id=\"enter2\" value=\"显示html\" onclick=\"testDiv();\" />\n        </p>\n\n        <p>\n            <input type=\"file\" value=\"打开文件\" />\n        </p>\n\n    </body>\n    <script>\n\n        function testDiv() {\n            document.getElementById(\"show\").innerHTML = document.getElementsByTagName(\"html\")[0].innerHTML;\n        }\n\n        function testClick() {\n            var str1 = document.getElementById(\"text1\").value;\n            var str2 = document.getElementById(\"text2\").value;\n\n            //send message to native\n            var data = {id: 1, content: \"这是一个图片 <img src=\\\"a.png\\\"/> test\\r\\nhahaha\"};\n            window.WebViewJavascriptBridge.send(\n                data\n                , function(responseData) {\n                    document.getElementById(\"show\").innerHTML = \"repsonseData from java, data = \" + responseData\n                }\n            );\n\n        }\n\n        function testClick1() {\n            var str1 = document.getElementById(\"text1\").value;\n            var str2 = document.getElementById(\"text2\").value;\n\n            //call native method\n            window.WebViewJavascriptBridge.callHandler(\n                'submitFromWeb'\n                , {'param': '中文测试'}\n                , function(responseData) {\n                    document.getElementById(\"show\").innerHTML = \"send get responseData from java, data = \" + responseData\n                }\n            );\n        }\n\n        function bridgeLog(logContent) {\n            document.getElementById(\"show\").innerHTML = logContent;\n        }\n\n        function connectWebViewJavascriptBridge(callback) {\n            if (window.WebViewJavascriptBridge) {\n                callback(WebViewJavascriptBridge)\n            } else {\n                document.addEventListener(\n                    'WebViewJavascriptBridgeReady'\n                    , function() {\n                        callback(WebViewJavascriptBridge)\n                    },\n                    false\n                );\n            }\n        }\n\n        connectWebViewJavascriptBridge(function(bridge) {\n            bridge.init(function(message, responseCallback) {\n                console.log('Js got a message', message);\n                var data = {\n                    'Javascript Responds': '测试中文!'\n                };\n                console.log('Js responding with', data);\n                responseCallback(data);\n            });\n\n            bridge.registerHandler(\"functionInJs\", function(data, responseCallback) {\n                document.getElementById(\"show\").innerHTML = (\"data from Java: = \" + data);\n                var responseData = \"Javascript Says Right back aka!\";\n                responseCallback(responseData);\n            });\n        })\n    </script>\n\n</html>"
  },
  {
    "path": "sample/src/main/assets/sms/sms.html",
    "content": "<html>\n<head>\n    <meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">\n    <meta content=\"text/html\" http-equiv=\"Content-Type\">\n    <meta content=\"width=device-width, initial-scale=1\" name=\"viewport\">\n    <link href=\"./callsms_files/main.css\" rel=\"stylesheet\">\n    <title>\n        电话短信邮件\n    </title>\n    <style>\n      p {\n        -webkit-margin-before: 5px;\n        -webkit-margin-after: 5px;\n        -webkit-margin-onStart: 0px;\n        -webkit-margin-end: 0px;\n      }\n      table {\n        border-collapse: collapse;\n      }\n      \n      table, th, td {\n        border: 1px solid #e9eaeb;\n        height: 44px;\n      }\n      \n      td {\n        padding: 5px;\n        font-size: 11px;\n      }\n\n    </style>\n</head>\n<body class=\"article_body\">\n<div class=\"article_title\">\n    电话短信邮件跳转\n</div>\n<div class=\"article_created_at\">\n\n</div>\n<div class=\"article_content\">\n\n    <doctype html=\"\">\n        <title></title>\n\n\n        <style type=\"text/css\">\n              a{\n                  font-size: 22px;\n                  display: block;\n                  margin: 25px;\n              }\n\n        </style>\n        <p><a href=\"tel:10086\">电话</a>\n            <a href=\"sms:10086\">短信</a>\n            <a href=\"mailto:xiaozhongcen@gmail.com\">邮件</a>\n            <a href=\"agentweb://sample.just.com?url=https://m.jd.com/\">打开应用内部页面</a>\n            <a href=\"weixin://\">打开微信</a>\n        </p>\n    </doctype>\n</div>\n\n\n</body>\n</html>"
  },
  {
    "path": "sample/src/main/assets/upload_file/event.js",
    "content": "// ---------- 事件绑定与删除绑定 ---------- //\nfunction bindEvent(element, eventName, func) {\n    var events = element['the'+eventName];    //用于保存某个事件序列\n    if(!events) {  //如果不存在一个序列，则创建它，并加入HTML标记当中的onEvent = function(){}形式的绑定\n        events = element['the'+eventName] = [];\n        if (element['on'+eventName]) { events.push(element['on'+eventName]); }\n    }\n    \n    //检测是否为重复绑定\n    for(var i=0; i<events.length; i++) {\n        if(events[i] === func) { flag = true; break; }\n    }\n    \n    // 非重复绑定，则加入该函数事件\n    if(i >= events.length) { events.push(func); }\n    \n    // 重新定义这个事件的执行方式\n    element['on'+eventName] = function(event) {\n        event = event || (function() { //修复IE的事件对象\n            var e = window.event;\n            e.preventDefault = function() { e.returnValue = false; }\n            e.stopPropagation = function() { e.cancelBubble = true; }\n            //根据需要继续修复\n            return e;\n        })();\n        //顺序执行这些函数\n        for(var i=0; i<events.length; i++) { events[i].call(element, event); }\n    }\n}\n\n// 删除事件绑定\nfunction unBindEvent(element, eventName, func) {\n    var events = this['the'+eventName];\n    //如果不存在一个事件序列\n    if(!events) { return false; }\n    \n    //检测该函数是否存在该事件序列当中\n    for(var i=0; i<events.length; i++) {\n        if(func === events[i]) {\n            [].splice.call(events, i, 1);\n            return true;\n        }\n    }\n    \n    // 函数不存在该事件序列当中\n    return false;\n}"
  },
  {
    "path": "sample/src/main/assets/upload_file/jsuploadfile.html",
    "content": "<!DOCTYPE html>\n<html style=\"width: 100%;height: 100%;margin: 0;padding: 0\">\n\n<head>\n    <meta charset=\"UTF-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1,maximum-scale=1\">\n    <meta name=\"format-detection\" content=\"telephone=no\">\n    <meta name=\"apple-mobile-web-app-capable\" content=\"yes\">\n    <meta http-equiv=\"Expires\" content=\"0\">\n    <meta http-equiv=\"Cache-Control\" content=\"no-cache\">\n    <meta http-equiv=\"Pragma\" content=\"no-cache\">\n    <link rel=\"stylesheet\" type=\"text/css\" href=\"./upload.css\">\n    <title>上传身份证</title>\n    <script type=\"text/javascript\" src=\"event.js\"></script>\n    <script language=\"javascript\">\n    /**\n     * 从 file 域获取 本地图片 url\n     * \n     */\n    function getFileUrl(sourceId) {\n        var url;\n        console.log(navigator.userAgent);\n        if (navigator.userAgent.indexOf(\"MSIE\") >= 1) { // IE\n            url = document.getElementById(sourceId).value;\n        } else if (navigator.userAgent.indexOf(\"Firefox\") > 0) { // Firefox\n            url = window.URL.createObjectURL(document.getElementById(sourceId).files.item(0));\n        } else if (navigator.userAgent.indexOf(\"Chrome\") > 0) { // Chrome\n            url = window.URL.createObjectURL(document.getElementById(sourceId).files.item(0));\n        }\n        return url;\n    }\n\n    /**\n     * 将本地图片 显示到浏览器上\n     *\n     */\n    function preImg(sourceId, targetId) {\n        document.getElementById(\"uploadFile\")\n        var url = getFileUrl(sourceId);\n        console.log(url);\n        var imgPre = document.getElementById(targetId);\n        imgPre.src = url;\n    }\n\n\n\n    bindEvent(window, 'load', function() {\n        var ip = document.getElementById(\"file_upload\");\n\n\n        bindEvent(ip, 'click', function(e) {\n            // alert(\"我是\" + this + \"元素, 你点击了我!\");\n\n            //alert(\"Js  调 Android 方法成功\");\n            if (window.agentWeb != null && typeof(window.agentWeb) != \"undefined\") {\n                window.agentWeb.uploadFile();\n            } else {\n                alert(typeof(window.agentWeb));\n            }\n        });\n\n\n\n\n    });\n\n    //这里返回来的是一个 Json 数组 //\n    function uploadFileResult(objs) {\n\n        // console.log(message);\n        //alert(objs);\n        //alert(\"Android 调 Js 方法\");\n\n        if (objs == null || typeof(objs) == \"undefined\" || objs.length == 0) {\n\n            //alert(\"\");\n\n        } else {\n\n            var img = document.getElementById(\"preview\");\n            /*for(var i=0;i<objs.length;i++){  //\n            img.src=\"data:image/png;base64,\" + objs[i].fileBase64*/\n            if (objs[0] == null || objs[0] == 'undefined' || objs[0] == '' || objs[0].fileBase64 == null || objs[0].fileBase64 == 'undefined') {\n\n            } else {\n                img.src = \"data:image/png;base64,\" + objs[0].fileBase64;\n            }\n\n        }\n\n\n\n\n    }\n    </script>\n</head>\n\n<body style=\"width: 100%;height: 100%;margin: 0;padding: 0; position: relative;\">\n    <div style=\"position: absolute; left: 0 ;right: 0 ; top: 0 ; margin:0 auto;max-width: 750px;height: 100%\">\n        <div id=\"div_id\" style=\"width: 100% ; height:100%;border: 1px ;background:#000000 ; margin: 0px;\">\n            <!-- 头部nav -->\n            <div id=\"nav\" style=\"width: 100%; height: 10%;background: #f9f9f9;padding-top: 0px ;position: absolute;padding: 0px\">\n                <div style=\"width: 200px;height: 100px; position:relative;left: 50%;top: 50%;margin-left: -100px;margin-top: -50px;overflow: scroll;\">\n                    <font color=\"#000\">\n                        <h4 style=\"text-align:center;line-height: 50px;\">请上传身份证</h4> </font>\n                </div>\n                <div style=\"width: 100%;height: 1px;background: #c6c6c6;position: relative;margin-top: -50px;top: 99%\">\n                </div>\n            </div>\n            <!-- body -->\n            <div id=\"content\" style=\"width: 100%; height: 90% ; background: #ebebeb ;position: absolute;top: 10%;\">\n                <div style=\"margin: 0 auto;width: 80%;height: 80%;border:3px solid #41a0f2;margin-top: 50px;overflow:scroll;\">\n                    <img id=\"preview\" src=\"./id.png\" style=\"display: block;margin: 0 auto;max-width: 80%;margin-top: 20px;\">\n                    </img>\n\n                    <p class=\"fontBlue ta-c mt5\" style=\"text-align: center;\">上传身份证</p>\n                    <div id=\"file_upload\" href=\"javascript:;\" class=\"file\" style=\"display: block;margin: 0 auto;width: 50%;height: 25px;text-align: center;line-height: 25px;margin-bottom: 20px\">\n                        选择文件\n                    </div>\n                </div>\n            </div>\n        </div>\n    </div>\n</body>\n\n</html>"
  },
  {
    "path": "sample/src/main/assets/upload_file/upload.css",
    "content": ".file {\n    position: relative;\n    display: inline-block;\n    background: #D0EEFF;\n    border: 1px solid #99D3F5;\n    border-radius: 4px;\n    padding: 4px 12px;\n    overflow: hidden;\n    color: #1E88C7;\n    text-decoration: none;\n    text-indent: 0;\n    line-height: 20px;\n}\n.file input {\n    position: absolute;\n    font-size: 100px;\n    right: 0;\n    top: 0;\n    opacity: 0;\n}\n.file:hover {\n    background: #AADFFD;\n    border-color: #78C3F3;\n    color: #004974;\n    text-decoration: none;\n}\n"
  },
  {
    "path": "sample/src/main/assets/upload_file/uploadfile.html",
    "content": "<!DOCTYPE html>\n<html style=\"width: 100%;height: 100%;margin: 0;padding: 0\">\n\n<head>\n    <meta charset=\"UTF-8\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1,maximum-scale=1\">\n    <meta name=\"format-detection\" content=\"telephone=no\">\n    <meta name=\"apple-mobile-web-app-capable\" content=\"yes\">\n    <meta http-equiv=\"Expires\" content=\"0\">\n    <meta http-equiv=\"Cache-Control\" content=\"no-cache\">\n    <meta http-equiv=\"Pragma\" content=\"no-cache\">\n    <link rel=\"stylesheet\" type=\"text/css\" href=\"./upload.css\">\n    <title>上传身份证</title>\n    <script type=\"text/javascript\" src=\"event.js\"></script>\n    <script type=\"text/javascript\" src=\"../jquery/jquery-3.2.1.min.js\"></script>\n    <!--<script type=\"text/javascript\" src=\"jquery.js\"></script>-->\n    <script language=\"javascript\">\n    /**\n     * 从 file 域获取 本地图片 url\n     *\n     */\n    function getFileUrl(sourceId) {\n        var url;\n        console.log(navigator.userAgent);\n        if (navigator.userAgent.indexOf(\"MSIE\") >= 1) { // IE\n            url = document.getElementById(sourceId).value;\n        } else if (navigator.userAgent.indexOf(\"Firefox\") > 0) { // Firefox\n            url = window.URL.createObjectURL(document.getElementById(sourceId).files.item(0));\n        } else if (navigator.userAgent.indexOf(\"Chrome\") > 0) { // Chrome\n            url = window.URL.createObjectURL(document.getElementById(sourceId).files.item(0));\n        }\n        return url;\n    }\n\n    $(function() {\n        form.reset(); //清除浏览器记录的上次记录\n        var file;\n        $(form).on(\"change\", \"#file_upload\", function() {\n\n            // alert(\"call back\")\n            var $file = $(this);\n            var fileObj = $file[0];\n            var windowURL = window.URL || window.webkitURL;\n            var dataURL;\n            var $img = $(\"#preview\");\n\n            if (fileObj && fileObj.files && fileObj.files[0]) {\n\n                dataURL = windowURL.createObjectURL(fileObj.files[0]);\n                //dataURL = getFileUrl('file_upload');;                \n                //console.log(\" upload:\"+dataURL+\"    file:\"+fileObj.files[0]);\n                $img.attr('src', dataURL);\n            } else {\n                console.log(\"else  upload\");\n                dataURL = $file.val();\n\n\n\n                var imgObj = document.getElementById(\"preview\");\n                // 两个坑:\n                // 1、在设置filter属性时，元素必须已经存在在DOM树中，动态创建的Node，也需要在设置属性前加入到DOM中，先设置属性在加入，无效；\n                // 2、src属性需要像下面的方式添加，上面的两种方式添加，无效；\n                imgObj.style.filter = \"progid:DXImageTransform.Microsoft.AlphaImageLoader(sizingMethod=scale)\";\n                imgObj.filters.item(\"DXImageTransform.Microsoft.AlphaImageLoader\").src = dataURL;\n\n            }\n            //输出选中结果\n            console.log(this.value);\n            alert(this.value);\n            //每次选中都保存旧元素，并使用新的控件替换\n            $(this).clone().replaceAll(file = this);\n        }).submit(function() {\n            //提交时把之前保存的旧元素替换回去\n            $(\"#file_upload\").replaceWith(file);\n        });\n\n         $(form2).on(\"change\", \"#file_upload2\", function() {\n            alert(this.value);\n            document.getElementById(\"preview_path\").innerText=this.value;\n         }).submit(function() {\n            //提交时把之前保存的旧元素替换回去\n            $(\"#file_upload2\").replaceWith(file);\n        });\n    });\n\n\n    </script>\n</head>\n\n<body style=\"width: 100%;height: 100%;margin: 0;padding: 0; position: relative;background: #ebebeb\">\n<div style=\"position: absolute; left: 0 ;right: 0 ; top: 0 ; margin:0 auto;max-width: 750px;height: 100%\">\n    <div id=\"div_id\" style=\"width: 100% ; height:100%;border: 1px ; margin: 0px;\">\n        <!-- 头部nav -->\n        <div id=\"nav\"\n             style=\"width: 100%; height: 10%;background: #f9f9f9;padding-top: 0px ;position: absolute;padding: 0px\">\n            <div style=\"width: 200px;height: 100px; position:relative;left: 50%;top: 50%;margin-left: -100px;margin-top: -50px;overflow: scroll;\">\n                <font color=\"#000\">\n                    <h4 style=\"text-align:center;line-height: 50px;\">请上传身份证</h4></font>\n            </div>\n            <div style=\"width: 100%;height: 1px;background: #c6c6c6;position: relative;margin-top: -50px;top: 99%\">\n            </div>\n        </div>\n        <!-- body -->\n        <div id=\"content\" style=\"width: 100%; height: 90%;position: absolute;top: 10%;\">\n            <div style=\"margin: 0 auto;width: 80%;height: 80%;border:3px solid #41a0f2;margin-top: 50px;overflow: scroll;\">\n                <img id=\"preview\" src=\"./id.png\"\n                     style=\"display: block;margin: 0 auto;max-width: 60%;margin-top: 20px\">\n                </img>\n\n                <p class=\"fontBlue ta-c mt5\" style=\"text-align: center;\">上传身份证</p>\n                <a href=\"javascript:;\" class=\"file\"\n                   style=\"display: block;margin: 0 auto;width: 50%;height: 30px;text-align: center;line-height: 30px;margin-bottom: 20px\">选择文件\n                    <form id=\"form\">\n                        <input id=\"file_upload\" type=\"file\" name=\"\" id=\"\" accept=*/*,\"\n                               capture=\"camera\" multiple=\"multiple\">\n                    </form>\n                </a>\n            </div>\n            <div style=\"margin: 0 auto;width: 80%;height: 30%;border:3px solid #41a0f2;margin-top: 50px;overflow: scroll;\">\n\n                <div id=\"preview_path\" class=\"fontBlue ta-c mt5\" style=\"text-align: center;color:#000000;\">暂无视屏</div>\n                <p class=\"fontBlue ta-c mt5\" style=\"text-align: center;\">上传视频</p>\n                <a href=\"javascript:;\" class=\"file\"\n                   style=\"display: block;margin: 0 auto;width: 50%;height: 30px;text-align: center;line-height: 30px;margin-bottom: 20px\">选择文件\n                    <form id=\"form2\">\n                        <input id=\"file_upload2\" type=\"file\" name=\"\" accept=\"video/*\"\n                               capture=\"camera\" multiple=\"multiple\">\n                    </form>\n                </a>\n            </div>\n\n            <div style=\"margin: 0 auto;width: 80%;height: 10%;margin-top: 50px;overflow: scroll;\">\n            </div>\n        </div>\n    </div>\n</div>\n</div>\n</body>\n\n</html>"
  },
  {
    "path": "sample/src/main/java/com/just/agentweb/sample/activity/AutoHidenToolbarActivity.java",
    "content": "package com.just.agentweb.sample.activity;\n\nimport android.os.Bundle;\nimport com.google.android.material.appbar.AppBarLayout;\nimport androidx.coordinatorlayout.widget.CoordinatorLayout;\nimport androidx.appcompat.app.AppCompatActivity;\nimport androidx.appcompat.widget.Toolbar;\nimport android.view.KeyEvent;\nimport android.view.View;\nimport android.widget.TextView;\nimport android.widget.Toast;\n\nimport com.just.agentweb.AgentWeb;\nimport com.just.agentweb.NestedScrollAgentWebView;\nimport com.just.agentweb.sample.R;\n\npublic class AutoHidenToolbarActivity extends AppCompatActivity implements View.OnClickListener {\n\n    private AgentWeb mAgentWeb;\n    private CoordinatorLayout main;\n    private Toolbar toolbar;\n    /**\n     * 后退\n     */\n    private TextView btnBack;\n    /**\n     * 前进\n     */\n    private TextView btnForward;\n    /**\n     * 刷新\n     */\n    private TextView btnRefresh;\n    /**\n     * 菜单\n     */\n    private TextView btnMenu;\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_auto_hiden_toolbar);\n\n        initView();\n\n        setSupportActionBar(toolbar);\n\n        NestedScrollAgentWebView webView = new NestedScrollAgentWebView(this);\n\n        CoordinatorLayout.LayoutParams lp = new CoordinatorLayout.LayoutParams(-1, -1);\n        lp.setBehavior(new AppBarLayout.ScrollingViewBehavior());\n\n        mAgentWeb = AgentWeb.with(this)\n                .setAgentWebParent(main, 1, lp)//lp记得设置behavior属性\n                .useDefaultIndicator()\n                .setWebView(webView)\n                .createAgentWeb()\n                .ready()\n                .go(\"http://m.jd.com/\");\n\n    }\n\n    private void initView() {\n        main = findViewById(R.id.main);\n        toolbar = findViewById(R.id.toolbar);\n        btnBack = (TextView) findViewById(R.id.btn_back);\n        btnForward = (TextView) findViewById(R.id.btn_forward);\n        btnRefresh = (TextView) findViewById(R.id.btn_refresh);\n        btnMenu = (TextView) findViewById(R.id.btn_menu);\n        btnBack.setOnClickListener(this);\n        btnForward.setOnClickListener(this);\n        btnRefresh.setOnClickListener(this);\n        btnMenu.setOnClickListener(this);\n    }\n\n    @Override\n    public void onClick(View view) {\n        switch (view.getId()) {\n            case R.id.btn_back:\n                if (mAgentWeb.getWebCreator().getWebView().canGoBack()) {\n                    mAgentWeb.back();\n                } else {\n                    Toast.makeText(this, \"无法后退\", Toast.LENGTH_SHORT).show();\n                }\n                break;\n            case R.id.btn_forward:\n                if (mAgentWeb.getWebCreator().getWebView().canGoForward()) {\n                    mAgentWeb.getWebCreator().getWebView().goForward();\n                } else {\n                    Toast.makeText(this, \"无法前进\", Toast.LENGTH_SHORT).show();\n                }\n                break;\n            case R.id.btn_refresh:\n                mAgentWeb.getWebCreator().getWebView().reload();\n                break;\n            default:\n                Toast.makeText(this, \"这是菜单选项\", Toast.LENGTH_SHORT).show();\n                break;\n        }\n    }\n\n    @Override\n    public boolean onKeyDown(int keyCode, KeyEvent event) {\n        if (mAgentWeb.handleKeyEvent(keyCode, event)) {\n            return true;\n        }\n        return super.onKeyDown(keyCode, event);\n    }\n}\n"
  },
  {
    "path": "sample/src/main/java/com/just/agentweb/sample/activity/BaseWebActivity.java",
    "content": "package com.just.agentweb.sample.activity;\n\nimport android.content.DialogInterface;\nimport android.content.Intent;\nimport android.graphics.Bitmap;\nimport android.graphics.Color;\nimport android.os.Bundle;\nimport androidx.annotation.Nullable;\nimport androidx.appcompat.app.AlertDialog;\nimport androidx.appcompat.app.AppCompatActivity;\nimport androidx.appcompat.widget.Toolbar;\nimport android.util.Log;\nimport android.view.KeyEvent;\nimport android.view.View;\nimport android.webkit.WebResourceRequest;\nimport android.webkit.WebView;\nimport android.widget.LinearLayout;\nimport android.widget.TextView;\n\nimport com.just.agentweb.AgentWeb;\nimport com.just.agentweb.DefaultWebClient;\nimport com.just.agentweb.WebChromeClient;\nimport com.just.agentweb.WebViewClient;\nimport com.just.agentweb.sample.R;\nimport com.just.agentweb.sample.widget.WebLayout;\n\n/**\n * Created by cenxiaozhong on 2017/5/26.\n * <p>\n * source code  https://github.com/Justson/AgentWeb\n */\n\npublic class BaseWebActivity extends AppCompatActivity {\n\n\n    protected AgentWeb mAgentWeb;\n    private LinearLayout mLinearLayout;\n    private Toolbar mToolbar;\n    private TextView mTitleTextView;\n    private AlertDialog mAlertDialog;\n\n\n    @Override\n    protected void onCreate(@Nullable Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n\n        setContentView(R.layout.activity_web);\n\n\n        mLinearLayout = (LinearLayout) this.findViewById(R.id.container);\n        mToolbar = (Toolbar) this.findViewById(R.id.toolbar);\n        mToolbar.setTitleTextColor(Color.WHITE);\n        mToolbar.setTitle(\"\");\n        mTitleTextView = (TextView) this.findViewById(R.id.toolbar_title);\n        this.setSupportActionBar(mToolbar);\n        if (getSupportActionBar() != null) {\n            // Enable the Up button\n            getSupportActionBar().setDisplayHomeAsUpEnabled(true);\n        }\n        mToolbar.setNavigationOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View v) {\n\n                showDialog();\n            }\n        });\n\n\n        long p = System.currentTimeMillis();\n\n        mAgentWeb = AgentWeb.with(this)\n                .setAgentWebParent(mLinearLayout, new LinearLayout.LayoutParams(-1, -1))\n                .useDefaultIndicator()\n                .setWebChromeClient(mWebChromeClient)\n                .setWebViewClient(mWebViewClient)\n                .setMainFrameErrorView(com.just.agentweb.R.layout.agentweb_error_page, -1)\n                .setSecurityType(AgentWeb.SecurityType.STRICT_CHECK)\n                .setWebLayout(new WebLayout(this))\n                .setOpenOtherPageWays(DefaultWebClient.OpenOtherPageWays.ASK)//打开其他应用时，弹窗咨询用户是否前往其他应用\n                .interceptUnkownUrl() //拦截找不到相关页面的Scheme\n                .createAgentWeb()\n                .ready()\n                .go(getUrl());\n\n        //mAgentWeb.getUrlLoader().loadUrl(getUrl());\n\n        long n = System.currentTimeMillis();\n        Log.i(\"Info\", \"init used time:\" + (n - p));\n\n\n    }\n\n    private com.just.agentweb.WebViewClient mWebViewClient = new WebViewClient() {\n        @Override\n        public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {\n            return super.shouldOverrideUrlLoading(view, request);\n        }\n\n        @Override\n        public void onPageStarted(WebView view, String url, Bitmap favicon) {\n            //do you  work\n            Log.i(\"Info\", \"BaseWebActivity onPageStarted\");\n        }\n    };\n    private com.just.agentweb.WebChromeClient mWebChromeClient = new WebChromeClient() {\n        @Override\n        public void onReceivedTitle(WebView view, String title) {\n            super.onReceivedTitle(view, title);\n            if (mTitleTextView != null) {\n                mTitleTextView.setText(title);\n            }\n        }\n    };\n\n    public String getUrl() {\n        return \"https://m.jd.com/\";\n    }\n\n\n    private void showDialog() {\n\n        if (mAlertDialog == null) {\n            mAlertDialog = new AlertDialog.Builder(this)\n                    .setMessage(\"您确定要关闭该页面吗?\")\n                    .setNegativeButton(\"再逛逛\", new DialogInterface.OnClickListener() {\n                        @Override\n                        public void onClick(DialogInterface dialog, int which) {\n                            if (mAlertDialog != null) {\n                                mAlertDialog.dismiss();\n                            }\n                        }\n                    })//\n                    .setPositiveButton(\"确定\", new DialogInterface.OnClickListener() {\n                        @Override\n                        public void onClick(DialogInterface dialog, int which) {\n\n                            if (mAlertDialog != null) {\n                                mAlertDialog.dismiss();\n                            }\n                            BaseWebActivity.this.finish();\n                        }\n                    }).create();\n        }\n        mAlertDialog.show();\n\n    }\n\n    @Override\n    public boolean onKeyDown(int keyCode, KeyEvent event) {\n\n        if (mAgentWeb.handleKeyEvent(keyCode, event)) {\n            return true;\n        }\n        return super.onKeyDown(keyCode, event);\n    }\n\n    @Override\n    protected void onPause() {\n        mAgentWeb.getWebLifeCycle().onPause();\n        super.onPause();\n\n    }\n\n    @Override\n    protected void onResume() {\n        mAgentWeb.getWebLifeCycle().onResume();\n        super.onResume();\n    }\n\n    @Override\n    protected void onActivityResult(int requestCode, int resultCode, Intent data) {\n\n        Log.i(\"Info\", \"onResult:\" + requestCode + \" onResult:\" + resultCode);\n        super.onActivityResult(requestCode, resultCode, data);\n    }\n\n\n    @Override\n    protected void onDestroy() {\n        super.onDestroy();\n        //mAgentWeb.destroy();\n        mAgentWeb.getWebLifeCycle().onDestroy();\n    }\n}\n"
  },
  {
    "path": "sample/src/main/java/com/just/agentweb/sample/activity/CommonActivity.java",
    "content": "package com.just.agentweb.sample.activity;\n\nimport android.content.Intent;\nimport android.os.Bundle;\nimport androidx.annotation.Nullable;\nimport androidx.fragment.app.FragmentManager;\nimport androidx.fragment.app.FragmentTransaction;\nimport androidx.appcompat.app.AppCompatActivity;\nimport android.view.KeyEvent;\nimport android.widget.FrameLayout;\n\nimport com.just.agentweb.sample.R;\nimport com.just.agentweb.sample.common.FragmentKeyDown;\nimport com.just.agentweb.sample.fragment.AgentWebFragment;\nimport com.just.agentweb.sample.fragment.BounceWebFragment;\nimport com.just.agentweb.sample.fragment.CustomIndicatorFragment;\nimport com.just.agentweb.sample.fragment.CustomSettingsFragment;\nimport com.just.agentweb.sample.fragment.CustomWebViewFragment;\nimport com.just.agentweb.sample.fragment.JsAgentWebFragment;\nimport com.just.agentweb.sample.fragment.JsbridgeWebFragment;\nimport com.just.agentweb.sample.fragment.SmartRefreshWebFragment;\nimport com.just.agentweb.sample.fragment.VasSonicFragment;\n\nimport static com.just.agentweb.sample.activity.MainActivity.FLAG_GUIDE_DICTIONARY_BOUNCE_EFFACT;\nimport static com.just.agentweb.sample.activity.MainActivity.FLAG_GUIDE_DICTIONARY_CUSTOM_PROGRESSBAR;\nimport static com.just.agentweb.sample.activity.MainActivity.FLAG_GUIDE_DICTIONARY_CUSTOM_WEBVIEW_SETTINGS;\nimport static com.just.agentweb.sample.activity.MainActivity.FLAG_GUIDE_DICTIONARY_CUTSTOM_WEBVIEW;\nimport static com.just.agentweb.sample.activity.MainActivity.FLAG_GUIDE_DICTIONARY_FILE_DOWNLOAD;\nimport static com.just.agentweb.sample.activity.MainActivity.FLAG_GUIDE_DICTIONARY_INPUT_TAG_PROBLEM;\nimport static com.just.agentweb.sample.activity.MainActivity.FLAG_GUIDE_DICTIONARY_JSBRIDGE_SAMPLE;\nimport static com.just.agentweb.sample.activity.MainActivity.FLAG_GUIDE_DICTIONARY_JS_JAVA_COMMUNICATION;\nimport static com.just.agentweb.sample.activity.MainActivity.FLAG_GUIDE_DICTIONARY_JS_JAVA_COMUNICATION_UPLOAD_FILE;\nimport static com.just.agentweb.sample.activity.MainActivity.FLAG_GUIDE_DICTIONARY_LINKS;\nimport static com.just.agentweb.sample.activity.MainActivity.FLAG_GUIDE_DICTIONARY_MAP;\nimport static com.just.agentweb.sample.activity.MainActivity.FLAG_GUIDE_DICTIONARY_PULL_DOWN_REFRESH;\nimport static com.just.agentweb.sample.activity.MainActivity.FLAG_GUIDE_DICTIONARY_USE_IN_FRAGMENT;\nimport static com.just.agentweb.sample.activity.MainActivity.FLAG_GUIDE_DICTIONARY_VASSONIC_SAMPLE;\nimport static com.just.agentweb.sample.activity.MainActivity.FLAG_GUIDE_DICTIONARY_VIDEO_FULL_SCREEN;\nimport static com.just.agentweb.sample.activity.MainActivity.FLAG_GUIDE_DICTIONARY_WEBRTC;\nimport static com.just.agentweb.sample.sonic.SonicJavaScriptInterface.PARAM_CLICK_TIME;\n\n/**\n * Created by cenxiaozhong on 2017/5/23.\n * source code  https://github.com/Justson/AgentWeb\n */\n\npublic class CommonActivity extends AppCompatActivity {\n\n\n\tprivate FrameLayout mFrameLayout;\n\tpublic static final String TYPE_KEY = \"type_key\";\n\tprivate FragmentManager mFragmentManager;\n\n\t@Override\n\tprotected void onCreate(@Nullable Bundle savedInstanceState) {\n\t\tsuper.onCreate(savedInstanceState);\n\n\t\tsetContentView(R.layout.activity_common);\n\n\t\tmFrameLayout = (FrameLayout) this.findViewById(R.id.container_framelayout);\n\t\tint key = getIntent().getIntExtra(TYPE_KEY, -1);\n\t\tmFragmentManager = this.getSupportFragmentManager();\n\t\topenFragment(key);\n\t}\n\n\n\tprivate AgentWebFragment mAgentWebFragment;\n\n\tprivate void openFragment(int key) {\n\n\t\tFragmentTransaction ft = mFragmentManager.beginTransaction();\n\t\tBundle mBundle = null;\n\n\n\t\tswitch (key) {\n\n            /*Fragment 使用AgenWeb*/\n\t\t\tcase FLAG_GUIDE_DICTIONARY_USE_IN_FRAGMENT: //项目中请使用常量代替0 ， 代码可读性更高\n\t\t\t\t/*下载文件*/\n\t\t\tcase FLAG_GUIDE_DICTIONARY_FILE_DOWNLOAD:\n\t\t\t\tft.add(R.id.container_framelayout, mAgentWebFragment = AgentWebFragment.getInstance(mBundle = new Bundle()), AgentWebFragment.class.getName());\n\t\t\t\tmBundle.putString(AgentWebFragment.URL_KEY, \"http://android.myapp.com/\");\n\t\t\t\tbreak;\n\t\t\t/*input标签上传文件*/\n\t\t\tcase FLAG_GUIDE_DICTIONARY_INPUT_TAG_PROBLEM:\n\t\t\t\tft.add(R.id.container_framelayout, mAgentWebFragment = AgentWebFragment.getInstance(mBundle = new Bundle()), AgentWebFragment.class.getName());\n\t\t\t\tmBundle.putString(AgentWebFragment.URL_KEY, \"file:///android_asset/upload_file/uploadfile.html\");\n\t\t\t\tbreak;\n            /*Js上传文件*/\n\t\t\tcase FLAG_GUIDE_DICTIONARY_JS_JAVA_COMUNICATION_UPLOAD_FILE:\n\t\t\t\tft.add(R.id.container_framelayout, mAgentWebFragment = AgentWebFragment.getInstance(mBundle = new Bundle()), AgentWebFragment.class.getName());\n\t\t\t\tmBundle.putString(AgentWebFragment.URL_KEY, \"file:///android_asset/upload_file/jsuploadfile.html\");\n\t\t\t\tbreak;\n            /*Js*/\n\t\t\tcase FLAG_GUIDE_DICTIONARY_JS_JAVA_COMMUNICATION:\n\t\t\t\tft.add(R.id.container_framelayout, mAgentWebFragment = JsAgentWebFragment.getInstance(mBundle = new Bundle()), JsAgentWebFragment.class.getName());\n\t\t\t\tmBundle.putString(AgentWebFragment.URL_KEY, \"file:///android_asset/js_interaction/hello.html\");\n\t\t\t\tbreak;\n\t\t\t/*webrtc*/\n\t\t\tcase FLAG_GUIDE_DICTIONARY_WEBRTC:\n\t\t\t\tft.add(R.id.container_framelayout, mAgentWebFragment = AgentWebFragment.getInstance(mBundle = new Bundle()), AgentWebFragment.class.getName());\n\t\t\t\tmBundle.putString(AgentWebFragment.URL_KEY, \"https://jeromeetienne.github.io/AR.js/three.js/examples/mobile-performance.html\");\n\t\t\t\tbreak;\n            /*优酷全屏播放视屏*/\n\t\t\tcase FLAG_GUIDE_DICTIONARY_VIDEO_FULL_SCREEN:\n\t\t\t\tft.add(R.id.container_framelayout, mAgentWebFragment = AgentWebFragment.getInstance(mBundle = new Bundle()), AgentWebFragment.class.getName());\n\t\t\t\tmBundle.putString(AgentWebFragment.URL_KEY, \"https://m.youku.com/alipay_video/id_XNTExMjg3Njg1Mg==.html?spm=a2hww.12630578.drawer1.dzj1_1\");\n//                mBundle.putString(AgentWebFragment.URL_KEY, \"https://v.qq.com/x/page/i0530nu6z1a.html\");\n\t\t\t\tbreak;\n            /*淘宝自定义进度条*/\n\t\t\tcase FLAG_GUIDE_DICTIONARY_CUSTOM_PROGRESSBAR:\n\t\t\t\tft.add(R.id.container_framelayout, mAgentWebFragment = CustomIndicatorFragment.getInstance(mBundle = new Bundle()), CustomIndicatorFragment.class.getName());\n\t\t\t\tmBundle.putString(AgentWebFragment.URL_KEY, \"https://m.taobao.com/?sprefer=sypc00\");\n\t\t\t\tbreak;\n            /*豌豆荚*/\n\t\t\tcase FLAG_GUIDE_DICTIONARY_CUSTOM_WEBVIEW_SETTINGS:\n\t\t\t\tft.add(R.id.container_framelayout, mAgentWebFragment = CustomSettingsFragment.getInstance(mBundle = new Bundle()), CustomSettingsFragment.class.getName());\n\t\t\t\tmBundle.putString(AgentWebFragment.URL_KEY, \"https://m.wandoujia.com/\");\n\t\t\t\tbreak;\n\n            /*短信*/\n\t\t\tcase FLAG_GUIDE_DICTIONARY_LINKS:\n\t\t\t\tft.add(R.id.container_framelayout, mAgentWebFragment = AgentWebFragment.getInstance(mBundle = new Bundle()), AgentWebFragment.class.getName());\n\t\t\t\tmBundle.putString(AgentWebFragment.URL_KEY, \"file:///android_asset/sms/sms.html\");\n\t\t\t\tbreak;\n            /* 自定义 WebView */\n\t\t\tcase FLAG_GUIDE_DICTIONARY_CUTSTOM_WEBVIEW:\n\t\t\t\tft.add(R.id.container_framelayout, mAgentWebFragment = CustomWebViewFragment.getInstance(mBundle = new Bundle()), CustomWebViewFragment.class.getName());\n\t\t\t\tmBundle.putString(AgentWebFragment.URL_KEY, \"\");\n\t\t\t\tbreak;\n            /*回弹效果*/\n\t\t\tcase FLAG_GUIDE_DICTIONARY_BOUNCE_EFFACT:\n\t\t\t\tft.add(R.id.container_framelayout, mAgentWebFragment = BounceWebFragment.getInstance(mBundle = new Bundle()), BounceWebFragment.class.getName());\n\t\t\t\tmBundle.putString(AgentWebFragment.URL_KEY, \"http://m.mogujie.com/?f=mgjlm&ptp=_qd._cps______3069826.152.1.0\");\n\t\t\t\tbreak;\n\n            /*JsBridge 演示*/\n\t\t\tcase FLAG_GUIDE_DICTIONARY_JSBRIDGE_SAMPLE:\n\t\t\t\tft.add(R.id.container_framelayout, mAgentWebFragment = JsbridgeWebFragment.getInstance(mBundle = new Bundle()), JsbridgeWebFragment.class.getName());\n\t\t\t\tmBundle.putString(AgentWebFragment.URL_KEY, \"file:///android_asset/jsbridge/demo.html\");\n\t\t\t\tbreak;\n\n            /*SmartRefresh 下拉刷新*/\n\t\t\tcase FLAG_GUIDE_DICTIONARY_PULL_DOWN_REFRESH:\n\t\t\t\tft.add(R.id.container_framelayout, mAgentWebFragment = SmartRefreshWebFragment.getInstance(mBundle = new Bundle()), SmartRefreshWebFragment.class.getName());\n\t\t\t\tmBundle.putString(AgentWebFragment.URL_KEY, \"http://www.163.com/\");\n\t\t\t\tbreak;\n                /*地图*/\n\t\t\tcase FLAG_GUIDE_DICTIONARY_MAP:\n\t\t\t\tft.add(R.id.container_framelayout, mAgentWebFragment = AgentWebFragment.getInstance(mBundle = new Bundle()), AgentWebFragment.class.getName());\n\t\t\t\tmBundle.putString(AgentWebFragment.URL_KEY, \"https://map.baidu.com/mobile/webapp/index/index/#index/index/foo=bar/vt=map\");\n\t\t\t\tbreak;\n                /*首屏秒开*/\n\t\t\tcase FLAG_GUIDE_DICTIONARY_VASSONIC_SAMPLE:\n\t\t\t\tft.add(R.id.container_framelayout, mAgentWebFragment = VasSonicFragment.create(mBundle = new Bundle()), AgentWebFragment.class.getName());\n\t\t\t\tmBundle.putLong(PARAM_CLICK_TIME, getIntent().getLongExtra(PARAM_CLICK_TIME, -1L));\n\t\t\t\tmBundle.putString(AgentWebFragment.URL_KEY, \"http://mc.vip.qq.com/demo/indexv3\");\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tbreak;\n\n\t\t}\n\t\tft.commit();\n\n\t}\n\n\t@Override\n\tprotected void onActivityResult(int requestCode, int resultCode, Intent data) {\n\t\tsuper.onActivityResult(requestCode, resultCode, data);\n\t\t//一定要保证 mAentWebFragemnt 回调\n//\t\tmAgentWebFragment.onActivityResult(requestCode, resultCode, data);\n\t}\n\n\t@Override\n\tpublic boolean onKeyDown(int keyCode, KeyEvent event) {\n\n\t\tAgentWebFragment mAgentWebFragment = this.mAgentWebFragment;\n\t\tif (mAgentWebFragment != null) {\n\t\t\tFragmentKeyDown mFragmentKeyDown = mAgentWebFragment;\n\t\t\tif (mFragmentKeyDown.onFragmentKeyDown(keyCode, event)) {\n\t\t\t\treturn true;\n\t\t\t} else {\n\t\t\t\treturn super.onKeyDown(keyCode, event);\n\t\t\t}\n\t\t}\n\n\t\treturn super.onKeyDown(keyCode, event);\n\t}\n\n\n\n\n\t@Override\n\tprotected void onDestroy() {\n\t\tsuper.onDestroy();\n\n\t}\n}\n"
  },
  {
    "path": "sample/src/main/java/com/just/agentweb/sample/activity/ContainerActivity.java",
    "content": "package com.just.agentweb.sample.activity;\n\nimport android.os.Bundle;\nimport androidx.annotation.Nullable;\nimport androidx.fragment.app.Fragment;\nimport androidx.appcompat.app.AppCompatActivity;\n\nimport com.just.agentweb.sample.R;\nimport com.just.agentweb.sample.fragment.EasyWebFragment;\n\n/**\n * Created by cenxiaozhong on 2017/7/22.\n */\n\npublic class ContainerActivity extends AppCompatActivity {\n\n    @Override\n    protected void onCreate(@Nullable Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n\n        setContentView(R.layout.activity_common);\n\n        Fragment mFragment=null;\n        getSupportFragmentManager()\n                .beginTransaction()\n                .add(R.id.container_framelayout,mFragment= EasyWebFragment.getInstance(new Bundle()),EasyWebFragment.class.getName())\n                .show(mFragment)\n                .commit();\n    }\n}\n"
  },
  {
    "path": "sample/src/main/java/com/just/agentweb/sample/activity/EasyWebActivity.java",
    "content": "package com.just.agentweb.sample.activity;\n\nimport android.graphics.Color;\nimport android.os.Bundle;\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.appcompat.widget.Toolbar;\nimport android.text.TextUtils;\nimport android.view.KeyEvent;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.webkit.WebView;\nimport android.widget.LinearLayout;\nimport android.widget.TextView;\n\nimport com.just.agentweb.sample.R;\nimport com.just.agentweb.sample.base.BaseAgentWebActivity;\n\n/**\n * Created by cenxiaozhong on 2017/7/22.\n * <p>\n */\npublic class EasyWebActivity extends BaseAgentWebActivity {\n\n    private TextView mTitleTextView;\n\n    @Override\n    protected void onCreate(@Nullable Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_web);\n\n        LinearLayout mLinearLayout = (LinearLayout) this.findViewById(R.id.container);\n        Toolbar mToolbar = (Toolbar) this.findViewById(R.id.toolbar);\n        mToolbar.setTitleTextColor(Color.WHITE);\n        mToolbar.setTitle(\"\");\n        mTitleTextView = (TextView) this.findViewById(R.id.toolbar_title);\n        this.setSupportActionBar(mToolbar);\n        if (getSupportActionBar() != null) {\n            getSupportActionBar().setDisplayHomeAsUpEnabled(true);\n        }\n        mToolbar.setNavigationOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                EasyWebActivity.this.finish();\n            }\n        });\n    }\n\n\n    @NonNull\n    @Override\n    protected ViewGroup getAgentWebParent() {\n        return (ViewGroup) this.findViewById(R.id.container);\n    }\n\n    @Override\n    public boolean onKeyDown(int keyCode, KeyEvent event) {\n        if (mAgentWeb != null && mAgentWeb.handleKeyEvent(keyCode, event)) {\n            return true;\n        }\n\n        return super.onKeyDown(keyCode, event);\n    }\n\n    @Override\n    protected int getIndicatorColor() {\n        return Color.parseColor(\"#ff0000\");\n    }\n\n    @Override\n    protected void setTitle(WebView view, String title) {\n        super.setTitle(view, title);\n        if (!TextUtils.isEmpty(title)) {\n            if (title.length() > 10) {\n                title = title.substring(0, 10).concat(\"...\");\n            }\n        }\n        mTitleTextView.setText(title);\n    }\n\n    @Override\n    protected int getIndicatorHeight() {\n        return 3;\n    }\n\n    @Nullable\n    @Override\n    protected String getUrl() {\n        return \"https://www.baidu.com/\";\n    }\n}\n"
  },
  {
    "path": "sample/src/main/java/com/just/agentweb/sample/activity/ExternalActivity.java",
    "content": "package com.just.agentweb.sample.activity;\n\nimport android.os.Bundle;\nimport androidx.annotation.Nullable;\nimport android.util.Log;\n\n/**\n * @author cenxiaozhong\n * @date 2019-05-19\n * @since 1.0.0\n */\npublic class ExternalActivity extends WebActivity {\n\n\tpublic static final String TAG = ExternalActivity.class.getSimpleName();\n\n\t@Override\n\tprotected void onCreate(@Nullable Bundle savedInstanceState) {\n\t\tsuper.onCreate(savedInstanceState);\n\t}\n\n\t@Override\n\tpublic String getUrl() {\n\t\tString url = getIntent().getData().getQueryParameter(\"url\");\n\t\tLog.e(TAG, \" url:\" + url);\n\t\treturn url;\n\n\t}\n}\n"
  },
  {
    "path": "sample/src/main/java/com/just/agentweb/sample/activity/MainActivity.java",
    "content": "package com.just.agentweb.sample.activity;\n\nimport android.content.Intent;\nimport android.graphics.Color;\nimport android.os.Build;\nimport android.os.Bundle;\nimport androidx.annotation.RequiresApi;\nimport androidx.appcompat.app.AppCompatActivity;\nimport androidx.appcompat.widget.Toolbar;\nimport android.util.Log;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.AdapterView;\nimport android.widget.BaseAdapter;\nimport android.widget.ListView;\nimport android.widget.TextView;\n\nimport com.flyingpigeon.library.ServiceManager;\nimport com.flyingpigeon.library.annotations.thread.MainThread;\nimport com.just.agentweb.AgentWebConfig;\nimport com.just.agentweb.sample.R;\nimport com.just.agentweb.sample.api.Api;\nimport com.just.agentweb.sample.common.GuideItemEntity;\nimport com.just.agentweb.sample.fragment.AgentWebFragment;\n\nimport static com.just.agentweb.sample.sonic.SonicJavaScriptInterface.PARAM_CLICK_TIME;\n\n/**\n * source code  https://github.com/Justson/AgentWeb\n */\npublic class MainActivity extends AppCompatActivity {\n\n\n    private ListView mListView;\n\n    private Toolbar mToolbar;\n    private TextView mTitleTextView;\n    private static final String TAG = MainActivity.class.getSimpleName();\n\n    public static final int FLAG_GUIDE_DICTIONARY_USE_IN_ACTIVITY = 0x01;\n    public static final int FLAG_GUIDE_DICTIONARY_USE_IN_FRAGMENT = FLAG_GUIDE_DICTIONARY_USE_IN_ACTIVITY << 1;\n    public static final int FLAG_GUIDE_DICTIONARY_FILE_DOWNLOAD = FLAG_GUIDE_DICTIONARY_USE_IN_FRAGMENT << 1;\n    public static final int FLAG_GUIDE_DICTIONARY_INPUT_TAG_PROBLEM = FLAG_GUIDE_DICTIONARY_FILE_DOWNLOAD << 1;\n    public static final int FLAG_GUIDE_DICTIONARY_JS_JAVA_COMMUNICATION = FLAG_GUIDE_DICTIONARY_INPUT_TAG_PROBLEM << 1;\n    public static final int FLAG_GUIDE_DICTIONARY_VIDEO_FULL_SCREEN = FLAG_GUIDE_DICTIONARY_JS_JAVA_COMMUNICATION << 1;\n    public static final int FLAG_GUIDE_DICTIONARY_CUSTOM_PROGRESSBAR = FLAG_GUIDE_DICTIONARY_VIDEO_FULL_SCREEN << 1;\n    public static final int FLAG_GUIDE_DICTIONARY_CUSTOM_WEBVIEW_SETTINGS = FLAG_GUIDE_DICTIONARY_CUSTOM_PROGRESSBAR << 1;\n    public static final int FLAG_GUIDE_DICTIONARY_LINKS = FLAG_GUIDE_DICTIONARY_CUSTOM_WEBVIEW_SETTINGS << 1;\n    public static final int FLAG_GUIDE_DICTIONARY_BOUNCE_EFFACT = FLAG_GUIDE_DICTIONARY_LINKS << 1;\n    public static final int FLAG_GUIDE_DICTIONARY_JSBRIDGE_SAMPLE = FLAG_GUIDE_DICTIONARY_BOUNCE_EFFACT << 1;\n    public static final int FLAG_GUIDE_DICTIONARY_EXTENDS_BASE_ACT = FLAG_GUIDE_DICTIONARY_JSBRIDGE_SAMPLE << 1;\n    public static final int FLAG_GUIDE_DICTIONARY_EXTENDS_BASE_FRAG = FLAG_GUIDE_DICTIONARY_EXTENDS_BASE_ACT << 1;\n    public static final int FLAG_GUIDE_DICTIONARY_PULL_DOWN_REFRESH = FLAG_GUIDE_DICTIONARY_EXTENDS_BASE_FRAG << 1;\n    public static final int FLAG_GUIDE_DICTIONARY_MAP = FLAG_GUIDE_DICTIONARY_PULL_DOWN_REFRESH << 1;\n    public static final int FLAG_GUIDE_DICTIONARY_VASSONIC_SAMPLE = FLAG_GUIDE_DICTIONARY_MAP << 1;\n    public static final int FLAG_GUIDE_DICTIONARY_LINKAGE_WITH_TOOLBAR = FLAG_GUIDE_DICTIONARY_VASSONIC_SAMPLE << 1;\n    public static final int FLAG_GUIDE_DICTIONARY_CUTSTOM_WEBVIEW = FLAG_GUIDE_DICTIONARY_LINKAGE_WITH_TOOLBAR << 1;\n    public static final int FLAG_GUIDE_DICTIONARY_JS_JAVA_COMUNICATION_UPLOAD_FILE = FLAG_GUIDE_DICTIONARY_CUTSTOM_WEBVIEW << 1;\n    public static final int FLAG_GUIDE_DICTIONARY_COMMON_FILE_DOWNLOAD = FLAG_GUIDE_DICTIONARY_JS_JAVA_COMUNICATION_UPLOAD_FILE << 1;\n    public static final int FLAG_GUIDE_DICTIONARY_IPC = FLAG_GUIDE_DICTIONARY_COMMON_FILE_DOWNLOAD << 1;\n    public static final int FLAG_GUIDE_DICTIONARY_WEBRTC = FLAG_GUIDE_DICTIONARY_IPC << 1;\n\n\n    public static final GuideItemEntity[] datas = new GuideItemEntity[]{\n            new GuideItemEntity(\"Activity 使用 AgentWeb\", FLAG_GUIDE_DICTIONARY_USE_IN_ACTIVITY),\n            new GuideItemEntity(\"Fragment 使用 AgentWeb \", FLAG_GUIDE_DICTIONARY_USE_IN_FRAGMENT),\n            new GuideItemEntity(\"IPC WebView独立进程\", FLAG_GUIDE_DICTIONARY_IPC),\n            new GuideItemEntity(\"WebRTC 使用\", FLAG_GUIDE_DICTIONARY_WEBRTC),\n            new GuideItemEntity(\"H5文件下载\", FLAG_GUIDE_DICTIONARY_FILE_DOWNLOAD),\n            new GuideItemEntity(\"input标签文件上传\", FLAG_GUIDE_DICTIONARY_INPUT_TAG_PROBLEM),\n            new GuideItemEntity(\"Js 通信文件上传,兼用Android 4.4Kitkat\", FLAG_GUIDE_DICTIONARY_JS_JAVA_COMUNICATION_UPLOAD_FILE),\n            new GuideItemEntity(\"Js 通信\", FLAG_GUIDE_DICTIONARY_JS_JAVA_COMMUNICATION),\n            new GuideItemEntity(\"Video 视频全屏播放\", FLAG_GUIDE_DICTIONARY_VIDEO_FULL_SCREEN),\n            new GuideItemEntity(\"自定义进度条\", FLAG_GUIDE_DICTIONARY_CUSTOM_PROGRESSBAR),\n            new GuideItemEntity(\"自定义设置\", FLAG_GUIDE_DICTIONARY_CUSTOM_WEBVIEW_SETTINGS),\n            new GuideItemEntity(\"电话 ， 信息 ， 邮件\", FLAG_GUIDE_DICTIONARY_LINKS),\n            new GuideItemEntity(\"自定义 WebView\", FLAG_GUIDE_DICTIONARY_CUTSTOM_WEBVIEW),\n            new GuideItemEntity(\"下拉回弹效果\", FLAG_GUIDE_DICTIONARY_BOUNCE_EFFACT),\n            new GuideItemEntity(\"Jsbridge 例子\", FLAG_GUIDE_DICTIONARY_JSBRIDGE_SAMPLE),\n            new GuideItemEntity(\"继承 BaseAgentWebActivity\", FLAG_GUIDE_DICTIONARY_EXTENDS_BASE_ACT),\n            new GuideItemEntity(\"继承 BaseAgentWebFragment\", FLAG_GUIDE_DICTIONARY_EXTENDS_BASE_FRAG),\n            new GuideItemEntity(\"SmartRefresh 下拉刷新\", FLAG_GUIDE_DICTIONARY_PULL_DOWN_REFRESH),\n            new GuideItemEntity(\"地图\", FLAG_GUIDE_DICTIONARY_MAP),\n            new GuideItemEntity(\"VasSonic 首屏秒开\", FLAG_GUIDE_DICTIONARY_VASSONIC_SAMPLE),\n            new GuideItemEntity(\"与ToolBar联动\", FLAG_GUIDE_DICTIONARY_LINKAGE_WITH_TOOLBAR),\n            new GuideItemEntity(\"原生文件下载\", FLAG_GUIDE_DICTIONARY_COMMON_FILE_DOWNLOAD),\n    };\n\n\n    @RequiresApi(api = Build.VERSION_CODES.KITKAT)\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n\n\n        setContentView(R.layout.activity_main);\n\n\n        mToolbar = (Toolbar) this.findViewById(R.id.toolbar);\n        mToolbar.setTitleTextColor(Color.WHITE);\n        mToolbar.setTitle(\"\");\n        mTitleTextView = (TextView) this.findViewById(R.id.toolbar_title);\n        mTitleTextView.setText(\"AgentWeb 使用指南\");\n        this.setSupportActionBar(mToolbar);\n        if (getSupportActionBar() != null) {\n            // Enable the Up button\n            getSupportActionBar().setDisplayHomeAsUpEnabled(true);\n        }\n        mToolbar.setNavigationOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                MainActivity.this.finish();\n            }\n        });\n\n        mListView = (ListView) this.findViewById(R.id.listView);\n        mListView.setAdapter(new MainAdapter());\n        mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {\n            @Override\n            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {\n                doClick(position);\n            }\n        });\n\n\n        if (AgentWebConfig.DEBUG) {\n            Log.i(\"Info\", \"Debug 模式\");\n        } else {\n            Log.i(\"Info\", \"release 模式\");\n        }\n\n        AgentWebConfig.debug();\n        ServiceManager.getInstance().publish(mApi);\n    }\n\n    private Api mApi = new Api() {\n\n        @MainThread\n        // default callback on the bind Thread , if you wanna it callback on mainThread , may be you should add MainThread annotation\n        @Override\n        public void onReady() {\n            Log.e(TAG, \"web process onReady, i am runing on main process , received web procecss onready signal.\");\n        }\n    };\n\n    private void doClick(int position) {\n\n        int index = datas[position].getGuideDictionary();\n        switch (index) {\n\n            /* Activity agentWeb */\n            case FLAG_GUIDE_DICTIONARY_USE_IN_ACTIVITY:\n\n                startActivity(new Intent(this, WebActivity.class));\n                break;\n            case FLAG_GUIDE_DICTIONARY_USE_IN_FRAGMENT:\n                startActivity(new Intent(this, CommonActivity.class)\n                        .putExtra(CommonActivity.TYPE_KEY, FLAG_GUIDE_DICTIONARY_USE_IN_FRAGMENT));\n                break;\n            case FLAG_GUIDE_DICTIONARY_FILE_DOWNLOAD:\n                startActivity(new Intent(this, CommonActivity.class)\n                        .putExtra(CommonActivity.TYPE_KEY, FLAG_GUIDE_DICTIONARY_FILE_DOWNLOAD));\n                break;\n            case FLAG_GUIDE_DICTIONARY_WEBRTC:\n                startActivity(new Intent(this, CommonActivity.class)\n                        .putExtra(CommonActivity.TYPE_KEY, FLAG_GUIDE_DICTIONARY_WEBRTC));\n                break;\n            case FLAG_GUIDE_DICTIONARY_INPUT_TAG_PROBLEM:\n                startActivity(new Intent(this, CommonActivity.class)\n                        .putExtra(CommonActivity.TYPE_KEY, FLAG_GUIDE_DICTIONARY_INPUT_TAG_PROBLEM));\n                break;\n            case FLAG_GUIDE_DICTIONARY_JS_JAVA_COMUNICATION_UPLOAD_FILE:\n                startActivity(new Intent(this, CommonActivity.class)\n                        .putExtra(CommonActivity.TYPE_KEY, FLAG_GUIDE_DICTIONARY_JS_JAVA_COMUNICATION_UPLOAD_FILE));\n                break;\n            case FLAG_GUIDE_DICTIONARY_JS_JAVA_COMMUNICATION:\n                startActivity(new Intent(this, CommonActivity.class)\n                        .putExtra(CommonActivity.TYPE_KEY, FLAG_GUIDE_DICTIONARY_JS_JAVA_COMMUNICATION));\n                break;\n            case FLAG_GUIDE_DICTIONARY_VIDEO_FULL_SCREEN:\n                startActivity(new Intent(this, CommonActivity.class)\n                        .putExtra(CommonActivity.TYPE_KEY, FLAG_GUIDE_DICTIONARY_VIDEO_FULL_SCREEN));\n                break;\n\n            case FLAG_GUIDE_DICTIONARY_CUSTOM_PROGRESSBAR:\n                startActivity(new Intent(this, CommonActivity.class)\n                        .putExtra(CommonActivity.TYPE_KEY, FLAG_GUIDE_DICTIONARY_CUSTOM_PROGRESSBAR));\n                break;\n            case FLAG_GUIDE_DICTIONARY_CUSTOM_WEBVIEW_SETTINGS:\n                startActivity(new Intent(this, CommonActivity.class)\n                        .putExtra(CommonActivity.TYPE_KEY, FLAG_GUIDE_DICTIONARY_CUSTOM_WEBVIEW_SETTINGS));\n                break;\n            case FLAG_GUIDE_DICTIONARY_LINKS:\n                startActivity(new Intent(this, CommonActivity.class)\n                        .putExtra(CommonActivity.TYPE_KEY, FLAG_GUIDE_DICTIONARY_LINKS));\n                break;\n            case FLAG_GUIDE_DICTIONARY_CUTSTOM_WEBVIEW:\n                startActivity(new Intent(this, CommonActivity.class)\n                        .putExtra(CommonActivity.TYPE_KEY, FLAG_GUIDE_DICTIONARY_CUTSTOM_WEBVIEW));\n                break;\n            case FLAG_GUIDE_DICTIONARY_BOUNCE_EFFACT:\n                startActivity(new Intent(this, CommonActivity.class)\n                        .putExtra(CommonActivity.TYPE_KEY, FLAG_GUIDE_DICTIONARY_BOUNCE_EFFACT));\n                break;\n            case FLAG_GUIDE_DICTIONARY_JSBRIDGE_SAMPLE:\n                startActivity(new Intent(this, CommonActivity.class)\n                        .putExtra(CommonActivity.TYPE_KEY, FLAG_GUIDE_DICTIONARY_JSBRIDGE_SAMPLE));\n                break;\n            case FLAG_GUIDE_DICTIONARY_EXTENDS_BASE_ACT:\n                startActivity(new Intent(this, EasyWebActivity.class));\n                break;\n\n            case FLAG_GUIDE_DICTIONARY_EXTENDS_BASE_FRAG:\n                startActivity(new Intent(this, ContainerActivity.class));\n                break;\n\n            case FLAG_GUIDE_DICTIONARY_PULL_DOWN_REFRESH:\n                startActivity(new Intent(this, CommonActivity.class)\n                        .putExtra(CommonActivity.TYPE_KEY, FLAG_GUIDE_DICTIONARY_PULL_DOWN_REFRESH));\n                break;\n            case FLAG_GUIDE_DICTIONARY_MAP:\n                startActivity(new Intent(this, CommonActivity.class)\n                        .putExtra(CommonActivity.TYPE_KEY, FLAG_GUIDE_DICTIONARY_MAP));\n                break;\n            case FLAG_GUIDE_DICTIONARY_VASSONIC_SAMPLE:\n                startActivity(new Intent(this,\n                        CommonActivity.class).putExtra(CommonActivity.TYPE_KEY, FLAG_GUIDE_DICTIONARY_VASSONIC_SAMPLE)\n                        .putExtra(PARAM_CLICK_TIME, System.currentTimeMillis()));\n                break;\n            case FLAG_GUIDE_DICTIONARY_LINKAGE_WITH_TOOLBAR:\n                startActivity(new Intent(this, AutoHidenToolbarActivity.class));\n                break;\n            case FLAG_GUIDE_DICTIONARY_COMMON_FILE_DOWNLOAD:\n                startActivity(new Intent(this, NativeDownloadActivity.class));\n                break;\n            case FLAG_GUIDE_DICTIONARY_IPC:\n                startActivity(new Intent(this, RemoteWebViewlActivity.class).putExtra(AgentWebFragment.URL_KEY, \"https://m.vip.com/?source=www&jump_https=1\"));\n                break;\n            default:\n                break;\n\n        }\n\n\n    }\n\n    @Override\n    protected void onDestroy() {\n        super.onDestroy();\n        ServiceManager.getInstance().unpublish(mApi);\n    }\n\n    public class MainAdapter extends BaseAdapter {\n\n        @Override\n        public int getCount() {\n            return datas.length;\n        }\n\n        @Override\n        public Object getItem(int position) {\n            return datas[position];\n        }\n\n        @Override\n        public long getItemId(int position) {\n            return position;\n        }\n\n        @Override\n        public View getView(int position, View convertView, ViewGroup parent) {\n            ViewHolder mViewHolder;\n            if (convertView == null) {\n                mViewHolder = new ViewHolder();\n                View mView = MainActivity.this.getLayoutInflater().inflate(R.layout.listview_main, parent, false);\n                mViewHolder.mTextView = (TextView) mView.findViewById(R.id.content);\n                mView.setTag(mViewHolder);\n                convertView = mView;\n            } else {\n                mViewHolder = (ViewHolder) convertView.getTag();\n            }\n\n            mViewHolder.mTextView.setText(datas[position].getGuideTitle());\n            return convertView;\n        }\n\n\n    }\n\n    class ViewHolder {\n        TextView mTextView;\n    }\n\n\n}\n"
  },
  {
    "path": "sample/src/main/java/com/just/agentweb/sample/activity/NativeDownloadActivity.java",
    "content": "package com.just.agentweb.sample.activity;\n\nimport android.content.Context;\nimport android.graphics.Bitmap;\nimport android.graphics.Canvas;\nimport android.graphics.Color;\nimport android.graphics.Paint;\nimport android.graphics.PorterDuff;\nimport android.graphics.PorterDuffXfermode;\nimport android.graphics.Rect;\nimport android.graphics.RectF;\nimport android.net.Uri;\nimport android.os.Bundle;\nimport android.os.SystemClock;\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.appcompat.app.AppCompatActivity;\nimport androidx.recyclerview.widget.DefaultItemAnimator;\nimport androidx.recyclerview.widget.LinearLayoutManager;\nimport androidx.recyclerview.widget.RecyclerView;\nimport androidx.appcompat.widget.Toolbar;\nimport android.util.Log;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.Button;\nimport android.widget.ImageView;\nimport android.widget.ProgressBar;\nimport android.widget.TextView;\nimport android.widget.Toast;\n\nimport com.download.library.DownloadException;\nimport com.download.library.DownloadImpl;\nimport com.download.library.DownloadListenerAdapter;\nimport com.download.library.DownloadTask;\nimport com.download.library.Downloader;\nimport com.download.library.Extra;\nimport com.just.agentweb.sample.R;\nimport com.squareup.picasso.Picasso;\n\nimport java.util.ArrayList;\nimport java.util.Locale;\n\n//import com.download.library.DownloadException;\n//import com.download.library.DownloadImpl;\n//import com.download.library.DownloadListenerAdapter;\n//import com.download.library.DownloadTask;\n//import com.download.library.Downloader;\n//import com.download.library.Extra;\n//import com.download.library.Runtime;\n\n/**\n * @author ringle-android\n * @date 19-2-12\n * @since 1.0.0\n */\npublic class NativeDownloadActivity extends AppCompatActivity {\n    private RecyclerView mRecyclerView;\n    private Toolbar mToolbar;\n    private TextView mTitleTextView;\n    private ArrayList<DownloadBean> mDownloadTasks = new ArrayList<DownloadBean>();\n    private static final String TAG = NativeDownloadActivity.class.getSimpleName();\n\n    @Override\n    protected void onCreate(@Nullable Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_native_download);\n        createDatasource();\n        mToolbar = (Toolbar) this.findViewById(R.id.toolbar);\n        mToolbar.setTitleTextColor(Color.WHITE);\n        mToolbar.setTitle(\"\");\n        mTitleTextView = (TextView) this.findViewById(R.id.toolbar_title);\n        mTitleTextView.setText(\"原生下载\");\n        mRecyclerView = this.findViewById(R.id.download_recyclerview);\n        mRecyclerView.setLayoutManager(new LinearLayoutManager(this));\n        mRecyclerView.setItemAnimator(new DefaultItemAnimator());\n        mRecyclerView.setAdapter(new NativeDownloadAdapter());\n\n       /*new Thread(new Runnable() {\n            @Override\n            public void run() {\n                File file = DownloadImpl.getInstance().with(getApplicationContext()).url(\"http://shouji.360tpcdn.com/170918/93d1695d87df5a0c0002058afc0361f1/com.ss.android.article.news_636.apk\").setDownloadingListener(new DownloadListenerAdapter() {\n                    @Override\n                    public void onProgress(String url, long downloaded, long length, long usedTime) {\n                        super.onProgress(url, downloaded, length, usedTime);\n                        Log.i(TAG, \" downloaded:\" + downloaded);\n                    }\n\n                    @Override\n                    public boolean onResult(Throwable throwable, Uri path, String url, Extra extra) {\n                        Log.i(TAG, \"downloaded onResult isSuccess:\" + (throwable == null) + \" url:\" + url + \" Thread:\" + Thread.currentThread().getName() + \" uri:\" + path.toString());\n\n                        return super.onResult(throwable, path, url, extra);\n                    }\n                }).get();\n                Log.i(TAG, \" download success:\" + ((File) file).length());\n            }\n        }).start();*/\n        /*DownloadImpl.getInstance()\n                .with(getApplicationContext())\n                .setEnableIndicator(true)\n                .url(\"http://shouji.360tpcdn.com/170918/f7aa8587561e4031553316ada312ab38/com.tencent.qqlive_13049.apk\")\n                .enqueue(new DownloadListenerAdapter() {\n                    @Override\n                    public void onProgress(String url, long downloaded, long length, long usedTime) {\n                        super.onProgress(url, downloaded, length, usedTime);\n                        Log.i(TAG, \" progress:\" + downloaded + \" url:\" + url);\n                    }\n\n                    @Override\n                    public boolean onResult(Throwable throwable, Uri path, String url, Extra extra) {\n                        Log.i(TAG, \" path:\" + path + \" url:\" + url + \" length:\" + new File(path.getPath()).length());\n                        return super.onResult(throwable, path, url, extra);\n                    }\n                });\n\n        File file = new File(this.getCacheDir(), \"测试.apk\");\n        try {\n            file.createNewFile();\n        } catch (IOException e) {\n            e.printStackTrace();\n        }\n        DownloadImpl.getInstance()\n                .with(getApplicationContext())\n                .target(file)\n                .url(\"http://shouji.360tpcdn.com/170918/93d1695d87df5a0c0002058afc0361f1/com.ss.android.article.news_636.apk\")\n                .enqueue(new DownloadListenerAdapter() {\n                    @Override\n                    public void onProgress(String url, long downloaded, long length, long usedTime) {\n                        super.onProgress(url, downloaded, length, usedTime);\n                        Log.i(TAG, \" progress:\" + downloaded + \" url:\" + url);\n                    }\n\n                    @Override\n                    public boolean onResult(Throwable throwable, Uri path, String url, Extra extra) {\n                        Log.i(TAG, \" path:\" + path + \" url:\" + url + \" length:\" + new File(path.getPath()).length());\n                        return super.onResult(throwable, path, url, extra);\n                    }\n                });*/\n\n       /* DownloadImpl.getInstance()\n                .with(getApplicationContext())\n                .target(new File(Runtime.getInstance().getDir(this, true).getAbsolutePath() + \"/\" + \"com.ss.android.article.news_636.apk\"), this.getPackageName() + \".DownloadFileProvider\")//自定义路径需指定目录和authority(FileContentProvide),需要相对应匹配才能启动通知，和自动打开文件\n                .setUniquePath(false)//是否唯一路径\n                .setForceDownload(true)//不管网络类型\n                .setRetry(4)//下载异常，自动重试,最多重试4次\n                .setBlockMaxTime(60000L) //以8KB位单位，默认60s ，如果60s内无法从网络流中读满8KB数据，则抛出异常 。\n                .setConnectTimeOut(10000L)//连接10超时\n                .addHeader(\"xx\",\"cookie\")//添加请求头\n                .setDownloadTimeOut(Long.MAX_VALUE)//下载最大时长\n                .setOpenBreakPointDownload(true)//打开断点续传\n                .setParallelDownload(true)//打开多线程下载\n                .autoOpenWithMD5(\"93d1695d87df5a0c0002058afc0361f1\")//校验md5通过后自动打开该文件,校验失败会回调异常\n//                .autoOpenIgnoreMD5()\n//                .closeAutoOpen()\n                .quickProgress()//快速连续回调进度，默认1.2s回调一次\n                .url(\"http://shouji.360tpcdn.com/170918/93d1695d87df5a0c0002058afc0361f1/com.ss.android.article.news_636.apk\")\n                .enqueue(new DownloadListenerAdapter() {\n                    @Override\n                    public void onStart(String url, String userAgent, String contentDisposition, String mimetype, long contentLength, Extra extra) {\n                        super.onStart(url, userAgent, contentDisposition, mimetype, contentLength, extra);\n                    }\n\n                    @MainThread //加上该注解，自动回调到主线程\n                    @Override\n                    public void onProgress(String url, long downloaded, long length, long usedTime) {\n                        super.onProgress(url, downloaded, length, usedTime);\n                        Log.i(TAG, \" progress:\" + downloaded + \" url:\" + url);\n                    }\n\n                    @Override\n                    public boolean onResult(Throwable throwable, Uri path, String url, Extra extra) {\n                        String md5 = Runtime.getInstance().md5(new File(path.getPath()));\n                        Log.i(TAG, \" path:\" + path + \" url:\" + url + \" length:\" + new File(path.getPath()).length() + \" md5:\" + md5 + \" extra.getFileMD5:\" + extra.getFileMD5());\n                        return super.onResult(throwable, path, url, extra);\n                    }\n                });*/\n\n    }\n\n    private class NativeDownloadAdapter extends RecyclerView.Adapter<NativeDownloadViewHolder> {\n        @NonNull\n        @Override\n        public NativeDownloadViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {\n            View v = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.recyclerview_item_download, viewGroup, false);\n            return new NativeDownloadViewHolder(v);\n        }\n\n        @Override\n        public void onBindViewHolder(@NonNull final NativeDownloadViewHolder nativeDownloadViewHolder, final int i) {\n            final DownloadBean downloadBean = mDownloadTasks.get(i);\n            Picasso.get().load(downloadBean.imageUrl)\n                    .resize(100, 100)\n                    .centerCrop().\n                    transform(new RoundTransform(NativeDownloadActivity.this.getApplicationContext()))\n                    .into(nativeDownloadViewHolder.mIconIv);\n            nativeDownloadViewHolder.mStatusButton.setEnabled(true);\n            nativeDownloadViewHolder.mStatusButton.setTag(downloadBean);\n            if (downloadBean.getTotalsLength() > 0L) {\n                int mProgress = (int) ((downloadBean.getLoaded()) / Float.valueOf(downloadBean.getTotalsLength()) * 100);\n                Log.e(TAG, \"mProgress:\" + mProgress + \" position:\" + i);\n                nativeDownloadViewHolder.mProgressBar.setProgress(mProgress);\n                nativeDownloadViewHolder.mCurrentProgress.setText(\"当前进度\" + byte2FitMemorySize(downloadBean.getLoaded()) + \"/\" + byte2FitMemorySize(downloadBean.getTotalsLength()) + \" 耗时:\" + ((downloadBean.getUsedTime()) / 1000) + \"s\");\n            } else {\n                nativeDownloadViewHolder.mProgressBar.setProgress(0);\n                nativeDownloadViewHolder.mCurrentProgress.setText(\"当前进度,已下载:\" + byte2FitMemorySize(downloadBean.getLoaded()) + \" 耗时:\" + ((downloadBean.getUsedTime()) / 1000) + \"s\");\n            }\n            Log.e(TAG, \"status:\" + downloadBean.getStatus() + \" position:\" + i);\n            if (downloadBean.getStatus() == DownloadTask.STATUS_NEW) {\n                nativeDownloadViewHolder.mStatusButton.setText(\"开始\");\n            } else if (downloadBean.getStatus() == DownloadTask.STATUS_PENDDING) {\n                nativeDownloadViewHolder.mStatusButton.setText(\"等待中...\");\n                nativeDownloadViewHolder.mStatusButton.setEnabled(false);\n            } else if (downloadBean.getStatus() == DownloadTask.STATUS_PAUSED) {\n                nativeDownloadViewHolder.mStatusButton.setText(\"继续\");\n            } else if (downloadBean.getStatus() == DownloadTask.STATUS_DOWNLOADING) {\n                nativeDownloadViewHolder.mStatusButton.setText(\"暂停\");\n            } else if (downloadBean.getStatus() == DownloadTask.STATUS_CANCELED || downloadBean.getStatus() == DownloadTask.STATUS_ERROR) {\n                nativeDownloadViewHolder.mStatusButton.setText(\"出错\");\n                nativeDownloadViewHolder.mStatusButton.setEnabled(false);\n            } else {\n                nativeDownloadViewHolder.mStatusButton.setText(\"已完成\");\n                nativeDownloadViewHolder.mStatusButton.setEnabled(false);\n            }\n            nativeDownloadViewHolder.mStatusButton.setOnClickListener(new View.OnClickListener() {\n                long lastTime = SystemClock.elapsedRealtime();\n\n                @Override\n                public void onClick(View v) {\n                    if (SystemClock.elapsedRealtime() - lastTime <= 500) {\n                        return;\n                    }\n                    lastTime = SystemClock.elapsedRealtime();\n                    if (downloadBean.getStatus() == DownloadTask.STATUS_NEW) {\n                        nativeDownloadViewHolder.mStatusButton.setText(\"等待中...\");\n                        nativeDownloadViewHolder.mStatusButton.setEnabled(false);\n                        boolean isStarted = DownloadImpl.getInstance(getApplicationContext()).enqueue(downloadBean);\n                        if (!isStarted) {\n                            bindViewHolder(nativeDownloadViewHolder, i);\n                        }\n                    } else if (downloadBean.getStatus() == DownloadTask.STATUS_PENDDING) {\n                    } else if (downloadBean.getStatus() == DownloadTask.STATUS_DOWNLOADING) {\n                        DownloadTask downloadTask = DownloadImpl.getInstance(getApplicationContext()).pause(downloadBean.getUrl());\n                        if (downloadTask != null) {\n                            nativeDownloadViewHolder.mStatusButton.setText(\"继续\");\n                        } else {\n                            bindViewHolder(nativeDownloadViewHolder, i);\n                        }\n                    } else if (downloadBean.getStatus() == DownloadTask.STATUS_PAUSED) {\n                        boolean isStarted = DownloadImpl.getInstance(getApplicationContext()).resume(downloadBean.getUrl());\n                        nativeDownloadViewHolder.mStatusButton.setText(\"等待中...\");\n                        nativeDownloadViewHolder.mStatusButton.setEnabled(false);\n                        if (!isStarted) {\n                            bindViewHolder(nativeDownloadViewHolder, i);\n                        }\n                    } else if (downloadBean.getStatus() == DownloadTask.STATUS_CANCELED) {\n                    } else {\n                        nativeDownloadViewHolder.mStatusButton.setEnabled(false);\n                        nativeDownloadViewHolder.mStatusButton.setText(\"已完成\");\n                    }\n                }\n            });\n            downloadBean.setDownloadListenerAdapter(new DownloadListenerAdapter() {\n                @Override\n                public void onStart(String url, String userAgent, String contentDisposition, String mimetype, long contentLength, Extra extra) {\n                    nativeDownloadViewHolder.mStatusButton.setText(\"暂停\");\n                    nativeDownloadViewHolder.mStatusButton.setEnabled(true);\n                    Log.i(TAG, \" isRunning:\" + DownloadImpl.getInstance(getApplicationContext()).isRunning(url));\n                }\n\n                @MainThread //回调到主线程，添加该注释\n                @Override\n                public void onProgress(String url, long downloaded, long length, long usedTime) {\n                    if (nativeDownloadViewHolder.mStatusButton.getTag() != downloadBean) {\n                        Log.e(TAG, \"onProgress item recycle\");\n                        return;\n                    }\n                    int mProgress = (int) ((downloaded) / Float.valueOf(length) * 100);\n                    Log.i(TAG, \"onProgress:\" + mProgress + \" url:\" + url + \" Thread:\" + Thread.currentThread().getName());\n                    nativeDownloadViewHolder.mProgressBar.setProgress(mProgress);\n                    if (length <= 0) {\n                        nativeDownloadViewHolder.mCurrentProgress.setText(\"当前进度,已下载:\" + byte2FitMemorySize(downloaded) + \" 耗时:\" + ((downloadBean.getUsedTime()) / 1000) + \"s\");\n                    } else {\n                        nativeDownloadViewHolder.mCurrentProgress.setText(\"当前进度\" + byte2FitMemorySize(downloaded) + \"/\" + byte2FitMemorySize(length) + \" 耗时:\" + ((downloadBean.getUsedTime()) / 1000) + \"s\");\n\n                    }\n                }\n\n                @Override\n                public boolean onResult(Throwable throwable, Uri uri, String url, Extra extra) {\n                    if (nativeDownloadViewHolder.mStatusButton.getTag() != downloadBean) {\n                        Log.e(TAG, \"item recycle\");\n                        return super.onResult(throwable, uri, url, extra);\n                    }\n                    Log.i(TAG, \"onResult isSuccess:\" + (throwable == null) + \" url:\" + url + \" Thread:\" + Thread.currentThread().getName() + \" uri:\" + uri.toString() + \" isPaused:\" + DownloadImpl.getInstance(getApplicationContext()).isPaused(url));\n                    nativeDownloadViewHolder.mStatusButton.setEnabled(false);\n                    if (throwable == null) {\n                        nativeDownloadViewHolder.mStatusButton.setText(\"已完成\");\n                    } else if (throwable instanceof DownloadException) {\n                        DownloadException downloadException = (DownloadException) throwable;\n                        if (downloadException.getCode() == Downloader.ERROR_USER_PAUSE) {\n                            nativeDownloadViewHolder.mStatusButton.setText(\"继续\");\n                            nativeDownloadViewHolder.mStatusButton.setEnabled(true);\n                        } else {\n                            nativeDownloadViewHolder.mStatusButton.setText(\"出错\");\n                        }\n                        Toast.makeText(NativeDownloadActivity.this, downloadException.getMsg(), 1).show();\n                    }\n                    return super.onResult(throwable, uri, url, extra);\n                }\n            });\n\n        }\n\n        @Override\n        public int getItemCount() {\n            return mDownloadTasks.size();\n        }\n    }\n\n    private static String byte2FitMemorySize(final long byteNum) {\n        if (byteNum < 0) {\n            return \"\";\n        } else if (byteNum < 1024) {\n            return String.format(Locale.getDefault(), \"%.1fB\", (double) byteNum);\n        } else if (byteNum < 1048576) {\n            return String.format(Locale.getDefault(), \"%.1fKB\", (double) byteNum / 1024);\n        } else if (byteNum < 1073741824) {\n            return String.format(Locale.getDefault(), \"%.1fMB\", (double) byteNum / 1048576);\n        } else {\n            return String.format(Locale.getDefault(), \"%.1fGB\", (double) byteNum / 1073741824);\n        }\n    }\n\n    private class NativeDownloadViewHolder extends RecyclerView.ViewHolder {\n        ProgressBar mProgressBar;\n        Button mStatusButton;\n        ImageView mIconIv;\n        private final TextView mCurrentProgress;\n\n        public NativeDownloadViewHolder(@NonNull View itemView) {\n            super(itemView);\n            mIconIv = itemView.findViewById(R.id.icon_iv);\n            mStatusButton = itemView.findViewById(R.id.start_button);\n            mProgressBar = itemView.findViewById(R.id.progressBar);\n            mProgressBar.setMax(100);\n            mCurrentProgress = itemView.findViewById(R.id.current_progress);\n        }\n    }\n\n    public static class DownloadBean extends DownloadTask {\n        public String title;\n        public String imageUrl;\n\n        public DownloadBean(String title, String imageUrl, String url) {\n            this.title = title;\n            this.imageUrl = imageUrl;\n            this.mUrl = url;\n        }\n\n        @Override\n        protected DownloadBean setDownloadListenerAdapter(DownloadListenerAdapter downloadListenerAdapter) {\n            return (DownloadBean) super.setDownloadListenerAdapter(downloadListenerAdapter);\n        }\n\n        @Override\n        public DownloadBean setUrl(String url) {\n            return (DownloadBean) super.setUrl(url);\n        }\n\n        @Override\n        public DownloadBean setContext(Context context) {\n            return (DownloadBean) super.setContext(context);\n        }\n\n        @Override\n        public DownloadBean setEnableIndicator(boolean enableIndicator) {\n            return (DownloadBean) super.setEnableIndicator(enableIndicator);\n        }\n\n        @Override\n        public DownloadBean setRetry(int retry) {\n            return (DownloadBean) super.setRetry(retry);\n        }\n\n        @Override\n        public DownloadBean setQuickProgress(boolean quickProgress) {\n            return (DownloadBean) super.setQuickProgress(quickProgress);\n        }\n\n        @Override\n        public DownloadBean autoOpenIgnoreMD5() {\n            return (DownloadBean) super.autoOpenIgnoreMD5();\n        }\n    }\n\n    public static class RoundTransform implements com.squareup.picasso.Transformation {\n\n        private Context mContext;\n\n        public RoundTransform(Context context) {\n            mContext = context;\n        }\n\n        @Override\n        public Bitmap transform(Bitmap source) {\n\n            int widthLight = source.getWidth();\n            int heightLight = source.getHeight();\n            int radius = dp2px(mContext, 8); // 圆角半径\n\n            Bitmap output = Bitmap.createBitmap(source.getWidth(), source.getHeight(), Bitmap.Config.ARGB_8888);\n\n            Canvas canvas = new Canvas(output);\n            Paint paintColor = new Paint();\n            paintColor.setFlags(Paint.ANTI_ALIAS_FLAG);\n\n            RectF rectF = new RectF(new Rect(0, 0, widthLight, heightLight));\n\n            canvas.drawRoundRect(rectF, radius, radius, paintColor);\n//        canvas.drawRoundRect(rectF, widthLight / 5, heightLight / 5, paintColor);\n\n            Paint paintImage = new Paint();\n            paintImage.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_ATOP));\n            canvas.drawBitmap(source, 0, 0, paintImage);\n            source.recycle();\n            return output;\n        }\n\n        @Override\n        public String key() {\n            return \"roundcorner\";\n        }\n\n    }\n\n    public void createDatasource() {\n        DownloadBean downloadBean = new DownloadBean(\"QQ\", \"http://p18.qhimg.com/dr/72__/t0111cb71dabfd83b21.png\", \"https://d71329e5c0be6cdc2b46d0df2b4bd841.dd.cdntips.com/imtt.dd.qq.com/16891/apk/06AB1F5B0A51BEFD859B2B0D6B9ED9D9.apk?mkey=5d47b9f223f7bc0d&f=1806&fsname=com.tencent.mobileqq_8.1.0_1232.apk&csr=1bbd&cip=35.247.154.248&proto=https\");\n        downloadBean.setQuickProgress(true);\n        downloadBean.setRetry(4);\n        downloadBean.setContext(this.getApplicationContext());\n        mDownloadTasks.add(downloadBean);\n        downloadBean = new DownloadBean(\"支付宝\", \"http://p18.qhimg.com/dr/72__/t01a16bcd9acd07d029.png\", \"http://shouji.360tpcdn.com/170919/e7f5386759129f378731520a4c953213/com.eg.android.AlipayGphone_115.apk\");\n        downloadBean.setContext(this.getApplicationContext());\n        downloadBean.setQuickProgress(true);\n        mDownloadTasks.add(downloadBean);\n\n        downloadBean = new DownloadBean(\"UC\", \"http://p19.qhimg.com/dr/72__/t01195d02b486ef8ebe.png\", \"http://shouji.360tpcdn.com/170919/9f1c0f93a445d7d788519f38fdb3de77/com.UCMobile_704.apk\");\n        downloadBean.setContext(this.getApplicationContext());\n        downloadBean.setQuickProgress(true);\n        mDownloadTasks.add(downloadBean);\n\n        downloadBean = new DownloadBean(\"腾讯视频\", \"http://p18.qhimg.com/dr/72__/t01ed14e0ab1a768377.png\", \"http://shouji.360tpcdn.com/170918/f7aa8587561e4031553316ada312ab38/com.tencent.qqlive_13049.apk\");\n        downloadBean.setContext(this.getApplicationContext());\n        mDownloadTasks.add(downloadBean);\n\n        downloadBean = new DownloadBean(\"头条\", \"http://p15.qhimg.com/dr/72__/t013d31024ae54d9c35.png\", \"http://shouji.360tpcdn.com/170918/93d1695d87df5a0c0002058afc0361f1/com.ss.android.article.news_636.apk\");\n        downloadBean.setContext(this.getApplicationContext());\n        downloadBean.setEnableIndicator(false);\n        downloadBean.setQuickProgress(true);\n        mDownloadTasks.add(downloadBean);\n\n        downloadBean = new DownloadBean(\"应用宝\", \"https://pp.myapp.com/ma_icon/0/icon_5848_1565090584/96\", \"http://imtt.dd.qq.com/16891/myapp/channel_78665107_1000047_48e7227d3afeb842447c73c4b7af2509.apk?hsr=5848\");\n        downloadBean.setContext(this.getApplicationContext());\n        downloadBean.setEnableIndicator(false);\n        downloadBean.setQuickProgress(true);\n        mDownloadTasks.add(downloadBean);\n\n        downloadBean = new DownloadBean(\"附近越爱\", \"https://pp.myapp.com/ma_icon/0/icon_52396134_1563435176/96\", \"https://wxz.myapp.com/16891/apk/66339C385B32951E838F89AFDBB8AFBF.apk?fsname=com.wangjiang.fjya_5.6.3_98.apk&hsr=4d5s\");\n        downloadBean.setContext(this.getApplicationContext());\n        downloadBean.setEnableIndicator(false);\n        downloadBean.setQuickProgress(true);\n        mDownloadTasks.add(downloadBean);\n\n        downloadBean = new DownloadBean(\"白菜二手车\", \"https://pp.myapp.com/ma_icon/0/icon_52728407_1565231751/96\", \"http://imtt.dd.qq.com/16891/myapp/channel_78665107_1000047_48e7227d3afeb842447c73c4b7af2509.apk?hsr=5848&fsname=YYB.998886.dad220fda3959275efcb77f06835b974.1000047.apk\");\n        downloadBean.setContext(this.getApplicationContext());\n        downloadBean.setEnableIndicator(false);\n        downloadBean.setQuickProgress(true);\n        mDownloadTasks.add(downloadBean);\n\n        downloadBean = new DownloadBean(\"老鹰抓小鸡\", \"https://pp.myapp.com/ma_icon/0/icon_12097212_1555095310/96\", \"http://183.235.254.177/cache/112.29.208.41/imtt.dd.qq.com/16891/myapp/channel_78665107_1000047_48e7227d3afeb842447c73c4b7af2509.apk?mkey=5d5016b578e7f75c&f=184b&hsr=5848&fsname=YYB.998886.2e4a1c0f5a55b75a2e7a10c0b53a3491.1000047.apk&cip=120.231.209.169&proto=http&ich_args2=6-11231103023581_c2af2d3056e749ee2654202c210b6535_10004303_9c896229d2c0f7d3903d518939a83798_e03b546f591096a2b6182b487572fb16\");\n        downloadBean.setContext(this.getApplicationContext());\n        downloadBean.setEnableIndicator(false);\n        downloadBean.setQuickProgress(true);\n        mDownloadTasks.add(downloadBean);\n\n        downloadBean = new DownloadBean(\"2345手机助手\", \"https://pp.myapp.com/ma_icon/0/icon_10427994_1565164413/96\", \"https://wxz.myapp.com/16891/apk/14004450452AC52D15749001DBD0E4EA.apk?fsname=com.market2345_7.0_115.apk&hsr=4d5s\");\n        downloadBean.setContext(this.getApplicationContext());\n        downloadBean.setEnableIndicator(false);\n        downloadBean.setQuickProgress(true);\n        mDownloadTasks.add(downloadBean);\n\n        downloadBean = new DownloadBean(\"随手借\", \"https://pp.myapp.com/ma_pic2/0/shot_12170461_3_1564367665/550\", \"https://fb187cdbcc69278c9f1e6ce8e7257596.dd.cdntips.com/wxz.myapp.com/16891/apk/B505BB2B5D831592D5E190BAD5E66CCA.apk?mkey=5d50161b78e7f75c&f=1026&fsname=audaque.SuiShouJie_4.11.11_49.apk&hsr=4d5s&cip=120.231.209.169&proto=https\");\n        downloadBean.setContext(this.getApplicationContext());\n        downloadBean.setEnableIndicator(false);\n        downloadBean.setQuickProgress(true);\n        mDownloadTasks.add(downloadBean);\n\n        downloadBean = new DownloadBean(\"极光单词\", \"https://pp.myapp.com/ma_pic2/0/shot_52835037_1_1564713577/550\", \"https://6b7e49d6fab5c817409329478a000160.dd.cdntips.com/wxz.myapp.com/16891/apk/C721DE2D7E4538772FA98C1E9830F92F.apk?mkey=5d5017df78e7f75c&f=9870&fsname=com.qingclass.jgdc_2.0.4_9.apk&hsr=4d5s&cip=120.231.209.169&proto=https\");\n        downloadBean.setContext(this.getApplicationContext());\n        downloadBean.setEnableIndicator(false);\n        downloadBean.setQuickProgress(true);\n        mDownloadTasks.add(downloadBean);\n\n\n        downloadBean = new DownloadBean(\"帮帮测\", \"https://pp.myapp.com/ma_pic2/0/shot_52499136_3_1561616032/550\", \"https://fb187cdbcc69278c9f1e6ce8e7257596.dd.cdntips.com/wxz.myapp.com/16891/5571F5786B8E9F15058BE615B419A28B.apk?mkey=5d50176c78e7f75c&f=8ea4&fsname=com.bangbangce.mm_4.1.4_3104.apk&hsr=4d5s&cip=120.231.209.169&proto=https\");\n        downloadBean.setContext(this.getApplicationContext());\n        downloadBean.setEnableIndicator(false);\n        downloadBean.setQuickProgress(true);\n        mDownloadTasks.add(downloadBean);\n\n        downloadBean = new DownloadBean(\"速贷之家\", \"https://pp.myapp.com/ma_pic2/0/shot_42330202_2_1564649857/550\", \"https://3e25603914f997244c41c1ed7fbedfb5.dd.cdntips.com/wxz.myapp.com/16891/apk/7AADD4A8C9D404FB97378EA3CA2E69E6.apk?mkey=5d50172c78e7f75c&f=184b&fsname=com.yeer.sdzj_3.2.8_328.apk&hsr=4d5s&cip=120.231.209.169&proto=https\");\n        downloadBean.setContext(this.getApplicationContext());\n        downloadBean.setEnableIndicator(false);\n        downloadBean.setQuickProgress(true);\n        mDownloadTasks.add(downloadBean);\n\n\n        downloadBean = new DownloadBean(\"中原消费金融\", \"https://pp.myapp.com/ma_pic2/0/shot_52471681_2_1565161792/550\", \"https://f437b8a1a8be40951a91f58666e659d0.dd.cdntips.com/wxz.myapp.com/16891/apk/B1C6CC0DB7D412DA47A3A446E28D9C09.apk?mkey=5d5014fb78e7f75c&f=24c5&fsname=com.hnzycfc.zyxj_3.0.1_52.apk&hsr=4d5s&cip=120.231.209.169&proto=https\");\n        downloadBean.setContext(this.getApplicationContext());\n        downloadBean.setEnableIndicator(false);\n        downloadBean.setQuickProgress(true);\n        mDownloadTasks.add(downloadBean);\n\n\n        downloadBean = new DownloadBean(\"店长直聘\", \"https://pp.myapp.com/ma_icon/0/icon_12216213_1564373730/96\", \"https://f437b8a1a8be40951a91f58666e659d0.dd.cdntips.com/wxz.myapp.com/16891/apk/FA29D09A6CD550DCBEBC1D89EA392109.apk?mkey=5d5014b478e7f75c&f=1849&fsname=com.hpbr.directhires_4.31_403010.apk&hsr=4d5s&cip=120.231.209.169&proto=https\");\n        downloadBean.setContext(this.getApplicationContext());\n        downloadBean.setEnableIndicator(false);\n        downloadBean.setQuickProgress(true);\n        mDownloadTasks.add(downloadBean);\n\n\n        downloadBean = new DownloadBean(\"淘卷吧\", \"https://pp.myapp.com/ma_icon/0/icon_42320744_1564583832/96\", \"https://11473001bb572df6cb60e7e0821a4586.dd.cdntips.com/wxz.myapp.com/16891/apk/4AA997287EEA4A96C2DFD97CEE0180AD.apk?mkey=5d50148f78e7f75c&f=24c5&fsname=com.ciyun.oneshop_7.07_69.apk&hsr=4d5s&cip=120.231.209.169&proto=https\");\n        downloadBean.setContext(this.getApplicationContext());\n        downloadBean.setEnableIndicator(false);\n        downloadBean.setQuickProgress(true);\n        mDownloadTasks.add(downloadBean);\n\n\n        downloadBean = new DownloadBean(\"本地寻爱\", \"https://pp.myapp.com/ma_icon/0/icon_53268261_1564479560/96\", \"https://ce7ce9c885b5c04b6771ea454e096946.dd.cdntips.com/wxz.myapp.com/16891/apk/AAB98D7BDAFB390FA4D37F6CBD910992.apk?mkey=5d50142d78e7f75c&f=07b4&fsname=com.kaitai.bdxa_5.6.3_98.apk&hsr=4d5s&cip=120.231.209.169&proto=https\");\n        downloadBean.setContext(this.getApplicationContext());\n        downloadBean.setEnableIndicator(false);\n        downloadBean.setQuickProgress(true);\n        mDownloadTasks.add(downloadBean);\n\n\n        downloadBean = new DownloadBean(\"360借条\", \"https://pp.myapp.com/ma_icon/0/icon_42379225_1564124706/96\", \"https://e2983106ebfb9f560ff3a8e230faa981.dd.cdntips.com/wxz.myapp.com/16891/apk/DEB654116EC627ABA4DB12A6E777EAAD.apk?mkey=5d5015d578e7f75c&f=1026&fsname=com.qihoo.loan_1.5.4_213.apk&hsr=4d5s&cip=120.231.209.169&proto=https\");\n        downloadBean.setContext(this.getApplicationContext());\n        downloadBean.setEnableIndicator(false);\n        downloadBean.setQuickProgress(true);\n        mDownloadTasks.add(downloadBean);\n\n\n        downloadBean = new DownloadBean(\"淘宝\", \"https://pp.myapp.com/ma_icon/0/icon_5080_1564463763/96\", \"http://shouji.360tpcdn.com/170901/ec1eaad9d0108b30d8bd602da9954bb7/com.taobao.taobao_161.apk\");\n        downloadBean.setContext(this.getApplicationContext());\n        mDownloadTasks.add(downloadBean);\n\n        //http://www.httpwatch.com/httpgallery/chunked/chunkedimage.aspx?0.04400023248109086\n\n        downloadBean = new DownloadBean(\"分块传输，图片\", \"http://www.httpwatch.com/httpgallery/chunked/chunkedimage.aspx?0.04400023248109086\", \"http://www.httpwatch.com/httpgallery/chunked/chunkedimage.aspx?0.04400023248109086\");\n        downloadBean.setContext(this.getApplicationContext());\n        downloadBean.autoOpenIgnoreMD5();\n        mDownloadTasks.add(downloadBean);\n\n\n        downloadBean = new DownloadBean(\"也爱直播\", \"https://pp.myapp.com/ma_icon/0/icon_10472625_1555686747/96\", \"https://a46fefcd092f5f917ed1ee349b85d3b7.dd.cdntips.com/wxz.myapp.com/16891/F9B7FA7EC195FC453AE9082F826E6B28.apk?mkey=5d4c6bdc78e5058d&f=1806&fsname=com.tiange.hz.paopao8_4.4.1_441.apk&hsr=4d5s&cip=120.229.35.120&proto=https\");\n        downloadBean.setContext(this.getApplicationContext());\n        downloadBean.autoOpenIgnoreMD5().setQuickProgress(true);\n        mDownloadTasks.add(downloadBean);\n\n        //\n    }\n\n    public static int dp2px(Context context, float dpValue) {\n        final float scale = context.getResources().getDisplayMetrics().density;\n        return (int) (dpValue * scale + 0.5f);\n    }\n\n    @Override\n    protected void onDestroy() {\n        super.onDestroy();\n        DownloadImpl.getInstance(getApplicationContext()).cancelAll();\n    }\n}\n"
  },
  {
    "path": "sample/src/main/java/com/just/agentweb/sample/activity/RemoteWebViewlActivity.java",
    "content": "package com.just.agentweb.sample.activity;\n\nimport android.os.Bundle;\nimport androidx.annotation.Nullable;\nimport android.util.Log;\n\nimport com.flyingpigeon.library.Pigeon;\nimport com.flyingpigeon.library.ServiceManager;\nimport com.flyingpigeon.library.annotations.Route;\nimport com.flyingpigeon.library.annotations.thread.MainThread;\nimport com.just.agentweb.sample.api.Api;\nimport com.just.agentweb.sample.provider.ServiceProvider;\nimport com.queue.library.GlobalQueue;\n\n\n/**\n * @author cenxiaozhong\n * @since 1.0.0\n */\npublic class RemoteWebViewlActivity extends WebActivity {\n\n    public static final String TAG = RemoteWebViewlActivity.class.getSimpleName();\n\n    @Override\n    protected void onCreate(@Nullable Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        ServiceManager.getInstance().publish(this);\n        GlobalQueue.getMainQueue().postRunnable(new Runnable() {\n            @Override\n            public void run() {\n                sayYes();\n            }\n        }, 500);\n    }\n\n    private void sayYes() {\n        Pigeon pigeon = Pigeon.newBuilder(this.getApplicationContext()).setAuthority(ServiceProvider.class).build();\n        Api api = pigeon.create(Api.class);\n        api.onReady();\n    }\n\n    @Override\n    public String getUrl() {\n        String url = getIntent().getStringExtra(\"url_key\");\n        Log.e(TAG, \" url:\" + url);\n        return url;\n    }\n\n    /**\n     * follow this , you could invoke this method anywhere\n     *\n     * Pigeon pigeon = Pigeon.newBuilder(this.getApplicationContext()).setAuthority(\"WebServiceProvider.class\").build();\n     * pigeon.route(\"/load/newUrl\").withString(\"url_key\", \"http://baidu.com\").fly();\n     * @param in\n     */\n    @Route(\"/load/newUrl\")\n    @MainThread\n    public void loadNewUrl(Bundle in) {\n        mAgentWeb.getUrlLoader().loadUrl(in.getString(\"url_key\"));\n    }\n\n\n    @Override\n    protected void onDestroy() {\n        super.onDestroy();\n        ServiceManager.getInstance().unpublish(this);\n    }\n}\n"
  },
  {
    "path": "sample/src/main/java/com/just/agentweb/sample/activity/WebActivity.java",
    "content": "package com.just.agentweb.sample.activity;\n\n/**\n * Created by cenxiaozhong on 2017/5/22.\n *  <p>\n *\n */\n\npublic class WebActivity extends BaseWebActivity {\n\n    @Override\n    public String getUrl() {\n        return super.getUrl();\n    }\n\n    @Override\n    protected void onStart() {\n        super.onStart();\n    }\n\n    @Override\n    protected void onResume() {\n        super.onResume();\n\n        //测试Cookies\n        /*try {\n\n            String targetUrl=\"\";\n            Log.i(\"Info\",\"cookies:\"+ AgentWebConfig.getCookiesByUrl(targetUrl=\"http://www.jd.com\"));\n            AgentWebConfig.removeAllCookies(new ValueCallback<Boolean>() {\n                @Override\n                public void onReceiveValue(Boolean value) {\n                    Log.i(\"Info\",\"onResume():\"+value);\n                }\n            });\n\n            String tagInfo=AgentWebConfig.getCookiesByUrl(targetUrl);\n            Log.i(\"Info\",\"tag:\"+tagInfo);\n            AgentWebConfig.syncCookie(\"http://www.jd.com\",\"ID=IDHl3NVU0N3ltZm9OWHhubHVQZW1BRThLdGhLaFc5TnVtQWd1S2g1REcwNVhTS3RXQVFBQEBFDA984906B62C444931EA0\");\n            String tag=AgentWebConfig.getCookiesByUrl(targetUrl);\n            Log.i(\"Info\",\"tag:\"+tag);\n            AgentWebConfig.removeSessionCookies();\n            Log.i(\"Info\",\"removeSessionCookies:\"+AgentWebConfig.getCookiesByUrl(targetUrl));\n        }  catch (Exception e){\n            e.printStackTrace();\n        }*/\n\n    }\n}\n"
  },
  {
    "path": "sample/src/main/java/com/just/agentweb/sample/api/Api.java",
    "content": "package com.just.agentweb.sample.api;\n\n/**\n * @author xiaozhongcen\n * @date 20-8-18\n * @since 1.0.0\n */\npublic interface Api {\n\n    void onReady();\n\n}\n"
  },
  {
    "path": "sample/src/main/java/com/just/agentweb/sample/app/App.java",
    "content": "package com.just.agentweb.sample.app;\n\nimport android.app.Application;\nimport android.content.Context;\nimport android.content.Intent;\n\nimport com.just.agentweb.AgentWebCompat;\nimport com.just.agentweb.sample.service.WebService;\nimport com.queue.library.GlobalQueue;\n\n/**\n * Created by cenxiaozhong on 2017/5/23.\n * source code  https://github.com/Justson/AgentWeb\n */\n\npublic class App extends Application {\n\n    @Override\n    public void onCreate() {\n        super.onCreate();\n\n        /**\n         * 说明， WebView 初处初始化耗时 250ms 左右。\n         * 提前初始化WebView ，好处可以提升页面初始化速度，减少白屏时间，\n         * 坏处，拖慢了App 冷启动速度，如果 WebView 配合 VasSonic 使用，\n         * 建议不要在此处提前初始化 WebView 。\n         */\n//        WebView mWebView=new WebView(new MutableContextWrapper(this));\n\n//        if (LeakCanary.isInAnalyzerProcess(this)) {\n//            // This process is dedicated to LeakCanary for heap analysis.\n//            // You should not init your app in this process.\n//            return;\n//        }\n//        LeakCanary.install(this);\n        // Normal app init code...\n\n        //implementation 'com.github.Justson:dispatch-queue:v1.0.5'\n        GlobalQueue.getMainQueue().postRunnableInIdleRunning(new Runnable() {\n            @Override\n            public void run() {\n                try {\n                    startService(new Intent(App.this, WebService.class));\n                } catch (Throwable throwable) {\n\n                }\n            }\n        });\n    }\n\n    public static Context mContext;\n\n    @Override\n    protected void attachBaseContext(Context base) {\n        super.attachBaseContext(base);\n        mContext = base;\n        AgentWebCompat.setDataDirectorySuffix(base);\n    }\n\n\n}\n"
  },
  {
    "path": "sample/src/main/java/com/just/agentweb/sample/base/BaseAgentWebActivity.java",
    "content": "package com.just.agentweb.sample.base;\n\nimport android.content.Intent;\nimport android.os.Bundle;\nimport androidx.annotation.ColorInt;\nimport androidx.annotation.LayoutRes;\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.appcompat.app.AppCompatActivity;\nimport android.view.KeyEvent;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.webkit.WebView;\n\nimport com.just.agentweb.AgentWeb;\nimport com.just.agentweb.AgentWebSettingsImpl;\nimport com.just.agentweb.AgentWebUIControllerImplBase;\nimport com.just.agentweb.DefaultWebClient;\nimport com.just.agentweb.IAgentWebSettings;\nimport com.just.agentweb.IWebLayout;\nimport com.just.agentweb.MiddlewareWebChromeBase;\nimport com.just.agentweb.MiddlewareWebClientBase;\nimport com.just.agentweb.PermissionInterceptor;\nimport com.just.agentweb.WebChromeClient;\nimport com.just.agentweb.WebViewClient;\n\n/**\n * Created by cenxiaozhong on 2017/7/22.\n * <p>\n * source code  https://github.com/Justson/AgentWeb\n */\n\npublic abstract class BaseAgentWebActivity extends AppCompatActivity {\n\n    protected AgentWeb mAgentWeb;\n    private AgentWebUIControllerImplBase mAgentWebUIController;\n    private ErrorLayoutEntity mErrorLayoutEntity;\n    private MiddlewareWebChromeBase mMiddleWareWebChrome;\n    private MiddlewareWebClientBase mMiddleWareWebClient;\n\n    @Override\n    protected void onCreate(@Nullable Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n\n    }\n\n    @Override\n    public void setContentView(@LayoutRes int layoutResID) {\n        super.setContentView(layoutResID);\n        buildAgentWeb();\n    }\n\n    @Override\n    public void setContentView(View view) {\n        super.setContentView(view);\n        buildAgentWeb();\n    }\n\n    protected void buildAgentWeb() {\n        ErrorLayoutEntity mErrorLayoutEntity = getErrorLayoutEntity();\n        mAgentWeb = AgentWeb.with(this)\n                .setAgentWebParent(getAgentWebParent(), new ViewGroup.LayoutParams(-1, -1))\n                .useDefaultIndicator(getIndicatorColor(), getIndicatorHeight())\n                .setWebChromeClient(getWebChromeClient())\n                .setWebViewClient(getWebViewClient())\n                .setWebView(getWebView())\n                .setPermissionInterceptor(getPermissionInterceptor())\n                .setWebLayout(getWebLayout())\n                .setAgentWebUIController(getAgentWebUIController())\n                .interceptUnkownUrl()\n                .setOpenOtherPageWays(getOpenOtherAppWay())\n                .useMiddlewareWebChrome(getMiddleWareWebChrome())\n                .useMiddlewareWebClient(getMiddleWareWebClient())\n                .setAgentWebWebSettings(getAgentWebSettings())\n                .setMainFrameErrorView(mErrorLayoutEntity.layoutRes, mErrorLayoutEntity.reloadId)\n                .setSecurityType(AgentWeb.SecurityType.STRICT_CHECK)\n                .createAgentWeb()\n                .ready()\n                .go(getUrl());\n\n\n    }\n\n\n    protected @NonNull\n    ErrorLayoutEntity getErrorLayoutEntity() {\n        if (this.mErrorLayoutEntity == null) {\n            this.mErrorLayoutEntity = new ErrorLayoutEntity();\n        }\n        return mErrorLayoutEntity;\n    }\n\n    protected AgentWeb getAgentWeb() {\n        return this.mAgentWeb;\n    }\n\n\n    protected static class ErrorLayoutEntity {\n        private int layoutRes = com.just.agentweb.R.layout.agentweb_error_page;\n        private int reloadId;\n\n        public void setLayoutRes(int layoutRes) {\n            this.layoutRes = layoutRes;\n            if (layoutRes <= 0) {\n                layoutRes = -1;\n            }\n        }\n\n        public void setReloadId(int reloadId) {\n            this.reloadId = reloadId;\n            if (reloadId <= 0) {\n                reloadId = -1;\n            }\n        }\n    }\n\n    @Override\n    public boolean onKeyDown(int keyCode, KeyEvent event) {\n\n        if (mAgentWeb != null && mAgentWeb.handleKeyEvent(keyCode, event)) {\n            return true;\n        }\n        return super.onKeyDown(keyCode, event);\n    }\n\n    @Override\n    protected void onPause() {\n        if (mAgentWeb != null) {\n            mAgentWeb.getWebLifeCycle().onPause();\n        }\n        super.onPause();\n\n    }\n\n    @Override\n    protected void onResume() {\n        if (mAgentWeb != null) {\n            mAgentWeb.getWebLifeCycle().onResume();\n        }\n        super.onResume();\n    }\n\n    @Override\n    protected void onActivityResult(int requestCode, int resultCode, Intent data) {\n        super.onActivityResult(requestCode, resultCode, data);\n    }\n\n\n    @Override\n    protected void onDestroy() {\n        if (mAgentWeb != null) {\n            mAgentWeb.getWebLifeCycle().onDestroy();\n        }\n        super.onDestroy();\n    }\n\n\n    protected\n    @Nullable\n    String getUrl() {\n        return null;\n    }\n\n    public @Nullable\n    IAgentWebSettings getAgentWebSettings() {\n        return AgentWebSettingsImpl.getInstance();\n    }\n\n    protected abstract @NonNull\n    ViewGroup getAgentWebParent();\n\n    protected @Nullable\n    WebChromeClient getWebChromeClient() {\n        return null;\n    }\n\n    protected @ColorInt\n    int getIndicatorColor() {\n        return -1;\n    }\n\n    protected int getIndicatorHeight() {\n        return -1;\n    }\n\n    protected @Nullable\n    WebViewClient getWebViewClient() {\n        return null;\n    }\n\n\n    protected @Nullable\n    WebView getWebView() {\n        return null;\n    }\n\n    protected @Nullable\n    IWebLayout getWebLayout() {\n        return null;\n    }\n\n    protected @Nullable\n    PermissionInterceptor getPermissionInterceptor() {\n        return null;\n    }\n\n    public @Nullable\n    AgentWebUIControllerImplBase getAgentWebUIController() {\n        return null;\n    }\n\n    public @Nullable\n    DefaultWebClient.OpenOtherPageWays getOpenOtherAppWay() {\n        return null;\n    }\n\n    protected @NonNull\n    MiddlewareWebChromeBase getMiddleWareWebChrome() {\n        return this.mMiddleWareWebChrome = new MiddlewareWebChromeBase() {\n            @Override\n            public void onReceivedTitle(WebView view, String title) {\n                super.onReceivedTitle(view, title);\n                setTitle(view, title);\n            }\n        };\n    }\n\n    protected void setTitle(WebView view, String title) {\n\n    }\n\n    protected @NonNull\n    MiddlewareWebClientBase getMiddleWareWebClient() {\n        return this.mMiddleWareWebClient = new MiddlewareWebClientBase() {\n        };\n    }\n}\n"
  },
  {
    "path": "sample/src/main/java/com/just/agentweb/sample/base/BaseAgentWebFragment.java",
    "content": "package com.just.agentweb.sample.base;\n\nimport android.content.Intent;\nimport android.os.Bundle;\nimport androidx.annotation.ColorInt;\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.fragment.app.Fragment;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.webkit.WebView;\n\nimport com.just.agentweb.AgentWeb;\nimport com.just.agentweb.AgentWebSettingsImpl;\nimport com.just.agentweb.AgentWebUIControllerImplBase;\nimport com.just.agentweb.DefaultWebClient;\nimport com.just.agentweb.IAgentWebSettings;\nimport com.just.agentweb.IWebLayout;\nimport com.just.agentweb.MiddlewareWebChromeBase;\nimport com.just.agentweb.MiddlewareWebClientBase;\nimport com.just.agentweb.PermissionInterceptor;\nimport com.just.agentweb.WebChromeClient;\nimport com.just.agentweb.WebViewClient;\n\n/**\n * Created by cenxiaozhong on 2017/7/22.\n * source code  https://github.com/Justson/AgentWeb\n */\n\npublic abstract class BaseAgentWebFragment extends Fragment {\n\n    protected AgentWeb mAgentWeb;\n    private MiddlewareWebChromeBase mMiddleWareWebChrome;\n    private MiddlewareWebClientBase mMiddleWareWebClient;\n    private ErrorLayoutEntity mErrorLayoutEntity;\n    private AgentWebUIControllerImplBase mAgentWebUIController;\n\n    @Override\n    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {\n        super.onViewCreated(view, savedInstanceState);\n        ErrorLayoutEntity mErrorLayoutEntity = getErrorLayoutEntity();\n        mAgentWeb = AgentWeb.with(this)\n                .setAgentWebParent(getAgentWebParent(), new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT))\n                .useDefaultIndicator(getIndicatorColor(), getIndicatorHeight())\n                .setWebView(getWebView())\n                .setWebLayout(getWebLayout())\n                .setAgentWebWebSettings(getAgentWebSettings())\n                .setWebViewClient(getWebViewClient())\n                .setPermissionInterceptor(getPermissionInterceptor())\n                .setWebChromeClient(getWebChromeClient())\n                .interceptUnkownUrl()\n                .setOpenOtherPageWays(DefaultWebClient.OpenOtherPageWays.ASK)\n                .setSecurityType(AgentWeb.SecurityType.STRICT_CHECK)\n                .setAgentWebUIController(getAgentWebUIController())\n                .setMainFrameErrorView(mErrorLayoutEntity.layoutRes, mErrorLayoutEntity.reloadId)\n                .useMiddlewareWebChrome(getMiddleWareWebChrome())\n                .useMiddlewareWebClient(getMiddleWareWebClient())\n                .createAgentWeb()//\n                .ready()//\n                .go(getUrl());\n    }\n\n\n    protected void setTitle(WebView view, String title) {\n\n    }\n\n    protected @NonNull\n    ErrorLayoutEntity getErrorLayoutEntity() {\n        if (this.mErrorLayoutEntity == null) {\n            this.mErrorLayoutEntity = new ErrorLayoutEntity();\n        }\n        return mErrorLayoutEntity;\n    }\n\n    protected @Nullable\n    AgentWebUIControllerImplBase getAgentWebUIController() {\n        return mAgentWebUIController;\n    }\n\n    protected static class ErrorLayoutEntity {\n        private int layoutRes = com.just.agentweb.R.layout.agentweb_error_page;\n        private int reloadId;\n\n        public void setLayoutRes(int layoutRes) {\n            this.layoutRes = layoutRes;\n            if (layoutRes <= 0) {\n                layoutRes = -1;\n            }\n        }\n\n        public void setReloadId(int reloadId) {\n            this.reloadId = reloadId;\n            if (reloadId <= 0) {\n                reloadId = -1;\n            }\n        }\n    }\n\n    @Override\n    public void onPause() {\n        if (mAgentWeb != null) {\n            mAgentWeb.getWebLifeCycle().onPause();\n        }\n        super.onPause();\n\n    }\n\n    @Override\n    public void onResume() {\n        if (mAgentWeb != null) {\n            mAgentWeb.getWebLifeCycle().onResume();\n        }\n        super.onResume();\n    }\n\n\n    @Override\n    public void onActivityResult(int requestCode, int resultCode, Intent data) {\n        super.onActivityResult(requestCode, resultCode, data);\n    }\n\n\n    protected @Nullable\n    String getUrl() {\n        return \"\";\n    }\n\n    @Override\n    public void onDestroy() {\n        if (mAgentWeb != null) {\n            mAgentWeb.getWebLifeCycle().onDestroy();\n        }\n        super.onDestroy();\n    }\n\n    protected @Nullable\n    IAgentWebSettings getAgentWebSettings() {\n        return AgentWebSettingsImpl.getInstance();\n    }\n\n    protected @Nullable\n    WebChromeClient getWebChromeClient() {\n        return null;\n    }\n\n    protected abstract @NonNull\n    ViewGroup getAgentWebParent();\n\n    protected @ColorInt\n    int getIndicatorColor() {\n        return -1;\n    }\n\n    protected int getIndicatorHeight() {\n        return -1;\n    }\n\n    protected @Nullable\n    WebViewClient getWebViewClient() {\n        return null;\n    }\n\n    protected @Nullable\n    WebView getWebView() {\n        return null;\n    }\n\n    protected @Nullable\n    IWebLayout getWebLayout() {\n        return null;\n    }\n\n    protected @Nullable\n    PermissionInterceptor getPermissionInterceptor() {\n        return null;\n    }\n\n    protected @NonNull\n    MiddlewareWebChromeBase getMiddleWareWebChrome() {\n        return this.mMiddleWareWebChrome = new MiddlewareWebChromeBase() {\n            @Override\n            public void onReceivedTitle(WebView view, String title) {\n                super.onReceivedTitle(view, title);\n                setTitle(view, title);\n            }\n        };\n    }\n\n    protected @NonNull\n    MiddlewareWebClientBase getMiddleWareWebClient() {\n        return this.mMiddleWareWebClient = new MiddlewareWebClientBase() {\n        };\n    }\n}\n"
  },
  {
    "path": "sample/src/main/java/com/just/agentweb/sample/base/FragmentKeyDown.java",
    "content": "package com.just.agentweb.sample.base;\n\nimport android.view.KeyEvent;\n\n/**\n * Created by cenxiaozhong\n * source code  https://github.com/Justson/AgentWeb\n */\n\npublic interface FragmentKeyDown {\n\n    boolean onFragmentKeyDown(int keyCode, KeyEvent event);\n}\n"
  },
  {
    "path": "sample/src/main/java/com/just/agentweb/sample/behavior/BottomNavigationViewBehavior.java",
    "content": "/*\n * Copyright (C)  LeonDevLifeLog(https://github.com/Justson/AgentWeb)\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.just.agentweb.sample.behavior;\n\nimport android.content.Context;\nimport com.google.android.material.appbar.AppBarLayout;\nimport androidx.coordinatorlayout.widget.CoordinatorLayout;\nimport androidx.core.view.ViewCompat;\nimport android.util.AttributeSet;\nimport android.view.View;\n\n/**\n * 与toolbar联动隐藏底部菜单\n *\n * @author LeonDevLifeLog <leondevlifelog@gmail.com>\n * @date 2018-02-24 08:59\n * @since V4.0.0\n */\npublic class BottomNavigationViewBehavior extends CoordinatorLayout.Behavior<View> {\n    public BottomNavigationViewBehavior() {\n    }\n\n    public BottomNavigationViewBehavior(Context context, AttributeSet attrs) {\n        super(context, attrs);\n    }\n\n    @Override\n    public boolean onLayoutChild(CoordinatorLayout parent, View child, int layoutDirection) {\n        ((CoordinatorLayout.LayoutParams) child.getLayoutParams()).topMargin = parent\n                .getMeasuredHeight() - child.getMeasuredHeight();\n        return super.onLayoutChild(parent, child, layoutDirection);\n    }\n\n    @Override\n    public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) {\n        return dependency instanceof AppBarLayout;\n    }\n\n    @Override\n    public boolean onDependentViewChanged(CoordinatorLayout parent, View child, View dependency) {\n        //得到依赖View的滑动距离\n        int top = ((AppBarLayout.Behavior) ((CoordinatorLayout.LayoutParams) dependency\n                .getLayoutParams()).getBehavior()).getTopAndBottomOffset();\n        //因为BottomNavigation的滑动与ToolBar是反向的，所以取负值\n        ViewCompat.setTranslationY(child, -(top * child.getMeasuredHeight() / dependency\n                .getMeasuredHeight()));\n        return false;\n    }\n}\n"
  },
  {
    "path": "sample/src/main/java/com/just/agentweb/sample/client/MiddlewareChromeClient.java",
    "content": "package com.just.agentweb.sample.client;\n\nimport android.util.Log;\nimport android.webkit.JsResult;\nimport android.webkit.WebView;\n\nimport com.just.agentweb.MiddlewareWebChromeBase;\n\n/**\n * Created by cenxiaozhong on 2017/12/16.\n * After agentweb 3.0.0  ， allow dev to custom self WebChromeClient's MiddleWare  .\n */\npublic class MiddlewareChromeClient extends MiddlewareWebChromeBase {\n    public MiddlewareChromeClient() {\n    }\n    @Override\n    public boolean onJsAlert(WebView view, String url, String message, JsResult result) {\n        Log.i(\"Info\",\"onJsAlert:\"+url);\n        return super.onJsAlert(view, url, message, result);\n    }\n\n    @Override\n    public void onProgressChanged(WebView view, int newProgress) {\n        super.onProgressChanged(view, newProgress);\n        Log.i(\"Info\",\"onProgressChanged:\");\n    }\n}\n"
  },
  {
    "path": "sample/src/main/java/com/just/agentweb/sample/client/MiddlewareWebViewClient.java",
    "content": "package com.just.agentweb.sample.client;\n\nimport android.util.Log;\nimport android.webkit.WebResourceRequest;\nimport android.webkit.WebView;\n\nimport com.just.agentweb.MiddlewareWebClientBase;\n\n/**\n * Created by cenxiaozhong on 2017/12/16.\n *\n *\n * 方法的执行顺序，例如下面用了7个中间件一个 WebViewClient\n *\n * .useMiddlewareWebClient(getMiddlewareWebClient())  // 1\n * .useMiddlewareWebClient(getMiddlewareWebClient())  // 2\n * .useMiddlewareWebClient(getMiddlewareWebClient())  // 3\n * .useMiddlewareWebClient(getMiddlewareWebClient())  // 4\n * .useMiddlewareWebClient(getMiddlewareWebClient())  // 5\n * .useMiddlewareWebClient(getMiddlewareWebClient())  // 6\n * .useMiddlewareWebClient(getMiddlewareWebClient())  // 7\n *  DefaultWebClient                                  // 8\n * .setWebViewClient(mWebViewClient)                 // 9\n *\n *\n * 典型的洋葱模型\n * 对象内部的方法执行顺序: 1->2->3->4->5->6->7->8->9->8->7->6->5->4->3->2->1\n *\n *\n * 中断中间件的执行， 删除super.methodName(...) 这行即可\n *\n */\n\npublic class MiddlewareWebViewClient extends MiddlewareWebClientBase {\n\n    public MiddlewareWebViewClient() {\n    }\n\n    private static int count = 1;\n\n    @Override\n    public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {\n        Log.i(\"Info\", \"MiddlewareWebViewClient -- >  shouldOverrideUrlLoading:\" + request.getUrl().toString() + \"  c:\" + (count++));\n        return super.shouldOverrideUrlLoading(view, request);\n\n    }\n\n    @Override\n    public boolean shouldOverrideUrlLoading(WebView view, String url) {\n        Log.i(\"Info\", \"MiddlewareWebViewClient -- >  shouldOverrideUrlLoading:\" + url + \"  c:\" + (count++));\n        return super.shouldOverrideUrlLoading(view, url);\n\n    }\n}\n"
  },
  {
    "path": "sample/src/main/java/com/just/agentweb/sample/client/SonicWebViewClient.java",
    "content": "package com.just.agentweb.sample.client;\n\nimport android.annotation.TargetApi;\nimport android.webkit.WebResourceRequest;\nimport android.webkit.WebResourceResponse;\nimport android.webkit.WebView;\n\nimport com.just.agentweb.MiddlewareWebClientBase;\nimport com.tencent.sonic.sdk.SonicSession;\n\n/**\n * Created by cenxiaozhong on 2017/12/17.\n */\n\npublic class SonicWebViewClient extends MiddlewareWebClientBase {\n\n    private SonicSession sonicSession;\n\n    public SonicWebViewClient(SonicSession sonicSession) {\n        this.sonicSession=sonicSession;\n    }\n\n    @Override\n    public void onPageFinished(WebView view, String url) {\n        super.onPageFinished(view, url);\n        if (sonicSession != null) {\n            sonicSession.getSessionClient().pageFinish(url);\n        }\n    }\n\n    @TargetApi(21)\n    @Override\n    public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) {\n        return shouldInterceptRequest(view, request.getUrl().toString());\n    }\n    @Override\n    public WebResourceResponse shouldInterceptRequest(WebView view, String url) {\n        if (sonicSession != null) {\n            return (WebResourceResponse) sonicSession.getSessionClient().requestResource(url);\n        }\n        return null;\n    }\n}\n"
  },
  {
    "path": "sample/src/main/java/com/just/agentweb/sample/common/AndroidInterface.java",
    "content": "package com.just.agentweb.sample.common;\n\nimport android.content.Context;\nimport android.os.Handler;\nimport android.os.Looper;\nimport android.util.Log;\nimport android.webkit.JavascriptInterface;\nimport android.widget.Toast;\n\nimport com.just.agentweb.AgentWeb;\n\n/**\n * Created by cenxiaozhong on 2017/5/14.\n *  source code  https://github.com/Justson/AgentWeb\n */\n\npublic class AndroidInterface {\n\n    private Handler deliver = new Handler(Looper.getMainLooper());\n    private AgentWeb agent;\n    private Context context;\n\n    public AndroidInterface(AgentWeb agent, Context context) {\n        this.agent = agent;\n        this.context = context;\n    }\n\n\n\n    @JavascriptInterface\n    public void callAndroid(final String msg) {\n\n        deliver.post(new Runnable() {\n            @Override\n            public void run() {\n\n                Log.i(\"Info\", \"main Thread:\" + Thread.currentThread());\n                Toast.makeText(context.getApplicationContext(), \"\" + msg, Toast.LENGTH_LONG).show();\n            }\n        });\n\n\n        Log.i(\"Info\", \"Thread:\" + Thread.currentThread());\n\n    }\n\n}\n"
  },
  {
    "path": "sample/src/main/java/com/just/agentweb/sample/common/CommonWebChromeClient.java",
    "content": "package com.just.agentweb.sample.common;\n\nimport android.util.Log;\nimport android.webkit.WebView;\n\nimport com.just.agentweb.WebChromeClient;\n\n/**\n * @author cenxiaozhong\n * @date 2019/2/19\n * @since 1.0.0\n */\npublic class CommonWebChromeClient extends WebChromeClient {\n\t@Override\n\tpublic void onProgressChanged(WebView view, int newProgress) {\n\t\t  super.onProgressChanged(view, newProgress);\n\t\tLog.i(\"CommonWebChromeClient\", \"onProgressChanged:\" + newProgress + \"  view:\" + view);\n\t}\n}\n"
  },
  {
    "path": "sample/src/main/java/com/just/agentweb/sample/common/CustomSettings.java",
    "content": "package com.just.agentweb.sample.common;\n\nimport android.app.Activity;\nimport android.os.Build;\nimport android.webkit.DownloadListener;\nimport android.webkit.WebView;\n\nimport com.just.agentweb.AbsAgentWebSettings;\nimport com.just.agentweb.AgentWeb;\nimport com.just.agentweb.DefaultDownloadImpl;\nimport com.just.agentweb.IAgentWebSettings;\nimport com.just.agentweb.WebListenerManager;\n\n/**\n * Created by cenxiaozhong on 2017/5/26.\n * source code  https://github.com/Justson/AgentWeb\n */\npublic class CustomSettings extends AbsAgentWebSettings {\n\n    public CustomSettings(Activity activity) {\n        super();\n        this.mActivity = activity;\n    }\n\n    private AgentWeb mAgentWeb;\n    private Activity mActivity;\n\n    @Override\n    protected void bindAgentWebSupport(AgentWeb agentWeb) {\n        this.mAgentWeb = agentWeb;\n    }\n\n\n    @Override\n    public IAgentWebSettings toSetting(WebView webView) {\n        super.toSetting(webView);\n\n        getWebSettings().setBlockNetworkImage(false);//是否阻塞加载网络图片  协议http or https\n        getWebSettings().setAllowFileAccess(false); //允许加载本地文件html  file协议, 这可能会造成不安全 , 建议重写关闭\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {\n            getWebSettings().setAllowFileAccessFromFileURLs(false); //通过 file mUrl 加载的 Javascript 读取其他的本地文件 .建议关闭\n            getWebSettings().setAllowUniversalAccessFromFileURLs(false);//允许通过 file mUrl 加载的 Javascript 可以访问其他的源，包括其他的文件和 http，https 等其他的源\n        }\n        getWebSettings().setNeedInitialFocus(true);\n        getWebSettings().setDefaultTextEncodingName(\"gb2312\");//设置编码格式\n        getWebSettings().setDefaultFontSize(16);\n        getWebSettings().setMinimumFontSize(12);//设置 WebView 支持的最小字体大小，默认为 8\n        getWebSettings().setGeolocationEnabled(true);\n        getWebSettings().setUserAgentString(getWebSettings().getUserAgentString().concat(\"agentweb/3.1.0\"));\n        return this;\n    }\n\n    @Override\n    public WebListenerManager setDownloader(WebView webView, DownloadListener downloadListener) {\n        return super.setDownloader(webView,\n                DefaultDownloadImpl.create(this.mActivity\n                        , webView, mAgentWeb.getPermissionInterceptor()));\n    }\n}\n"
  },
  {
    "path": "sample/src/main/java/com/just/agentweb/sample/common/FragmentKeyDown.java",
    "content": "package com.just.agentweb.sample.common;\n\nimport android.view.KeyEvent;\n\n/**\n * Created by cenxiaozhong on 2017/5/23.\n * source code  https://github.com/Justson/AgentWeb\n */\n\npublic interface FragmentKeyDown {\n\n    boolean onFragmentKeyDown(int keyCode, KeyEvent event);\n}\n"
  },
  {
    "path": "sample/src/main/java/com/just/agentweb/sample/common/GuideItemEntity.java",
    "content": "/*\n * Copyright (C)  Justson(https://github.com/Justson/AgentWeb)\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.just.agentweb.sample.common;\n\n/**\n * @author cenxiaozhong\n * @date 2018/7/15\n * @since 1.0.0\n */\npublic class GuideItemEntity {\n\n\tprivate String guideTitle;\n\tprivate int guideDictionary;\n\tprivate int extra;\n\n\tpublic GuideItemEntity(String guideTitle, int guideDictionary) {\n\t\tthis.guideTitle = guideTitle;\n\t\tthis.guideDictionary = guideDictionary;\n\t}\n\n\n\tpublic String getGuideTitle() {\n\t\treturn guideTitle;\n\t}\n\n\tpublic void setGuideTitle(String guideTitle) {\n\t\tthis.guideTitle = guideTitle;\n\t}\n\n\tpublic int getGuideDictionary() {\n\t\treturn guideDictionary;\n\t}\n\n\tpublic void setGuideDictionary(int guideDictionary) {\n\t\tthis.guideDictionary = guideDictionary;\n\t}\n}\n"
  },
  {
    "path": "sample/src/main/java/com/just/agentweb/sample/common/UIController.java",
    "content": "package com.just.agentweb.sample.common;\n\nimport android.app.Activity;\nimport android.os.Handler;\nimport android.util.Log;\nimport android.webkit.WebView;\n\nimport com.just.agentweb.AgentWebUIControllerImplBase;\n\n/**\n * Created by cenxiaozhong on 2017/12/23.\n */\n\n/**\n * 如果你需要修改某一个AgentWeb 内部的某一个弹窗 ，请看下面的例子\n * 注意写法一定要参照 DefaultUIController 的写法 ，因为UI自由定制，但是回调的方式是固定的，并且一定要回调。\n */\npublic class UIController extends AgentWebUIControllerImplBase {\n\n    private Activity mActivity;\n    public UIController(Activity activity){\n        this.mActivity=activity;\n    }\n\n    @Override\n    public void onShowMessage(String message, String from) {\n        super.onShowMessage(message,from);\n        Log.i(TAG,\"message:\"+message);\n    }\n\n    @Override\n    public void onSelectItemsPrompt(WebView view, String url, String[] items, Handler.Callback callback) {\n       super.onSelectItemsPrompt(view,url,items,callback); // 使用默认的UI\n    }\n\n\n    /**\n     * 修改文件选择的弹窗\n     */\n   /* @Override\n    public void onSelectItemsPrompt(WebView view, String mUrl, String[] ways, final Handler.Callback callback) {\n        //super.onSelectItemsPrompt(view,mUrl,ways,callback); //这行应该注释或者删除掉\n        final AlertDialog mAlertDialog = new AlertDialog.Builder(mActivity)//\n                .setSingleChoiceItems(ways, -1, new DialogInterface.OnClickListener() {\n                    @Override\n                    public void onClick(DialogInterface dialog, int which) {\n                        dialog.dismiss();\n                        if (callback != null) {\n                            Message mMessage = Message.obtain();\n                            mMessage.what = which;  //mMessage.what 必须等于ways的index\n                            callback.handleMessage(mMessage); //最后callback一定要回调\n                        }\n\n                    }\n                }).setOnCancelListener(new DialogInterface.OnCancelListener() {\n                    @Override\n                    public void onCancel(DialogInterface dialog) {\n                        dialog.dismiss();\n                        if (callback != null) {\n                            callback.handleMessage(Message.obtain(null, -1)); //-1表示取消  //最后callback一定要回调\n                        }\n                    }\n                }).create();\n        mAlertDialog.show();\n\n    }*/\n}\n"
  },
  {
    "path": "sample/src/main/java/com/just/agentweb/sample/fragment/AgentWebFragment.java",
    "content": "package com.just.agentweb.sample.fragment;\n\n\nimport android.annotation.SuppressLint;\nimport android.content.ClipData;\nimport android.content.ClipboardManager;\nimport android.content.Context;\nimport android.content.Intent;\nimport android.graphics.Bitmap;\nimport android.net.Uri;\nimport android.net.http.SslError;\nimport android.os.AsyncTask;\nimport android.os.Build;\nimport android.os.Bundle;\nimport android.text.TextUtils;\nimport android.util.Log;\nimport android.util.TypedValue;\nimport android.view.KeyEvent;\nimport android.view.LayoutInflater;\nimport android.view.MenuItem;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.view.inputmethod.EditorInfo;\nimport android.webkit.MimeTypeMap;\nimport android.webkit.SslErrorHandler;\nimport android.webkit.ValueCallback;\nimport android.webkit.WebResourceError;\nimport android.webkit.WebResourceRequest;\nimport android.webkit.WebResourceResponse;\nimport android.webkit.WebView;\nimport android.widget.EditText;\nimport android.widget.ImageView;\nimport android.widget.LinearLayout;\nimport android.widget.TextView;\nimport android.widget.Toast;\n\nimport com.download.library.DownloadImpl;\nimport com.download.library.DownloadListenerAdapter;\nimport com.download.library.Extra;\nimport com.download.library.ResourceRequest;\nimport com.ferfalk.simplesearchview.SimpleSearchView;\nimport com.google.gson.Gson;\nimport com.just.agentweb.AbsAgentWebSettings;\nimport com.just.agentweb.AgentWeb;\nimport com.just.agentweb.AgentWebConfig;\nimport com.just.agentweb.AgentWebUtils;\nimport com.just.agentweb.DefaultDownloadImpl;\nimport com.just.agentweb.DefaultWebClient;\nimport com.just.agentweb.IAgentWebSettings;\nimport com.just.agentweb.MiddlewareWebChromeBase;\nimport com.just.agentweb.MiddlewareWebClientBase;\nimport com.just.agentweb.PermissionInterceptor;\nimport com.just.agentweb.WebChromeClient;\nimport com.just.agentweb.WebListenerManager;\nimport com.just.agentweb.filechooser.FileCompressor;\nimport com.just.agentweb.sample.R;\nimport com.just.agentweb.sample.app.App;\nimport com.just.agentweb.sample.client.MiddlewareChromeClient;\nimport com.just.agentweb.sample.client.MiddlewareWebViewClient;\nimport com.just.agentweb.sample.common.CommonWebChromeClient;\nimport com.just.agentweb.sample.common.FragmentKeyDown;\nimport com.just.agentweb.sample.common.UIController;\nimport com.just.agentweb.sample.utils.FileUtils;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.util.HashMap;\nimport java.util.Locale;\nimport java.util.Objects;\n\nimport androidx.annotation.Nullable;\nimport androidx.annotation.RequiresApi;\nimport androidx.appcompat.widget.PopupMenu;\nimport androidx.fragment.app.Fragment;\nimport top.zibin.luban.Luban;\n\n/**\n * Created by cenxiaozhong on 2017/5/15.\n * source code  https://github.com/Justson/AgentWeb\n */\n\npublic class AgentWebFragment extends Fragment implements FragmentKeyDown, FileCompressor.FileCompressEngine {\n\n    private ImageView mBackImageView;\n    private View mLineView;\n    private ImageView mFinishImageView;\n    private TextView mTitleTextView;\n    protected AgentWeb mAgentWeb;\n    public static final String URL_KEY = \"url_key\";\n    private ImageView mMoreImageView;\n    private PopupMenu mPopupMenu;\n    /**\n     * 用于方便打印测试\n     */\n    private Gson mGson = new Gson();\n    public static final String TAG = AgentWebFragment.class.getSimpleName();\n    private MiddlewareWebClientBase mMiddleWareWebClient;\n    private MiddlewareWebChromeBase mMiddleWareWebChrome;\n\n    public static AgentWebFragment getInstance(Bundle bundle) {\n\n        AgentWebFragment mAgentWebFragment = new AgentWebFragment();\n        if (bundle != null) {\n            mAgentWebFragment.setArguments(bundle);\n        }\n\n        return mAgentWebFragment;\n\n    }\n\n    @Nullable\n    @Override\n    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {\n        return inflater.inflate(R.layout.fragment_agentweb, container, false);\n    }\n\n\n    @Override\n    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {\n        super.onViewCreated(view, savedInstanceState);\n\n\n        mAgentWeb = AgentWeb.with(this)//\n                .setAgentWebParent((LinearLayout) view, -1, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT))//传入AgentWeb的父控件。\n                .useDefaultIndicator(-1, 3)//设置进度条颜色与高度，-1为默认值，高度为2，单位为dp。\n                .setAgentWebWebSettings(getSettings())//设置 IAgentWebSettings。\n                .setWebViewClient(mWebViewClient)//WebViewClient ， 与 WebView 使用一致 ，但是请勿获取WebView调用setWebViewClient(xx)方法了,会覆盖AgentWeb DefaultWebClient,同时相应的中间件也会失效。\n                .setWebChromeClient(new CommonWebChromeClient()) //WebChromeClient\n                .setPermissionInterceptor(mPermissionInterceptor) //权限拦截 2.0.0 加入。\n                .setSecurityType(AgentWeb.SecurityType.STRICT_CHECK) //严格模式 Android 4.2.2 以下会放弃注入对象 ，使用AgentWebView没影响。\n                .setAgentWebUIController(new UIController(getActivity())) //自定义UI  AgentWeb3.0.0 加入。\n                .setMainFrameErrorView(com.just.agentweb.R.layout.agentweb_error_page, -1) //参数1是错误显示的布局，参数2点击刷新控件ID -1表示点击整个布局都刷新， AgentWeb 3.0.0 加入。\n                .useMiddlewareWebChrome(getMiddlewareWebChrome()) //设置WebChromeClient中间件，支持多个WebChromeClient，AgentWeb 3.0.0 加入。\n                .additionalHttpHeader(getUrl(), \"cookie\", \"41bc7ddf04a26b91803f6b11817a5a1c\")\n                .useMiddlewareWebClient(getMiddlewareWebClient()) //设置WebViewClient中间件，支持多个WebViewClient， AgentWeb 3.0.0 加入。\n                .setOpenOtherPageWays(DefaultWebClient.OpenOtherPageWays.ASK)//打开其他页面时，弹窗质询用户前往其他应用 AgentWeb 3.0.0 加入。\n                .interceptUnkownUrl() //拦截找不到相关页面的Url AgentWeb 3.0.0 加入。\n                .createAgentWeb()//创建AgentWeb。\n                .ready()//设置 WebSettings。\n                .go(getUrl()); //WebView载入该url地址的页面并显示。\n\n\n        AgentWebConfig.debug();\n\n        initView(view);\n\n\n        // AgentWeb 没有把WebView的功能全面覆盖 ，所以某些设置 AgentWeb 没有提供 ， 请从WebView方面入手设置。\n        mAgentWeb.getWebCreator().getWebView().setOverScrollMode(WebView.OVER_SCROLL_NEVER);\n        //mAgentWeb.getWebCreator().getWebView()  获取WebView .\n\n//\t\tmAgentWeb.getWebCreator().getWebView().setOnLongClickListener();\n\n//\t\tRuntime.getInstance().setFileComparatorFactory(new FileComparator.FileComparatorFactory() {\n//\t\t\t@Override\n//\t\t\tpublic FileComparator newFileComparator() {\n//\t\t\t\treturn new FileComparator() {\n//\t\t\t\t\t@Override\n//\t\t\t\t\tpublic int compare(String url, File originFile, String inputMD5, String originFileMD5) {\n//\t\t\t\t\t\treturn FileComparator.COMPARE_RESULT_SUCCESSFUL;\n//\t\t\t\t\t}\n//\t\t\t\t};\n//\t\t\t}\n//\t\t});\n    }\n\n\n    protected PermissionInterceptor mPermissionInterceptor = new PermissionInterceptor() {\n\n        /**\n         * PermissionInterceptor 能达到 url1 允许授权， url2 拒绝授权的效果。\n         * @param url\n         * @param permissions\n         * @param action\n         * @return true 该Url对应页面请求权限进行拦截 ，false 表示不拦截。\n         */\n        @Override\n        public boolean intercept(String url, String[] permissions, String action) {\n            Log.i(TAG, \"mUrl:\" + url + \"  permission:\" + mGson.toJson(permissions) + \" action:\" + action);\n            return false;\n        }\n    };\n\n\n    /**\n     * @return IAgentWebSettings\n     */\n    public IAgentWebSettings getSettings() {\n        return new AbsAgentWebSettings() {\n            private AgentWeb mAgentWeb;\n\n            @Override\n            protected void bindAgentWebSupport(AgentWeb agentWeb) {\n                this.mAgentWeb = agentWeb;\n            }\n\n            /**\n             * AgentWeb 4.0.0 内部删除了 DownloadListener 监听 ，以及相关API ，将 Download 部分完全抽离出来独立一个库，\n             * 如果你需要使用 AgentWeb Download 部分 ， 请依赖上 compile 'com.download.library:Downloader:4.1.1' ，\n             * 如果你需要监听下载结果，请自定义 AgentWebSetting ， New 出 DefaultDownloadImpl\n             * 实现进度或者结果监听，例如下面这个例子，如果你不需要监听进度，或者下载结果，下面 setDownloader 的例子可以忽略。\n             * @param webView\n             * @param downloadListener\n             * @return WebListenerManager\n             */\n            @Override\n            public WebListenerManager setDownloader(WebView webView, android.webkit.DownloadListener downloadListener) {\n                return super.setDownloader(webView,\n                        new DefaultDownloadImpl(getActivity(),\n                                webView,\n                                this.mAgentWeb.getPermissionInterceptor()) {\n\n                            @Override\n                            protected ResourceRequest createResourceRequest(String url) {\n                                return DownloadImpl.getInstance(getContext())\n                                        .url(url)\n                                        .quickProgress()\n                                        .addHeader(\"\", \"\")\n                                        .setEnableIndicator(true)\n                                        .autoOpenIgnoreMD5()\n                                        .setRetry(5)\n                                        .setBlockMaxTime(100000L);\n                            }\n\n                            @Override\n                            protected void taskEnqueue(ResourceRequest resourceRequest) {\n                                resourceRequest.enqueue(new DownloadListenerAdapter() {\n                                    @Override\n                                    public void onStart(String url, String userAgent, String contentDisposition, String mimetype, long contentLength, Extra extra) {\n                                        super.onStart(url, userAgent, contentDisposition, mimetype, contentLength, extra);\n                                    }\n\n                                    @MainThread\n                                    @Override\n                                    public void onProgress(String url, long downloaded, long length, long usedTime) {\n                                        super.onProgress(url, downloaded, length, usedTime);\n                                    }\n\n                                    @Override\n                                    public boolean onResult(Throwable throwable, Uri path, String url, Extra extra) {\n                                        return super.onResult(throwable, path, url, extra);\n                                    }\n                                });\n                            }\n                        });\n            }\n        };\n    }\n\n    /**\n     * 页面空白，请检查scheme是否加上， scheme://host:port/path?query&query 。\n     *\n     * @return mUrl\n     */\n    public String getUrl() {\n        String target = \"\";\n\n        if (TextUtils.isEmpty(target = this.getArguments().getString(URL_KEY))) {\n            target = \"http://cw.gzyunjuchuang.com/\";\n        }\n\n//\t\treturn \"http://ggzy.sqzwfw.gov.cn/WebBuilderDS/WebbuilderMIS/attach/downloadZtbAttach.jspx?attachGuid=af982055-3d76-4b00-b5ab-36dee1f90b11&appUrlFlag=sqztb&siteGuid=7eb5f7f1-9041-43ad-8e13-8fcb82ea831a\";\n        return target;\n    }\n\n    protected com.just.agentweb.WebChromeClient mWebChromeClient = new WebChromeClient() {\n        @Override\n        public void onProgressChanged(WebView view, int newProgress) {\n            super.onProgressChanged(view, newProgress);\n            Log.i(TAG, \"onProgressChanged:\" + newProgress + \"  view:\" + view);\n        }\n\n        @Override\n        public void onReceivedTitle(WebView view, String title) {\n            super.onReceivedTitle(view, title);\n            if (mTitleTextView != null && !TextUtils.isEmpty(title)) {\n                if (title.length() > 10) {\n                    title = title.substring(0, 10).concat(\"...\");\n                }\n            }\n            mTitleTextView.setText(title);\n        }\n    };\n    /**\n     * 注意，重写WebViewClient的方法,super.xxx()请务必正确调用， 如果没有调用super.xxx(),则无法执行DefaultWebClient的方法\n     * 可能会影响到AgentWeb自带提供的功能,尽可能调用super.xxx()来完成洋葱模型\n     */\n    protected com.just.agentweb.WebViewClient mWebViewClient = new com.just.agentweb.WebViewClient() {\n\n        private HashMap<String, Long> timer = new HashMap<>();\n\n        @Override\n        public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) {\n            super.onReceivedError(view, request, error);\n        }\n\n        @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)\n        @Override\n        public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {\n            return super.shouldOverrideUrlLoading(view, request);\n        }\n\n        @Nullable\n        @Override\n        public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) {\n            return super.shouldInterceptRequest(view, request);\n        }\n\n        //\n        @Override\n        public boolean shouldOverrideUrlLoading(final WebView view, String url) {\n\n            Log.i(TAG, \"view:\" + new Gson().toJson(view.getHitTestResult()));\n            Log.i(TAG, \"mWebViewClient shouldOverrideUrlLoading:\" + url);\n            //优酷想唤起自己应用播放该视频 ， 下面拦截地址返回 true  则会在应用内 H5 播放 ，禁止优酷唤起播放该视频， 如果返回 false ， DefaultWebClient  会根据intent 协议处理 该地址 ， 首先匹配该应用存不存在 ，如果存在 ， 唤起该应用播放 ， 如果不存在 ， 则跳到应用市场下载该应用 .\n            if (url.startsWith(\"intent://\") && url.contains(\"com.youku.phone\")) {\n                return true;\n            }\n\t\t\t/*else if (isAlipay(view, mUrl))   //1.2.5开始不用调用该方法了 ，只要引入支付宝sdk即可 ， DefaultWebClient 默认会处理相应url调起支付宝\n\t\t\t    return true;*/\n            return super.shouldOverrideUrlLoading(view, url);\n        }\n\n        @Override\n        public void onPageStarted(WebView view, String url, Bitmap favicon) {\n            super.onPageStarted(view, url, favicon);\n            Log.i(TAG, \"mUrl:\" + url + \" onPageStarted  target:\" + getUrl());\n            timer.put(url, System.currentTimeMillis());\n            if (url.equals(getUrl())) {\n                pageNavigator(View.GONE);\n            } else {\n                pageNavigator(View.VISIBLE);\n            }\n        }\n\n        @Override\n        public void onPageFinished(WebView view, String url) {\n            super.onPageFinished(view, url);\n\n            if (timer.get(url) != null) {\n                long overTime = System.currentTimeMillis();\n                Long startTime = timer.get(url);\n                Log.i(TAG, \"  page mUrl:\" + url + \"  used time:\" + (overTime - startTime));\n            }\n\n        }\n        /*错误页回调该方法 ， 如果重写了该方法， 上面传入了布局将不会显示 ， 交由开发者实现，注意参数对齐。*/\n\t   /* public void onMainFrameError(AbsAgentWebUIController agentWebUIController, WebView view, int errorCode, String description, String failingUrl) {\n\n            Log.i(TAG, \"AgentWebFragment onMainFrameError\");\n            agentWebUIController.onMainFrameError(view,errorCode,description,failingUrl);\n\n        }*/\n\n        @Override\n        public void onReceivedHttpError(WebView view, WebResourceRequest request, WebResourceResponse errorResponse) {\n            super.onReceivedHttpError(view, request, errorResponse);\n\n//\t\t\tLog.i(TAG, \"onReceivedHttpError:\" + 3 + \"  request:\" + mGson.toJson(request) + \"  errorResponse:\" + mGson.toJson(errorResponse));\n        }\n\n        @Override\n        public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {\n            handler.proceed();\n            super.onReceivedSslError(view, handler, error);\n        }\n\n        @Override\n        public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {\n            super.onReceivedError(view, errorCode, description, failingUrl);\n\n//\t\t\tLog.i(TAG, \"onReceivedError:\" + errorCode + \"  description:\" + description + \"  errorResponse:\" + failingUrl);\n        }\n    };\n\n\n    @Override\n    public void onActivityResult(int requestCode, int resultCode, Intent data) {\n        super.onActivityResult(requestCode, resultCode, data);\n\n        /**\n         * 2.0.0开始 废弃该api ，没有api代替 ,使用 ActionActivity 绕过该方法 ,降低使用门槛,4.0.0 删除该API。\n         */\n//        mAgentWeb.uploadFileResult(requestCode, resultCode, data);\n    }\n\n    private SimpleSearchView mSimpleSearchView;\n    private ImageView mSearchImageView;\n\n    protected void initView(View view) {\n        mBackImageView = (ImageView) view.findViewById(R.id.iv_back);\n        mLineView = view.findViewById(R.id.view_line);\n        mFinishImageView = (ImageView) view.findViewById(R.id.iv_finish);\n        mTitleTextView = (TextView) view.findViewById(R.id.toolbar_title);\n        mBackImageView.setOnClickListener(mOnClickListener);\n        mFinishImageView.setOnClickListener(mOnClickListener);\n        mMoreImageView = (ImageView) view.findViewById(R.id.iv_more);\n        mMoreImageView.setOnClickListener(mOnClickListener);\n        mSearchImageView = view.findViewById(R.id.iv_search);\n        mSearchImageView.setOnClickListener(mOnClickListener);\n        mSimpleSearchView = view.findViewById(R.id.search_view);\n        pageNavigator(View.GONE);\n        mSimpleSearchView.setHint(\"请输入网址\");\n        EditText editText = mSimpleSearchView.findViewById(com.ferfalk.simplesearchview.R.id.searchEditText);\n        editText.setImeOptions(EditorInfo.IME_ACTION_GO);\n//        mSimpleSearchView.setSearchBackground(new ColorDrawable(getColorPrimary()));\n        mSimpleSearchView.setOnQueryTextListener(new SimpleSearchView.OnQueryTextListener() {\n            @Override\n            public boolean onQueryTextSubmit(String s) {\n                String completeUrl = s;\n                if (!completeUrl.startsWith(\"http\")) {\n                    completeUrl = \"http://\" + s;\n                }\n                mAgentWeb.getUrlLoader().loadUrl(completeUrl);\n                return false;\n            }\n\n            @Override\n            public boolean onQueryTextChange(String s) {\n                return false;\n            }\n\n            @Override\n            public boolean onQueryTextCleared() {\n                return false;\n            }\n        });\n        FileCompressor.getInstance().registerFileCompressEngine(this);\n\n    }\n\n//    public int getColorPrimary() {\n//        TypedValue typedValue = new TypedValue();\n//        requireActivity().getTheme().resolveAttribute(R.attr.colorPrimary, typedValue, true);\n//        return typedValue.data;\n//    }\n\n\n    private void pageNavigator(int tag) {\n\n        mBackImageView.setVisibility(tag);\n        mLineView.setVisibility(tag);\n    }\n\n    private View.OnClickListener mOnClickListener = new View.OnClickListener() {\n        @Override\n        public void onClick(View v) {\n\n\n            switch (v.getId()) {\n                case R.id.iv_back:\n                    // true表示AgentWeb处理了该事件\n                    if (!mAgentWeb.back()) {\n                        AgentWebFragment.this.getActivity().finish();\n                    }\n                    break;\n                case R.id.iv_finish:\n                    AgentWebFragment.this.getActivity().finish();\n                    break;\n                case R.id.iv_more:\n                    showPoPup(v);\n                    break;\n                case R.id.iv_search:\n                    mSimpleSearchView.showSearch();\n                    break;\n                default:\n                    break;\n\n            }\n        }\n\n    };\n\n    /**\n     * 打开浏览器\n     *\n     * @param targetUrl 外部浏览器打开的地址\n     */\n    private void openBrowser(String targetUrl) {\n        if (TextUtils.isEmpty(targetUrl) || targetUrl.startsWith(\"file://\")) {\n            Toast.makeText(this.getContext(), targetUrl + \" 该链接无法使用浏览器打开。\", Toast.LENGTH_SHORT).show();\n            return;\n        }\n        Intent intent = new Intent();\n        intent.setAction(\"android.intent.action.VIEW\");\n        Uri mUri = Uri.parse(targetUrl);\n        intent.setData(mUri);\n        startActivity(intent);\n    }\n\n\n    /**\n     * 显示更多菜单\n     *\n     * @param view 菜单依附在该View下面\n     */\n    private void showPoPup(View view) {\n        if (mPopupMenu == null) {\n            mPopupMenu = new PopupMenu(this.getActivity(), view);\n            mPopupMenu.inflate(R.menu.toolbar_menu);\n            mPopupMenu.setOnMenuItemClickListener(mOnMenuItemClickListener);\n        }\n        mPopupMenu.show();\n    }\n\n    /**\n     * 菜单事件\n     */\n    private PopupMenu.OnMenuItemClickListener mOnMenuItemClickListener = new PopupMenu.OnMenuItemClickListener() {\n        @SuppressLint(\"NonConstantResourceId\")\n        @Override\n        public boolean onMenuItemClick(MenuItem item) {\n\n            switch (item.getItemId()) {\n\n                case R.id.refresh:\n                    if (mAgentWeb != null) {\n                        mAgentWeb.getUrlLoader().reload(); // 刷新\n                    }\n                    return true;\n\n                case R.id.copy:\n                    if (mAgentWeb != null) {\n                        toCopy(AgentWebFragment.this.getContext(), mAgentWeb.getWebCreator().getWebView().getUrl());\n                    }\n                    return true;\n                case R.id.default_browser:\n                    if (mAgentWeb != null) {\n                        openBrowser(mAgentWeb.getWebCreator().getWebView().getUrl());\n                    }\n                    return true;\n                case R.id.default_clean:\n                    toCleanWebCache();\n                    return true;\n                case R.id.error_website:\n                    loadErrorWebSite();\n                    // test DownloadingService\n//\t\t\t        LogUtils.i(TAG, \" :\" + mDownloadingService + \"  \" + (mDownloadingService == null ? \"\" : mDownloadingService.isShutdown()) + \"  :\" + mExtraService);\n//                    if (mDownloadingService != null && !mDownloadingService.isShutdown()) {\n//                        mExtraService = mDownloadingService.shutdownNow();\n//                        LogUtils.i(TAG, \"mExtraService::\" + mExtraService);\n//                        return true;\n//                    }\n//                    if (mExtraService != null) {\n//                        mExtraService.performReDownload();\n//                    }\n\n                    return true;\n                default:\n                    return false;\n            }\n\n        }\n    };\n\n    /**\n     * 测试错误页的显示\n     */\n    private void loadErrorWebSite() {\n        if (mAgentWeb != null) {\n            mAgentWeb.getUrlLoader().loadUrl(\"http://www.unkownwebsiteblog.me\");\n        }\n    }\n\n    /**\n     * 清除 WebView 缓存\n     */\n    private void toCleanWebCache() {\n\n        if (this.mAgentWeb != null) {\n\n            //清理所有跟WebView相关的缓存 ，数据库， 历史记录 等。\n            this.mAgentWeb.clearWebCache();\n            Toast.makeText(getActivity(), \"已清理缓存\", Toast.LENGTH_SHORT).show();\n            //清空所有 AgentWeb 硬盘缓存，包括 WebView 的缓存 , AgentWeb 下载的图片 ，视频 ，apk 等文件。\n//            AgentWebConfig.clearDiskCache(this.getContext());\n        }\n\n    }\n\n\n    /**\n     * 复制字符串\n     *\n     * @param context\n     * @param text\n     */\n    private void toCopy(Context context, String text) {\n\n        ClipboardManager mClipboardManager = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);\n        mClipboardManager.setPrimaryClip(ClipData.newPlainText(null, text));\n\n    }\n\n\n    @Override\n    public void onResume() {\n        mAgentWeb.getWebLifeCycle().onResume();//恢复\n        super.onResume();\n    }\n\n    @Override\n    public void onPause() {\n\n        mAgentWeb.getWebLifeCycle().onPause(); //暂停应用内所有WebView ， 调用mWebView.resumeTimers();/mAgentWeb.getWebLifeCycle().onResume(); 恢复。\n        super.onPause();\n    }\n\n    @Override\n    public boolean onFragmentKeyDown(int keyCode, KeyEvent event) {\n        if (mSimpleSearchView.onBackPressed()) {\n            return true;\n        }\n        return mAgentWeb.handleKeyEvent(keyCode, event);\n    }\n\n    @Override\n    public void onDestroyView() {\n        mAgentWeb.getWebLifeCycle().onDestroy();\n        FileCompressor.getInstance().unregisterFileCompressEngine(this);\n        super.onDestroyView();\n    }\n\n    /**\n     * MiddlewareWebClientBase 是 AgentWeb 3.0.0 提供一个强大的功能，\n     * 如果用户需要使用 AgentWeb 提供的功能， 不想重写 WebClientView方\n     * 法覆盖AgentWeb提供的功能，那么 MiddlewareWebClientBase 是一个\n     * 不错的选择 。\n     *\n     * @return\n     */\n    protected MiddlewareWebClientBase getMiddlewareWebClient() {\n        return this.mMiddleWareWebClient = new MiddlewareWebViewClient() {\n            /**\n             *\n             * @param view\n             * @param url\n             * @return\n             */\n            @Override\n            public boolean shouldOverrideUrlLoading(WebView view, String url) {\n                Log.e(TAG, \"MiddlewareWebClientBase#shouldOverrideUrlLoading url:\" + url);\n\t\t\t\t/*if (url.startsWith(\"agentweb\")) { // 拦截 url，不执行 DefaultWebClient#shouldOverrideUrlLoading\n\t\t\t\t\tLog.i(TAG, \"agentweb scheme ~\");\n\t\t\t\t\treturn true;\n\t\t\t\t}*/\n\n                if (super.shouldOverrideUrlLoading(view, url)) { // 执行 DefaultWebClient#shouldOverrideUrlLoading\n                    return true;\n                }\n                // do you work\n                return false;\n            }\n\n            @Override\n            public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {\n                Log.e(TAG, \"MiddlewareWebClientBase#shouldOverrideUrlLoading request url:\" + request.getUrl().toString());\n                return super.shouldOverrideUrlLoading(view, request);\n            }\n\n            @Override\n            public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) {\n                if (request.isForMainFrame() && error.getErrorCode() != -1) {\n                    super.onReceivedError(view, request, error);\n                }\n            }\n        };\n    }\n\n    protected MiddlewareWebChromeBase getMiddlewareWebChrome() {\n        return this.mMiddleWareWebChrome = new MiddlewareChromeClient() {\n        };\n    }\n\n    /**\n     * 选择文件后回调该方法， 这里可以做文件压缩 / 也可以做图片的方向调整\n     *\n     * @param type     customize/system  ， customize 表示通过js方式获取文件， 把文件\n     *                 转成base64的方式返回给js，这种方式兼容性高，但是存在文件过大转成base64时\n     *                 字符串长度过长，导致与js通信失败问题，所以很有必要压缩文件， 尽量控制字符串长度在512kb以内。\n     *                 <p>\n     *                 system 这种方式，是由input/file 标签触发的文件选择，这种方式缺点是在Android 4.4 不回调\n     *                 fileChooser，存在兼容性问题，但是经过升级，基本可以忽略了，api 的兼容性越来越好了， 回调\n     *                 返回是于uri形式，所以不存在文件大小问题，作图片预览也很快。(推荐这种方式)\n     * @param uri      文件的uri\n     * @param callback\n     */\n    @Override\n    public void compressFile(String type, final Uri[] uri, ValueCallback<Uri[]> callback) {\n        Log.e(TAG, \"compressFile type:\" + type);\n        if (\"system\".equals(type)) { // input/file 标签触发的文件选择，这种方式不存在性能问题，可压缩也可以不压缩，具体看自己业务要求\n            callback.onReceiveValue(uri);\n            return;\n        }\n        // customize.equals(type)  这种方式强烈建议文件压缩\n        if (uri == null || uri.length == 0) {\n            callback.onReceiveValue(uri);\n        } else {\n            final String[] paths = AgentWebUtils.uriToPath(getActivity(), uri);\n            if (paths == null || paths.length == 0) {\n                callback.onReceiveValue(uri);\n                return;\n            }\n\n            AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> {\n                try {\n                    Uri[] result = new Uri[paths.length];\n                    for (int i = 0; i < paths.length; i++) {\n                        String filePath = paths[i];\n                        String mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(FileUtils.getExtensionByFilePath(filePath));\n                        if (TextUtils.isEmpty(mimeType) || !mimeType.startsWith(\"image\")) {\n                            result[i] = uri[i];\n                        } else {\n                            File origin = new File(filePath);\n                            File file = Luban.with(App.mContext).ignoreBy(100).setTargetDir(AgentWebUtils.getAgentWebFilePath(App.mContext)).get(filePath);\n                            Log.e(TAG, \"原文件大小：\" + byte2FitMemorySize(origin.length()));\n                            Log.e(TAG, \"压缩后文件大小：\" + byte2FitMemorySize(file.length()));\n\n                            Uri fileUri = AgentWebUtils.getUriFromFile(App.mContext, file);\n                            result[i] = fileUri;\n\n                        }\n                    }\n                    AgentWebUtils.runInUiThread(() -> callback.onReceiveValue(result));\n                } catch (IOException e) {\n                    e.printStackTrace();\n                    AgentWebUtils.runInUiThread(() -> callback.onReceiveValue(uri));\n                }\n            });\n\n        }\n\n    }\n\n    private static String byte2FitMemorySize(final long byteNum) {\n        if (byteNum < 0) {\n            return \"shouldn't be less than zero!\";\n        } else if (byteNum < 1024) {\n            return String.format(Locale.getDefault(), \"%.1fB\", (double) byteNum);\n        } else if (byteNum < 1048576) {\n            return String.format(Locale.getDefault(), \"%.1fKB\", (double) byteNum / 1024);\n        } else if (byteNum < 1073741824) {\n            return String.format(Locale.getDefault(), \"%.1fMB\", (double) byteNum / 1048576);\n        } else {\n            return String.format(Locale.getDefault(), \"%.1fGB\", (double) byteNum / 1073741824);\n        }\n    }\n}\n"
  },
  {
    "path": "sample/src/main/java/com/just/agentweb/sample/fragment/BounceWebFragment.java",
    "content": "package com.just.agentweb.sample.fragment;\n\nimport android.graphics.Color;\nimport android.os.Bundle;\nimport androidx.annotation.Nullable;\nimport android.view.Gravity;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.FrameLayout;\nimport android.widget.LinearLayout;\nimport android.widget.TextView;\n\nimport com.just.agentweb.AgentWeb;\nimport com.just.agentweb.DefaultWebClient;\nimport com.just.agentweb.IWebLayout;\nimport com.just.agentweb.sample.R;\nimport com.just.agentweb.sample.widget.WebLayout;\n\n/**\n * Created by cenxiaozhong on 2017/7/1.\n * source code  https://github.com/Justson/AgentWeb\n */\n\npublic class BounceWebFragment extends AgentWebFragment {\n\n\tpublic static BounceWebFragment getInstance(Bundle bundle) {\n\t\tBounceWebFragment mBounceWebFragment = new BounceWebFragment();\n\t\tif (mBounceWebFragment != null){\n\t\t\tmBounceWebFragment.setArguments(bundle);\n\t\t}\n\t\treturn mBounceWebFragment;\n\t}\n\n\n\t@Override\n\tpublic String getUrl() {\n\t\treturn super.getUrl();\n\t}\n\n\t@Override\n\tpublic void onViewCreated(View view, @Nullable Bundle savedInstanceState) {\n\t\tmAgentWeb = AgentWeb.with(this)\n\t\t\t\t.setAgentWebParent((ViewGroup) view, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT))\n\t\t\t\t.useDefaultIndicator(-1, 2)\n\t\t\t\t.setAgentWebWebSettings(getSettings())\n\t\t\t\t.setWebViewClient(mWebViewClient)\n\t\t\t\t.setWebChromeClient(mWebChromeClient)\n\t\t\t\t.setWebLayout(getWebLayout())\n\t\t\t\t.setSecurityType(AgentWeb.SecurityType.STRICT_CHECK)\n\t\t\t\t.interceptUnkownUrl()\n\t\t\t\t.setOpenOtherPageWays(DefaultWebClient.OpenOtherPageWays.ASK)\n\t\t\t\t.setMainFrameErrorView(com.just.agentweb.R.layout.agentweb_error_page, -1)\n\t\t\t\t.createAgentWeb()//\n\t\t\t\t.ready()//\n\t\t\t\t.go(getUrl());\n\t\t// 得到 AgentWeb 最底层的控件\n\t\taddBGChild((FrameLayout) mAgentWeb.getWebCreator().getWebParentLayout());\n\t\tinitView(view);\n\n\n\t}\n\n\tprotected IWebLayout getWebLayout() {\n\t\treturn new WebLayout(getActivity());\n\t}\n\n\tprotected void addBGChild(FrameLayout frameLayout) {\n\n\t\tTextView mTextView = new TextView(frameLayout.getContext());\n\t\tmTextView.setText(\"技术由 AgentWeb 提供\");\n\t\tmTextView.setTextSize(16);\n\t\tmTextView.setTextColor(Color.parseColor(\"#727779\"));\n\t\tframeLayout.setBackgroundColor(Color.parseColor(\"#272b2d\"));\n\t\tFrameLayout.LayoutParams mFlp = new FrameLayout.LayoutParams(-2, -2);\n\t\tmFlp.gravity = Gravity.CENTER_HORIZONTAL;\n\t\tfinal float scale = frameLayout.getContext().getResources().getDisplayMetrics().density;\n\t\tmFlp.topMargin = (int) (15 * scale + 0.5f);\n\t\tframeLayout.addView(mTextView, 0, mFlp);\n\t}\n\n\n}\n"
  },
  {
    "path": "sample/src/main/java/com/just/agentweb/sample/fragment/CustomIndicatorFragment.java",
    "content": "package com.just.agentweb.sample.fragment;\n\nimport android.os.Bundle;\nimport androidx.annotation.Nullable;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.LinearLayout;\n\nimport com.just.agentweb.AgentWeb;\nimport com.just.agentweb.AgentWebSettingsImpl;\nimport com.just.agentweb.DefaultWebClient;\nimport com.just.agentweb.sample.widget.CoolIndicatorLayout;\n\n/**\n * Created by cenxiaozhong on 2017/5/26.\n * source code  https://github.com/Justson/AgentWeb\n */\n\npublic class CustomIndicatorFragment extends AgentWebFragment {\n\tpublic static CustomIndicatorFragment getInstance(Bundle bundle) {\n\t\tCustomIndicatorFragment mCustomIndicatorFragment = new CustomIndicatorFragment();\n\t\tif (bundle != null){\n\t\t\tmCustomIndicatorFragment.setArguments(bundle);\n\t\t}\n\t\treturn mCustomIndicatorFragment;\n\t}\n\n\t@Override\n\tpublic void onViewCreated(View view, @Nullable Bundle savedInstanceState) {\n\n//        CommonIndicator mCommonIndicator=new CommonIndicator(this.getActivity());\n//        FrameLayout.LayoutParams lp=new FrameLayout.LayoutParams(-2,-2);\n//        lp.gravity= Gravity.CENTER;\n//        ProgressBar mProgressBar=new ProgressBar(this.getActivity());\n//        mProgressBar.setBackground(this.getResources().getDrawable(R.drawable.indicator_shape));\n//        mCommonIndicator.addView(mProgressBar,lp);\n\n\n\t\tCoolIndicatorLayout mCoolIndicatorLayout = new CoolIndicatorLayout(this.getActivity());\n\t\tthis.mAgentWeb = AgentWeb.with(this)//\n\t\t\t\t.setAgentWebParent((ViewGroup) view, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT))\n\t\t\t\t.setCustomIndicator(mCoolIndicatorLayout)\n\t\t\t\t.setAgentWebWebSettings(AgentWebSettingsImpl.getInstance())\n\t\t\t\t.setWebViewClient(mWebViewClient)\n\t\t\t\t.setPermissionInterceptor(mPermissionInterceptor)\n\t\t\t\t.setSecurityType(AgentWeb.SecurityType.STRICT_CHECK)\n\t\t\t\t.interceptUnkownUrl()\n\t\t\t\t.setOpenOtherPageWays(DefaultWebClient.OpenOtherPageWays.ASK)\n\t\t\t\t.createAgentWeb()//\n\t\t\t\t.ready()//\n\t\t\t\t.go(getUrl());\n\n\n\t\tinitView(view);\n\t}\n}\n"
  },
  {
    "path": "sample/src/main/java/com/just/agentweb/sample/fragment/CustomSettingsFragment.java",
    "content": "package com.just.agentweb.sample.fragment;\n\nimport android.os.Bundle;\n\nimport com.just.agentweb.IAgentWebSettings;\nimport com.just.agentweb.sample.common.CustomSettings;\n\n/**\n * Created by cenxiaozhong on 2017/5/26.\n * source code  https://github.com/Justson/AgentWeb\n */\n\npublic class CustomSettingsFragment extends AgentWebFragment {\n\n    public static AgentWebFragment getInstance(Bundle bundle) {\n\n        CustomSettingsFragment mCustomSettingsFragment = new CustomSettingsFragment();\n        if (bundle != null){\n            mCustomSettingsFragment.setArguments(bundle);\n        }\n        return mCustomSettingsFragment;\n\n    }\n\n    @Override\n    public IAgentWebSettings getSettings() {\n        return new CustomSettings(getActivity());\n    }\n}\n"
  },
  {
    "path": "sample/src/main/java/com/just/agentweb/sample/fragment/CustomWebViewFragment.java",
    "content": "package com.just.agentweb.sample.fragment;\n\nimport android.os.Bundle;\nimport androidx.annotation.Nullable;\nimport android.text.Editable;\nimport android.text.TextWatcher;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.view.WindowManager;\nimport android.widget.EditText;\nimport android.widget.LinearLayout;\n\nimport com.just.agentweb.AgentWeb;\nimport com.just.agentweb.sample.R;\n\nimport us.feras.mdv.MarkdownView;\n\n/**\n * Created by cenxiaozhong on 2017/6/17.\n * source code  https://github.com/Justson/AgentWeb\n */\n\npublic class CustomWebViewFragment extends AgentWebFragment {\n\n\n\tprivate MarkdownView mMarkdownWebView;\n\tprivate EditText markdownEditText;\n\n\tpublic static final CustomWebViewFragment getInstance(Bundle bundle) {\n\n\t\tCustomWebViewFragment mCustomWebViewFragment = new CustomWebViewFragment();\n\t\tif (bundle != null) {\n\t\t\tmCustomWebViewFragment.setArguments(bundle);\n\t\t}\n\t\treturn mCustomWebViewFragment;\n\t}\n\n\t@Nullable\n\t@Override\n\tpublic View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {\n\t\treturn inflater.inflate(R.layout.markdown_view, container, false);\n\t}\n\n\t@Override\n\tpublic void onViewCreated(View view, @Nullable Bundle savedInstanceState) {\n\t\tgetActivity().getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN | WindowManager.LayoutParams.SOFT_INPUT_ADJUST_UNSPECIFIED);\n\t\t//MarkdownView 是 WebView  的一个子类\n\t\tmMarkdownWebView = new MarkdownView(getActivity());\n\t\tmarkdownEditText = (EditText) view.findViewById(R.id.markdownText);\n\n\t\tLinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 0);\n\t\tlp.weight = 1f;\n\t\tmAgentWeb = AgentWeb.with(this)//\n\t\t\t\t.setAgentWebParent((ViewGroup) view, lp)//\n\t\t\t\t.closeIndicator()//\n\t\t\t\t.setWebViewClient(mWebViewClient)\n\t\t\t\t.setWebView(mMarkdownWebView)\n\t\t\t\t.setSecurityType(AgentWeb.SecurityType.STRICT_CHECK)\n\t\t\t\t.createAgentWeb()//\n\t\t\t\t.ready()//\n\t\t\t\t.go(null);\n\n\n\t\tString text = \"## AgentWeb 功能\\n\" +\n\t\t\t\t\"***\\n\\n\" +\n\t\t\t\t\"1. 支持进度条以及自定义进度条\\n\" +\n\t\t\t\t\"2. 支持文件下载\\n\" +\n\t\t\t\t\"3. 支持文件下载断点续传\\n\" +\n\t\t\t\t\"4. 支持下载通知形式提示进度\\n\" +\n\t\t\t\t\"5. 简化 Javascript 通信 \\n\" +\n\t\t\t\t\"6. 支持 Android 4.4 Kitkat 以及其他版本文件上传\\n\" +\n\t\t\t\t\"7. 支持注入 Cookies\\n\" +\n\t\t\t\t\"8. 加强 Web 安全\\n\" +\n\t\t\t\t\"9. 支持全屏播放视频\\n\" +\n\t\t\t\t\"10. 兼容低版本 Js 安全通信\\n\" +\n\t\t\t\t\"11. 更省电 。\\n\" +\n\t\t\t\t\"12. 支持调起微信支付\\n\" +\n\t\t\t\t\"13. 支持调起支付宝（请参照sample）\\n\" +\n\t\t\t\t\"14. 默认支持定位\";\n\n\t\tmarkdownEditText.setText(text);\n\n\t\tupdateMarkdownView();\n\n\t\tmarkdownEditText.addTextChangedListener(new TextWatcher() {\n\n\n\t\t\t@Override\n\t\t\tpublic void afterTextChanged(Editable s) {\n\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void beforeTextChanged(CharSequence s, int start, int count, int after) {\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void onTextChanged(CharSequence s, int start, int before, int count) {\n\t\t\t\tupdateMarkdownView();\n\t\t\t}\n\t\t});\n\n\n\t\tinitView(view);\n\n\t}\n\n\n\tprivate void updateMarkdownView() {\n\t\tmMarkdownWebView.loadMarkdown(markdownEditText.getText().toString());\n\t}\n}\n"
  },
  {
    "path": "sample/src/main/java/com/just/agentweb/sample/fragment/EasyWebFragment.java",
    "content": "package com.just.agentweb.sample.fragment;\n\nimport android.os.Bundle;\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.appcompat.widget.PopupMenu;\nimport android.text.TextUtils;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.webkit.WebView;\nimport android.widget.ImageView;\nimport android.widget.TextView;\n\nimport com.just.agentweb.sample.R;\nimport com.just.agentweb.sample.base.BaseAgentWebFragment;\n\n/**\n * Created by cenxiaozhong on 2017/7/22.\n */\n\npublic class EasyWebFragment extends BaseAgentWebFragment {\n\n    private ViewGroup mViewGroup;\n    private ImageView mBackImageView;\n    private View mLineView;\n    private ImageView mFinishImageView;\n    private TextView mTitleTextView;\n    private ImageView mMoreImageView;\n    private PopupMenu mPopupMenu;\n\n    public static EasyWebFragment getInstance(Bundle bundle) {\n        EasyWebFragment mEasyWebFragment = new EasyWebFragment();\n        if (bundle != null) {\n            mEasyWebFragment.setArguments(bundle);\n        }\n        return mEasyWebFragment;\n\n    }\n\n    @Nullable\n    @Override\n    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {\n        return mViewGroup = (ViewGroup) inflater.inflate(R.layout.fragment_agentweb, container, false);\n    }\n\n    @NonNull\n    @Override\n    protected ViewGroup getAgentWebParent() {\n        return (ViewGroup) this.mViewGroup.findViewById(R.id.linearLayout);\n    }\n\n    @Override\n    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {\n        super.onViewCreated(view, savedInstanceState);\n\n        initView(view);\n    }\n\n    protected void initView(View view) {\n        mBackImageView = (ImageView) view.findViewById(R.id.iv_back);\n        mLineView = view.findViewById(R.id.view_line);\n        mFinishImageView = (ImageView) view.findViewById(R.id.iv_finish);\n        mTitleTextView = (TextView) view.findViewById(R.id.toolbar_title);\n        mBackImageView.setOnClickListener(mOnClickListener);\n        mFinishImageView.setOnClickListener(mOnClickListener);\n        mMoreImageView = (ImageView) view.findViewById(R.id.iv_more);\n        mMoreImageView.setVisibility(View.GONE);\n        pageNavigator(View.GONE);\n    }\n\n    private void pageNavigator(int tag) {\n\n        mBackImageView.setVisibility(tag);\n        mLineView.setVisibility(tag);\n    }\n\n    private View.OnClickListener mOnClickListener = new View.OnClickListener() {\n        @Override\n        public void onClick(View v) {\n\n\n            if (v.getId() == R.id.iv_back) {\n                if (!mAgentWeb.back()) {\n                    EasyWebFragment.this.getActivity().finish();\n                }\n            } else if (v.getId() == R.id.iv_finish) {\n                EasyWebFragment.this.getActivity().finish();\n            }\n\n        }\n    };\n\n    @Override\n    protected void setTitle(WebView view, String title) {\n        super.setTitle(view, title);\n        if (!TextUtils.isEmpty(title)) {\n            if (title.length() > 10) {\n                title = title.substring(0, 10).concat(\"...\");\n            }\n        }\n        mTitleTextView.setText(title);\n    }\n\n    @Nullable\n    @Override\n    protected String getUrl() {\n        return \"https://m.v.qq.com/index.html\";\n    }\n}\n"
  },
  {
    "path": "sample/src/main/java/com/just/agentweb/sample/fragment/JsAgentWebFragment.java",
    "content": "package com.just.agentweb.sample.fragment;\n\nimport android.os.Build;\nimport android.os.Bundle;\nimport androidx.annotation.Nullable;\nimport androidx.annotation.RequiresApi;\nimport android.util.Log;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.webkit.ValueCallback;\nimport android.widget.LinearLayout;\n\nimport com.just.agentweb.sample.common.AndroidInterface;\nimport com.just.agentweb.sample.R;\n\nimport org.json.JSONObject;\n\n/**\n * Created by cenxiaozhong on 2017/5/26.\n * source code  https://github.com/Justson/AgentWeb\n */\n\npublic class JsAgentWebFragment extends AgentWebFragment {\n\n    public static final JsAgentWebFragment getInstance(Bundle bundle) {\n\n        JsAgentWebFragment mJsAgentWebFragment = new JsAgentWebFragment();\n        if (bundle != null){\n            mJsAgentWebFragment.setArguments(bundle);\n        }\n\n        return mJsAgentWebFragment;\n\n    }\n\n    @Nullable\n    @Override\n    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {\n        return super.onCreateView(inflater, container, savedInstanceState);\n    }\n\n    @Override\n    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {\n\n        LinearLayout mLinearLayout= (LinearLayout) view;\n        LayoutInflater.from(getContext()).inflate(R.layout.fragment_js,mLinearLayout,true);\n        super.onViewCreated(view, savedInstanceState);\n\n\n        if(mAgentWeb!=null){\n            //注入对象\n            mAgentWeb.getJsInterfaceHolder().addJavaObject(\"android\",new AndroidInterface(mAgentWeb,this.getActivity()));\n        }\n        view.findViewById(R.id.callJsNoParamsButton).setOnClickListener(mOnClickListener);\n        view.findViewById(R.id.callJsOneParamsButton).setOnClickListener(mOnClickListener);\n        view.findViewById(R.id.callJsMoreParamsButton).setOnClickListener(mOnClickListener);\n        view.findViewById(R.id.jsJavaCommunicationButton).setOnClickListener(mOnClickListener);\n\n\n\n    }\n\n    private View.OnClickListener mOnClickListener=new View.OnClickListener() {\n        @RequiresApi(api = Build.VERSION_CODES.KITKAT)\n        @Override\n        public void onClick(View v) {\n\n\n            switch (v.getId()){\n\n                case R.id.callJsNoParamsButton:\n                    mAgentWeb.getJsAccessEntrace().quickCallJs(\"callByAndroid\");\n                    break;\n\n                case R.id.callJsOneParamsButton:\n                    mAgentWeb.getJsAccessEntrace().quickCallJs(\"callByAndroidParam\",\"Hello ! Agentweb\");\n                    break;\n\n                case R.id.callJsMoreParamsButton:\n                    mAgentWeb.getJsAccessEntrace().quickCallJs(\"callByAndroidMoreParams\", new ValueCallback<String>() {\n                        @Override\n                        public void onReceiveValue(String value) {\n                            Log.i(\"Info\",\"value:\"+value);\n                        }\n                    },getJson(),\"say:\", \" Hello! Agentweb\");\n\n                    break;\n                case R.id.jsJavaCommunicationButton:\n                    mAgentWeb.getJsAccessEntrace().quickCallJs(\"callByAndroidInteraction\",\"你好Js\");\n                    break;\n            }\n\n        }\n    };\n\n    private String getJson(){\n\n        String result=\"\";\n        try {\n            JSONObject mJSONObject=new JSONObject();\n            mJSONObject.put(\"id\",1);\n            mJSONObject.put(\"name\",\"Agentweb\");\n            mJSONObject.put(\"age\",18);\n            result= mJSONObject.toString();\n        }catch (Exception e){\n\n        }\n\n        return result;\n    }\n\n\n}\n"
  },
  {
    "path": "sample/src/main/java/com/just/agentweb/sample/fragment/JsbridgeWebFragment.java",
    "content": "package com.just.agentweb.sample.fragment;\n\nimport android.graphics.Bitmap;\nimport android.os.Build;\nimport android.os.Bundle;\nimport android.util.Log;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.webkit.WebResourceRequest;\nimport android.webkit.WebView;\nimport android.widget.LinearLayout;\n\nimport com.github.lzyzsd.jsbridge.BridgeHandler;\nimport com.github.lzyzsd.jsbridge.BridgeWebView;\nimport com.github.lzyzsd.jsbridge.BridgeWebViewClient;\nimport com.github.lzyzsd.jsbridge.CallBackFunction;\nimport com.google.gson.Gson;\nimport com.just.agentweb.AgentWeb;\nimport com.just.agentweb.WebViewClient;\n\nimport androidx.annotation.Nullable;\n\n/**\n * Created by cenxiaozhong on 2017/7/1.\n * source code  https://github.com/Justson/AgentWeb\n */\n\npublic class JsbridgeWebFragment extends AgentWebFragment {\n\n\tpublic static JsbridgeWebFragment getInstance(Bundle bundle) {\n\n\t\tJsbridgeWebFragment mJsbridgeWebFragment = new JsbridgeWebFragment();\n\t\tif (mJsbridgeWebFragment != null) {\n\t\t\tmJsbridgeWebFragment.setArguments(bundle);\n\t\t}\n\n\t\treturn mJsbridgeWebFragment;\n\t}\n\n\tprivate BridgeWebView mBridgeWebView;\n\n\t@Override\n\tpublic String getUrl() {\n\t\treturn super.getUrl();\n\t}\n\n\t@Override\n\tpublic void onViewCreated(View view, @Nullable Bundle savedInstanceState) {\n\n\t\tmBridgeWebView = new BridgeWebView(getActivity());\n\t\tmAgentWeb = AgentWeb.with(this)\n\t\t\t\t.setAgentWebParent((ViewGroup) view, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT))\n\t\t\t\t.useDefaultIndicator(-1, 2)\n\t\t\t\t.setAgentWebWebSettings(getSettings())\n\t\t\t\t.setWebChromeClient(mWebChromeClient)\n\t\t\t\t.setWebViewClient(getWebViewClient())\n\t\t\t\t.setWebView(mBridgeWebView)\n\t\t\t\t.setSecurityType(AgentWeb.SecurityType.STRICT_CHECK)\n//                .setDownloadListener(mDownloadListener) 4.0.0 删除该API\n\t\t\t\t.createAgentWeb()//\n\t\t\t\t.ready()//\n\t\t\t\t.go(getUrl());\n\n\n\t\tinitView(view);\n\n\n\t\tmBridgeWebView.registerHandler(\"submitFromWeb\", new BridgeHandler() {\n\n\t\t\t@Override\n\t\t\tpublic void handler(String data, CallBackFunction function) {\n\t\t\t\tfunction.onCallBack(\"submitFromWeb exe, response data 中文 from Java\");\n\t\t\t}\n\n\t\t});\n\n\t\tUser user = new User();\n\t\tLocation location = new Location();\n\t\tlocation.address = \"SDU\";\n\t\tuser.location = location;\n\t\tuser.name = \"Agentweb --> Jsbridge\";\n\t\tmBridgeWebView.callHandler(\"functionInJs\", new Gson().toJson(user), new CallBackFunction() {\n\t\t\t@Override\n\t\t\tpublic void onCallBack(String data) {\n\t\t\t\tLog.i(TAG, \"data:\" + data);\n\t\t\t}\n\t\t});\n\n\t\tmBridgeWebView.send(\"hello\");\n\t}\n\n\tprivate WebViewClient getWebViewClient() {\n\t\treturn new WebViewClient() {\n\t\t\tBridgeWebViewClient mBridgeWebViewClient = new BridgeWebViewClient(mBridgeWebView);\n\n\t\t\t@Override\n\t\t\tpublic boolean shouldOverrideUrlLoading(WebView view, String url) {\n\t\t\t\tif (mBridgeWebViewClient.shouldOverrideUrlLoading(view, url)) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t\treturn super.shouldOverrideUrlLoading(view, url);\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {\n\t\t\t\tif (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {\n\t\t\t\t\tif (mBridgeWebViewClient.shouldOverrideUrlLoading(view, request.getUrl().toString())) {\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn super.shouldOverrideUrlLoading(view, request);\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void onPageStarted(WebView view, String url, Bitmap favicon) {\n\t\t\t\tsuper.onPageStarted(view, url, favicon);\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void onPageFinished(WebView view, String url) {\n\t\t\t\tsuper.onPageFinished(view, url);\n\t\t\t\tmBridgeWebViewClient.onPageFinished(view, url);\n\t\t\t}\n\n\t\t};\n\t}\n\n\tstatic class Location {\n\t\tString address;\n\t}\n\n\tstatic class User {\n\t\tString name;\n\t\tLocation location;\n\t\tString testStr;\n\t}\n\n}\n"
  },
  {
    "path": "sample/src/main/java/com/just/agentweb/sample/fragment/SmartRefreshWebFragment.java",
    "content": "package com.just.agentweb.sample.fragment;\n\nimport android.graphics.Color;\nimport android.os.Bundle;\nimport androidx.annotation.Nullable;\nimport android.view.View;\nimport android.webkit.WebView;\nimport android.widget.FrameLayout;\n\nimport com.just.agentweb.IWebLayout;\nimport com.just.agentweb.sample.widget.SmartRefreshWebLayout;\nimport com.scwang.smartrefresh.layout.SmartRefreshLayout;\nimport com.scwang.smartrefresh.layout.api.RefreshLayout;\nimport com.scwang.smartrefresh.layout.listener.OnRefreshListener;\n\n/**\n * Created by cenxiaozhong on 2017/7/1.\n * source code  https://github.com/Justson/AgentWeb\n */\n\npublic class SmartRefreshWebFragment extends BounceWebFragment {\n\n    public static SmartRefreshWebFragment getInstance(Bundle bundle) {\n\n        SmartRefreshWebFragment mSmartRefreshWebFragment = new SmartRefreshWebFragment();\n        if (mSmartRefreshWebFragment != null) {\n            mSmartRefreshWebFragment.setArguments(bundle);\n        }\n\n        return mSmartRefreshWebFragment;\n    }\n\n    private SmartRefreshWebLayout mSmartRefreshWebLayout = null;\n\n    @Override\n    public String getUrl() {\n        return super.getUrl();\n    }\n\n\n    @Override\n    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {\n        super.onViewCreated(view, savedInstanceState);\n\n        final SmartRefreshLayout mSmartRefreshLayout = (SmartRefreshLayout) this.mSmartRefreshWebLayout.getLayout();\n\n        final WebView mWebView = this.mSmartRefreshWebLayout.getWebView();\n        mSmartRefreshLayout.setOnRefreshListener(new OnRefreshListener() {\n            @Override\n            public void onRefresh(RefreshLayout refreshlayout) {\n                mAgentWeb.getUrlLoader().reload();\n\n                mSmartRefreshLayout.postDelayed(new Runnable() {\n                    @Override\n                    public void run() {\n                        mSmartRefreshLayout.finishRefresh();\n                    }\n                }, 2000);\n            }\n        });\n        mSmartRefreshLayout.autoRefresh();\n\n\n    }\n\n\n    @Override\n    protected IWebLayout getWebLayout() {\n        return this.mSmartRefreshWebLayout = new SmartRefreshWebLayout(this.getActivity());\n    }\n\n\n    @Override\n    protected void addBGChild(FrameLayout frameLayout) {\n\n        frameLayout.setBackgroundColor(Color.TRANSPARENT);\n\n    }\n\n\n}\n"
  },
  {
    "path": "sample/src/main/java/com/just/agentweb/sample/fragment/VasSonicFragment.java",
    "content": "package com.just.agentweb.sample.fragment;\n\nimport android.content.Intent;\nimport android.os.Bundle;\nimport androidx.annotation.Nullable;\nimport android.view.View;\n\nimport com.just.agentweb.MiddlewareWebClientBase;\nimport com.just.agentweb.sample.sonic.SonicImpl;\nimport com.just.agentweb.sample.sonic.SonicJavaScriptInterface;\n\nimport static com.just.agentweb.sample.sonic.SonicJavaScriptInterface.PARAM_CLICK_TIME;\n\n/**\n * Created by cenxiaozhong on 2017/12/18.\n *\n * If you wanna use VasSonic to fast open first page , please\n * follow as sample to update your code;\n */\n\npublic class VasSonicFragment extends AgentWebFragment {\n    private SonicImpl mSonicImpl;\n    public static VasSonicFragment create(Bundle bundle){\n\n        VasSonicFragment mVasSonicFragment =new VasSonicFragment();\n        if(bundle!=null){\n            mVasSonicFragment.setArguments(bundle);\n        }\n        return mVasSonicFragment;\n    }\n\n\n    @Override\n    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {\n\n        // 1. 首先创建SonicImpl\n        mSonicImpl = new SonicImpl(this.getArguments().getString(URL_KEY), this.getContext());\n        // 2. 调用 onCreateSession\n        mSonicImpl.onCreateSession();\n        //3. 创建AgentWeb ，注意创建AgentWeb的时候应该使用加入SonicWebViewClient中间件\n        super.onViewCreated(view, savedInstanceState); // 创建 AgentWeb 注意的 go(\"\") 传入的 mUrl 应该null 或者\"\"\n        //4. 注入 JavaScriptInterface\n        mAgentWeb.getJsInterfaceHolder().addJavaObject(\"sonic\", new SonicJavaScriptInterface(mSonicImpl.getSonicSessionClient(), new Intent().putExtra(PARAM_CLICK_TIME,getArguments().getLong(PARAM_CLICK_TIME)).putExtra(\"loadUrlTime\", System.currentTimeMillis())));\n        //5. 最后绑定AgentWeb\n        mSonicImpl.bindAgentWeb(mAgentWeb);\n\n    }\n\n    //在步骤3的时候应该传入给AgentWeb\n    @Override\n    public MiddlewareWebClientBase getMiddlewareWebClient() {\n        return mSonicImpl.createSonicClientMiddleWare();\n    }\n\n    //getUrl 应该为null\n    @Override\n    public String getUrl() {\n        return null;\n    }\n\n    @Override\n    public void onDestroyView() {\n        super.onDestroyView();\n        //销毁SonicSession\n        if(mSonicImpl !=null){\n            mSonicImpl.destrory();\n        }\n    }\n}\n"
  },
  {
    "path": "sample/src/main/java/com/just/agentweb/sample/provider/ServiceProvider.java",
    "content": "package com.just.agentweb.sample.provider;\n\nimport com.flyingpigeon.library.ServiceContentProvider;\n\n/**\n * @author xiaozhongcen\n * @date 20-8-18\n * @since 1.0.0\n */\npublic class ServiceProvider extends ServiceContentProvider {\n}\n"
  },
  {
    "path": "sample/src/main/java/com/just/agentweb/sample/provider/WebServiceProvider.java",
    "content": "package com.just.agentweb.sample.provider;\n\nimport com.flyingpigeon.library.ServiceContentProvider;\n\n/**\n * @author xiaozhongcen\n * @date 20-8-18\n * @since 1.0.0\n */\npublic class WebServiceProvider extends ServiceContentProvider {\n}\n"
  },
  {
    "path": "sample/src/main/java/com/just/agentweb/sample/service/WebService.java",
    "content": "package com.just.agentweb.sample.service;\n\nimport android.app.Service;\nimport android.content.Intent;\nimport android.os.IBinder;\nimport androidx.annotation.Nullable;\nimport android.util.Log;\nimport android.webkit.WebView;\n\n/**\n * @author xiaozhongcen\n * @date 20-8-18\n * @since 1.0.0\n * 提前初始化进程减少白屏\n */\npublic class WebService extends Service {\n\n    private static final String TAG = WebService.class.getSimpleName();\n\n    @Override\n    public void onCreate() {\n        super.onCreate();\n        Log.e(TAG, \"init process\");\n        try {\n            new WebView(this.getApplicationContext());\n        }catch (Throwable throwable){\n            throwable.printStackTrace();\n        }\n    }\n\n    @Nullable\n    @Override\n    public IBinder onBind(Intent intent) {\n        return null;\n    }\n}\n"
  },
  {
    "path": "sample/src/main/java/com/just/agentweb/sample/sonic/DefaultSonicRuntimeImpl.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making VasSonic available.\n *\n * Copyright (C) 2017 THL A29 Limited, a Tencent company. All rights reserved.\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use this file except in compliance with the License. You may obtain a copy of the License at\n *\n * https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.\n *\n *\n */\n\npackage com.just.agentweb.sample.sonic;\n\nimport android.content.Context;\nimport android.os.Build;\nimport android.os.Environment;\nimport android.text.TextUtils;\nimport android.util.Log;\nimport android.webkit.CookieManager;\nimport android.webkit.WebResourceResponse;\n\nimport com.tencent.sonic.sdk.BuildConfig;\nimport com.tencent.sonic.sdk.SonicRuntime;\nimport com.tencent.sonic.sdk.SonicSessionClient;\n\nimport java.io.File;\nimport java.io.InputStream;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * the sonic host application must implement SonicRuntime to do right things.\n */\n\npublic class DefaultSonicRuntimeImpl extends SonicRuntime {\n\n    public DefaultSonicRuntimeImpl(Context context) {\n        super(context);\n    }\n\n    /**\n     * 获取用户UA信息\n     * @return\n     */\n    @Override\n    public String getUserAgent() {\n        return \"Mozilla/5.0 (Linux; Android 5.1.1; Nexus 6 Build/LYZ28E) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Mobile Safari/537.36\";\n    }\n\n    /**\n     * 获取用户ID信息\n     * @return\n     */\n    @Override\n    public String getCurrentUserAccount() {\n        return \"sonic-demo-master\";\n    }\n\n    @Override\n    public String getCookie(String url) {\n        CookieManager cookieManager = CookieManager.getInstance();\n        return cookieManager.getCookie(url);\n    }\n\n    @Override\n    public void log(String tag, int level, String message) {\n        switch (level) {\n            case Log.ERROR:\n                Log.e(tag, message);\n                break;\n            case Log.INFO:\n                Log.i(tag, message);\n                break;\n            default:\n                Log.d(tag, message);\n        }\n    }\n\n    @Override\n    public Object createWebResourceResponse(String mimeType, String encoding, InputStream data, Map<String, String> headers) {\n        WebResourceResponse resourceResponse =  new WebResourceResponse(mimeType, encoding, data);\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {\n            resourceResponse.setResponseHeaders(headers);\n        }\n        return resourceResponse;\n    }\n\n    @Override\n    public void showToast(CharSequence text, int duration) {\n\n    }\n\n    @Override\n    public void notifyError(SonicSessionClient client, String url, int errorCode) {\n\n    }\n\n    @Override\n    public boolean isSonicUrl(String url) {\n        return true;\n    }\n\n    @Override\n    public boolean setCookie(String url, List<String> cookies) {\n        if (!TextUtils.isEmpty(url) && cookies != null && cookies.size() > 0) {\n            CookieManager cookieManager = CookieManager.getInstance();\n            for (String cookie : cookies) {\n                cookieManager.setCookie(url, cookie);\n            }\n            return true;\n        }\n        return false;\n    }\n\n    @Override\n    public boolean isNetworkValid() {\n        return true;\n    }\n\n    @Override\n    public void postTaskToThread(Runnable task, long delayMillis) {\n        Thread thread = new Thread(task, \"SonicThread\");\n        thread.start();\n    }\n\n    @Override\n    public File getSonicCacheDir() {\n        if (BuildConfig.DEBUG) {\n            String path = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + \"sonic/\";\n            File file = new File(path.trim());\n            if(!file.exists()){\n                file.mkdir();\n            }\n            return file;\n        }\n       return super.getSonicCacheDir();\n    }\n\n    @Override\n    public String getHostDirectAddress(String url) {\n        return null;\n    }\n}\n"
  },
  {
    "path": "sample/src/main/java/com/just/agentweb/sample/sonic/SonicImpl.java",
    "content": "package com.just.agentweb.sample.sonic;\n\nimport android.content.Context;\n\nimport com.just.agentweb.AgentWeb;\nimport com.just.agentweb.MiddlewareWebClientBase;\nimport com.tencent.sonic.sdk.SonicConfig;\nimport com.tencent.sonic.sdk.SonicEngine;\nimport com.tencent.sonic.sdk.SonicSession;\nimport com.tencent.sonic.sdk.SonicSessionConfig;\n\n/**\n * Created by cenxiaozhong on 2017/12/17.\n */\n\npublic class SonicImpl {\n\n    private SonicSession sonicSession;\n    private Context mContext;\n    private String url;\n    private SonicSessionClientImpl sonicSessionClient;\n    public SonicImpl(String url , Context context){\n        this.url=url;\n        this.mContext=context;\n\n    }\n    \n    /**\n     */\n    public void onCreateSession() {\n\n        SonicSessionConfig.Builder sessionConfigBuilder = new SonicSessionConfig.Builder();\n        sessionConfigBuilder.setSupportLocalServer(true);\n        SonicEngine.createInstance(new DefaultSonicRuntimeImpl(mContext.getApplicationContext()), new SonicConfig.Builder().build());\n        // create sonic session and run sonic flow\n        sonicSession = SonicEngine.getInstance().createSession(url, sessionConfigBuilder.build());\n        if (null != sonicSession) {\n            sonicSession.bindClient(sonicSessionClient = new SonicSessionClientImpl());\n        } else {\n            // throw new UnknownError(\"create session fail!\");\n//            Toast.makeText(this, \"create sonic session fail!\", Toast.LENGTH_LONG).show();\n        }\n    }\n\n    public SonicSessionClientImpl getSonicSessionClient(){\n        return this.sonicSessionClient;\n    }\n\n    /**\n     * 不使用中间件，使用普通的 WebViewClient 也是可以的。\n     * @return MiddlewareWebClientBase\n     */\n    public MiddlewareWebClientBase createSonicClientMiddleWare(){\n        return new SonicWebViewClient(sonicSession);\n    }\n\n    public void bindAgentWeb(AgentWeb agentWeb){\n        if (sonicSessionClient != null) {\n            sonicSessionClient.bindWebView(agentWeb);\n            sonicSessionClient.clientReady();\n        } else { // default mode\n            agentWeb.getUrlLoader().loadUrl(url);\n        }\n    }\n\n    public void destrory(){\n        if(sonicSession!=null){\n            sonicSession.destroy();\n        }\n    }\n\n\n}\n"
  },
  {
    "path": "sample/src/main/java/com/just/agentweb/sample/sonic/SonicJavaScriptInterface.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making VasSonic available.\n *\n * Copyright (C) 2017 THL A29 Limited, a Tencent company. All rights reserved.\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use this file except in compliance with the License. You may obtain a copy of the License at\n *\n * https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.\n *\n *\n */\n\npackage com.just.agentweb.sample.sonic;\n\n\nimport android.content.Intent;\nimport android.os.Handler;\nimport android.os.Looper;\nimport android.util.Log;\nimport android.webkit.JavascriptInterface;\n\nimport com.tencent.sonic.sdk.SonicDiffDataCallback;\n\nimport org.json.JSONObject;\n\n/**\n * Sonic javaScript Interface (Android API Level >= 17)\n */\n\npublic class SonicJavaScriptInterface {\n\n    private final SonicSessionClientImpl sessionClient;\n\n    private final Intent intent;\n\n    public static final String PARAM_CLICK_TIME = \"clickTime\";\n\n    public static final String PARAM_LOAD_URL_TIME = \"loadUrlTime\";\n\n    public SonicJavaScriptInterface(SonicSessionClientImpl sessionClient, Intent intent) {\n        this.sessionClient = sessionClient;\n        this.intent = intent;\n    }\n\n    @JavascriptInterface\n    public void getDiffData() {\n        // the callback function of demo page is hardcode as 'getDiffDataCallback'\n        getDiffData2(\"getDiffDataCallback\");\n    }\n\n    @JavascriptInterface\n    public void getDiffData2(final String jsCallbackFunc) {\n        Log.i(\"Info\",\"getDiffData2\");\n        if (null != sessionClient) {\n            sessionClient.getDiffData(new SonicDiffDataCallback() {\n                @Override\n                public void callback(final String resultData) {\n                    Runnable callbackRunnable = new Runnable() {\n                        @Override\n                        public void run() {\n                            String jsCode = \"javascript:\" + jsCallbackFunc + \"('\"+ toJsString(resultData) + \"')\";\n                            sessionClient.getWebView().loadUrl(jsCode);\n                        }\n                    };\n                    if (Looper.getMainLooper() == Looper.myLooper()) {\n                        callbackRunnable.run();\n                    } else {\n                        new Handler(Looper.getMainLooper()).post(callbackRunnable);\n                    }\n                }\n            });\n        }\n    }\n\n    @JavascriptInterface\n    public String getPerformance() {\n        long clickTime = intent.getLongExtra(PARAM_CLICK_TIME, -1);\n        long loadUrlTime = intent.getLongExtra(PARAM_LOAD_URL_TIME, -1);\n        try {\n            JSONObject result = new JSONObject();\n            result.put(PARAM_CLICK_TIME, clickTime);\n            result.put(PARAM_LOAD_URL_TIME, loadUrlTime);\n            Log.i(\"Info\",\"getPerformance\");\n            return result.toString();\n        } catch (Exception e) {\n\n        }\n\n        return \"\";\n    }\n\n    /*\n    * * From RFC 4627, \"All Unicode characters may be placed within the quotation marks except\n    * for the characters that must be escaped: quotation mark,\n    * reverse solidus, and the control characters (U+0000 through U+001F).\"\n    */\n    private static String toJsString(String value) {\n        if (value == null) {\n            return \"null\";\n        }\n        StringBuilder out = new StringBuilder(1024);\n        for (int i = 0, length = value.length(); i < length; i++) {\n            char c = value.charAt(i);\n\n\n            switch (c) {\n                case '\"':\n                case '\\\\':\n                case '/':\n                    out.append('\\\\').append(c);\n                    break;\n\n                case '\\t':\n                    out.append(\"\\\\t\");\n                    break;\n\n                case '\\b':\n                    out.append(\"\\\\b\");\n                    break;\n\n                case '\\n':\n                    out.append(\"\\\\n\");\n                    break;\n\n                case '\\r':\n                    out.append(\"\\\\r\");\n                    break;\n\n                case '\\f':\n                    out.append(\"\\\\f\");\n                    break;\n\n                default:\n                    if (c <= 0x1F) {\n                        out.append(String.format(\"\\\\u%04x\", (int) c));\n                    } else {\n                        out.append(c);\n                    }\n                    break;\n            }\n\n        }\n        return out.toString();\n    }\n}\n"
  },
  {
    "path": "sample/src/main/java/com/just/agentweb/sample/sonic/SonicSessionClientImpl.java",
    "content": "/*\n * Tencent is pleased to support the open source community by making VasSonic available.\n *\n * Copyright (C) 2017 THL A29 Limited, a Tencent company. All rights reserved.\n * Licensed under the BSD 3-Clause License (the \"License\"); you may not use this file except in compliance with the License. You may obtain a copy of the License at\n *\n * https://opensource.org/licenses/BSD-3-Clause\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.\n *\n *\n */\n\npackage com.just.agentweb.sample.sonic;\n\nimport android.os.Bundle;\nimport android.webkit.WebView;\n\nimport com.just.agentweb.AgentWeb;\nimport com.tencent.sonic.sdk.SonicSessionClient;\n\nimport java.util.HashMap;\n\n/**\n *  a implement of SonicSessionClient which need to connect webview and content data.\n */\n\npublic class SonicSessionClientImpl extends SonicSessionClient {\n\n\n    private AgentWeb mAgentWeb;\n    public void bindWebView(AgentWeb agentWeb) {\n        this.mAgentWeb = agentWeb;\n    }\n\n    public WebView getWebView() {\n        return this.mAgentWeb.getWebCreator().getWebView();\n    }\n\n    @Override\n    public void loadUrl(String url, Bundle extraData) {\n        this.mAgentWeb.getUrlLoader().loadUrl(url);\n\n    }\n\n    @Override\n    public void loadDataWithBaseUrl(String baseUrl, String data, String mimeType, String encoding, String historyUrl) {\n        this.mAgentWeb.getUrlLoader().loadDataWithBaseURL(baseUrl,data,mimeType,encoding,historyUrl);\n\n    }\n\n\n    @Override\n    public void loadDataWithBaseUrlAndHeader(String baseUrl, String data, String mimeType, String encoding, String historyUrl, HashMap<String, String> headers) {\n        loadDataWithBaseUrl(baseUrl, data, mimeType, encoding, historyUrl);\n    }\n\n\n\n}\n"
  },
  {
    "path": "sample/src/main/java/com/just/agentweb/sample/sonic/SonicWebViewClient.java",
    "content": "package com.just.agentweb.sample.sonic;\n\nimport android.annotation.TargetApi;\nimport android.webkit.WebResourceRequest;\nimport android.webkit.WebResourceResponse;\nimport android.webkit.WebView;\n\nimport com.just.agentweb.MiddlewareWebClientBase;\nimport com.tencent.sonic.sdk.SonicSession;\n\n/**\n * Created by cenxiaozhong on 2017/12/17.\n */\n\npublic class SonicWebViewClient extends MiddlewareWebClientBase {\n\n    private SonicSession sonicSession;\n\n    public SonicWebViewClient(SonicSession sonicSession) {\n        this.sonicSession=sonicSession;\n    }\n\n    @Override\n    public void onPageFinished(WebView view, String url) {\n        super.onPageFinished(view, url);\n        if (sonicSession != null) {\n            sonicSession.getSessionClient().pageFinish(url);\n        }\n    }\n\n    @TargetApi(21)\n    @Override\n    public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) {\n        return shouldInterceptRequest(view, request.getUrl().toString());\n    }\n\n    @Override\n    public WebResourceResponse shouldInterceptRequest(WebView view, String url) {\n        if (sonicSession != null) {\n            return (WebResourceResponse) sonicSession.getSessionClient().requestResource(url);\n        }\n        return null;\n    }\n}\n"
  },
  {
    "path": "sample/src/main/java/com/just/agentweb/sample/utils/FileUtils.java",
    "content": "package com.just.agentweb.sample.utils;\n\n/**\n * @author cenxiaozhong\n * @date 2021/11/27\n * @since 1.0.0\n */\npublic class FileUtils {\n\n    public static String getExtensionByFilePath(String filePath){\n        String fe = \"\";\n        int i = filePath.lastIndexOf('.');\n        if (i > 0) {\n            fe = filePath.substring(i+1);\n        }\n        System.out.println(\"File extension is : \"+fe);\n        return fe;\n    }\n}\n\n"
  },
  {
    "path": "sample/src/main/java/com/just/agentweb/sample/utils/ProcessUtils.java",
    "content": "package com.just.agentweb.sample.utils;\n\nimport android.app.ActivityManager;\nimport android.app.Application;\nimport android.content.Context;\nimport android.text.TextUtils;\n\nimport java.io.BufferedReader;\nimport java.io.File;\nimport java.io.FileReader;\nimport java.lang.reflect.Field;\nimport java.lang.reflect.Method;\nimport java.util.List;\n\n/**\n * Adapted from com.blankj.utilcode.util.ProcessUtils#getCurrentProcessName\n */\npublic class ProcessUtils {\n\n    public static String getCurrentProcessName(Context context) {\n        String name = getCurrentProcessNameByFile();\n        if (!TextUtils.isEmpty(name)) return name;\n        name = getCurrentProcessNameByAms(context);\n        if (!TextUtils.isEmpty(name)) return name;\n        name = getCurrentProcessNameByReflect(context);\n        return name;\n    }\n\n    private static String getCurrentProcessNameByFile() {\n        try {\n            File file = new File(\"/proc/\" + android.os.Process.myPid() + \"/\" + \"cmdline\");\n            BufferedReader mBufferedReader = new BufferedReader(new FileReader(file));\n            String processName = mBufferedReader.readLine().trim();\n            mBufferedReader.close();\n            return processName;\n        } catch (Exception e) {\n            e.printStackTrace();\n            return \"\";\n        }\n    }\n\n    private static String getCurrentProcessNameByAms(Context context) {\n        ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);\n        if (am == null) return \"\";\n        List<ActivityManager.RunningAppProcessInfo> info = am.getRunningAppProcesses();\n        if (info == null || info.size() == 0) return \"\";\n        int pid = android.os.Process.myPid();\n        for (ActivityManager.RunningAppProcessInfo aInfo : info) {\n            if (aInfo.pid == pid) {\n                if (aInfo.processName != null) {\n                    return aInfo.processName;\n                }\n            }\n        }\n        return \"\";\n    }\n\n    private static String getCurrentProcessNameByReflect(Context context) {\n        String processName = \"\";\n        try {\n            Application app = (Application) context.getApplicationContext();\n            Field loadedApkField = app.getClass().getField(\"mLoadedApk\");\n            loadedApkField.setAccessible(true);\n            Object loadedApk = loadedApkField.get(app);\n\n            Field activityThreadField = loadedApk.getClass().getDeclaredField(\"mActivityThread\");\n            activityThreadField.setAccessible(true);\n            Object activityThread = activityThreadField.get(loadedApk);\n\n            Method getProcessName = activityThread.getClass().getDeclaredMethod(\"getProcessName\");\n            processName = (String) getProcessName.invoke(activityThread);\n        } catch (Exception e) {\n            e.printStackTrace();\n        }\n        return processName;\n    }\n\n}\n"
  },
  {
    "path": "sample/src/main/java/com/just/agentweb/sample/utils/WebCompat.java",
    "content": "package com.just.agentweb.sample.utils;\n\nimport android.annotation.SuppressLint;\nimport android.annotation.TargetApi;\nimport android.app.ActivityManager;\nimport android.app.Application;\nimport android.content.Context;\nimport android.os.Build;\nimport android.os.Environment;\nimport android.text.TextUtils;\nimport android.webkit.WebView;\n\nimport java.io.BufferedReader;\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.FileReader;\nimport java.io.IOException;\nimport java.io.InputStreamReader;\nimport java.io.RandomAccessFile;\nimport java.lang.reflect.Field;\nimport java.lang.reflect.Method;\nimport java.nio.channels.FileLock;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Properties;\nimport java.util.Set;\n\npublic class WebCompat {\n\n\n    /**\n     * fix Using WebView from more than one process\n     *\n     * @param context\n     */\n    public static void setDataDirectorySuffix(Context context) {\n        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) {\n            return;\n        }\n        try {\n\n            Set<String> pathSet = new HashSet<>();\n            String suffix = \"\";\n            String dataPath = context.getDataDir().getAbsolutePath();\n            String webViewDir = \"/app_webview\";\n            String huaweiWebViewDir = \"/app_hws_webview\";\n            String lockFile = \"/webview_data.lock\";\n            String processName = getCurrentProcessName(context);\n            if (!TextUtils.equals(context.getPackageName(), processName)) {//判断不等于默认进程名称\n                suffix = TextUtils.isEmpty(processName) ? context.getPackageName() : processName;\n                WebView.setDataDirectorySuffix(suffix);\n                suffix = \"_\" + suffix;\n                pathSet.add(dataPath + webViewDir + suffix + lockFile);\n                if (isHuawei()) {\n                    pathSet.add(dataPath + huaweiWebViewDir + suffix + lockFile);\n                }\n            } else {\n                //主进程\n                suffix = \"_\" + processName;\n                pathSet.add(dataPath + webViewDir + lockFile);//默认未添加进程名后缀\n                pathSet.add(dataPath + webViewDir + suffix + lockFile);//系统自动添加了进程名后缀\n                if (isHuawei()) {//部分华为手机更改了webview目录名\n                    pathSet.add(dataPath + huaweiWebViewDir + lockFile);\n                    pathSet.add(dataPath + huaweiWebViewDir + suffix + lockFile);\n                }\n            }\n            for (String path : pathSet) {\n                File file = new File(path);\n                if (file.exists()) {\n                    tryLockOrRecreateFile(file);\n                    break;\n                }\n            }\n        } catch (Exception e) {\n            e.printStackTrace();\n        }\n    }\n\n    @TargetApi(Build.VERSION_CODES.P)\n    private static void tryLockOrRecreateFile(File file) {\n        try {\n            FileLock tryLock = new RandomAccessFile(file, \"rw\").getChannel().tryLock();\n            if (tryLock != null) {\n                tryLock.close();\n            } else {\n                createFile(file, file.delete());\n            }\n        } catch (Exception e) {\n            e.printStackTrace();\n            boolean deleted = false;\n            if (file.exists()) {\n                deleted = file.delete();\n            }\n            createFile(file, deleted);\n        }\n    }\n\n    private static void createFile(File file, boolean deleted) {\n        try {\n            if (deleted && !file.exists()) {\n                file.createNewFile();\n            }\n        } catch (Exception e) {\n            e.printStackTrace();\n        }\n    }\n\n    /********Adapted from com.blankj.utilcode.util.ProcessUtils#getCurrentProcessName******************/\n    private static String getCurrentProcessName(Context context) {\n        String name = getCurrentProcessNameByFile();\n        if (!TextUtils.isEmpty(name)) return name;\n        name = getCurrentProcessNameByAms(context);\n        if (!TextUtils.isEmpty(name)) return name;\n        name = getCurrentProcessNameByReflect(context);\n        return name;\n    }\n\n    private static String getCurrentProcessNameByFile() {\n        try {\n            File file = new File(\"/proc/\" + android.os.Process.myPid() + \"/\" + \"cmdline\");\n            BufferedReader mBufferedReader = new BufferedReader(new FileReader(file));\n            String processName = mBufferedReader.readLine().trim();\n            mBufferedReader.close();\n            return processName;\n        } catch (Exception e) {\n            e.printStackTrace();\n            return \"\";\n        }\n    }\n\n    private static String getCurrentProcessNameByAms(Context context) {\n        ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);\n        if (am == null) return \"\";\n        List<ActivityManager.RunningAppProcessInfo> info = am.getRunningAppProcesses();\n        if (info == null || info.size() == 0) return \"\";\n        int pid = android.os.Process.myPid();\n        for (ActivityManager.RunningAppProcessInfo aInfo : info) {\n            if (aInfo.pid == pid) {\n                if (aInfo.processName != null) {\n                    return aInfo.processName;\n                }\n            }\n        }\n        return \"\";\n    }\n\n    private static String getCurrentProcessNameByReflect(Context context) {\n        String processName = \"\";\n        try {\n            Application app = (Application) context.getApplicationContext();\n            Field loadedApkField = app.getClass().getField(\"mLoadedApk\");\n            loadedApkField.setAccessible(true);\n            Object loadedApk = loadedApkField.get(app);\n\n            Field activityThreadField = loadedApk.getClass().getDeclaredField(\"mActivityThread\");\n            activityThreadField.setAccessible(true);\n            Object activityThread = activityThreadField.get(loadedApk);\n\n            Method getProcessName = activityThread.getClass().getDeclaredMethod(\"getProcessName\");\n            processName = (String) getProcessName.invoke(activityThread);\n        } catch (Exception e) {\n            e.printStackTrace();\n        }\n        return processName;\n    }\n\n\n    /***********************RomUtils**********************************/\n    private static final String[] ROM_HUAWEI = {\"huawei\"};\n    private static final String VERSION_PROPERTY_HUAWEI = \"ro.build.version.emui\";\n    private final static String UNKNOWN = \"unknown\";\n\n    private static RomInfo bean = null;\n\n\n    /**\n     * Return whether the rom is made by huawei.\n     *\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    private static boolean isHuawei() {\n        return ROM_HUAWEI[0].equals(getRomInfo().name);\n    }\n\n    /**\n     * Return the rom's information.\n     *\n     * @return the rom's information\n     */\n    private static RomInfo getRomInfo() {\n        if (bean != null) return bean;\n        bean = new RomInfo();\n        final String brand = getBrand();\n        final String manufacturer = getManufacturer();\n        if (isRightRom(brand, manufacturer, ROM_HUAWEI)) {\n            bean.name = ROM_HUAWEI[0];\n            String version = getRomVersion(VERSION_PROPERTY_HUAWEI);\n            String[] temp = version.split(\"_\");\n            if (temp.length > 1) {\n                bean.version = temp[1];\n            } else {\n                bean.version = version;\n            }\n            return bean;\n        } else {\n            bean.name = manufacturer;\n        }\n        bean.version = getRomVersion(\"\");\n        return bean;\n    }\n\n    private static boolean isRightRom(final String brand, final String manufacturer, final String... names) {\n        for (String name : names) {\n            if (brand.contains(name) || manufacturer.contains(name)) {\n                return true;\n            }\n        }\n        return false;\n    }\n\n    private static String getManufacturer() {\n        try {\n            String manufacturer = Build.MANUFACTURER;\n            if (!TextUtils.isEmpty(manufacturer)) {\n                return manufacturer.toLowerCase();\n            }\n        } catch (Throwable ignore) {/**/}\n        return UNKNOWN;\n    }\n\n    private static String getBrand() {\n        try {\n            String brand = Build.BRAND;\n            if (!TextUtils.isEmpty(brand)) {\n                return brand.toLowerCase();\n            }\n        } catch (Throwable ignore) {/**/}\n        return UNKNOWN;\n    }\n\n    private static String getRomVersion(final String propertyName) {\n        String ret = \"\";\n        if (!TextUtils.isEmpty(propertyName)) {\n            ret = getSystemProperty(propertyName);\n        }\n        if (TextUtils.isEmpty(ret) || ret.equals(UNKNOWN)) {\n            try {\n                String display = Build.DISPLAY;\n                if (!TextUtils.isEmpty(display)) {\n                    ret = display.toLowerCase();\n                }\n            } catch (Throwable ignore) {/**/}\n        }\n        if (TextUtils.isEmpty(ret)) {\n            return UNKNOWN;\n        }\n        return ret;\n    }\n\n    private static String getSystemProperty(final String name) {\n        String prop = getSystemPropertyByShell(name);\n        if (!TextUtils.isEmpty(prop)) return prop;\n        prop = getSystemPropertyByStream(name);\n        if (!TextUtils.isEmpty(prop)) return prop;\n        if (Build.VERSION.SDK_INT < 28) {\n            return getSystemPropertyByReflect(name);\n        }\n        return prop;\n    }\n\n    private static String getSystemPropertyByShell(final String propName) {\n        String line;\n        BufferedReader input = null;\n        try {\n            Process p = Runtime.getRuntime().exec(\"getprop \" + propName);\n            input = new BufferedReader(new InputStreamReader(p.getInputStream()), 1024);\n            String ret = input.readLine();\n            if (ret != null) {\n                return ret;\n            }\n        } catch (IOException ignore) {\n        } finally {\n            if (input != null) {\n                try {\n                    input.close();\n                } catch (IOException ignore) {/**/}\n            }\n        }\n        return \"\";\n    }\n\n    private static String getSystemPropertyByStream(final String key) {\n        try {\n            Properties prop = new Properties();\n            FileInputStream is = new FileInputStream(\n                    new File(Environment.getRootDirectory(), \"build.prop\")\n            );\n            prop.load(is);\n            return prop.getProperty(key, \"\");\n        } catch (Exception ignore) {/**/}\n        return \"\";\n    }\n\n    private static String getSystemPropertyByReflect(String key) {\n        try {\n            @SuppressLint(\"PrivateApi\")\n            Class<?> clz = Class.forName(\"android.os.SystemProperties\");\n            Method getMethod = clz.getMethod(\"get\", String.class, String.class);\n            return (String) getMethod.invoke(clz, key, \"\");\n        } catch (Exception e) {/**/}\n        return \"\";\n    }\n\n    private static class RomInfo {\n        private String name;\n        private String version;\n\n        public String getName() {\n            return name;\n        }\n\n        public String getVersion() {\n            return version;\n        }\n\n        @Override\n        public String toString() {\n            return \"RomInfo{name=\" + name +\n                    \", version=\" + version + \"}\";\n        }\n    }\n\n}\n\n"
  },
  {
    "path": "sample/src/main/java/com/just/agentweb/sample/widget/CommonIndicator.java",
    "content": "package com.just.agentweb.sample.widget;\n\nimport android.content.Context;\nimport androidx.annotation.Nullable;\nimport android.util.AttributeSet;\nimport android.view.View;\nimport android.widget.FrameLayout;\n\nimport com.just.agentweb.BaseIndicatorView;\n\n/**\n * Created by cenxiaozhong on 2017/5/26.\n * source code  https://github.com/Justson/AgentWeb\n */\n\npublic class CommonIndicator extends BaseIndicatorView {\n\tpublic CommonIndicator(Context context) {\n\t\tsuper(context);\n\t}\n\n\tpublic CommonIndicator(Context context, @Nullable AttributeSet attrs) {\n\t\tsuper(context, attrs);\n\t}\n\n\tpublic CommonIndicator(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {\n\t\tsuper(context, attrs, defStyleAttr);\n\t}\n\n\n\t@Override\n\tpublic void show() {\n\t\tthis.setVisibility(View.VISIBLE);\n\t}\n\n\t@Override\n\tpublic void hide() {\n\t\tthis.setVisibility(View.GONE);\n\t}\n\n\n\t@Override\n\tpublic LayoutParams offerLayoutParams() {\n\t\treturn new FrameLayout.LayoutParams(-1, -1);\n\t}\n}\n"
  },
  {
    "path": "sample/src/main/java/com/just/agentweb/sample/widget/CoolIndicatorLayout.java",
    "content": "\n/*\n * Copyright (C)  Justson(https://github.com/Justson/AgentWeb)\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.just.agentweb.sample.widget;\n\nimport android.app.Activity;\nimport android.content.Context;\nimport android.os.Build;\nimport androidx.annotation.Nullable;\nimport android.util.AttributeSet;\nimport android.view.View;\nimport android.widget.FrameLayout;\n\nimport com.coolindicator.sdk.CoolIndicator;\nimport com.just.agentweb.AgentWebUtils;\nimport com.just.agentweb.BaseIndicatorView;\n\n/**\n * @author cenxiaozhong\n * @date 2018/2/23\n * @since 1.0.0\n */\npublic class CoolIndicatorLayout extends BaseIndicatorView {\n\n\n\tprivate static final String TAG = CoolIndicatorLayout.class.getSimpleName();\n\tprivate CoolIndicator mCoolIndicator = null;\n\n\n\tpublic CoolIndicatorLayout(Context context) {\n\t\tthis(context, null);\n\t}\n\n\tpublic CoolIndicatorLayout(Context context, @Nullable AttributeSet attrs) {\n\t\tthis(context, attrs, -1);\n\t}\n\n\tpublic CoolIndicatorLayout(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {\n\t\tsuper(context, attrs, defStyleAttr);\n\t\tmCoolIndicator = CoolIndicator.create((Activity) context);\n\t\tif (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {\n\t\t\tmCoolIndicator.setProgressDrawable(context.getResources().getDrawable(com.coolindicator.sdk.R.drawable.default_drawable_indicator, context.getTheme()));\n\t\t} else {\n\t\t\tmCoolIndicator.setProgressDrawable(context.getResources().getDrawable(com.coolindicator.sdk.R.drawable.default_drawable_indicator));\n\t\t}\n\n\t\tthis.addView(mCoolIndicator, offerLayoutParams());\n\n\t}\n\n\t@Override\n\tpublic void show() {\n\t\tthis.setVisibility(View.VISIBLE);\n\t\tmCoolIndicator.start();\n\t}\n\n\t@Override\n\tpublic void setProgress(int newProgress) {\n\t}\n\n\t@Override\n\tpublic void hide() {\n\t\tmCoolIndicator.complete();\n\t}\n\n\t@Override\n\tpublic LayoutParams offerLayoutParams() {\n\t\treturn new FrameLayout.LayoutParams(-1, AgentWebUtils.dp2px(getContext(), 3));\n\t}\n}\n"
  },
  {
    "path": "sample/src/main/java/com/just/agentweb/sample/widget/SmartRefreshWebLayout.java",
    "content": "package com.just.agentweb.sample.widget;\n\nimport android.app.Activity;\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.webkit.WebView;\n\nimport com.just.agentweb.IWebLayout;\nimport com.just.agentweb.sample.R;\nimport com.scwang.smartrefresh.layout.SmartRefreshLayout;\n\n/**\n * Created by cenxiaozhong on 2017/8/14.\n */\n\npublic class SmartRefreshWebLayout implements IWebLayout {\n\n    private final SmartRefreshLayout mSmartRefreshLayout;\n    private final WebView mWebView;\n\n    public SmartRefreshWebLayout(Activity activity){\n\n        View mView=activity.getLayoutInflater().inflate(R.layout.fragment_srl_web,null);\n        View smarkView = mView.findViewById(R.id.smarkLayout);\n        mSmartRefreshLayout = (SmartRefreshLayout) smarkView;\n        mWebView = (WebView) mSmartRefreshLayout.findViewById(R.id.webView);\n\n    }\n\n    @NonNull\n    @Override\n    public ViewGroup getLayout() {\n        return mSmartRefreshLayout;\n    }\n\n    @Nullable\n    @Override\n    public WebView getWebView() {\n        return mWebView;\n    }\n}\n"
  },
  {
    "path": "sample/src/main/java/com/just/agentweb/sample/widget/WebLayout.java",
    "content": "package com.just.agentweb.sample.widget;\n\nimport android.app.Activity;\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport android.view.LayoutInflater;\nimport android.view.ViewGroup;\nimport android.webkit.WebView;\n\nimport com.just.agentweb.IWebLayout;\nimport com.just.agentweb.sample.R;\nimport com.lcodecore.tkrefreshlayout.TwinklingRefreshLayout;\n\n/**\n * Created by cenxiaozhong on 2017/7/1.\n * source code  https://github.com/Justson/AgentWeb\n */\n\npublic class WebLayout implements IWebLayout {\n\n    private Activity mActivity;\n    private final TwinklingRefreshLayout mTwinklingRefreshLayout;\n    private WebView mWebView = null;\n\n    public WebLayout(Activity activity) {\n        this.mActivity = activity;\n        mTwinklingRefreshLayout = (TwinklingRefreshLayout) LayoutInflater.from(activity).inflate(R.layout.fragment_twk_web, null);\n        mTwinklingRefreshLayout.setPureScrollModeOn();\n        mWebView = (WebView) mTwinklingRefreshLayout.findViewById(R.id.webView);\n    }\n\n    @NonNull\n    @Override\n    public ViewGroup getLayout() {\n        return mTwinklingRefreshLayout;\n    }\n\n    @Nullable\n    @Override\n    public WebView getWebView() {\n        return mWebView;\n    }\n\n\n\n}\n"
  },
  {
    "path": "sample/src/main/res/drawable/btn_shape.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\"\n       android:shape=\"rectangle\">\n\n\n    <solid android:color=\"@color/blue_color_light\"></solid>\n    <corners android:radius=\"5dp\"></corners>\n\n\n</shape>"
  },
  {
    "path": "sample/src/main/res/drawable/btn_shape_s.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\"\n       android:shape=\"rectangle\">\n\n\n    <solid android:color=\"@color/blue_color\"></solid>\n    <corners android:radius=\"4dp\"></corners>\n\n\n</shape>"
  },
  {
    "path": "sample/src/main/res/drawable/ic_baseline_search_24.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\"\n    android:tint=\"?attr/colorControlNormal\">\n  <path\n      android:fillColor=\"#ffffff\"\n      android:pathData=\"M15.5,14h-0.79l-0.28,-0.27C15.41,12.59 16,11.11 16,9.5 16,5.91 13.09,3 9.5,3S3,5.91 3,9.5 5.91,16 9.5,16c1.61,0 3.09,-0.59 4.23,-1.57l0.27,0.28v0.79l5,4.99L20.49,19l-4.99,-5zM9.5,14C7.01,14 5,11.99 5,9.5S7.01,5 9.5,5 14,7.01 14,9.5 11.99,14 9.5,14z\"/>\n</vector>\n"
  },
  {
    "path": "sample/src/main/res/drawable/indicator_shape.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\" android:shape=\"rectangle\">\n\n\n    <solid android:color=\"@color/colorPrimary_light_trsp\"></solid>\n    <corners android:radius=\"5dp\"></corners>\n\n</shape>"
  },
  {
    "path": "sample/src/main/res/drawable/iv_back_selector.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n    <item android:drawable=\"@color/colorPrimary\"  android:state_pressed=\"false\"></item>\n    <item android:drawable=\"@color/select_color\" android:state_pressed=\"true\"></item>\n</selector>"
  },
  {
    "path": "sample/src/main/res/drawable/selector_drawable_for_btn.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:drawable=\"@drawable/btn_shape\" android:state_pressed=\"true\"/>\n    <item android:drawable=\"@drawable/btn_shape_s\"/>\n</selector>\n"
  },
  {
    "path": "sample/src/main/res/drawable-v21/ripple_for_btn.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<ripple xmlns:android=\"http://schemas.android.com/apk/res/android\"\n        android:color=\"@color/colorPrimary\"\n    >\n    <item\n        android:id=\"@android:id/mask\"\n        android:drawable=\"@drawable/btn_shape_s\"/>\n\n</ripple>\n"
  },
  {
    "path": "sample/src/main/res/drawable-v21/selector_drawable_for_btn.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ripple xmlns:android=\"http://schemas.android.com/apk/res/android\"\n        android:color=\"@color/blue_color_light\"\n    >\n    <item\n        android:drawable=\"@drawable/btn_shape_s\"/>\n</ripple>"
  },
  {
    "path": "sample/src/main/res/layout/activity_auto_hiden_toolbar.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:id=\"@+id/main\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    tools:context=\"com.just.agentweb.sample.activity.AutoHidenToolbarActivity\">\n\n    <com.google.android.material.appbar.AppBarLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"?attr/actionBarSize\">\n\n        <androidx.appcompat.widget.Toolbar\n            android:id=\"@+id/toolbar\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\"\n            app:layout_scrollFlags=\"scroll|enterAlways\" />\n    </com.google.android.material.appbar.AppBarLayout>\n\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"?attr/actionBarSize\"\n        android:layout_gravity=\"bottom\"\n        android:background=\"@color/colorPrimary\"\n        android:gravity=\"center_vertical\"\n        android:weightSum=\"4\"\n        app:layout_behavior=\"com.just.agentweb.sample.behavior.BottomNavigationViewBehavior\">\n\n        <TextView\n            android:id=\"@+id/btn_back\"\n            android:layout_width=\"0dp\"\n            android:layout_height=\"match_parent\"\n            android:layout_weight=\"1\"\n            android:gravity=\"center\"\n            android:text=\"后退\"\n            android:textColor=\"#fff\" />\n\n        <TextView\n            android:id=\"@+id/btn_forward\"\n            android:layout_width=\"0dp\"\n            android:layout_height=\"match_parent\"\n            android:layout_weight=\"1\"\n            android:gravity=\"center\"\n            android:text=\"前进\"\n            android:textColor=\"#fff\" />\n\n        <TextView\n            android:id=\"@+id/btn_refresh\"\n            android:layout_width=\"0dp\"\n            android:layout_height=\"match_parent\"\n            android:layout_weight=\"1\"\n            android:gravity=\"center\"\n            android:text=\"刷新\"\n            android:textColor=\"#fff\" />\n\n        <TextView\n            android:id=\"@+id/btn_menu\"\n            android:layout_width=\"0dp\"\n            android:layout_height=\"match_parent\"\n            android:layout_weight=\"1\"\n            android:gravity=\"center\"\n            android:text=\"菜单\"\n            android:textColor=\"#fff\" />\n    </LinearLayout>\n</androidx.coordinatorlayout.widget.CoordinatorLayout>\n"
  },
  {
    "path": "sample/src/main/res/layout/activity_common.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<FrameLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:id=\"@+id/container_framelayout\"\n    >\n\n</FrameLayout>\n"
  },
  {
    "path": "sample/src/main/res/layout/activity_main.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:orientation=\"vertical\"\n    android:layout_height=\"match_parent\">\n\n    <androidx.appcompat.widget.Toolbar\n        android:id=\"@+id/toolbar\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"?android:actionBarSize\"\n        android:background=\"?attr/colorPrimary\"\n        android:titleTextColor=\"@android:color/white\"\n        app:theme=\"@style/toolBar\"\n        app:titleTextColor=\"@android:color/white\">\n\n\n        <TextView\n            android:id=\"@+id/toolbar_title\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_gravity=\"center\"\n            android:singleLine=\"true\"\n            android:textColor=\"@android:color/white\"\n            android:textSize=\"14sp\"/>\n    </androidx.appcompat.widget.Toolbar>\n\n\n    <ListView\n        android:id=\"@+id/listView\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"></ListView>\n\n</LinearLayout>\n"
  },
  {
    "path": "sample/src/main/res/layout/activity_native_download.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:orientation=\"vertical\">\n\n    <androidx.appcompat.widget.Toolbar\n        android:id=\"@+id/toolbar\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"?android:actionBarSize\"\n        android:background=\"?attr/colorPrimary\"\n        android:titleTextColor=\"@android:color/white\"\n        app:theme=\"@style/toolBar\"\n        app:titleTextColor=\"@android:color/white\">\n\n\n        <TextView\n            android:id=\"@+id/toolbar_title\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_gravity=\"center\"\n            android:singleLine=\"true\"\n            android:textColor=\"@android:color/white\"\n            android:textSize=\"14sp\"/>\n    </androidx.appcompat.widget.Toolbar>\n\n    <androidx.recyclerview.widget.RecyclerView\n        android:id=\"@+id/download_recyclerview\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\">\n    </androidx.recyclerview.widget.RecyclerView>\n</LinearLayout>\n"
  },
  {
    "path": "sample/src/main/res/layout/activity_web.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:id=\"@+id/container\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:orientation=\"vertical\"\n    tools:context=\"com.just.agentweb.sample.activity.MainActivity\">\n\n\n    <androidx.appcompat.widget.Toolbar\n        android:id=\"@+id/toolbar\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"?android:actionBarSize\"\n        android:background=\"?attr/colorPrimary\"\n        android:titleTextColor=\"@android:color/white\"\n        app:theme=\"@style/toolBar\"\n        app:titleTextColor=\"@android:color/white\">\n\n\n        <TextView\n            android:id=\"@+id/toolbar_title\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_gravity=\"center\"\n            android:singleLine=\"true\"\n            android:textColor=\"@android:color/white\"\n            android:textSize=\"14sp\"/>\n    </androidx.appcompat.widget.Toolbar>\n\n    \n\n</LinearLayout>\n"
  },
  {
    "path": "sample/src/main/res/layout/fragment_agentweb.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:id=\"@+id/linearLayout\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:orientation=\"vertical\">\n\n\n    <include android:id=\"@+id/toolbar\" layout=\"@layout/toorbar_main\"></include>\n\n</LinearLayout>\n"
  },
  {
    "path": "sample/src/main/res/layout/fragment_js.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:orientation=\"vertical\">\n\n\n    <Button\n        android:textColor=\"@color/white\"\n        android:layout_marginTop=\"30dp\"\n        android:text=\"调用Js，无参数方法\"\n        android:textSize=\"14sp\"\n        android:id=\"@+id/callJsNoParamsButton\"\n        android:background=\"@drawable/selector_drawable_for_btn\"\n        android:layout_marginLeft=\"20dp\"\n        android:textAllCaps=\"false\"\n        android:layout_marginRight=\"20dp\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"/>\n\n    <Button\n        android:layout_marginTop=\"10dp\"\n        android:background=\"@drawable/selector_drawable_for_btn\"\n        android:layout_marginLeft=\"20dp\"\n        android:text=\"调用Js，一个参数方法\"\n        android:textAllCaps=\"false\"\n        android:textSize=\"14sp\"\n        android:id=\"@+id/callJsOneParamsButton\"\n        android:textColor=\"@color/white\"\n        android:layout_marginRight=\"20dp\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"/>\n    <Button\n        android:text=\"调用Js，多个参数方法\"\n        android:textSize=\"14sp\"\n        android:id=\"@+id/callJsMoreParamsButton\"\n        android:textColor=\"@color/white\"\n        android:layout_marginTop=\"10dp\"\n        android:textAllCaps=\"false\"\n        android:background=\"@drawable/selector_drawable_for_btn\"\n        android:layout_marginLeft=\"20dp\"\n        android:layout_marginRight=\"20dp\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        />\n\n    <Button\n        android:text=\"Android-Js-Android\"\n        android:textSize=\"14sp\"\n        android:id=\"@+id/jsJavaCommunicationButton\"\n        android:textColor=\"@color/white\"\n        android:layout_marginTop=\"10dp\"\n        android:background=\"@drawable/selector_drawable_for_btn\"\n        android:layout_marginLeft=\"20dp\"\n        android:layout_marginRight=\"20dp\"\n        android:layout_width=\"match_parent\"\n        android:textAllCaps=\"false\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginBottom=\"30dp\"/>\n\n</LinearLayout>\n"
  },
  {
    "path": "sample/src/main/res/layout/fragment_srl_web.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<com.scwang.smartrefresh.layout.SmartRefreshLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:id=\"@+id/smarkLayout\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    app:srlAccentColor=\"@android:color/white\"\n    app:srlEnableHeaderTranslationContent=\"false\"\n    app:srlEnablePreviewInEditMode=\"false\"\n    app:srlPrimaryColor=\"@color/colorPrimary\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\">\n    <!--<com.scwang.smartrefresh.header.PhoenixHeader-->\n    <!--android:id=\"@+id/header\"-->\n    <!--android:layout_width=\"match_parent\"-->\n    <!--android:layout_height=\"wrap_content\"-->\n    <!--android:layout_marginTop=\"?attr/actionBarSize\"/>-->\n    <com.scwang.smartrefresh.header.MaterialHeader\n        android:id=\"@+id/header\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginTop=\"?attr/actionBarSize\"/>\n\n    <WebView\n        android:id=\"@+id/webView\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:clipToPadding=\"false\"\n        android:paddingTop=\"?attr/actionBarSize\"/>\n</com.scwang.smartrefresh.layout.SmartRefreshLayout>\n"
  },
  {
    "path": "sample/src/main/res/layout/fragment_twk_web.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<com.lcodecore.tkrefreshlayout.TwinklingRefreshLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:id=\"@+id/refreshLayout\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:background=\"@android:color/transparent\"\n    app:tr_pureScrollMode_on=\"true\">\n\n    <WebView\n        android:id=\"@+id/webView\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:background=\"#fff\"\n        android:visibility=\"visible\"\n        android:overScrollMode=\"never\"/>\n</com.lcodecore.tkrefreshlayout.TwinklingRefreshLayout>\n"
  },
  {
    "path": "sample/src/main/res/layout/listview_main.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"50dp\">\n\n    <TextView\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:text=\"\"\n        android:textColor=\"@color/colorPrimary\"\n        android:layout_alignParentLeft=\"true\"\n        android:layout_centerVertical=\"true\"\n        android:textSize=\"16sp\"\n        android:layout_marginLeft=\"20dp\"\n        android:id=\"@+id/content\"\n        />\n\n</RelativeLayout>\n"
  },
  {
    "path": "sample/src/main/res/layout/markdown_view.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout\nxmlns:android=\"http://schemas.android.com/apk/res/android\"\nandroid:layout_width=\"match_parent\"\nandroid:layout_height=\"match_parent\"\nxmlns:app=\"http://schemas.android.com/apk/res-auto\"\nandroid:weightSum=\"2\"\nandroid:orientation=\"vertical\">\n\n\n<include android:id=\"@+id/toolbar\" layout=\"@layout/toorbar_main\"></include>\n\n<EditText\n    android:id=\"@+id/markdownText\"\n    android:layout_width=\"fill_parent\"\n    android:layout_height=\"0dp\"\n    android:layout_weight=\"1\"\n    android:gravity=\"left\"\n    android:background=\"#e2e2e2\"\n    android:inputType=\"textMultiLine\" >\n\n\n</EditText>\n\n</LinearLayout>"
  },
  {
    "path": "sample/src/main/res/layout/recyclerview_item_download.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"60dp\"\n    android:orientation=\"vertical\">\n\n    <ImageView\n        android:id=\"@+id/icon_iv\"\n        android:layout_width=\"45dp\"\n        android:layout_height=\"45dp\"\n        android:layout_centerVertical=\"true\"\n        android:layout_marginLeft=\"5dp\"\n        />\n\n\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_centerVertical=\"true\"\n        android:layout_marginStart=\"8dp\"\n        android:layout_marginEnd=\"80dp\"\n        android:layout_toEndOf=\"@+id/icon_iv\"\n        android:orientation=\"vertical\">\n\n        <ProgressBar\n            android:id=\"@+id/progressBar\"\n            style=\"?android:attr/progressBarStyleHorizontal\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_centerVertical=\"true\"\n            android:layout_marginTop=\"8dp\"\n            tools:ignore=\"RtlCompat\"/>\n\n        <TextView\n            android:id=\"@+id/current_progress\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginRight=\"5dp\"\n            android:gravity=\"left\"\n            android:text=\"当前进度\"\n            android:textColor=\"@color/colorPrimary\"\n            android:textSize=\"12sp\"\n            />\n    </LinearLayout>\n\n\n    <Button\n        android:id=\"@+id/start_button\"\n        android:layout_width=\"100dp\"\n        android:layout_height=\"50dp\"\n        android:layout_alignParentEnd=\"true\"\n        android:layout_centerVertical=\"true\"\n        android:text=\"开始\"\n        tools:ignore=\"RtlCompat\"/>\n\n</RelativeLayout>"
  },
  {
    "path": "sample/src/main/res/layout/toorbar_main.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.appcompat.widget.Toolbar\n    android:id=\"@+id/toolbar\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"?android:actionBarSize\"\n    android:background=\"?attr/colorPrimary\"\n    app:contentInsetLeft=\"0dp\"\n    app:contentInsetStart=\"0dp\"\n    android:titleTextColor=\"@android:color/white\"\n    app:titleTextColor=\"@android:color/white\"\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\">\n\n   <FrameLayout\n       android:layout_width=\"match_parent\"\n       android:layout_height=\"match_parent\">\n       <RelativeLayout\n           android:layout_width=\"match_parent\"\n           android:layout_height=\"match_parent\">\n\n           <ImageView\n               android:id=\"@+id/iv_back\"\n               android:layout_width=\"55dp\"\n               android:layout_height=\"match_parent\"\n               android:layout_centerVertical=\"true\"\n               android:src=\"@mipmap/back\"\n               android:background=\"@drawable/iv_back_selector\"\n               android:scaleType=\"center\"\n               android:clickable=\"true\"\n               />\n           <View\n               android:id=\"@+id/view_line\"\n               android:layout_width=\"1px\"\n               android:layout_marginTop=\"10dp\"\n               android:layout_marginBottom=\"10dp\"\n               android:layout_height=\"match_parent\"\n               android:background=\"@color/black\"\n               android:layout_toRightOf=\"@+id/iv_back\"\n               ></View>\n           <ImageView\n               android:id=\"@+id/iv_finish\"\n               android:layout_toRightOf=\"@+id/view_line\"\n               android:layout_width=\"55dp\"\n               android:layout_height=\"match_parent\"\n               android:src=\"@mipmap/cha\"\n               android:scaleType=\"center\"\n               android:background=\"@drawable/iv_back_selector\"\n               />\n\n           <TextView\n               android:id=\"@+id/toolbar_title\"\n               android:layout_width=\"wrap_content\"\n               android:layout_height=\"wrap_content\"\n               android:layout_centerInParent=\"true\"\n               android:singleLine=\"true\"\n               android:textColor=\"@android:color/white\"\n               android:textSize=\"14sp\"/>\n\n\n           <ImageView\n               android:id=\"@+id/iv_search\"\n               android:layout_width=\"55dp\"\n               android:layout_height=\"match_parent\"\n               android:src=\"@drawable/ic_baseline_search_24\"\n               android:scaleType=\"center\"\n               android:tint=\"@color/white\"\n               android:layout_toLeftOf=\"@+id/iv_more\"\n               android:background=\"@drawable/iv_back_selector\"\n               />\n\n\n           <ImageView\n               android:id=\"@+id/iv_more\"\n               android:layout_width=\"55dp\"\n               android:layout_height=\"match_parent\"\n               android:src=\"@mipmap/more\"\n               android:scaleType=\"center\"\n               android:layout_alignParentRight=\"true\"\n               android:background=\"@drawable/iv_back_selector\"\n               />\n       </RelativeLayout>\n       <com.ferfalk.simplesearchview.SimpleSearchView\n           android:id=\"@+id/search_view\"\n           android:layout_width=\"match_parent\"\n           android:layout_height=\"match_parent\"\n           android:background=\"@color/colorPrimary\" />\n   </FrameLayout>\n\n\n</androidx.appcompat.widget.Toolbar>\n\n"
  },
  {
    "path": "sample/src/main/res/menu/toolbar_menu.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<menu\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\">\n\n    <item\n        android:id=\"@+id/refresh\"\n        android:title=\"刷新网页\"\n        android:visible=\"true\"\n        app:showAsAction=\"never\"/>\n\n\n    <item\n        android:id=\"@+id/copy\"\n        android:title=\"复制链接\"\n        android:visible=\"true\"\n        app:showAsAction=\"never\"/>\n\n    <item\n        android:id=\"@+id/default_clean\"\n        android:title=\"清空缓存\"\n        android:visible=\"false\"\n        app:showAsAction=\"never\"/>\n\n    <item\n        android:id=\"@+id/error_website\"\n        android:title=\"显示错误页\"\n        android:visible=\"true\"\n        app:showAsAction=\"never\"/>\n\n\n    <item\n        android:id=\"@+id/default_browser\"\n        android:title=\"在浏览器打开\"\n        android:visible=\"true\"\n        app:showAsAction=\"never\"/>\n\n\n\n</menu>"
  },
  {
    "path": "sample/src/main/res/values/colors.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <color name=\"colorPrimary\">#393a3f</color>\n    <color name=\"colorPrimaryDark\">#303135</color>\n    <color name=\"colorAccent\">#1aad19</color>\n\n    <color name=\"blue_color\">#1B9AF7</color>\n    <color name=\"blue_color_light\">#58b2f2</color>\n    <color name=\"select_color\">#2e2e32</color>\n    <color name=\"black\">#000000</color>\n    <color name=\"white\">#ffffff</color>\n    <color name=\"colorPrimary_light_trsp\" >#7b393a3f</color>\n</resources>\n"
  },
  {
    "path": "sample/src/main/res/values/strings.xml",
    "content": "<resources>\n    <string name=\"app_name\" translatable=\"false\">AgentWeb</string>\n</resources>\n"
  },
  {
    "path": "sample/src/main/res/values/styles.xml",
    "content": "<resources>\n\n    <!-- Base application theme. -->\n    <style name=\"BaseAppTheme\" parent=\"AppTheme\">\n        <!-- Customize your theme here. -->\n        <item name=\"colorPrimary\">@color/colorPrimary</item>\n        <item name=\"colorPrimaryDark\">@color/colorPrimaryDark</item>\n        <item name=\"colorAccent\">@color/colorAccent</item>\n        <item name=\"android:actionBarSize\">48dp</item>\n\n    </style>\n\n        <style name=\"AppBaseTheme\" parent=\"Theme.AppCompat.Light.DarkActionBar\"></style>\n        <style name=\"AppTheme\" parent=\"AppBaseTheme\">\n            <item name=\"windowActionBar\">false</item>\n            <item name=\"windowNoTitle\">true</item>\n        </style>\n\n    <style name=\"toolBar\">\n        <item name=\"colorControlNormal\">#ffffff</item>\n        <item name=\"android:homeAsUpIndicator\">@mipmap/cha</item>\n        <item name=\"android:titleTextStyle\">@style/titlestyle</item>\n\n    </style>\n\n    <style name=\"titlestyle\">\n\n        <item name=\"android:textSize\">12sp</item>\n    </style>\n\n\n\n\n\n</resources>\n"
  },
  {
    "path": "sample/src/main/res/xml/network_security_config.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<network-security-config>\r\n    <base-config cleartextTrafficPermitted=\"true\">\r\n        <trust-anchors>\r\n            <certificates src=\"system\" overridePins=\"true\" /> <!--信任系统证书-->\r\n        </trust-anchors>\r\n    </base-config>\r\n</network-security-config>"
  },
  {
    "path": "sample/src/test/java/com/just/agentweb/sample/ExampleUnitTest.java",
    "content": "package com.just.agentweb.sample;\n\nimport org.junit.Test;\n\nimport static org.junit.Assert.*;\n\n/**\n * Example local unit test, which will execute on the development machine (host).\n *\n * @see <a href=\"http://d.android.com/tools/testing\">Testing documentation</a>\n */\npublic class ExampleUnitTest {\n    @Test\n    public void addition_isCorrect() throws Exception {\n        assertEquals(4, 2 + 2);\n    }\n}"
  },
  {
    "path": "settings.gradle",
    "content": "include ':sample', ':agentweb-core',  ':agentweb-filechooser'\n"
  }
]