@capgo/inappbrowser 6.13.1 → 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 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(obj)`: Sends a message from the webview to the app
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.addListener("messageFromNative", listenerFunc: (event: <a href="#record">Record</a>&lt;string, any&gt;) =&gt; void)
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>&lt;string, any&gt;) =&gt; 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 | Description | Since |
443
- | ---------------------------- | --------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------- | ----- |
444
- | **`url`** | <code>string</code> | Target URL to load. | 0.1.0 |
445
- | **`headers`** | <code><a href="#headers">Headers</a></code> | <a href="#headers">Headers</a> to send with the request. | 0.1.0 |
446
- | **`credentials`** | <code><a href="#credentials">Credentials</a></code> | <a href="#credentials">Credentials</a> to send with the request and all subsequent requests for the same host. | 6.1.0 |
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
- call.reject("Camera permission is required");
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
- @NonNull ComponentName name,
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", null);
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 != null) {
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
- Field[] drawables = R.drawable.class.getFields();
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.hasOption("preShowScript") &&
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.hasOption("closeModalTitle") ||
557
- call.hasOption("closeModalDescription") ||
558
- call.hasOption("closeModalOk") ||
559
- call.hasOption("closeModalCancel")
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 (call.hasOption("shareDisclaimer") && !call.hasOption("shareSubject")) {
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.hasOption("buttonNearDone")) {
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) {
@@ -792,7 +830,6 @@ public class InAppBrowserPlugin
792
830
  new JSObject().put("url", webViewDialog.getUrl())
793
831
  );
794
832
  webViewDialog.dismiss();
795
- webViewDialog.destroy();
796
833
  webViewDialog = null;
797
834
  } else {
798
835
  Intent intent = new Intent(
@@ -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
  }