From 2ba40c39c9bce7ffcf4cd456f8733229e4a489d4 Mon Sep 17 00:00:00 2001 From: Oleguer Llopart Date: Mon, 27 Jul 2026 11:50:46 +0200 Subject: [PATCH 1/3] feat(statusbar): add setNavigationBarBackgroundColor to change the navigation bar --- cordova-js-src/plugin/android/statusbar.js | 25 ++++++++ .../org/apache/cordova/SystemBarPlugin.java | 60 +++++++++++++++++++ 2 files changed, 85 insertions(+) diff --git a/cordova-js-src/plugin/android/statusbar.js b/cordova-js-src/plugin/android/statusbar.js index d7d07e037..59c45e595 100644 --- a/cordova-js-src/plugin/android/statusbar.js +++ b/cordova-js-src/plugin/android/statusbar.js @@ -103,4 +103,29 @@ Object.defineProperty(statusBar, 'setBackgroundColor', { } }); +/** + * Sets the background color of the navigation bar, this will only work if Android old devices or if gesture navigation is disabled (3 button navigation). + * Supports valid CSS color values, e.g. `rebeccapurple`, `#RRGGBBAA`, `rgb(255 0 153)`. + */ +Object.defineProperty(statusBar, 'setNavigationBarBackgroundColor', { + configurable: false, + enumerable: false, + writable: false, + value: function (value) { + statusBarScript.style.color = value; + var rgbStr = window.getComputedStyle(statusBarScript).getPropertyValue('color'); + + if (!rgbStr.match(/^rgb/)) { + return; + } + + var rgbVals = rgbStr.match(/[\d.]+/g).map(function (v, i) { return (i < 3) ? parseInt(v, 10) : parseFloat(v); }); + if (rgbVals.length < 3) { + return; + } + + exec(null, null, 'SystemBarPlugin', 'setNavigationBarBackgroundColor', rgbVals); + } +}); + module.exports = statusBar; diff --git a/framework/src/org/apache/cordova/SystemBarPlugin.java b/framework/src/org/apache/cordova/SystemBarPlugin.java index ebcc87dae..9313c24db 100644 --- a/framework/src/org/apache/cordova/SystemBarPlugin.java +++ b/framework/src/org/apache/cordova/SystemBarPlugin.java @@ -85,6 +85,8 @@ public boolean execute(String action, JSONArray args, CallbackContext callbackCo cordova.getActivity().runOnUiThread(() -> setStatusBarVisible(visible)); } else if ("setStatusBarBackgroundColor".equals(action)) { cordova.getActivity().runOnUiThread(() -> setStatusBarBackgroundColor(args)); + } else if ("setNavigationBarBackgroundColor".equals(action)) { + cordova.getActivity().runOnUiThread(() -> setNavigationBarBackgroundColor(args)); } else { return false; } @@ -359,4 +361,62 @@ private Integer parseColorFromString(final String colorPref) { return null; } } + + /** + * Allow the app to override the navigation bar background color from JS API. + * If the supplied ARGB is invalid or fails to parse, it will silently ignore + * the change request. + * + * @param argbVals {R, G, B, A} + */ + private void setNavigationBarBackgroundColor(JSONArray argbVals) { + try { + if (Build.VERSION.SDK_INT >= 21) { + if (argbVals != null && argbVals.length() >= 3) { + int r = argbVals.getInt(0); + int g = argbVals.getInt(1); + int b = argbVals.getInt(2); + int a = Math.round(255 * (float)argbVals.optDouble(3, 1.0)); + + int navigationBarColor = Color.argb(a, r, g, b); + int opaqueNavigationBarColor = Color.rgb(r, g, b); + boolean useLightNavigationBarIcons = isColorLight(Color.rgb(r, g, b)); + boolean shouldUseTransparentNavigationBar = Color.alpha(navigationBarColor) == 0; + + final Window window = cordova.getActivity().getWindow(); + final View decorView = window.getDecorView(); + int uiOptions = decorView.getSystemUiVisibility(); + + // 0x80000000 FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS + // 0x00000010 SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR + // 0x00000200 FLAG_LAYOUT_NO_LIMITS + + uiOptions = uiOptions | 0x80000000; + + if(Build.VERSION.SDK_INT >= 26) { + WindowInsetsControllerCompat windowInsetsControllerCompat = WindowCompat.getInsetsController(window, decorView); + if(useLightNavigationBarIcons) { + windowInsetsControllerCompat.setAppearanceLightNavigationBars(true); + } else { + windowInsetsControllerCompat.setAppearanceLightNavigationBars(false); + } + } else { + uiOptions = uiOptions & ~0x00000010; + } + + if(Build.VERSION.SDK_INT >= 30 && shouldUseTransparentNavigationBar) { + uiOptions = uiOptions | 0x00000200; // window.addFlags(0x00000200); + } else { + uiOptions = uiOptions & ~0x00000200; // window.clearFlags(0x00000200); + } + + decorView.setSystemUiVisibility(uiOptions); + window.setNavigationBarColor(Build.VERSION.SDK_INT < 30 ? opaqueNavigationBarColor : navigationBarColor); + } + } + + } catch (JSONException e) { + // Silently skip + } + } } From 61e2295c39cc87a37bc6b29cf8071fec11b4d40c Mon Sep 17 00:00:00 2001 From: Oleguer Llopart Date: Mon, 27 Jul 2026 15:18:01 +0200 Subject: [PATCH 2/3] feat(SystemBarPlugin): apply navigation bar background color to the view --- .../org/apache/cordova/SystemBarPlugin.java | 30 ++++++++++++------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/framework/src/org/apache/cordova/SystemBarPlugin.java b/framework/src/org/apache/cordova/SystemBarPlugin.java index 9313c24db..4fa55a215 100644 --- a/framework/src/org/apache/cordova/SystemBarPlugin.java +++ b/framework/src/org/apache/cordova/SystemBarPlugin.java @@ -48,6 +48,7 @@ public class SystemBarPlugin extends CordovaPlugin { private Context context; private Resources resources; private Integer overrideStatusBarBackgroundColor = null; + private boolean hasNavigationBarBackgroundColorOverride = false; private boolean canEdgeToEdge = false; @@ -182,9 +183,11 @@ private void updateSystemBars() { private void updateRootView(int bgColor) { Window window = cordova.getActivity().getWindow(); - // Set the root view's background color. Works on SDK 36+ - View root = cordova.getActivity().findViewById(android.R.id.content); - if (root != null) root.setBackgroundColor(bgColor); + // Keep root background controlled by navigation override once it has been explicitly set. + if (!hasNavigationBarBackgroundColorOverride) { + View root = cordova.getActivity().findViewById(android.R.id.content); + if (root != null) root.setBackgroundColor(bgColor); + } // Automatically set the font and icon color of the system bars based on background color. boolean isBackgroundColorLight; @@ -204,13 +207,15 @@ private void updateRootView(int bgColor) { } } } - WindowInsetsControllerCompat controllerCompat = WindowCompat.getInsetsController(window, window.getDecorView()); - controllerCompat.setAppearanceLightNavigationBars(isBackgroundColorLight); - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - window.setNavigationBarColor(bgColor); - } else { - window.setNavigationBarColor(Color.BLACK); + if (!hasNavigationBarBackgroundColorOverride) { + WindowInsetsControllerCompat controllerCompat = WindowCompat.getInsetsController(window, window.getDecorView()); + controllerCompat.setAppearanceLightNavigationBars(isBackgroundColorLight); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + window.setNavigationBarColor(bgColor); + } else { + window.setNavigationBarColor(Color.BLACK); + } } } @@ -373,6 +378,7 @@ private void setNavigationBarBackgroundColor(JSONArray argbVals) { try { if (Build.VERSION.SDK_INT >= 21) { if (argbVals != null && argbVals.length() >= 3) { + hasNavigationBarBackgroundColorOverride = true; int r = argbVals.getInt(0); int g = argbVals.getInt(1); int b = argbVals.getInt(2); @@ -383,6 +389,10 @@ private void setNavigationBarBackgroundColor(JSONArray argbVals) { boolean useLightNavigationBarIcons = isColorLight(Color.rgb(r, g, b)); boolean shouldUseTransparentNavigationBar = Color.alpha(navigationBarColor) == 0; + // Set the root view's background color. + View root = cordova.getActivity().findViewById(android.R.id.content); + if (root != null) root.setBackgroundColor(opaqueNavigationBarColor); + final Window window = cordova.getActivity().getWindow(); final View decorView = window.getDecorView(); int uiOptions = decorView.getSystemUiVisibility(); From d263a689157357db385422f29035185cb98f4eef Mon Sep 17 00:00:00 2001 From: Oleguer Llopart Date: Wed, 29 Jul 2026 15:08:06 +0200 Subject: [PATCH 3/3] refactor(SystemBarPlugin): move navigation bar handling into navigationbar.js --- cordova-js-src/platform.js | 4 ++ .../plugin/android/navigationbar.js | 53 +++++++++++++++++++ cordova-js-src/plugin/android/statusbar.js | 25 --------- 3 files changed, 57 insertions(+), 25 deletions(-) create mode 100644 cordova-js-src/plugin/android/navigationbar.js diff --git a/cordova-js-src/platform.js b/cordova-js-src/platform.js index a49ccfb61..aa197816a 100644 --- a/cordova-js-src/platform.js +++ b/cordova-js-src/platform.js @@ -41,6 +41,10 @@ module.exports = { // see the file under plugin/android/statusbar.js modulemapper.clobbers('cordova/plugin/android/statusbar', 'window.statusbar'); + // Attach the internal navigationBar utility to window.navigationbar + // see the file under plugin/android/navigationbar.js + modulemapper.clobbers('cordova/plugin/android/navigationbar', 'window.navigationbar'); + var APP_PLUGIN_NAME = Number(cordova.platformVersion.split('.')[0]) >= 4 ? 'CoreAndroid' : 'App'; // Inject a listener for the backbutton on the document. diff --git a/cordova-js-src/plugin/android/navigationbar.js b/cordova-js-src/plugin/android/navigationbar.js new file mode 100644 index 000000000..c26c6c315 --- /dev/null +++ b/cordova-js-src/plugin/android/navigationbar.js @@ -0,0 +1,53 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. +*/ + +var exec = require('cordova/exec'); + +var navigationBar = {}; + +// This