From 331e20655d9f5f753d10f1a8df4bf840337724a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pau=20Ferrer=20Oca=C3=B1a?= Date: Fri, 9 Jun 2023 10:51:56 +0200 Subject: [PATCH] MOBILE-4331 app: Change navigation bar color according to the theme. --- .../src/android/SystemUI.java | 39 ++++++++++++----- .../src/ts/plugins/SystemUI.ts | 5 ++- src/app/app.component.ts | 3 +- .../settings/services/settings-helper.ts | 2 +- src/core/features/styles/services/styles.ts | 12 +++--- src/core/services/app.ts | 42 +++++++++++++++++-- src/core/singletons/colors.ts | 19 +++++++++ 7 files changed, 97 insertions(+), 25 deletions(-) diff --git a/cordova-plugin-moodleapp/src/android/SystemUI.java b/cordova-plugin-moodleapp/src/android/SystemUI.java index 217c72df9d3..9014c819f93 100644 --- a/cordova-plugin-moodleapp/src/android/SystemUI.java +++ b/cordova-plugin-moodleapp/src/android/SystemUI.java @@ -22,6 +22,10 @@ import org.apache.cordova.CordovaPlugin; import org.apache.cordova.CallbackContext; +import android.view.View; +import androidx.core.view.WindowCompat; +import androidx.core.view.WindowInsetsControllerCompat; + import org.json.JSONArray; public class SystemUI extends CordovaPlugin { @@ -33,7 +37,7 @@ public boolean execute(String action, JSONArray args, CallbackContext callbackCo try { switch (action) { case "setNavigationBarColor": - this.setNavigationBarColor(args.getString(0)); + this.setNavigationBarColor(args.getString(0), args.getBoolean(1)); callbackContext.success(); return true; @@ -45,7 +49,7 @@ public boolean execute(String action, JSONArray args, CallbackContext callbackCo return false; } - private void setNavigationBarColor(String color) { + private void setNavigationBarColor(String color, Boolean lightNavigationBar) { if (Build.VERSION.SDK_INT < 21) { return; } @@ -54,20 +58,35 @@ private void setNavigationBarColor(String color) { return; } + final boolean useLightNavigationBar = lightNavigationBar != null && lightNavigationBar; + Log.d(TAG, "Setting navigation bar color to " + color); this.cordova.getActivity().runOnUiThread(new Runnable() { @Override public void run() { - final int FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS = 0x80000000; - final int SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR = 0x00000010; final Window window = cordova.getActivity().getWindow(); - int uiOptions = window.getDecorView().getSystemUiVisibility(); - - uiOptions = uiOptions | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; - uiOptions = uiOptions & ~SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR; - - window.getDecorView().setSystemUiVisibility(uiOptions); + final View decorView = window.getDecorView(); + WindowInsetsControllerCompat windowInsetController = WindowCompat.getInsetsController(window, decorView); + + if (windowInsetController != null) { + windowInsetController.setAppearanceLightNavigationBars(useLightNavigationBar); + } else { + // Deprectated in SDK 30. + final int FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS = 0x80000000; + final int SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR = 0x00000010; + int uiOptions = decorView.getSystemUiVisibility(); + + uiOptions = uiOptions | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; + uiOptions = uiOptions & ~SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR; + if (Build.VERSION.SDK_INT >= 26 && useLightNavigationBar) { + uiOptions = uiOptions | SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR; + } else { + uiOptions = uiOptions & ~SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR; + } + + decorView.setSystemUiVisibility(uiOptions); + } try { // Using reflection makes sure any 5.0+ device will work without having to compile with SDK level 21 diff --git a/cordova-plugin-moodleapp/src/ts/plugins/SystemUI.ts b/cordova-plugin-moodleapp/src/ts/plugins/SystemUI.ts index 28494b2b1ce..e50e86ddd9f 100644 --- a/cordova-plugin-moodleapp/src/ts/plugins/SystemUI.ts +++ b/cordova-plugin-moodleapp/src/ts/plugins/SystemUI.ts @@ -21,10 +21,11 @@ export class SystemUI { * Set navigation bar color. * * @param color Color. + * @param lightNavigationBar Wether to use light colors on buttors to contrast or not. */ - async setNavigationBarColor(color: string): Promise { + async setNavigationBarColor(color: string, lightNavigationBar: boolean): Promise { await new Promise((resolve, reject) => { - cordova.exec(resolve, reject, 'SystemUI', 'setNavigationBarColor', [color]); + cordova.exec(resolve, reject, 'SystemUI', 'setNavigationBarColor', [color, !!lightNavigationBar]); }); } diff --git a/src/app/app.component.ts b/src/app/app.component.ts index dafc3ff30e2..04490cf6d66 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -241,8 +241,7 @@ export class AppComponent implements OnInit, AfterViewInit { const isOnline = CoreNetwork.isOnline(); CoreDomUtils.toggleModeClass('core-offline', !isOnline, { includeLegacy: true }); - // Set StatusBar properties. - CoreApp.setStatusBarColor(); + CoreApp.setSystemUIColors(); } /** diff --git a/src/core/features/settings/services/settings-helper.ts b/src/core/features/settings/services/settings-helper.ts index 6e96f877432..b5fdf00ad0e 100644 --- a/src/core/features/settings/services/settings-helper.ts +++ b/src/core/features/settings/services/settings-helper.ts @@ -465,7 +465,7 @@ export class CoreSettingsHelperProvider { CoreDomUtils.toggleModeClass('dark', enable, { includeLegacy: true }); this.darkModeObservable.next(enable); - CoreApp.setStatusBarColor(); + CoreApp.setSystemUIColors(); } } diff --git a/src/core/features/styles/services/styles.ts b/src/core/features/styles/services/styles.ts index 3414d71c2d0..119730d1202 100644 --- a/src/core/features/styles/services/styles.ts +++ b/src/core/features/styles/services/styles.ts @@ -278,8 +278,7 @@ export class CoreStylesService { this.disableStyleElement(style, true); }); - // Set StatusBar properties. - CoreApp.setStatusBarColor(); + CoreApp.setSystemUIColors(); } /** @@ -341,7 +340,7 @@ export class CoreStylesService { this.disableStyleElementByName(siteId, sourceName, false); } - CoreApp.setStatusBarColor(); + CoreApp.setSystemUIColors(); } } @@ -371,8 +370,7 @@ export class CoreStylesService { })); if (!disabled) { - // Set StatusBar properties. - CoreApp.setStatusBarColor(); + CoreApp.setSystemUIColors(); } } @@ -390,7 +388,7 @@ export class CoreStylesService { await this.setStyle(CoreStylesService.TMP_SITE_ID, handler, false, config); })); - CoreApp.setStatusBarColor(); + CoreApp.setSystemUIColors(); } /** @@ -438,7 +436,7 @@ export class CoreStylesService { } delete this.stylesEls[siteId]; - CoreApp.setStatusBarColor(); + CoreApp.setSystemUIColors(); } } diff --git a/src/core/services/app.ts b/src/core/services/app.ts index 56263b14365..f7611def9a5 100644 --- a/src/core/services/app.ts +++ b/src/core/services/app.ts @@ -15,7 +15,7 @@ import { Injectable } from '@angular/core'; import { CoreDB } from '@services/db'; -import { CoreEvents } from '@singletons/events'; +import { CoreEventObserver, CoreEvents } from '@singletons/events'; import { SQLiteDB, SQLiteDBTableSchema } from '@classes/sqlitedb'; import { makeSingleton, Keyboard, StatusBar } from '@singletons'; @@ -31,6 +31,8 @@ import { CorePromisedValue } from '@classes/promised-value'; import { Subscription } from 'rxjs'; import { CorePlatform } from '@services/platform'; import { CoreNetwork, CoreNetworkConnection } from '@services/network'; +import { CoreNative } from '@features/native/services/native'; +import { CoreMainMenuProvider } from '@features/mainmenu/services/mainmenu'; /** * Factory to provide some global functionalities, like access to the global app database. @@ -56,9 +58,14 @@ export class CoreAppProvider { protected keyboardClosing = false; protected redirect?: CoreRedirectData; protected schemaVersionsTable = asyncInstance>(); + protected mainMenuListener?: CoreEventObserver; constructor() { this.logger = CoreLogger.getInstance('CoreAppProvider'); + if (CorePlatform.isAndroid()) { + this.mainMenuListener = + CoreEvents.on(CoreMainMenuProvider.MAIN_MENU_VISIBILITY_UPDATED, () => this.setAndroidNavigationBarColor()); + } } /** @@ -200,7 +207,7 @@ export class CoreAppProvider { * @returns Store URL. */ getAppStoreUrl(storesConfig: CoreStoreConfig): string | undefined { - if (this.isIOS() && storesConfig.ios) { + if (CorePlatform.isIOS() && storesConfig.ios) { return 'itms-apps://itunes.apple.com/app/' + storesConfig.ios; } @@ -618,6 +625,14 @@ export class CoreAppProvider { return () => false; } + /** + * Set System UI Colors. + */ + setSystemUIColors(): void { + this.setStatusBarColor(); + this.setAndroidNavigationBarColor(); + } + /** * Set StatusBar color depending on platform. * @@ -638,7 +653,7 @@ export class CoreAppProvider { const useLightText = CoreColors.isWhiteContrastingBetter(color); // styleDefault will use white text on iOS when darkmode is on. Force the background to black. - if (this.isIOS() && !useLightText && window.matchMedia('(prefers-color-scheme: dark)').matches) { + if (CorePlatform.isIOS() && !useLightText && window.matchMedia('(prefers-color-scheme: dark)').matches) { StatusBar.backgroundColorByHexString('#000000'); StatusBar.styleLightContent(); } else { @@ -684,6 +699,27 @@ export class CoreAppProvider { } } + /** + * Set NavigationBar color for Android + * + * @param color RGB color to use as background. If not set the css variable will be read. + */ + protected setAndroidNavigationBarColor(color?: string): void { + if (!CorePlatform.isAndroid()) { + return; + } + + if (!color) { + // Get the default color to change it. + color = CoreColors.getBottomPageBackgroundColor(); + } + + const useLightText = CoreColors.isWhiteContrastingBetter(color); + + console.error(color, useLightText); + CoreNative.plugin('systemUI')?.setNavigationBarColor(color, !useLightText); + } + } export const CoreApp = makeSingleton(CoreAppProvider); diff --git a/src/core/singletons/colors.ts b/src/core/singletons/colors.ts index 44affaa5909..806fffe7412 100644 --- a/src/core/singletons/colors.ts +++ b/src/core/singletons/colors.ts @@ -206,4 +206,23 @@ export class CoreColors { return CoreColors.getColorHex(color); } + /** + * Get the bottom page current background color. Bottom bar if shown or page background otherwise. + * + * @returns Color in hex format. + */ + static getBottomPageBackgroundColor(): string { + const element = document.querySelector('ion-tabs.placement-bottom:not(.tabshidden) ion-tab-bar.mainmenu-tabs'); + let color: string; + + if (element) { + color = getComputedStyle(element).getPropertyValue('--background').trim(); + } else { + // Fallback, it won't always work. + color = getComputedStyle(document.body).getPropertyValue('--ion-background-color').trim(); + } + + return CoreColors.getColorHex(color); + } + }