Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions cordova-js-src/platform.js
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
53 changes: 53 additions & 0 deletions cordova-js-src/plugin/android/navigationbar.js
Original file line number Diff line number Diff line change
@@ -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 <script> element is explicitly used by Cordova's navigationbar for computing color. (Do not use this element)
const navigationBarScript = document.createElement('script');
document.head.appendChild(navigationBarScript);

/**
* Sets the background color of the navigation bar, this will only work on old Android 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(navigationBar, 'setBackgroundColor', {
configurable: false,
enumerable: false,
writable: false,
value: function (value) {
navigationBarScript.style.color = value;
var rgbStr = window.getComputedStyle(navigationBarScript).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 = navigationBar;

Check failure on line 53 in cordova-js-src/plugin/android/navigationbar.js

View workflow job for this annotation

GitHub Actions / NodeJS 20.x on macos-26

Newline required at end of file but not found

Check failure on line 53 in cordova-js-src/plugin/android/navigationbar.js

View workflow job for this annotation

GitHub Actions / NodeJS 22.x on macos-26

Newline required at end of file but not found

Check failure on line 53 in cordova-js-src/plugin/android/navigationbar.js

View workflow job for this annotation

GitHub Actions / NodeJS 24.x on macos-26

Newline required at end of file but not found

Check failure on line 53 in cordova-js-src/plugin/android/navigationbar.js

View workflow job for this annotation

GitHub Actions / NodeJS 26.x on macos-26

Newline required at end of file but not found
90 changes: 80 additions & 10 deletions framework/src/org/apache/cordova/SystemBarPlugin.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -85,6 +86,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;
}
Expand Down Expand Up @@ -180,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;
Expand All @@ -202,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);
}
}
}

Expand Down Expand Up @@ -359,4 +366,67 @@ 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) {
hasNavigationBarBackgroundColorOverride = true;
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;

// 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();

// 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
}
}
}
Loading