@capacitor-plus/android 8.0.0-beta.0 → 8.0.4
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/README.md +55 -0
- package/capacitor/src/main/assets/native-bridge.js +6 -0
- package/capacitor/src/main/java/com/getcapacitor/BridgeWebViewClient.java +12 -0
- package/capacitor/src/main/java/com/getcapacitor/WebViewListener.java +10 -0
- package/capacitor/src/main/java/com/getcapacitor/plugin/SystemBars.java +91 -40
- package/package.json +4 -3
package/README.md
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# @capacitor-plus/android
|
|
2
|
+
|
|
3
|
+
The Android native runtime for Capacitor+.
|
|
4
|
+
|
|
5
|
+
## What is Capacitor+?
|
|
6
|
+
|
|
7
|
+
**Capacitor+** is an automated fork of [Capacitor](https://github.com/ionic-team/capacitor) maintained by [Capgo](https://capgo.app). It provides the same functionality as `@capacitor/android` with these benefits:
|
|
8
|
+
|
|
9
|
+
- **Faster Updates**: Get upstream fixes as soon as they pass CI, not months later
|
|
10
|
+
- **Community PRs Merged**: Valuable PRs stuck in the upstream queue are merged here
|
|
11
|
+
- **Security Reviewed**: Every change is analyzed by Claude Code for vulnerabilities
|
|
12
|
+
- **Drop-in Replacement**: Same native APIs, just a different package scope
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npm install @capacitor-plus/android
|
|
18
|
+
npx cap add android
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Requirements
|
|
22
|
+
|
|
23
|
+
- Android Studio
|
|
24
|
+
- Android SDK 22+ (target SDK 35)
|
|
25
|
+
- Java 21+
|
|
26
|
+
- Gradle 8+
|
|
27
|
+
|
|
28
|
+
## Why Use This Instead of @capacitor/android?
|
|
29
|
+
|
|
30
|
+
The official Capacitor release cycle can leave bug fixes and improvements waiting for months. Capacitor+ automatically syncs with upstream daily, runs full CI (including Android builds and tests), and publishes verified releases immediately.
|
|
31
|
+
|
|
32
|
+
**Have a PR stuck in the Capacitor repo?** Open an issue linking to it, and we'll merge it here so you can use your fix today.
|
|
33
|
+
|
|
34
|
+
## What's Included
|
|
35
|
+
|
|
36
|
+
This package contains:
|
|
37
|
+
|
|
38
|
+
- **Capacitor Android Library**: The core native bridge between your web app and Android
|
|
39
|
+
- **WebView Configuration**: Optimized WebView settings for hybrid apps
|
|
40
|
+
- **Plugin System**: Native plugin architecture for extending functionality
|
|
41
|
+
- **ProGuard Rules**: Pre-configured rules for production builds
|
|
42
|
+
|
|
43
|
+
## Compatibility
|
|
44
|
+
|
|
45
|
+
This package maintains API compatibility with `@capacitor/android`. All Capacitor plugins work with both the official and Capacitor+ Android runtime.
|
|
46
|
+
|
|
47
|
+
## Links
|
|
48
|
+
|
|
49
|
+
- [Main Repository](https://github.com/Cap-go/capacitor-plus)
|
|
50
|
+
- [Android Documentation](https://capacitorjs.com/docs/android)
|
|
51
|
+
- [Capgo](https://capgo.app)
|
|
52
|
+
|
|
53
|
+
## License
|
|
54
|
+
|
|
55
|
+
MIT - See [LICENSE](../LICENSE)
|
|
@@ -367,6 +367,12 @@ var nativeBridge = (function (exports) {
|
|
|
367
367
|
}
|
|
368
368
|
};
|
|
369
369
|
const platform = getPlatformId(win);
|
|
370
|
+
if (platform == 'android' && typeof win.CapacitorSystemBarsAndroidInterface !== 'undefined') {
|
|
371
|
+
// add DOM ready listener for System Bars
|
|
372
|
+
document.addEventListener('DOMContentLoaded', function () {
|
|
373
|
+
win.CapacitorSystemBarsAndroidInterface.onDOMReady();
|
|
374
|
+
});
|
|
375
|
+
}
|
|
370
376
|
if (platform == 'android' || platform == 'ios') {
|
|
371
377
|
// patch document.cookie on Android/iOS
|
|
372
378
|
win.CapacitorCookiesDescriptor =
|
|
@@ -102,4 +102,16 @@ public class BridgeWebViewClient extends WebViewClient {
|
|
|
102
102
|
|
|
103
103
|
return result;
|
|
104
104
|
}
|
|
105
|
+
|
|
106
|
+
@Override
|
|
107
|
+
public void onPageCommitVisible(WebView view, String url) {
|
|
108
|
+
super.onPageCommitVisible(view, url);
|
|
109
|
+
|
|
110
|
+
List<WebViewListener> webViewListeners = bridge.getWebViewListeners();
|
|
111
|
+
if (webViewListeners != null) {
|
|
112
|
+
for (WebViewListener listener : bridge.getWebViewListeners()) {
|
|
113
|
+
listener.onPageCommitVisible(view, url);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
105
117
|
}
|
|
@@ -54,4 +54,14 @@ public abstract class WebViewListener {
|
|
|
54
54
|
// Override me to add behavior to the web view render process gone event
|
|
55
55
|
return false;
|
|
56
56
|
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Callback for page start event.
|
|
60
|
+
*
|
|
61
|
+
* @param view The WebView for which the navigation occurred.
|
|
62
|
+
* @param url The URL corresponding to the page navigation that triggered this callback.
|
|
63
|
+
*/
|
|
64
|
+
public void onPageCommitVisible(WebView view, String url) {
|
|
65
|
+
// Override me to add behavior to handle the onPageCommitVisible event
|
|
66
|
+
}
|
|
57
67
|
}
|
|
@@ -1,22 +1,23 @@
|
|
|
1
1
|
package com.getcapacitor.plugin;
|
|
2
2
|
|
|
3
|
-
import android.content.pm.PackageInfo;
|
|
4
3
|
import android.content.res.Configuration;
|
|
4
|
+
import android.os.Build;
|
|
5
5
|
import android.view.View;
|
|
6
|
+
import android.view.ViewGroup;
|
|
6
7
|
import android.view.Window;
|
|
8
|
+
import android.webkit.JavascriptInterface;
|
|
9
|
+
import android.webkit.WebView;
|
|
7
10
|
import androidx.core.graphics.Insets;
|
|
8
11
|
import androidx.core.view.ViewCompat;
|
|
9
12
|
import androidx.core.view.WindowCompat;
|
|
10
13
|
import androidx.core.view.WindowInsetsCompat;
|
|
11
14
|
import androidx.core.view.WindowInsetsControllerCompat;
|
|
12
|
-
import androidx.webkit.WebViewCompat;
|
|
13
15
|
import com.getcapacitor.Plugin;
|
|
14
16
|
import com.getcapacitor.PluginCall;
|
|
15
17
|
import com.getcapacitor.PluginMethod;
|
|
18
|
+
import com.getcapacitor.WebViewListener;
|
|
16
19
|
import com.getcapacitor.annotation.CapacitorPlugin;
|
|
17
20
|
import java.util.Locale;
|
|
18
|
-
import java.util.regex.Matcher;
|
|
19
|
-
import java.util.regex.Pattern;
|
|
20
21
|
|
|
21
22
|
@CapacitorPlugin
|
|
22
23
|
public class SystemBars extends Plugin {
|
|
@@ -27,6 +28,9 @@ public class SystemBars extends Plugin {
|
|
|
27
28
|
static final String BAR_STATUS_BAR = "StatusBar";
|
|
28
29
|
static final String BAR_GESTURE_BAR = "NavigationBar";
|
|
29
30
|
|
|
31
|
+
static final String INSETS_HANDLING_CSS = "css";
|
|
32
|
+
static final String INSETS_HANDLING_DISABLE = "disable";
|
|
33
|
+
|
|
30
34
|
static final String viewportMetaJSFunction = """
|
|
31
35
|
function capacitorSystemBarsCheckMetaViewport() {
|
|
32
36
|
const meta = document.querySelectorAll("meta[name=viewport]");
|
|
@@ -37,42 +41,52 @@ public class SystemBars extends Plugin {
|
|
|
37
41
|
const metaContent = meta[meta.length - 1].content;
|
|
38
42
|
return metaContent.includes("viewport-fit=cover");
|
|
39
43
|
}
|
|
40
|
-
|
|
41
44
|
capacitorSystemBarsCheckMetaViewport();
|
|
42
45
|
""";
|
|
43
46
|
|
|
47
|
+
private boolean insetHandlingEnabled = true;
|
|
48
|
+
private boolean hasViewportCover = false;
|
|
49
|
+
|
|
44
50
|
@Override
|
|
45
51
|
public void load() {
|
|
52
|
+
getBridge().getWebView().addJavascriptInterface(this, "CapacitorSystemBarsAndroidInterface");
|
|
46
53
|
super.load();
|
|
54
|
+
|
|
47
55
|
initSystemBars();
|
|
48
56
|
}
|
|
49
57
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
58
|
+
@Override
|
|
59
|
+
protected void handleOnStart() {
|
|
60
|
+
super.handleOnStart();
|
|
61
|
+
|
|
62
|
+
this.getBridge().addWebViewListener(
|
|
63
|
+
new WebViewListener() {
|
|
64
|
+
@Override
|
|
65
|
+
public void onPageCommitVisible(WebView view, String url) {
|
|
66
|
+
super.onPageCommitVisible(view, url);
|
|
67
|
+
getBridge().getWebView().requestApplyInsets();
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
);
|
|
71
|
+
}
|
|
61
72
|
|
|
62
|
-
|
|
73
|
+
@Override
|
|
74
|
+
protected void handleOnConfigurationChanged(Configuration newConfig) {
|
|
75
|
+
super.handleOnConfigurationChanged(newConfig);
|
|
76
|
+
setStyle(STYLE_DEFAULT, "");
|
|
63
77
|
}
|
|
64
78
|
|
|
65
79
|
private void initSystemBars() {
|
|
66
80
|
String style = getConfig().getString("style", STYLE_DEFAULT).toUpperCase(Locale.US);
|
|
67
81
|
boolean hidden = getConfig().getBoolean("hidden", false);
|
|
68
|
-
boolean disableCSSInsets = getConfig().getBoolean("disableInsets", false);
|
|
69
82
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
83
|
+
String insetsHandling = getConfig().getString("insetsHandling", "css");
|
|
84
|
+
if (insetsHandling.equals(INSETS_HANDLING_DISABLE)) {
|
|
85
|
+
insetHandlingEnabled = false;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
initWindowInsetsListener();
|
|
89
|
+
initSafeAreaInsets();
|
|
76
90
|
|
|
77
91
|
getBridge().executeOnMainThread(() -> {
|
|
78
92
|
setStyle(style, "");
|
|
@@ -116,28 +130,65 @@ public class SystemBars extends Plugin {
|
|
|
116
130
|
call.resolve();
|
|
117
131
|
}
|
|
118
132
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
133
|
+
@JavascriptInterface
|
|
134
|
+
public void onDOMReady() {
|
|
135
|
+
getActivity().runOnUiThread(() -> {
|
|
136
|
+
this.bridge.getWebView().evaluateJavascript(viewportMetaJSFunction, (res) -> {
|
|
137
|
+
hasViewportCover = res.equals("true");
|
|
124
138
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
139
|
+
getBridge().getWebView().requestApplyInsets();
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
}
|
|
128
143
|
|
|
129
|
-
|
|
144
|
+
private Insets calcSafeAreaInsets(WindowInsetsCompat insets) {
|
|
145
|
+
Insets safeArea = insets.getInsets(WindowInsetsCompat.Type.systemBars() | WindowInsetsCompat.Type.displayCutout());
|
|
146
|
+
return Insets.of(safeArea.left, safeArea.top, safeArea.right, safeArea.bottom);
|
|
147
|
+
}
|
|
130
148
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
149
|
+
private void initSafeAreaInsets() {
|
|
150
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM && insetHandlingEnabled) {
|
|
151
|
+
View v = (View) this.getBridge().getWebView().getParent();
|
|
152
|
+
WindowInsetsCompat insets = ViewCompat.getRootWindowInsets(v);
|
|
153
|
+
if (insets != null) {
|
|
154
|
+
Insets safeAreaInsets = calcSafeAreaInsets(insets);
|
|
155
|
+
injectSafeAreaCSS(safeAreaInsets.top, safeAreaInsets.right, safeAreaInsets.bottom, safeAreaInsets.left);
|
|
135
156
|
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
136
159
|
|
|
137
|
-
|
|
160
|
+
private void initWindowInsetsListener() {
|
|
161
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM && insetHandlingEnabled) {
|
|
162
|
+
ViewCompat.setOnApplyWindowInsetsListener((View) getBridge().getWebView().getParent(), (v, insets) -> {
|
|
163
|
+
if (hasViewportCover) {
|
|
164
|
+
Insets safeAreaInsets = calcSafeAreaInsets(insets);
|
|
165
|
+
boolean keyboardVisible = insets.isVisible(WindowInsetsCompat.Type.ime());
|
|
138
166
|
|
|
139
|
-
|
|
140
|
-
|
|
167
|
+
if (keyboardVisible) {
|
|
168
|
+
safeAreaInsets = Insets.of(safeAreaInsets.left, safeAreaInsets.top, safeAreaInsets.right, 0);
|
|
169
|
+
|
|
170
|
+
Insets imeInsets = insets.getInsets(WindowInsetsCompat.Type.ime());
|
|
171
|
+
setViewMargins(v, Insets.of(0, 0, 0, imeInsets.bottom));
|
|
172
|
+
} else {
|
|
173
|
+
setViewMargins(v, Insets.NONE);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
injectSafeAreaCSS(safeAreaInsets.top, safeAreaInsets.right, safeAreaInsets.bottom, safeAreaInsets.left);
|
|
177
|
+
return WindowInsetsCompat.CONSUMED;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
return insets;
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
private void setViewMargins(View v, Insets insets) {
|
|
186
|
+
ViewGroup.MarginLayoutParams mlp = (ViewGroup.MarginLayoutParams) v.getLayoutParams();
|
|
187
|
+
mlp.leftMargin = insets.left;
|
|
188
|
+
mlp.bottomMargin = insets.bottom;
|
|
189
|
+
mlp.rightMargin = insets.right;
|
|
190
|
+
mlp.topMargin = insets.top;
|
|
191
|
+
v.setLayoutParams(mlp);
|
|
141
192
|
}
|
|
142
193
|
|
|
143
194
|
private void injectSafeAreaCSS(int top, int right, int bottom, int left) {
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@capacitor-plus/android",
|
|
3
|
-
"version": "8.0.
|
|
3
|
+
"version": "8.0.4",
|
|
4
4
|
"description": "Capacitor+: Enhanced Capacitor with automated upstream sync - Cross-platform apps with JavaScript and the web",
|
|
5
|
-
"homepage": "https://
|
|
5
|
+
"homepage": "https://capgo.app/docs/plugins/capacitor-plus/",
|
|
6
6
|
"author": "Capgo Team <support@capgo.app> (https://capgo.app)",
|
|
7
7
|
"license": "MIT",
|
|
8
8
|
"repository": {
|
|
@@ -23,7 +23,8 @@
|
|
|
23
23
|
"verify": "./gradlew clean lint build test -b capacitor/build.gradle"
|
|
24
24
|
},
|
|
25
25
|
"peerDependencies": {
|
|
26
|
-
"@capacitor-plus/core": "^8.0.0
|
|
26
|
+
"@capacitor-plus/core": "^8.0.0",
|
|
27
|
+
"@capacitor/core": "^8.0.0"
|
|
27
28
|
},
|
|
28
29
|
"publishConfig": {
|
|
29
30
|
"access": "public"
|