@capgo/capacitor-inappbrowser 8.18.1 → 8.20.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 +126 -5
- package/android/src/main/java/ee/forgr/capacitor_inappbrowser/CapgoInAppBrowserPlugin.java +75 -3
- package/android/src/main/java/ee/forgr/capacitor_inappbrowser/FullscreenWindowState.java +72 -0
- package/android/src/main/java/ee/forgr/capacitor_inappbrowser/Options.java +21 -0
- package/android/src/main/java/ee/forgr/capacitor_inappbrowser/WebViewDialog.java +238 -21
- package/android/src/main/res/drawable/ic_fullscreen_exit.xml +10 -0
- package/android/src/main/res/layout/fullscreen_exit_button.xml +24 -0
- package/android/src/main/res/values/fullscreen_exit.xml +10 -0
- package/android/src/main/res/values/strings.xml +1 -0
- package/android/src/main/res/xml/fullscreen_exit_shape.xml +6 -0
- package/dist/docs.json +129 -1
- package/dist/esm/definitions.d.ts +78 -1
- package/dist/esm/definitions.js.map +1 -1
- package/dist/esm/web.d.ts +9 -0
- package/dist/esm/web.js +11 -0
- package/dist/esm/web.js.map +1 -1
- package/dist/plugin.cjs.js +11 -0
- package/dist/plugin.cjs.js.map +1 -1
- package/dist/plugin.js +11 -0
- package/dist/plugin.js.map +1 -1
- package/ios/Sources/InAppBrowserPlugin/BrowserFullscreenSupport.swift +178 -0
- package/ios/Sources/InAppBrowserPlugin/DownloadPreviewSupport.swift +28 -0
- package/ios/Sources/InAppBrowserPlugin/InAppBrowserPlugin.swift +90 -4
- package/ios/Sources/InAppBrowserPlugin/WKWebViewController.swift +40 -7
- package/package.json +2 -2
|
@@ -40,6 +40,7 @@ import android.util.Log;
|
|
|
40
40
|
import android.util.TypedValue;
|
|
41
41
|
import android.view.Gravity;
|
|
42
42
|
import android.view.KeyEvent;
|
|
43
|
+
import android.view.LayoutInflater;
|
|
43
44
|
import android.view.MotionEvent;
|
|
44
45
|
import android.view.View;
|
|
45
46
|
import android.view.ViewGroup;
|
|
@@ -74,6 +75,8 @@ import androidx.activity.OnBackPressedCallback;
|
|
|
74
75
|
import androidx.activity.result.ActivityResult;
|
|
75
76
|
import androidx.activity.result.ActivityResultLauncher;
|
|
76
77
|
import androidx.activity.result.contract.ActivityResultContracts;
|
|
78
|
+
import androidx.appcompat.view.ContextThemeWrapper;
|
|
79
|
+
import androidx.appcompat.widget.TooltipCompat;
|
|
77
80
|
import androidx.coordinatorlayout.widget.CoordinatorLayout;
|
|
78
81
|
import androidx.core.content.FileProvider;
|
|
79
82
|
import androidx.core.content.res.ResourcesCompat;
|
|
@@ -91,6 +94,8 @@ import com.caverock.androidsvg.SVG;
|
|
|
91
94
|
import com.caverock.androidsvg.SVGParseException;
|
|
92
95
|
import com.getcapacitor.JSObject;
|
|
93
96
|
import com.getcapacitor.PluginCall;
|
|
97
|
+
import com.google.android.material.button.MaterialButton;
|
|
98
|
+
import com.google.android.material.color.DynamicColors;
|
|
94
99
|
import java.io.ByteArrayInputStream;
|
|
95
100
|
import java.io.ByteArrayOutputStream;
|
|
96
101
|
import java.io.File;
|
|
@@ -338,6 +343,154 @@ public class WebViewDialog extends ComponentDialog implements ProxyResponseRouti
|
|
|
338
343
|
private SwipeRefreshLayout swipeRefreshLayout;
|
|
339
344
|
private boolean reloadFromGestureInProgress = false;
|
|
340
345
|
private WebView _webView;
|
|
346
|
+
private FullscreenWindowState nativeFullscreenWindow;
|
|
347
|
+
private FullscreenWindowState mediaFullscreenWindow;
|
|
348
|
+
private MaterialButton fullscreenExitButton;
|
|
349
|
+
private java.util.function.Consumer<Boolean> fullscreenChangeListener;
|
|
350
|
+
private final java.util.Map<View, Integer> fullscreenVisibility = new java.util.HashMap<>();
|
|
351
|
+
private int[] fullscreenPadding;
|
|
352
|
+
private CoordinatorLayout.Behavior<?> fullscreenContentBehavior;
|
|
353
|
+
private String fullscreenOrigin;
|
|
354
|
+
|
|
355
|
+
public void setFullscreenChangeListener(java.util.function.Consumer<Boolean> listener) {
|
|
356
|
+
fullscreenChangeListener = listener;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
public boolean isFullscreen() {
|
|
360
|
+
return nativeFullscreenWindow != null;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
public void clearFullscreen() {
|
|
364
|
+
if (_options != null) _options.setFullscreen(false);
|
|
365
|
+
exitCustomFullscreenView();
|
|
366
|
+
setFullscreen(false);
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
public void setFullscreen(boolean enabled) {
|
|
370
|
+
if (!enabled && _options != null) _options.setFullscreen(false);
|
|
371
|
+
if (enabled == isFullscreen()) return;
|
|
372
|
+
if (enabled) {
|
|
373
|
+
if (
|
|
374
|
+
_webView == null ||
|
|
375
|
+
!activeForBackNavigation ||
|
|
376
|
+
isDismissing ||
|
|
377
|
+
isHiddenModeActive ||
|
|
378
|
+
(_options != null &&
|
|
379
|
+
(_options.isHidden() ||
|
|
380
|
+
_options.isToBack() ||
|
|
381
|
+
_options.getWidth() != null ||
|
|
382
|
+
_options.getHeight() != null ||
|
|
383
|
+
_options.getX() != null ||
|
|
384
|
+
_options.getY() != null)) ||
|
|
385
|
+
backLayerActive ||
|
|
386
|
+
getWindow() == null
|
|
387
|
+
) {
|
|
388
|
+
throw new IllegalStateException("Fullscreen requires a visible, full-size front WebView");
|
|
389
|
+
}
|
|
390
|
+
// Finish media first: its saved window state must not outlive the browser baseline.
|
|
391
|
+
exitCustomFullscreenView();
|
|
392
|
+
nativeFullscreenWindow = new FullscreenWindowState(getWindow());
|
|
393
|
+
fullscreenOrigin = origin(_webView.getUrl() != null ? _webView.getUrl() : _options.getUrl());
|
|
394
|
+
for (int id : new int[] { R.id.app_bar_layout, R.id.status_bar_color_view }) {
|
|
395
|
+
View view = findBrowserContentDescendant(id);
|
|
396
|
+
if (view != null) {
|
|
397
|
+
fullscreenVisibility.put(view, view.getVisibility());
|
|
398
|
+
view.setVisibility(View.GONE);
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
View container = findBrowserContentDescendant(R.id.content_browser_layout);
|
|
402
|
+
if (container != null) {
|
|
403
|
+
fullscreenPadding = new int[] {
|
|
404
|
+
container.getPaddingLeft(),
|
|
405
|
+
container.getPaddingTop(),
|
|
406
|
+
container.getPaddingRight(),
|
|
407
|
+
container.getPaddingBottom()
|
|
408
|
+
};
|
|
409
|
+
container.setPadding(0, 0, 0, 0);
|
|
410
|
+
if (container.getLayoutParams() instanceof CoordinatorLayout.LayoutParams params) {
|
|
411
|
+
// A hidden appbar retains its measured height; its scrolling behavior still reserves that space.
|
|
412
|
+
fullscreenContentBehavior = params.getBehavior();
|
|
413
|
+
params.setBehavior(null);
|
|
414
|
+
container.setLayoutParams(params);
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
nativeFullscreenWindow.enter();
|
|
418
|
+
// Scope Material styling to the overlay, including hosts without a Material theme.
|
|
419
|
+
Context buttonContext = DynamicColors.wrapContextIfAvailable(
|
|
420
|
+
new ContextThemeWrapper(getContext(), com.google.android.material.R.style.Theme_Material3_DayNight_NoActionBar)
|
|
421
|
+
);
|
|
422
|
+
ViewGroup decor = (ViewGroup) getWindow().getDecorView();
|
|
423
|
+
fullscreenExitButton = (MaterialButton) LayoutInflater.from(buttonContext).inflate(
|
|
424
|
+
R.layout.fullscreen_exit_button,
|
|
425
|
+
decor,
|
|
426
|
+
false
|
|
427
|
+
);
|
|
428
|
+
TooltipCompat.setTooltipText(fullscreenExitButton, getContext().getString(R.string.exit_fullscreen));
|
|
429
|
+
fullscreenExitButton.setOnClickListener((v) -> clearFullscreen());
|
|
430
|
+
decor.addView(fullscreenExitButton);
|
|
431
|
+
ViewCompat.setOnApplyWindowInsetsListener(fullscreenExitButton, (v, insets) -> {
|
|
432
|
+
updateFullscreenExitInsets(insets);
|
|
433
|
+
return insets;
|
|
434
|
+
});
|
|
435
|
+
ViewCompat.requestApplyInsets(fullscreenExitButton);
|
|
436
|
+
} else {
|
|
437
|
+
exitCustomFullscreenView();
|
|
438
|
+
FullscreenWindowState baseline = nativeFullscreenWindow;
|
|
439
|
+
nativeFullscreenWindow = null;
|
|
440
|
+
if (fullscreenExitButton != null && fullscreenExitButton.getParent() instanceof ViewGroup parent) parent.removeView(
|
|
441
|
+
fullscreenExitButton
|
|
442
|
+
);
|
|
443
|
+
fullscreenExitButton = null;
|
|
444
|
+
for (java.util.Map.Entry<View, Integer> entry : fullscreenVisibility.entrySet()) entry.getKey().setVisibility(entry.getValue());
|
|
445
|
+
fullscreenVisibility.clear();
|
|
446
|
+
View container = findBrowserContentDescendant(R.id.content_browser_layout);
|
|
447
|
+
if (container != null && fullscreenPadding != null) container.setPadding(
|
|
448
|
+
fullscreenPadding[0],
|
|
449
|
+
fullscreenPadding[1],
|
|
450
|
+
fullscreenPadding[2],
|
|
451
|
+
fullscreenPadding[3]
|
|
452
|
+
);
|
|
453
|
+
fullscreenPadding = null;
|
|
454
|
+
if (container != null && container.getLayoutParams() instanceof CoordinatorLayout.LayoutParams params) {
|
|
455
|
+
params.setBehavior(fullscreenContentBehavior);
|
|
456
|
+
container.setLayoutParams(params);
|
|
457
|
+
}
|
|
458
|
+
fullscreenContentBehavior = null;
|
|
459
|
+
baseline.restore();
|
|
460
|
+
reapplyInsetsFromWindowRoot();
|
|
461
|
+
}
|
|
462
|
+
if (fullscreenChangeListener != null) fullscreenChangeListener.accept(enabled);
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
private void updateFullscreenExitInsets(WindowInsetsCompat insets) {
|
|
466
|
+
if (fullscreenExitButton == null || insets == null) return;
|
|
467
|
+
// Hidden status bars need no space in landscape; cutouts and navigation gestures always do.
|
|
468
|
+
Insets safe = Insets.max(
|
|
469
|
+
insets.getInsets(WindowInsetsCompat.Type.systemBars()),
|
|
470
|
+
insets.getInsetsIgnoringVisibility(WindowInsetsCompat.Type.navigationBars() | WindowInsetsCompat.Type.displayCutout())
|
|
471
|
+
);
|
|
472
|
+
FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) fullscreenExitButton.getLayoutParams();
|
|
473
|
+
int margin = getContext().getResources().getDimensionPixelSize(R.dimen.fullscreen_exit_margin);
|
|
474
|
+
params.topMargin = safe.top + margin;
|
|
475
|
+
params.rightMargin = safe.right + margin;
|
|
476
|
+
fullscreenExitButton.setLayoutParams(params);
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
private static String origin(String url) {
|
|
480
|
+
if (url == null) return null;
|
|
481
|
+
try {
|
|
482
|
+
URI uri = new URI(url);
|
|
483
|
+
String scheme = uri.getScheme();
|
|
484
|
+
String host = uri.getHost();
|
|
485
|
+
if (scheme == null || host == null) return url;
|
|
486
|
+
int port = uri.getPort();
|
|
487
|
+
if (port == -1) port = "https".equalsIgnoreCase(scheme) ? 443 : 80;
|
|
488
|
+
return scheme.toLowerCase(java.util.Locale.ROOT) + "://" + host.toLowerCase(java.util.Locale.ROOT) + ":" + port;
|
|
489
|
+
} catch (Exception error) {
|
|
490
|
+
return url;
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
|
|
341
494
|
// HTML5/iframe fullscreen (e.g. embedded YouTube) routes through WebChromeClient custom views.
|
|
342
495
|
private View customFullscreenView;
|
|
343
496
|
private WebChromeClient.CustomViewCallback customViewCallback;
|
|
@@ -469,11 +622,24 @@ public class WebViewDialog extends ComponentDialog implements ProxyResponseRouti
|
|
|
469
622
|
@Override
|
|
470
623
|
protected void onStart() {
|
|
471
624
|
super.onStart();
|
|
625
|
+
applyStartupFullscreen();
|
|
472
626
|
// ComponentDialog registers its own back callback in onCreate; re-add ours last so
|
|
473
627
|
// handleBrowserBackNavigation runs before the built-in cancel callback (LIFO order).
|
|
474
628
|
ensureDialogBackHandlerOnTop();
|
|
475
629
|
}
|
|
476
630
|
|
|
631
|
+
private void applyStartupFullscreen() {
|
|
632
|
+
if (_options != null && _options.isFullscreen() && !_options.isHidden()) {
|
|
633
|
+
_options.setFullscreen(false);
|
|
634
|
+
try {
|
|
635
|
+
setFullscreen(true);
|
|
636
|
+
} catch (IllegalStateException error) {
|
|
637
|
+
// Deferred presentation or page-initiated show may target an inactive WebView.
|
|
638
|
+
Log.w("InAppBrowser", "Skipping deferred fullscreen entry: " + error.getMessage());
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
|
|
477
643
|
@Override
|
|
478
644
|
public void show() {
|
|
479
645
|
super.show();
|
|
@@ -698,6 +864,7 @@ public class WebViewDialog extends ComponentDialog implements ProxyResponseRouti
|
|
|
698
864
|
}
|
|
699
865
|
|
|
700
866
|
public boolean sendToBack(boolean transparentBackground) {
|
|
867
|
+
clearFullscreen();
|
|
701
868
|
if (_options != null) {
|
|
702
869
|
_options.setToBack(true);
|
|
703
870
|
_options.setTransparentBackground(transparentBackground);
|
|
@@ -925,7 +1092,10 @@ public class WebViewDialog extends ComponentDialog implements ProxyResponseRouti
|
|
|
925
1092
|
Log.e("InAppBrowser", "Cannot hide - activity is null");
|
|
926
1093
|
return;
|
|
927
1094
|
}
|
|
928
|
-
activity.runOnUiThread(() ->
|
|
1095
|
+
activity.runOnUiThread(() -> {
|
|
1096
|
+
clearFullscreen();
|
|
1097
|
+
setHidden(true);
|
|
1098
|
+
});
|
|
929
1099
|
}
|
|
930
1100
|
|
|
931
1101
|
@JavascriptInterface
|
|
@@ -2162,7 +2332,10 @@ public class WebViewDialog extends ComponentDialog implements ProxyResponseRouti
|
|
|
2162
2332
|
setContentView(R.layout.activity_browser);
|
|
2163
2333
|
|
|
2164
2334
|
// If custom dimensions are set, configure for touch passthrough
|
|
2165
|
-
if (
|
|
2335
|
+
if (
|
|
2336
|
+
_options != null &&
|
|
2337
|
+
(_options.getWidth() != null || _options.getHeight() != null || _options.getX() != null || _options.getY() != null)
|
|
2338
|
+
) {
|
|
2166
2339
|
Window window = getWindow();
|
|
2167
2340
|
if (window != null) {
|
|
2168
2341
|
// Make the dialog background transparent
|
|
@@ -2850,9 +3023,9 @@ public class WebViewDialog extends ComponentDialog implements ProxyResponseRouti
|
|
|
2850
3023
|
}
|
|
2851
3024
|
|
|
2852
3025
|
try {
|
|
2853
|
-
loadInitialWebViewContent(requestHeaders);
|
|
2854
3026
|
setupToolbar();
|
|
2855
3027
|
setWebViewClient();
|
|
3028
|
+
loadInitialWebViewContent(requestHeaders);
|
|
2856
3029
|
|
|
2857
3030
|
if (this._options.isHidden()) {
|
|
2858
3031
|
if (_options.isPopupWindowMode() || _options.getInvisibilityMode() == Options.InvisibilityMode.FAKE_VISIBLE) {
|
|
@@ -3002,6 +3175,7 @@ public class WebViewDialog extends ComponentDialog implements ProxyResponseRouti
|
|
|
3002
3175
|
if (activeForBackNavigation == active) {
|
|
3003
3176
|
return;
|
|
3004
3177
|
}
|
|
3178
|
+
if (!active && (isFullscreen() || customFullscreenView != null)) clearFullscreen();
|
|
3005
3179
|
activeForBackNavigation = active;
|
|
3006
3180
|
syncBackNavigationHandlers();
|
|
3007
3181
|
}
|
|
@@ -3030,10 +3204,18 @@ public class WebViewDialog extends ComponentDialog implements ProxyResponseRouti
|
|
|
3030
3204
|
}
|
|
3031
3205
|
|
|
3032
3206
|
private boolean shouldConsumeBackPress() {
|
|
3033
|
-
return WebViewBackNavigationSupport.shouldConsumeBackPress(isShowing(), isHiddenModeActive, backLayerActive);
|
|
3207
|
+
return isFullscreen() || WebViewBackNavigationSupport.shouldConsumeBackPress(isShowing(), isHiddenModeActive, backLayerActive);
|
|
3034
3208
|
}
|
|
3035
3209
|
|
|
3036
3210
|
private void handleBrowserBackNavigation() {
|
|
3211
|
+
if (WebViewCustomFullscreenSupport.shouldConsumeBackPress(customFullscreenView != null)) {
|
|
3212
|
+
exitCustomFullscreenView();
|
|
3213
|
+
return;
|
|
3214
|
+
}
|
|
3215
|
+
if (isFullscreen()) {
|
|
3216
|
+
clearFullscreen();
|
|
3217
|
+
return;
|
|
3218
|
+
}
|
|
3037
3219
|
if (_options == null) {
|
|
3038
3220
|
dismiss();
|
|
3039
3221
|
return;
|
|
@@ -3106,8 +3288,9 @@ public class WebViewDialog extends ComponentDialog implements ProxyResponseRouti
|
|
|
3106
3288
|
_webView.setVisibility(View.INVISIBLE);
|
|
3107
3289
|
}
|
|
3108
3290
|
|
|
3109
|
-
window
|
|
3110
|
-
|
|
3291
|
+
mediaFullscreenWindow = new FullscreenWindowState(window);
|
|
3292
|
+
mediaFullscreenWindow.enter();
|
|
3293
|
+
if (fullscreenExitButton != null) fullscreenExitButton.bringToFront();
|
|
3111
3294
|
|
|
3112
3295
|
if (WebViewCustomFullscreenSupport.shouldRegisterHostBackHandler(backLayerActive)) {
|
|
3113
3296
|
registerCustomFullscreenBackHandler();
|
|
@@ -3162,15 +3345,12 @@ public class WebViewDialog extends ComponentDialog implements ProxyResponseRouti
|
|
|
3162
3345
|
_webView.setVisibility(View.VISIBLE);
|
|
3163
3346
|
}
|
|
3164
3347
|
|
|
3165
|
-
|
|
3166
|
-
|
|
3167
|
-
|
|
3168
|
-
WebViewCustomFullscreenSupport.exitImmersiveFullscreen(window, window.getDecorView());
|
|
3169
|
-
reapplyInsetsFromWindowRoot();
|
|
3170
|
-
if (SystemUiChromeSupport.requiresEdgeToEdgeChrome(Build.VERSION.SDK_INT)) {
|
|
3171
|
-
refreshEdgeToEdgeChrome();
|
|
3172
|
-
}
|
|
3348
|
+
if (mediaFullscreenWindow != null) {
|
|
3349
|
+
mediaFullscreenWindow.restore();
|
|
3350
|
+
mediaFullscreenWindow = null;
|
|
3173
3351
|
}
|
|
3352
|
+
if (nativeFullscreenWindow != null) nativeFullscreenWindow.enter();
|
|
3353
|
+
else reapplyInsetsFromWindowRoot();
|
|
3174
3354
|
|
|
3175
3355
|
unregisterCustomFullscreenBackHandler();
|
|
3176
3356
|
|
|
@@ -3275,6 +3455,7 @@ public class WebViewDialog extends ComponentDialog implements ProxyResponseRouti
|
|
|
3275
3455
|
}
|
|
3276
3456
|
|
|
3277
3457
|
public void setHidden(boolean hidden) {
|
|
3458
|
+
if (hidden && (isFullscreen() || (_options != null && !_options.isHidden()))) clearFullscreen();
|
|
3278
3459
|
if (hidden) {
|
|
3279
3460
|
if (!isHiddenModeActive) {
|
|
3280
3461
|
if (getWindow() == null) {
|
|
@@ -3314,6 +3495,9 @@ public class WebViewDialog extends ComponentDialog implements ProxyResponseRouti
|
|
|
3314
3495
|
}
|
|
3315
3496
|
if (_options != null) {
|
|
3316
3497
|
_options.setHidden(hidden);
|
|
3498
|
+
if (isShowing()) {
|
|
3499
|
+
applyStartupFullscreen();
|
|
3500
|
+
}
|
|
3317
3501
|
}
|
|
3318
3502
|
}
|
|
3319
3503
|
|
|
@@ -3508,6 +3692,11 @@ public class WebViewDialog extends ComponentDialog implements ProxyResponseRouti
|
|
|
3508
3692
|
* after portrait↔landscape transitions.
|
|
3509
3693
|
*/
|
|
3510
3694
|
private void refreshLayoutForConfigurationChange() {
|
|
3695
|
+
if (isFullscreen()) {
|
|
3696
|
+
nativeFullscreenWindow.enter();
|
|
3697
|
+
updateFullscreenExitInsets(ViewCompat.getRootWindowInsets(getWindow().getDecorView()));
|
|
3698
|
+
return;
|
|
3699
|
+
}
|
|
3511
3700
|
if (isDismissing || _webView == null) {
|
|
3512
3701
|
return;
|
|
3513
3702
|
}
|
|
@@ -3684,6 +3873,18 @@ public class WebViewDialog extends ComponentDialog implements ProxyResponseRouti
|
|
|
3684
3873
|
return;
|
|
3685
3874
|
}
|
|
3686
3875
|
|
|
3876
|
+
if (isFullscreen()) {
|
|
3877
|
+
updateFullscreenExitInsets(windowInsets);
|
|
3878
|
+
View container = findBrowserContentDescendant(R.id.content_browser_layout);
|
|
3879
|
+
if (container != null) {
|
|
3880
|
+
// FLAG_FULLSCREEN disables adjustResize even before Android 15; keep only the keyboard inset.
|
|
3881
|
+
int imeBottom = windowInsets.isVisible(WindowInsetsCompat.Type.ime())
|
|
3882
|
+
? windowInsets.getInsets(WindowInsetsCompat.Type.ime()).bottom
|
|
3883
|
+
: 0;
|
|
3884
|
+
container.setPadding(0, 0, 0, imeBottom);
|
|
3885
|
+
}
|
|
3886
|
+
return;
|
|
3887
|
+
}
|
|
3687
3888
|
Insets bars = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars());
|
|
3688
3889
|
Insets navigationBars = windowInsets.getInsets(WindowInsetsCompat.Type.navigationBars());
|
|
3689
3890
|
Insets systemGestures = windowInsets.getInsets(WindowInsetsCompat.Type.systemGestures());
|
|
@@ -6316,9 +6517,20 @@ public class WebViewDialog extends ComponentDialog implements ProxyResponseRouti
|
|
|
6316
6517
|
super.onLoadResource(view, url);
|
|
6317
6518
|
}
|
|
6318
6519
|
|
|
6520
|
+
@Override
|
|
6521
|
+
public boolean onRenderProcessGone(WebView view, android.webkit.RenderProcessGoneDetail detail) {
|
|
6522
|
+
clearFullscreen();
|
|
6523
|
+
dismiss();
|
|
6524
|
+
return true;
|
|
6525
|
+
}
|
|
6526
|
+
|
|
6319
6527
|
@Override
|
|
6320
6528
|
public void onPageStarted(WebView view, String url, Bitmap favicon) {
|
|
6321
6529
|
super.onPageStarted(view, url, favicon);
|
|
6530
|
+
if (
|
|
6531
|
+
(isFullscreen() && !java.util.Objects.equals(fullscreenOrigin, origin(url))) ||
|
|
6532
|
+
(_options.isFullscreen() && !java.util.Objects.equals(origin(_options.getUrl()), origin(url)))
|
|
6533
|
+
) clearFullscreen();
|
|
6322
6534
|
if (view == null || _webView == null) {
|
|
6323
6535
|
return;
|
|
6324
6536
|
}
|
|
@@ -6361,13 +6573,13 @@ public class WebViewDialog extends ComponentDialog implements ProxyResponseRouti
|
|
|
6361
6573
|
@Override
|
|
6362
6574
|
public void onPageFinished(WebView view, String url) {
|
|
6363
6575
|
super.onPageFinished(view, url);
|
|
6364
|
-
if (view == null || _webView
|
|
6576
|
+
if (view == null || _webView != view || isDismissing) {
|
|
6365
6577
|
return;
|
|
6366
6578
|
}
|
|
6367
6579
|
if (!isInitialized) {
|
|
6368
6580
|
isInitialized = true;
|
|
6369
6581
|
_webView.clearHistory();
|
|
6370
|
-
if (_options.isPresentAfterPageLoad()) {
|
|
6582
|
+
if (_options.isPresentAfterPageLoad() && !_options.isHidden()) {
|
|
6371
6583
|
boolean usePreShowScript =
|
|
6372
6584
|
_options.getPreShowScript() != null &&
|
|
6373
6585
|
!_options.getPreShowScript().isEmpty() &&
|
|
@@ -6388,7 +6600,9 @@ public class WebViewDialog extends ComponentDialog implements ProxyResponseRouti
|
|
|
6388
6600
|
new Runnable() {
|
|
6389
6601
|
@Override
|
|
6390
6602
|
public void run() {
|
|
6391
|
-
|
|
6603
|
+
if (isDismissing || _webView != view) return;
|
|
6604
|
+
// Visibility can change while the pre-show script is running.
|
|
6605
|
+
if (!_options.isHidden()) showAccordingToLayerModeOrFallback();
|
|
6392
6606
|
resolveOpenWebViewIfNeeded();
|
|
6393
6607
|
}
|
|
6394
6608
|
}
|
|
@@ -6554,7 +6768,7 @@ public class WebViewDialog extends ComponentDialog implements ProxyResponseRouti
|
|
|
6554
6768
|
|
|
6555
6769
|
@Override
|
|
6556
6770
|
public void dismiss() {
|
|
6557
|
-
|
|
6771
|
+
clearFullscreen();
|
|
6558
6772
|
unregisterConfigurationCallbacks();
|
|
6559
6773
|
scheduleHostWebViewInsetRestore();
|
|
6560
6774
|
detachBackLayer();
|
|
@@ -6962,6 +7176,8 @@ public class WebViewDialog extends ComponentDialog implements ProxyResponseRouti
|
|
|
6962
7176
|
? Base64.encodeToString(httpBody.getBytes(StandardCharsets.UTF_8), Base64.NO_WRAP)
|
|
6963
7177
|
: "";
|
|
6964
7178
|
|
|
7179
|
+
// View.post waits for attachment; deferred presentation waits for this first load to finish.
|
|
7180
|
+
// Dispatch through the main looper so hidden and present-after-load WebViews can start loading.
|
|
6965
7181
|
executorService.execute(() -> {
|
|
6966
7182
|
NativeRequestContext requestContext = new NativeRequestContext(
|
|
6967
7183
|
initialUrl,
|
|
@@ -6984,7 +7200,7 @@ public class WebViewDialog extends ComponentDialog implements ProxyResponseRouti
|
|
|
6984
7200
|
if (_webView == null) {
|
|
6985
7201
|
return;
|
|
6986
7202
|
}
|
|
6987
|
-
|
|
7203
|
+
mainHandler.post(() -> loadInitialUrlDirect(initialDirectHeaders, initialMethod, httpBody));
|
|
6988
7204
|
return;
|
|
6989
7205
|
}
|
|
6990
7206
|
|
|
@@ -7000,7 +7216,7 @@ public class WebViewDialog extends ComponentDialog implements ProxyResponseRouti
|
|
|
7000
7216
|
return;
|
|
7001
7217
|
}
|
|
7002
7218
|
|
|
7003
|
-
|
|
7219
|
+
mainHandler.post(() -> {
|
|
7004
7220
|
if (_webView == null) {
|
|
7005
7221
|
return;
|
|
7006
7222
|
}
|
|
@@ -7015,7 +7231,7 @@ public class WebViewDialog extends ComponentDialog implements ProxyResponseRouti
|
|
|
7015
7231
|
ProxyRequestSupport.createNativeRequestFailureBody(initialUrl, error),
|
|
7016
7232
|
StandardCharsets.UTF_8
|
|
7017
7233
|
);
|
|
7018
|
-
|
|
7234
|
+
mainHandler.post(() -> {
|
|
7019
7235
|
if (_webView == null) {
|
|
7020
7236
|
return;
|
|
7021
7237
|
}
|
|
@@ -7700,6 +7916,7 @@ public class WebViewDialog extends ComponentDialog implements ProxyResponseRouti
|
|
|
7700
7916
|
* Update dimensions at runtime
|
|
7701
7917
|
*/
|
|
7702
7918
|
public void updateDimensions(Integer width, Integer height, Integer x, Integer y) {
|
|
7919
|
+
clearFullscreen();
|
|
7703
7920
|
// Update options
|
|
7704
7921
|
if (width != null) {
|
|
7705
7922
|
_options.setWidth(width);
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="utf-8"?>
|
|
2
|
+
<!-- Google Material Icons: fullscreen_exit (Apache License 2.0). -->
|
|
3
|
+
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
|
4
|
+
android:width="24dp"
|
|
5
|
+
android:height="24dp"
|
|
6
|
+
android:viewportWidth="24"
|
|
7
|
+
android:viewportHeight="24">
|
|
8
|
+
<path android:fillColor="#FF000000"
|
|
9
|
+
android:pathData="M5,16h3v3h2v-5H5v2zM8,8H5v2h5V5H8v3zM14,19h2v-3h3v-2h-5v5zM16,8V5h-2v5h5V8h-3z" />
|
|
10
|
+
</vector>
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="utf-8"?>
|
|
2
|
+
<!-- The native exit control stays physically top-right in every layout direction. -->
|
|
3
|
+
<com.google.android.material.button.MaterialButton
|
|
4
|
+
xmlns:android="http://schemas.android.com/apk/res/android"
|
|
5
|
+
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
6
|
+
xmlns:tools="http://schemas.android.com/tools"
|
|
7
|
+
tools:ignore="RtlHardcoded"
|
|
8
|
+
style="@style/Widget.Material3.Button.IconButton.Filled.Tonal"
|
|
9
|
+
android:layout_width="48dp"
|
|
10
|
+
android:layout_height="48dp"
|
|
11
|
+
android:layout_gravity="top|right"
|
|
12
|
+
android:layout_marginTop="@dimen/fullscreen_exit_margin"
|
|
13
|
+
android:layout_marginRight="@dimen/fullscreen_exit_margin"
|
|
14
|
+
android:contentDescription="@string/exit_fullscreen"
|
|
15
|
+
android:insetLeft="4dp"
|
|
16
|
+
android:insetTop="4dp"
|
|
17
|
+
android:insetRight="4dp"
|
|
18
|
+
android:insetBottom="4dp"
|
|
19
|
+
android:padding="12dp"
|
|
20
|
+
app:icon="@drawable/ic_fullscreen_exit"
|
|
21
|
+
app:iconGravity="textStart"
|
|
22
|
+
app:iconSize="24dp"
|
|
23
|
+
app:iconPadding="0dp"
|
|
24
|
+
app:shapeAppearance="@xml/fullscreen_exit_shape" />
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="utf-8"?>
|
|
2
|
+
<resources>
|
|
3
|
+
<!-- With the button's 4dp visual inset, this leaves an 8dp gap inside the safe area. -->
|
|
4
|
+
<dimen name="fullscreen_exit_margin">4dp</dimen>
|
|
5
|
+
<style name="FullscreenExitShape" parent="ShapeAppearance.Material3.Corner.Full" />
|
|
6
|
+
<style name="FullscreenExitPressedShape" parent="">
|
|
7
|
+
<item name="cornerFamily">rounded</item>
|
|
8
|
+
<item name="cornerSize">8dp</item>
|
|
9
|
+
</style>
|
|
10
|
+
</resources>
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
<string name="my_string">Just a simple string</string>
|
|
4
4
|
<string name="title_activity_browser">BrowserActivity</string>
|
|
5
5
|
<string name="title_activity_basic">BasicActivity</string>
|
|
6
|
+
<string name="exit_fullscreen">Exit fullscreen</string>
|
|
6
7
|
<string name="close_button">Close Button</string>
|
|
7
8
|
<string name="button_near_done">Button near "done"</string>
|
|
8
9
|
<string name="reload_button">Reload Button</string>
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="utf-8"?>
|
|
2
|
+
<selector xmlns:android="http://schemas.android.com/apk/res/android"
|
|
3
|
+
xmlns:app="http://schemas.android.com/apk/res-auto">
|
|
4
|
+
<item android:state_pressed="true" app:shapeAppearance="@style/FullscreenExitPressedShape" />
|
|
5
|
+
<item app:shapeAppearance="@style/FullscreenExitShape" />
|
|
6
|
+
</selector>
|