From ff24186a3296167ff4bf3955c9076706aef5c415 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pau=20Ferrer=20Oca=C3=B1a?= Date: Mon, 23 Nov 2020 12:29:56 +0100 Subject: [PATCH 1/3] MOBILE-3565 core: Listen to some login events to apply customizations --- config.xml | 1 + src/app/app.component.ts | 77 ++++++++++++++++++++++++++++++++++- src/core/services/lang.ts | 14 +++++++ src/core/singletons/events.ts | 5 +++ 4 files changed, 96 insertions(+), 1 deletion(-) diff --git a/config.xml b/config.xml index 6c14fa033..66c859986 100644 --- a/config.xml +++ b/config.xml @@ -31,6 +31,7 @@ + diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 1bd3a63de..0135f049a 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -17,9 +17,16 @@ import { NavController } from '@ionic/angular'; import { CoreLangProvider } from '@services/lang'; import { CoreLoginHelperProvider } from '@features/login/services/login.helper'; -import { CoreEvents, CoreEventSessionExpiredData } from '@singletons/events'; +import { + CoreEvents, + CoreEventSessionExpiredData, + CoreEventSiteAddedData, + CoreEventSiteData, + CoreEventSiteUpdatedData, +} from '@singletons/events'; import { Network, NgZone, Platform } from '@singletons/core.singletons'; import { CoreApp } from '@services/app'; +import { CoreSites } from '@services/sites'; @Component({ selector: 'app-root', @@ -37,6 +44,14 @@ export class AppComponent implements OnInit { /** * Component being initialized. + * + * @todo Review all old code to see if something is missing: + * - IAB events listening. + * - Platform pause/resume subscriptions. + * - handleOpenURL and openWindowSafely. + * - Screen orientation events (probably it can be removed). + * - Back button registering to close modal first. + * - Note: HideKeyboardFormAccessoryBar has been moved to config.xml. */ ngOnInit(): void { CoreEvents.on(CoreEvents.LOGOUT, () => { @@ -55,9 +70,59 @@ export class AppComponent implements OnInit { this.loginHelper.sessionExpired(data); }); + // Listen for passwordchange and usernotfullysetup events to open InAppBrowser. + CoreEvents.on(CoreEvents.PASSWORD_CHANGE_FORCED, (data: CoreEventSiteData) => { + this.loginHelper.passwordChangeForced(data.siteId!); + }); + CoreEvents.on(CoreEvents.USER_NOT_FULLY_SETUP, (data: CoreEventSiteData) => { + this.loginHelper.openInAppForEdit(data.siteId!, '/user/edit.php', 'core.usernotfullysetup'); + }); + + // Listen for sitepolicynotagreed event to accept the site policy. + CoreEvents.on(CoreEvents.SITE_POLICY_NOT_AGREED, (data: CoreEventSiteData) => { + this.loginHelper.sitePolicyNotAgreed(data.siteId); + }); + + CoreEvents.on(CoreEvents.LOGIN, async (data: CoreEventSiteData) => { + if (data.siteId) { + const site = await CoreSites.instance.getSite(data.siteId); + const info = site.getInfo(); + if (info) { + // Add version classes to body. + this.removeVersionClass(); + this.addVersionClass(CoreSites.instance.getReleaseNumber(info.release || '')); + } + } + + this.loadCustomStrings(); + }); + + CoreEvents.on(CoreEvents.SITE_UPDATED, (data: CoreEventSiteUpdatedData) => { + if (data.siteId == CoreSites.instance.getCurrentSiteId()) { + this.loadCustomStrings(); + + // Add version classes to body. + this.removeVersionClass(); + this.addVersionClass(CoreSites.instance.getReleaseNumber(data.release || '')); + } + }); + + CoreEvents.on(CoreEvents.SITE_ADDED, (data: CoreEventSiteAddedData) => { + if (data.siteId == CoreSites.instance.getCurrentSiteId()) { + this.loadCustomStrings(); + + // Add version classes to body. + this.removeVersionClass(); + this.addVersionClass(CoreSites.instance.getReleaseNumber(data.release || '')); + } + }); + this.onPlatformReady(); } + /** + * Async init function on platform ready. + */ protected async onPlatformReady(): Promise { await Platform.instance.ready(); @@ -83,6 +148,16 @@ export class AppComponent implements OnInit { }); } + /** + * Load custom lang strings. This cannot be done inside the lang provider because it causes circular dependencies. + */ + protected loadCustomStrings(): void { + const currentSite = CoreSites.instance.getCurrentSite(); + if (currentSite) { + this.langProvider.loadCustomStringsFromSite(currentSite); + } + } + /** * Convenience function to add version to body classes. * diff --git a/src/core/services/lang.ts b/src/core/services/lang.ts index e1e0d6879..e4663d0a7 100644 --- a/src/core/services/lang.ts +++ b/src/core/services/lang.ts @@ -21,6 +21,7 @@ import { CoreConfig } from '@services/config'; import { makeSingleton, Translate, Platform } from '@singletons/core.singletons'; import * as moment from 'moment'; +import { CoreSite } from '../classes/site'; /* * Service to handle language features, like changing the current language. @@ -311,6 +312,19 @@ export class CoreLangProvider { }); } + /** + * Loads custom strings obtained from site. + * + * @param currentSite Current site object. + */ + loadCustomStringsFromSite(currentSite: CoreSite): void { + const customStrings = currentSite.getStoredConfig('tool_mobile_customlangstrings'); + + if (typeof customStrings != 'undefined') { + this.loadCustomStrings(customStrings); + } + } + /** * Load certain custom strings. * diff --git a/src/core/singletons/events.ts b/src/core/singletons/events.ts index e58dc9740..f7033acd0 100644 --- a/src/core/singletons/events.ts +++ b/src/core/singletons/events.ts @@ -205,6 +205,11 @@ export type CoreEventSiteData = { */ export type CoreEventSiteUpdatedData = CoreEventSiteData & CoreSiteInfoResponse; +/** + * Data passed to SITE_ADDED event. + */ +export type CoreEventSiteAddedData = CoreEventSiteData & CoreSiteInfoResponse; + /** * Data passed to SESSION_EXPIRED event. */ From 2c38c1683daa299e58288ccaedf60ae3fc79964f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pau=20Ferrer=20Oca=C3=B1a?= Date: Mon, 23 Nov 2020 12:30:30 +0100 Subject: [PATCH 2/3] MOBILE-3565 core: Apply status bar color changes --- config/config.json | 8 --- src/app/app.component.ts | 3 + src/core/services/app.ts | 51 +++++++-------- src/core/singletons/colors.ts | 117 ++++++++++++++++++++++++++++++++++ src/theme/variables.scss | 3 +- src/types/global.d.ts | 8 --- 6 files changed, 144 insertions(+), 46 deletions(-) create mode 100644 src/core/singletons/colors.ts diff --git a/config/config.json b/config/config.json index 601928dfa..ee5a78a74 100644 --- a/config/config.json +++ b/config/config.json @@ -86,14 +86,6 @@ "forcedefaultlanguage": false, "privacypolicy": "https:\/\/moodle.net\/moodle-app-privacy\/", "notificoncolor": "#f98012", - "statusbarbg": false, - "statusbarlighttext": false, - "statusbarbgios": "#f98012", - "statusbarlighttextios": true, - "statusbarbgandroid": "#df7310", - "statusbarlighttextandroid": true, - "statusbarbgremotetheme": "#000000", - "statusbarlighttextremotetheme": true, "enableanalytics": false, "enableonboarding": true, "forceColorScheme": "", diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 0135f049a..91c79bbaf 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -146,6 +146,9 @@ export class AppComponent implements OnInit { } }); }); + + // Set StatusBar properties. + CoreApp.instance.setStatusBarColor(); } /** diff --git a/src/core/services/app.ts b/src/core/services/app.ts index a09eea202..6d91795c4 100644 --- a/src/core/services/app.ts +++ b/src/core/services/app.ts @@ -26,6 +26,7 @@ import { CoreConstants } from '@/core/constants'; import { makeSingleton, Keyboard, Network, StatusBar, Platform, Device } from '@singletons/core.singletons'; import { CoreLogger } from '@singletons/logger'; import { DBNAME, SCHEMA_VERSIONS_TABLE_NAME, SCHEMA_VERSIONS_TABLE_SCHEMA, SchemaVersionsDBEntry } from '@services/app.db'; +import { CoreColors } from '../singletons/colors'; /** * Factory to provide some global functionalities, like access to the global app database. @@ -611,43 +612,35 @@ export class CoreAppProvider { /** * Set StatusBar color depending on platform. + * + * @param color RGB color to use as status bar background. If not set the css variable will be read. */ - setStatusBarColor(): void { - if (typeof CoreConstants.CONFIG.statusbarbgios == 'string' && this.isIOS()) { - // IOS Status bar properties. - StatusBar.instance.overlaysWebView(false); - StatusBar.instance.backgroundColorByHexString(CoreConstants.CONFIG.statusbarbgios); - CoreConstants.CONFIG.statusbarlighttextios ? StatusBar.instance.styleLightContent() : StatusBar.instance.styleDefault(); - } else if (typeof CoreConstants.CONFIG.statusbarbgandroid == 'string' && this.isAndroid()) { - // Android Status bar properties. - StatusBar.instance.backgroundColorByHexString(CoreConstants.CONFIG.statusbarbgandroid); - CoreConstants.CONFIG.statusbarlighttextandroid ? - StatusBar.instance.styleLightContent() : StatusBar.instance.styleDefault(); - } else if (typeof CoreConstants.CONFIG.statusbarbg == 'string') { - // Generic Status bar properties. - this.isIOS() && StatusBar.instance.overlaysWebView(false); - StatusBar.instance.backgroundColorByHexString(CoreConstants.CONFIG.statusbarbg); - CoreConstants.CONFIG.statusbarlighttext ? StatusBar.instance.styleLightContent() : StatusBar.instance.styleDefault(); - } else { - // Default Status bar properties. - this.isAndroid() ? StatusBar.instance.styleLightContent() : StatusBar.instance.styleDefault(); + setStatusBarColor(color?: string): void { + if (!color) { + // Get the default color to reset it. + color = getComputedStyle(document.documentElement).getPropertyValue('--ion-statusbar-background').trim(); } + + // Make darker on Android. + if (this.isAndroid()) { + color = CoreColors.darker(color); + } + + const useLightText = CoreColors.isWhiteContrastingBetter(color); + const statusBar = StatusBar.instance; + statusBar.backgroundColorByHexString(color); + useLightText ? statusBar.styleLightContent() : statusBar.styleDefault(); + + this.isIOS() && statusBar.overlaysWebView(false); } /** * Reset StatusBar color if any was set. + * + * @deprecated Use setStatusBarColor passing the color of the new statusbar color loaded on remote theme or no color to reset. */ resetStatusBarColor(): void { - if (typeof CoreConstants.CONFIG.statusbarbgremotetheme == 'string' && - ((typeof CoreConstants.CONFIG.statusbarbgios == 'string' && this.isIOS()) || - (typeof CoreConstants.CONFIG.statusbarbgandroid == 'string' && this.isAndroid()) || - typeof CoreConstants.CONFIG.statusbarbg == 'string')) { - // If the status bar has been overriden and there's a fallback color for remote themes, use it now. - this.isIOS() && StatusBar.instance.overlaysWebView(false); - StatusBar.instance.backgroundColorByHexString(CoreConstants.CONFIG.statusbarbgremotetheme); - CoreConstants.CONFIG.statusbarlighttextremotetheme ? - StatusBar.instance.styleLightContent() : StatusBar.instance.styleDefault(); - } + this.setStatusBarColor(); } /** diff --git a/src/core/singletons/colors.ts b/src/core/singletons/colors.ts new file mode 100644 index 000000000..c8cc5cce9 --- /dev/null +++ b/src/core/singletons/colors.ts @@ -0,0 +1,117 @@ +// (C) Copyright 2015 Moodle Pty Ltd. +// +// Licensed 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. + +/** + * Color components contained within a rgb color. + */ +interface ColorComponents { + red: number; // Red component of an RGB color [0-255]. + green: number; // Green component of an RGB color [0-255]. + blue: number; // Blue component of an RGB color [0-255]. +} + +/** + * Singleton with helper functions for colors. + */ +export class CoreColors { + + /** + * Returns better contrast color. + * + * @param color Black or white texts. + * @return True if white contrasts better than black. False otherwise. + */ + static isWhiteContrastingBetter(color: string): boolean { + return CoreColors.luma(color) < 165; + } + + /** + * Returns the same color 10% darker to be used as status bar on Android. + * + * @param color Color to get darker. + * @return Darker Hex RGB color. + */ + static darker(color: string, percent: number = 10): string { + percent = 1 - (percent / 100); + const components = CoreColors.hexToRGB(color); + components.red = Math.floor(components.red * percent) ; + components.green = Math.floor(components.green * percent) ; + components.blue = Math.floor(components.blue * percent) ; + + return CoreColors.RGBToHex(components); + } + + /** + * Gets the luma of a color. + * + * @param color Hex RGB color. + * @return Luma number based on SMPTE C, Rec. 709 weightings. + */ + protected static luma(color: string): number { + const rgb = CoreColors.hexToRGB(color); + + return (rgb.red * 0.2126) + (rgb.green * 0.7152) + (rgb.blue * 0.0722); + } + + /** + * Converts Hex RGB to Color components. + * + * @param color Hexadec RGB Color. + * @return RGB color components. + */ + protected static hexToRGB(color: string): ColorComponents { + if (color.charAt(0) == '#') { + color = color.substr(1); + } + + if (color.length === 3) { + color = color.charAt(0) + color.charAt(0) + color.charAt(1) + color.charAt(1) + color.charAt(2) + color.charAt(2); + } else if (color.length !== 6) { + throw('Invalid hex color: ' + color); + } + + return { + red: parseInt(color.substr(0, 2), 16), + green: parseInt(color.substr(2, 2), 16), + blue: parseInt(color.substr(4, 2), 16), + }; + + } + + /** + * Converts RGB components to Hex string. + * + * @param color Color components. + * @return RGB color in string. + */ + protected static RGBToHex(color: ColorComponents): string { + return '#' + CoreColors.componentToHex(color.red) + + CoreColors.componentToHex(color.green) + + CoreColors.componentToHex(color.blue); + + } + + /** + * Converts a color component from decimal to hexadec. + * + * @param c color component in decimal. + * @return Hexadec of the color component. + */ + protected static componentToHex(c: number): string { + const hex = c.toString(16); + + return hex.length == 1 ? '0' + hex : hex; + } + +} diff --git a/src/theme/variables.scss b/src/theme/variables.scss index e35b04f3c..aeb85e89e 100644 --- a/src/theme/variables.scss +++ b/src/theme/variables.scss @@ -100,9 +100,10 @@ --color: var(--custom-bottom-tabs-color, var(--white)); } + --ion-statusbar-background: var(--custom-toolbar-background, var(--ion-color-primary)); ion-toolbar { --color: var(--custom-toolbar-color, var(--ion-color-primary-contrast)); - --background: var(--custom-toolbar-background, var(--ion-color-primary)); + --background: var(--ion-statusbar-background); ion-spinner { --color: var(--custom-toolbar-color, var(--ion-color-primary-contrast)); diff --git a/src/types/global.d.ts b/src/types/global.d.ts index 9d4eedb56..21f818810 100644 --- a/src/types/global.d.ts +++ b/src/types/global.d.ts @@ -50,14 +50,6 @@ declare global { forcedefaultlanguage: boolean; privacypolicy: string; notificoncolor: string; - statusbarbg: boolean; - statusbarlighttext: boolean; - statusbarbgios: string; - statusbarlighttextios: boolean; - statusbarbgandroid: string; - statusbarlighttextandroid: boolean; - statusbarbgremotetheme: string; - statusbarlighttextremotetheme: boolean; enableanalytics: boolean; enableonboarding: boolean; forceColorScheme: CoreColorScheme; From 9181ac67572b540766343e78cb9bd46da446c87c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pau=20Ferrer=20Oca=C3=B1a?= Date: Mon, 23 Nov 2020 12:33:20 +0100 Subject: [PATCH 3/3] MOBILE-3565 search: Remove CoreSearchComponentsModule as a provider --- package-lock.json | 2 +- src/core/features/search/search.module.ts | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 229012d24..cb5a21026 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6522,7 +6522,7 @@ "integrity": "sha512-EYC5eQFVkoYXq39l7tYKE6lEjHJ04mvTmKXxGL7quHLdFPfJMNzru/UYpn92AOfpl3PQaZmou78C7EgmFOwFQQ==" }, "cordova-plugin-wkuserscript": { - "version": "git+https://github.com/moodlemobile/cordova-plugin-wkuserscript.git#6413f4bb3c2565f353e690b5c1450b69ad9e860e", + "version": "git+https://github.com/moodlemobile/cordova-plugin-wkuserscript.git#aa77d0f98a3fb106f2e798e5adf5882f01a2c947", "from": "git+https://github.com/moodlemobile/cordova-plugin-wkuserscript.git" }, "cordova-plugin-wkwebview-cookies": { diff --git a/src/core/features/search/search.module.ts b/src/core/features/search/search.module.ts index a59da3976..89ba8527e 100644 --- a/src/core/features/search/search.module.ts +++ b/src/core/features/search/search.module.ts @@ -20,13 +20,10 @@ import { CoreSearchComponentsModule } from './components/components.module'; import { SITE_SCHEMA } from './services/search-history-db'; @NgModule({ - declarations: [ - ], imports: [ CoreSearchComponentsModule, ], providers: [ - CoreSearchComponentsModule, { provide: CORE_SITE_SCHEMAS, useValue: [SITE_SCHEMA], multi: true }, ], })