@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.
@@ -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 ReadableStream || body instanceof Uint8Array) {
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 = new TextDecoder().decode(encodedData);
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 = 'image';
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
- if (nativeResponse.status === 204) {
545
- data = null;
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(), Collections.singleton(allowedOrigin));
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
- postMessage(message.getData());
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 totalRange = responseStream.available();
351
+ int totalSize = responseStream.available();
352
352
  String[] parts = rangeString.split("=");
353
353
  String[] streamParts = parts[1].split("-");
354
- String fromRange = streamParts[0];
355
- int range = totalRange - 1;
356
- if (streamParts.length > 1) {
357
- range = Integer.parseInt(streamParts[1]);
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 + "-" + range + "/" + totalRange);
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 boolean insetHandlingEnabled = true;
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().requestApplyInsets();
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 insetsHandling = getConfig().getString("insetsHandling", "css");
98
- if (insetsHandling.equals(INSETS_HANDLING_DISABLE)) {
99
- insetHandlingEnabled = false;
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
- getActivity().runOnUiThread(() -> {
150
- this.bridge.getWebView().evaluateJavascript(viewportMetaJSFunction, (res) -> {
151
- hasViewportCover = res.equals("true");
152
-
153
- getBridge().getWebView().requestApplyInsets();
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
- if (insets.isVisible(WindowInsetsCompat.Type.ime())) {
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, safeArea.bottom);
222
+ return Insets.of(safeArea.left, safeArea.top, safeArea.right, bottom);
164
223
  }
165
224
 
166
- private void initSafeAreaCSSVariables() {
167
- WindowInsetsCompat insets;
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
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM) {
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
- } else {
173
- insets = WindowInsetsCompat.CONSUMED;
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
- Insets safeAreaInsets = calcSafeAreaInsets(insets);
195
- injectSafeAreaCSS(safeAreaInsets.top, safeAreaInsets.right, safeAreaInsets.bottom, safeAreaInsets.left);
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
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM) {
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
- Insets safeAreaInsets = calcSafeAreaInsets(newInsets);
228
- injectSafeAreaCSS(safeAreaInsets.top, safeAreaInsets.right, safeAreaInsets.bottom, safeAreaInsets.left);
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 = style;
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 = style;
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() || bar.equals(BAR_STATUS_BAR)) {
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() || bar.equals(BAR_STATUS_BAR)) {
373
+ if (bar.isEmpty()) {
301
374
  windowInsetsControllerCompat.show(WindowInsetsCompat.Type.systemBars());
302
- }
303
- if (bar.isEmpty() || bar.equals(BAR_GESTURE_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
 
@@ -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
- try (DataOutputStream os = new DataOutputStream(connection.getOutputStream())) {
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
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
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.10",
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)",