├── .github ├── ISSUE_TEMPLATE │ └── bug_report.md ├── webviews.jpg └── workflows │ └── android.yml ├── .gitignore ├── .idea ├── codeStyles │ └── Project.xml ├── compiler.xml ├── gradle.xml ├── jarRepositories.xml ├── migrations.xml ├── misc.xml └── vcs.xml ├── LICENSE ├── README.md ├── app ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── assets │ └── xposed_init │ ├── java │ └── com │ │ └── thinkdifferent │ │ └── anywebview │ │ ├── AnyWebView.java │ │ ├── BaseXposedHookLoadPackage.java │ │ └── XposedHelpersWraper.java │ └── res │ └── values │ └── strings.xml ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: "[Bug] XXX webview will not display in the webview list" 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | System webview information: 15 | Package name: [e.g. com.android.webview] 16 | Arch: [e.g. aarch64] 17 | Download link: [e.g. https://www.example.com/systemwebview.apk] 18 | 19 | **Expected behavior** 20 | A clear and concise description of what you expected to happen. 21 | 22 | **Screenshots** 23 | If applicable, add screenshots to help explain your problem. 24 | 25 | **Smartphone (please complete the following information):** 26 | - Device: [e.g. Pixel 8] 27 | - OS: [e.g. Android 14] 28 | - Arch: [e.g. aarch64] 29 | 30 | **Additional context** 31 | Add any other context about the problem here. 32 | 33 | **Error log** 34 | Exposed Bridge output error log. 35 | -------------------------------------------------------------------------------- /.github/webviews.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neoblackxt/AnyWebView/89b62f6eed4bc64ce7a95cca996ecf3af4cdc6ad/.github/webviews.jpg -------------------------------------------------------------------------------- /.github/workflows/android.yml: -------------------------------------------------------------------------------- 1 | name: Android CI 2 | 3 | on: [push] 4 | 5 | jobs: 6 | build: 7 | 8 | runs-on: ubuntu-latest 9 | 10 | steps: 11 | - uses: actions/checkout@v3 12 | - name: set up JDK 17 13 | uses: actions/setup-java@v3 14 | with: 15 | distribution: 'adopt' 16 | java-version: '17' 17 | cache: gradle 18 | - name: Grant execute permission for gradlew 19 | run: chmod +x gradlew 20 | - name: Build with Gradle 21 | run: ./gradlew build 22 | - uses: actions/upload-artifact@v3 23 | with: 24 | name: artifact 25 | path: app/build/outputs/apk/ 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/caches 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | /.idea/navEditor.xml 9 | /.idea/assetWizardSettings.xml 10 | .DS_Store 11 | /build 12 | /captures 13 | .externalNativeBuild 14 | .cxx 15 | 16 | # Project exclude paths 17 | /app/build/ 18 | /app/build/intermediates/javac/debug/classes/ -------------------------------------------------------------------------------- /.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 |
7 | 8 | 9 | 10 | xmlns:android 11 | 12 | ^$ 13 | 14 | 15 | 16 |
17 |
18 | 19 | 20 | 21 | xmlns:.* 22 | 23 | ^$ 24 | 25 | 26 | BY_NAME 27 | 28 |
29 |
30 | 31 | 32 | 33 | .*:id 34 | 35 | http://schemas.android.com/apk/res/android 36 | 37 | 38 | 39 |
40 |
41 | 42 | 43 | 44 | .*:name 45 | 46 | http://schemas.android.com/apk/res/android 47 | 48 | 49 | 50 |
51 |
52 | 53 | 54 | 55 | name 56 | 57 | ^$ 58 | 59 | 60 | 61 |
62 |
63 | 64 | 65 | 66 | style 67 | 68 | ^$ 69 | 70 | 71 | 72 |
73 |
74 | 75 | 76 | 77 | .* 78 | 79 | ^$ 80 | 81 | 82 | BY_NAME 83 | 84 |
85 |
86 | 87 | 88 | 89 | .* 90 | 91 | http://schemas.android.com/apk/res/android 92 | 93 | 94 | ANDROID_ATTRIBUTE_ORDER 95 | 96 |
97 |
98 | 99 | 100 | 101 | .* 102 | 103 | .* 104 | 105 | 106 | BY_NAME 107 | 108 |
109 |
110 |
111 |
112 |
113 |
-------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 19 | 20 | -------------------------------------------------------------------------------- /.idea/jarRepositories.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 14 | 15 | 19 | 20 | 24 | 25 | 29 | 30 | -------------------------------------------------------------------------------- /.idea/migrations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 10 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 14 | 15 | 16 | 17 | 18 | Android API 19 Platform 19 | 20 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Neo 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AnyWebView 2 | 3 | Any WebView is OK! 4 | 5 | ### Feature 6 | 7 | It tries to detect all system webviews and add them to the developer options -> WebView implementation list. 8 | 9 | 10 | 11 | ### Usage 12 | 13 | Android Framework should be selected in LSPosed. 14 | 15 | A webview app must be installed for all users (or in all spaces, so-called Dual app, Second space, etc.) to be selectable. Maybe deleting redundant users is alternative. 16 | adb command: 17 | 18 | Enable "[redundant packages](https://chromium.googlesource.com/chromium/src/+/HEAD/android_webview/docs/quick-start.md#valid-package-is-not-installed_enabled-for-all-users)" (Maybe it won't work, but that's OK.): 19 | 20 | `adb shell cmd webviewupdate enable-redundant-packages` 21 | 22 | Get USER_ID list: 23 | 24 | `adb shell pm list users` 25 | 26 | Each user entry is as follow: UserInfo{USER_ID:USERNAME:INT} , USER_ID 0 is the main user. 27 | 28 | Install apk for specific USER_ID: 29 | 30 | `adb install --user USER_ID PATH/TO/APK/ON/COMPUTER` 31 | 32 | or 33 | 34 | `adb shell pm install-existing --user USER_ID PACKAGE.NAME.OF.APK` (for apks already installed for one user) 35 | 36 | or 37 | 38 | `adb push PATH/TO/APK/ON/COMPUTER PATH/TO/APK/ON/PHONE` (copy an apk file to phone from computer) 39 | 40 | `adb shell pm install --user USER_ID PATH/TO/APK/ON/PHONE` 41 | 42 | Delete a user (NOT RECOMENDED, be careful, you may lose important data): 43 | 44 | `adb shell pm remove-user USER_ID` 45 | 46 | All the `adb shell pm ...` commands above can be run in an Android terminal simulator(root access granted) as `pm ...` 47 | 48 | Reboot to take effect. 49 | 50 | ### FAQ 51 | 52 | Can I set Chrome as the system webview implementation? 53 | 54 | Only supported on Android 8-9, not supported on Android 10+. It is Google's policy that Google Chrome app is no longer the WebView provider in Android 10. Even though it is listed in the WebView implementation, it does not work on Android 10+. Related discussion: [AnyWebView#12](https://github.com/neoblackxt/AnyWebView/issues/12#issuecomment-1644258502) 55 | 56 | Is Bromite/Mulch/Vanadium etc. system webview supported? 57 | 58 | Yes. 59 | 60 | ### Learn More (For Developers) 61 | 62 | [WebView quick start](https://chromium.googlesource.com/chromium/src/+/HEAD/android_webview/docs/quick-start.md) 63 | 64 | [WebView for AOSP system integrators](https://chromium.googlesource.com/chromium/src/+/HEAD/android_webview/docs/aosp-system-integration.md) 65 | 66 | [WebView Architecture](https://chromium.googlesource.com/chromium/src/+/HEAD/android_webview/docs/architecture.md) 67 | 68 | [Installing SystemWebView](https://github.com/bromite/bromite/wiki/Installing-SystemWebView) 69 | 70 | [android_webview/nonembedded](https://chromium.googlesource.com/chromium/src/+/HEAD/android_webview/nonembedded/) 71 | 72 | [WebView Providers](https://chromium.googlesource.com/chromium/src/+/HEAD/android_webview/docs/webview-providers.md) 73 | 74 | [WebViewFactory.java](https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/webkit/WebViewFactory.java) 75 | 76 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdk 35 5 | defaultConfig { 6 | applicationId "com.thinkdifferent.anywebview" 7 | minSdk 24 8 | targetSdk 35 9 | versionCode 4 10 | versionName '1.3' 11 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 12 | } 13 | buildTypes { 14 | release { 15 | minifyEnabled false 16 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 17 | } 18 | } 19 | compileOptions { 20 | sourceCompatibility = JavaVersion.VERSION_11 21 | targetCompatibility = JavaVersion.VERSION_11 22 | } 23 | namespace 'com.thinkdifferent.anywebview' 24 | } 25 | 26 | repositories { 27 | google() 28 | mavenCentral() 29 | maven { url 'https://api.xposed.info/' } 30 | } 31 | 32 | dependencies { 33 | implementation fileTree(dir: 'libs', include: ['*.jar']) 34 | compileOnly 'de.robv.android.xposed:api:53' 35 | } 36 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 10 | 13 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /app/src/main/assets/xposed_init: -------------------------------------------------------------------------------- 1 | com.thinkdifferent.anywebview.AnyWebView -------------------------------------------------------------------------------- /app/src/main/java/com/thinkdifferent/anywebview/AnyWebView.java: -------------------------------------------------------------------------------- 1 | package com.thinkdifferent.anywebview; 2 | 3 | import static com.thinkdifferent.anywebview.XposedHelpersWraper.findAndHookConstructor; 4 | import static com.thinkdifferent.anywebview.XposedHelpersWraper.findAndHookMethod; 5 | import static com.thinkdifferent.anywebview.XposedHelpersWraper.findClass; 6 | import static com.thinkdifferent.anywebview.XposedHelpersWraper.findConstructorBestMatch; 7 | import static com.thinkdifferent.anywebview.XposedHelpersWraper.findField; 8 | import static com.thinkdifferent.anywebview.XposedHelpersWraper.findFirstFieldByExactType; 9 | import static com.thinkdifferent.anywebview.XposedHelpersWraper.findMethodExact; 10 | import static com.thinkdifferent.anywebview.XposedHelpersWraper.log; 11 | 12 | import android.content.pm.PackageInfo; 13 | import android.content.pm.PackageManager; 14 | import android.os.Build; 15 | import android.os.Bundle; 16 | import android.util.Base64; 17 | 18 | import java.io.ByteArrayInputStream; 19 | import java.io.InputStream; 20 | import java.lang.reflect.Array; 21 | import java.lang.reflect.Constructor; 22 | import java.lang.reflect.Field; 23 | import java.lang.reflect.Method; 24 | import java.security.cert.CertificateFactory; 25 | import java.security.cert.X509Certificate; 26 | import java.util.ArrayList; 27 | import java.util.Arrays; 28 | import java.util.List; 29 | 30 | import de.robv.android.xposed.XC_MethodHook; 31 | import de.robv.android.xposed.callbacks.XC_LoadPackage; 32 | 33 | public class AnyWebView extends BaseXposedHookLoadPackage { 34 | 35 | @Override 36 | public void handleLoadPackage(XC_LoadPackage.LoadPackageParam lpparam) { 37 | //services.jar 38 | if (lpparam.packageName.equals("android")) { 39 | Initialize initialize = new Initialize(lpparam).invoke(); 40 | Class classWebViewProviderInfo = initialize.getClassWebViewProviderInfo(); 41 | Class classWebViewProviderInfoArray = initialize.getClassWebViewProviderInfoArray(); 42 | Class classSystemImpl = initialize.getClassSystemImpl(); 43 | Class classWebViewUpdater = initialize.getClassWebViewUpdater(); 44 | Constructor constructorWebViewProviderInfo = initialize.getConstructorWebViewProviderInfo(); 45 | 46 | Class classSystemServer = findClass("com.android.server.SystemServer", lpparam.classLoader); 47 | Field mPackageManager = findField(classSystemServer, "mPackageManager"); 48 | mPackageManager.setAccessible(true); 49 | final PackageManager[] mPm = new PackageManager[1]; 50 | Class classComtextImpl = findClass("android.app.ContextImpl", lpparam.classLoader); 51 | final boolean[] findFlag = new boolean[]{false}; 52 | findAndHookMethod(classComtextImpl, "getPackageManager", new XC_MethodHook() { 53 | @Override 54 | protected void afterHookedMethod(MethodHookParam param) { 55 | StackTraceElement[] stackTrace = new Throwable().getStackTrace(); 56 | // TODO Need a better filter condition or determination logic to get a PackageManager Object with privilege permission 57 | // instead of (stackTrace.length<=20;i<=5) 58 | if (!findFlag[0] && stackTrace.length <= 20) { 59 | log("enumerate method"); 60 | for (int i = 0; i < stackTrace.length && i <= 5; i++) { 61 | StackTraceElement ste = stackTrace[i]; 62 | if (ste.getMethodName().equals("startBootstrapServices")) { 63 | log("setting mPm"); 64 | mPm[0] = (PackageManager) param.getResult(); 65 | findFlag[0] = true; 66 | break; 67 | } 68 | } 69 | log(Arrays.toString(stackTrace)); 70 | } 71 | } 72 | }); 73 | 74 | Method getInstalledPackages = findMethodExact(PackageManager.class, "getInstalledPackages", int.class); 75 | 76 | findAndHookConstructor(classSystemImpl, new XC_MethodHook() { 77 | @Override 78 | protected void beforeHookedMethod(MethodHookParam param) { 79 | try { 80 | log("installedPackageInfoList"); 81 | List installedPackageInfoList = (ArrayList) getInstalledPackages.invoke(mPm[0], PackageManager.MATCH_ALL | PackageManager.GET_META_DATA | PackageManager.GET_SIGNATURES); 82 | if (installedPackageInfoList == null) { 83 | log("installedPackageInfoList is null"); 84 | } else { 85 | log("installedPackageInfoList length:" + installedPackageInfoList.toArray().length); 86 | } 87 | List webviewPkgNameList = new ArrayList<>(); 88 | List webviewLabelList = new ArrayList<>(); 89 | List webviewSignaturesList = new ArrayList<>(); 90 | for (PackageInfo packageInfo : installedPackageInfoList) { 91 | Bundle metaData = packageInfo.applicationInfo.metaData; 92 | if (metaData == null) { 93 | continue; 94 | } 95 | String awLib = metaData.getString("com.android.webview.WebViewLibrary"); 96 | if (awLib != null && awLib.length() > 0) { 97 | webviewPkgNameList.add(packageInfo.packageName); 98 | webviewLabelList.add(mPm[0].getApplicationLabel(packageInfo.applicationInfo).toString()); 99 | int signsLen = packageInfo.signatures.length; 100 | log("signsLen:" + signsLen); 101 | String[] signatures = new String[signsLen]; 102 | for (int i = 0; i < signsLen; i++) { 103 | byte[] rawCert = packageInfo.signatures[i].toByteArray(); 104 | InputStream certStream = new ByteArrayInputStream(rawCert); 105 | CertificateFactory certFactory = CertificateFactory.getInstance("X509"); 106 | X509Certificate x509Cert = (X509Certificate) certFactory.generateCertificate(certStream); 107 | signatures[i] = Base64.encodeToString(x509Cert.getEncoded(), Base64.DEFAULT); 108 | 109 | } 110 | webviewSignaturesList.add(signatures); 111 | } 112 | } 113 | log("webviewPkgNameList:" + webviewPkgNameList.toString()); 114 | log("webviewLabelList:" + webviewLabelList.toString()); 115 | log("webviewSignaturesList:"); 116 | for (String[] sarr : webviewSignaturesList) { 117 | log("sarr len:" + sarr.length); 118 | for (String s : sarr) { 119 | log("sign:" + s); 120 | } 121 | } 122 | 123 | int size = webviewPkgNameList.size(); 124 | Object webViewProviders = Array.newInstance(classWebViewProviderInfo, size); 125 | for (int i = 0; i < size; i++) { 126 | Array.set(webViewProviders, i, constructorWebViewProviderInfo.newInstance(webviewPkgNameList.get(i), webviewLabelList.get(i), true, false, webviewSignaturesList.get(i))); 127 | } 128 | 129 | Field mWebViewProviderPackages = findFirstFieldByExactType(classSystemImpl, classWebViewProviderInfoArray); 130 | mWebViewProviderPackages.setAccessible(true); 131 | mWebViewProviderPackages.set(param.thisObject, webViewProviders); 132 | param.setResult(param.thisObject); 133 | } catch (Throwable t) { 134 | log(t); 135 | } 136 | } 137 | }); 138 | // Disable signature validity check 139 | /*if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { 140 | findAndHookMethod(classWebViewUpdater, "validityResult", classWebViewProviderInfo, PackageInfo.class, new XC_MethodHook() { 141 | @Override 142 | protected void beforeHookedMethod(MethodHookParam param) { 143 | param.setResult(*//*VALIDITY_OK*//*0); 144 | } 145 | }); 146 | } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { 147 | findAndHookMethod(classWebViewUpdater, "isValidProvider", classWebViewProviderInfo, PackageInfo.class, new XC_MethodHook() { 148 | @Override 149 | protected void beforeHookedMethod(MethodHookParam param) { 150 | param.setResult(true); 151 | } 152 | }); 153 | }*/ 154 | } 155 | } 156 | 157 | @Override 158 | public void initZygote(StartupParam startupParam) { 159 | } 160 | 161 | private static class Initialize { 162 | private XC_LoadPackage.LoadPackageParam lpparam; 163 | private Class classWebViewProviderInfo; 164 | private Class classWebViewProviderInfoArray; 165 | private Class classSystemImpl; 166 | private Class classWebViewUpdater; 167 | private Constructor constructorWebViewProviderInfo; 168 | 169 | public Initialize(XC_LoadPackage.LoadPackageParam lpparam) { 170 | this.lpparam = lpparam; 171 | } 172 | 173 | public Class getClassWebViewProviderInfo() { 174 | return classWebViewProviderInfo; 175 | } 176 | 177 | public Class getClassWebViewProviderInfoArray() { 178 | return classWebViewProviderInfoArray; 179 | } 180 | 181 | public Class getClassSystemImpl() { 182 | return classSystemImpl; 183 | } 184 | 185 | public Class getClassWebViewUpdater() { 186 | return classWebViewUpdater; 187 | } 188 | 189 | public Constructor getConstructorWebViewProviderInfo() { 190 | return constructorWebViewProviderInfo; 191 | } 192 | 193 | 194 | public Initialize invoke() { 195 | log("invoke init"); 196 | classWebViewProviderInfo = findClass( 197 | "android.webkit.WebViewProviderInfo", lpparam.classLoader); 198 | constructorWebViewProviderInfo = findConstructorBestMatch( 199 | classWebViewProviderInfo,/*packageName*/String.class, 200 | /*description*/String.class,/*TAG_AVAILABILITY*/boolean.class, 201 | /*TAG_FALLBACK*/boolean.class,/*readSignatures*/String[].class); 202 | 203 | classWebViewProviderInfoArray = findClass( 204 | "[Landroid.webkit.WebViewProviderInfo;", lpparam.classLoader); 205 | classSystemImpl = findClass("com.android.server.webkit.SystemImpl", lpparam.classLoader); 206 | 207 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { 208 | classWebViewUpdater = findClass("com.android.server.webkit.WebViewUpdateServiceImpl", lpparam.classLoader); 209 | } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { 210 | classWebViewUpdater = findClass("com.android.server.webkit.WebViewUpdater", lpparam.classLoader); 211 | } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { 212 | classWebViewUpdater = findClass("com.android.server.webkit.WebViewUpdateServiceImpl$WebViewUpdater", lpparam.classLoader); 213 | } 214 | 215 | return this; 216 | } 217 | } 218 | } 219 | -------------------------------------------------------------------------------- /app/src/main/java/com/thinkdifferent/anywebview/BaseXposedHookLoadPackage.java: -------------------------------------------------------------------------------- 1 | package com.thinkdifferent.anywebview; 2 | 3 | import de.robv.android.xposed.IXposedHookLoadPackage; 4 | import de.robv.android.xposed.IXposedHookZygoteInit; 5 | 6 | import static com.thinkdifferent.anywebview.XposedHelpersWraper.setTAG; 7 | 8 | 9 | public abstract class BaseXposedHookLoadPackage implements IXposedHookLoadPackage, IXposedHookZygoteInit { 10 | protected Boolean iib; 11 | static { 12 | setTAG("anywebview"); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /app/src/main/java/com/thinkdifferent/anywebview/XposedHelpersWraper.java: -------------------------------------------------------------------------------- 1 | package com.thinkdifferent.anywebview; 2 | 3 | import android.content.res.Resources; 4 | import android.util.Log; 5 | 6 | import java.lang.reflect.Constructor; 7 | import java.lang.reflect.Field; 8 | import java.lang.reflect.Member; 9 | import java.lang.reflect.Method; 10 | import java.util.Set; 11 | 12 | import de.robv.android.xposed.XC_MethodHook; 13 | import de.robv.android.xposed.XposedBridge; 14 | import de.robv.android.xposed.XposedHelpers; 15 | 16 | public class XposedHelpersWraper { 17 | //TODO global prevent force close 18 | 19 | private static String TAG = "Xposed"; 20 | 21 | public static String getTAG() { 22 | return TAG; 23 | } 24 | 25 | public static void setTAG(String TAG) { 26 | XposedHelpersWraper.TAG = TAG; 27 | } 28 | 29 | public static Set hookAllConstructors(Class hookClass, XC_MethodHook callback) { 30 | try { 31 | return XposedBridge.hookAllConstructors(hookClass, callback); 32 | } catch (Throwable t) { 33 | log(t); 34 | } 35 | return null; 36 | } 37 | 38 | public static Set hookAllMethods(Class hookClass, String methodName, XC_MethodHook callback) { 39 | try { 40 | return XposedBridge.hookAllMethods(hookClass, methodName, callback); 41 | } catch (Throwable t) { 42 | log(t); 43 | } 44 | return null; 45 | } 46 | 47 | public static XC_MethodHook.Unhook hookMethod(Member hookMethod, XC_MethodHook callback) { 48 | try { 49 | return XposedBridge.hookMethod(hookMethod, callback); 50 | } catch (Throwable t) { 51 | log(t); 52 | } 53 | return null; 54 | } 55 | 56 | public static Object invokeOriginalMethod(Member method, Object thisObject, Object[] args) { 57 | try { 58 | return XposedBridge.invokeOriginalMethod(method, thisObject, args); 59 | } catch (Throwable t) { 60 | log(t); 61 | } 62 | return null; 63 | } 64 | 65 | public synchronized static void log(String text) { 66 | Log.d(TAG, text); 67 | } 68 | 69 | public synchronized static void log(String TAG, String text) { 70 | Log.d(TAG, text); 71 | } 72 | 73 | public synchronized static void log(Throwable t) { 74 | Log.e(TAG, Log.getStackTraceString(t)); 75 | } 76 | 77 | public synchronized static void log(String TAG, Throwable t) { 78 | Log.e(TAG, Log.getStackTraceString(t)); 79 | } 80 | 81 | public static Class findClass(String className, ClassLoader classLoader) { 82 | try { 83 | return XposedHelpers.findClass(className, classLoader); 84 | } catch (Throwable t) { 85 | log(t); 86 | } 87 | return null; 88 | } 89 | 90 | public static Field findField(Class clazz, String fieldName) { 91 | try { 92 | return XposedHelpers.findField(clazz, fieldName); 93 | } catch (Throwable t) { 94 | log(t); 95 | } 96 | return null; 97 | } 98 | 99 | public static Field findFirstFieldByExactType(Class clazz, Class type) { 100 | try { 101 | return XposedHelpers.findFirstFieldByExactType(clazz, type); 102 | } catch (Throwable t) { 103 | log(t); 104 | } 105 | return null; 106 | } 107 | 108 | public static XC_MethodHook.Unhook findAndHookMethod(Class clazz, String methodName, Object... parameterTypesAndCallback) { 109 | try { 110 | return XposedHelpers.findAndHookMethod(clazz, methodName, parameterTypesAndCallback); 111 | } catch (Throwable t) { 112 | log(t); 113 | } 114 | return null; 115 | } 116 | 117 | public static XC_MethodHook.Unhook findAndHookMethod(String className, ClassLoader classLoader, String methodName, Object... parameterTypesAndCallback) { 118 | try { 119 | return XposedHelpers.findAndHookMethod(className, classLoader, methodName, parameterTypesAndCallback); 120 | } catch (Throwable t) { 121 | log(t); 122 | } 123 | return null; 124 | } 125 | 126 | public static Method findMethodExact(Class clazz, String methodName, Object... parameterTypes) { 127 | try { 128 | return XposedHelpers.findMethodExact(clazz, methodName, parameterTypes); 129 | } catch (Throwable t) { 130 | log(t); 131 | } 132 | return null; 133 | } 134 | 135 | public static Method findMethodExact(String className, ClassLoader classLoader, String methodName, Object... parameterTypes) { 136 | try { 137 | return XposedHelpers.findMethodExact(className, classLoader, methodName, parameterTypes); 138 | } catch (Throwable t) { 139 | log(t); 140 | } 141 | return null; 142 | } 143 | 144 | public static Method findMethodExact(Class clazz, String methodName, Class... parameterTypes) { 145 | try { 146 | return XposedHelpers.findMethodExact(clazz, methodName, parameterTypes); 147 | } catch (Throwable t) { 148 | log(t); 149 | } 150 | return null; 151 | } 152 | 153 | public static Method[] findMethodsByExactParameters(Class clazz, Class returnType, Class... parameterTypes) { 154 | try { 155 | return XposedHelpers.findMethodsByExactParameters(clazz, returnType, parameterTypes); 156 | } catch (Throwable t) { 157 | log(t); 158 | } 159 | return null; 160 | } 161 | 162 | public static Method findMethodBestMatch(Class clazz, String methodName, Class... parameterTypes) { 163 | try { 164 | return XposedHelpers.findMethodBestMatch(clazz, methodName, parameterTypes); 165 | } catch (Throwable t) { 166 | log(t); 167 | } 168 | return null; 169 | } 170 | 171 | public static Method findMethodBestMatch(Class clazz, String methodName, Object... args) { 172 | try { 173 | return XposedHelpers.findMethodBestMatch(clazz, methodName, args); 174 | } catch (Throwable t) { 175 | log(t); 176 | } 177 | return null; 178 | } 179 | 180 | public static Method findMethodBestMatch(Class clazz, String methodName, Class[] parameterTypes, Object[] args) { 181 | try { 182 | return XposedHelpers.findMethodBestMatch(clazz, methodName, parameterTypes, args); 183 | } catch (Throwable t) { 184 | log(t); 185 | } 186 | return null; 187 | } 188 | 189 | public static Class[] getParameterTypes(Object... args) { 190 | try { 191 | return XposedHelpers.getParameterTypes(args); 192 | } catch (Throwable t) { 193 | log(t); 194 | } 195 | return null; 196 | } 197 | 198 | public static Class[] getClassesAsArray(Class... clazzes) { 199 | try { 200 | return XposedHelpers.getClassesAsArray(clazzes); 201 | } catch (Throwable t) { 202 | log(t); 203 | } 204 | return null; 205 | } 206 | 207 | public static Constructor findConstructorExact(Class clazz, Object... parameterTypes) { 208 | try { 209 | return XposedHelpers.findConstructorExact(clazz, parameterTypes); 210 | } catch (Throwable t) { 211 | log(t); 212 | } 213 | return null; 214 | } 215 | 216 | public static Constructor findConstructorExact(String className, ClassLoader classLoader, Object... parameterTypes) { 217 | try { 218 | return XposedHelpers.findConstructorExact(className, classLoader, parameterTypes); 219 | } catch (Throwable t) { 220 | log(t); 221 | } 222 | return null; 223 | } 224 | 225 | public static Constructor findConstructorExact(Class clazz, Class... parameterTypes) { 226 | try { 227 | return XposedHelpers.findConstructorExact(clazz, parameterTypes); 228 | } catch (Throwable t) { 229 | log(t); 230 | } 231 | return null; 232 | } 233 | 234 | public static XC_MethodHook.Unhook findAndHookConstructor(Class clazz, Object... parameterTypesAndCallback) { 235 | try { 236 | return XposedHelpers.findAndHookConstructor(clazz, parameterTypesAndCallback); 237 | } catch (Throwable t) { 238 | log(t); 239 | } 240 | return null; 241 | } 242 | 243 | public static XC_MethodHook.Unhook findAndHookConstructor(String className, ClassLoader classLoader, Object... parameterTypesAndCallback) { 244 | try { 245 | return XposedHelpers.findAndHookConstructor(className, classLoader, parameterTypesAndCallback); 246 | } catch (Throwable t) { 247 | log(t); 248 | } 249 | return null; 250 | } 251 | 252 | public static Constructor findConstructorBestMatch(Class clazz, Class... parameterTypes) { 253 | try { 254 | return XposedHelpers.findConstructorBestMatch(clazz, parameterTypes); 255 | } catch (Throwable t) { 256 | log(t); 257 | } 258 | return null; 259 | } 260 | 261 | public static Constructor findConstructorBestMatch(Class clazz, Object... args) { 262 | try { 263 | return XposedHelpers.findConstructorBestMatch(clazz, args); 264 | } catch (Throwable t) { 265 | log(t); 266 | } 267 | return null; 268 | } 269 | 270 | public static Constructor findConstructorBestMatch(Class clazz, Class[] parameterTypes, Object[] args) { 271 | try { 272 | return XposedHelpers.findConstructorBestMatch(clazz, parameterTypes, args); 273 | } catch (Throwable t) { 274 | log(t); 275 | } 276 | return null; 277 | } 278 | 279 | public static void setObjectField(Object obj, String fieldName, Object value) { 280 | try { 281 | XposedHelpers.setObjectField(obj, fieldName, value); 282 | } catch (Throwable t) { 283 | log(t); 284 | } 285 | } 286 | 287 | public static void setBooleanField(Object obj, String fieldName, boolean value) { 288 | try { 289 | XposedHelpers.setBooleanField(obj, fieldName, value); 290 | } catch (Throwable t) { 291 | log(t); 292 | } 293 | } 294 | 295 | public static void setByteField(Object obj, String fieldName, byte value) { 296 | try { 297 | XposedHelpers.setByteField(obj, fieldName, value); 298 | } catch (Throwable t) { 299 | log(t); 300 | } 301 | } 302 | 303 | public static void setCharField(Object obj, String fieldName, char value) { 304 | try { 305 | XposedHelpers.setCharField(obj, fieldName, value); 306 | } catch (Throwable t) { 307 | log(t); 308 | } 309 | } 310 | 311 | public static void setDoubleField(Object obj, String fieldName, double value) { 312 | try { 313 | XposedHelpers.setDoubleField(obj, fieldName, value); 314 | } catch (Throwable t) { 315 | log(t); 316 | } 317 | } 318 | 319 | public static void setFloatField(Object obj, String fieldName, float value) { 320 | try { 321 | XposedHelpers.setFloatField(obj, fieldName, value); 322 | } catch (Throwable t) { 323 | log(t); 324 | } 325 | } 326 | 327 | public static void setIntField(Object obj, String fieldName, int value) { 328 | try { 329 | XposedHelpers.setIntField(obj, fieldName, value); 330 | } catch (Throwable t) { 331 | log(t); 332 | } 333 | } 334 | 335 | public static void setLongField(Object obj, String fieldName, long value) { 336 | try { 337 | XposedHelpers.setLongField(obj, fieldName, value); 338 | } catch (Throwable t) { 339 | log(t); 340 | } 341 | } 342 | 343 | public static void setShortField(Object obj, String fieldName, short value) { 344 | try { 345 | XposedHelpers.setShortField(obj, fieldName, value); 346 | } catch (Throwable t) { 347 | log(t); 348 | } 349 | } 350 | 351 | public static Object getObjectField(Object obj, String fieldName) { 352 | try { 353 | return XposedHelpers.getObjectField(obj, fieldName); 354 | } catch (Throwable t) { 355 | log(t); 356 | } 357 | return null; 358 | } 359 | 360 | public static Object getSurroundingThis(Object obj) { 361 | try { 362 | return XposedHelpers.getSurroundingThis(obj); 363 | } catch (Throwable t) { 364 | log(t); 365 | } 366 | return null; 367 | } 368 | 369 | public static boolean getBooleanField(Object obj, String fieldName) { 370 | try { 371 | return XposedHelpers.getBooleanField(obj, fieldName); 372 | } catch (Throwable t) { 373 | log(t); 374 | } 375 | return false; 376 | } 377 | 378 | public static byte getByteField(Object obj, String fieldName) { 379 | try { 380 | return XposedHelpers.getByteField(obj, fieldName); 381 | } catch (Throwable t) { 382 | log(t); 383 | } 384 | return 0; 385 | } 386 | 387 | public static char getCharField(Object obj, String fieldName) { 388 | try { 389 | return XposedHelpers.getCharField(obj, fieldName); 390 | } catch (Throwable t) { 391 | log(t); 392 | } 393 | return 0; 394 | } 395 | 396 | public static double getDoubleField(Object obj, String fieldName) { 397 | try { 398 | return XposedHelpers.getDoubleField(obj, fieldName); 399 | } catch (Throwable t) { 400 | log(t); 401 | } 402 | return 0; 403 | } 404 | 405 | public static float getFloatField(Object obj, String fieldName) { 406 | try { 407 | return XposedHelpers.getFloatField(obj, fieldName); 408 | } catch (Throwable t) { 409 | log(t); 410 | } 411 | return 0; 412 | } 413 | 414 | public static int getIntField(Object obj, String fieldName) { 415 | try { 416 | return XposedHelpers.getIntField(obj, fieldName); 417 | } catch (Throwable t) { 418 | log(t); 419 | } 420 | return 0; 421 | } 422 | 423 | public static long getLongField(Object obj, String fieldName) { 424 | try { 425 | return XposedHelpers.getLongField(obj, fieldName); 426 | } catch (Throwable t) { 427 | log(t); 428 | } 429 | return 0; 430 | } 431 | 432 | public static short getShortField(Object obj, String fieldName) { 433 | try { 434 | return XposedHelpers.getShortField(obj, fieldName); 435 | } catch (Throwable t) { 436 | log(t); 437 | } 438 | return 0; 439 | } 440 | 441 | public static void setStaticObjectField(Class clazz, String fieldName, Object value) { 442 | try { 443 | XposedHelpers.setStaticObjectField(clazz, fieldName, value); 444 | } catch (Throwable t) { 445 | log(t); 446 | } 447 | } 448 | 449 | public static void setStaticBooleanField(Class clazz, String fieldName, boolean value) { 450 | try { 451 | XposedHelpers.setStaticBooleanField(clazz, fieldName, value); 452 | } catch (Throwable t) { 453 | log(t); 454 | } 455 | } 456 | 457 | public static void setStaticByteField(Class clazz, String fieldName, byte value) { 458 | try { 459 | XposedHelpers.setStaticByteField(clazz, fieldName, value); 460 | } catch (Throwable t) { 461 | log(t); 462 | } 463 | } 464 | 465 | public static void setStaticCharField(Class clazz, String fieldName, char value) { 466 | try { 467 | XposedHelpers.setStaticCharField(clazz, fieldName, value); 468 | } catch (Throwable t) { 469 | log(t); 470 | } 471 | } 472 | 473 | public static void setStaticDoubleField(Class clazz, String fieldName, double value) { 474 | try { 475 | XposedHelpers.setStaticDoubleField(clazz, fieldName, value); 476 | } catch (Throwable t) { 477 | log(t); 478 | } 479 | } 480 | 481 | public static void setStaticFloatField(Class clazz, String fieldName, float value) { 482 | try { 483 | XposedHelpers.setStaticFloatField(clazz, fieldName, value); 484 | } catch (Throwable t) { 485 | log(t); 486 | } 487 | } 488 | 489 | public static void setStaticIntField(Class clazz, String fieldName, int value) { 490 | try { 491 | XposedHelpers.setStaticIntField(clazz, fieldName, value); 492 | } catch (Throwable t) { 493 | log(t); 494 | } 495 | } 496 | 497 | public static void setStaticLongField(Class clazz, String fieldName, long value) { 498 | try { 499 | XposedHelpers.setStaticLongField(clazz, fieldName, value); 500 | } catch (Throwable t) { 501 | log(t); 502 | } 503 | } 504 | 505 | public static void setStaticShortField(Class clazz, String fieldName, short value) { 506 | try { 507 | XposedHelpers.setStaticShortField(clazz, fieldName, value); 508 | } catch (Throwable t) { 509 | log(t); 510 | } 511 | } 512 | 513 | public static Object getStaticObjectField(Class clazz, String fieldName) { 514 | try { 515 | return XposedHelpers.getStaticObjectField(clazz, fieldName); 516 | } catch (Throwable t) { 517 | log(t); 518 | } 519 | return null; 520 | } 521 | 522 | public static boolean getStaticBooleanField(Class clazz, String fieldName) { 523 | try { 524 | return XposedHelpers.getStaticBooleanField(clazz, fieldName); 525 | } catch (Throwable t) { 526 | log(t); 527 | } 528 | return false; 529 | } 530 | 531 | public static byte getStaticByteField(Class clazz, String fieldName) { 532 | try { 533 | return XposedHelpers.getStaticByteField(clazz, fieldName); 534 | } catch (Throwable t) { 535 | log(t); 536 | } 537 | return 0; 538 | } 539 | 540 | public static char getStaticCharField(Class clazz, String fieldName) { 541 | try { 542 | return XposedHelpers.getStaticCharField(clazz, fieldName); 543 | } catch (Throwable t) { 544 | log(t); 545 | } 546 | return 0; 547 | } 548 | 549 | public static double getStaticDoubleField(Class clazz, String fieldName) { 550 | try { 551 | return XposedHelpers.getStaticDoubleField(clazz, fieldName); 552 | } catch (Throwable t) { 553 | log(t); 554 | } 555 | return 0; 556 | } 557 | 558 | public static float getStaticFloatField(Class clazz, String fieldName) { 559 | try { 560 | return XposedHelpers.getStaticFloatField(clazz, fieldName); 561 | } catch (Throwable t) { 562 | log(t); 563 | } 564 | return 0; 565 | } 566 | 567 | public static int getStaticIntField(Class clazz, String fieldName) { 568 | try { 569 | return XposedHelpers.getStaticIntField(clazz, fieldName); 570 | } catch (Throwable t) { 571 | log(t); 572 | } 573 | return 0; 574 | } 575 | 576 | public static long getStaticLongField(Class clazz, String fieldName) { 577 | try { 578 | return XposedHelpers.getStaticLongField(clazz, fieldName); 579 | } catch (Throwable t) { 580 | log(t); 581 | } 582 | return 0; 583 | } 584 | 585 | public static short getStaticShortField(Class clazz, String fieldName) { 586 | try { 587 | return XposedHelpers.getStaticShortField(clazz, fieldName); 588 | } catch (Throwable t) { 589 | log(t); 590 | } 591 | return 0; 592 | } 593 | 594 | public static Object callMethod(Object obj, String methodName, Object... args) { 595 | try { 596 | return XposedHelpers.callMethod(obj, methodName, args); 597 | } catch (Throwable t) { 598 | log(t); 599 | } 600 | return null; 601 | } 602 | 603 | public static Object callMethod(Object obj, String methodName, Class[] parameterTypes, Object... args) { 604 | try { 605 | return XposedHelpers.callMethod(obj, methodName, parameterTypes, args); 606 | } catch (Throwable t) { 607 | log(t); 608 | } 609 | return null; 610 | } 611 | 612 | public static Object callStaticMethod(Class clazz, String methodName, Object... args) { 613 | try { 614 | return XposedHelpers.callMethod(clazz, methodName, args); 615 | } catch (Throwable t) { 616 | log(t); 617 | } 618 | return null; 619 | } 620 | 621 | public static Object callStaticMethod(Class clazz, String methodName, Class[] parameterTypes, Object... args) { 622 | try { 623 | return XposedHelpers.callStaticMethod(clazz, methodName, parameterTypes, args); 624 | } catch (Throwable t) { 625 | log(t); 626 | } 627 | return null; 628 | } 629 | 630 | public static Object newInstance(Class clazz, Object... args) { 631 | try { 632 | return XposedHelpers.newInstance(clazz, args); 633 | } catch (Throwable t) { 634 | log(t); 635 | } 636 | return null; 637 | } 638 | 639 | public static Object newInstance(Class clazz, Class[] parameterTypes, Object... args) { 640 | try { 641 | return XposedHelpers.newInstance(clazz, parameterTypes, args); 642 | } catch (Throwable t) { 643 | log(t); 644 | } 645 | return null; 646 | } 647 | 648 | public static Object setAdditionalInstanceField(Object obj, String key, Object value) { 649 | try { 650 | return XposedHelpers.setAdditionalInstanceField(obj, key, value); 651 | } catch (Throwable t) { 652 | log(t); 653 | } 654 | return null; 655 | } 656 | 657 | public static Object getAdditionalInstanceField(Object obj, String key) { 658 | try { 659 | return XposedHelpers.getAdditionalInstanceField(obj, key); 660 | } catch (Throwable t) { 661 | log(t); 662 | } 663 | return null; 664 | } 665 | 666 | public static Object removeAdditionalInstanceField(Object obj, String key) { 667 | try { 668 | return XposedHelpers.removeAdditionalInstanceField(obj, key); 669 | } catch (Throwable t) { 670 | log(t); 671 | } 672 | return null; 673 | } 674 | 675 | public static Object setAdditionalStaticField(Object obj, String key, Object value) { 676 | try { 677 | return XposedHelpers.setAdditionalStaticField(obj, key, value); 678 | } catch (Throwable t) { 679 | log(t); 680 | } 681 | return null; 682 | } 683 | 684 | public static Object getAdditionalStaticField(Object obj, String key) { 685 | try { 686 | return XposedHelpers.getAdditionalStaticField(obj, key); 687 | } catch (Throwable t) { 688 | log(t); 689 | } 690 | return null; 691 | } 692 | 693 | public static Object removeAdditionalStaticField(Object obj, String key) { 694 | try { 695 | return XposedHelpers.removeAdditionalStaticField(obj, key); 696 | } catch (Throwable t) { 697 | log(t); 698 | } 699 | return null; 700 | } 701 | 702 | public static Object setAdditionalStaticField(Class clazz, String key, Object value) { 703 | try { 704 | return XposedHelpers.setAdditionalStaticField(clazz, key, value); 705 | } catch (Throwable t) { 706 | log(t); 707 | } 708 | return null; 709 | } 710 | 711 | public static Object getAdditionalStaticField(Class clazz, String key) { 712 | try { 713 | return XposedHelpers.getAdditionalStaticField(clazz, key); 714 | } catch (Throwable t) { 715 | log(t); 716 | } 717 | return null; 718 | } 719 | 720 | public static Object removeAdditionalStaticField(Class clazz, String key) { 721 | try { 722 | return XposedHelpers.removeAdditionalStaticField(clazz, key); 723 | } catch (Throwable t) { 724 | log(t); 725 | } 726 | return null; 727 | } 728 | 729 | public static byte[] assetAsByteArray(Resources res, String path) { 730 | try { 731 | return XposedHelpers.assetAsByteArray(res, path); 732 | } catch (Throwable t) { 733 | log(t); 734 | } 735 | return null; 736 | } 737 | 738 | public static String getMD5Sum(String file) { 739 | try { 740 | return XposedHelpers.getMD5Sum(file); 741 | } catch (Throwable t) { 742 | log(t); 743 | } 744 | return null; 745 | } 746 | 747 | } 748 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | AnyWebView 3 | Any WebView is OK! 4 | 5 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | 5 | repositories { 6 | google() 7 | mavenCentral() 8 | } 9 | dependencies { 10 | classpath 'com.android.tools.build:gradle:8.7.3' 11 | 12 | 13 | // NOTE: Do not place your application dependencies here; they belong 14 | // in the individual module build.gradle files 15 | } 16 | } 17 | 18 | allprojects { 19 | repositories { 20 | google() 21 | mavenCentral() 22 | } 23 | } 24 | 25 | tasks.register('clean', Delete) { 26 | delete rootProject.layout.buildDirectory 27 | } 28 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | org.gradle.jvmargs=-Xmx1536m 10 | # When configured, Gradle will run in incubating parallel mode. 11 | # This option should only be used with decoupled projects. More details, visit 12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 13 | # org.gradle.parallel=true 14 | # AndroidX package structure to make it clearer which packages are bundled with the 15 | # Android operating system, and which are packaged with your app's APK 16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn 17 | android.useAndroidX=true 18 | # Automatically convert third-party libraries to use AndroidX 19 | android.enableJetifier=true 20 | android.defaults.buildfeatures.buildconfig=true 21 | android.nonTransitiveRClass=false 22 | android.nonFinalResIds=false 23 | 24 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neoblackxt/AnyWebView/89b62f6eed4bc64ce7a95cca996ecf3af4cdc6ad/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Mon Dec 16 00:46:35 CST 2024 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip 5 | zipStoreBase=GRADLE_USER_HOME 6 | zipStorePath=wrapper/dists 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn () { 37 | echo "$*" 38 | } 39 | 40 | die () { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save () { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | --------------------------------------------------------------------------------