Skip to content

Commit a69e1a6

Browse files
committed
feat: implement internal SystemBar plugin
1 parent 57779cc commit a69e1a6

File tree

4 files changed

+236
-11
lines changed

4 files changed

+236
-11
lines changed

framework/src/org/apache/cordova/ConfigXmlParser.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,14 @@ public void parse(Context action) {
7777
)
7878
);
7979

80+
pluginEntries.add(
81+
new PluginEntry(
82+
SystemBarPlugin.PLUGIN_NAME,
83+
"org.apache.cordova.SystemBarPlugin",
84+
true
85+
)
86+
);
87+
8088
pluginEntries.add(
8189
new PluginEntry(
8290
SplashScreenPlugin.PLUGIN_NAME,

framework/src/org/apache/cordova/CordovaActivity.java

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -235,17 +235,6 @@ protected void createViews() {
235235
ViewCompat.requestApplyInsets(rootLayout);
236236
}
237237

238-
if (preferences.contains("BackgroundColor")) {
239-
try {
240-
int backgroundColor = preferences.getInteger("BackgroundColor", Color.BLACK);
241-
// Background of activity:
242-
webView.setBackgroundColor(backgroundColor);
243-
}
244-
catch (NumberFormatException e){
245-
e.printStackTrace();
246-
}
247-
}
248-
249238
rootLayout.addView(webView);
250239
setContentView(rootLayout);
251240
webView.requestFocusFromTouch();

framework/src/org/apache/cordova/SplashScreenPlugin.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,10 +155,13 @@ public void onSplashScreenExit(@NonNull SplashScreenViewProvider splashScreenVie
155155
public void onAnimationEnd(Animator animation) {
156156
super.onAnimationEnd(animation);
157157
splashScreenViewProvider.remove();
158+
webView.getPluginManager().postMessage("updateSystemBars", null);
158159
}
159160
}).start();
160161
}
161162
});
163+
} else {
164+
webView.getPluginManager().postMessage("updateSystemBars", null);
162165
}
163166
}
164167

Lines changed: 225 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,225 @@
1+
/*
2+
Licensed to the Apache Software Foundation (ASF) under one
3+
or more contributor license agreements. See the NOTICE file
4+
distributed with this work for additional information
5+
regarding copyright ownership. The ASF licenses this file
6+
to you under the Apache License, Version 2.0 (the
7+
"License"); you may not use this file except in compliance
8+
with the License. You may obtain a copy of the License at
9+
10+
http://www.apache.org/licenses/LICENSE-2.0
11+
12+
Unless required by applicable law or agreed to in writing,
13+
software distributed under the License is distributed on an
14+
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
KIND, either express or implied. See the License for the
16+
specific language governing permissions and limitations
17+
under the License.
18+
*/
19+
20+
package org.apache.cordova;
21+
22+
import android.annotation.SuppressLint;
23+
import android.content.Context;
24+
import android.content.res.Configuration;
25+
import android.content.res.Resources;
26+
import android.graphics.Color;
27+
import android.os.Build;
28+
import android.view.View;
29+
import android.view.ViewParent;
30+
import android.view.Window;
31+
import android.view.WindowInsetsController;
32+
import android.widget.FrameLayout;
33+
34+
import androidx.core.content.ContextCompat;
35+
import androidx.core.view.WindowCompat;
36+
import androidx.core.view.WindowInsetsControllerCompat;
37+
38+
public class SystemBarPlugin extends CordovaPlugin {
39+
static final String PLUGIN_NAME = "SystemBarPlugin";
40+
41+
static final int INVALID_COLOR = -1;
42+
43+
// Internal variables
44+
private Context context;
45+
private Resources resources;
46+
private int overrideStatusBarBackgroundColor = INVALID_COLOR;
47+
48+
49+
@Override
50+
protected void pluginInitialize() {
51+
context = cordova.getContext();
52+
resources = context.getResources();
53+
}
54+
55+
@Override
56+
public void onConfigurationChanged(Configuration newConfig) {
57+
super.onConfigurationChanged(newConfig);
58+
cordova.getActivity().runOnUiThread(this::updateSystemBars);
59+
}
60+
61+
@Override
62+
public void onResume(boolean multitasking) {
63+
super.onResume(multitasking);
64+
cordova.getActivity().runOnUiThread(this::updateSystemBars);
65+
}
66+
67+
@Override
68+
public Object onMessage(String id, Object data) {
69+
if (id.equals("updateSystemBars")) {
70+
cordova.getActivity().runOnUiThread(this::updateSystemBars);
71+
}
72+
return null;
73+
}
74+
75+
private void updateSystemBars() {
76+
// Update Root View Background Color
77+
int rootViewBackgroundColor = getPreferenceBackgroundColor();
78+
if (rootViewBackgroundColor == INVALID_COLOR) {
79+
rootViewBackgroundColor = getUiModeColor();
80+
}
81+
updateRootView(rootViewBackgroundColor);
82+
83+
// Update StatusBar Background Color
84+
int statusBarBackgroundColor;
85+
if (overrideStatusBarBackgroundColor != INVALID_COLOR) {
86+
statusBarBackgroundColor = overrideStatusBarBackgroundColor;
87+
} else if (preferences.contains("StatusBarBackgroundColor")) {
88+
statusBarBackgroundColor = getPreferenceStatusBarBackgroundColor();
89+
} else if(preferences.contains("BackgroundColor")){
90+
statusBarBackgroundColor = rootViewBackgroundColor;
91+
} else {
92+
statusBarBackgroundColor = getUiModeColor();
93+
}
94+
95+
updateStatusBar(statusBarBackgroundColor);
96+
}
97+
98+
private void updateRootView(int bgColor) {
99+
Window window = cordova.getActivity().getWindow();
100+
101+
// Set the root view's background color. Works on SDK 36+
102+
View root = cordova.getActivity().findViewById(android.R.id.content);
103+
if (root != null) root.setBackgroundColor(bgColor);
104+
105+
// Automatically set the font and icon color of the system bars based on background color.
106+
boolean isBackgroundColorLight = isColorLight(bgColor);
107+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
108+
WindowInsetsController controller = window.getInsetsController();
109+
if (controller != null) {
110+
int appearance = WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS;
111+
if (isBackgroundColorLight) {
112+
controller.setSystemBarsAppearance(0, appearance);
113+
} else {
114+
controller.setSystemBarsAppearance(appearance, appearance);
115+
}
116+
}
117+
}
118+
WindowInsetsControllerCompat controllerCompat = WindowCompat.getInsetsController(window, window.getDecorView());
119+
controllerCompat.setAppearanceLightNavigationBars(isBackgroundColorLight);
120+
121+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
122+
// Allow custom navigation bar background color for SDK 26 and greater.
123+
window.setNavigationBarColor(bgColor);
124+
} else {
125+
// Force navigation bar to black for SDK 25 and less.
126+
window.setNavigationBarColor(Color.BLACK);
127+
}
128+
}
129+
130+
private void updateStatusBar(int bgColor) {
131+
Window window = cordova.getActivity().getWindow();
132+
133+
if (!preferences.getBoolean("AndroidEdgeToEdge", false)) {
134+
View statusBar = getStatusBarView(webView);
135+
if (statusBar != null) {
136+
statusBar.setBackgroundColor(bgColor);
137+
}
138+
}
139+
140+
// Automatically set the font and icon color of the system bars based on background color.
141+
boolean isStatusBarBackgroundColorLight = isColorLight(bgColor);
142+
WindowInsetsControllerCompat controllerCompat = WindowCompat.getInsetsController(window, window.getDecorView());
143+
controllerCompat.setAppearanceLightStatusBars(isStatusBarBackgroundColorLight);
144+
145+
// Allow custom background color for StatusBar.
146+
window.setStatusBarColor(bgColor);
147+
}
148+
149+
private static boolean isColorLight(int color) {
150+
double r = Color.red(color) / 255.0;
151+
double g = Color.green(color) / 255.0;
152+
double b = Color.blue(color) / 255.0;
153+
double luminance = 0.299 * r + 0.587 * g + 0.114 * b;
154+
return luminance > 0.5;
155+
}
156+
157+
private int getPreferenceStatusBarBackgroundColor() {
158+
String colorString = preferences.getString("StatusBarBackgroundColor", null);
159+
160+
int parsedColor = parseColorFromString(colorString);
161+
if (parsedColor != INVALID_COLOR) return parsedColor;
162+
163+
return getUiModeColor(); // fallback
164+
}
165+
166+
private int getPreferenceBackgroundColor() {
167+
try {
168+
return preferences.getInteger("BackgroundColor", INVALID_COLOR);
169+
} catch (NumberFormatException e) {
170+
LOG.e(PLUGIN_NAME, "Invalid background color argument. Example valid string: '0x00000000'");
171+
return INVALID_COLOR;
172+
}
173+
}
174+
175+
private FrameLayout getRootLayout(CordovaWebView webView) {
176+
ViewParent parent = webView.getView().getParent();
177+
if (parent instanceof FrameLayout) {
178+
return (FrameLayout) parent;
179+
}
180+
181+
return null;
182+
}
183+
184+
private View getStatusBarView(CordovaWebView webView) {
185+
FrameLayout rootView = getRootLayout(webView);
186+
for (int i = 0; i < (rootView != null ? rootView.getChildCount() : 0); i++) {
187+
View child = rootView.getChildAt(i);
188+
Object tag = child.getTag();
189+
if ("statusBarView".equals(tag)) {
190+
return child;
191+
}
192+
}
193+
return null;
194+
}
195+
196+
private int getUiModeColor() {
197+
// Hardcoded fallback values matches system ui values (R.color) which were added in SDK 34.
198+
return isNightMode()
199+
? getThemeColor("cdv_background_color_dark", "#121318")
200+
: getThemeColor("cdv_background_color_light", "#FAF8FF");
201+
}
202+
203+
private boolean isNightMode() {
204+
return (resources.getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES;
205+
}
206+
207+
private int parseColorFromString(final String colorPref) {
208+
if (colorPref.isEmpty()) return INVALID_COLOR;
209+
210+
try {
211+
return Color.parseColor(colorPref);
212+
} catch (IllegalArgumentException ignore) {
213+
LOG.e(PLUGIN_NAME, "Invalid color hex code. Valid format: #RRGGBB or #AARRGGBB");
214+
return INVALID_COLOR;
215+
}
216+
}
217+
218+
@SuppressLint("DiscouragedApi")
219+
private int getThemeColor(String colorKey, String fallbackColor) {
220+
int colorResId = resources.getIdentifier(colorKey, "color", context.getPackageName());
221+
return colorResId != 0
222+
? ContextCompat.getColor(context, colorResId)
223+
: Color.parseColor(fallbackColor);
224+
}
225+
}

0 commit comments

Comments
 (0)