capacitor-plugin-status-bar 2.0.11 → 2.0.13

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.
@@ -48,37 +48,32 @@ public class CapacitorStatusBar extends Plugin {
48
48
  // CapacitorStatusBarPlugin.load().
49
49
 
50
50
  /**
51
- * Ensures edge-to-edge is properly configured for Android 15+.
52
- * Sets up a unified insets listener on the decorView that handles both
53
- * IME insets and overlay view sizing.
51
+ * Ensures edge-to-edge is properly configured for Android 12+.
52
+ * Sets up a unified insets listener on the decorView that handles
53
+ * overlay view sizing and WebView padding so content stays within
54
+ * the safe area.
54
55
  *
55
56
  * @param activity The activity to configure
57
+ * @param webView The Capacitor WebView to apply safe-area padding to (may be null)
56
58
  */
57
- public void ensureEdgeToEdgeConfigured(Activity activity) {
59
+ public void ensureEdgeToEdgeConfigured(Activity activity, @Nullable View webView) {
58
60
  Window window = activity.getWindow();
59
61
  View decorView = window.getDecorView();
60
62
 
61
63
  if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
62
- // Android 12+ (API 31+): enable edge-to-edge layout.
63
- // The nav bar uses overlay views because gesture navigation ignores
64
- // setNavigationBarColor().
65
- // The status bar still uses setStatusBarColor() reliably on API 31–34;
66
- // overlay is only needed on API 35+ where edge-to-edge is enforced and the
67
- // native API is a no-op.
68
64
  WindowCompat.setDecorFitsSystemWindows(window, false);
69
65
 
70
- // Set a transparent baseline; setStyle() will apply the actual color via
71
- // setStatusBarColor()
72
- // (API 31-34) or the status bar overlay (API 35+).
73
66
  window.setStatusBarColor(Color.TRANSPARENT);
74
67
  window.setNavigationBarColor(Color.TRANSPARENT);
75
68
  window.setStatusBarContrastEnforced(false);
76
69
  window.setNavigationBarContrastEnforced(false);
77
70
 
78
- // Single unified insets listener on decorView for overlay sizing
79
71
  ViewCompat.setOnApplyWindowInsetsListener(decorView, (v, insets) -> {
80
- // Size status bar overlay
81
72
  int top = insets.getInsets(WindowInsetsCompat.Type.statusBars()).top;
73
+ int navBottom = insets.getInsets(WindowInsetsCompat.Type.navigationBars()).bottom;
74
+ boolean imeVisible = insets.isVisible(WindowInsetsCompat.Type.ime());
75
+
76
+ // Size status bar overlay
82
77
  View statusOverlay = ((ViewGroup) v).findViewWithTag(STATUS_BAR_OVERLAY_TAG);
83
78
  if (statusOverlay != null) {
84
79
  ViewGroup.LayoutParams params = statusOverlay.getLayoutParams();
@@ -89,29 +84,49 @@ public class CapacitorStatusBar extends Plugin {
89
84
  }
90
85
  }
91
86
 
92
- // Size navigation bar overlay
93
- int bottom = insets.getInsets(WindowInsetsCompat.Type.navigationBars()).bottom;
87
+ // Size navigation bar overlay — collapse it when the keyboard is
88
+ // open since the nav bar is hidden behind the IME.
89
+ int overlayBottom = imeVisible ? 0 : navBottom;
94
90
  View navOverlay = ((ViewGroup) v).findViewWithTag(NAV_BAR_OVERLAY_TAG);
95
91
  if (navOverlay != null) {
96
92
  ViewGroup.LayoutParams params = navOverlay.getLayoutParams();
97
- if (params.height != bottom) {
98
- params.height = bottom;
93
+ if (params.height != overlayBottom) {
94
+ params.height = overlayBottom;
99
95
  navOverlay.setLayoutParams(params);
100
- Log.d(TAG, "insetsListener: nav bar overlay height=" + bottom);
96
+ Log.d(TAG, "insetsListener: nav bar overlay height=" + overlayBottom
97
+ + " (imeVisible=" + imeVisible + ")");
98
+ }
99
+ }
100
+
101
+ // Inset the WebView via layout margins so it never renders behind
102
+ // system bars. When the keyboard is open the nav bar is behind the
103
+ // IME, so drop the bottom margin to avoid a gap above the keyboard.
104
+ if (webView != null
105
+ && webView.getLayoutParams() instanceof ViewGroup.MarginLayoutParams) {
106
+ ViewGroup.MarginLayoutParams mlp =
107
+ (ViewGroup.MarginLayoutParams) webView.getLayoutParams();
108
+ int left = insets.getInsets(WindowInsetsCompat.Type.systemBars()).left;
109
+ int right = insets.getInsets(WindowInsetsCompat.Type.systemBars()).right;
110
+ int bottomMargin = imeVisible ? 0 : navBottom;
111
+ if (mlp.topMargin != top || mlp.bottomMargin != bottomMargin
112
+ || mlp.leftMargin != left || mlp.rightMargin != right) {
113
+ mlp.topMargin = top;
114
+ mlp.bottomMargin = bottomMargin;
115
+ mlp.leftMargin = left;
116
+ mlp.rightMargin = right;
117
+ webView.setLayoutParams(mlp);
118
+ Log.d(TAG, "insetsListener: webView margins t=" + top
119
+ + " b=" + bottomMargin + " l=" + left + " r=" + right
120
+ + " (imeVisible=" + imeVisible + ")");
101
121
  }
102
122
  }
103
123
 
104
- ViewCompat.onApplyWindowInsets(v, insets);
105
- return insets;
124
+ return WindowInsetsCompat.CONSUMED;
106
125
  });
107
126
 
127
+ decorView.requestApplyInsets();
108
128
  Log.d(TAG, "ensureEdgeToEdgeConfigured: edge-to-edge with overlay views, API=" + Build.VERSION.SDK_INT);
109
129
  } else {
110
- // Android < 12 (API < 31): Only disable contrast enforcement.
111
- // Do NOT call setDecorFitsSystemWindows(false), create overlay views, or set
112
- // insets listener.
113
- // This avoids conflicts with plugins like
114
- // @capawesome/capacitor-android-edge-to-edge-support.
115
130
  if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
116
131
  window.setStatusBarContrastEnforced(false);
117
132
  window.setNavigationBarContrastEnforced(false);
@@ -494,15 +509,12 @@ public class CapacitorStatusBar extends Plugin {
494
509
 
495
510
  private void applyStatusBarBackground(Activity activity, @ColorInt int color) {
496
511
  Log.d(TAG, "applyStatusBarBackground: color=#" + Integer.toHexString(color) + ", API=" + Build.VERSION.SDK_INT);
497
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM) {
498
- // Android 15+ (API 35+): edge-to-edge is enforced, setStatusBarColor() is a
499
- // no-op
512
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
513
+ // API 31+: edge-to-edge is enabled, setStatusBarColor() is unreliable.
514
+ // Use an overlay View drawn on top of the status bar area.
500
515
  ensureStatusBarOverlay(activity, color);
501
516
  } else {
502
- // API 29–34: setStatusBarColor() works reliably for the status bar on all
503
- // navigation modes.
504
- // Note: gesture navigation only ignores setNavigationBarColor(), not
505
- // setStatusBarColor().
517
+ // API 29–30: no edge-to-edge, native API works reliably.
506
518
  activity.getWindow().setStatusBarColor(color);
507
519
  }
508
520
  }
@@ -510,15 +522,13 @@ public class CapacitorStatusBar extends Plugin {
510
522
  private void applyNavigationBarBackground(Activity activity, @ColorInt int color) {
511
523
  Log.d(TAG, "applyNavigationBarBackground: color=#" + Integer.toHexString(color) + ", API="
512
524
  + Build.VERSION.SDK_INT);
513
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM) {
514
- // Android 15+ (API 35+): edge-to-edge enforced, setNavigationBarColor() is a
515
- // no-op
525
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
526
+ // API 31+: edge-to-edge is enabled, setNavigationBarColor() is unreliable
527
+ // (especially gesture navigation which ignores it entirely).
528
+ // Use an overlay View drawn at the bottom of the screen.
516
529
  ensureNavBarOverlay(activity, color);
517
530
  } else {
518
- // API 29–34: setNavigationBarColor() works for button navigation.
519
- // Gesture navigation ignores it (system enforces transparency for gesture
520
- // handles),
521
- // but in that case there is no visible nav bar to color anyway.
531
+ // API 29–30: no edge-to-edge, native API works for button navigation.
522
532
  activity.getWindow().setNavigationBarColor(color);
523
533
  }
524
534
  }
@@ -639,8 +649,8 @@ public class CapacitorStatusBar extends Plugin {
639
649
  Window window = activity.getWindow();
640
650
  ViewGroup decorView = (ViewGroup) window.getDecorView();
641
651
 
642
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM) {
643
- // Android 15+: both bars use overlay views
652
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
653
+ // API 31+: edge-to-edge uses overlay views for both bars
644
654
  View statusBarOverlay = decorView.findViewWithTag(STATUS_BAR_OVERLAY_TAG);
645
655
  if (statusBarOverlay != null) {
646
656
  statusBarOverlay.setBackgroundColor(Color.TRANSPARENT);
@@ -652,7 +662,7 @@ public class CapacitorStatusBar extends Plugin {
652
662
  Log.d(TAG, "makeStatusBarBackgroundTransparent: nav bar overlay made transparent");
653
663
  }
654
664
  } else {
655
- // API 29–34: both bars use native window API
665
+ // API 29–30: no edge-to-edge, native window API
656
666
  window.setStatusBarColor(Color.TRANSPARENT);
657
667
  window.setNavigationBarColor(Color.TRANSPARENT);
658
668
  Log.d(TAG, "makeStatusBarBackgroundTransparent: set native bar colors to transparent");
@@ -1,12 +1,7 @@
1
1
  package com.cap.plugins.statusbar;
2
2
 
3
- import android.os.Build;
4
3
  import android.view.View;
5
4
 
6
- import androidx.core.graphics.Insets;
7
- import androidx.core.view.ViewCompat;
8
- import androidx.core.view.WindowInsetsCompat;
9
-
10
5
  import com.getcapacitor.Plugin;
11
6
  import com.getcapacitor.PluginCall;
12
7
  import com.getcapacitor.PluginMethod;
@@ -19,21 +14,10 @@ public class CapacitorStatusBarPlugin extends Plugin {
19
14
  @Override
20
15
  public void load() {
21
16
  super.load();
22
- // Apply default style based on system theme on plugin load
23
17
  getActivity().runOnUiThread(() -> {
24
- implementation.ensureEdgeToEdgeConfigured(getActivity());
18
+ View webView = getBridge().getWebView();
19
+ implementation.ensureEdgeToEdgeConfigured(getActivity(), webView);
25
20
  implementation.applyDefaultStyle(getActivity());
26
-
27
- // On Android 15+ (API 35+), edge-to-edge is enforced and the WebView content
28
- // extends under system bars. Apply padding so Ionic content stays in the safe area.
29
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM) {
30
- View webView = getBridge().getWebView();
31
- ViewCompat.setOnApplyWindowInsetsListener(webView, (v, insets) -> {
32
- Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
33
- v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
34
- return insets;
35
- });
36
- }
37
21
  });
38
22
  }
39
23
 
@@ -241,11 +241,14 @@ import Capacitor
241
241
  let safeAreaInsets = window.safeAreaInsets
242
242
 
243
243
  insets["top"] = self.sanitizedInsetValue(safeAreaInsets.top)
244
- insets["bottom"] = 0
244
+ let homeIndicatorHeight: CGFloat = safeAreaInsets.bottom > 0
245
+ ? min(13.0, safeAreaInsets.bottom)
246
+ : 0
247
+ insets["bottom"] = self.sanitizedInsetValue(homeIndicatorHeight)
245
248
  insets["left"] = 0
246
249
  insets["right"] = 0
247
250
 
248
- print("CapacitorStatusBar: getSafeAreaInsets - top=\(safeAreaInsets.top), bottom=\(safeAreaInsets.bottom), left=\(safeAreaInsets.left), right=\(safeAreaInsets.right)")
251
+ print("CapacitorStatusBar: getSafeAreaInsets - top=\(safeAreaInsets.top), bottom=\(homeIndicatorHeight) (raw: \(safeAreaInsets.bottom)), left=\(safeAreaInsets.left), right=\(safeAreaInsets.right)")
249
252
  } else {
250
253
  print("CapacitorStatusBar: getSafeAreaInsets - Unable to get window, returning zero insets")
251
254
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "capacitor-plugin-status-bar",
3
- "version": "2.0.11",
3
+ "version": "2.0.13",
4
4
  "description": "Capacitor plugin for managing the status bar style, visibility, and color on iOS and Android. Control overlay modes, background colors, and appearance for native mobile applications.",
5
5
  "main": "dist/plugin.cjs.js",
6
6
  "module": "dist/esm/index.js",