dpogue commented on code in PR #1817:
URL: https://github.com/apache/cordova-android/pull/1817#discussion_r2193955577


##########
framework/src/org/apache/cordova/CordovaActivity.java:
##########
@@ -180,26 +194,56 @@ protected void loadConfig() {
     //Suppressing warnings in AndroidStudio
     @SuppressWarnings({"deprecation", "ResourceType"})
     protected void createViews() {
-        //Why are we setting a constant as the ID? This should be investigated
-        appView.getView().setId(100);
-        appView.getView().setLayoutParams(new FrameLayout.LayoutParams(
+        WindowCompat.setDecorFitsSystemWindows(getWindow(), false);
+
+        // Root FrameLayout
+        FrameLayout rootLayout = new FrameLayout(this);
+        rootLayout.setLayoutParams(new FrameLayout.LayoutParams(
                 ViewGroup.LayoutParams.MATCH_PARENT,
-                ViewGroup.LayoutParams.MATCH_PARENT));
+                ViewGroup.LayoutParams.MATCH_PARENT
+        ));
 
-        setContentView(appView.getView());
+        // WebView
+        View webView = appView.getView();
+        webView.setLayoutParams(new FrameLayout.LayoutParams(
+                ViewGroup.LayoutParams.MATCH_PARENT,
+                ViewGroup.LayoutParams.MATCH_PARENT
+        ));
+
+        // Create StatusBar view that will overlay the top inset
+        View statusBarView = new View(this);
+        statusBarView.setTag("statusBarView");
+
+        // Handle Window Insets
+        ViewCompat.setOnApplyWindowInsetsListener(rootLayout, (v, insets) -> {
+            Insets bars = insets.getInsets(
+                    WindowInsetsCompat.Type.systemBars() | 
WindowInsetsCompat.Type.displayCutout()
+            );
+
+            boolean isStatusBarVisible = statusBarView.getVisibility() != 
View.GONE;
+            int top = isStatusBarVisible && !canEdgeToEdge && !isFullScreen ? 
bars.top : 0;
+            int bottom = !canEdgeToEdge && !isFullScreen ? bars.bottom : 0;
+
+            FrameLayout.LayoutParams webViewParams = 
(FrameLayout.LayoutParams) webView.getLayoutParams();
+            webViewParams.setMargins(bars.left, top, bars.right, bottom);
+            webView.setLayoutParams(webViewParams);
+
+            FrameLayout.LayoutParams statusBarParams = new 
FrameLayout.LayoutParams(
+                    ViewGroup.LayoutParams.MATCH_PARENT,
+                    top,
+                    Gravity.TOP
+            );
+            statusBarView.setLayoutParams(statusBarParams);
+
+            return WindowInsetsCompat.CONSUMED;

Review Comment:
   I worry returning `CONSUMED` here will break the `safe-area-inset` stuff for 
the webview. If we are doing edge-to-edge, I think we want to return the inset 
we were called with?



##########
framework/src/org/apache/cordova/SystemBarPlugin.java:
##########
@@ -0,0 +1,364 @@
+/*
+       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.
+*/
+
+package org.apache.cordova;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.graphics.Color;
+import android.os.Build;
+import android.view.View;
+import android.view.ViewParent;
+import android.view.Window;
+import android.view.WindowInsetsController;
+import android.widget.FrameLayout;
+
+import androidx.core.content.ContextCompat;
+import androidx.core.view.ViewCompat;
+import androidx.core.view.WindowCompat;
+import androidx.core.view.WindowInsetsControllerCompat;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+
+public class SystemBarPlugin extends CordovaPlugin {
+    static final String PLUGIN_NAME = "SystemBarPlugin";
+
+    static final int INVALID_COLOR = -1;
+
+    // Internal variables
+    private Context context;
+    private Resources resources;
+    private int overrideStatusBarBackgroundColor = INVALID_COLOR;
+
+    private boolean canEdgeToEdge = false;
+
+    @Override
+    protected void pluginInitialize() {
+        context = cordova.getContext();
+        resources = context.getResources();
+        canEdgeToEdge = preferences.getBoolean("AndroidEdgeToEdge", false)
+                && Build.VERSION.SDK_INT >= 
Build.VERSION_CODES.VANILLA_ICE_CREAM;
+    }
+
+    @Override
+    public void onConfigurationChanged(Configuration newConfig) {
+        super.onConfigurationChanged(newConfig);
+        cordova.getActivity().runOnUiThread(this::updateSystemBars);
+    }
+
+    @Override
+    public void onResume(boolean multitasking) {
+        super.onResume(multitasking);
+        cordova.getActivity().runOnUiThread(this::updateSystemBars);
+    }
+
+    @Override
+    public Object onMessage(String id, Object data) {
+        if (id.equals("updateSystemBars")) {
+            cordova.getActivity().runOnUiThread(this::updateSystemBars);
+        }
+        return null;
+    }
+
+    @Override
+    public boolean execute(String action, JSONArray args, CallbackContext 
callbackContext) throws JSONException {
+        if(canEdgeToEdge) {
+            return false;
+        }
+
+        if ("setStatusBarVisible".equals(action)) {
+            boolean visible = args.getBoolean(0);
+            cordova.getActivity().runOnUiThread(() -> 
setStatusBarVisible(visible));
+        } else if ("setStatusBarBackgroundColor".equals(action)) {
+            cordova.getActivity().runOnUiThread(() -> 
setStatusBarBackgroundColor(args));
+        } else {
+            return false;
+        }
+
+        callbackContext.success();
+        return true;
+    }
+
+    /**
+     * Allow the app to override the status bar visibility from JS API.
+     * If for some reason the statusBarView could not be discovered, it will 
silently ignore
+     * the change request
+     *
+     * @param visible should the status bar be visible?
+     */
+    private void setStatusBarVisible(final boolean visible) {
+        View statusBar = getStatusBarView(webView);
+        if (statusBar != null) {
+            statusBar.setVisibility(visible ? View.VISIBLE : View.GONE);
+
+            FrameLayout rootLayout = getRootLayout(webView);
+            if (rootLayout != null) {
+                ViewCompat.requestApplyInsets(rootLayout);
+            }
+        }
+    }
+
+    /**
+     * Allow the app to override the status 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 {A, R, G, B}
+     */
+    private void setStatusBarBackgroundColor(JSONArray argbVals) {
+        try {
+            int a = argbVals.getInt(0);
+            int r = argbVals.getInt(1);
+            int g = argbVals.getInt(2);
+            int b = argbVals.getInt(3);
+            String hexColor = String.format("#%02X%02X%02X%02X", a, r, g, b);
+
+            int parsedColor = parseColorFromString(hexColor);
+            if (parsedColor == INVALID_COLOR) return;
+
+            overrideStatusBarBackgroundColor = parsedColor;
+            updateStatusBar(overrideStatusBarBackgroundColor);
+        } catch (JSONException e) {
+            // Silently skip
+        }
+    }
+
+    /**
+     * Attempt to update all system bars (status, navigation and gesture bars) 
in various points
+     * of the apps life cycle.
+     * For example:
+     *  1. Device configurations between (E.g. between dark and light mode)
+     *  2. User resumes the app
+     *  3. App transitions from SplashScreen Theme to App's Theme
+     */
+    private void updateSystemBars() {
+        // Update Root View Background Color
+        int rootViewBackgroundColor = getPreferenceBackgroundColor();
+        if (rootViewBackgroundColor == INVALID_COLOR) {
+            rootViewBackgroundColor = canEdgeToEdge ? Color.TRANSPARENT : 
getUiModeColor();
+        }
+        updateRootView(rootViewBackgroundColor);
+
+        // Update StatusBar Background Color
+        int statusBarBackgroundColor;
+        if (overrideStatusBarBackgroundColor != INVALID_COLOR) {
+            statusBarBackgroundColor = overrideStatusBarBackgroundColor;
+        } else if (preferences.contains("StatusBarBackgroundColor")) {
+            statusBarBackgroundColor = getPreferenceStatusBarBackgroundColor();
+        } else if(preferences.contains("BackgroundColor")){
+            statusBarBackgroundColor =  rootViewBackgroundColor;
+        } else {
+            statusBarBackgroundColor = canEdgeToEdge ? Color.TRANSPARENT : 
getUiModeColor();
+        }
+
+        updateStatusBar(statusBarBackgroundColor);
+    }
+
+    /**
+     * Updates the root layout's background color with the supplied color int.
+     * It will also determine if the background color is light or dark to 
properly adjust the
+     * appearance of the navigation/gesture bar's icons so it will not clash 
with the background.
+     * <p>
+     * System bars (navigation & gesture) on SDK 25 or lower is forced to 
black as the appearance
+     * of the fonts can not be updated.
+     * System bars (navigation & gesture) on SDK 26 or greater allows custom 
background color.
+     * <p/>
+     *
+     * @param bgColor Background color
+     */
+    @SuppressWarnings("deprecation")
+    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);
+
+        // Automatically set the font and icon color of the system bars based 
on background color.
+        boolean isBackgroundColorLight = isColorLight(bgColor);
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
+            WindowInsetsController controller = window.getInsetsController();
+            if (controller != null) {
+                int appearance = 
WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS;
+                if (isBackgroundColorLight) {
+                    controller.setSystemBarsAppearance(0, appearance);
+                } else {
+                    controller.setSystemBarsAppearance(appearance, appearance);
+                }
+            }
+        }
+        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);
+        }
+    }
+
+    /**
+     * Updates the statusBarView background color with the supplied color int.
+     * It will also determine if the background color is light or dark to 
properly adjust the
+     * appearance of the status bar so the font will not clash with the 
background.
+     *
+     * @param bgColor Background color
+     */
+    private void updateStatusBar(int bgColor) {
+        Window window = cordova.getActivity().getWindow();
+
+        View statusBar = getStatusBarView(webView);
+        if (statusBar != null) {
+            statusBar.setBackgroundColor(bgColor);
+        }
+
+        // Automatically set the font and icon color of the system bars based 
on background color.
+        boolean isStatusBarBackgroundColorLight = isColorLight(bgColor);

Review Comment:
   Just noting there's an edge case here (that I'm not sure is worth handling) 
where the status bar background colour is `#00000000` (transparent black) and 
the text colour will be calculated as white because it doesn't look at the 
alpha.
   
   Doing that properly is going to involve a whole lot of complexity so maybe 
we just intentionally don't handle that...



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: issues-unsubscr...@cordova.apache.org

For queries about this service, please contact Infrastructure at:
us...@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscr...@cordova.apache.org
For additional commands, e-mail: issues-h...@cordova.apache.org

Reply via email to