@capgo/capacitor-inappbrowser 8.6.34 → 8.7.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.
@@ -16,6 +16,7 @@ import android.graphics.Color;
16
16
  import android.graphics.Paint;
17
17
  import android.graphics.PorterDuff;
18
18
  import android.graphics.PorterDuffColorFilter;
19
+ import android.graphics.drawable.ColorDrawable;
19
20
  import android.graphics.drawable.Drawable;
20
21
  import android.net.Uri;
21
22
  import android.net.http.SslError;
@@ -23,6 +24,7 @@ import android.os.Build;
23
24
  import android.os.Environment;
24
25
  import android.os.Handler;
25
26
  import android.os.Looper;
27
+ import android.os.SystemClock;
26
28
  import android.print.PrintAttributes;
27
29
  import android.print.PrintDocumentAdapter;
28
30
  import android.print.PrintManager;
@@ -35,6 +37,7 @@ import android.util.Log;
35
37
  import android.util.TypedValue;
36
38
  import android.view.Gravity;
37
39
  import android.view.KeyEvent;
40
+ import android.view.MotionEvent;
38
41
  import android.view.View;
39
42
  import android.view.ViewGroup;
40
43
  import android.view.Window;
@@ -52,8 +55,10 @@ import android.webkit.WebChromeClient;
52
55
  import android.webkit.WebResourceError;
53
56
  import android.webkit.WebResourceRequest;
54
57
  import android.webkit.WebResourceResponse;
58
+ import android.webkit.WebSettings;
55
59
  import android.webkit.WebView;
56
60
  import android.webkit.WebViewClient;
61
+ import android.widget.FrameLayout;
57
62
  import android.widget.ImageButton;
58
63
  import android.widget.ImageView;
59
64
  import android.widget.RelativeLayout;
@@ -344,6 +349,21 @@ public class WebViewDialog extends Dialog implements ProxyResponseRouting.ProxyR
344
349
  private ViewGroup.LayoutParams previousWebViewLayoutParams;
345
350
  private float previousDecorAlpha = 1f;
346
351
  private int previousDecorVisibility = View.VISIBLE;
352
+ private View browserContentView;
353
+ private ViewGroup backLayerParent;
354
+ private boolean backLayerActive = false;
355
+ private boolean usingHostTransparency = false;
356
+ private Runnable hostTransparencyApplyRunnable;
357
+ private long forwardedInputDownTime = 0L;
358
+ private static int hostTransparencyUseCount = 0;
359
+ private static Drawable originalCapacitorWebViewBackground;
360
+ private static boolean originalCapacitorWebViewBackgroundCaptured = false;
361
+ private static Drawable originalCapacitorParentBackground;
362
+ private static boolean originalCapacitorParentBackgroundCaptured = false;
363
+ private static Drawable originalCapacitorWindowBackground;
364
+ private static boolean originalCapacitorWindowBackgroundCaptured = false;
365
+ private static float originalCapacitorWebViewAlpha = 1f;
366
+ private static float originalCapacitorParentAlpha = 1f;
347
367
  private float previousWebViewAlpha = 1f;
348
368
  private int previousWebViewVisibility = View.VISIBLE;
349
369
 
@@ -400,6 +420,337 @@ public class WebViewDialog extends Dialog implements ProxyResponseRouting.ProxyR
400
420
  return _webView;
401
421
  }
402
422
 
423
+ private View getBrowserContentView() {
424
+ if (browserContentView == null) {
425
+ browserContentView = findViewById(R.id.coordinator_layout);
426
+ }
427
+ return browserContentView;
428
+ }
429
+
430
+ private ViewGroup getBackLayerParent() {
431
+ if (capacitorWebView != null && capacitorWebView.getParent() instanceof ViewGroup viewGroup) {
432
+ return viewGroup;
433
+ }
434
+ if (activity != null && activity.getWindow() != null) {
435
+ View content = activity.getWindow().getDecorView().findViewById(android.R.id.content);
436
+ if (content instanceof ViewGroup viewGroup) {
437
+ return viewGroup;
438
+ }
439
+ }
440
+ return null;
441
+ }
442
+
443
+ private ViewGroup.LayoutParams createBackLayerLayoutParams(ViewGroup parent) {
444
+ int width =
445
+ _options != null && _options.getWidth() != null ? (int) getPixels(_options.getWidth()) : ViewGroup.LayoutParams.MATCH_PARENT;
446
+ int height =
447
+ _options != null && _options.getHeight() != null ? (int) getPixels(_options.getHeight()) : ViewGroup.LayoutParams.MATCH_PARENT;
448
+ int leftMargin = _options != null && _options.getX() != null ? (int) getPixels(_options.getX()) : 0;
449
+ int topMargin = _options != null && _options.getY() != null ? (int) getPixels(_options.getY()) : 0;
450
+
451
+ if (parent instanceof FrameLayout) {
452
+ FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(width, height, Gravity.TOP | Gravity.START);
453
+ params.leftMargin = leftMargin;
454
+ params.topMargin = topMargin;
455
+ return params;
456
+ }
457
+ if (parent instanceof RelativeLayout) {
458
+ RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(width, height);
459
+ params.leftMargin = leftMargin;
460
+ params.topMargin = topMargin;
461
+ return params;
462
+ }
463
+ if (parent instanceof CoordinatorLayout) {
464
+ CoordinatorLayout.LayoutParams params = new CoordinatorLayout.LayoutParams(width, height);
465
+ params.leftMargin = leftMargin;
466
+ params.topMargin = topMargin;
467
+ return params;
468
+ }
469
+
470
+ ViewGroup.MarginLayoutParams params = new ViewGroup.MarginLayoutParams(width, height);
471
+ params.leftMargin = leftMargin;
472
+ params.topMargin = topMargin;
473
+ return params;
474
+ }
475
+
476
+ private void applyBackLayerDimensions() {
477
+ View contentView = getBrowserContentView();
478
+ if (!backLayerActive || contentView == null || backLayerParent == null) {
479
+ return;
480
+ }
481
+ contentView.setLayoutParams(createBackLayerLayoutParams(backLayerParent));
482
+ contentView.requestLayout();
483
+ }
484
+
485
+ private boolean isMiuiDevice() {
486
+ String manufacturer = Build.MANUFACTURER != null ? Build.MANUFACTURER.toLowerCase(Locale.US) : "";
487
+ String brand = Build.BRAND != null ? Build.BRAND.toLowerCase(Locale.US) : "";
488
+ return manufacturer.contains("xiaomi") || brand.contains("xiaomi") || brand.contains("redmi") || brand.contains("poco");
489
+ }
490
+
491
+ private boolean usesFullStackTransparentBackgroundWorkaround() {
492
+ String manufacturer = Build.MANUFACTURER != null ? Build.MANUFACTURER.toLowerCase(Locale.US) : "";
493
+ String brand = Build.BRAND != null ? Build.BRAND.toLowerCase(Locale.US) : "";
494
+ return (
495
+ isMiuiDevice() ||
496
+ manufacturer.contains("huawei") ||
497
+ manufacturer.contains("honor") ||
498
+ brand.contains("huawei") ||
499
+ brand.contains("honor")
500
+ );
501
+ }
502
+
503
+ private void applyHostTransparency(boolean enabled) {
504
+ if (!enabled || capacitorWebView == null || usingHostTransparency) {
505
+ return;
506
+ }
507
+ ViewGroup capacitorParent = capacitorWebView.getParent() instanceof ViewGroup ? (ViewGroup) capacitorWebView.getParent() : null;
508
+ Window window = activity != null ? activity.getWindow() : null;
509
+ synchronized (WebViewDialog.class) {
510
+ if (hostTransparencyUseCount == 0) {
511
+ originalCapacitorWebViewBackground = capacitorWebView.getBackground();
512
+ originalCapacitorWebViewBackgroundCaptured = true;
513
+ originalCapacitorWebViewAlpha = capacitorWebView.getAlpha();
514
+ if (capacitorParent != null) {
515
+ originalCapacitorParentBackground = capacitorParent.getBackground();
516
+ originalCapacitorParentBackgroundCaptured = true;
517
+ originalCapacitorParentAlpha = capacitorParent.getAlpha();
518
+ }
519
+ if (window != null) {
520
+ originalCapacitorWindowBackground = window.getDecorView().getBackground();
521
+ originalCapacitorWindowBackgroundCaptured = true;
522
+ }
523
+ }
524
+ hostTransparencyUseCount++;
525
+ usingHostTransparency = true;
526
+ }
527
+
528
+ final Runnable[] applyRef = new Runnable[1];
529
+ Runnable apply = () -> {
530
+ synchronized (WebViewDialog.class) {
531
+ if (!usingHostTransparency || hostTransparencyApplyRunnable != applyRef[0]) {
532
+ return;
533
+ }
534
+ }
535
+ boolean fullStackWorkaround = usesFullStackTransparentBackgroundWorkaround();
536
+ if (window != null && fullStackWorkaround) {
537
+ window.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
538
+ }
539
+ if (capacitorParent != null && fullStackWorkaround) {
540
+ capacitorParent.setBackgroundColor(Color.TRANSPARENT);
541
+ }
542
+ capacitorWebView.setBackgroundColor(isMiuiDevice() ? Color.argb(1, 255, 255, 255) : Color.TRANSPARENT);
543
+ capacitorWebView.setAlpha(isMiuiDevice() ? 0.99f : originalCapacitorWebViewAlpha);
544
+ if (capacitorParent != null) {
545
+ capacitorParent.requestTransparentRegion(capacitorWebView);
546
+ }
547
+ };
548
+
549
+ applyRef[0] = apply;
550
+ hostTransparencyApplyRunnable = apply;
551
+ apply.run();
552
+ if (isMiuiDevice()) {
553
+ mainHandler.postDelayed(apply, 50);
554
+ mainHandler.postDelayed(apply, 250);
555
+ }
556
+ }
557
+
558
+ private void restoreHostTransparency() {
559
+ if (!usingHostTransparency || capacitorWebView == null) {
560
+ return;
561
+ }
562
+ ViewGroup capacitorParent = capacitorWebView.getParent() instanceof ViewGroup ? (ViewGroup) capacitorWebView.getParent() : null;
563
+ Window window = activity != null ? activity.getWindow() : null;
564
+ if (hostTransparencyApplyRunnable != null) {
565
+ mainHandler.removeCallbacks(hostTransparencyApplyRunnable);
566
+ hostTransparencyApplyRunnable = null;
567
+ }
568
+ synchronized (WebViewDialog.class) {
569
+ hostTransparencyUseCount = Math.max(0, hostTransparencyUseCount - 1);
570
+ usingHostTransparency = false;
571
+ if (hostTransparencyUseCount > 0) {
572
+ return;
573
+ }
574
+ if (originalCapacitorWebViewBackgroundCaptured) {
575
+ capacitorWebView.setBackground(originalCapacitorWebViewBackground);
576
+ }
577
+ capacitorWebView.setAlpha(originalCapacitorWebViewAlpha);
578
+ if (capacitorParent != null && originalCapacitorParentBackgroundCaptured) {
579
+ capacitorParent.setBackground(originalCapacitorParentBackground);
580
+ capacitorParent.setAlpha(originalCapacitorParentAlpha);
581
+ }
582
+ if (window != null && originalCapacitorWindowBackgroundCaptured) {
583
+ window.setBackgroundDrawable(originalCapacitorWindowBackground);
584
+ }
585
+ originalCapacitorWebViewBackground = null;
586
+ originalCapacitorWebViewBackgroundCaptured = false;
587
+ originalCapacitorParentBackground = null;
588
+ originalCapacitorParentBackgroundCaptured = false;
589
+ originalCapacitorWindowBackground = null;
590
+ originalCapacitorWindowBackgroundCaptured = false;
591
+ originalCapacitorWebViewAlpha = 1f;
592
+ originalCapacitorParentAlpha = 1f;
593
+ }
594
+ }
595
+
596
+ private void detachBackLayer() {
597
+ View contentView = getBrowserContentView();
598
+ if (contentView != null && contentView.getParent() instanceof ViewGroup parent) {
599
+ if (parent == backLayerParent || backLayerActive) {
600
+ parent.removeView(contentView);
601
+ }
602
+ }
603
+ backLayerActive = false;
604
+ backLayerParent = null;
605
+ restoreHostTransparency();
606
+ }
607
+
608
+ private void attachContentToDialogWindow() {
609
+ View contentView = getBrowserContentView();
610
+ if (contentView == null) {
611
+ return;
612
+ }
613
+ if (contentView.getParent() instanceof ViewGroup parent) {
614
+ parent.removeView(contentView);
615
+ }
616
+ setContentView(contentView);
617
+ }
618
+
619
+ public boolean sendToBack(boolean transparentBackground) {
620
+ if (_options != null) {
621
+ _options.setToBack(true);
622
+ _options.setTransparentBackground(transparentBackground);
623
+ }
624
+ View contentView = getBrowserContentView();
625
+ ViewGroup parent = getBackLayerParent();
626
+ if (contentView == null || parent == null) {
627
+ Log.w("InAppBrowser", "Unable to send webview to back: missing content or parent view");
628
+ return false;
629
+ }
630
+ if (contentView.getParent() instanceof ViewGroup currentParent) {
631
+ currentParent.removeView(contentView);
632
+ }
633
+ if (transparentBackground) {
634
+ applyHostTransparency(true);
635
+ } else {
636
+ restoreHostTransparency();
637
+ }
638
+ int targetIndex = capacitorWebView != null ? parent.indexOfChild(capacitorWebView) : 0;
639
+ parent.addView(contentView, Math.max(0, targetIndex), createBackLayerLayoutParams(parent));
640
+ contentView.setVisibility(View.VISIBLE);
641
+ contentView.setAlpha(1f);
642
+ backLayerParent = parent;
643
+ backLayerActive = true;
644
+ if (isShowing()) {
645
+ super.hide();
646
+ }
647
+ return true;
648
+ }
649
+
650
+ public void bringToFrontLayer() {
651
+ if (_options != null) {
652
+ _options.setToBack(false);
653
+ }
654
+ detachBackLayer();
655
+ attachContentToDialogWindow();
656
+ if (!isShowing()) {
657
+ show();
658
+ }
659
+ applyDimensions();
660
+ }
661
+
662
+ private boolean showAccordingToLayerMode() {
663
+ if (_options != null && _options.isToBack()) {
664
+ return sendToBack(_options.getTransparentBackground());
665
+ }
666
+ bringToFrontLayer();
667
+ return true;
668
+ }
669
+
670
+ private void showAccordingToLayerModeOrFallback() {
671
+ if (!showAccordingToLayerMode()) {
672
+ Log.w("InAppBrowser", "Unable to send webview to back; showing it in front");
673
+ bringToFrontLayer();
674
+ }
675
+ }
676
+
677
+ private boolean dispatchMotionEvent(int action, double x, double y) {
678
+ if (_webView == null) {
679
+ return false;
680
+ }
681
+ long now = SystemClock.uptimeMillis();
682
+ if (action == MotionEvent.ACTION_DOWN || forwardedInputDownTime == 0L) {
683
+ forwardedInputDownTime = now;
684
+ }
685
+ MotionEvent event = MotionEvent.obtain(forwardedInputDownTime, now, action, getPixels(x), getPixels(y), 0);
686
+ boolean handled = _webView.dispatchTouchEvent(event);
687
+ event.recycle();
688
+ if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
689
+ forwardedInputDownTime = 0L;
690
+ }
691
+ return handled;
692
+ }
693
+
694
+ public boolean dispatchInputEvent(String type, double x, double y, double deltaX, double deltaY) {
695
+ if (_webView == null || type == null) {
696
+ return false;
697
+ }
698
+ switch (type) {
699
+ case "click":
700
+ forwardedInputDownTime = SystemClock.uptimeMillis();
701
+ dispatchMotionEvent(MotionEvent.ACTION_DOWN, x, y);
702
+ return dispatchMotionEvent(MotionEvent.ACTION_UP, x, y);
703
+ case "touchstart":
704
+ return dispatchMotionEvent(MotionEvent.ACTION_DOWN, x, y);
705
+ case "touchmove":
706
+ return dispatchMotionEvent(MotionEvent.ACTION_MOVE, x, y);
707
+ case "touchend":
708
+ return dispatchMotionEvent(MotionEvent.ACTION_UP, x, y);
709
+ case "touchcancel":
710
+ return dispatchMotionEvent(MotionEvent.ACTION_CANCEL, x, y);
711
+ case "scroll":
712
+ return dispatchScrollEvent(x, y, deltaX, deltaY);
713
+ default:
714
+ return false;
715
+ }
716
+ }
717
+
718
+ private boolean dispatchScrollEvent(double x, double y, double deltaX, double deltaY) {
719
+ if (_webView == null) {
720
+ return false;
721
+ }
722
+ String script =
723
+ "(function() {" +
724
+ " const x = " +
725
+ Double.toString(x) +
726
+ ";" +
727
+ " const y = " +
728
+ Double.toString(y) +
729
+ ";" +
730
+ " const dx = " +
731
+ Double.toString(deltaX) +
732
+ ";" +
733
+ " const dy = " +
734
+ Double.toString(deltaY) +
735
+ ";" +
736
+ " let target = Number.isFinite(x) && Number.isFinite(y) ? document.elementFromPoint(x, y) : null;" +
737
+ " while (target && target !== document.body && target !== document.documentElement) {" +
738
+ " const style = window.getComputedStyle(target);" +
739
+ " const canScroll = /(auto|scroll)/.test(style.overflow + style.overflowX + style.overflowY) &&" +
740
+ " (target.scrollHeight > target.clientHeight || target.scrollWidth > target.clientWidth);" +
741
+ " if (canScroll && typeof target.scrollBy === 'function') {" +
742
+ " target.scrollBy(dx, dy);" +
743
+ " return true;" +
744
+ " }" +
745
+ " target = target.parentElement;" +
746
+ " }" +
747
+ " window.scrollBy(dx, dy);" +
748
+ " return true;" +
749
+ "})();";
750
+ _webView.evaluateJavascript(script, null);
751
+ return true;
752
+ }
753
+
403
754
  private void resolveOpenWebViewIfNeeded() {
404
755
  if (openWebViewResolved || _options == null) {
405
756
  return;
@@ -493,10 +844,8 @@ public class WebViewDialog extends Dialog implements ProxyResponseRouting.ProxyR
493
844
  return;
494
845
  }
495
846
  activity.runOnUiThread(() -> {
496
- if (!isShowing()) {
497
- WebViewDialog.this.show();
498
- }
499
847
  setHidden(false);
848
+ showAccordingToLayerModeOrFallback();
500
849
  });
501
850
  }
502
851
 
@@ -1834,6 +2183,17 @@ public class WebViewDialog extends Dialog implements ProxyResponseRouting.ProxyR
1834
2183
  _webView.getSettings().setUseWideViewPort(true);
1835
2184
  _webView.getSettings().setAllowFileAccessFromFileURLs(true);
1836
2185
  _webView.getSettings().setAllowUniversalAccessFromFileURLs(true);
2186
+ if (!_options.getPersistWebViewData()) {
2187
+ _webView.getSettings().setCacheMode(WebSettings.LOAD_NO_CACHE);
2188
+ _webView.getSettings().setDatabaseEnabled(false);
2189
+ _webView.getSettings().setDomStorageEnabled(false);
2190
+ _webView.clearCache(true);
2191
+ _webView.clearHistory();
2192
+ _webView.clearFormData();
2193
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
2194
+ CookieManager.getInstance().setAcceptThirdPartyCookies(_webView, false);
2195
+ }
2196
+ }
1837
2197
  _webView.getSettings().setMediaPlaybackRequiresUserGesture(false);
1838
2198
  injectDocumentStartJavaScriptInterface();
1839
2199
 
@@ -2410,9 +2770,9 @@ public class WebViewDialog extends Dialog implements ProxyResponseRouting.ProxyR
2410
2770
  }
2411
2771
  resolveOpenWebViewIfNeeded();
2412
2772
  } else if (_options.isPopupWindowMode()) {
2413
- show();
2773
+ showAccordingToLayerModeOrFallback();
2414
2774
  } else if (!this._options.isPresentAfterPageLoad()) {
2415
- show();
2775
+ showAccordingToLayerModeOrFallback();
2416
2776
  resolveOpenWebViewIfNeeded();
2417
2777
  }
2418
2778
  }
@@ -2512,6 +2872,10 @@ public class WebViewDialog extends Dialog implements ProxyResponseRouting.ProxyR
2512
2872
  if (!isHiddenModeActive) {
2513
2873
  if (getWindow() == null) {
2514
2874
  try {
2875
+ if (backLayerActive) {
2876
+ detachBackLayer();
2877
+ attachContentToDialogWindow();
2878
+ }
2515
2879
  show();
2516
2880
  Window window = getWindow();
2517
2881
  if (window == null) {
@@ -4842,7 +5206,7 @@ public class WebViewDialog extends Dialog implements ProxyResponseRouting.ProxyR
4842
5206
  if (_options.isPresentAfterPageLoad()) {
4843
5207
  boolean usePreShowScript = _options.getPreShowScript() != null && !_options.getPreShowScript().isEmpty();
4844
5208
  if (!usePreShowScript) {
4845
- show();
5209
+ showAccordingToLayerModeOrFallback();
4846
5210
  resolveOpenWebViewIfNeeded();
4847
5211
  } else {
4848
5212
  executorService.execute(
@@ -4857,7 +5221,7 @@ public class WebViewDialog extends Dialog implements ProxyResponseRouting.ProxyR
4857
5221
  new Runnable() {
4858
5222
  @Override
4859
5223
  public void run() {
4860
- show();
5224
+ showAccordingToLayerModeOrFallback();
4861
5225
  resolveOpenWebViewIfNeeded();
4862
5226
  }
4863
5227
  }
@@ -5026,6 +5390,7 @@ public class WebViewDialog extends Dialog implements ProxyResponseRouting.ProxyR
5026
5390
  @Override
5027
5391
  public void dismiss() {
5028
5392
  scheduleHostWebViewInsetRestore();
5393
+ detachBackLayer();
5029
5394
 
5030
5395
  // First, stop any ongoing operations and disable further interactions
5031
5396
  if (_webView != null) {
@@ -5079,6 +5444,13 @@ public class WebViewDialog extends Dialog implements ProxyResponseRouting.ProxyR
5079
5444
  Log.w("InAppBrowser", "Could not clear file inputs (WebView may be in invalid state): " + e.getMessage());
5080
5445
  }
5081
5446
 
5447
+ if (_options != null && !_options.getPersistWebViewData()) {
5448
+ _webView.clearCache(true);
5449
+ _webView.clearHistory();
5450
+ _webView.clearFormData();
5451
+ _webView.clearSslPreferences();
5452
+ }
5453
+
5082
5454
  forceStopMediaCapture(_webView);
5083
5455
 
5084
5456
  // Remove JavaScript interfaces before destroying
@@ -6097,7 +6469,11 @@ public class WebViewDialog extends Dialog implements ProxyResponseRouting.ProxyR
6097
6469
  }
6098
6470
 
6099
6471
  // Apply new dimensions
6100
- applyDimensions();
6472
+ if (backLayerActive) {
6473
+ applyBackLayerDimensions();
6474
+ } else {
6475
+ applyDimensions();
6476
+ }
6101
6477
  }
6102
6478
 
6103
6479
  public void setEnabledSafeTopMargin(boolean enabled) {
@@ -6118,4 +6494,8 @@ public class WebViewDialog extends Dialog implements ProxyResponseRouting.ProxyR
6118
6494
  private float getPixels(int dp) {
6119
6495
  return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, _context.getResources().getDisplayMetrics());
6120
6496
  }
6497
+
6498
+ private float getPixels(double dp) {
6499
+ return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, (float) dp, _context.getResources().getDisplayMetrics());
6500
+ }
6121
6501
  }