@capacitor/android 4.2.0 → 4.2.1-dev-20220921T210130.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -5,6 +5,33 @@
5
5
  const nativeBridge = (function (exports) {
6
6
  'use strict';
7
7
 
8
+ var ExceptionCode;
9
+ (function (ExceptionCode) {
10
+ /**
11
+ * API is not implemented.
12
+ *
13
+ * This usually means the API can't be used because it is not implemented for
14
+ * the current platform.
15
+ */
16
+ ExceptionCode["Unimplemented"] = "UNIMPLEMENTED";
17
+ /**
18
+ * API is not available.
19
+ *
20
+ * This means the API can't be used right now because:
21
+ * - it is currently missing a prerequisite, such as network connectivity
22
+ * - it requires a particular platform or browser version
23
+ */
24
+ ExceptionCode["Unavailable"] = "UNAVAILABLE";
25
+ })(ExceptionCode || (ExceptionCode = {}));
26
+ class CapacitorException extends Error {
27
+ constructor(message, code, data) {
28
+ super(message);
29
+ this.message = message;
30
+ this.code = code;
31
+ this.data = data;
32
+ }
33
+ }
34
+
8
35
  // For removing exports for iOS/Android, keep let for reassignment
9
36
  // eslint-disable-next-line
10
37
  let dummy = {};
@@ -241,6 +268,274 @@ const nativeBridge = (function (exports) {
241
268
  }
242
269
  return String(msg);
243
270
  };
271
+ /**
272
+ * Safely web decode a string value (inspired by js-cookie)
273
+ * @param str The string value to decode
274
+ */
275
+ const decode = (str) => str.replace(/(%[\dA-F]{2})+/gi, decodeURIComponent);
276
+ const platform = getPlatformId(win);
277
+ if (platform == 'android' || platform == 'ios') {
278
+ // patch document.cookie on Android/iOS
279
+ win.CapacitorCookiesDescriptor =
280
+ Object.getOwnPropertyDescriptor(Document.prototype, 'cookie') ||
281
+ Object.getOwnPropertyDescriptor(HTMLDocument.prototype, 'cookie');
282
+ let doPatchCookies = false;
283
+ // check if capacitor cookies is disabled before patching
284
+ if (platform === 'ios') {
285
+ // Use prompt to synchronously get capacitor cookies config.
286
+ // https://stackoverflow.com/questions/29249132/wkwebview-complex-communication-between-javascript-native-code/49474323#49474323
287
+ const payload = {
288
+ type: 'CapacitorCookies.isEnabled',
289
+ };
290
+ const isCookiesEnabled = prompt(JSON.stringify(payload));
291
+ if (isCookiesEnabled === 'true') {
292
+ doPatchCookies = true;
293
+ }
294
+ }
295
+ else if (typeof win.CapacitorCookiesAndroidInterface !== 'undefined') {
296
+ const isCookiesEnabled = win.CapacitorCookiesAndroidInterface.isEnabled();
297
+ if (isCookiesEnabled === true) {
298
+ doPatchCookies = true;
299
+ }
300
+ }
301
+ if (doPatchCookies) {
302
+ Object.defineProperty(document, 'cookie', {
303
+ get: function () {
304
+ if (platform === 'ios') {
305
+ // Use prompt to synchronously get cookies.
306
+ // https://stackoverflow.com/questions/29249132/wkwebview-complex-communication-between-javascript-native-code/49474323#49474323
307
+ const payload = {
308
+ type: 'CapacitorCookies',
309
+ };
310
+ const res = prompt(JSON.stringify(payload));
311
+ return res;
312
+ }
313
+ else if (typeof win.CapacitorCookiesAndroidInterface !== 'undefined') {
314
+ return win.CapacitorCookiesAndroidInterface.getCookies();
315
+ }
316
+ },
317
+ set: function (val) {
318
+ const cookiePairs = val.split(';');
319
+ for (const cookiePair of cookiePairs) {
320
+ const cookieKey = cookiePair.split('=')[0];
321
+ const cookieValue = cookiePair.split('=')[1];
322
+ if (null == cookieValue) {
323
+ continue;
324
+ }
325
+ cap.toNative('CapacitorCookies', 'setCookie', {
326
+ key: cookieKey,
327
+ value: decode(cookieValue),
328
+ });
329
+ }
330
+ },
331
+ });
332
+ }
333
+ // patch fetch / XHR on Android/iOS
334
+ // store original fetch & XHR functions
335
+ win.CapacitorWebFetch = window.fetch;
336
+ win.CapacitorWebXMLHttpRequest = {
337
+ abort: window.XMLHttpRequest.prototype.abort,
338
+ open: window.XMLHttpRequest.prototype.open,
339
+ send: window.XMLHttpRequest.prototype.send,
340
+ setRequestHeader: window.XMLHttpRequest.prototype.setRequestHeader,
341
+ };
342
+ let doPatchHttp = false;
343
+ // check if capacitor http is disabled before patching
344
+ if (platform === 'ios') {
345
+ // Use prompt to synchronously get capacitor http config.
346
+ // https://stackoverflow.com/questions/29249132/wkwebview-complex-communication-between-javascript-native-code/49474323#49474323
347
+ const payload = {
348
+ type: 'CapacitorHttp',
349
+ };
350
+ const isHttpEnabled = prompt(JSON.stringify(payload));
351
+ if (isHttpEnabled === 'true') {
352
+ doPatchHttp = true;
353
+ }
354
+ }
355
+ else if (typeof win.CapacitorHttpAndroidInterface !== 'undefined') {
356
+ const isHttpEnabled = win.CapacitorHttpAndroidInterface.isEnabled();
357
+ if (isHttpEnabled === true) {
358
+ doPatchHttp = true;
359
+ }
360
+ }
361
+ if (doPatchHttp) {
362
+ // fetch patch
363
+ window.fetch = async (resource, options) => {
364
+ if (resource.toString().startsWith('data:') ||
365
+ resource.toString().startsWith('blob:')) {
366
+ return win.CapacitorWebFetch(resource, options);
367
+ }
368
+ try {
369
+ // intercept request & pass to the bridge
370
+ const nativeResponse = await cap.nativePromise('CapacitorHttp', 'request', {
371
+ url: resource,
372
+ method: (options === null || options === void 0 ? void 0 : options.method) ? options.method : undefined,
373
+ data: (options === null || options === void 0 ? void 0 : options.body) ? options.body : undefined,
374
+ headers: (options === null || options === void 0 ? void 0 : options.headers) ? options.headers : undefined,
375
+ });
376
+ const data = typeof nativeResponse.data === 'string'
377
+ ? nativeResponse.data
378
+ : JSON.stringify(nativeResponse.data);
379
+ // intercept & parse response before returning
380
+ const response = new Response(data, {
381
+ headers: nativeResponse.headers,
382
+ status: nativeResponse.status,
383
+ });
384
+ return response;
385
+ }
386
+ catch (error) {
387
+ return Promise.reject(error);
388
+ }
389
+ };
390
+ // XHR event listeners
391
+ const addEventListeners = function () {
392
+ this.addEventListener('abort', function () {
393
+ if (typeof this.onabort === 'function')
394
+ this.onabort();
395
+ });
396
+ this.addEventListener('error', function () {
397
+ if (typeof this.onerror === 'function')
398
+ this.onerror();
399
+ });
400
+ this.addEventListener('load', function () {
401
+ if (typeof this.onload === 'function')
402
+ this.onload();
403
+ });
404
+ this.addEventListener('loadend', function () {
405
+ if (typeof this.onloadend === 'function')
406
+ this.onloadend();
407
+ });
408
+ this.addEventListener('loadstart', function () {
409
+ if (typeof this.onloadstart === 'function')
410
+ this.onloadstart();
411
+ });
412
+ this.addEventListener('readystatechange', function () {
413
+ if (typeof this.onreadystatechange === 'function')
414
+ this.onreadystatechange();
415
+ });
416
+ this.addEventListener('timeout', function () {
417
+ if (typeof this.ontimeout === 'function')
418
+ this.ontimeout();
419
+ });
420
+ };
421
+ // XHR patch abort
422
+ window.XMLHttpRequest.prototype.abort = function () {
423
+ this.readyState = 0;
424
+ this.dispatchEvent(new Event('abort'));
425
+ this.dispatchEvent(new Event('loadend'));
426
+ };
427
+ // XHR patch open
428
+ window.XMLHttpRequest.prototype.open = function (method, url) {
429
+ Object.defineProperties(this, {
430
+ _headers: {
431
+ value: {},
432
+ writable: true,
433
+ },
434
+ readyState: {
435
+ get: function () {
436
+ var _a;
437
+ return (_a = this._readyState) !== null && _a !== void 0 ? _a : 0;
438
+ },
439
+ set: function (val) {
440
+ this._readyState = val;
441
+ this.dispatchEvent(new Event('readystatechange'));
442
+ },
443
+ },
444
+ response: {
445
+ value: '',
446
+ writable: true,
447
+ },
448
+ responseText: {
449
+ value: '',
450
+ writable: true,
451
+ },
452
+ responseURL: {
453
+ value: '',
454
+ writable: true,
455
+ },
456
+ status: {
457
+ value: 0,
458
+ writable: true,
459
+ },
460
+ });
461
+ addEventListeners.call(this);
462
+ this._method = method;
463
+ this._url = url;
464
+ this.readyState = 1;
465
+ };
466
+ // XHR patch set request header
467
+ window.XMLHttpRequest.prototype.setRequestHeader = function (header, value) {
468
+ this._headers[header] = value;
469
+ };
470
+ // XHR patch send
471
+ window.XMLHttpRequest.prototype.send = function (body) {
472
+ try {
473
+ this.readyState = 2;
474
+ // intercept request & pass to the bridge
475
+ cap
476
+ .nativePromise('CapacitorHttp', 'request', {
477
+ url: this._url,
478
+ method: this._method,
479
+ data: body !== null ? body : undefined,
480
+ headers: this._headers,
481
+ })
482
+ .then((nativeResponse) => {
483
+ // intercept & parse response before returning
484
+ if (this.readyState == 2) {
485
+ this.dispatchEvent(new Event('loadstart'));
486
+ this._headers = nativeResponse.headers;
487
+ this.status = nativeResponse.status;
488
+ this.response = nativeResponse.data;
489
+ this.responseText =
490
+ typeof nativeResponse.data === 'string'
491
+ ? nativeResponse.data
492
+ : JSON.stringify(nativeResponse.data);
493
+ this.responseURL = nativeResponse.url;
494
+ this.readyState = 4;
495
+ this.dispatchEvent(new Event('load'));
496
+ this.dispatchEvent(new Event('loadend'));
497
+ }
498
+ })
499
+ .catch((error) => {
500
+ this.dispatchEvent(new Event('loadstart'));
501
+ this.status = error.status;
502
+ this._headers = error.headers;
503
+ this.response = error.data;
504
+ this.responseText = JSON.stringify(error.data);
505
+ this.responseURL = error.url;
506
+ this.readyState = 4;
507
+ this.dispatchEvent(new Event('error'));
508
+ this.dispatchEvent(new Event('loadend'));
509
+ });
510
+ }
511
+ catch (error) {
512
+ this.dispatchEvent(new Event('loadstart'));
513
+ this.status = 500;
514
+ this._headers = {};
515
+ this.response = error;
516
+ this.responseText = error.toString();
517
+ this.responseURL = this._url;
518
+ this.readyState = 4;
519
+ this.dispatchEvent(new Event('error'));
520
+ this.dispatchEvent(new Event('loadend'));
521
+ }
522
+ };
523
+ // XHR patch getAllResponseHeaders
524
+ window.XMLHttpRequest.prototype.getAllResponseHeaders = function () {
525
+ let returnString = '';
526
+ for (const key in this._headers) {
527
+ if (key != 'Set-Cookie') {
528
+ returnString += key + ': ' + this._headers[key] + '\r\n';
529
+ }
530
+ }
531
+ return returnString;
532
+ };
533
+ // XHR patch getResponseHeader
534
+ window.XMLHttpRequest.prototype.getResponseHeader = function (name) {
535
+ return this._headers[name];
536
+ };
537
+ }
538
+ }
244
539
  // patch window.console on iOS and store original console fns
245
540
  const isIos = getPlatformId(win) === 'ios';
246
541
  if (win.console && isIos) {
@@ -468,6 +763,7 @@ const nativeBridge = (function (exports) {
468
763
  });
469
764
  };
470
765
  cap.withPlugin = (_pluginId, _fn) => dummy;
766
+ cap.Exception = CapacitorException;
471
767
  initEvents(win, cap);
472
768
  initLegacyHandlers(win, cap);
473
769
  initVendor(win, cap);
@@ -351,7 +351,7 @@ public class Bridge {
351
351
  }
352
352
  }
353
353
 
354
- if (!url.toString().contains(appUrl) && !appAllowNavigationMask.matches(url.getHost())) {
354
+ if (!url.toString().startsWith(appUrl) && !appAllowNavigationMask.matches(url.getHost())) {
355
355
  try {
356
356
  Intent openIntent = new Intent(Intent.ACTION_VIEW, url);
357
357
  getContext().startActivity(openIntent);
@@ -556,7 +556,9 @@ public class Bridge {
556
556
  * Register our core Plugin APIs
557
557
  */
558
558
  private void registerAllPlugins() {
559
+ this.registerPlugin(com.getcapacitor.plugin.CapacitorCookies.class);
559
560
  this.registerPlugin(com.getcapacitor.plugin.WebView.class);
561
+ this.registerPlugin(com.getcapacitor.plugin.CapacitorHttp.class);
560
562
 
561
563
  for (Class<? extends Plugin> pluginClass : this.initialPlugins) {
562
564
  this.registerPlugin(pluginClass);
@@ -1299,6 +1301,7 @@ public class Bridge {
1299
1301
 
1300
1302
  public void setWebViewClient(BridgeWebViewClient client) {
1301
1303
  this.webViewClient = client;
1304
+ webView.setWebViewClient(client);
1302
1305
  }
1303
1306
 
1304
1307
  List<WebViewListener> getWebViewListeners() {
@@ -0,0 +1,65 @@
1
+ package com.getcapacitor;
2
+
3
+ import org.json.JSONException;
4
+
5
+ /**
6
+ * Represents a single user-data value of any type on the capacitor PluginCall object.
7
+ */
8
+ public class JSValue {
9
+
10
+ private final Object value;
11
+
12
+ /**
13
+ * @param call The capacitor plugin call, used for accessing the value safely.
14
+ * @param name The name of the property to access.
15
+ */
16
+ public JSValue(PluginCall call, String name) {
17
+ this.value = this.toValue(call, name);
18
+ }
19
+
20
+ /**
21
+ * Returns the coerced but uncasted underlying value.
22
+ */
23
+ public Object getValue() {
24
+ return this.value;
25
+ }
26
+
27
+ @Override
28
+ public String toString() {
29
+ return this.getValue().toString();
30
+ }
31
+
32
+ /**
33
+ * Returns the underlying value as a JSObject, or throwing if it cannot.
34
+ *
35
+ * @throws JSONException If the underlying value is not a JSObject.
36
+ */
37
+ public JSObject toJSObject() throws JSONException {
38
+ if (this.value instanceof JSObject) return (JSObject) this.value;
39
+ throw new JSONException("JSValue could not be coerced to JSObject.");
40
+ }
41
+
42
+ /**
43
+ * Returns the underlying value as a JSArray, or throwing if it cannot.
44
+ *
45
+ * @throws JSONException If the underlying value is not a JSArray.
46
+ */
47
+ public JSArray toJSArray() throws JSONException {
48
+ if (this.value instanceof JSArray) return (JSArray) this.value;
49
+ throw new JSONException("JSValue could not be coerced to JSArray.");
50
+ }
51
+
52
+ /**
53
+ * Returns the underlying value this object represents, coercing it into a capacitor-friendly object if supported.
54
+ */
55
+ private Object toValue(PluginCall call, String name) {
56
+ Object value = null;
57
+ value = call.getArray(name, null);
58
+ if (value != null) return value;
59
+ value = call.getObject(name, null);
60
+ if (value != null) return value;
61
+ value = call.getString(name, null);
62
+ if (value != null) return value;
63
+ return call.getData().opt(name);
64
+ }
65
+ }
@@ -29,6 +29,7 @@ import java.net.URLConnection;
29
29
  import java.nio.charset.StandardCharsets;
30
30
  import java.util.ArrayList;
31
31
  import java.util.HashMap;
32
+ import java.util.List;
32
33
  import java.util.Map;
33
34
 
34
35
  /**
@@ -360,9 +361,12 @@ public class WebViewLocalServer {
360
361
  String base64 = Base64.encodeToString(userInfoBytes, Base64.NO_WRAP);
361
362
  conn.setRequestProperty("Authorization", "Basic " + base64);
362
363
  }
363
- String cookie = conn.getHeaderField("Set-Cookie");
364
- if (cookie != null) {
365
- CookieManager.getInstance().setCookie(url, cookie);
364
+
365
+ List<String> cookies = conn.getHeaderFields().get("Set-Cookie");
366
+ if (cookies != null) {
367
+ for (String cookie : cookies) {
368
+ CookieManager.getInstance().setCookie(url, cookie);
369
+ }
366
370
  }
367
371
  InputStream responseStream = conn.getInputStream();
368
372
  responseStream = jsInjector.getInjectedStream(responseStream);
@@ -0,0 +1,172 @@
1
+ package com.getcapacitor.plugin;
2
+
3
+ import java.net.CookieManager;
4
+ import java.net.CookiePolicy;
5
+ import java.net.CookieStore;
6
+ import java.net.HttpCookie;
7
+ import java.net.URI;
8
+ import java.util.ArrayList;
9
+ import java.util.Collections;
10
+ import java.util.HashMap;
11
+ import java.util.List;
12
+ import java.util.Map;
13
+ import java.util.Objects;
14
+
15
+ public class CapacitorCookieManager extends CookieManager {
16
+
17
+ private final android.webkit.CookieManager webkitCookieManager;
18
+
19
+ /**
20
+ * Create a new cookie manager with the default cookie store and policy
21
+ */
22
+ public CapacitorCookieManager() {
23
+ this(null, null);
24
+ }
25
+
26
+ /**
27
+ * Create a new cookie manager with specified cookie store and cookie policy.
28
+ * @param store a {@code CookieStore} to be used by CookieManager. if {@code null}, cookie
29
+ * manager will use a default one, which is an in-memory CookieStore implementation.
30
+ * @param policy a {@code CookiePolicy} instance to be used by cookie manager as policy
31
+ * callback. if {@code null}, ACCEPT_ORIGINAL_SERVER will be used.
32
+ */
33
+ public CapacitorCookieManager(CookieStore store, CookiePolicy policy) {
34
+ super(store, policy);
35
+ webkitCookieManager = android.webkit.CookieManager.getInstance();
36
+ }
37
+
38
+ /**
39
+ * Gets the cookies for the given URL.
40
+ * @param url the URL for which the cookies are requested
41
+ * @return value the cookies as a string, using the format of the 'Cookie' HTTP request header
42
+ */
43
+ public String getCookieString(String url) {
44
+ return webkitCookieManager.getCookie(url);
45
+ }
46
+
47
+ /**
48
+ * Gets a cookie value for the given URL and key.
49
+ * @param url the URL for which the cookies are requested
50
+ * @param key the key of the cookie to search for
51
+ * @return the {@code HttpCookie} value of the cookie at the key,
52
+ * otherwise it will return a new empty {@code HttpCookie}
53
+ */
54
+ public HttpCookie getCookie(String url, String key) {
55
+ HttpCookie[] cookies = getCookies(url);
56
+ for (HttpCookie cookie : cookies) {
57
+ if (cookie.getName().equals(key)) {
58
+ return cookie;
59
+ }
60
+ }
61
+
62
+ return null;
63
+ }
64
+
65
+ /**
66
+ * Gets an array of {@code HttpCookie} given a URL.
67
+ * @param url the URL for which the cookies are requested
68
+ * @return an {@code HttpCookie} array of non-expired cookies
69
+ */
70
+ public HttpCookie[] getCookies(String url) {
71
+ try {
72
+ ArrayList<HttpCookie> cookieList = new ArrayList<>();
73
+ String cookieString = getCookieString(url);
74
+ if (cookieString != null) {
75
+ String[] singleCookie = cookieString.split(";");
76
+ for (String c : singleCookie) {
77
+ HttpCookie parsed = HttpCookie.parse(c).get(0);
78
+ parsed.setValue(parsed.getValue());
79
+ cookieList.add(parsed);
80
+ }
81
+ }
82
+ HttpCookie[] cookies = new HttpCookie[cookieList.size()];
83
+ return cookieList.toArray(cookies);
84
+ } catch (Exception ex) {
85
+ return new HttpCookie[0];
86
+ }
87
+ }
88
+
89
+ /**
90
+ * Sets a cookie for the given URL. Any existing cookie with the same host, path and name will
91
+ * be replaced with the new cookie. The cookie being set will be ignored if it is expired.
92
+ * @param url the URL for which the cookie is to be set
93
+ * @param value the cookie as a string, using the format of the 'Set-Cookie' HTTP response header
94
+ */
95
+ public void setCookie(String url, String value) {
96
+ webkitCookieManager.setCookie(url, value);
97
+ flush();
98
+ }
99
+
100
+ /**
101
+ * Sets a cookie for the given URL. Any existing cookie with the same host, path and name will
102
+ * be replaced with the new cookie. The cookie being set will be ignored if it is expired.
103
+ * @param url the URL for which the cookie is to be set
104
+ * @param key the {@code HttpCookie} name to use for lookup
105
+ * @param value the value of the {@code HttpCookie} given a key
106
+ */
107
+ public void setCookie(String url, String key, String value) {
108
+ String cookieValue = key + "=" + value;
109
+ setCookie(url, cookieValue);
110
+ }
111
+
112
+ /**
113
+ * Removes all cookies. This method is asynchronous.
114
+ */
115
+ public void removeAllCookies() {
116
+ webkitCookieManager.removeAllCookies(null);
117
+ flush();
118
+ }
119
+
120
+ /**
121
+ * Ensures all cookies currently accessible through the getCookie API are written to persistent
122
+ * storage. This call will block the caller until it is done and may perform I/O.
123
+ */
124
+ public void flush() {
125
+ webkitCookieManager.flush();
126
+ }
127
+
128
+ @Override
129
+ public void put(URI uri, Map<String, List<String>> responseHeaders) {
130
+ // make sure our args are valid
131
+ if ((uri == null) || (responseHeaders == null)) return;
132
+
133
+ // save our url once
134
+ String url = uri.toString();
135
+
136
+ // go over the headers
137
+ for (String headerKey : responseHeaders.keySet()) {
138
+ // ignore headers which aren't cookie related
139
+ if ((headerKey == null) || !(headerKey.equalsIgnoreCase("Set-Cookie2") || headerKey.equalsIgnoreCase("Set-Cookie"))) continue;
140
+
141
+ // process each of the headers
142
+ for (String headerValue : Objects.requireNonNull(responseHeaders.get(headerKey))) {
143
+ setCookie(url, headerValue);
144
+ }
145
+ }
146
+ }
147
+
148
+ @Override
149
+ public Map<String, List<String>> get(URI uri, Map<String, List<String>> requestHeaders) {
150
+ // make sure our args are valid
151
+ if ((uri == null) || (requestHeaders == null)) throw new IllegalArgumentException("Argument is null");
152
+
153
+ // save our url once
154
+ String url = uri.toString();
155
+
156
+ // prepare our response
157
+ Map<String, List<String>> res = new HashMap<>();
158
+
159
+ // get the cookie
160
+ String cookie = getCookieString(url);
161
+
162
+ // return it
163
+ if (cookie != null) res.put("Cookie", Collections.singletonList(cookie));
164
+ return res;
165
+ }
166
+
167
+ @Override
168
+ public CookieStore getCookieStore() {
169
+ // we don't want anyone to work with this cookie store directly
170
+ throw new UnsupportedOperationException();
171
+ }
172
+ }