@capacitor/android 8.0.0-alpha.2 → 8.0.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/capacitor/build.gradle +18 -20
- package/capacitor/src/main/assets/native-bridge.js +1 -1
- package/capacitor/src/main/java/com/getcapacitor/Bridge.java +14 -14
- package/capacitor/src/main/java/com/getcapacitor/BridgeWebChromeClient.java +1 -6
- package/capacitor/src/main/java/com/getcapacitor/CapConfig.java +0 -8
- package/capacitor/src/main/java/com/getcapacitor/CapacitorWebView.java +0 -32
- package/capacitor/src/main/java/com/getcapacitor/JSExport.java +24 -24
- package/capacitor/src/main/java/com/getcapacitor/MessageHandler.java +7 -7
- package/capacitor/src/main/java/com/getcapacitor/plugin/CapacitorCookies.java +17 -17
- package/capacitor/src/main/java/com/getcapacitor/plugin/SystemBars.java +220 -0
- package/package.json +2 -2
- package/capacitor/src/main/res/layout/bridge_layout_main.xml +0 -15
package/capacitor/build.gradle
CHANGED
|
@@ -1,28 +1,27 @@
|
|
|
1
1
|
ext {
|
|
2
|
-
androidxActivityVersion = project.hasProperty('androidxActivityVersion') ? rootProject.ext.androidxActivityVersion : '1.
|
|
3
|
-
androidxAppCompatVersion = project.hasProperty('androidxAppCompatVersion') ? rootProject.ext.androidxAppCompatVersion : '1.7.
|
|
4
|
-
androidxCoordinatorLayoutVersion = project.hasProperty('androidxCoordinatorLayoutVersion') ? rootProject.ext.androidxCoordinatorLayoutVersion : '1.
|
|
5
|
-
androidxCoreVersion = project.hasProperty('androidxCoreVersion') ? rootProject.ext.androidxCoreVersion : '1.
|
|
6
|
-
androidxFragmentVersion = project.hasProperty('androidxFragmentVersion') ? rootProject.ext.androidxFragmentVersion : '1.8.
|
|
7
|
-
androidxWebkitVersion = project.hasProperty('androidxWebkitVersion') ? rootProject.ext.androidxWebkitVersion : '1.
|
|
2
|
+
androidxActivityVersion = project.hasProperty('androidxActivityVersion') ? rootProject.ext.androidxActivityVersion : '1.11.0'
|
|
3
|
+
androidxAppCompatVersion = project.hasProperty('androidxAppCompatVersion') ? rootProject.ext.androidxAppCompatVersion : '1.7.1'
|
|
4
|
+
androidxCoordinatorLayoutVersion = project.hasProperty('androidxCoordinatorLayoutVersion') ? rootProject.ext.androidxCoordinatorLayoutVersion : '1.3.0'
|
|
5
|
+
androidxCoreVersion = project.hasProperty('androidxCoreVersion') ? rootProject.ext.androidxCoreVersion : '1.17.0'
|
|
6
|
+
androidxFragmentVersion = project.hasProperty('androidxFragmentVersion') ? rootProject.ext.androidxFragmentVersion : '1.8.9'
|
|
7
|
+
androidxWebkitVersion = project.hasProperty('androidxWebkitVersion') ? rootProject.ext.androidxWebkitVersion : '1.14.0'
|
|
8
8
|
junitVersion = project.hasProperty('junitVersion') ? rootProject.ext.junitVersion : '4.13.2'
|
|
9
|
-
androidxJunitVersion = project.hasProperty('androidxJunitVersion') ? rootProject.ext.androidxJunitVersion : '1.
|
|
10
|
-
androidxEspressoCoreVersion = project.hasProperty('androidxEspressoCoreVersion') ? rootProject.ext.androidxEspressoCoreVersion : '3.
|
|
11
|
-
cordovaAndroidVersion = project.hasProperty('cordovaAndroidVersion') ? rootProject.ext.cordovaAndroidVersion : '
|
|
9
|
+
androidxJunitVersion = project.hasProperty('androidxJunitVersion') ? rootProject.ext.androidxJunitVersion : '1.3.0'
|
|
10
|
+
androidxEspressoCoreVersion = project.hasProperty('androidxEspressoCoreVersion') ? rootProject.ext.androidxEspressoCoreVersion : '3.7.0'
|
|
11
|
+
cordovaAndroidVersion = project.hasProperty('cordovaAndroidVersion') ? rootProject.ext.cordovaAndroidVersion : '14.0.1'
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
|
|
15
15
|
buildscript {
|
|
16
|
-
ext.kotlin_version = project.hasProperty("kotlin_version") ? rootProject.ext.kotlin_version : '1.9.25'
|
|
17
16
|
repositories {
|
|
18
17
|
google()
|
|
19
18
|
mavenCentral()
|
|
20
19
|
maven {
|
|
21
|
-
url "https://plugins.gradle.org/m2/"
|
|
20
|
+
url = "https://plugins.gradle.org/m2/"
|
|
22
21
|
}
|
|
23
22
|
}
|
|
24
23
|
dependencies {
|
|
25
|
-
classpath 'com.android.tools.build:gradle:8.
|
|
24
|
+
classpath 'com.android.tools.build:gradle:8.13.0'
|
|
26
25
|
|
|
27
26
|
if (System.getenv("CAP_PUBLISH") == "true") {
|
|
28
27
|
classpath 'io.github.gradle-nexus:publish-plugin:1.3.0'
|
|
@@ -41,8 +40,8 @@ if (System.getenv("CAP_PUBLISH") == "true") {
|
|
|
41
40
|
}
|
|
42
41
|
|
|
43
42
|
android {
|
|
44
|
-
namespace "com.getcapacitor.android"
|
|
45
|
-
compileSdk project.hasProperty('compileSdkVersion') ? rootProject.ext.compileSdkVersion : 36
|
|
43
|
+
namespace = "com.getcapacitor.android"
|
|
44
|
+
compileSdk = project.hasProperty('compileSdkVersion') ? rootProject.ext.compileSdkVersion : 36
|
|
46
45
|
defaultConfig {
|
|
47
46
|
minSdkVersion project.hasProperty('minSdkVersion') ? rootProject.ext.minSdkVersion : 24
|
|
48
47
|
targetSdkVersion project.hasProperty('targetSdkVersion') ? rootProject.ext.targetSdkVersion : 36
|
|
@@ -59,9 +58,9 @@ android {
|
|
|
59
58
|
}
|
|
60
59
|
lintOptions {
|
|
61
60
|
baseline file("lint-baseline.xml")
|
|
62
|
-
abortOnError true
|
|
63
|
-
warningsAsErrors true
|
|
64
|
-
lintConfig file('lint.xml')
|
|
61
|
+
abortOnError = true
|
|
62
|
+
warningsAsErrors = true
|
|
63
|
+
lintConfig = file('lint.xml')
|
|
65
64
|
}
|
|
66
65
|
compileOptions {
|
|
67
66
|
sourceCompatibility JavaVersion.VERSION_21
|
|
@@ -79,7 +78,6 @@ repositories {
|
|
|
79
78
|
|
|
80
79
|
dependencies {
|
|
81
80
|
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
|
82
|
-
implementation platform("org.jetbrains.kotlin:kotlin-bom:$kotlin_version")
|
|
83
81
|
implementation "androidx.appcompat:appcompat:$androidxAppCompatVersion"
|
|
84
82
|
implementation "androidx.core:core:$androidxCoreVersion"
|
|
85
83
|
implementation "androidx.activity:activity:$androidxActivityVersion"
|
|
@@ -90,7 +88,7 @@ dependencies {
|
|
|
90
88
|
androidTestImplementation "androidx.test.ext:junit:$androidxJunitVersion"
|
|
91
89
|
androidTestImplementation "androidx.test.espresso:espresso-core:$androidxEspressoCoreVersion"
|
|
92
90
|
implementation "org.apache.cordova:framework:$cordovaAndroidVersion"
|
|
93
|
-
testImplementation 'org.json:json:
|
|
94
|
-
testImplementation 'org.mockito:mockito-core:5.
|
|
91
|
+
testImplementation 'org.json:json:20250517'
|
|
92
|
+
testImplementation 'org.mockito:mockito-core:5.20.0'
|
|
95
93
|
}
|
|
96
94
|
|
|
@@ -462,9 +462,9 @@ public class Bridge {
|
|
|
462
462
|
if (ex instanceof SocketTimeoutException) {
|
|
463
463
|
Logger.error(
|
|
464
464
|
"Unable to load app. Ensure the server is running at " +
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
465
|
+
appUrl +
|
|
466
|
+
", or modify the " +
|
|
467
|
+
"appUrl setting in capacitor.config.json (make sure to npx cap copy after to commit changes).",
|
|
468
468
|
ex
|
|
469
469
|
);
|
|
470
470
|
}
|
|
@@ -655,6 +655,7 @@ public class Bridge {
|
|
|
655
655
|
this.registerPlugin(com.getcapacitor.plugin.CapacitorCookies.class);
|
|
656
656
|
this.registerPlugin(com.getcapacitor.plugin.WebView.class);
|
|
657
657
|
this.registerPlugin(com.getcapacitor.plugin.CapacitorHttp.class);
|
|
658
|
+
this.registerPlugin(com.getcapacitor.plugin.SystemBars.class);
|
|
658
659
|
|
|
659
660
|
for (Class<? extends Plugin> pluginClass : this.initialPlugins) {
|
|
660
661
|
this.registerPlugin(pluginClass);
|
|
@@ -748,9 +749,9 @@ public class Bridge {
|
|
|
748
749
|
private void logInvalidPluginException(Class<? extends Plugin> clazz) {
|
|
749
750
|
Logger.error(
|
|
750
751
|
"NativePlugin " +
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
752
|
+
clazz.getName() +
|
|
753
|
+
" is invalid. Ensure the @CapacitorPlugin annotation exists on the plugin class and" +
|
|
754
|
+
" the class extends Plugin"
|
|
754
755
|
);
|
|
755
756
|
}
|
|
756
757
|
|
|
@@ -825,13 +826,13 @@ public class Bridge {
|
|
|
825
826
|
if (Logger.shouldLog()) {
|
|
826
827
|
Logger.verbose(
|
|
827
828
|
"callback: " +
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
829
|
+
call.getCallbackId() +
|
|
830
|
+
", pluginId: " +
|
|
831
|
+
plugin.getId() +
|
|
832
|
+
", methodName: " +
|
|
833
|
+
methodName +
|
|
834
|
+
", methodData: " +
|
|
835
|
+
call.getData().toString()
|
|
835
836
|
);
|
|
836
837
|
}
|
|
837
838
|
|
|
@@ -1610,7 +1611,6 @@ public class Bridge {
|
|
|
1610
1611
|
|
|
1611
1612
|
if (webView instanceof CapacitorWebView capacitorWebView) {
|
|
1612
1613
|
capacitorWebView.setBridge(bridge);
|
|
1613
|
-
capacitorWebView.edgeToEdgeHandler(bridge);
|
|
1614
1614
|
}
|
|
1615
1615
|
|
|
1616
1616
|
bridge.setCordovaWebView(mockWebView);
|
|
@@ -447,12 +447,7 @@ public class BridgeWebChromeClient extends WebChromeClient {
|
|
|
447
447
|
}
|
|
448
448
|
|
|
449
449
|
public boolean isValidMsg(String msg) {
|
|
450
|
-
return !(
|
|
451
|
-
msg.contains("%cresult %c") ||
|
|
452
|
-
(msg.contains("%cnative %c")) ||
|
|
453
|
-
msg.equalsIgnoreCase("[object Object]") ||
|
|
454
|
-
msg.equalsIgnoreCase("console.groupEnd")
|
|
455
|
-
);
|
|
450
|
+
return !(msg.contains("%cresult %c") || (msg.contains("%cnative %c")) || msg.equalsIgnoreCase("console.groupEnd"));
|
|
456
451
|
}
|
|
457
452
|
|
|
458
453
|
private Uri createImageFileUri() throws IOException {
|
|
@@ -54,7 +54,6 @@ public class CapConfig {
|
|
|
54
54
|
private String errorPath;
|
|
55
55
|
private boolean zoomableWebView = false;
|
|
56
56
|
private boolean resolveServiceWorkerRequests = true;
|
|
57
|
-
private String adjustMarginsForEdgeToEdge = "disable";
|
|
58
57
|
|
|
59
58
|
// Embedded
|
|
60
59
|
private String startPath;
|
|
@@ -182,7 +181,6 @@ public class CapConfig {
|
|
|
182
181
|
this.errorPath = builder.errorPath;
|
|
183
182
|
this.zoomableWebView = builder.zoomableWebView;
|
|
184
183
|
this.resolveServiceWorkerRequests = builder.resolveServiceWorkerRequests;
|
|
185
|
-
this.adjustMarginsForEdgeToEdge = builder.adjustMarginsForEdgeToEdge;
|
|
186
184
|
|
|
187
185
|
// Embedded
|
|
188
186
|
this.startPath = builder.startPath;
|
|
@@ -288,7 +286,6 @@ public class CapConfig {
|
|
|
288
286
|
webContentsDebuggingEnabled = JSONUtils.getBoolean(configJSON, "android.webContentsDebuggingEnabled", isDebug);
|
|
289
287
|
zoomableWebView = JSONUtils.getBoolean(configJSON, "android.zoomEnabled", JSONUtils.getBoolean(configJSON, "zoomEnabled", false));
|
|
290
288
|
resolveServiceWorkerRequests = JSONUtils.getBoolean(configJSON, "android.resolveServiceWorkerRequests", true);
|
|
291
|
-
adjustMarginsForEdgeToEdge = JSONUtils.getString(configJSON, "android.adjustMarginsForEdgeToEdge", "disable");
|
|
292
289
|
|
|
293
290
|
String logBehavior = JSONUtils.getString(
|
|
294
291
|
configJSON,
|
|
@@ -405,10 +402,6 @@ public class CapConfig {
|
|
|
405
402
|
return useLegacyBridge;
|
|
406
403
|
}
|
|
407
404
|
|
|
408
|
-
public String adjustMarginsForEdgeToEdge() {
|
|
409
|
-
return adjustMarginsForEdgeToEdge;
|
|
410
|
-
}
|
|
411
|
-
|
|
412
405
|
public int getMinWebViewVersion() {
|
|
413
406
|
if (minWebViewVersion < MINIMUM_ANDROID_WEBVIEW_VERSION) {
|
|
414
407
|
Logger.warn("Specified minimum webview version is too low, defaulting to " + MINIMUM_ANDROID_WEBVIEW_VERSION);
|
|
@@ -589,7 +582,6 @@ public class CapConfig {
|
|
|
589
582
|
private int minHuaweiWebViewVersion = DEFAULT_HUAWEI_WEBVIEW_VERSION;
|
|
590
583
|
private boolean zoomableWebView = false;
|
|
591
584
|
private boolean resolveServiceWorkerRequests = true;
|
|
592
|
-
private String adjustMarginsForEdgeToEdge = "disable";
|
|
593
585
|
|
|
594
586
|
// Embedded
|
|
595
587
|
private String startPath = null;
|
|
@@ -54,36 +54,4 @@ public class CapacitorWebView extends WebView {
|
|
|
54
54
|
}
|
|
55
55
|
return super.dispatchKeyEvent(event);
|
|
56
56
|
}
|
|
57
|
-
|
|
58
|
-
public void edgeToEdgeHandler(Bridge bridge) {
|
|
59
|
-
String configEdgeToEdge = bridge.getConfig().adjustMarginsForEdgeToEdge();
|
|
60
|
-
|
|
61
|
-
if (configEdgeToEdge.equals("disable")) return;
|
|
62
|
-
|
|
63
|
-
boolean autoMargins = false;
|
|
64
|
-
boolean forceMargins = configEdgeToEdge.equals("force");
|
|
65
|
-
|
|
66
|
-
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM && configEdgeToEdge.equals("auto")) {
|
|
67
|
-
TypedValue value = new TypedValue();
|
|
68
|
-
boolean foundOptOut = getContext().getTheme().resolveAttribute(android.R.attr.windowOptOutEdgeToEdgeEnforcement, value, true);
|
|
69
|
-
boolean optOutValue = value.data != 0; // value is set to -1 on true as of Android 15, so we have to do this.
|
|
70
|
-
|
|
71
|
-
autoMargins = !(foundOptOut && optOutValue);
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
if (forceMargins || autoMargins) {
|
|
75
|
-
ViewCompat.setOnApplyWindowInsetsListener(this, (v, windowInsets) -> {
|
|
76
|
-
Insets insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars() | WindowInsetsCompat.Type.displayCutout());
|
|
77
|
-
MarginLayoutParams mlp = (MarginLayoutParams) v.getLayoutParams();
|
|
78
|
-
mlp.leftMargin = insets.left;
|
|
79
|
-
mlp.bottomMargin = insets.bottom;
|
|
80
|
-
mlp.rightMargin = insets.right;
|
|
81
|
-
mlp.topMargin = insets.top;
|
|
82
|
-
v.setLayoutParams(mlp);
|
|
83
|
-
|
|
84
|
-
// Don't pass window insets to children
|
|
85
|
-
return WindowInsetsCompat.CONSUMED;
|
|
86
|
-
});
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
57
|
}
|
|
@@ -64,16 +64,16 @@ public class JSExport {
|
|
|
64
64
|
for (PluginHandle plugin : plugins) {
|
|
65
65
|
lines.add(
|
|
66
66
|
"(function(w) {\n" +
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
67
|
+
"var a = (w.Capacitor = w.Capacitor || {});\n" +
|
|
68
|
+
"var p = (a.Plugins = a.Plugins || {});\n" +
|
|
69
|
+
"var t = (p['" +
|
|
70
|
+
plugin.getId() +
|
|
71
|
+
"'] = {});\n" +
|
|
72
|
+
"t.addListener = function(eventName, callback) {\n" +
|
|
73
|
+
" return w.Capacitor.addListener('" +
|
|
74
|
+
plugin.getId() +
|
|
75
|
+
"', eventName, callback);\n" +
|
|
76
|
+
"}"
|
|
77
77
|
);
|
|
78
78
|
Collection<PluginMethodHandle> methods = plugin.getMethods();
|
|
79
79
|
for (PluginMethodHandle method : methods) {
|
|
@@ -171,12 +171,12 @@ public class JSExport {
|
|
|
171
171
|
case PluginMethod.RETURN_NONE:
|
|
172
172
|
lines.add(
|
|
173
173
|
"return w.Capacitor.nativeCallback('" +
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
174
|
+
plugin.getId() +
|
|
175
|
+
"', '" +
|
|
176
|
+
method.getName() +
|
|
177
|
+
"', " +
|
|
178
|
+
CATCHALL_OPTIONS_PARAM +
|
|
179
|
+
")"
|
|
180
180
|
);
|
|
181
181
|
break;
|
|
182
182
|
case PluginMethod.RETURN_PROMISE:
|
|
@@ -187,14 +187,14 @@ public class JSExport {
|
|
|
187
187
|
case PluginMethod.RETURN_CALLBACK:
|
|
188
188
|
lines.add(
|
|
189
189
|
"return w.Capacitor.nativeCallback('" +
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
190
|
+
plugin.getId() +
|
|
191
|
+
"', '" +
|
|
192
|
+
method.getName() +
|
|
193
|
+
"', " +
|
|
194
|
+
CATCHALL_OPTIONS_PARAM +
|
|
195
|
+
", " +
|
|
196
|
+
CALLBACK_PARAM +
|
|
197
|
+
")"
|
|
198
198
|
);
|
|
199
199
|
break;
|
|
200
200
|
default:
|
|
@@ -69,13 +69,13 @@ public class MessageHandler {
|
|
|
69
69
|
Logger.verbose(
|
|
70
70
|
Logger.tags("Plugin"),
|
|
71
71
|
"To native (Cordova plugin): callbackId: " +
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
72
|
+
callbackId +
|
|
73
|
+
", service: " +
|
|
74
|
+
service +
|
|
75
|
+
", action: " +
|
|
76
|
+
action +
|
|
77
|
+
", actionArgs: " +
|
|
78
|
+
actionArgs
|
|
79
79
|
);
|
|
80
80
|
|
|
81
81
|
this.callCordovaPluginMethod(callbackId, service, action, actionArgs);
|
|
@@ -47,30 +47,30 @@ public class CapacitorCookies extends Plugin {
|
|
|
47
47
|
@PluginMethod
|
|
48
48
|
public void getCookies(PluginCall call) {
|
|
49
49
|
this.bridge.eval("document.cookie", (value) -> {
|
|
50
|
-
|
|
51
|
-
|
|
50
|
+
String cookies = value.substring(1, value.length() - 1);
|
|
51
|
+
String[] cookieArray = cookies.split(";");
|
|
52
52
|
|
|
53
|
-
|
|
53
|
+
JSObject cookieMap = new JSObject();
|
|
54
54
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
55
|
+
for (String cookie : cookieArray) {
|
|
56
|
+
if (cookie.length() > 0) {
|
|
57
|
+
String[] keyValue = cookie.split("=", 2);
|
|
58
58
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
59
|
+
if (keyValue.length == 2) {
|
|
60
|
+
String key = keyValue[0].trim();
|
|
61
|
+
String val = keyValue[1].trim();
|
|
62
|
+
try {
|
|
63
|
+
key = URLDecoder.decode(keyValue[0].trim(), StandardCharsets.UTF_8.name());
|
|
64
|
+
val = URLDecoder.decode(keyValue[1].trim(), StandardCharsets.UTF_8.name());
|
|
65
|
+
} catch (UnsupportedEncodingException ignored) {}
|
|
66
66
|
|
|
67
|
-
|
|
68
|
-
}
|
|
67
|
+
cookieMap.put(key, val);
|
|
69
68
|
}
|
|
70
69
|
}
|
|
70
|
+
}
|
|
71
71
|
|
|
72
|
-
|
|
73
|
-
|
|
72
|
+
call.resolve(cookieMap);
|
|
73
|
+
});
|
|
74
74
|
}
|
|
75
75
|
|
|
76
76
|
@PluginMethod
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
package com.getcapacitor.plugin;
|
|
2
|
+
|
|
3
|
+
import android.content.pm.PackageInfo;
|
|
4
|
+
import android.content.res.Configuration;
|
|
5
|
+
import android.view.View;
|
|
6
|
+
import android.view.Window;
|
|
7
|
+
import androidx.core.graphics.Insets;
|
|
8
|
+
import androidx.core.view.ViewCompat;
|
|
9
|
+
import androidx.core.view.WindowCompat;
|
|
10
|
+
import androidx.core.view.WindowInsetsCompat;
|
|
11
|
+
import androidx.core.view.WindowInsetsControllerCompat;
|
|
12
|
+
import androidx.webkit.WebViewCompat;
|
|
13
|
+
import com.getcapacitor.Plugin;
|
|
14
|
+
import com.getcapacitor.PluginCall;
|
|
15
|
+
import com.getcapacitor.PluginMethod;
|
|
16
|
+
import com.getcapacitor.annotation.CapacitorPlugin;
|
|
17
|
+
import java.util.Locale;
|
|
18
|
+
import java.util.regex.Matcher;
|
|
19
|
+
import java.util.regex.Pattern;
|
|
20
|
+
|
|
21
|
+
@CapacitorPlugin
|
|
22
|
+
public class SystemBars extends Plugin {
|
|
23
|
+
|
|
24
|
+
static final String STYLE_LIGHT = "LIGHT";
|
|
25
|
+
static final String STYLE_DARK = "DARK";
|
|
26
|
+
static final String STYLE_DEFAULT = "DEFAULT";
|
|
27
|
+
static final String BAR_STATUS_BAR = "StatusBar";
|
|
28
|
+
static final String BAR_GESTURE_BAR = "NavigationBar";
|
|
29
|
+
|
|
30
|
+
static final String viewportMetaJSFunction = """
|
|
31
|
+
function capacitorSystemBarsCheckMetaViewport() {
|
|
32
|
+
const meta = document.querySelectorAll("meta[name=viewport]");
|
|
33
|
+
if (meta.length == 0) {
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
// get the last found meta viewport tag
|
|
37
|
+
const metaContent = meta[meta.length - 1].content;
|
|
38
|
+
return metaContent.includes("viewport-fit=cover");
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
capacitorSystemBarsCheckMetaViewport();
|
|
42
|
+
""";
|
|
43
|
+
|
|
44
|
+
@Override
|
|
45
|
+
public void load() {
|
|
46
|
+
super.load();
|
|
47
|
+
initSystemBars();
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
private boolean hasFixedWebView() {
|
|
51
|
+
PackageInfo packageInfo = WebViewCompat.getCurrentWebViewPackage(bridge.getContext());
|
|
52
|
+
Pattern pattern = Pattern.compile("(\\d+)");
|
|
53
|
+
Matcher matcher = pattern.matcher(packageInfo.versionName);
|
|
54
|
+
|
|
55
|
+
if (!matcher.find()) {
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
String majorVersionStr = matcher.group(0);
|
|
60
|
+
int majorVersion = Integer.parseInt(majorVersionStr);
|
|
61
|
+
|
|
62
|
+
return majorVersion >= 140;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
private void initSystemBars() {
|
|
66
|
+
String style = getConfig().getString("style", STYLE_DEFAULT).toUpperCase(Locale.US);
|
|
67
|
+
boolean hidden = getConfig().getBoolean("hidden", false);
|
|
68
|
+
boolean disableCSSInsets = getConfig().getBoolean("disableInsets", false);
|
|
69
|
+
|
|
70
|
+
this.bridge.getWebView().evaluateJavascript(viewportMetaJSFunction, (res) -> {
|
|
71
|
+
boolean hasMetaViewportCover = res.equals("true");
|
|
72
|
+
if (!disableCSSInsets) {
|
|
73
|
+
setupSafeAreaInsets(this.hasFixedWebView(), hasMetaViewportCover);
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
getBridge().executeOnMainThread(() -> {
|
|
78
|
+
setStyle(style, "");
|
|
79
|
+
setHidden(hidden, "");
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
@PluginMethod
|
|
84
|
+
public void setStyle(final PluginCall call) {
|
|
85
|
+
String bar = call.getString("bar", "");
|
|
86
|
+
String style = call.getString("style", STYLE_DEFAULT);
|
|
87
|
+
|
|
88
|
+
getBridge().executeOnMainThread(() -> {
|
|
89
|
+
setStyle(style, bar);
|
|
90
|
+
call.resolve();
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
@PluginMethod
|
|
95
|
+
public void show(final PluginCall call) {
|
|
96
|
+
String bar = call.getString("bar", "");
|
|
97
|
+
|
|
98
|
+
getBridge().executeOnMainThread(() -> {
|
|
99
|
+
setHidden(false, bar);
|
|
100
|
+
call.resolve();
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
@PluginMethod
|
|
105
|
+
public void hide(final PluginCall call) {
|
|
106
|
+
String bar = call.getString("bar", "");
|
|
107
|
+
|
|
108
|
+
getBridge().executeOnMainThread(() -> {
|
|
109
|
+
setHidden(true, bar);
|
|
110
|
+
call.resolve();
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
@PluginMethod
|
|
115
|
+
public void setAnimation(final PluginCall call) {
|
|
116
|
+
call.resolve();
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
private void setupSafeAreaInsets(boolean hasFixedWebView, boolean hasMetaViewportCover) {
|
|
120
|
+
ViewCompat.setOnApplyWindowInsetsListener((View) getBridge().getWebView().getParent(), (v, insets) -> {
|
|
121
|
+
if (hasFixedWebView && hasMetaViewportCover) {
|
|
122
|
+
return insets;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
Insets safeArea = insets.getInsets(WindowInsetsCompat.Type.systemBars() | WindowInsetsCompat.Type.displayCutout());
|
|
126
|
+
Insets imeInsets = insets.getInsets(WindowInsetsCompat.Type.ime());
|
|
127
|
+
boolean keyboardVisible = insets.isVisible(WindowInsetsCompat.Type.ime());
|
|
128
|
+
|
|
129
|
+
int bottomInsets = safeArea.bottom;
|
|
130
|
+
|
|
131
|
+
if (keyboardVisible) {
|
|
132
|
+
// When https://issues.chromium.org/issues/457682720 is fixed and released,
|
|
133
|
+
// add behind a WebView version check
|
|
134
|
+
bottomInsets = imeInsets.bottom - bottomInsets;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
injectSafeAreaCSS(safeArea.top, safeArea.right, bottomInsets, safeArea.left);
|
|
138
|
+
|
|
139
|
+
return WindowInsetsCompat.CONSUMED;
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
private void injectSafeAreaCSS(int top, int right, int bottom, int left) {
|
|
144
|
+
// Convert pixels to density-independent pixels
|
|
145
|
+
float density = getActivity().getResources().getDisplayMetrics().density;
|
|
146
|
+
float topPx = top / density;
|
|
147
|
+
float rightPx = right / density;
|
|
148
|
+
float bottomPx = bottom / density;
|
|
149
|
+
float leftPx = left / density;
|
|
150
|
+
|
|
151
|
+
// Execute JavaScript to inject the CSS
|
|
152
|
+
getBridge().executeOnMainThread(() -> {
|
|
153
|
+
if (bridge != null && bridge.getWebView() != null) {
|
|
154
|
+
String script = String.format(
|
|
155
|
+
Locale.US,
|
|
156
|
+
"""
|
|
157
|
+
try {
|
|
158
|
+
document.documentElement.style.setProperty("--safe-area-inset-top", "%dpx");
|
|
159
|
+
document.documentElement.style.setProperty("--safe-area-inset-right", "%dpx");
|
|
160
|
+
document.documentElement.style.setProperty("--safe-area-inset-bottom", "%dpx");
|
|
161
|
+
document.documentElement.style.setProperty("--safe-area-inset-left", "%dpx");
|
|
162
|
+
} catch(e) { console.error('Error injecting safe area CSS:', e); }
|
|
163
|
+
""",
|
|
164
|
+
(int) topPx,
|
|
165
|
+
(int) rightPx,
|
|
166
|
+
(int) bottomPx,
|
|
167
|
+
(int) leftPx
|
|
168
|
+
);
|
|
169
|
+
|
|
170
|
+
bridge.getWebView().evaluateJavascript(script, null);
|
|
171
|
+
}
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
private void setStyle(String style, String bar) {
|
|
176
|
+
if (style.equals(STYLE_DEFAULT)) {
|
|
177
|
+
style = getStyleForTheme();
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
Window window = getActivity().getWindow();
|
|
181
|
+
WindowInsetsControllerCompat windowInsetsControllerCompat = WindowCompat.getInsetsController(window, window.getDecorView());
|
|
182
|
+
if (bar.isEmpty() || bar.equals(BAR_STATUS_BAR)) {
|
|
183
|
+
windowInsetsControllerCompat.setAppearanceLightStatusBars(!style.equals(STYLE_DARK));
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
if (bar.isEmpty() || bar.equals(BAR_GESTURE_BAR)) {
|
|
187
|
+
windowInsetsControllerCompat.setAppearanceLightNavigationBars(!style.equals(STYLE_DARK));
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
private void setHidden(boolean hide, String bar) {
|
|
192
|
+
Window window = getActivity().getWindow();
|
|
193
|
+
WindowInsetsControllerCompat windowInsetsControllerCompat = WindowCompat.getInsetsController(window, window.getDecorView());
|
|
194
|
+
|
|
195
|
+
if (hide) {
|
|
196
|
+
if (bar.isEmpty() || bar.equals(BAR_STATUS_BAR)) {
|
|
197
|
+
windowInsetsControllerCompat.hide(WindowInsetsCompat.Type.statusBars());
|
|
198
|
+
}
|
|
199
|
+
if (bar.isEmpty() || bar.equals(BAR_GESTURE_BAR)) {
|
|
200
|
+
windowInsetsControllerCompat.hide(WindowInsetsCompat.Type.navigationBars());
|
|
201
|
+
}
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
if (bar.isEmpty() || bar.equals(BAR_STATUS_BAR)) {
|
|
206
|
+
windowInsetsControllerCompat.show(WindowInsetsCompat.Type.systemBars());
|
|
207
|
+
}
|
|
208
|
+
if (bar.isEmpty() || bar.equals(BAR_GESTURE_BAR)) {
|
|
209
|
+
windowInsetsControllerCompat.show(WindowInsetsCompat.Type.navigationBars());
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
private String getStyleForTheme() {
|
|
214
|
+
int currentNightMode = getActivity().getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
|
|
215
|
+
if (currentNightMode != Configuration.UI_MODE_NIGHT_YES) {
|
|
216
|
+
return STYLE_LIGHT;
|
|
217
|
+
}
|
|
218
|
+
return STYLE_DARK;
|
|
219
|
+
}
|
|
220
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@capacitor/android",
|
|
3
|
-
"version": "8.0.0-
|
|
3
|
+
"version": "8.0.0-beta.0",
|
|
4
4
|
"description": "Capacitor: Cross-platform apps with JavaScript and the web",
|
|
5
5
|
"homepage": "https://capacitorjs.com",
|
|
6
6
|
"author": "Ionic Team <hi@ionic.io> (https://ionic.io)",
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
"verify": "./gradlew clean lint build test -b capacitor/build.gradle"
|
|
24
24
|
},
|
|
25
25
|
"peerDependencies": {
|
|
26
|
-
"@capacitor/core": "^8.0.0-
|
|
26
|
+
"@capacitor/core": "^8.0.0-beta.0"
|
|
27
27
|
},
|
|
28
28
|
"publishConfig": {
|
|
29
29
|
"access": "public"
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
<?xml version="1.0" encoding="utf-8"?>
|
|
2
|
-
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
3
|
-
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
4
|
-
xmlns:tools="http://schemas.android.com/tools"
|
|
5
|
-
android:layout_width="match_parent"
|
|
6
|
-
android:layout_height="match_parent"
|
|
7
|
-
tools:context="com.getcapacitor.BridgeActivity"
|
|
8
|
-
>
|
|
9
|
-
|
|
10
|
-
<com.getcapacitor.CapacitorWebView
|
|
11
|
-
android:id="@+id/webview"
|
|
12
|
-
android:layout_width="fill_parent"
|
|
13
|
-
android:layout_height="fill_parent" />
|
|
14
|
-
|
|
15
|
-
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|