@capacitor-plus/android 8.3.10 → 8.3.12
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/src/main/assets/native-bridge.js +68 -18
- package/capacitor/src/main/java/com/getcapacitor/Bridge.java +8 -1
- package/capacitor/src/main/java/com/getcapacitor/MessageHandler.java +6 -2
- package/capacitor/src/main/java/com/getcapacitor/WebViewLocalServer.java +54 -6
- package/capacitor/src/main/java/com/getcapacitor/plugin/SystemBars.java +121 -45
- package/capacitor/src/main/java/com/getcapacitor/plugin/util/CapacitorHttpUrlConnection.java +14 -14
- package/package.json +1 -1
|
@@ -44,6 +44,33 @@ var nativeBridge = (function (exports) {
|
|
|
44
44
|
reader.onerror = reject;
|
|
45
45
|
reader.readAsBinaryString(file);
|
|
46
46
|
});
|
|
47
|
+
const readArrayBufferAsBase64 = (arrayBuffer) => {
|
|
48
|
+
const bytes = new Uint8Array(arrayBuffer);
|
|
49
|
+
const chunkSize = 0x8000;
|
|
50
|
+
let binary = '';
|
|
51
|
+
for (let i = 0; i < bytes.length; i += chunkSize) {
|
|
52
|
+
binary += String.fromCharCode(...bytes.subarray(i, i + chunkSize));
|
|
53
|
+
}
|
|
54
|
+
return btoa(binary);
|
|
55
|
+
};
|
|
56
|
+
const readBlobAsBase64 = (blob) => new Promise((resolve, reject) => {
|
|
57
|
+
const reader = new FileReader();
|
|
58
|
+
reader.onload = () => {
|
|
59
|
+
const result = reader.result;
|
|
60
|
+
resolve(result.indexOf(',') >= 0 ? result.split(',')[1] : result);
|
|
61
|
+
};
|
|
62
|
+
reader.onerror = reject;
|
|
63
|
+
reader.readAsDataURL(blob);
|
|
64
|
+
});
|
|
65
|
+
const readDataViewAsBase64 = (dataView) => readArrayBufferAsBase64(dataView.buffer.slice(dataView.byteOffset, dataView.byteOffset + dataView.byteLength));
|
|
66
|
+
const base64StringToArrayBuffer = (base64) => {
|
|
67
|
+
const binary = atob(base64);
|
|
68
|
+
const bytes = new Uint8Array(binary.length);
|
|
69
|
+
for (let i = 0; i < binary.length; i++) {
|
|
70
|
+
bytes[i] = binary.charCodeAt(i);
|
|
71
|
+
}
|
|
72
|
+
return bytes.buffer;
|
|
73
|
+
};
|
|
47
74
|
const convertFormData = async (formData) => {
|
|
48
75
|
const newFormData = [];
|
|
49
76
|
for (const pair of formData.entries()) {
|
|
@@ -65,9 +92,38 @@ var nativeBridge = (function (exports) {
|
|
|
65
92
|
return newFormData;
|
|
66
93
|
};
|
|
67
94
|
const convertBody = async (body, contentType) => {
|
|
68
|
-
if (body instanceof
|
|
95
|
+
if (body instanceof File) {
|
|
96
|
+
const fileData = await readFileAsBase64(body);
|
|
97
|
+
return {
|
|
98
|
+
data: fileData,
|
|
99
|
+
type: 'file',
|
|
100
|
+
headers: { 'Content-Type': body.type },
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
else if (body instanceof Blob) {
|
|
104
|
+
return {
|
|
105
|
+
data: await readBlobAsBase64(body),
|
|
106
|
+
type: 'binary',
|
|
107
|
+
headers: { 'Content-Type': contentType || body.type || 'application/octet-stream' },
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
else if (body instanceof ArrayBuffer) {
|
|
111
|
+
return {
|
|
112
|
+
data: readArrayBufferAsBase64(body),
|
|
113
|
+
type: 'binary',
|
|
114
|
+
headers: { 'Content-Type': contentType || 'application/octet-stream' },
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
else if (ArrayBuffer.isView(body)) {
|
|
118
|
+
return {
|
|
119
|
+
data: readDataViewAsBase64(body),
|
|
120
|
+
type: 'binary',
|
|
121
|
+
headers: { 'Content-Type': contentType || 'application/octet-stream' },
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
if ((typeof ReadableStream !== 'undefined' && body instanceof ReadableStream) || body instanceof Uint8Array) {
|
|
69
125
|
let encodedData;
|
|
70
|
-
if (body instanceof ReadableStream) {
|
|
126
|
+
if (typeof ReadableStream !== 'undefined' && body instanceof ReadableStream) {
|
|
71
127
|
const reader = body.getReader();
|
|
72
128
|
const chunks = [];
|
|
73
129
|
while (true) {
|
|
@@ -87,7 +143,9 @@ var nativeBridge = (function (exports) {
|
|
|
87
143
|
else {
|
|
88
144
|
encodedData = body;
|
|
89
145
|
}
|
|
90
|
-
let data =
|
|
146
|
+
let data = (contentType === null || contentType === void 0 ? void 0 : contentType.startsWith('image')) || contentType === 'application/octet-stream'
|
|
147
|
+
? readDataViewAsBase64(encodedData)
|
|
148
|
+
: new TextDecoder().decode(encodedData);
|
|
91
149
|
let type;
|
|
92
150
|
if (contentType === 'application/json') {
|
|
93
151
|
try {
|
|
@@ -102,7 +160,7 @@ var nativeBridge = (function (exports) {
|
|
|
102
160
|
type = 'formData';
|
|
103
161
|
}
|
|
104
162
|
else if (contentType === null || contentType === void 0 ? void 0 : contentType.startsWith('image')) {
|
|
105
|
-
type = '
|
|
163
|
+
type = 'binary';
|
|
106
164
|
}
|
|
107
165
|
else if (contentType === 'application/octet-stream') {
|
|
108
166
|
type = 'binary';
|
|
@@ -128,14 +186,6 @@ var nativeBridge = (function (exports) {
|
|
|
128
186
|
type: 'formData',
|
|
129
187
|
};
|
|
130
188
|
}
|
|
131
|
-
else if (body instanceof Blob) {
|
|
132
|
-
const fileData = await readFileAsBase64(body);
|
|
133
|
-
return {
|
|
134
|
-
data: fileData,
|
|
135
|
-
type: 'file',
|
|
136
|
-
headers: { 'Content-Type': body.type },
|
|
137
|
-
};
|
|
138
|
-
}
|
|
139
189
|
return { data: body, type: 'json' };
|
|
140
190
|
};
|
|
141
191
|
const CAPACITOR_HTTP_INTERCEPTOR = '/_capacitor_http_interceptor_';
|
|
@@ -535,15 +585,15 @@ var nativeBridge = (function (exports) {
|
|
|
535
585
|
data: requestData,
|
|
536
586
|
dataType: type,
|
|
537
587
|
headers: nativeHeaders,
|
|
588
|
+
responseType: 'arraybuffer',
|
|
538
589
|
});
|
|
539
590
|
const contentType = nativeResponse.headers['Content-Type'] || nativeResponse.headers['content-type'];
|
|
540
|
-
let data = (contentType === null || contentType === void 0 ? void 0 : contentType.startsWith('application/json'))
|
|
541
|
-
? JSON.stringify(nativeResponse.data)
|
|
542
|
-
: nativeResponse.data;
|
|
543
591
|
// use null data for 204 No Content HTTP response
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
592
|
+
const data = nativeResponse.status === 204
|
|
593
|
+
? null
|
|
594
|
+
: (contentType === null || contentType === void 0 ? void 0 : contentType.startsWith('application/json'))
|
|
595
|
+
? JSON.stringify(nativeResponse.data)
|
|
596
|
+
: base64StringToArrayBuffer(nativeResponse.data);
|
|
547
597
|
// intercept & parse response before returning
|
|
548
598
|
const response = new Response(data, {
|
|
549
599
|
headers: nativeResponse.headers,
|
|
@@ -265,8 +265,15 @@ public class Bridge {
|
|
|
265
265
|
JSInjector injector = getJSInjector();
|
|
266
266
|
if (WebViewFeature.isFeatureSupported(WebViewFeature.DOCUMENT_START_SCRIPT)) {
|
|
267
267
|
String allowedOrigin = Uri.parse(appUrl).buildUpon().path(null).fragment(null).clearQuery().build().toString();
|
|
268
|
+
final String finalAllowedOrigin = allowedOrigin;
|
|
269
|
+
Set<String> allowedOrigins = new HashSet<String>() {
|
|
270
|
+
{
|
|
271
|
+
add(finalAllowedOrigin);
|
|
272
|
+
addAll(allowedOriginRules);
|
|
273
|
+
}
|
|
274
|
+
};
|
|
268
275
|
try {
|
|
269
|
-
WebViewCompat.addDocumentStartJavaScript(webView, injector.getScriptString(),
|
|
276
|
+
WebViewCompat.addDocumentStartJavaScript(webView, injector.getScriptString(), allowedOrigins);
|
|
270
277
|
injector = null;
|
|
271
278
|
} catch (IllegalArgumentException ex) {
|
|
272
279
|
Logger.warn("Invalid url, using fallback");
|
|
@@ -26,8 +26,7 @@ public class MessageHandler {
|
|
|
26
26
|
if (WebViewFeature.isFeatureSupported(WebViewFeature.WEB_MESSAGE_LISTENER) && !bridge.getConfig().isUsingLegacyBridge()) {
|
|
27
27
|
WebViewCompat.WebMessageListener capListener = (view, message, sourceOrigin, isMainFrame, replyProxy) -> {
|
|
28
28
|
if (isMainFrame) {
|
|
29
|
-
|
|
30
|
-
javaScriptReplyProxy = replyProxy;
|
|
29
|
+
onModernBridgeMessage(message.getData(), replyProxy);
|
|
31
30
|
} else {
|
|
32
31
|
Logger.warn("Plugin execution is allowed in Main Frame only");
|
|
33
32
|
}
|
|
@@ -42,6 +41,11 @@ public class MessageHandler {
|
|
|
42
41
|
}
|
|
43
42
|
}
|
|
44
43
|
|
|
44
|
+
void onModernBridgeMessage(String data, JavaScriptReplyProxy replyProxy) {
|
|
45
|
+
javaScriptReplyProxy = replyProxy;
|
|
46
|
+
postMessage(data);
|
|
47
|
+
}
|
|
48
|
+
|
|
45
49
|
/**
|
|
46
50
|
* The main message handler that will be called from JavaScript
|
|
47
51
|
* to send a message to the native bridge.
|
|
@@ -348,16 +348,22 @@ public class WebViewLocalServer {
|
|
|
348
348
|
Map<String, String> tempResponseHeaders = handler.buildDefaultResponseHeaders();
|
|
349
349
|
int statusCode = 206;
|
|
350
350
|
try {
|
|
351
|
-
int
|
|
351
|
+
int totalSize = responseStream.available();
|
|
352
352
|
String[] parts = rangeString.split("=");
|
|
353
353
|
String[] streamParts = parts[1].split("-");
|
|
354
|
-
|
|
355
|
-
int
|
|
356
|
-
if (streamParts.length > 1) {
|
|
357
|
-
|
|
354
|
+
int fromRange = Integer.parseInt(streamParts[0]);
|
|
355
|
+
int endRange = totalSize - 1;
|
|
356
|
+
if (streamParts.length > 1 && !streamParts[1].isEmpty()) {
|
|
357
|
+
endRange = Integer.parseInt(streamParts[1]);
|
|
358
358
|
}
|
|
359
|
+
|
|
360
|
+
// Truncate the stream at the end of the requested range.
|
|
361
|
+
// Somewhere in the request pipeline, the stream gets automatically
|
|
362
|
+
// seeked to the correct start position, so we don't need to skip to the fromRange position here.
|
|
363
|
+
responseStream = new BoundedInputStream(responseStream, endRange + 1);
|
|
364
|
+
|
|
359
365
|
tempResponseHeaders.put("Accept-Ranges", "bytes");
|
|
360
|
-
tempResponseHeaders.put("Content-Range", "bytes " + fromRange + "-" +
|
|
366
|
+
tempResponseHeaders.put("Content-Range", "bytes " + fromRange + "-" + endRange + "/" + totalSize);
|
|
361
367
|
} catch (IOException e) {
|
|
362
368
|
statusCode = 404;
|
|
363
369
|
}
|
|
@@ -750,6 +756,48 @@ public class WebViewLocalServer {
|
|
|
750
756
|
}
|
|
751
757
|
}
|
|
752
758
|
|
|
759
|
+
/**
|
|
760
|
+
* An InputStream wrapper that limits the number of bytes that can be read.
|
|
761
|
+
*/
|
|
762
|
+
static class BoundedInputStream extends InputStream {
|
|
763
|
+
|
|
764
|
+
private final InputStream in;
|
|
765
|
+
private long remaining;
|
|
766
|
+
|
|
767
|
+
public BoundedInputStream(InputStream in, long limit) {
|
|
768
|
+
this.in = in;
|
|
769
|
+
this.remaining = limit;
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
@Override
|
|
773
|
+
public int available() throws IOException {
|
|
774
|
+
int available = in.available();
|
|
775
|
+
return (int) Math.min(available, remaining);
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
@Override
|
|
779
|
+
public int read() throws IOException {
|
|
780
|
+
if (remaining <= 0) return -1;
|
|
781
|
+
int result = in.read();
|
|
782
|
+
if (result != -1) remaining--;
|
|
783
|
+
return result;
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
@Override
|
|
787
|
+
public int read(byte[] b, int off, int len) throws IOException {
|
|
788
|
+
if (remaining <= 0) return -1;
|
|
789
|
+
int toRead = (int) Math.min(len, remaining);
|
|
790
|
+
int result = in.read(b, off, toRead);
|
|
791
|
+
if (result > 0) remaining -= result;
|
|
792
|
+
return result;
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
@Override
|
|
796
|
+
public void close() throws IOException {
|
|
797
|
+
in.close();
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
|
|
753
801
|
// For L and above.
|
|
754
802
|
private static class LollipopLazyInputStream extends LazyInputStream {
|
|
755
803
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
package com.getcapacitor.plugin;
|
|
2
2
|
|
|
3
|
+
import android.annotation.SuppressLint;
|
|
3
4
|
import android.content.Context;
|
|
4
5
|
import android.content.pm.PackageInfo;
|
|
5
6
|
import android.content.res.Configuration;
|
|
@@ -16,6 +17,7 @@ import androidx.core.view.WindowCompat;
|
|
|
16
17
|
import androidx.core.view.WindowInsetsCompat;
|
|
17
18
|
import androidx.core.view.WindowInsetsControllerCompat;
|
|
18
19
|
import androidx.webkit.WebViewCompat;
|
|
20
|
+
import com.getcapacitor.Logger;
|
|
19
21
|
import com.getcapacitor.Plugin;
|
|
20
22
|
import com.getcapacitor.PluginCall;
|
|
21
23
|
import com.getcapacitor.PluginMethod;
|
|
@@ -32,6 +34,7 @@ public class SystemBars extends Plugin {
|
|
|
32
34
|
static final String BAR_STATUS_BAR = "StatusBar";
|
|
33
35
|
static final String BAR_GESTURE_BAR = "NavigationBar";
|
|
34
36
|
|
|
37
|
+
// TODO: In Cap 9, add an additional option "full"
|
|
35
38
|
static final String INSETS_HANDLING_CSS = "css";
|
|
36
39
|
static final String INSETS_HANDLING_DISABLE = "disable";
|
|
37
40
|
|
|
@@ -53,12 +56,14 @@ public class SystemBars extends Plugin {
|
|
|
53
56
|
capacitorSystemBarsCheckMetaViewport();
|
|
54
57
|
""";
|
|
55
58
|
|
|
56
|
-
private
|
|
59
|
+
private String insetsHandling = INSETS_HANDLING_CSS;
|
|
57
60
|
private boolean hasViewportCover = false;
|
|
58
61
|
|
|
59
62
|
private String currentStatusBarStyle = STYLE_DEFAULT;
|
|
60
63
|
private String currentGestureBarStyle = STYLE_DEFAULT;
|
|
61
64
|
|
|
65
|
+
private boolean navBarVisible = true;
|
|
66
|
+
|
|
62
67
|
@Override
|
|
63
68
|
public void load() {
|
|
64
69
|
getBridge().getWebView().addJavascriptInterface(this, "CapacitorSystemBarsAndroidInterface");
|
|
@@ -76,7 +81,15 @@ public class SystemBars extends Plugin {
|
|
|
76
81
|
@Override
|
|
77
82
|
public void onPageCommitVisible(WebView view, String url) {
|
|
78
83
|
super.onPageCommitVisible(view, url);
|
|
79
|
-
getBridge().getWebView().
|
|
84
|
+
View parentView = (View) getBridge().getWebView().getParent();
|
|
85
|
+
ViewCompat.requestApplyInsets(parentView);
|
|
86
|
+
if (INSETS_HANDLING_CSS.equals(insetsHandling)) {
|
|
87
|
+
WindowInsetsCompat rootInsets = ViewCompat.getRootWindowInsets(parentView);
|
|
88
|
+
if (rootInsets != null) {
|
|
89
|
+
Insets safeArea = calcSafeAreaInsets(rootInsets);
|
|
90
|
+
injectSafeAreaCSS(safeArea.top, safeArea.right, safeArea.bottom, safeArea.left);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
80
93
|
}
|
|
81
94
|
}
|
|
82
95
|
);
|
|
@@ -94,17 +107,32 @@ public class SystemBars extends Plugin {
|
|
|
94
107
|
String style = getConfig().getString("style", STYLE_DEFAULT).toUpperCase(Locale.US);
|
|
95
108
|
boolean hidden = getConfig().getBoolean("hidden", false);
|
|
96
109
|
|
|
97
|
-
String
|
|
98
|
-
if (
|
|
99
|
-
|
|
110
|
+
String configuredInsetsHandling = getConfig().getString("insetsHandling", INSETS_HANDLING_CSS);
|
|
111
|
+
if (INSETS_HANDLING_CSS.equals(configuredInsetsHandling) || INSETS_HANDLING_DISABLE.equals(configuredInsetsHandling)) {
|
|
112
|
+
insetsHandling = configuredInsetsHandling;
|
|
113
|
+
} else {
|
|
114
|
+
Logger.warn(
|
|
115
|
+
"SystemBars",
|
|
116
|
+
"Unknown insetsHandling value '" + configuredInsetsHandling + "'. Falling back to '" + INSETS_HANDLING_CSS + "'."
|
|
117
|
+
);
|
|
118
|
+
insetsHandling = INSETS_HANDLING_CSS;
|
|
100
119
|
}
|
|
101
120
|
|
|
102
121
|
initWindowInsetsListener();
|
|
103
122
|
initSafeAreaCSSVariables();
|
|
104
123
|
|
|
105
124
|
getBridge().executeOnMainThread(() -> {
|
|
125
|
+
Window window = getActivity().getWindow();
|
|
126
|
+
WindowCompat.setDecorFitsSystemWindows(window, false);
|
|
127
|
+
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.VANILLA_ICE_CREAM) {
|
|
128
|
+
window.setNavigationBarColor(android.graphics.Color.TRANSPARENT);
|
|
129
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
|
130
|
+
window.setNavigationBarContrastEnforced(false);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
106
133
|
setStyle(style, "");
|
|
107
134
|
setHidden(hidden, "");
|
|
135
|
+
ViewCompat.requestApplyInsets((View) getBridge().getWebView().getParent());
|
|
108
136
|
});
|
|
109
137
|
}
|
|
110
138
|
|
|
@@ -144,43 +172,88 @@ public class SystemBars extends Plugin {
|
|
|
144
172
|
call.resolve();
|
|
145
173
|
}
|
|
146
174
|
|
|
175
|
+
@Override
|
|
176
|
+
protected void handleOnResume() {
|
|
177
|
+
super.handleOnResume();
|
|
178
|
+
getBridge().executeOnMainThread(() -> {
|
|
179
|
+
Window window = getActivity().getWindow();
|
|
180
|
+
WindowCompat.setDecorFitsSystemWindows(window, false);
|
|
181
|
+
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.VANILLA_ICE_CREAM) {
|
|
182
|
+
window.setNavigationBarColor(android.graphics.Color.TRANSPARENT);
|
|
183
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
|
184
|
+
window.setNavigationBarContrastEnforced(false);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
setStyle(currentGestureBarStyle, BAR_GESTURE_BAR);
|
|
188
|
+
setStyle(currentStatusBarStyle, BAR_STATUS_BAR);
|
|
189
|
+
ViewCompat.requestApplyInsets((View) getBridge().getWebView().getParent());
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
|
|
147
193
|
@JavascriptInterface
|
|
148
194
|
public void onDOMReady() {
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
195
|
+
if (INSETS_HANDLING_CSS.equals(insetsHandling)) {
|
|
196
|
+
getActivity().runOnUiThread(() -> {
|
|
197
|
+
this.bridge.getWebView().evaluateJavascript(viewportMetaJSFunction, (res) -> {
|
|
198
|
+
hasViewportCover = res.equals("true");
|
|
199
|
+
ViewCompat.requestApplyInsets((View) getBridge().getWebView().getParent());
|
|
200
|
+
});
|
|
154
201
|
});
|
|
155
|
-
}
|
|
202
|
+
}
|
|
156
203
|
}
|
|
157
204
|
|
|
158
205
|
private Insets calcSafeAreaInsets(WindowInsetsCompat insets) {
|
|
159
206
|
Insets safeArea = insets.getInsets(WindowInsetsCompat.Type.systemBars() | WindowInsetsCompat.Type.displayCutout());
|
|
160
|
-
|
|
207
|
+
|
|
208
|
+
int bottom = safeArea.bottom;
|
|
209
|
+
|
|
210
|
+
if (bottom == 0 && Build.VERSION.SDK_INT < Build.VERSION_CODES.R && safeArea.left == 0 && safeArea.right == 0) {
|
|
211
|
+
// skip if nav bar is on a side (landscape)
|
|
212
|
+
bottom = getNavBarHeightFromResources();
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
boolean imeVisible = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
|
|
216
|
+
? insets.isVisible(WindowInsetsCompat.Type.ime())
|
|
217
|
+
: insets.getInsets(WindowInsetsCompat.Type.ime()).bottom > 0;
|
|
218
|
+
|
|
219
|
+
if (imeVisible) {
|
|
161
220
|
return Insets.of(safeArea.left, safeArea.top, safeArea.right, 0);
|
|
162
221
|
}
|
|
163
|
-
return Insets.of(safeArea.left, safeArea.top, safeArea.right,
|
|
222
|
+
return Insets.of(safeArea.left, safeArea.top, safeArea.right, bottom);
|
|
164
223
|
}
|
|
165
224
|
|
|
166
|
-
|
|
167
|
-
|
|
225
|
+
// Only reached on API < 30 when the window reports no inset on any edge, so the
|
|
226
|
+
// choice is this approximation or a hardcoded 0. Lint rejects the internal
|
|
227
|
+
// resource because OEMs may change it; a wrong height still beats no inset.
|
|
228
|
+
@SuppressLint({ "InternalInsetResource", "DiscouragedApi" })
|
|
229
|
+
private int getNavBarHeightFromResources() {
|
|
230
|
+
if (!navBarVisible) return 0;
|
|
231
|
+
android.content.res.Resources res = getActivity().getResources();
|
|
232
|
+
int heightId = res.getIdentifier("navigation_bar_height", "dimen", "android");
|
|
233
|
+
return heightId > 0 ? res.getDimensionPixelSize(heightId) : 0;
|
|
234
|
+
}
|
|
168
235
|
|
|
169
|
-
|
|
236
|
+
private void initSafeAreaCSSVariables() {
|
|
237
|
+
if (INSETS_HANDLING_CSS.equals(insetsHandling)) {
|
|
170
238
|
View v = (View) this.getBridge().getWebView().getParent();
|
|
171
|
-
insets = ViewCompat.getRootWindowInsets(v);
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
if (insets != null) {
|
|
177
|
-
Insets safeAreaInsets = calcSafeAreaInsets(insets);
|
|
178
|
-
injectSafeAreaCSS(safeAreaInsets.top, safeAreaInsets.right, safeAreaInsets.bottom, safeAreaInsets.left);
|
|
239
|
+
WindowInsetsCompat insets = ViewCompat.getRootWindowInsets(v);
|
|
240
|
+
if (insets != null) {
|
|
241
|
+
Insets safeAreaInsets = calcSafeAreaInsets(insets);
|
|
242
|
+
injectSafeAreaCSS(safeAreaInsets.top, safeAreaInsets.right, safeAreaInsets.bottom, safeAreaInsets.left);
|
|
243
|
+
}
|
|
179
244
|
}
|
|
180
245
|
}
|
|
181
246
|
|
|
182
247
|
private void initWindowInsetsListener() {
|
|
248
|
+
if (INSETS_HANDLING_DISABLE.equals(insetsHandling)) {
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
|
|
183
252
|
ViewCompat.setOnApplyWindowInsetsListener((View) getBridge().getWebView().getParent(), (v, insets) -> {
|
|
253
|
+
// getRootWindowInsets() bypasses AppCompat's intermediate view consuming the bottom inset on API < 30
|
|
254
|
+
WindowInsetsCompat rawInsets = ViewCompat.getRootWindowInsets(v);
|
|
255
|
+
WindowInsetsCompat safeAreaSource = (rawInsets != null) ? rawInsets : insets;
|
|
256
|
+
|
|
184
257
|
boolean shouldPassthroughInsets = getWebViewMajorVersion() >= WEBVIEW_VERSION_WITH_SAFE_AREA_FIX && hasViewportCover;
|
|
185
258
|
|
|
186
259
|
Insets systemBarsInsets = insets.getInsets(WindowInsetsCompat.Type.systemBars() | WindowInsetsCompat.Type.displayCutout());
|
|
@@ -191,8 +264,10 @@ public class SystemBars extends Plugin {
|
|
|
191
264
|
// We need to correct for a possible shown IME
|
|
192
265
|
v.setPadding(0, 0, 0, keyboardVisible ? imeInsets.bottom : 0);
|
|
193
266
|
|
|
194
|
-
|
|
195
|
-
|
|
267
|
+
if (hasViewportCover && INSETS_HANDLING_CSS.equals(insetsHandling)) {
|
|
268
|
+
Insets safeAreaInsets = calcSafeAreaInsets(safeAreaSource);
|
|
269
|
+
injectSafeAreaCSS(safeAreaInsets.top, safeAreaInsets.right, safeAreaInsets.bottom, safeAreaInsets.left);
|
|
270
|
+
}
|
|
196
271
|
|
|
197
272
|
return new WindowInsetsCompat.Builder(insets)
|
|
198
273
|
.setInsets(
|
|
@@ -207,15 +282,7 @@ public class SystemBars extends Plugin {
|
|
|
207
282
|
.build();
|
|
208
283
|
}
|
|
209
284
|
|
|
210
|
-
|
|
211
|
-
// We need to correct for a possible shown IME
|
|
212
|
-
v.setPadding(
|
|
213
|
-
systemBarsInsets.left,
|
|
214
|
-
systemBarsInsets.top,
|
|
215
|
-
systemBarsInsets.right,
|
|
216
|
-
keyboardVisible ? imeInsets.bottom : systemBarsInsets.bottom
|
|
217
|
-
);
|
|
218
|
-
}
|
|
285
|
+
v.setPadding(0, 0, 0, keyboardVisible ? imeInsets.bottom : 0);
|
|
219
286
|
|
|
220
287
|
// Returning `WindowInsetsCompat.CONSUMED` breaks recalculation of safe area insets
|
|
221
288
|
// So we have to explicitly set insets to `0`
|
|
@@ -224,8 +291,10 @@ public class SystemBars extends Plugin {
|
|
|
224
291
|
.setInsets(WindowInsetsCompat.Type.systemBars() | WindowInsetsCompat.Type.displayCutout(), Insets.of(0, 0, 0, 0))
|
|
225
292
|
.build();
|
|
226
293
|
|
|
227
|
-
|
|
228
|
-
|
|
294
|
+
if (INSETS_HANDLING_CSS.equals(insetsHandling)) {
|
|
295
|
+
Insets safeAreaInsets = calcSafeAreaInsets(safeAreaSource);
|
|
296
|
+
injectSafeAreaCSS(safeAreaInsets.top, safeAreaInsets.right, safeAreaInsets.bottom, safeAreaInsets.left);
|
|
297
|
+
}
|
|
229
298
|
|
|
230
299
|
return newInsets;
|
|
231
300
|
});
|
|
@@ -264,6 +333,7 @@ public class SystemBars extends Plugin {
|
|
|
264
333
|
}
|
|
265
334
|
|
|
266
335
|
private void setStyle(String style, String bar) {
|
|
336
|
+
String requestedStyle = style;
|
|
267
337
|
if (style.equals(STYLE_DEFAULT)) {
|
|
268
338
|
style = getStyleForTheme();
|
|
269
339
|
}
|
|
@@ -271,12 +341,12 @@ public class SystemBars extends Plugin {
|
|
|
271
341
|
Window window = getActivity().getWindow();
|
|
272
342
|
WindowInsetsControllerCompat windowInsetsControllerCompat = WindowCompat.getInsetsController(window, window.getDecorView());
|
|
273
343
|
if (bar.isEmpty() || bar.equals(BAR_STATUS_BAR)) {
|
|
274
|
-
currentStatusBarStyle =
|
|
344
|
+
currentStatusBarStyle = requestedStyle;
|
|
275
345
|
windowInsetsControllerCompat.setAppearanceLightStatusBars(!style.equals(STYLE_DARK));
|
|
276
346
|
}
|
|
277
347
|
|
|
278
348
|
if (bar.isEmpty() || bar.equals(BAR_GESTURE_BAR)) {
|
|
279
|
-
currentGestureBarStyle =
|
|
349
|
+
currentGestureBarStyle = requestedStyle;
|
|
280
350
|
windowInsetsControllerCompat.setAppearanceLightNavigationBars(!style.equals(STYLE_DARK));
|
|
281
351
|
}
|
|
282
352
|
|
|
@@ -288,20 +358,26 @@ public class SystemBars extends Plugin {
|
|
|
288
358
|
WindowInsetsControllerCompat windowInsetsControllerCompat = WindowCompat.getInsetsController(window, window.getDecorView());
|
|
289
359
|
|
|
290
360
|
if (hide) {
|
|
291
|
-
if (bar.isEmpty()
|
|
361
|
+
if (bar.isEmpty()) {
|
|
362
|
+
windowInsetsControllerCompat.hide(WindowInsetsCompat.Type.systemBars());
|
|
363
|
+
navBarVisible = false;
|
|
364
|
+
} else if (bar.equals(BAR_STATUS_BAR)) {
|
|
292
365
|
windowInsetsControllerCompat.hide(WindowInsetsCompat.Type.statusBars());
|
|
293
|
-
}
|
|
294
|
-
if (bar.isEmpty() || bar.equals(BAR_GESTURE_BAR)) {
|
|
366
|
+
} else if (bar.equals(BAR_GESTURE_BAR)) {
|
|
295
367
|
windowInsetsControllerCompat.hide(WindowInsetsCompat.Type.navigationBars());
|
|
368
|
+
navBarVisible = false;
|
|
296
369
|
}
|
|
297
370
|
return;
|
|
298
371
|
}
|
|
299
372
|
|
|
300
|
-
if (bar.isEmpty()
|
|
373
|
+
if (bar.isEmpty()) {
|
|
301
374
|
windowInsetsControllerCompat.show(WindowInsetsCompat.Type.systemBars());
|
|
302
|
-
|
|
303
|
-
if (bar.
|
|
375
|
+
navBarVisible = true;
|
|
376
|
+
} else if (bar.equals(BAR_STATUS_BAR)) {
|
|
377
|
+
windowInsetsControllerCompat.show(WindowInsetsCompat.Type.statusBars());
|
|
378
|
+
} else if (bar.equals(BAR_GESTURE_BAR)) {
|
|
304
379
|
windowInsetsControllerCompat.show(WindowInsetsCompat.Type.navigationBars());
|
|
380
|
+
navBarVisible = true;
|
|
305
381
|
}
|
|
306
382
|
}
|
|
307
383
|
|
package/capacitor/src/main/java/com/getcapacitor/plugin/util/CapacitorHttpUrlConnection.java
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
package com.getcapacitor.plugin.util;
|
|
2
2
|
|
|
3
|
-
import android.os.Build;
|
|
4
3
|
import android.os.LocaleList;
|
|
5
4
|
import android.text.TextUtils;
|
|
6
5
|
import com.getcapacitor.Bridge;
|
|
@@ -19,7 +18,6 @@ import java.net.URL;
|
|
|
19
18
|
import java.net.URLEncoder;
|
|
20
19
|
import java.net.UnknownServiceException;
|
|
21
20
|
import java.nio.charset.StandardCharsets;
|
|
22
|
-
import java.util.Base64;
|
|
23
21
|
import java.util.Iterator;
|
|
24
22
|
import java.util.List;
|
|
25
23
|
import java.util.Locale;
|
|
@@ -209,13 +207,8 @@ public class CapacitorHttpUrlConnection implements ICapacitorHttpUrlConnection {
|
|
|
209
207
|
dataString = call.getString("data");
|
|
210
208
|
}
|
|
211
209
|
this.writeRequestBody(dataString != null ? dataString : "");
|
|
212
|
-
} else if (bodyType != null && bodyType.equals("file")) {
|
|
213
|
-
|
|
214
|
-
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
|
215
|
-
os.write(Base64.getDecoder().decode(body.toString()));
|
|
216
|
-
}
|
|
217
|
-
os.flush();
|
|
218
|
-
}
|
|
210
|
+
} else if (bodyType != null && (bodyType.equals("file") || bodyType.equals("binary"))) {
|
|
211
|
+
this.writeRequestBody(decodeBase64RequestBody(body.toString()));
|
|
219
212
|
} else if (contentType.contains("application/x-www-form-urlencoded")) {
|
|
220
213
|
try {
|
|
221
214
|
JSObject obj = body.toJSObject();
|
|
@@ -252,6 +245,17 @@ public class CapacitorHttpUrlConnection implements ICapacitorHttpUrlConnection {
|
|
|
252
245
|
}
|
|
253
246
|
}
|
|
254
247
|
|
|
248
|
+
private void writeRequestBody(byte[] body) throws IOException {
|
|
249
|
+
try (DataOutputStream os = new DataOutputStream(connection.getOutputStream())) {
|
|
250
|
+
os.write(body);
|
|
251
|
+
os.flush();
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
static byte[] decodeBase64RequestBody(String body) {
|
|
256
|
+
return android.util.Base64.decode(body, android.util.Base64.DEFAULT);
|
|
257
|
+
}
|
|
258
|
+
|
|
255
259
|
private void writeObjectRequestBody(JSObject object) throws IOException, JSONException {
|
|
256
260
|
try (DataOutputStream os = new DataOutputStream(connection.getOutputStream())) {
|
|
257
261
|
Iterator<String> keys = object.keys();
|
|
@@ -296,11 +300,7 @@ public class CapacitorHttpUrlConnection implements ICapacitorHttpUrlConnection {
|
|
|
296
300
|
os.writeBytes("Content-Transfer-Encoding: binary" + lineEnd);
|
|
297
301
|
os.writeBytes(lineEnd);
|
|
298
302
|
|
|
299
|
-
|
|
300
|
-
os.write(Base64.getDecoder().decode(value));
|
|
301
|
-
} else {
|
|
302
|
-
os.write(android.util.Base64.decode(value, android.util.Base64.DEFAULT));
|
|
303
|
-
}
|
|
303
|
+
os.write(decodeBase64RequestBody(value));
|
|
304
304
|
|
|
305
305
|
os.writeBytes(lineEnd);
|
|
306
306
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@capacitor-plus/android",
|
|
3
|
-
"version": "8.3.
|
|
3
|
+
"version": "8.3.12",
|
|
4
4
|
"description": "Capacitor+: Enhanced Capacitor with automated upstream sync - Cross-platform apps with JavaScript and the web",
|
|
5
5
|
"homepage": "https://capgo.app/docs/plugins/capacitor-plus/",
|
|
6
6
|
"author": "Capgo Team <support@capgo.app> (https://capgo.app)",
|