capacitor-plugin-status-bar 2.0.10 → 2.0.11

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.
@@ -21,7 +21,6 @@ import androidx.core.view.WindowInsetsCompat;
21
21
 
22
22
  import com.getcapacitor.Plugin;
23
23
 
24
-
25
24
  /**
26
25
  * Android Status Bar utilities with Android 10-15+ (API 29-35+) support.
27
26
  * Supports:
@@ -42,9 +41,11 @@ public class CapacitorStatusBar extends Plugin {
42
41
  private int currentStatusBarColor = Color.BLACK;
43
42
  private int currentNavBarColor = Color.BLACK;
44
43
 
45
- // Note: load() is not called since CapacitorStatusBar is instantiated via new CapacitorStatusBar()
44
+ // Note: load() is not called since CapacitorStatusBar is instantiated via new
45
+ // CapacitorStatusBar()
46
46
  // from CapacitorStatusBarPlugin, not registered as a Capacitor plugin itself.
47
- // All initialization happens via ensureEdgeToEdgeConfigured() called from CapacitorStatusBarPlugin.load().
47
+ // All initialization happens via ensureEdgeToEdgeConfigured() called from
48
+ // CapacitorStatusBarPlugin.load().
48
49
 
49
50
  /**
50
51
  * Ensures edge-to-edge is properly configured for Android 15+.
@@ -59,12 +60,15 @@ public class CapacitorStatusBar extends Plugin {
59
60
 
60
61
  if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
61
62
  // Android 12+ (API 31+): enable edge-to-edge layout.
62
- // The nav bar uses overlay views because gesture navigation ignores setNavigationBarColor().
63
+ // The nav bar uses overlay views because gesture navigation ignores
64
+ // setNavigationBarColor().
63
65
  // The status bar still uses setStatusBarColor() reliably on API 31–34;
64
- // overlay is only needed on API 35+ where edge-to-edge is enforced and the native API is a no-op.
66
+ // overlay is only needed on API 35+ where edge-to-edge is enforced and the
67
+ // native API is a no-op.
65
68
  WindowCompat.setDecorFitsSystemWindows(window, false);
66
69
 
67
- // Set a transparent baseline; setStyle() will apply the actual color via setStatusBarColor()
70
+ // Set a transparent baseline; setStyle() will apply the actual color via
71
+ // setStatusBarColor()
68
72
  // (API 31-34) or the status bar overlay (API 35+).
69
73
  window.setStatusBarColor(Color.TRANSPARENT);
70
74
  window.setNavigationBarColor(Color.TRANSPARENT);
@@ -104,8 +108,10 @@ public class CapacitorStatusBar extends Plugin {
104
108
  Log.d(TAG, "ensureEdgeToEdgeConfigured: edge-to-edge with overlay views, API=" + Build.VERSION.SDK_INT);
105
109
  } else {
106
110
  // Android < 12 (API < 31): Only disable contrast enforcement.
107
- // Do NOT call setDecorFitsSystemWindows(false), create overlay views, or set insets listener.
108
- // This avoids conflicts with plugins like @capawesome/capacitor-android-edge-to-edge-support.
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.
109
115
  if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
110
116
  window.setStatusBarContrastEnforced(false);
111
117
  window.setNavigationBarContrastEnforced(false);
@@ -157,57 +163,42 @@ public class CapacitorStatusBar extends Plugin {
157
163
  showNavigationBar(activity, true);
158
164
  }
159
165
 
160
- public void hideStatusBar(Activity activity, String animation) {
161
- Log.d(TAG, "hideStatusBar: animation=" + animation + ", API=" + Build.VERSION.SDK_INT);
166
+ public void hideStatusBar(Activity activity) {
162
167
  Window window = activity.getWindow();
163
168
  View decorView = window.getDecorView();
164
169
 
165
- String animationType = animation != null ? animation.toLowerCase() : "slide";
170
+ // Slide mode: Hide status bar and navigation bar completely (current behavior)
171
+ Log.d(TAG, "hideStatusBar: slide mode - hiding bars completely");
166
172
 
167
- if ("fade".equals(animationType)) {
168
- // Fade mode: Make background transparent without removing status bar and
169
- // navigation bar
170
- Log.d(TAG, "hideStatusBar: fade mode - making backgrounds transparent");
171
- makeStatusBarBackgroundTransparent(activity);
172
- hideNavigationBar(activity, "fade");
173
- } else if ("slide".equals(animationType)) {
174
- // Slide mode: Hide status bar and navigation bar completely (current behavior)
175
- Log.d(TAG, "hideStatusBar: slide mode - hiding bars completely");
176
-
177
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
178
- // API 30+ (Android 11+) - Use WindowInsetsController
179
- WindowInsetsController controller = window.getInsetsController();
180
- if (controller != null) {
181
- Log.d(TAG, "hideStatusBar: hiding system bars (API 30+)");
182
- // Hide both status and navigation bars together
183
- controller.hide(WindowInsets.Type.systemBars());
184
- // Set behavior for immersive mode (user can swipe to reveal temporarily)
185
- controller.setSystemBarsBehavior(
186
- WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE);
187
- } else {
188
- Log.w(TAG, "hideStatusBar: WindowInsetsController is null");
189
- }
173
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
174
+ // API 30+ (Android 11+) - Use WindowInsetsController
175
+ WindowInsetsController controller = window.getInsetsController();
176
+ if (controller != null) {
177
+ Log.d(TAG, "hideStatusBar: hiding system bars (API 30+)");
178
+ // Hide both status and navigation bars together
179
+ controller.hide(WindowInsets.Type.systemBars());
180
+ // Set behavior for immersive mode (user can swipe to reveal temporarily)
181
+ controller.setSystemBarsBehavior(
182
+ WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE);
190
183
  } else {
191
- // API 29 (Android 10) - Use system UI visibility flags (deprecated but
192
- // necessary)
193
- Log.d(TAG, "hideStatusBar: hiding using system UI flags (API 29)");
194
- // Use immersive sticky mode with proper layout flags for Android 10
195
- decorView.setSystemUiVisibility(
196
- View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
197
- | View.SYSTEM_UI_FLAG_LAYOUT_STABLE
198
- | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
199
- | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
200
- | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
201
- | View.SYSTEM_UI_FLAG_FULLSCREEN);
184
+ Log.w(TAG, "hideStatusBar: WindowInsetsController is null");
202
185
  }
203
-
204
- // Make the overlay backgrounds transparent so content shows through
205
- makeStatusBarBackgroundTransparent(activity);
206
186
  } else {
207
- // Unknown animation type, default to slide
208
- Log.w(TAG, "hideStatusBar: unknown animation type '" + animationType + "', defaulting to slide");
209
- hideStatusBar(activity, "slide");
187
+ // API 29 (Android 10) - Use system UI visibility flags (deprecated but
188
+ // necessary)
189
+ Log.d(TAG, "hideStatusBar: hiding using system UI flags (API 29)");
190
+ // Use immersive sticky mode with proper layout flags for Android 10
191
+ decorView.setSystemUiVisibility(
192
+ View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
193
+ | View.SYSTEM_UI_FLAG_LAYOUT_STABLE
194
+ | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
195
+ | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
196
+ | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
197
+ | View.SYSTEM_UI_FLAG_FULLSCREEN);
210
198
  }
199
+
200
+ // Make the overlay backgrounds transparent so content shows through
201
+ makeStatusBarBackgroundTransparent(activity);
211
202
  }
212
203
 
213
204
  public void setStyle(Activity activity, String style, @Nullable String colorHex) {
@@ -262,7 +253,8 @@ public class CapacitorStatusBar extends Plugin {
262
253
  }
263
254
 
264
255
  // Set icon appearance AFTER background operations.
265
- // On Android 13 (API 33), applyStatusBarBackground triggers requestApplyInsets()
256
+ // On Android 13 (API 33), applyStatusBarBackground triggers
257
+ // requestApplyInsets()
266
258
  // which causes the WindowInsetsController to reset setSystemBarsAppearance.
267
259
  // Calling setLightStatusBarIcons last ensures the icon style is not overridden.
268
260
  setLightStatusBarIcons(window, lightBackground);
@@ -291,7 +283,8 @@ public class CapacitorStatusBar extends Plugin {
291
283
  * Returns the insets for status bar, navigation bar, and notch areas.
292
284
  *
293
285
  * @param activity The activity to get the insets from
294
- * @return A map containing top, bottom, left, and right inset values in dp (density-independent pixels)
286
+ * @return A map containing top, bottom, left, and right inset values in dp
287
+ * (density-independent pixels)
295
288
  */
296
289
  public java.util.Map<String, Integer> getSafeAreaInsets(Activity activity) {
297
290
  Log.d(TAG, "getSafeAreaInsets");
@@ -302,10 +295,12 @@ public class CapacitorStatusBar extends Plugin {
302
295
  WindowInsetsCompat windowInsets = ViewCompat.getRootWindowInsets(decorView);
303
296
 
304
297
  if (windowInsets != null) {
305
- // Use WindowInsetsCompat for accurate, type-specific insets across all API levels
298
+ // Use WindowInsetsCompat for accurate, type-specific insets across all API
299
+ // levels
306
300
  androidx.core.graphics.Insets statusBars = windowInsets.getInsets(WindowInsetsCompat.Type.statusBars());
307
301
  androidx.core.graphics.Insets navBars = windowInsets.getInsets(WindowInsetsCompat.Type.navigationBars());
308
- androidx.core.graphics.Insets displayCutout = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout());
302
+ androidx.core.graphics.Insets displayCutout = windowInsets
303
+ .getInsets(WindowInsetsCompat.Type.displayCutout());
309
304
 
310
305
  int topPx = Math.max(statusBars.top, displayCutout.top);
311
306
  int bottomPx = Math.max(navBars.bottom, displayCutout.bottom);
@@ -500,11 +495,14 @@ public class CapacitorStatusBar extends Plugin {
500
495
  private void applyStatusBarBackground(Activity activity, @ColorInt int color) {
501
496
  Log.d(TAG, "applyStatusBarBackground: color=#" + Integer.toHexString(color) + ", API=" + Build.VERSION.SDK_INT);
502
497
  if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM) {
503
- // Android 15+ (API 35+): edge-to-edge is enforced, setStatusBarColor() is a no-op
498
+ // Android 15+ (API 35+): edge-to-edge is enforced, setStatusBarColor() is a
499
+ // no-op
504
500
  ensureStatusBarOverlay(activity, color);
505
501
  } else {
506
- // API 29–34: setStatusBarColor() works reliably for the status bar on all navigation modes.
507
- // Note: gesture navigation only ignores setNavigationBarColor(), not setStatusBarColor().
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().
508
506
  activity.getWindow().setStatusBarColor(color);
509
507
  }
510
508
  }
@@ -513,11 +511,13 @@ public class CapacitorStatusBar extends Plugin {
513
511
  Log.d(TAG, "applyNavigationBarBackground: color=#" + Integer.toHexString(color) + ", API="
514
512
  + Build.VERSION.SDK_INT);
515
513
  if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM) {
516
- // Android 15+ (API 35+): edge-to-edge enforced, setNavigationBarColor() is a no-op
514
+ // Android 15+ (API 35+): edge-to-edge enforced, setNavigationBarColor() is a
515
+ // no-op
517
516
  ensureNavBarOverlay(activity, color);
518
517
  } else {
519
518
  // API 29–34: setNavigationBarColor() works for button navigation.
520
- // Gesture navigation ignores it (system enforces transparency for gesture handles),
519
+ // Gesture navigation ignores it (system enforces transparency for gesture
520
+ // handles),
521
521
  // but in that case there is no visible nav bar to color anyway.
522
522
  activity.getWindow().setNavigationBarColor(color);
523
523
  }
@@ -547,7 +547,8 @@ public class CapacitorStatusBar extends Plugin {
547
547
  overlay.setLayoutParams(lp);
548
548
 
549
549
  decorView.addView(overlay);
550
- // Sizing updates are handled by the unified listener in ensureEdgeToEdgeConfigured
550
+ // Sizing updates are handled by the unified listener in
551
+ // ensureEdgeToEdgeConfigured
551
552
  decorView.requestApplyInsets();
552
553
  } else {
553
554
  Log.d(TAG, "ensureStatusBarOverlay: updating existing overlay");
@@ -597,7 +598,8 @@ public class CapacitorStatusBar extends Plugin {
597
598
  overlay.setLayoutParams(lp);
598
599
 
599
600
  decorView.addView(overlay);
600
- // Sizing updates are handled by the unified listener in ensureEdgeToEdgeConfigured
601
+ // Sizing updates are handled by the unified listener in
602
+ // ensureEdgeToEdgeConfigured
601
603
  decorView.requestApplyInsets();
602
604
  } else {
603
605
  Log.d(TAG, "ensureNavBarOverlay: updating existing overlay");
@@ -623,9 +625,9 @@ public class CapacitorStatusBar extends Plugin {
623
625
  }
624
626
 
625
627
  private void applyWindowBackground(Activity activity, @ColorInt int color) {
626
- Log.d(TAG, "applyWindowBackground: color=#" + Integer.toHexString(color));
628
+ Log.d(TAG, "applyWindowBackground: body is always transparent; status/nav bars use overlays/native APIs");
627
629
  View decorView = activity.getWindow().getDecorView();
628
- decorView.setBackgroundColor(color);
630
+ decorView.setBackgroundColor(Color.TRANSPARENT);
629
631
  }
630
632
 
631
633
  /**
@@ -71,7 +71,7 @@ public class CapacitorStatusBarPlugin extends Plugin {
71
71
  public void hide(PluginCall call) {
72
72
  try {
73
73
  getActivity().runOnUiThread(() -> {
74
- implementation.hideStatusBar(getActivity(), "slide");
74
+ implementation.hideStatusBar(getActivity());
75
75
  call.resolve();
76
76
  });
77
77
  } catch (Exception e) {
@@ -227,8 +227,8 @@ import Capacitor
227
227
  return
228
228
  }
229
229
 
230
- window.backgroundColor = color
231
- print("CapacitorStatusBar: setBackground - Set window background to \(colorHex)")
230
+ window.backgroundColor = .clear
231
+ print("CapacitorStatusBar: setBackground - Body is always transparent; status/nav bars use overlays/native APIs")
232
232
  }
233
233
  }
234
234
 
@@ -237,20 +237,15 @@ import Capacitor
237
237
  var insets: [String: CGFloat] = ["top": 0, "bottom": 0, "left": 0, "right": 0]
238
238
 
239
239
  if let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene,
240
- let window = windowScene.windows.first {
241
- // Use statusBarManager for accurate status bar height
242
- let statusBarHeight = windowScene.statusBarManager?.statusBarFrame.height ?? 0
240
+ let window = windowScene.windows.first(where: { $0.isKeyWindow }) ?? windowScene.windows.first {
243
241
  let safeAreaInsets = window.safeAreaInsets
244
242
 
245
- // top: status bar height specifically
246
- insets["top"] = statusBarHeight
247
- // bottom: home indicator / navigation bar area
248
- insets["bottom"] = safeAreaInsets.bottom
249
- // left/right: safe area for landscape / display cutouts
250
- insets["left"] = safeAreaInsets.left
251
- insets["right"] = safeAreaInsets.right
243
+ insets["top"] = self.sanitizedInsetValue(safeAreaInsets.top)
244
+ insets["bottom"] = 0
245
+ insets["left"] = 0
246
+ insets["right"] = 0
252
247
 
253
- print("CapacitorStatusBar: getSafeAreaInsets - top=\(statusBarHeight), bottom=\(safeAreaInsets.bottom), left=\(safeAreaInsets.left), right=\(safeAreaInsets.right)")
248
+ print("CapacitorStatusBar: getSafeAreaInsets - top=\(safeAreaInsets.top), bottom=\(safeAreaInsets.bottom), left=\(safeAreaInsets.left), right=\(safeAreaInsets.right)")
254
249
  } else {
255
250
  print("CapacitorStatusBar: getSafeAreaInsets - Unable to get window, returning zero insets")
256
251
  }
@@ -399,6 +394,12 @@ import Capacitor
399
394
  return (0.299 * red + 0.587 * green + 0.114 * blue)
400
395
  }
401
396
 
397
+ /// Guard against non-finite/negative inset values before bridging to JS.
398
+ private func sanitizedInsetValue(_ value: CGFloat) -> CGFloat {
399
+ guard value.isFinite else { return 0 }
400
+ return max(0, value)
401
+ }
402
+
402
403
  private func isSystemInDarkMode() -> Bool {
403
404
  guard let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene,
404
405
  let window = windowScene.windows.first else {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "capacitor-plugin-status-bar",
3
- "version": "2.0.10",
3
+ "version": "2.0.11",
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",