Skip to content

chore: sync upstream PR #8454 - Fix navigation bar overlay in edge-to-edge mode on Android#67

Open
riderx wants to merge 3 commits into
plusfrom
sync/upstream-pr-8454
Open

chore: sync upstream PR #8454 - Fix navigation bar overlay in edge-to-edge mode on Android#67
riderx wants to merge 3 commits into
plusfrom
sync/upstream-pr-8454

Conversation

@riderx
Copy link
Copy Markdown
Member

@riderx riderx commented May 6, 2026

Upstream PR Sync

This PR syncs changes from an external contributor's PR on the official Capacitor repository.

Original PR

Automation

  • CI will run automatically
  • Claude Code will review for security/breaking changes
  • If approved, this PR will be auto-merged
  • A new release will be published automatically

Synced from upstream by Capacitor+ Bot

Summary by CodeRabbit

  • New Features

    • More consistent translucent/transparent navigation bar handling and appearance alignment with app theme
    • Safer safe-area injection to adapt layouts on newer WebView versions
  • Bug Fixes

    • Improved safe-area calculations and inset requests across Android versions and devices
    • More reliable keyboard detection and bottom padding behavior when IME is visible
    • Better tracking of system bar visibility to prevent layout jumps

Added navigation bar visibility handling and adjusted safe area calculations. Updated insets handling for improved layout compatibility.
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 6, 2026

Review Change Stack

📝 Walkthrough

Walkthrough

The Android Capacitor plugin's system bars handling is refactored to improve navigation bar visibility tracking, insets workflow, and safe-area CSS injection. Changes include adding navigation bar visibility state, restructuring insets computation via parent views, enhancing safe-area calculations with keyboard awareness and webview version guards, and refining style application logic.

Changes

System Bar and Insets Handling

Layer / File(s) Summary
State and Initialization
android/capacitor/src/main/java/com/getcapacitor/plugin/SystemBars.java (lines 62–63, 116–126, 166–182)
Add navBarVisible field to track navigation bar state. In initSystemBars/resume, call WindowCompat.setDecorFitsSystemWindows(window, false), apply transparent navigation bar and contrast behavior, and request insets on the webview parent.
Page / DOM Integration & CSS Injection
android/capacitor/src/main/java/com/getcapacitor/plugin/SystemBars.java (lines 81–89, 184–192, 222–232)
In onPageCommitVisible and onDOMReady, request insets on the webview parent and inject safe-area CSS variables derived from root window insets when inset handling and viewport-cover conditions hold.
Insets Sourcing & Safe-Area Calculation
android/capacitor/src/main/java/com/getcapacitor/plugin/SystemBars.java (lines 194–213, 233–238, 249–252, 276–279)
In initWindowInsetsListener and calcSafeAreaInsets, use getRootWindowInsets() as safeAreaSource, enhance bottom inset computation (consider navBarVisible, resource nav height, and IME visibility), and update safe-area injection/recalculation paths to use the safeAreaSource.
IME / Padding Behavior
android/capacitor/src/main/java/com/getcapacitor/plugin/SystemBars.java (lines 267, 215–220)
When keyboard is visible, set webview parent's bottom padding to IME bottom (otherwise 0); getNavBarHeightFromResources returns 0 when navBarVisible is false to influence bottom safe-area.
Style & Visibility Handling
android/capacitor/src/main/java/com/getcapacitor/plugin/SystemBars.java (lines 318–333, 348–359)
Refactor setStyle to track requestedStyle separately from computed theme-derived style and apply computed appearance for status/navigation bars; update setHidden to toggle navBarVisible when showing/hiding bars.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 I hopped through insets, root and wide,

Nav bars hide, then come outside.
CSS sprouts safe-area bloom,
Keyboards whisper, padding resumes.
A rabbit cheers — styles now abide.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the primary change: fixing navigation bar overlay in edge-to-edge mode on Android, which aligns with the substantial SystemBars.java modifications addressing safe-area calculations, inset handling, and navigation bar visibility.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch sync/upstream-pr-8454

Comment @coderabbitai help to get the list of available commands and usage tips.

@riderx riderx force-pushed the sync/upstream-pr-8454 branch from d6113cc to 6136d3c Compare May 7, 2026 06:54
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
android/capacitor/src/main/java/com/getcapacitor/plugin/SystemBars.java (1)

353-359: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

navBarVisible not updated when show(BAR_STATUS_BAR) implicitly reveals the navigation bar

windowInsetsControllerCompat.show(WindowInsetsCompat.Type.systemBars()) (line 354) encompasses both status and navigation bars. When bar = BAR_STATUS_BAR and the navigation bar was previously hidden, the nav bar becomes physically visible, but the second if block (line 356) is skipped, leaving navBarVisible = false. On pre-API 30 devices this causes getNavBarHeightFromResources() to return 0, so the fallback bottom safe-area inset will be under-reported despite the nav bar being on screen.

🐛 Proposed fix
         if (bar.isEmpty() || bar.equals(BAR_STATUS_BAR)) {
             windowInsetsControllerCompat.show(WindowInsetsCompat.Type.systemBars());
         }
         if (bar.isEmpty() || bar.equals(BAR_GESTURE_BAR)) {
             windowInsetsControllerCompat.show(WindowInsetsCompat.Type.navigationBars());
             navBarVisible = true;
         }

Either align the show path with the hide path (use statusBars() for BAR_STATUS_BAR):

         if (bar.isEmpty() || bar.equals(BAR_STATUS_BAR)) {
-            windowInsetsControllerCompat.show(WindowInsetsCompat.Type.systemBars());
+            windowInsetsControllerCompat.show(WindowInsetsCompat.Type.statusBars());
         }
         if (bar.isEmpty() || bar.equals(BAR_GESTURE_BAR)) {
             windowInsetsControllerCompat.show(WindowInsetsCompat.Type.navigationBars());
             navBarVisible = true;
         }

Or, if retaining systemBars() is intentional, also update navBarVisible there:

         if (bar.isEmpty() || bar.equals(BAR_STATUS_BAR)) {
             windowInsetsControllerCompat.show(WindowInsetsCompat.Type.systemBars());
+            navBarVisible = true; // systemBars() restores navigation bar as well
         }
         if (bar.isEmpty() || bar.equals(BAR_GESTURE_BAR)) {
             windowInsetsControllerCompat.show(WindowInsetsCompat.Type.navigationBars());
             navBarVisible = true;
         }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@android/capacitor/src/main/java/com/getcapacitor/plugin/SystemBars.java`
around lines 353 - 359, The show logic in SystemBars.java incorrectly leaves
navBarVisible false when calling
windowInsetsControllerCompat.show(WindowInsetsCompat.Type.systemBars()) for
BAR_STATUS_BAR because systemBars() reveals both status and navigation bars;
update the navBarVisible state when showing systemBars() (set navBarVisible =
true) or change the BAR_STATUS_BAR branch to call
windowInsetsControllerCompat.show(WindowInsetsCompat.Type.statusBars()) instead
so the navBarVisible flag remains correct for getNavBarHeightFromResources() and
subsequent safe-area calculations; adjust the conditional handling around
BAR_STATUS_BAR, BAR_GESTURE_BAR and the navBarVisible variable in the same
method to keep hide and show paths symmetric.
🧹 Nitpick comments (1)
android/capacitor/src/main/java/com/getcapacitor/plugin/SystemBars.java (1)

166-182: ⚡ Quick win

Extract duplicated window-flag setup into a shared helper

The window initialization block in handleOnResume() (lines 170–177) is identical to the one inside initSystemBars() (lines 116–123). If either copy drifts it will silently break one of the two code paths.

♻️ Suggested refactor
+    private void applyEdgeToEdgeWindowFlags() {
+        Window window = getActivity().getWindow();
+        WindowCompat.setDecorFitsSystemWindows(window, false);
+        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.VANILLA_ICE_CREAM) {
+            window.setNavigationBarColor(android.graphics.Color.TRANSPARENT);
+            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
+                window.setNavigationBarContrastEnforced(false);
+            }
+        }
+    }

     private void initSystemBars() {
         ...
         getBridge().executeOnMainThread(() -> {
-            Window window = getActivity().getWindow();
-            WindowCompat.setDecorFitsSystemWindows(window, false);
-            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.VANILLA_ICE_CREAM) {
-                window.setNavigationBarColor(android.graphics.Color.TRANSPARENT);
-                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
-                    window.setNavigationBarContrastEnforced(false);
-                }
-            }
+            applyEdgeToEdgeWindowFlags();
             setStyle(style, "");
             setHidden(hidden, "");
             ViewCompat.requestApplyInsets((View) getBridge().getWebView().getParent());
         });
     }

     `@Override`
     protected void handleOnResume() {
         super.handleOnResume();
         getBridge().executeOnMainThread(() -> {
-            Window window = getActivity().getWindow();
-            WindowCompat.setDecorFitsSystemWindows(window, false);
-            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.VANILLA_ICE_CREAM) {
-                window.setNavigationBarColor(android.graphics.Color.TRANSPARENT);
-                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
-                    window.setNavigationBarContrastEnforced(false);
-                }
-            }
+            applyEdgeToEdgeWindowFlags();
             setStyle(currentGestureBarStyle, BAR_GESTURE_BAR);
             setStyle(currentStatusBarStyle, BAR_STATUS_BAR);
             ViewCompat.requestApplyInsets((View) getBridge().getWebView().getParent());
         });
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@android/capacitor/src/main/java/com/getcapacitor/plugin/SystemBars.java`
around lines 166 - 182, The window initialization block duplicated in
handleOnResume() and initSystemBars() should be extracted into a single private
helper (e.g., configureWindowFlags(Window window) or
setupSystemBarWindowFlags(Window window)); move the logic that calls
WindowCompat.setDecorFitsSystemWindows(window, false), sets transparent
navigation bar for older SDKs and toggles navigationBarContrastEnforced for Q+,
into that helper and replace the duplicated blocks with calls to it from both
handleOnResume() and initSystemBars(), preserving current Build.VERSION.SDK_INT
checks and behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@android/capacitor/src/main/java/com/getcapacitor/plugin/SystemBars.java`:
- Around line 353-359: The show logic in SystemBars.java incorrectly leaves
navBarVisible false when calling
windowInsetsControllerCompat.show(WindowInsetsCompat.Type.systemBars()) for
BAR_STATUS_BAR because systemBars() reveals both status and navigation bars;
update the navBarVisible state when showing systemBars() (set navBarVisible =
true) or change the BAR_STATUS_BAR branch to call
windowInsetsControllerCompat.show(WindowInsetsCompat.Type.statusBars()) instead
so the navBarVisible flag remains correct for getNavBarHeightFromResources() and
subsequent safe-area calculations; adjust the conditional handling around
BAR_STATUS_BAR, BAR_GESTURE_BAR and the navBarVisible variable in the same
method to keep hide and show paths symmetric.

---

Nitpick comments:
In `@android/capacitor/src/main/java/com/getcapacitor/plugin/SystemBars.java`:
- Around line 166-182: The window initialization block duplicated in
handleOnResume() and initSystemBars() should be extracted into a single private
helper (e.g., configureWindowFlags(Window window) or
setupSystemBarWindowFlags(Window window)); move the logic that calls
WindowCompat.setDecorFitsSystemWindows(window, false), sets transparent
navigation bar for older SDKs and toggles navigationBarContrastEnforced for Q+,
into that helper and replace the duplicated blocks with calls to it from both
handleOnResume() and initSystemBars(), preserving current Build.VERSION.SDK_INT
checks and behavior.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 12280262-28ba-4398-9ac2-b99f1d39613a

📥 Commits

Reviewing files that changed from the base of the PR and between d6113cc and 6136d3c.

📒 Files selected for processing (1)
  • android/capacitor/src/main/java/com/getcapacitor/plugin/SystemBars.java

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants