Skip to content

Commit 76aa938

Browse files
authored
feat!: support previous non-E2E (#1817)
* feat: support previous non-e2e (add FrameLayout wrapper) * feat: implement internal SystemBar plugin * feat: implement StatusBar plugin JS API (SystemBarPlugin) * feat!: force custom statusbarView for all SDKs * chore: various cleanup, refactors, fixes, and docs from recent changes * feat: use getComputedStyle for setBackgroundColor * chore: suppress deprecation warnings for method using setNavigationBarColor * chore: return null when rootView is null * fix: setOnApplyWindowInsetsListener to return insets * fix: setting appearance when e2e is enabled * fix: set statusBarColor to transparent, use new statusBar UI
1 parent 7d7f511 commit 76aa938

File tree

8 files changed

+538
-38
lines changed

8 files changed

+538
-38
lines changed

cordova-js-src/platform.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,10 @@ module.exports = {
3939
// Core Splash Screen
4040
modulemapper.clobbers('cordova/plugin/android/splashscreen', 'navigator.splashscreen');
4141

42+
// Attach the internal statusBar utility to window.statusbar
43+
// see the file under plugin/android/statusbar.js
44+
modulemapper.clobbers('cordova/plugin/android/statusbar', 'window.statusbar');
45+
4246
var APP_PLUGIN_NAME = Number(cordova.platformVersion.split('.')[0]) >= 4 ? 'CoreAndroid' : 'App';
4347

4448
// Inject a listener for the backbutton on the document.
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
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+
21+
var exec = require('cordova/exec');
22+
23+
var statusBarVisible = true;
24+
var statusBar = {};
25+
26+
Object.defineProperty(statusBar, 'visible', {
27+
configurable: false,
28+
enumerable: true,
29+
get: function () {
30+
if (window.StatusBar) {
31+
// try to let the StatusBar plugin handle it
32+
return window.StatusBar.isVisible;
33+
}
34+
35+
return statusBarVisible;
36+
},
37+
set: function (value) {
38+
if (window.StatusBar) {
39+
// try to let the StatusBar plugin handle it
40+
if (value) {
41+
window.StatusBar.show();
42+
} else {
43+
window.StatusBar.hide();
44+
}
45+
} else {
46+
statusBarVisible = value;
47+
exec(null, null, 'SystemBarPlugin', 'setStatusBarVisible', [!!value]);
48+
}
49+
}
50+
});
51+
52+
Object.defineProperty(statusBar, 'setBackgroundColor', {
53+
configurable: false,
54+
enumerable: false,
55+
writable: false,
56+
value: function (value) {
57+
var script = document.querySelector('script[src$="cordova.js"]');
58+
script.style.color = value;
59+
var rgbStr = window.getComputedStyle(script).getPropertyValue('color');
60+
61+
if (!rgbStr.match(/^rgb/)) { return; }
62+
63+
var rgbVals = rgbStr.match(/\d+/g).map(function (v) { return parseInt(v, 10); });
64+
65+
if (rgbVals.length < 3) {
66+
return;
67+
} else if (rgbVals.length === 3) {
68+
rgbVals = [255].concat(rgbVals);
69+
}
70+
71+
const padRgb = (val) => val.toString(16).padStart(2, '0');
72+
const a = padRgb(rgbVals[0]);
73+
const r = padRgb(rgbVals[1]);
74+
const g = padRgb(rgbVals[2]);
75+
const b = padRgb(rgbVals[3]);
76+
const hexStr = '#' + a + r + g + b;
77+
78+
if (window.StatusBar) {
79+
window.StatusBar.backgroundColorByHexString(hexStr);
80+
} else {
81+
exec(null, null, 'SystemBarPlugin', 'setStatusBarBackgroundColor', rgbVals);
82+
}
83+
}
84+
});
85+
86+
module.exports = statusBar;

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: 62 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,10 @@ Licensed to the Apache Software Foundation (ASF) under one
2929
import android.content.DialogInterface;
3030
import android.content.Intent;
3131
import android.content.res.Configuration;
32-
import android.graphics.Color;
3332
import android.media.AudioManager;
33+
import android.os.Build;
3434
import android.os.Bundle;
35+
import android.view.Gravity;
3536
import android.view.Menu;
3637
import android.view.MenuItem;
3738
import android.view.View;
@@ -42,7 +43,11 @@ Licensed to the Apache Software Foundation (ASF) under one
4243
import android.widget.FrameLayout;
4344

4445
import androidx.appcompat.app.AppCompatActivity;
46+
import androidx.core.graphics.Insets;
4547
import androidx.core.splashscreen.SplashScreen;
48+
import androidx.core.view.ViewCompat;
49+
import androidx.core.view.WindowCompat;
50+
import androidx.core.view.WindowInsetsCompat;
4651

4752
/**
4853
* This class is the main Android activity that represents the Cordova
@@ -100,6 +105,9 @@ public class CordovaActivity extends AppCompatActivity {
100105

101106
private SplashScreen splashScreen;
102107

108+
private boolean canEdgeToEdge = false;
109+
private boolean isFullScreen = false;
110+
103111
/**
104112
* Called when the activity is first created.
105113
*/
@@ -113,6 +121,9 @@ public void onCreate(Bundle savedInstanceState) {
113121
// need to activate preferences before super.onCreate to avoid "requestFeature() must be called before adding content" exception
114122
loadConfig();
115123

124+
canEdgeToEdge = preferences.getBoolean("AndroidEdgeToEdge", false)
125+
&& Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM;
126+
116127
String logLevel = preferences.getString("loglevel", "ERROR");
117128
LOG.setLogLevel(logLevel);
118129

@@ -127,7 +138,10 @@ public void onCreate(Bundle savedInstanceState) {
127138
LOG.d(TAG, "The SetFullscreen configuration is deprecated in favor of Fullscreen, and will be removed in a future version.");
128139
preferences.set("Fullscreen", true);
129140
}
130-
if (preferences.getBoolean("Fullscreen", false)) {
141+
142+
isFullScreen = preferences.getBoolean("Fullscreen", false);
143+
144+
if (isFullScreen) {
131145
// NOTE: use the FullscreenNotImmersive configuration key to set the activity in a REAL full screen
132146
// (as was the case in previous cordova versions)
133147
if (!preferences.getBoolean("FullscreenNotImmersive", false)) {
@@ -184,26 +198,56 @@ protected void loadConfig() {
184198
//Suppressing warnings in AndroidStudio
185199
@SuppressWarnings({"deprecation", "ResourceType"})
186200
protected void createViews() {
187-
//Why are we setting a constant as the ID? This should be investigated
188-
appView.getView().setId(100);
189-
appView.getView().setLayoutParams(new FrameLayout.LayoutParams(
201+
WindowCompat.setDecorFitsSystemWindows(getWindow(), false);
202+
203+
// Root FrameLayout
204+
FrameLayout rootLayout = new FrameLayout(this);
205+
rootLayout.setLayoutParams(new FrameLayout.LayoutParams(
190206
ViewGroup.LayoutParams.MATCH_PARENT,
191-
ViewGroup.LayoutParams.MATCH_PARENT));
207+
ViewGroup.LayoutParams.MATCH_PARENT
208+
));
192209

193-
setContentView(appView.getView());
210+
// WebView
211+
View webView = appView.getView();
212+
webView.setLayoutParams(new FrameLayout.LayoutParams(
213+
ViewGroup.LayoutParams.MATCH_PARENT,
214+
ViewGroup.LayoutParams.MATCH_PARENT
215+
));
216+
217+
// Create StatusBar view that will overlay the top inset
218+
View statusBarView = new View(this);
219+
statusBarView.setTag("statusBarView");
220+
221+
// Handle Window Insets
222+
ViewCompat.setOnApplyWindowInsetsListener(rootLayout, (v, insets) -> {
223+
Insets bars = insets.getInsets(
224+
WindowInsetsCompat.Type.systemBars() | WindowInsetsCompat.Type.displayCutout()
225+
);
226+
227+
boolean isStatusBarVisible = statusBarView.getVisibility() != View.GONE;
228+
int top = isStatusBarVisible && !canEdgeToEdge && !isFullScreen ? bars.top : 0;
229+
int bottom = !canEdgeToEdge && !isFullScreen ? bars.bottom : 0;
230+
231+
FrameLayout.LayoutParams webViewParams = (FrameLayout.LayoutParams) webView.getLayoutParams();
232+
webViewParams.setMargins(bars.left, top, bars.right, bottom);
233+
webView.setLayoutParams(webViewParams);
234+
235+
FrameLayout.LayoutParams statusBarParams = new FrameLayout.LayoutParams(
236+
ViewGroup.LayoutParams.MATCH_PARENT,
237+
top,
238+
Gravity.TOP
239+
);
240+
statusBarView.setLayoutParams(statusBarParams);
241+
242+
return insets;
243+
});
194244

195-
if (preferences.contains("BackgroundColor")) {
196-
try {
197-
int backgroundColor = preferences.getInteger("BackgroundColor", Color.BLACK);
198-
// Background of activity:
199-
appView.getView().setBackgroundColor(backgroundColor);
200-
}
201-
catch (NumberFormatException e){
202-
e.printStackTrace();
203-
}
204-
}
245+
rootLayout.addView(webView);
246+
rootLayout.addView(statusBarView);
205247

206-
appView.getView().requestFocusFromTouch();
248+
setContentView(rootLayout);
249+
rootLayout.post(() -> ViewCompat.requestApplyInsets(rootLayout));
250+
webView.requestFocusFromTouch();
207251
}
208252

209253
/**

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

0 commit comments

Comments
 (0)