@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.
- package/capacitor/src/main/assets/native-bridge.js +296 -0
- package/capacitor/src/main/java/com/getcapacitor/Bridge.java +4 -1
- package/capacitor/src/main/java/com/getcapacitor/JSValue.java +65 -0
- package/capacitor/src/main/java/com/getcapacitor/WebViewLocalServer.java +7 -3
- package/capacitor/src/main/java/com/getcapacitor/plugin/CapacitorCookieManager.java +172 -0
- package/capacitor/src/main/java/com/getcapacitor/plugin/CapacitorCookies.java +129 -0
- package/capacitor/src/main/java/com/getcapacitor/plugin/CapacitorHttp.java +81 -0
- package/capacitor/src/main/java/com/getcapacitor/plugin/util/CapacitorHttpUrlConnection.java +381 -0
- package/capacitor/src/main/java/com/getcapacitor/plugin/util/HttpRequestHandler.java +410 -0
- package/capacitor/src/main/java/com/getcapacitor/plugin/util/ICapacitorHttpUrlConnection.java +15 -0
- package/capacitor/src/main/java/com/getcapacitor/plugin/util/MimeType.java +17 -0
- package/package.json +3 -3
|
@@ -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().
|
|
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
|
-
|
|
364
|
-
|
|
365
|
-
|
|
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
|
+
}
|