@capgo/capacitor-inappbrowser 8.14.0 → 8.14.2
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/android/src/main/java/ee/forgr/capacitor_inappbrowser/CapgoInAppBrowserPlugin.java +62 -5
- package/android/src/main/java/ee/forgr/capacitor_inappbrowser/CustomTabsLifecycleSupport.java +84 -0
- package/android/src/main/java/ee/forgr/capacitor_inappbrowser/WebViewDialog.java +83 -28
- package/ios/Sources/InAppBrowserPlugin/InAppBrowserPlugin.swift +1 -1
- package/package.json +1 -1
|
@@ -76,6 +76,14 @@ public class CapgoInAppBrowserPlugin extends Plugin implements WebViewDialog.Per
|
|
|
76
76
|
}
|
|
77
77
|
);
|
|
78
78
|
|
|
79
|
+
private static final ExecutorService CUSTOM_TABS_LIFECYCLE_EXECUTOR = Executors.newSingleThreadExecutor((runnable) -> {
|
|
80
|
+
Thread thread = new Thread(runnable, "CapgoInAppBrowser-CustomTabsLifecycle");
|
|
81
|
+
thread.setDaemon(true);
|
|
82
|
+
return thread;
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
private final CustomTabsLifecycleSupport customTabsLifecycleSupport = new CustomTabsLifecycleSupport(CUSTOM_TABS_LIFECYCLE_EXECUTOR);
|
|
86
|
+
|
|
79
87
|
private final String pluginVersion = "8.6.17";
|
|
80
88
|
|
|
81
89
|
private boolean resolveClientCertificatePrompt(PluginCall call) {
|
|
@@ -670,10 +678,16 @@ public class CapgoInAppBrowserPlugin extends Plugin implements WebViewDialog.Per
|
|
|
670
678
|
currentPermissionNeedsMicrophone = false;
|
|
671
679
|
}
|
|
672
680
|
|
|
681
|
+
private final Object customTabsClientLock = new Object();
|
|
682
|
+
private static final long CUSTOM_TABS_CONNECTION_TIMEOUT_MS = 2000;
|
|
683
|
+
|
|
673
684
|
CustomTabsServiceConnection connection = new CustomTabsServiceConnection() {
|
|
674
685
|
@Override
|
|
675
686
|
public void onCustomTabsServiceConnected(ComponentName name, CustomTabsClient client) {
|
|
676
687
|
customTabsClient = client;
|
|
688
|
+
synchronized (customTabsClientLock) {
|
|
689
|
+
customTabsClientLock.notifyAll();
|
|
690
|
+
}
|
|
677
691
|
}
|
|
678
692
|
|
|
679
693
|
@Override
|
|
@@ -682,6 +696,27 @@ public class CapgoInAppBrowserPlugin extends Plugin implements WebViewDialog.Per
|
|
|
682
696
|
}
|
|
683
697
|
};
|
|
684
698
|
|
|
699
|
+
private final CustomTabsLifecycleSupport.Binder customTabsBinder = new CustomTabsLifecycleSupport.Binder() {
|
|
700
|
+
@Override
|
|
701
|
+
public boolean bindCustomTabsService() {
|
|
702
|
+
try {
|
|
703
|
+
boolean ok = CustomTabsClient.bindCustomTabsService(getContext(), CUSTOM_TAB_PACKAGE_NAME, connection);
|
|
704
|
+
if (!ok) {
|
|
705
|
+
Log.e(getLogTag(), "Error binding to custom tabs service");
|
|
706
|
+
}
|
|
707
|
+
return ok;
|
|
708
|
+
} catch (RuntimeException e) {
|
|
709
|
+
Log.e(getLogTag(), "Error binding to custom tabs service", e);
|
|
710
|
+
return false;
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
@Override
|
|
715
|
+
public void unbindCustomTabsService() {
|
|
716
|
+
getContext().unbindService(connection);
|
|
717
|
+
}
|
|
718
|
+
};
|
|
719
|
+
|
|
685
720
|
@PluginMethod
|
|
686
721
|
public void requestCameraPermission(PluginCall call) {
|
|
687
722
|
if (isAndroidPermissionGranted(Manifest.permission.CAMERA)) {
|
|
@@ -748,8 +783,10 @@ public class CapgoInAppBrowserPlugin extends Plugin implements WebViewDialog.Per
|
|
|
748
783
|
|
|
749
784
|
if (TextUtils.isEmpty(url)) {
|
|
750
785
|
call.reject("Invalid URL");
|
|
786
|
+
return;
|
|
751
787
|
}
|
|
752
788
|
currentUrl = url;
|
|
789
|
+
awaitCustomTabsClientIfNeeded();
|
|
753
790
|
CustomTabsIntent.Builder builder = new CustomTabsIntent.Builder(getCustomTabsSession());
|
|
754
791
|
|
|
755
792
|
// --- Chrome Custom Tab UI customization ---
|
|
@@ -1839,10 +1876,8 @@ public class CapgoInAppBrowserPlugin extends Plugin implements WebViewDialog.Per
|
|
|
1839
1876
|
}
|
|
1840
1877
|
|
|
1841
1878
|
protected void handleOnResume() {
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
Log.e(getLogTag(), "Error binding to custom tabs service");
|
|
1845
|
-
}
|
|
1879
|
+
customTabsLifecycleSupport.onResume(customTabsBinder);
|
|
1880
|
+
|
|
1846
1881
|
// If we have a saved call and user returned without callback, reject
|
|
1847
1882
|
if (openSecureWindowSavedCall != null) {
|
|
1848
1883
|
openSecureWindowSavedCall.reject("OAuth cancelled or no callback received");
|
|
@@ -1851,7 +1886,29 @@ public class CapgoInAppBrowserPlugin extends Plugin implements WebViewDialog.Per
|
|
|
1851
1886
|
}
|
|
1852
1887
|
|
|
1853
1888
|
protected void handleOnPause() {
|
|
1854
|
-
|
|
1889
|
+
customTabsLifecycleSupport.onPause(customTabsBinder);
|
|
1890
|
+
}
|
|
1891
|
+
|
|
1892
|
+
private void awaitCustomTabsClientIfNeeded() {
|
|
1893
|
+
if (customTabsClient != null || !customTabsLifecycleSupport.isBindingOrBound()) {
|
|
1894
|
+
return;
|
|
1895
|
+
}
|
|
1896
|
+
|
|
1897
|
+
synchronized (customTabsClientLock) {
|
|
1898
|
+
long deadline = System.currentTimeMillis() + CUSTOM_TABS_CONNECTION_TIMEOUT_MS;
|
|
1899
|
+
while (customTabsClient == null && customTabsLifecycleSupport.isBindingOrBound() && System.currentTimeMillis() < deadline) {
|
|
1900
|
+
long remaining = deadline - System.currentTimeMillis();
|
|
1901
|
+
if (remaining <= 0) {
|
|
1902
|
+
break;
|
|
1903
|
+
}
|
|
1904
|
+
try {
|
|
1905
|
+
customTabsClientLock.wait(remaining);
|
|
1906
|
+
} catch (InterruptedException e) {
|
|
1907
|
+
Thread.currentThread().interrupt();
|
|
1908
|
+
break;
|
|
1909
|
+
}
|
|
1910
|
+
}
|
|
1911
|
+
}
|
|
1855
1912
|
}
|
|
1856
1913
|
|
|
1857
1914
|
public CustomTabsSession getCustomTabsSession() {
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
package ee.forgr.capacitor_inappbrowser;
|
|
2
|
+
|
|
3
|
+
import java.util.concurrent.Executor;
|
|
4
|
+
|
|
5
|
+
final class CustomTabsLifecycleSupport {
|
|
6
|
+
|
|
7
|
+
interface Binder {
|
|
8
|
+
boolean bindCustomTabsService();
|
|
9
|
+
|
|
10
|
+
void unbindCustomTabsService();
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
private final Executor executor;
|
|
14
|
+
private final Object lock = new Object();
|
|
15
|
+
private boolean bound = false;
|
|
16
|
+
private boolean bindInProgress = false;
|
|
17
|
+
|
|
18
|
+
CustomTabsLifecycleSupport(Executor executor) {
|
|
19
|
+
this.executor = executor;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
void onResume(Binder binder) {
|
|
23
|
+
executor.execute(() -> performBind(binder));
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
void onPause(Binder binder) {
|
|
27
|
+
executor.execute(() -> performUnbind(binder));
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
boolean isBindingOrBound() {
|
|
31
|
+
synchronized (lock) {
|
|
32
|
+
return bindInProgress || bound;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
boolean isBound() {
|
|
37
|
+
synchronized (lock) {
|
|
38
|
+
return bound;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
private void performBind(Binder binder) {
|
|
43
|
+
synchronized (lock) {
|
|
44
|
+
if (bound || bindInProgress) {
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
bindInProgress = true;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
boolean ok = false;
|
|
51
|
+
try {
|
|
52
|
+
ok = binder.bindCustomTabsService();
|
|
53
|
+
} catch (RuntimeException ignored) {
|
|
54
|
+
// Caller logs bind failures.
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
synchronized (lock) {
|
|
58
|
+
bindInProgress = false;
|
|
59
|
+
if (!ok) {
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
bound = true;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
private void performUnbind(Binder binder) {
|
|
68
|
+
synchronized (lock) {
|
|
69
|
+
if (bindInProgress) {
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
if (!bound) {
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
bound = false;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
try {
|
|
79
|
+
binder.unbindCustomTabsService();
|
|
80
|
+
} catch (RuntimeException ignored) {
|
|
81
|
+
// Service may already be unbound.
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
@@ -3013,30 +3013,7 @@ public class WebViewDialog extends Dialog implements ProxyResponseRouting.ProxyR
|
|
|
3013
3013
|
final View insetsSourceView = resolveSafeAreaInsetsSourceView();
|
|
3014
3014
|
|
|
3015
3015
|
ViewCompat.setOnApplyWindowInsetsListener(insetsSourceView, (v, windowInsets) -> {
|
|
3016
|
-
|
|
3017
|
-
Insets navigationBars = windowInsets.getInsets(WindowInsetsCompat.Type.navigationBars());
|
|
3018
|
-
Insets systemGestures = windowInsets.getInsets(WindowInsetsCompat.Type.systemGestures());
|
|
3019
|
-
Insets mandatoryGestures = windowInsets.getInsets(WindowInsetsCompat.Type.mandatorySystemGestures());
|
|
3020
|
-
Insets ime = windowInsets.getInsets(WindowInsetsCompat.Type.ime());
|
|
3021
|
-
boolean keyboardVisible = windowInsets.isVisible(WindowInsetsCompat.Type.ime());
|
|
3022
|
-
|
|
3023
|
-
boolean appBarHandlesTopInset =
|
|
3024
|
-
isAndroid15Plus &&
|
|
3025
|
-
!TextUtils.equals(_options.getToolbarType(), "blank") &&
|
|
3026
|
-
toolbarView != null &&
|
|
3027
|
-
toolbarView.getVisibility() == View.VISIBLE &&
|
|
3028
|
-
toolbarView.getParent() instanceof com.google.android.material.appbar.AppBarLayout;
|
|
3029
|
-
applySafeAreaMargins(
|
|
3030
|
-
bars,
|
|
3031
|
-
navigationBars,
|
|
3032
|
-
systemGestures,
|
|
3033
|
-
mandatoryGestures,
|
|
3034
|
-
ime,
|
|
3035
|
-
keyboardVisible,
|
|
3036
|
-
appBarHandlesTopInset,
|
|
3037
|
-
isAndroid15Plus
|
|
3038
|
-
);
|
|
3039
|
-
|
|
3016
|
+
applyWindowInsetsToWebView(windowInsets, isAndroid15Plus, toolbarView);
|
|
3040
3017
|
return windowInsets;
|
|
3041
3018
|
});
|
|
3042
3019
|
requestSafeAreaInsets();
|
|
@@ -3209,6 +3186,7 @@ public class WebViewDialog extends Dialog implements ProxyResponseRouting.ProxyR
|
|
|
3209
3186
|
|
|
3210
3187
|
if (isHiddenModeActive) {
|
|
3211
3188
|
requestSafeAreaInsets();
|
|
3189
|
+
reapplyInsetsFromWindowRoot();
|
|
3212
3190
|
return;
|
|
3213
3191
|
}
|
|
3214
3192
|
|
|
@@ -3235,21 +3213,24 @@ public class WebViewDialog extends Dialog implements ProxyResponseRouting.ProxyR
|
|
|
3235
3213
|
}
|
|
3236
3214
|
|
|
3237
3215
|
if (swipeRefreshLayout != null) {
|
|
3216
|
+
swipeRefreshLayout.setRefreshing(false);
|
|
3238
3217
|
swipeRefreshLayout.requestLayout();
|
|
3239
3218
|
}
|
|
3240
3219
|
|
|
3241
3220
|
_webView.requestLayout();
|
|
3242
3221
|
_webView.invalidate();
|
|
3243
3222
|
requestSafeAreaInsets();
|
|
3223
|
+
reapplyInsetsFromWindowRoot();
|
|
3244
3224
|
|
|
3245
|
-
mainHandler.post(this::
|
|
3246
|
-
mainHandler.postDelayed(this::
|
|
3247
|
-
mainHandler.postDelayed(this::
|
|
3225
|
+
mainHandler.post(this::reapplyInsetsFromWindowRoot);
|
|
3226
|
+
mainHandler.postDelayed(this::reapplyInsetsFromWindowRoot, 100);
|
|
3227
|
+
mainHandler.postDelayed(this::reapplyInsetsFromWindowRoot, 300);
|
|
3248
3228
|
|
|
3249
3229
|
_webView.post(() -> {
|
|
3250
3230
|
if (_webView == null) {
|
|
3251
3231
|
return;
|
|
3252
3232
|
}
|
|
3233
|
+
requestWebViewContentRelayout();
|
|
3253
3234
|
_webView.evaluateJavascript("(function(){window.dispatchEvent(new Event('resize'));})();", null);
|
|
3254
3235
|
});
|
|
3255
3236
|
}
|
|
@@ -3340,6 +3321,80 @@ public class WebViewDialog extends Dialog implements ProxyResponseRouting.ProxyR
|
|
|
3340
3321
|
});
|
|
3341
3322
|
}
|
|
3342
3323
|
|
|
3324
|
+
private void applyWindowInsetsToWebView(WindowInsetsCompat windowInsets, boolean isAndroid15Plus, View toolbarView) {
|
|
3325
|
+
if (windowInsets == null || _options == null) {
|
|
3326
|
+
return;
|
|
3327
|
+
}
|
|
3328
|
+
|
|
3329
|
+
Insets bars = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars());
|
|
3330
|
+
Insets navigationBars = windowInsets.getInsets(WindowInsetsCompat.Type.navigationBars());
|
|
3331
|
+
Insets systemGestures = windowInsets.getInsets(WindowInsetsCompat.Type.systemGestures());
|
|
3332
|
+
Insets mandatoryGestures = windowInsets.getInsets(WindowInsetsCompat.Type.mandatorySystemGestures());
|
|
3333
|
+
Insets ime = windowInsets.getInsets(WindowInsetsCompat.Type.ime());
|
|
3334
|
+
boolean keyboardVisible = windowInsets.isVisible(WindowInsetsCompat.Type.ime());
|
|
3335
|
+
|
|
3336
|
+
boolean appBarHandlesTopInset =
|
|
3337
|
+
isAndroid15Plus &&
|
|
3338
|
+
!TextUtils.equals(_options.getToolbarType(), "blank") &&
|
|
3339
|
+
toolbarView != null &&
|
|
3340
|
+
toolbarView.getVisibility() == View.VISIBLE &&
|
|
3341
|
+
toolbarView.getParent() instanceof com.google.android.material.appbar.AppBarLayout;
|
|
3342
|
+
|
|
3343
|
+
applySafeAreaMargins(
|
|
3344
|
+
bars,
|
|
3345
|
+
navigationBars,
|
|
3346
|
+
systemGestures,
|
|
3347
|
+
mandatoryGestures,
|
|
3348
|
+
ime,
|
|
3349
|
+
keyboardVisible,
|
|
3350
|
+
appBarHandlesTopInset,
|
|
3351
|
+
isAndroid15Plus
|
|
3352
|
+
);
|
|
3353
|
+
}
|
|
3354
|
+
|
|
3355
|
+
/**
|
|
3356
|
+
* Re-read root window insets and apply WebView margins plus container padding.
|
|
3357
|
+
* Dialog windows may not re-dispatch inset listeners after rotation, leaving stale
|
|
3358
|
+
* portrait margins that break vertical scrolling in landscape.
|
|
3359
|
+
*/
|
|
3360
|
+
private void reapplyInsetsFromWindowRoot() {
|
|
3361
|
+
if (_webView == null || _options == null) {
|
|
3362
|
+
return;
|
|
3363
|
+
}
|
|
3364
|
+
|
|
3365
|
+
Window window = getWindow();
|
|
3366
|
+
View decorView = window != null ? window.getDecorView() : null;
|
|
3367
|
+
if (decorView == null) {
|
|
3368
|
+
return;
|
|
3369
|
+
}
|
|
3370
|
+
|
|
3371
|
+
WindowInsetsCompat windowInsets = ViewCompat.getRootWindowInsets(decorView);
|
|
3372
|
+
if (windowInsets == null) {
|
|
3373
|
+
// Root insets not ready yet; do not mutate margins/padding (would desync WebView
|
|
3374
|
+
// margins from container padding). Delayed retries and requestApplyInsets will retry.
|
|
3375
|
+
return;
|
|
3376
|
+
}
|
|
3377
|
+
|
|
3378
|
+
boolean isAndroid15Plus = Build.VERSION.SDK_INT >= 35;
|
|
3379
|
+
View toolbarView = findViewById(R.id.tool_bar);
|
|
3380
|
+
applyWindowInsetsToWebView(windowInsets, isAndroid15Plus, toolbarView);
|
|
3381
|
+
}
|
|
3382
|
+
|
|
3383
|
+
private void requestWebViewContentRelayout() {
|
|
3384
|
+
View container = findViewById(R.id.content_browser_layout);
|
|
3385
|
+
if (container != null) {
|
|
3386
|
+
container.requestLayout();
|
|
3387
|
+
container.invalidate();
|
|
3388
|
+
}
|
|
3389
|
+
|
|
3390
|
+
if (_webView == null) {
|
|
3391
|
+
return;
|
|
3392
|
+
}
|
|
3393
|
+
|
|
3394
|
+
_webView.requestLayout();
|
|
3395
|
+
_webView.invalidate();
|
|
3396
|
+
}
|
|
3397
|
+
|
|
3343
3398
|
private void applySafeAreaMargins(
|
|
3344
3399
|
Insets bars,
|
|
3345
3400
|
Insets navigationBars,
|
|
@@ -3554,7 +3609,7 @@ public class WebViewDialog extends Dialog implements ProxyResponseRouting.ProxyR
|
|
|
3554
3609
|
ViewCompat.requestApplyInsets(insetsSourceView);
|
|
3555
3610
|
insetsSourceView.post(() -> {
|
|
3556
3611
|
ViewCompat.requestApplyInsets(insetsSourceView);
|
|
3557
|
-
|
|
3612
|
+
reapplyInsetsFromWindowRoot();
|
|
3558
3613
|
});
|
|
3559
3614
|
}
|
|
3560
3615
|
|
|
@@ -415,7 +415,7 @@ public class CapgoInAppBrowserPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
|
415
415
|
case aware = "AWARE"
|
|
416
416
|
case fakeVisible = "FAKE_VISIBLE"
|
|
417
417
|
}
|
|
418
|
-
private let pluginVersion: String = "8.14.
|
|
418
|
+
private let pluginVersion: String = "8.14.2"
|
|
419
419
|
public let identifier = "CapgoInAppBrowserPlugin"
|
|
420
420
|
public let jsName = "CapgoInAppBrowser"
|
|
421
421
|
public let pluginMethods: [CAPPluginMethod] = [
|