mirror of
https://github.com/NativeScript/NativeScript.git
synced 2025-11-05 13:26:48 +08:00
feat(mode-night): apply ns-dark|ns-light class to root view at app launch
This commit is contained in:
@@ -42,7 +42,7 @@ import {
|
||||
} from "./application";
|
||||
|
||||
import { CLASS_PREFIX, pushToRootViewCssClasses, removeFromRootViewCssClasses } from "../css/system-classes";
|
||||
import { DeviceOrientation } from "../ui/enums/enums";
|
||||
import { DeviceOrientation, SystemAppearance } from "../ui/enums/enums";
|
||||
|
||||
export { UnhandledErrorEventData, DiscardedErrorEventData, CssChangedEventData, LoadAppCSSEventData };
|
||||
|
||||
@@ -55,6 +55,7 @@ export const lowMemoryEvent = "lowMemory";
|
||||
export const uncaughtErrorEvent = "uncaughtError";
|
||||
export const discardedErrorEvent = "discardedError";
|
||||
export const orientationChangedEvent = "orientationChanged";
|
||||
export const systemAppearanceChangedEvent = "systemAppearanceChanged";
|
||||
|
||||
const ORIENTATION_CSS_CLASSES = [
|
||||
`${CLASS_PREFIX}${DeviceOrientation.portrait}`,
|
||||
@@ -62,6 +63,11 @@ const ORIENTATION_CSS_CLASSES = [
|
||||
`${CLASS_PREFIX}${DeviceOrientation.unknown}`
|
||||
];
|
||||
|
||||
const SYSTEM_APPEARANCE_CSS_CLASSES = [
|
||||
`${CLASS_PREFIX}${SystemAppearance.light}`,
|
||||
`${CLASS_PREFIX}${SystemAppearance.dark}`
|
||||
];
|
||||
|
||||
let cssFile: string = "./app.css";
|
||||
|
||||
let resources: any = {};
|
||||
@@ -126,13 +132,13 @@ export function loadAppCss(): void {
|
||||
}
|
||||
}
|
||||
|
||||
export function applyCssClass(rootView: View, cssClass: string) {
|
||||
function applyCssClass(rootView: View, cssClass: string) {
|
||||
pushToRootViewCssClasses(cssClass);
|
||||
rootView.cssClasses.add(cssClass);
|
||||
rootView._onCssStateChange();
|
||||
}
|
||||
|
||||
export function removeCssClass(rootView: View, cssClass: string) {
|
||||
function removeCssClass(rootView: View, cssClass: string) {
|
||||
removeFromRootViewCssClasses(cssClass);
|
||||
rootView.cssClasses.delete(cssClass);
|
||||
}
|
||||
@@ -145,6 +151,14 @@ export function orientationChanged(rootView: View, newOrientation: "portrait" |
|
||||
}
|
||||
}
|
||||
|
||||
export function systemAppearanceChanged(rootView: View, newSystemAppearance: "dark" | "light"): void {
|
||||
const newSystemAppearanceCssClass = `${CLASS_PREFIX}${newSystemAppearance}`;
|
||||
if (!rootView.cssClasses.has(newSystemAppearanceCssClass)) {
|
||||
SYSTEM_APPEARANCE_CSS_CLASSES.forEach(cssClass => removeCssClass(rootView, cssClass));
|
||||
applyCssClass(rootView, newSystemAppearanceCssClass);
|
||||
}
|
||||
}
|
||||
|
||||
global.__onUncaughtError = function (error: NativeScriptError) {
|
||||
events.notify(<UnhandledErrorEventData>{ eventName: uncaughtErrorEvent, object: app, android: error, ios: error, error: error });
|
||||
};
|
||||
|
||||
@@ -9,12 +9,14 @@ import {
|
||||
AndroidApplication as AndroidApplicationDefinition,
|
||||
ApplicationEventData,
|
||||
CssChangedEventData,
|
||||
OrientationChangedEventData
|
||||
OrientationChangedEventData,
|
||||
SystemAppearanceChangedEventData
|
||||
} from ".";
|
||||
|
||||
import {
|
||||
displayedEvent, hasListeners, livesync, lowMemoryEvent, notify, Observable, on,
|
||||
orientationChanged, orientationChangedEvent, setApplication, suspendEvent
|
||||
orientationChanged, orientationChangedEvent, setApplication, suspendEvent,
|
||||
systemAppearanceChanged, systemAppearanceChangedEvent
|
||||
} from "./application-common";
|
||||
|
||||
import { profile } from "../profiling";
|
||||
@@ -51,6 +53,7 @@ export class AndroidApplication extends Observable implements AndroidApplication
|
||||
public static activityRequestPermissionsEvent = ActivityRequestPermissions;
|
||||
|
||||
private _orientation: "portrait" | "landscape" | "unknown";
|
||||
private _systemAppearance: "light" | "dark";
|
||||
public paused: boolean;
|
||||
public nativeApp: android.app.Application;
|
||||
public context: android.content.Context;
|
||||
@@ -105,6 +108,22 @@ export class AndroidApplication extends Observable implements AndroidApplication
|
||||
this._orientation = value;
|
||||
}
|
||||
|
||||
get systemAppearance(): "light" | "dark" {
|
||||
if (!this._systemAppearance) {
|
||||
const resources = this.context.getResources();
|
||||
const configuration = <android.content.res.Configuration>resources.getConfiguration();
|
||||
const systemAppearance = configuration.uiMode & android.content.res.Configuration.UI_MODE_NIGHT_MASK;
|
||||
|
||||
this._systemAppearance = getSystemAppearanceValue(systemAppearance);
|
||||
}
|
||||
|
||||
return this._systemAppearance;
|
||||
}
|
||||
|
||||
set systemAppearance(value: "light" | "dark") {
|
||||
this._systemAppearance = value;
|
||||
}
|
||||
|
||||
public registerBroadcastReceiver(intentFilter: string, onReceiveCallback: (context: android.content.Context, intent: android.content.Intent) => void) {
|
||||
ensureBroadCastReceiverClass();
|
||||
const that = this;
|
||||
@@ -131,6 +150,7 @@ export class AndroidApplication extends Observable implements AndroidApplication
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export interface AndroidApplication {
|
||||
on(eventNames: string, callback: (data: AndroidActivityEventData) => void, thisArg?: any);
|
||||
on(event: "activityCreated", callback: (args: AndroidActivityBundleEventData) => void, thisArg?: any);
|
||||
@@ -259,6 +279,13 @@ on(orientationChangedEvent, (args: OrientationChangedEventData) => {
|
||||
}
|
||||
});
|
||||
|
||||
on(systemAppearanceChangedEvent, (args: SystemAppearanceChangedEventData) => {
|
||||
const rootView = getRootView();
|
||||
if (rootView) {
|
||||
systemAppearanceChanged(rootView, args.newValue);
|
||||
}
|
||||
});
|
||||
|
||||
global.__onLiveSync = function __onLiveSync(context?: ModuleContext) {
|
||||
if (androidApp && androidApp.paused) {
|
||||
return;
|
||||
@@ -279,6 +306,16 @@ function getOrientationValue(orientation: number): "portrait" | "landscape" | "u
|
||||
}
|
||||
}
|
||||
|
||||
function getSystemAppearanceValue(systemAppearance: number): "dark" | "light" {
|
||||
switch (systemAppearance) {
|
||||
case android.content.res.Configuration.UI_MODE_NIGHT_YES:
|
||||
return "dark";
|
||||
case android.content.res.Configuration.UI_MODE_NIGHT_NO:
|
||||
case android.content.res.Configuration.UI_MODE_NIGHT_UNDEFINED:
|
||||
return "light";
|
||||
}
|
||||
}
|
||||
|
||||
function initLifecycleCallbacks() {
|
||||
const setThemeOnLaunch = profile("setThemeOnLaunch", (activity: androidx.appcompat.app.AppCompatActivity) => {
|
||||
// Set app theme after launch screen was used during startup
|
||||
@@ -394,6 +431,20 @@ function initComponentCallbacks() {
|
||||
object: androidApp
|
||||
});
|
||||
}
|
||||
|
||||
const newConfigSystemAppearance = newConfig.uiMode & android.content.res.Configuration.UI_MODE_NIGHT_MASK;
|
||||
const newSystemAppearance = getSystemAppearanceValue(newConfigSystemAppearance);
|
||||
|
||||
if (androidApp.systemAppearance !== newSystemAppearance) {
|
||||
androidApp.systemAppearance = newSystemAppearance;
|
||||
|
||||
notify(<SystemAppearanceChangedEventData>{
|
||||
eventName: systemAppearanceChangedEvent,
|
||||
android: androidApp.nativeApp,
|
||||
newValue: androidApp.systemAppearance,
|
||||
object: androidApp
|
||||
});
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
|
||||
44
tns-core-modules/application/application.d.ts
vendored
44
tns-core-modules/application/application.d.ts
vendored
@@ -101,6 +101,16 @@ export interface OrientationChangedEventData extends ApplicationEventData {
|
||||
newValue: "portrait" | "landscape" | "unknown";
|
||||
}
|
||||
|
||||
/**
|
||||
* Event data containing information for system appearance changed event.
|
||||
*/
|
||||
export interface SystemAppearanceChangedEventData extends ApplicationEventData {
|
||||
/**
|
||||
* New system appearance value.
|
||||
*/
|
||||
newValue: "light" | "dark";
|
||||
}
|
||||
|
||||
/**
|
||||
* Event data containing information about unhandled application errors.
|
||||
*/
|
||||
@@ -281,30 +291,20 @@ export function on(event: "uncaughtError", callback: (args: UnhandledErrorEventD
|
||||
export function on(event: "discardedError", callback: (args: DiscardedErrorEventData) => void, thisArg?: any);
|
||||
|
||||
/**
|
||||
* This event is raised the orientation of the current device has changed.
|
||||
* This event is raised when the orientation of the application changes.
|
||||
*/
|
||||
export function on(event: "orientationChanged", callback: (args: OrientationChangedEventData) => void, thisArg?: any);
|
||||
|
||||
/**
|
||||
<<<<<<< HEAD
|
||||
* This event is raised when the system appearance changes.
|
||||
*/
|
||||
export function on(event: "systemAppearanceChanged", callback: (args: SystemAppearanceChangedEventData) => void, thisArg?: any);
|
||||
|
||||
/**
|
||||
* Gets the orientation of the application.
|
||||
* Available values: "portrait", "landscape", "unknown".
|
||||
*/
|
||||
export function orientation(): "portrait" | "landscape" | "unknown";
|
||||
=======
|
||||
* Appends new CSS class to the system classes and applies it to the root view.
|
||||
* @param rootView - The root view of the application.
|
||||
* @param cssClass - The CSS class to apply.
|
||||
*/
|
||||
export function applyCssClass(rootView: View, cssClass: string);
|
||||
|
||||
/**
|
||||
* Removes CSS class from the system classes and deletes it from the root view.
|
||||
* @param rootView - The root view of the application.
|
||||
* @param cssClass - The CSS class to delete.
|
||||
*/
|
||||
export function removeCssClass(rootView: View, cssClass: string);
|
||||
>>>>>>> refactor(dark-mode): application module
|
||||
|
||||
/**
|
||||
* This is the Android-specific application object instance.
|
||||
@@ -440,6 +440,12 @@ export class AndroidApplication extends Observable {
|
||||
*/
|
||||
orientation: "portrait" | "landscape" | "unknown";
|
||||
|
||||
/**
|
||||
* Gets the system appearance.
|
||||
* Available values: "dark", "light".
|
||||
*/
|
||||
systemAppearance: "dark" | "light";
|
||||
|
||||
/**
|
||||
* The name of the application package.
|
||||
*/
|
||||
@@ -619,6 +625,12 @@ export interface iOSApplication {
|
||||
*/
|
||||
orientation: "portrait" | "landscape" | "unknown";
|
||||
|
||||
/**
|
||||
* Gets the system appearance.
|
||||
* Available values: "dark", "light".
|
||||
*/
|
||||
systemAppearance: "dark" | "light";
|
||||
|
||||
/**
|
||||
* The [UIApplication](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIApplication_Class/index.html).
|
||||
*/
|
||||
|
||||
@@ -4,12 +4,14 @@ import {
|
||||
iOSApplication as IOSApplicationDefinition,
|
||||
LaunchEventData,
|
||||
LoadAppCSSEventData,
|
||||
OrientationChangedEventData
|
||||
OrientationChangedEventData,
|
||||
SystemAppearanceChangedEventData
|
||||
} from ".";
|
||||
|
||||
import {
|
||||
applyCssClass, displayedEvent, exitEvent, getCssFileName, launchEvent, livesync, lowMemoryEvent, notify, on,
|
||||
orientationChanged, orientationChangedEvent, removeCssClass, resumeEvent, setApplication, suspendEvent
|
||||
displayedEvent, exitEvent, getCssFileName, launchEvent, livesync, lowMemoryEvent, notify, on,
|
||||
orientationChanged, orientationChangedEvent, resumeEvent, setApplication, suspendEvent,
|
||||
systemAppearanceChanged, systemAppearanceChangedEvent
|
||||
} from "./application-common";
|
||||
|
||||
// First reexport so that app module is initialized.
|
||||
@@ -25,18 +27,12 @@ import {
|
||||
} from "../css/system-classes";
|
||||
import { ios as iosView, View } from "../ui/core/view";
|
||||
import { Frame, NavigationEntry } from "../ui/frame";
|
||||
import { UserInterfaceStyle } from "../ui/enums/enums";
|
||||
import { device } from "../platform/platform";
|
||||
import { profile } from "../profiling";
|
||||
import { ios } from "../utils/utils";
|
||||
|
||||
const IOS_PLATFORM = "ios";
|
||||
|
||||
const UI_STYLE_CSS_CLASSES = [
|
||||
`${CLASS_PREFIX}${UserInterfaceStyle.light}`,
|
||||
`${CLASS_PREFIX}${UserInterfaceStyle.dark}`
|
||||
];
|
||||
|
||||
const getVisibleViewController = ios.getVisibleViewController;
|
||||
|
||||
// NOTE: UIResponder with implementation of window - related to https://github.com/NativeScript/ios-runtime/issues/430
|
||||
@@ -97,6 +93,7 @@ class IOSApplication implements IOSApplicationDefinition {
|
||||
private _observers: Array<NotificationObserver>;
|
||||
private _orientation: "portrait" | "landscape" | "unknown";
|
||||
private _rootView: View;
|
||||
private _systemAppearance: "light" | "dark";
|
||||
|
||||
constructor() {
|
||||
this._observers = new Array<NotificationObserver>();
|
||||
@@ -121,6 +118,15 @@ class IOSApplication implements IOSApplicationDefinition {
|
||||
return this._window.rootViewController;
|
||||
}
|
||||
|
||||
get systemAppearance(): "light" | "dark" {
|
||||
if (!this._systemAppearance) {
|
||||
const userInterfaceStyle = this.rootController.traitCollection.userInterfaceStyle;
|
||||
this._systemAppearance = getSystemAppearanceValue(userInterfaceStyle);
|
||||
}
|
||||
|
||||
return this._systemAppearance;
|
||||
}
|
||||
|
||||
get nativeApp(): UIApplication {
|
||||
return UIApplication.sharedApplication;
|
||||
}
|
||||
@@ -293,9 +299,21 @@ class IOSApplication implements IOSApplicationDefinition {
|
||||
this._window.makeKeyAndVisible();
|
||||
}
|
||||
|
||||
setupRootViewCssClasses(controller, rootView);
|
||||
setupRootViewCssClasses(rootView);
|
||||
rootView.on(iosView.traitCollectionColorAppearanceChangedEvent, () => {
|
||||
traitCollectionColorAppearanceChanged(controller, rootView);
|
||||
const userInterfaceStyle = controller.traitCollection.userInterfaceStyle;
|
||||
const newSystemAppearance = getSystemAppearanceValue(userInterfaceStyle);
|
||||
|
||||
if (this._systemAppearance !== newSystemAppearance) {
|
||||
this._systemAppearance = newSystemAppearance;
|
||||
|
||||
notify(<SystemAppearanceChangedEventData>{
|
||||
eventName: systemAppearanceChangedEvent,
|
||||
ios: this,
|
||||
newValue: this._systemAppearance,
|
||||
object: this
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -312,17 +330,6 @@ setApplication(iosApp);
|
||||
|
||||
let mainEntry: NavigationEntry;
|
||||
|
||||
function traitCollectionColorAppearanceChanged(controller: UIViewController, rootView: View) {
|
||||
const newUserInterfaceStyle = controller.traitCollection.userInterfaceStyle;
|
||||
const newUserInterfaceStyleValue = getUserInterfaceStyleValue(newUserInterfaceStyle);
|
||||
const newUserInterfaceStyleCssClass = `${CLASS_PREFIX}${newUserInterfaceStyleValue}`;
|
||||
|
||||
if (!rootView.cssClasses.has(newUserInterfaceStyleCssClass)) {
|
||||
UI_STYLE_CSS_CLASSES.forEach(cssClass => removeCssClass(rootView, cssClass));
|
||||
applyCssClass(rootView, newUserInterfaceStyleCssClass);
|
||||
}
|
||||
}
|
||||
|
||||
function createRootView(v?: View) {
|
||||
let rootView = v;
|
||||
if (!rootView) {
|
||||
@@ -379,9 +386,21 @@ export function _start(entry?: string | NavigationEntry) {
|
||||
visibleVC.presentViewControllerAnimatedCompletion(controller, true, null);
|
||||
}
|
||||
|
||||
setupRootViewCssClasses(controller, rootView);
|
||||
setupRootViewCssClasses(rootView);
|
||||
rootView.on(iosView.traitCollectionColorAppearanceChangedEvent, () => {
|
||||
traitCollectionColorAppearanceChanged(controller, rootView);
|
||||
const userInterfaceStyle = controller.traitCollection.userInterfaceStyle;
|
||||
const newSystemAppearance = getSystemAppearanceValue(userInterfaceStyle);
|
||||
|
||||
if (this._systemAppearance !== newSystemAppearance) {
|
||||
this._systemAppearance = newSystemAppearance;
|
||||
|
||||
notify(<SystemAppearanceChangedEventData>{
|
||||
eventName: systemAppearanceChangedEvent,
|
||||
ios: this,
|
||||
newValue: this._systemAppearance,
|
||||
object: this
|
||||
});
|
||||
}
|
||||
});
|
||||
iosApp.notifyAppStarted();
|
||||
}
|
||||
@@ -413,7 +432,7 @@ export function getNativeApplication(): UIApplication {
|
||||
return iosApp.nativeApp;
|
||||
}
|
||||
|
||||
function getUserInterfaceStyleValue(userInterfaceStyle: number): "dark" | "light" | "unspecified" {
|
||||
function getSystemAppearanceValue(userInterfaceStyle: number): "dark" | "light" {
|
||||
switch (userInterfaceStyle) {
|
||||
case UIUserInterfaceStyle.Unspecified:
|
||||
case UIUserInterfaceStyle.Light:
|
||||
@@ -449,17 +468,14 @@ function setViewControllerView(view: View): void {
|
||||
}
|
||||
}
|
||||
|
||||
function setupRootViewCssClasses(controller: UIViewController, rootView: View): void {
|
||||
function setupRootViewCssClasses(rootView: View): void {
|
||||
resetRootViewCssClasses();
|
||||
|
||||
const deviceType = device.deviceType.toLowerCase();
|
||||
const userInterfaceStyle = controller.traitCollection.userInterfaceStyle;
|
||||
const userInterfaceStyleValue = getUserInterfaceStyleValue(userInterfaceStyle);
|
||||
|
||||
pushToRootViewCssClasses(`${CLASS_PREFIX}${IOS_PLATFORM}`);
|
||||
pushToRootViewCssClasses(`${CLASS_PREFIX}${deviceType}`);
|
||||
pushToRootViewCssClasses(`${CLASS_PREFIX}${iosApp.orientation}`);
|
||||
pushToRootViewCssClasses(`${CLASS_PREFIX}${userInterfaceStyleValue}`);
|
||||
pushToRootViewCssClasses(`${CLASS_PREFIX}${iosApp.systemAppearance}`);
|
||||
|
||||
const rootViewCssClasses = getRootViewCssClasses();
|
||||
rootViewCssClasses.forEach(c => rootView.cssClasses.add(c));
|
||||
@@ -476,6 +492,13 @@ on(orientationChangedEvent, (args: OrientationChangedEventData) => {
|
||||
}
|
||||
});
|
||||
|
||||
on(systemAppearanceChangedEvent, (args: SystemAppearanceChangedEventData) => {
|
||||
const rootView = getRootView();
|
||||
if (rootView) {
|
||||
systemAppearanceChanged(rootView, args.newValue);
|
||||
}
|
||||
});
|
||||
|
||||
global.__onLiveSync = function __onLiveSync(context?: ModuleContext) {
|
||||
if (!started) {
|
||||
return;
|
||||
|
||||
@@ -188,7 +188,7 @@ export module StatusBarStyle {
|
||||
export const dark = "dark";
|
||||
}
|
||||
|
||||
export module UserInterfaceStyle {
|
||||
export module SystemAppearance {
|
||||
export const light = "light";
|
||||
export const dark = "dark";
|
||||
}
|
||||
|
||||
@@ -1289,6 +1289,7 @@ class ActivityCallbacksImplementation implements AndroidActivityCallbacks {
|
||||
pushToRootViewCssClasses(`${CLASS_PREFIX}${ANDROID_PLATFORM}`);
|
||||
pushToRootViewCssClasses(`${CLASS_PREFIX}${deviceType}`);
|
||||
pushToRootViewCssClasses(`${CLASS_PREFIX}${application.android.orientation}`);
|
||||
pushToRootViewCssClasses(`${CLASS_PREFIX}${application.android.systemAppearance}`);
|
||||
|
||||
const rootViewCssClasses = getRootViewCssClasses();
|
||||
rootViewCssClasses.forEach(c => this._rootView.cssClasses.add(c));
|
||||
|
||||
Reference in New Issue
Block a user