├── .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 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/.idea/migrations.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 | Android API 19 Platform
19 |
20 |
21 |
22 |
23 |
24 |
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 |
--------------------------------------------------------------------------------