@capgo/inappbrowser 6.13.2 → 6.14.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/README.md +20 -21
- package/android/src/main/java/ee/forgr/capacitor_inappbrowser/InAppBrowserPlugin.java +118 -80
- package/android/src/main/java/ee/forgr/capacitor_inappbrowser/Options.java +9 -0
- package/android/src/main/java/ee/forgr/capacitor_inappbrowser/WebViewDialog.java +739 -37
- package/dist/docs.json +54 -62
- package/dist/esm/definitions.d.ts +12 -13
- package/dist/esm/definitions.js.map +1 -1
- package/dist/plugin.cjs.js.map +1 -1
- package/dist/plugin.js.map +1 -1
- package/ios/Plugin/InAppBrowserPlugin.swift +38 -22
- package/ios/Plugin/WKWebViewController.swift +16 -107
- package/package.json +1 -1
- package/ios/Plugin/Assets.xcassets/Contents.json +0 -6
package/README.md
CHANGED
|
@@ -107,7 +107,7 @@ Add the following to your `Info.plist` file:
|
|
|
107
107
|
open(options: OpenOptions) => Promise<any>
|
|
108
108
|
```
|
|
109
109
|
|
|
110
|
-
Open url in a new window fullscreen
|
|
110
|
+
Open url in a new window fullscreen, on android it use chrome custom tabs, on ios it use SFSafariViewController
|
|
111
111
|
|
|
112
112
|
| Param | Type |
|
|
113
113
|
| ------------- | --------------------------------------------------- |
|
|
@@ -210,7 +210,7 @@ Open url in a new webview with toolbars, and enhanced capabilities, like camera
|
|
|
210
210
|
JavaScript Interface:
|
|
211
211
|
When you open a webview with this method, a JavaScript interface is automatically injected that provides:
|
|
212
212
|
- `window.mobileApp.close()`: Closes the webview from JavaScript
|
|
213
|
-
- `window.mobileApp.postMessage(
|
|
213
|
+
- `window.mobileApp.postMessage({detail: {message: 'myMessage'}})`: Sends a message from the webview to the app, detail object is the data you want to send to the webview
|
|
214
214
|
|
|
215
215
|
| Param | Type |
|
|
216
216
|
| ------------- | ----------------------------------------------------------------- |
|
|
@@ -244,7 +244,7 @@ Injects JavaScript code into the InAppBrowser window.
|
|
|
244
244
|
postMessage(options: { detail: Record<string, any>; }) => Promise<void>
|
|
245
245
|
```
|
|
246
246
|
|
|
247
|
-
Sends an event to the webview(inappbrowser). you can listen to this event in the inappbrowser JS with window.
|
|
247
|
+
Sends an event to the webview(inappbrowser). you can listen to this event in the inappbrowser JS with window.addEventListener("messageFromNative", listenerFunc: (event: <a href="#record">Record</a><string, any>) => void)
|
|
248
248
|
detail is the data you want to send to the webview, it's a requirement of Capacitor we cannot send direct objects
|
|
249
249
|
Your object has to be serializable to JSON, so no functions or other non-JSON-serializable types are allowed.
|
|
250
250
|
|
|
@@ -439,24 +439,11 @@ Reload the current web page.
|
|
|
439
439
|
|
|
440
440
|
#### OpenOptions
|
|
441
441
|
|
|
442
|
-
| Prop | Type
|
|
443
|
-
| ---------------------------- |
|
|
444
|
-
| **`url`** | <code>string</code>
|
|
445
|
-
| **`
|
|
446
|
-
| **`
|
|
447
|
-
| **`isPresentAfterPageLoad`** | <code>boolean</code> | if true, the browser will be presented after the page is loaded, if false, the browser will be presented immediately. | 0.1.0 |
|
|
448
|
-
| **`preventDeeplink`** | <code>boolean</code> | if true the deeplink will not be opened, if false the deeplink will be opened when clicked on the link | 0.1.0 |
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
#### Headers
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
#### Credentials
|
|
455
|
-
|
|
456
|
-
| Prop | Type |
|
|
457
|
-
| -------------- | ------------------- |
|
|
458
|
-
| **`username`** | <code>string</code> |
|
|
459
|
-
| **`password`** | <code>string</code> |
|
|
442
|
+
| Prop | Type | Description | Since |
|
|
443
|
+
| ---------------------------- | -------------------- | --------------------------------------------------------------------------------------------------------------------- | ----- |
|
|
444
|
+
| **`url`** | <code>string</code> | Target URL to load. | 0.1.0 |
|
|
445
|
+
| **`isPresentAfterPageLoad`** | <code>boolean</code> | if true, the browser will be presented after the page is loaded, if false, the browser will be presented immediately. | 0.1.0 |
|
|
446
|
+
| **`preventDeeplink`** | <code>boolean</code> | if true the deeplink will not be opened, if false the deeplink will be opened when clicked on the link | 0.1.0 |
|
|
460
447
|
|
|
461
448
|
|
|
462
449
|
#### ClearCookieOptions
|
|
@@ -517,6 +504,18 @@ Reload the current web page.
|
|
|
517
504
|
| **`proxyRequests`** | <code>string</code> | proxyRequests is a regex expression. Please see [this pr](https://github.com/Cap-go/capacitor-inappbrowser/pull/222) for more info. (Android only) | | 6.9.0 |
|
|
518
505
|
| **`buttonNearDone`** | <code>{ ios: { iconType: 'sf-symbol' \| 'asset'; icon: string; }; android: { iconType: 'asset' \| 'vector'; icon: string; width?: number; height?: number; }; }</code> | buttonNearDone allows for a creation of a custom button near the done/close button. The button is only shown when toolbarType is not "activity", "navigation", or "blank". For Android: - iconType must be "asset" - icon path should be in the public folder (e.g. "monkey.svg") - width and height are optional, defaults to 48dp - button is positioned at the end of toolbar with 8dp margin For iOS: - iconType can be "sf-symbol" or "asset" - for sf-symbol, icon should be the symbol name - for asset, icon should be the asset name | | 6.7.0 |
|
|
519
506
|
| **`textZoom`** | <code>number</code> | textZoom: sets the text zoom of the page in percent. Allows users to increase or decrease the text size for better readability. | <code>100</code> | 7.6.0 |
|
|
507
|
+
| **`preventDeeplink`** | <code>boolean</code> | preventDeeplink: if true, the deeplink will not be opened, if false the deeplink will be opened when clicked on the link. on IOS each schema need to be added to info.plist file under LSApplicationQueriesSchemes when false to make it work. | <code>false</code> | 0.1.0 |
|
|
508
|
+
|
|
509
|
+
|
|
510
|
+
#### Headers
|
|
511
|
+
|
|
512
|
+
|
|
513
|
+
#### Credentials
|
|
514
|
+
|
|
515
|
+
| Prop | Type |
|
|
516
|
+
| -------------- | ------------------- |
|
|
517
|
+
| **`username`** | <code>string</code> |
|
|
518
|
+
| **`password`** | <code>string</code> |
|
|
520
519
|
|
|
521
520
|
|
|
522
521
|
#### DisclaimerOptions
|
|
@@ -12,6 +12,9 @@ import android.text.TextUtils;
|
|
|
12
12
|
import android.util.Log;
|
|
13
13
|
import android.webkit.CookieManager;
|
|
14
14
|
import android.webkit.PermissionRequest;
|
|
15
|
+
import androidx.activity.result.ActivityResult;
|
|
16
|
+
import androidx.activity.result.ActivityResultLauncher;
|
|
17
|
+
import androidx.activity.result.contract.ActivityResultContracts;
|
|
15
18
|
import androidx.annotation.NonNull;
|
|
16
19
|
import androidx.browser.customtabs.CustomTabsCallback;
|
|
17
20
|
import androidx.browser.customtabs.CustomTabsClient;
|
|
@@ -29,7 +32,6 @@ import com.getcapacitor.annotation.PermissionCallback;
|
|
|
29
32
|
import java.lang.reflect.Field;
|
|
30
33
|
import java.util.ArrayList;
|
|
31
34
|
import java.util.Iterator;
|
|
32
|
-
import java.util.Objects;
|
|
33
35
|
import java.util.regex.Pattern;
|
|
34
36
|
import java.util.regex.PatternSyntaxException;
|
|
35
37
|
import org.json.JSONException;
|
|
@@ -62,6 +64,54 @@ public class InAppBrowserPlugin
|
|
|
62
64
|
|
|
63
65
|
private PermissionRequest currentPermissionRequest;
|
|
64
66
|
|
|
67
|
+
private ActivityResultLauncher<Intent> fileChooserLauncher;
|
|
68
|
+
|
|
69
|
+
@Override
|
|
70
|
+
public void load() {
|
|
71
|
+
super.load();
|
|
72
|
+
fileChooserLauncher = getActivity()
|
|
73
|
+
.registerForActivityResult(
|
|
74
|
+
new ActivityResultContracts.StartActivityForResult(),
|
|
75
|
+
this::handleFileChooserResult
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
private void handleFileChooserResult(ActivityResult result) {
|
|
80
|
+
if (webViewDialog != null && webViewDialog.mFilePathCallback != null) {
|
|
81
|
+
Uri[] results = null;
|
|
82
|
+
Intent data = result.getData();
|
|
83
|
+
|
|
84
|
+
if (result.getResultCode() == Activity.RESULT_OK) {
|
|
85
|
+
// Handle camera capture result
|
|
86
|
+
if (
|
|
87
|
+
webViewDialog.tempCameraUri != null &&
|
|
88
|
+
(data == null || data.getData() == null)
|
|
89
|
+
) {
|
|
90
|
+
results = new Uri[] { webViewDialog.tempCameraUri };
|
|
91
|
+
}
|
|
92
|
+
// Handle regular file picker result
|
|
93
|
+
else if (data != null) {
|
|
94
|
+
if (data.getClipData() != null) {
|
|
95
|
+
// Handle multiple files
|
|
96
|
+
int count = data.getClipData().getItemCount();
|
|
97
|
+
results = new Uri[count];
|
|
98
|
+
for (int i = 0; i < count; i++) {
|
|
99
|
+
results[i] = data.getClipData().getItemAt(i).getUri();
|
|
100
|
+
}
|
|
101
|
+
} else if (data.getData() != null) {
|
|
102
|
+
// Handle single file
|
|
103
|
+
results = new Uri[] { data.getData() };
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Send the result to WebView and clean up
|
|
109
|
+
webViewDialog.mFilePathCallback.onReceiveValue(results);
|
|
110
|
+
webViewDialog.mFilePathCallback = null;
|
|
111
|
+
webViewDialog.tempCameraUri = null;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
65
115
|
public void handleMicrophonePermissionRequest(PermissionRequest request) {
|
|
66
116
|
this.currentPermissionRequest = request;
|
|
67
117
|
if (getPermissionState("microphone") != PermissionState.GRANTED) {
|
|
@@ -126,57 +176,6 @@ public class InAppBrowserPlugin
|
|
|
126
176
|
}
|
|
127
177
|
}
|
|
128
178
|
|
|
129
|
-
@Override
|
|
130
|
-
public void handleOnActivityResult(
|
|
131
|
-
int requestCode,
|
|
132
|
-
int resultCode,
|
|
133
|
-
Intent data
|
|
134
|
-
) {
|
|
135
|
-
super.handleOnActivityResult(requestCode, resultCode, data);
|
|
136
|
-
|
|
137
|
-
// Handle file chooser result
|
|
138
|
-
if (requestCode == WebViewDialog.FILE_CHOOSER_REQUEST_CODE) {
|
|
139
|
-
if (webViewDialog != null && webViewDialog.mFilePathCallback != null) {
|
|
140
|
-
Uri[] results = null;
|
|
141
|
-
|
|
142
|
-
// Check if response is valid
|
|
143
|
-
if (resultCode == Activity.RESULT_OK) {
|
|
144
|
-
if (data != null) {
|
|
145
|
-
// Get the data
|
|
146
|
-
if (data.getClipData() != null) {
|
|
147
|
-
// Handle multiple files
|
|
148
|
-
int count = data.getClipData().getItemCount();
|
|
149
|
-
results = new Uri[count];
|
|
150
|
-
for (int i = 0; i < count; i++) {
|
|
151
|
-
results[i] = data.getClipData().getItemAt(i).getUri();
|
|
152
|
-
}
|
|
153
|
-
} else if (data.getData() != null) {
|
|
154
|
-
// Handle single file
|
|
155
|
-
results = new Uri[] { data.getData() };
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
// Send the result to WebView
|
|
161
|
-
webViewDialog.mFilePathCallback.onReceiveValue(results);
|
|
162
|
-
webViewDialog.mFilePathCallback = null;
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
@PermissionCallback
|
|
168
|
-
private void cameraPermissionCallback() {
|
|
169
|
-
if (getPermissionState("camera") == PermissionState.GRANTED) {
|
|
170
|
-
grantCameraPermission();
|
|
171
|
-
} else {
|
|
172
|
-
if (currentPermissionRequest != null) {
|
|
173
|
-
currentPermissionRequest.deny();
|
|
174
|
-
currentPermissionRequest = null;
|
|
175
|
-
}
|
|
176
|
-
// Handle the case where permission was not granted
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
|
|
180
179
|
@PermissionCallback
|
|
181
180
|
private void cameraPermissionCallback(PluginCall call) {
|
|
182
181
|
if (getPermissionState("camera") == PermissionState.GRANTED) {
|
|
@@ -194,7 +193,23 @@ public class InAppBrowserPlugin
|
|
|
194
193
|
currentPermissionRequest.deny();
|
|
195
194
|
currentPermissionRequest = null;
|
|
196
195
|
}
|
|
197
|
-
|
|
196
|
+
|
|
197
|
+
// Reject only if there's a call - could be null for WebViewDialog flow
|
|
198
|
+
if (call != null) {
|
|
199
|
+
call.reject("Camera permission is required");
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
@PermissionCallback
|
|
205
|
+
private void cameraPermissionCallback() {
|
|
206
|
+
if (getPermissionState("camera") == PermissionState.GRANTED) {
|
|
207
|
+
grantCameraPermission();
|
|
208
|
+
} else {
|
|
209
|
+
if (currentPermissionRequest != null) {
|
|
210
|
+
currentPermissionRequest.deny();
|
|
211
|
+
currentPermissionRequest = null;
|
|
212
|
+
}
|
|
198
213
|
}
|
|
199
214
|
}
|
|
200
215
|
|
|
@@ -210,15 +225,16 @@ public class InAppBrowserPlugin
|
|
|
210
225
|
CustomTabsServiceConnection connection = new CustomTabsServiceConnection() {
|
|
211
226
|
@Override
|
|
212
227
|
public void onCustomTabsServiceConnected(
|
|
213
|
-
|
|
228
|
+
ComponentName name,
|
|
214
229
|
CustomTabsClient client
|
|
215
230
|
) {
|
|
216
231
|
customTabsClient = client;
|
|
217
|
-
client.warmup(0);
|
|
218
232
|
}
|
|
219
233
|
|
|
220
234
|
@Override
|
|
221
|
-
public void onServiceDisconnected(ComponentName name) {
|
|
235
|
+
public void onServiceDisconnected(ComponentName name) {
|
|
236
|
+
customTabsClient = null;
|
|
237
|
+
}
|
|
222
238
|
};
|
|
223
239
|
|
|
224
240
|
@PluginMethod
|
|
@@ -252,9 +268,17 @@ public class InAppBrowserPlugin
|
|
|
252
268
|
@PluginMethod
|
|
253
269
|
public void open(PluginCall call) {
|
|
254
270
|
String url = call.getString("url");
|
|
271
|
+
if (url == null) {
|
|
272
|
+
call.reject("URL is required");
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
255
275
|
|
|
256
276
|
// get the deeplink prevention, if provided
|
|
257
|
-
Boolean preventDeeplink = call.getBoolean("preventDeeplink",
|
|
277
|
+
Boolean preventDeeplink = call.getBoolean("preventDeeplink", false);
|
|
278
|
+
Boolean isPresentAfterPageLoad = call.getBoolean(
|
|
279
|
+
"isPresentAfterPageLoad",
|
|
280
|
+
false
|
|
281
|
+
);
|
|
258
282
|
|
|
259
283
|
if (url == null || TextUtils.isEmpty(url)) {
|
|
260
284
|
call.reject("Invalid URL");
|
|
@@ -275,7 +299,7 @@ public class InAppBrowserPlugin
|
|
|
275
299
|
this.getHeaders(call)
|
|
276
300
|
);
|
|
277
301
|
|
|
278
|
-
if (preventDeeplink !=
|
|
302
|
+
if (preventDeeplink != false) {
|
|
279
303
|
String browserPackageName = "";
|
|
280
304
|
Intent browserIntent = new Intent(
|
|
281
305
|
Intent.ACTION_VIEW,
|
|
@@ -294,6 +318,10 @@ public class InAppBrowserPlugin
|
|
|
294
318
|
}
|
|
295
319
|
}
|
|
296
320
|
|
|
321
|
+
if (isPresentAfterPageLoad) {
|
|
322
|
+
tabsIntent.intent.putExtra("isPresentAfterPageLoad", true);
|
|
323
|
+
}
|
|
324
|
+
|
|
297
325
|
tabsIntent.launchUrl(getContext(), Uri.parse(url));
|
|
298
326
|
|
|
299
327
|
call.resolve();
|
|
@@ -473,21 +501,7 @@ public class InAppBrowserPlugin
|
|
|
473
501
|
Log.e("InAppBrowser", "Vector resource not found: " + icon);
|
|
474
502
|
// List available drawable resources to help debugging
|
|
475
503
|
try {
|
|
476
|
-
|
|
477
|
-
StringBuilder availableResources = new StringBuilder(
|
|
478
|
-
"Available resources: "
|
|
479
|
-
);
|
|
480
|
-
for (int i = 0; i < Math.min(10, drawables.length); i++) {
|
|
481
|
-
availableResources
|
|
482
|
-
.append(drawables[i].getName())
|
|
483
|
-
.append(", ");
|
|
484
|
-
}
|
|
485
|
-
if (drawables.length > 10) {
|
|
486
|
-
availableResources
|
|
487
|
-
.append("... (")
|
|
488
|
-
.append(drawables.length - 10)
|
|
489
|
-
.append(" more)");
|
|
490
|
-
}
|
|
504
|
+
final StringBuilder availableResources = getStringBuilder();
|
|
491
505
|
Log.d("InAppBrowser", availableResources.toString());
|
|
492
506
|
} catch (Exception e) {
|
|
493
507
|
Log.e(
|
|
@@ -531,10 +545,13 @@ public class InAppBrowserPlugin
|
|
|
531
545
|
options.setPreShowScript(call.getString("preShowScript", null));
|
|
532
546
|
options.setShareSubject(call.getString("shareSubject", null));
|
|
533
547
|
options.setToolbarType(call.getString("toolbarType", ""));
|
|
548
|
+
options.setPreventDeeplink(
|
|
549
|
+
Boolean.TRUE.equals(call.getBoolean("preventDeeplink", false))
|
|
550
|
+
);
|
|
534
551
|
|
|
535
552
|
// Validate preShowScript requires isPresentAfterPageLoad
|
|
536
553
|
if (
|
|
537
|
-
call.
|
|
554
|
+
call.getData().has("preShowScript") &&
|
|
538
555
|
!Boolean.TRUE.equals(call.getBoolean("isPresentAfterPageLoad", false))
|
|
539
556
|
) {
|
|
540
557
|
call.reject("preShowScript requires isPresentAfterPageLoad to be true");
|
|
@@ -553,10 +570,10 @@ public class InAppBrowserPlugin
|
|
|
553
570
|
} else {
|
|
554
571
|
// Reject if closeModal is false but closeModal options are provided
|
|
555
572
|
if (
|
|
556
|
-
call.
|
|
557
|
-
call.
|
|
558
|
-
call.
|
|
559
|
-
call.
|
|
573
|
+
call.getData().has("closeModalTitle") ||
|
|
574
|
+
call.getData().has("closeModalDescription") ||
|
|
575
|
+
call.getData().has("closeModalOk") ||
|
|
576
|
+
call.getData().has("closeModalCancel")
|
|
560
577
|
) {
|
|
561
578
|
call.reject("closeModal options require closeModal to be true");
|
|
562
579
|
return;
|
|
@@ -565,13 +582,16 @@ public class InAppBrowserPlugin
|
|
|
565
582
|
}
|
|
566
583
|
|
|
567
584
|
// Validate shareDisclaimer requires shareSubject
|
|
568
|
-
if (
|
|
585
|
+
if (
|
|
586
|
+
call.getData().has("shareDisclaimer") &&
|
|
587
|
+
!call.getData().has("shareSubject")
|
|
588
|
+
) {
|
|
569
589
|
call.reject("shareDisclaimer requires shareSubject to be provided");
|
|
570
590
|
return;
|
|
571
591
|
}
|
|
572
592
|
|
|
573
593
|
// Validate buttonNearDone compatibility with toolbar type
|
|
574
|
-
if (call.
|
|
594
|
+
if (call.getData().has("buttonNearDone")) {
|
|
575
595
|
String toolbarType = options.getToolbarType();
|
|
576
596
|
if (
|
|
577
597
|
TextUtils.equals(toolbarType, "activity") ||
|
|
@@ -695,6 +715,24 @@ public class InAppBrowserPlugin
|
|
|
695
715
|
);
|
|
696
716
|
}
|
|
697
717
|
|
|
718
|
+
@NonNull
|
|
719
|
+
private static StringBuilder getStringBuilder() {
|
|
720
|
+
Field[] drawables = R.drawable.class.getFields();
|
|
721
|
+
StringBuilder availableResources = new StringBuilder(
|
|
722
|
+
"Available resources: "
|
|
723
|
+
);
|
|
724
|
+
for (int i = 0; i < Math.min(10, drawables.length); i++) {
|
|
725
|
+
availableResources.append(drawables[i].getName()).append(", ");
|
|
726
|
+
}
|
|
727
|
+
if (drawables.length > 10) {
|
|
728
|
+
availableResources
|
|
729
|
+
.append("... (")
|
|
730
|
+
.append(drawables.length - 10)
|
|
731
|
+
.append(" more)");
|
|
732
|
+
}
|
|
733
|
+
return availableResources;
|
|
734
|
+
}
|
|
735
|
+
|
|
698
736
|
@PluginMethod
|
|
699
737
|
public void postMessage(PluginCall call) {
|
|
700
738
|
if (webViewDialog == null) {
|
|
@@ -174,6 +174,7 @@ public class Options {
|
|
|
174
174
|
private Pattern proxyRequestsPattern = null;
|
|
175
175
|
private boolean materialPicker = false;
|
|
176
176
|
private int textZoom = 100; // Default text zoom is 100%
|
|
177
|
+
private boolean preventDeeplink = false;
|
|
177
178
|
|
|
178
179
|
public int getTextZoom() {
|
|
179
180
|
return textZoom;
|
|
@@ -404,4 +405,12 @@ public class Options {
|
|
|
404
405
|
public void setPreShowScript(String preLoadScript) {
|
|
405
406
|
this.preShowScript = preLoadScript;
|
|
406
407
|
}
|
|
408
|
+
|
|
409
|
+
public boolean getPreventDeeplink() {
|
|
410
|
+
return preventDeeplink;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
public void setPreventDeeplink(boolean preventDeeplink) {
|
|
414
|
+
this.preventDeeplink = preventDeeplink;
|
|
415
|
+
}
|
|
407
416
|
}
|