@capgo/capacitor-inappbrowser 8.13.4 → 8.13.5

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.
@@ -0,0 +1,41 @@
1
+ package ee.forgr.capacitor_inappbrowser;
2
+
3
+ /**
4
+ * Helpers for deciding when a managed WebView dialog must relayout after a configuration
5
+ * change. Capacitor activities declare {@code android:configChanges}, so the host activity
6
+ * (and this dialog) are not recreated on rotation — layouts and window insets must be
7
+ * refreshed explicitly or the WebView can stop scrolling after portrait→landscape.
8
+ */
9
+ final class OrientationLayoutSupport {
10
+
11
+ private OrientationLayoutSupport() {}
12
+
13
+ static boolean shouldRefreshBrowserLayout(
14
+ Integer previousOrientation,
15
+ Integer previousScreenWidthDp,
16
+ Integer previousScreenHeightDp,
17
+ Integer previousSmallestScreenWidthDp,
18
+ Integer previousDensityDpi,
19
+ int currentOrientation,
20
+ int currentScreenWidthDp,
21
+ int currentScreenHeightDp,
22
+ int currentSmallestScreenWidthDp,
23
+ int currentDensityDpi
24
+ ) {
25
+ if (previousOrientation == null) {
26
+ return true;
27
+ }
28
+
29
+ return (
30
+ previousOrientation != currentOrientation ||
31
+ previousScreenWidthDp == null ||
32
+ previousScreenWidthDp != currentScreenWidthDp ||
33
+ previousScreenHeightDp == null ||
34
+ previousScreenHeightDp != currentScreenHeightDp ||
35
+ previousSmallestScreenWidthDp == null ||
36
+ previousSmallestScreenWidthDp != currentSmallestScreenWidthDp ||
37
+ previousDensityDpi == null ||
38
+ previousDensityDpi != currentDensityDpi
39
+ );
40
+ }
41
+ }
@@ -5,10 +5,12 @@ import android.app.Activity;
5
5
  import android.app.AlertDialog;
6
6
  import android.app.Dialog;
7
7
  import android.content.ActivityNotFoundException;
8
+ import android.content.ComponentCallbacks2;
8
9
  import android.content.Context;
9
10
  import android.content.DialogInterface;
10
11
  import android.content.Intent;
11
12
  import android.content.res.AssetManager;
13
+ import android.content.res.Configuration;
12
14
  import android.content.res.Resources;
13
15
  import android.graphics.Bitmap;
14
16
  import android.graphics.Canvas;
@@ -356,6 +358,20 @@ public class WebViewDialog extends Dialog implements ProxyResponseRouting.ProxyR
356
358
  private boolean cachedTitleIconResolved;
357
359
  private boolean isHiddenModeActive = false;
358
360
  private boolean toolbarHideInProgress = false;
361
+ private boolean configurationCallbacksRegistered = false;
362
+ private Configuration lastConfiguration;
363
+ private final ComponentCallbacks2 configurationCallbacks = new ComponentCallbacks2() {
364
+ @Override
365
+ public void onConfigurationChanged(Configuration newConfig) {
366
+ handleConfigurationChanged(newConfig);
367
+ }
368
+
369
+ @Override
370
+ public void onLowMemory() {}
371
+
372
+ @Override
373
+ public void onTrimMemory(int level) {}
374
+ };
359
375
  private WindowManager.LayoutParams previousWindowAttributes;
360
376
  private Drawable previousWindowBackground;
361
377
  private ViewGroup.LayoutParams previousWebViewLayoutParams;
@@ -2824,6 +2840,9 @@ public class WebViewDialog extends Dialog implements ProxyResponseRouting.ProxyR
2824
2840
  showAccordingToLayerModeOrFallback();
2825
2841
  resolveOpenWebViewIfNeeded();
2826
2842
  }
2843
+
2844
+ // Capacitor activities handle orientation themselves; refresh dialog layout explicitly.
2845
+ registerConfigurationCallbacks();
2827
2846
  }
2828
2847
 
2829
2848
  private void applyHiddenMode() {
@@ -2974,22 +2993,11 @@ public class WebViewDialog extends Dialog implements ProxyResponseRouting.ProxyR
2974
2993
  // Check if we need Android 15+ specific fixes
2975
2994
  boolean isAndroid15Plus = Build.VERSION.SDK_INT >= 35;
2976
2995
 
2977
- // Find status bar color view and toolbar for Android 15+ specific handling
2978
- View statusBarColorView = findViewById(R.id.status_bar_color_view);
2979
2996
  View toolbarView = findViewById(R.id.tool_bar);
2980
2997
 
2981
2998
  // Fix content browser layout height for all Android versions to allow proper scrolling
2982
2999
  // This fixes landscape scrolling issues where bottom content is unreachable
2983
- View contentBrowserLayout = findViewById(R.id.content_browser_layout);
2984
- if (contentBrowserLayout != null) {
2985
- ViewGroup.LayoutParams layoutParams = contentBrowserLayout.getLayoutParams();
2986
- if (layoutParams != null) {
2987
- // Use MATCH_PARENT for height to allow proper scrolling in all orientations
2988
- // The AppBarLayout's layout_behavior will handle positioning automatically
2989
- layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT;
2990
- contentBrowserLayout.setLayoutParams(layoutParams);
2991
- }
2992
- }
3000
+ ensureContentBrowserMatchParentHeight();
2993
3001
 
2994
3002
  boolean isBlankToolbar = _options != null && TextUtils.equals(_options.getToolbarType(), "blank");
2995
3003
  if (isBlankToolbar) {
@@ -2998,53 +3006,7 @@ public class WebViewDialog extends Dialog implements ProxyResponseRouting.ProxyR
2998
3006
 
2999
3007
  // Special handling for Android 15+
3000
3008
  if (isAndroid15Plus && !isBlankToolbar) {
3001
- // Get AppBarLayout which contains the toolbar
3002
- if (toolbarView != null && toolbarView.getParent() instanceof com.google.android.material.appbar.AppBarLayout appBarLayout) {
3003
- // Remove elevation to eliminate shadows (only on Android 15+)
3004
- appBarLayout.setElevation(0);
3005
- appBarLayout.setStateListAnimator(null);
3006
- appBarLayout.setOutlineProvider(null);
3007
-
3008
- // Determine background color to use
3009
- int backgroundColor = Color.BLACK; // Default fallback
3010
- if (_options.getToolbarColor() != null && !_options.getToolbarColor().isEmpty()) {
3011
- try {
3012
- backgroundColor = Color.parseColor(_options.getToolbarColor());
3013
- } catch (IllegalArgumentException e) {
3014
- Log.e("InAppBrowser", "Invalid toolbar color, using black: " + e.getMessage());
3015
- }
3016
- } else {
3017
- // Follow system theme if no color specified
3018
- boolean isDarkTheme = isDarkThemeEnabled();
3019
- backgroundColor = isDarkTheme ? Color.BLACK : Color.WHITE;
3020
- }
3021
-
3022
- // Apply fixes for Android 15+ using a delayed post
3023
- final int finalBgColor = backgroundColor;
3024
- _webView.post(() -> {
3025
- // Get status bar height
3026
- int statusBarHeight = 0;
3027
- int resourceId = getContext().getResources().getIdentifier("status_bar_height", "dimen", "android");
3028
- if (resourceId > 0) {
3029
- statusBarHeight = getContext().getResources().getDimensionPixelSize(resourceId);
3030
- }
3031
-
3032
- // Fix status bar view
3033
- if (statusBarColorView != null) {
3034
- ViewGroup.LayoutParams params = statusBarColorView.getLayoutParams();
3035
- params.height = statusBarHeight;
3036
- statusBarColorView.setLayoutParams(params);
3037
- statusBarColorView.setBackgroundColor(finalBgColor);
3038
- statusBarColorView.setVisibility(View.VISIBLE);
3039
- }
3040
-
3041
- // Fix AppBarLayout position
3042
- ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) appBarLayout.getLayoutParams();
3043
- params.topMargin = statusBarHeight;
3044
- appBarLayout.setLayoutParams(params);
3045
- appBarLayout.setBackgroundColor(finalBgColor);
3046
- });
3047
- }
3009
+ refreshEdgeToEdgeChrome();
3048
3010
  }
3049
3011
 
3050
3012
  // Resolve insets from the dialog window root; layout children can receive already-fitted zero insets.
@@ -3152,6 +3114,232 @@ public class WebViewDialog extends Dialog implements ProxyResponseRouting.ProxyR
3152
3114
  mainHandler.postDelayed(this::applyContainerInsetsSnapshot, 1200);
3153
3115
  }
3154
3116
 
3117
+ private void registerConfigurationCallbacks() {
3118
+ if (configurationCallbacksRegistered) {
3119
+ return;
3120
+ }
3121
+
3122
+ Context callbackContext = activity != null ? activity : _context;
3123
+ if (callbackContext == null) {
3124
+ return;
3125
+ }
3126
+
3127
+ lastConfiguration = new Configuration(callbackContext.getResources().getConfiguration());
3128
+ callbackContext.registerComponentCallbacks(configurationCallbacks);
3129
+ configurationCallbacksRegistered = true;
3130
+ }
3131
+
3132
+ private void unregisterConfigurationCallbacks() {
3133
+ if (!configurationCallbacksRegistered) {
3134
+ return;
3135
+ }
3136
+
3137
+ Context callbackContext = activity != null ? activity : _context;
3138
+ if (callbackContext != null) {
3139
+ try {
3140
+ callbackContext.unregisterComponentCallbacks(configurationCallbacks);
3141
+ } catch (Exception e) {
3142
+ Log.w("InAppBrowser", "Failed to unregister configuration callbacks: " + e.getMessage());
3143
+ }
3144
+ }
3145
+
3146
+ configurationCallbacksRegistered = false;
3147
+ lastConfiguration = null;
3148
+ }
3149
+
3150
+ private void handleConfigurationChanged(Configuration newConfig) {
3151
+ if (isDismissing || _webView == null) {
3152
+ return;
3153
+ }
3154
+
3155
+ Integer previousOrientation = lastConfiguration != null ? lastConfiguration.orientation : null;
3156
+ Integer previousScreenWidthDp = lastConfiguration != null ? lastConfiguration.screenWidthDp : null;
3157
+ Integer previousScreenHeightDp = lastConfiguration != null ? lastConfiguration.screenHeightDp : null;
3158
+ Integer previousSmallestScreenWidthDp = lastConfiguration != null ? lastConfiguration.smallestScreenWidthDp : null;
3159
+ Integer previousDensityDpi = lastConfiguration != null ? lastConfiguration.densityDpi : null;
3160
+
3161
+ int currentOrientation = newConfig != null ? newConfig.orientation : (previousOrientation != null ? previousOrientation : 0);
3162
+ int currentScreenWidthDp =
3163
+ newConfig != null ? newConfig.screenWidthDp : (previousScreenWidthDp != null ? previousScreenWidthDp : 0);
3164
+ int currentScreenHeightDp =
3165
+ newConfig != null ? newConfig.screenHeightDp : (previousScreenHeightDp != null ? previousScreenHeightDp : 0);
3166
+ int currentSmallestScreenWidthDp =
3167
+ newConfig != null
3168
+ ? newConfig.smallestScreenWidthDp
3169
+ : (previousSmallestScreenWidthDp != null ? previousSmallestScreenWidthDp : 0);
3170
+ int currentDensityDpi = newConfig != null ? newConfig.densityDpi : (previousDensityDpi != null ? previousDensityDpi : 0);
3171
+
3172
+ boolean shouldRefresh = OrientationLayoutSupport.shouldRefreshBrowserLayout(
3173
+ previousOrientation,
3174
+ previousScreenWidthDp,
3175
+ previousScreenHeightDp,
3176
+ previousSmallestScreenWidthDp,
3177
+ previousDensityDpi,
3178
+ currentOrientation,
3179
+ currentScreenWidthDp,
3180
+ currentScreenHeightDp,
3181
+ currentSmallestScreenWidthDp,
3182
+ currentDensityDpi
3183
+ );
3184
+
3185
+ if (newConfig != null) {
3186
+ lastConfiguration = new Configuration(newConfig);
3187
+ }
3188
+
3189
+ if (!shouldRefresh) {
3190
+ return;
3191
+ }
3192
+
3193
+ if (Looper.myLooper() == Looper.getMainLooper()) {
3194
+ refreshLayoutForConfigurationChange();
3195
+ } else {
3196
+ mainHandler.post(this::refreshLayoutForConfigurationChange);
3197
+ }
3198
+ }
3199
+
3200
+ /**
3201
+ * Capacitor keeps the host activity alive across orientation changes. Re-apply fullscreen
3202
+ * window dimensions, system chrome, and safe-area insets so the WebView stays scrollable
3203
+ * after portrait↔landscape transitions.
3204
+ */
3205
+ private void refreshLayoutForConfigurationChange() {
3206
+ if (isDismissing || _webView == null) {
3207
+ return;
3208
+ }
3209
+
3210
+ if (isHiddenModeActive) {
3211
+ requestSafeAreaInsets();
3212
+ return;
3213
+ }
3214
+
3215
+ if (backLayerActive) {
3216
+ applyBackLayerDimensions();
3217
+ } else {
3218
+ applyDimensions();
3219
+ }
3220
+
3221
+ ensureContentBrowserMatchParentHeight();
3222
+
3223
+ boolean isBlankToolbar = _options != null && TextUtils.equals(_options.getToolbarType(), "blank");
3224
+ if (isBlankToolbar) {
3225
+ configureBlankToolbarLayout();
3226
+ } else if (Build.VERSION.SDK_INT >= 35) {
3227
+ refreshEdgeToEdgeChrome();
3228
+ } else {
3229
+ refreshStatusBarColorViewHeight();
3230
+ }
3231
+
3232
+ View coordinator = findViewById(R.id.coordinator_layout);
3233
+ if (coordinator != null) {
3234
+ coordinator.requestLayout();
3235
+ }
3236
+
3237
+ if (swipeRefreshLayout != null) {
3238
+ swipeRefreshLayout.requestLayout();
3239
+ }
3240
+
3241
+ _webView.requestLayout();
3242
+ _webView.invalidate();
3243
+ requestSafeAreaInsets();
3244
+
3245
+ mainHandler.post(this::applyContainerInsetsSnapshot);
3246
+ mainHandler.postDelayed(this::applyContainerInsetsSnapshot, 100);
3247
+ mainHandler.postDelayed(this::applyContainerInsetsSnapshot, 300);
3248
+
3249
+ _webView.post(() -> {
3250
+ if (_webView == null) {
3251
+ return;
3252
+ }
3253
+ _webView.evaluateJavascript("(function(){window.dispatchEvent(new Event('resize'));})();", null);
3254
+ });
3255
+ }
3256
+
3257
+ private void ensureContentBrowserMatchParentHeight() {
3258
+ View contentBrowserLayout = findViewById(R.id.content_browser_layout);
3259
+ if (contentBrowserLayout == null) {
3260
+ return;
3261
+ }
3262
+
3263
+ ViewGroup.LayoutParams layoutParams = contentBrowserLayout.getLayoutParams();
3264
+ if (layoutParams == null) {
3265
+ return;
3266
+ }
3267
+
3268
+ layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT;
3269
+ contentBrowserLayout.setLayoutParams(layoutParams);
3270
+ }
3271
+
3272
+ private void refreshStatusBarColorViewHeight() {
3273
+ View statusBarColorView = findViewById(R.id.status_bar_color_view);
3274
+ if (statusBarColorView == null) {
3275
+ return;
3276
+ }
3277
+
3278
+ int statusBarHeight = getSystemStatusBarHeight();
3279
+ ViewGroup.LayoutParams params = statusBarColorView.getLayoutParams();
3280
+ if (params == null) {
3281
+ return;
3282
+ }
3283
+
3284
+ params.height = statusBarHeight;
3285
+ statusBarColorView.setLayoutParams(params);
3286
+ statusBarColorView.requestLayout();
3287
+ }
3288
+
3289
+ private void refreshEdgeToEdgeChrome() {
3290
+ if (_webView == null || _options == null) {
3291
+ return;
3292
+ }
3293
+
3294
+ View statusBarColorView = findViewById(R.id.status_bar_color_view);
3295
+ View toolbarView = findViewById(R.id.tool_bar);
3296
+ if (toolbarView == null || !(toolbarView.getParent() instanceof com.google.android.material.appbar.AppBarLayout appBarLayout)) {
3297
+ refreshStatusBarColorViewHeight();
3298
+ return;
3299
+ }
3300
+
3301
+ appBarLayout.setElevation(0);
3302
+ appBarLayout.setStateListAnimator(null);
3303
+ appBarLayout.setOutlineProvider(null);
3304
+
3305
+ int backgroundColor = Color.BLACK;
3306
+ if (_options.getToolbarColor() != null && !_options.getToolbarColor().isEmpty()) {
3307
+ try {
3308
+ backgroundColor = Color.parseColor(_options.getToolbarColor());
3309
+ } catch (IllegalArgumentException e) {
3310
+ Log.e("InAppBrowser", "Invalid toolbar color, using black: " + e.getMessage());
3311
+ }
3312
+ } else {
3313
+ backgroundColor = isDarkThemeEnabled() ? Color.BLACK : Color.WHITE;
3314
+ }
3315
+
3316
+ final int finalBgColor = backgroundColor;
3317
+ _webView.post(() -> {
3318
+ if (_webView == null) {
3319
+ return;
3320
+ }
3321
+
3322
+ int statusBarHeight = getSystemStatusBarHeight();
3323
+
3324
+ if (statusBarColorView != null) {
3325
+ ViewGroup.LayoutParams params = statusBarColorView.getLayoutParams();
3326
+ if (params != null) {
3327
+ params.height = statusBarHeight;
3328
+ statusBarColorView.setLayoutParams(params);
3329
+ }
3330
+ statusBarColorView.setBackgroundColor(finalBgColor);
3331
+ statusBarColorView.setVisibility(View.VISIBLE);
3332
+ }
3333
+
3334
+ ViewGroup.LayoutParams appBarParams = appBarLayout.getLayoutParams();
3335
+ if (appBarParams instanceof ViewGroup.MarginLayoutParams marginParams) {
3336
+ marginParams.topMargin = statusBarHeight;
3337
+ appBarLayout.setLayoutParams(marginParams);
3338
+ }
3339
+ appBarLayout.setBackgroundColor(finalBgColor);
3340
+ });
3341
+ }
3342
+
3155
3343
  private void applySafeAreaMargins(
3156
3344
  Insets bars,
3157
3345
  Insets navigationBars,
@@ -5859,6 +6047,7 @@ public class WebViewDialog extends Dialog implements ProxyResponseRouting.ProxyR
5859
6047
 
5860
6048
  @Override
5861
6049
  public void dismiss() {
6050
+ unregisterConfigurationCallbacks();
5862
6051
  scheduleHostWebViewInsetRestore();
5863
6052
  detachBackLayer();
5864
6053
 
@@ -6969,7 +7158,12 @@ public class WebViewDialog extends Dialog implements ProxyResponseRouting.ProxyR
6969
7158
  Integer x = _options.getX();
6970
7159
  Integer y = _options.getY();
6971
7160
 
6972
- WindowManager.LayoutParams params = getWindow().getAttributes();
7161
+ Window window = getWindow();
7162
+ if (window == null) {
7163
+ return;
7164
+ }
7165
+
7166
+ WindowManager.LayoutParams params = window.getAttributes();
6973
7167
 
6974
7168
  // If both width and height are specified, use custom dimensions
6975
7169
  if (width != null && height != null) {
@@ -6979,6 +7173,7 @@ public class WebViewDialog extends Dialog implements ProxyResponseRouting.ProxyR
6979
7173
  params.x = (x != null) ? (int) getPixels(x) : 0;
6980
7174
  params.y = (y != null) ? (int) getPixels(y) : 0;
6981
7175
  params.gravity = Gravity.TOP | Gravity.START;
7176
+ window.setAttributes(params);
6982
7177
  } else if (height != null && width == null) {
6983
7178
  // If only height is specified, use custom height with fullscreen width
6984
7179
  params.gravity = Gravity.TOP | Gravity.LEFT;
@@ -6987,15 +7182,16 @@ public class WebViewDialog extends Dialog implements ProxyResponseRouting.ProxyR
6987
7182
  params.x = 0;
6988
7183
  params.y = (y != null) ? (int) getPixels(y) : 0;
6989
7184
  params.gravity = Gravity.TOP | Gravity.START;
7185
+ window.setAttributes(params);
6990
7186
  } else {
6991
- // Default to fullscreen
6992
- params.width = WindowManager.LayoutParams.MATCH_PARENT;
6993
- params.height = WindowManager.LayoutParams.MATCH_PARENT;
7187
+ // Default to fullscreen. Prefer setLayout so MATCH_PARENT is not replaced by
7188
+ // previously resolved portrait/landscape pixel sizes after a configuration change.
6994
7189
  params.x = 0;
6995
7190
  params.y = 0;
7191
+ params.gravity = Gravity.TOP | Gravity.START;
7192
+ window.setAttributes(params);
7193
+ window.setLayout(WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.MATCH_PARENT);
6996
7194
  }
6997
-
6998
- getWindow().setAttributes(params);
6999
7195
  }
7000
7196
 
7001
7197
  /**
@@ -412,7 +412,7 @@ public class CapgoInAppBrowserPlugin: CAPPlugin, CAPBridgedPlugin {
412
412
  case aware = "AWARE"
413
413
  case fakeVisible = "FAKE_VISIBLE"
414
414
  }
415
- private let pluginVersion: String = "8.13.4"
415
+ private let pluginVersion: String = "8.13.5"
416
416
  public let identifier = "CapgoInAppBrowserPlugin"
417
417
  public let jsName = "CapgoInAppBrowser"
418
418
  public let pluginMethods: [CAPPluginMethod] = [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@capgo/capacitor-inappbrowser",
3
- "version": "8.13.4",
3
+ "version": "8.13.5",
4
4
  "description": "Capacitor plugin in app browser",
5
5
  "main": "dist/plugin.cjs.js",
6
6
  "module": "dist/esm/index.js",