@capacitor-plus/android 8.3.10 → 8.3.11
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 +89 -31
- 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;
|
|
@@ -59,6 +60,8 @@ public class SystemBars extends Plugin {
|
|
|
59
60
|
private String currentStatusBarStyle = STYLE_DEFAULT;
|
|
60
61
|
private String currentGestureBarStyle = STYLE_DEFAULT;
|
|
61
62
|
|
|
63
|
+
private boolean navBarVisible = true;
|
|
64
|
+
|
|
62
65
|
@Override
|
|
63
66
|
public void load() {
|
|
64
67
|
getBridge().getWebView().addJavascriptInterface(this, "CapacitorSystemBarsAndroidInterface");
|
|
@@ -76,7 +79,15 @@ public class SystemBars extends Plugin {
|
|
|
76
79
|
@Override
|
|
77
80
|
public void onPageCommitVisible(WebView view, String url) {
|
|
78
81
|
super.onPageCommitVisible(view, url);
|
|
79
|
-
getBridge().getWebView().
|
|
82
|
+
View parentView = (View) getBridge().getWebView().getParent();
|
|
83
|
+
ViewCompat.requestApplyInsets(parentView);
|
|
84
|
+
if (insetHandlingEnabled) {
|
|
85
|
+
WindowInsetsCompat rootInsets = ViewCompat.getRootWindowInsets(parentView);
|
|
86
|
+
if (rootInsets != null) {
|
|
87
|
+
Insets safeArea = calcSafeAreaInsets(rootInsets);
|
|
88
|
+
injectSafeAreaCSS(safeArea.top, safeArea.right, safeArea.bottom, safeArea.left);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
80
91
|
}
|
|
81
92
|
}
|
|
82
93
|
);
|
|
@@ -103,8 +114,17 @@ public class SystemBars extends Plugin {
|
|
|
103
114
|
initSafeAreaCSSVariables();
|
|
104
115
|
|
|
105
116
|
getBridge().executeOnMainThread(() -> {
|
|
117
|
+
Window window = getActivity().getWindow();
|
|
118
|
+
WindowCompat.setDecorFitsSystemWindows(window, false);
|
|
119
|
+
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.VANILLA_ICE_CREAM) {
|
|
120
|
+
window.setNavigationBarColor(android.graphics.Color.TRANSPARENT);
|
|
121
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
|
122
|
+
window.setNavigationBarContrastEnforced(false);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
106
125
|
setStyle(style, "");
|
|
107
126
|
setHidden(hidden, "");
|
|
127
|
+
ViewCompat.requestApplyInsets((View) getBridge().getWebView().getParent());
|
|
108
128
|
});
|
|
109
129
|
}
|
|
110
130
|
|
|
@@ -144,43 +164,82 @@ public class SystemBars extends Plugin {
|
|
|
144
164
|
call.resolve();
|
|
145
165
|
}
|
|
146
166
|
|
|
167
|
+
@Override
|
|
168
|
+
protected void handleOnResume() {
|
|
169
|
+
super.handleOnResume();
|
|
170
|
+
getBridge().executeOnMainThread(() -> {
|
|
171
|
+
Window window = getActivity().getWindow();
|
|
172
|
+
WindowCompat.setDecorFitsSystemWindows(window, false);
|
|
173
|
+
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.VANILLA_ICE_CREAM) {
|
|
174
|
+
window.setNavigationBarColor(android.graphics.Color.TRANSPARENT);
|
|
175
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
|
176
|
+
window.setNavigationBarContrastEnforced(false);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
setStyle(currentGestureBarStyle, BAR_GESTURE_BAR);
|
|
180
|
+
setStyle(currentStatusBarStyle, BAR_STATUS_BAR);
|
|
181
|
+
ViewCompat.requestApplyInsets((View) getBridge().getWebView().getParent());
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
|
|
147
185
|
@JavascriptInterface
|
|
148
186
|
public void onDOMReady() {
|
|
149
187
|
getActivity().runOnUiThread(() -> {
|
|
150
188
|
this.bridge.getWebView().evaluateJavascript(viewportMetaJSFunction, (res) -> {
|
|
151
189
|
hasViewportCover = res.equals("true");
|
|
152
|
-
|
|
153
|
-
getBridge().getWebView().requestApplyInsets();
|
|
190
|
+
ViewCompat.requestApplyInsets((View) getBridge().getWebView().getParent());
|
|
154
191
|
});
|
|
155
192
|
});
|
|
156
193
|
}
|
|
157
194
|
|
|
158
195
|
private Insets calcSafeAreaInsets(WindowInsetsCompat insets) {
|
|
159
196
|
Insets safeArea = insets.getInsets(WindowInsetsCompat.Type.systemBars() | WindowInsetsCompat.Type.displayCutout());
|
|
160
|
-
|
|
197
|
+
|
|
198
|
+
int bottom = safeArea.bottom;
|
|
199
|
+
|
|
200
|
+
if (bottom == 0 && Build.VERSION.SDK_INT < Build.VERSION_CODES.R && safeArea.left == 0 && safeArea.right == 0) {
|
|
201
|
+
// skip if nav bar is on a side (landscape)
|
|
202
|
+
bottom = getNavBarHeightFromResources();
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
boolean imeVisible = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
|
|
206
|
+
? insets.isVisible(WindowInsetsCompat.Type.ime())
|
|
207
|
+
: insets.getInsets(WindowInsetsCompat.Type.ime()).bottom > 0;
|
|
208
|
+
|
|
209
|
+
if (imeVisible) {
|
|
161
210
|
return Insets.of(safeArea.left, safeArea.top, safeArea.right, 0);
|
|
162
211
|
}
|
|
163
|
-
return Insets.of(safeArea.left, safeArea.top, safeArea.right,
|
|
212
|
+
return Insets.of(safeArea.left, safeArea.top, safeArea.right, bottom);
|
|
164
213
|
}
|
|
165
214
|
|
|
166
|
-
|
|
167
|
-
|
|
215
|
+
// Only reached on API < 30 when the window reports no inset on any edge, so the
|
|
216
|
+
// choice is this approximation or a hardcoded 0. Lint rejects the internal
|
|
217
|
+
// resource because OEMs may change it; a wrong height still beats no inset.
|
|
218
|
+
@SuppressLint({ "InternalInsetResource", "DiscouragedApi" })
|
|
219
|
+
private int getNavBarHeightFromResources() {
|
|
220
|
+
if (!navBarVisible) return 0;
|
|
221
|
+
android.content.res.Resources res = getActivity().getResources();
|
|
222
|
+
int heightId = res.getIdentifier("navigation_bar_height", "dimen", "android");
|
|
223
|
+
return heightId > 0 ? res.getDimensionPixelSize(heightId) : 0;
|
|
224
|
+
}
|
|
168
225
|
|
|
169
|
-
|
|
226
|
+
private void initSafeAreaCSSVariables() {
|
|
227
|
+
if (insetHandlingEnabled) {
|
|
170
228
|
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);
|
|
229
|
+
WindowInsetsCompat insets = ViewCompat.getRootWindowInsets(v);
|
|
230
|
+
if (insets != null) {
|
|
231
|
+
Insets safeAreaInsets = calcSafeAreaInsets(insets);
|
|
232
|
+
injectSafeAreaCSS(safeAreaInsets.top, safeAreaInsets.right, safeAreaInsets.bottom, safeAreaInsets.left);
|
|
233
|
+
}
|
|
179
234
|
}
|
|
180
235
|
}
|
|
181
236
|
|
|
182
237
|
private void initWindowInsetsListener() {
|
|
183
238
|
ViewCompat.setOnApplyWindowInsetsListener((View) getBridge().getWebView().getParent(), (v, insets) -> {
|
|
239
|
+
// getRootWindowInsets() bypasses AppCompat's intermediate view consuming the bottom inset on API < 30
|
|
240
|
+
WindowInsetsCompat rawInsets = ViewCompat.getRootWindowInsets(v);
|
|
241
|
+
WindowInsetsCompat safeAreaSource = (rawInsets != null) ? rawInsets : insets;
|
|
242
|
+
|
|
184
243
|
boolean shouldPassthroughInsets = getWebViewMajorVersion() >= WEBVIEW_VERSION_WITH_SAFE_AREA_FIX && hasViewportCover;
|
|
185
244
|
|
|
186
245
|
Insets systemBarsInsets = insets.getInsets(WindowInsetsCompat.Type.systemBars() | WindowInsetsCompat.Type.displayCutout());
|
|
@@ -191,8 +250,10 @@ public class SystemBars extends Plugin {
|
|
|
191
250
|
// We need to correct for a possible shown IME
|
|
192
251
|
v.setPadding(0, 0, 0, keyboardVisible ? imeInsets.bottom : 0);
|
|
193
252
|
|
|
194
|
-
|
|
195
|
-
|
|
253
|
+
if (hasViewportCover && insetHandlingEnabled) {
|
|
254
|
+
Insets safeAreaInsets = calcSafeAreaInsets(safeAreaSource);
|
|
255
|
+
injectSafeAreaCSS(safeAreaInsets.top, safeAreaInsets.right, safeAreaInsets.bottom, safeAreaInsets.left);
|
|
256
|
+
}
|
|
196
257
|
|
|
197
258
|
return new WindowInsetsCompat.Builder(insets)
|
|
198
259
|
.setInsets(
|
|
@@ -207,15 +268,7 @@ public class SystemBars extends Plugin {
|
|
|
207
268
|
.build();
|
|
208
269
|
}
|
|
209
270
|
|
|
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
|
-
}
|
|
271
|
+
v.setPadding(0, 0, 0, keyboardVisible ? imeInsets.bottom : 0);
|
|
219
272
|
|
|
220
273
|
// Returning `WindowInsetsCompat.CONSUMED` breaks recalculation of safe area insets
|
|
221
274
|
// So we have to explicitly set insets to `0`
|
|
@@ -224,8 +277,10 @@ public class SystemBars extends Plugin {
|
|
|
224
277
|
.setInsets(WindowInsetsCompat.Type.systemBars() | WindowInsetsCompat.Type.displayCutout(), Insets.of(0, 0, 0, 0))
|
|
225
278
|
.build();
|
|
226
279
|
|
|
227
|
-
|
|
228
|
-
|
|
280
|
+
if (insetHandlingEnabled) {
|
|
281
|
+
Insets safeAreaInsets = calcSafeAreaInsets(safeAreaSource);
|
|
282
|
+
injectSafeAreaCSS(safeAreaInsets.top, safeAreaInsets.right, safeAreaInsets.bottom, safeAreaInsets.left);
|
|
283
|
+
}
|
|
229
284
|
|
|
230
285
|
return newInsets;
|
|
231
286
|
});
|
|
@@ -264,6 +319,7 @@ public class SystemBars extends Plugin {
|
|
|
264
319
|
}
|
|
265
320
|
|
|
266
321
|
private void setStyle(String style, String bar) {
|
|
322
|
+
String requestedStyle = style;
|
|
267
323
|
if (style.equals(STYLE_DEFAULT)) {
|
|
268
324
|
style = getStyleForTheme();
|
|
269
325
|
}
|
|
@@ -271,12 +327,12 @@ public class SystemBars extends Plugin {
|
|
|
271
327
|
Window window = getActivity().getWindow();
|
|
272
328
|
WindowInsetsControllerCompat windowInsetsControllerCompat = WindowCompat.getInsetsController(window, window.getDecorView());
|
|
273
329
|
if (bar.isEmpty() || bar.equals(BAR_STATUS_BAR)) {
|
|
274
|
-
currentStatusBarStyle =
|
|
330
|
+
currentStatusBarStyle = requestedStyle;
|
|
275
331
|
windowInsetsControllerCompat.setAppearanceLightStatusBars(!style.equals(STYLE_DARK));
|
|
276
332
|
}
|
|
277
333
|
|
|
278
334
|
if (bar.isEmpty() || bar.equals(BAR_GESTURE_BAR)) {
|
|
279
|
-
currentGestureBarStyle =
|
|
335
|
+
currentGestureBarStyle = requestedStyle;
|
|
280
336
|
windowInsetsControllerCompat.setAppearanceLightNavigationBars(!style.equals(STYLE_DARK));
|
|
281
337
|
}
|
|
282
338
|
|
|
@@ -293,6 +349,7 @@ public class SystemBars extends Plugin {
|
|
|
293
349
|
}
|
|
294
350
|
if (bar.isEmpty() || bar.equals(BAR_GESTURE_BAR)) {
|
|
295
351
|
windowInsetsControllerCompat.hide(WindowInsetsCompat.Type.navigationBars());
|
|
352
|
+
navBarVisible = false;
|
|
296
353
|
}
|
|
297
354
|
return;
|
|
298
355
|
}
|
|
@@ -302,6 +359,7 @@ public class SystemBars extends Plugin {
|
|
|
302
359
|
}
|
|
303
360
|
if (bar.isEmpty() || bar.equals(BAR_GESTURE_BAR)) {
|
|
304
361
|
windowInsetsControllerCompat.show(WindowInsetsCompat.Type.navigationBars());
|
|
362
|
+
navBarVisible = true;
|
|
305
363
|
}
|
|
306
364
|
}
|
|
307
365
|
|
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.11",
|
|
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)",
|