feat: add deprecated shims to keep b/w compatibility

This commit is contained in:
Igor Randjelovic
2023-05-11 20:51:45 +02:00
parent d315d35d00
commit 5b4624a308
9 changed files with 1544 additions and 1273 deletions

View File

@@ -51,8 +51,8 @@ export function testAndroidApplicationInitialized() {
Application.android.foregroundActivity,
'Android foregroundActivity not initialized.'
);
// @ts-expect-error
TKUnit.assert(
// @ts-expect-error
Application.android.foregroundActivity.isNativeScriptActivity,
'Android foregroundActivity.isNativeScriptActivity is false.'
);

View File

@@ -5,136 +5,6 @@ if (typeof NSDate !== 'undefined') {
start = java.lang.System.currentTimeMillis();
}
// Utils.android.dismissSoftInput();
// Utils.ad.dismissSoftInput();
// Utils.ios.animateWithSpring();
// Utils.android.isRealDevice()
// isAndroid
// const {
// GC,
// SDK_VERSION,
// RESOURCE_PREFIX,
// FILE_PREFIX,
// isFontIconURI,
// isDataURI,
// isFileOrResourcePath,
// getFileExtension,
// executeOnMainThread,
// executeOnUIThread,
// mainThreadify,
// isMainThread,
// dispatchToMainThread,
// queueMacrotask,
// queueGC,
// debounce,
// throttle,
// releaseNativeObject,
// convertString,
// escapeRegexSymbols,
// getModuleName,
// openFile,
// openUrl,
// isRealDevice,
// layout,
// android,
// // legacy (a lot of plugins use the shorthand "ad" Utils.ad instead of Utils.android)
// ad,
// ios,
// dataSerialize,
// dataDeserialize,
// numberHasDecimals,
// numberIs64Bit,
// setTimeout,
// setInterval,
// clearInterval,
// clearTimeout,
// Source,
// ClassInfo,
// getClass,
// getBaseClasses,
// getClassInfo,
// isBoolean,
// isDefined,
// isFunction,
// isNullOrUndefined,
// isNumber,
// isObject,
// isString,
// isUndefined,
// toUIString,
// verifyCallback,
// dismissSoftInput,
// dismissKeyboard,
// copyToClipboard,
// isEmoji,
// getDurationWithDampingFromSpring,
// // wtf
// } = Utils;
// const {
// launchEvent,
// displayedEvent,
// uncaughtErrorEvent,
// discardedErrorEvent,
// suspendEvent,
// resumeEvent,
// exitEvent,
// foregroundEvent,
// backgroundEvent,
// lowMemoryEvent,
// orientationChangedEvent,
// systemAppearanceChangedEvent,
// systemAppearanceChanged,
// fontScaleChangedEvent,
// setMaxRefreshRate,
// getMainEntry,
// getRootView,
// resetRootView,
// getResources,
// setResources,
// setCssFileName,
// getCssFileName,
// loadAppCss,
// addCss,
// on,
// off,
// notify,
// hasListeners,
// run,
// orientation,
// getNativeApplication,
// hasLaunched,
// systemAppearance,
// setAutoSystemAppearanceChanged,
// android: appAndroid,
// inBackground,
// suspended,
// ios: iosApp,
// // get android() {
// // ensureNativeApplication();
// // return appAndroid;
// // },
// // get ios() {
// // ensureNativeApplication();
// // return iosApp;
// // },
// // get suspended() {
// // return suspended;
// // },
// // get inBackground() {
// // return inBackground;
// // },
// } = Application;
// Application.suspendEvent
import {
Application,
ApplicationEventData,
@@ -148,10 +18,7 @@ import {
AndroidActivityBackPressedEventData,
} from '@nativescript/core';
// import { Application } from "@nativescript/core/application-new";
// Application.AndroidApplication
// Application2.AndroidApplication
// import * as Application from "@nativescript/core/application";
if (Application.ios) {
// Observe application notifications.

View File

@@ -0,0 +1,393 @@
import { Application } from './application';
/**
* @deprecated Deep imports into the Application module are depreacted and will be removed in a future release.
* Use the `Application` class imported from "@nativescript/core" instead:
* ```ts
* import { Application } from "@nativescript/core";
* Application.addCss()
* ```
*/
export const addCss = Application.addCss.bind(Application);
/**
* @deprecated Deep imports into the Application module are depreacted and will be removed in a future release.
* Use the `Application` class imported from "@nativescript/core" instead:
* ```ts
* import { Application } from "@nativescript/core";
* Application.getCssFileName()
* ```
*/
export const getCssFileName = Application.getCssFileName.bind(Application);
/**
* @deprecated Deep imports into the Application module are depreacted and will be removed in a future release.
* Use the `Application` class imported from "@nativescript/core" instead:
* ```ts
* import { Application } from "@nativescript/core";
* Application.getMainEntry()
* ```
*/
export const getMainEntry = Application.getMainEntry.bind(Application);
/**
* @deprecated Deep imports into the Application module are depreacted and will be removed in a future release.
* Use the `Application` class imported from "@nativescript/core" instead:
* ```ts
* import { Application } from "@nativescript/core";
* Application.getNativeApplication()
* ```
*/
export const getNativeApplication = Application.getNativeApplication.bind(Application);
/**
* @deprecated Deep imports into the Application module are depreacted and will be removed in a future release.
* Use the `Application` class imported from "@nativescript/core" instead:
* ```ts
* import { Application } from "@nativescript/core";
* Application.getResources()
* ```
*/
export const getResources = Application.getResources.bind(Application);
/**
* @deprecated Deep imports into the Application module are depreacted and will be removed in a future release.
* Use the `Application` class imported from "@nativescript/core" instead:
* ```ts
* import { Application } from "@nativescript/core";
* Application.getRootView()
* ```
*/
export const getRootView = Application.getRootView.bind(Application);
/**
* @deprecated Deep imports into the Application module are depreacted and will be removed in a future release.
* Use the `Application` class imported from "@nativescript/core" instead:
* ```ts
* import { Application } from "@nativescript/core";
* Application.hasLaunched()
* ```
*/
export const hasLaunched = Application.hasLaunched.bind(Application);
/**
* @deprecated Deep imports into the Application module are depreacted and will be removed in a future release.
* Use the `Application` class imported from "@nativescript/core" instead:
* ```ts
* import { Application } from "@nativescript/core";
* Application.hasListeners()
* ```
*/
export const hasListeners = Application.hasListeners.bind(Application);
/**
* @deprecated Deep imports into the Application module are depreacted and will be removed in a future release.
* Use the `Application` class imported from "@nativescript/core" instead:
* ```ts
* import { Application } from "@nativescript/core";
* Application.loadAppCss()
* ```
*/
export const loadAppCss = Application.loadAppCss.bind(Application);
/**
* @deprecated Deep imports into the Application module are depreacted and will be removed in a future release.
* Use the `Application` class imported from "@nativescript/core" instead:
* ```ts
* import { Application } from "@nativescript/core";
* Application.notify()
* ```
*/
export const notify = Application.notify.bind(Application);
/**
* @deprecated Deep imports into the Application module are depreacted and will be removed in a future release.
* Use the `Application` class imported from "@nativescript/core" instead:
* ```ts
* import { Application } from "@nativescript/core";
* Application.off()
* ```
*/
export const off = Application.off.bind(Application);
/**
* @deprecated Deep imports into the Application module are depreacted and will be removed in a future release.
* Use the `Application` class imported from "@nativescript/core" instead:
* ```ts
* import { Application } from "@nativescript/core";
* Application.on()
* ```
*/
export const on = Application.on.bind(Application);
/**
* @deprecated Deep imports into the Application module are depreacted and will be removed in a future release.
* Use the `Application` class imported from "@nativescript/core" instead:
* ```ts
* import { Application } from "@nativescript/core";
* Application.orientation
* ```
*/
export const orientation = () => Application.orientation;
/**
* @deprecated Deep imports into the Application module are depreacted and will be removed in a future release.
* Use the `Application` class imported from "@nativescript/core" instead:
* ```ts
* import { Application } from "@nativescript/core";
* Application.resetRootView()
* ```
*/
export const resetRootView = Application.resetRootView.bind(Application);
/**
* @deprecated Deep imports into the Application module are depreacted and will be removed in a future release.
* Use the `Application` class imported from "@nativescript/core" instead:
* ```ts
* import { Application } from "@nativescript/core";
* Application.resetRootView()
* ```
*/
export const _resetRootView = Application.resetRootView.bind(Application);
/**
* @deprecated Deep imports into the Application module are depreacted and will be removed in a future release.
* Use the `Application` class imported from "@nativescript/core" instead:
* ```ts
* import { Application } from "@nativescript/core";
* Application.run()
* ```
*/
export const run = Application.run.bind(Application);
/**
* @deprecated Deep imports into the Application module are depreacted and will be removed in a future release.
* Use the `Application` class imported from "@nativescript/core" instead:
* ```ts
* import { Application } from "@nativescript/core";
* Application.setAutoSystemAppearanceChanged()
* ```
*/
export const setAutoSystemAppearanceChanged =
Application.setAutoSystemAppearanceChanged.bind(Application);
/**
* @deprecated Deep imports into the Application module are depreacted and will be removed in a future release.
* Use the `Application` class imported from "@nativescript/core" instead:
* ```ts
* import { Application } from "@nativescript/core";
* Application.setCssFileName()
* ```
*/
export const setCssFileName = Application.setCssFileName.bind(Application);
/**
* @deprecated Deep imports into the Application module are depreacted and will be removed in a future release.
* Use the `Application` class imported from "@nativescript/core" instead:
* ```ts
* import { Application } from "@nativescript/core";
* Application.setMaxRefreshRate()
* ```
*/
export const setMaxRefreshRate = Application.setMaxRefreshRate.bind(Application);
/**
* @deprecated Deep imports into the Application module are depreacted and will be removed in a future release.
* Use the `Application` class imported from "@nativescript/core" instead:
* ```ts
* import { Application } from "@nativescript/core";
* Application.setResources()
* ```
*/
export const setResources = Application.setResources.bind(Application);
/**
* @deprecated Deep imports into the Application module are depreacted and will be removed in a future release.
* Use the `Application` class imported from "@nativescript/core" instead:
* ```ts
* import { Application } from "@nativescript/core";
* Application.systemAppearance
* ```
*/
export const systemAppearance = () => Application.systemAppearance;
/**
* @deprecated Deep imports into the Application module are depreacted and will be removed in a future release.
* Use the `Application` class imported from "@nativescript/core" instead:
* ```ts
* import { Application } from "@nativescript/core";
* Application.systemAppearanceChanged()
* ```
*/
export const systemAppearanceChanged =
Application.systemAppearanceChanged.bind(Application);
/**
* @deprecated Deep imports into the Application module are depreacted and will be removed in a future release.
* Use the `Application` class imported from "@nativescript/core" instead:
* ```ts
* import { Application } from "@nativescript/core";
* Application.discardedErrorEvent
* ```
*/
export const discardedErrorEvent = Application.discardedErrorEvent;
/**
* @deprecated Deep imports into the Application module are depreacted and will be removed in a future release.
* Use the `Application` class imported from "@nativescript/core" instead:
* ```ts
* import { Application } from "@nativescript/core";
* Application.uncaughtErrorEvent
* ```
*/
export const uncaughtErrorEvent = Application.uncaughtErrorEvent;
/**
* @deprecated Deep imports into the Application module are depreacted and will be removed in a future release.
* Use the `Application` class imported from "@nativescript/core" instead:
* ```ts
* import { Application } from "@nativescript/core";
* Application.displayedEvent
* ```
*/
export const displayedEvent = Application.displayedEvent;
/**
* @deprecated Deep imports into the Application module are depreacted and will be removed in a future release.
* Use the `Application` class imported from "@nativescript/core" instead:
* ```ts
* import { Application } from "@nativescript/core";
* Application.exitEvent
* ```
*/
export const exitEvent = Application.exitEvent;
/**
* @deprecated Deep imports into the Application module are depreacted and will be removed in a future release.
* Use the `Application` class imported from "@nativescript/core" instead:
* ```ts
* import { Application } from "@nativescript/core";
* Application.fontScaleChangedEvent
* ```
*/
export const fontScaleChangedEvent = Application.fontScaleChangedEvent;
/**
* @deprecated Deep imports into the Application module are depreacted and will be removed in a future release.
* Use the `Application` class imported from "@nativescript/core" instead:
* ```ts
* import { Application } from "@nativescript/core";
* Application.launchEvent
* ```
*/
export const launchEvent = Application.launchEvent;
/**
* @deprecated Deep imports into the Application module are depreacted and will be removed in a future release.
* Use the `Application` class imported from "@nativescript/core" instead:
* ```ts
* import { Application } from "@nativescript/core";
* Application.lowMemoryEvent
* ```
*/
export const lowMemoryEvent = Application.lowMemoryEvent;
/**
* @deprecated Deep imports into the Application module are depreacted and will be removed in a future release.
* Use the `Application` class imported from "@nativescript/core" instead:
* ```ts
* import { Application } from "@nativescript/core";
* Application.resumeEvent
* ```
*/
export const resumeEvent = Application.resumeEvent;
/**
* @deprecated Deep imports into the Application module are depreacted and will be removed in a future release.
* Use the `Application` class imported from "@nativescript/core" instead:
* ```ts
* import { Application } from "@nativescript/core";
* Application.orientationChangedEvent
* ```
*/
export const orientationChangedEvent = Application.orientationChangedEvent;
/**
* @deprecated Deep imports into the Application module are depreacted and will be removed in a future release.
* Use the `Application` class imported from "@nativescript/core" instead:
* ```ts
* import { Application } from "@nativescript/core";
* Application.suspendEvent
* ```
*/
export const suspendEvent = Application.suspendEvent;
/**
* @deprecated Deep imports into the Application module are depreacted and will be removed in a future release.
* Use the `Application` class imported from "@nativescript/core" instead:
* ```ts
* import { Application } from "@nativescript/core";
* Application.systemAppearanceChangedEvent
* ```
*/
export const systemAppearanceChangedEvent = Application.systemAppearanceChangedEvent;
/**
* @deprecated Deep imports into the Application module are depreacted and will be removed in a future release.
* Use the `Application` class imported from "@nativescript/core" instead:
* ```ts
* import { Application } from "@nativescript/core";
* Application.backgroundEvent
* ```
*/
export const backgroundEvent = Application.backgroundEvent;
/**
* @deprecated Deep imports into the Application module are depreacted and will be removed in a future release.
* Use the `Application` class imported from "@nativescript/core" instead:
* ```ts
* import { Application } from "@nativescript/core";
* Application.foregroundEvent
* ```
*/
export const foregroundEvent = Application.foregroundEvent;
/**
* @deprecated Deep imports into the Application module are depreacted and will be removed in a future release.
* Use the `Application` class imported from "@nativescript/core" instead:
* ```ts
* import { Application } from "@nativescript/core";
* Application.inBackground
* ```
*/
export const inBackground = Application.inBackground;
/**
* @deprecated Deep imports into the Application module are depreacted and will be removed in a future release.
* Use the `Application` class imported from "@nativescript/core" instead:
* ```ts
* import { Application } from "@nativescript/core";
* Application.suspended
* ```
*/
export const suspended = Application.suspended;
/**
* @deprecated Deep imports into the Application module are depreacted and will be removed in a future release.
* Use the `Application` class imported from "@nativescript/core" instead:
* ```ts
* import { Application } from "@nativescript/core";
* Application.android
* ```
*/
export const android = Application.android;
/**
* @deprecated Deep imports into the Application module are depreacted and will be removed in a future release.
* Use the `Application` class imported from "@nativescript/core" instead:
* ```ts
* import { Application } from "@nativescript/core";
* Application.ios
* ```
*/
export const ios = Application.ios;

View File

@@ -0,0 +1,532 @@
import { initAccessibilityCssHelper } from '../accessibility/accessibility-css-helper';
import { initAccessibilityFontScale } from '../accessibility/font-scale';
import { profile } from '../profiling';
import { View } from '../ui';
import { AndroidActivityCallbacks, NavigationEntry } from '../ui/frame/frame-common';
import type {
AndroidActivityBundleEventData,
AndroidActivityEventData,
ApplicationEventData,
AndroidApplication as IAndroidApplication,
} from '.';
import { ApplicationCommon } from './application-common';
declare namespace com {
namespace tns {
class NativeScriptApplication extends android.app.Application {
static getInstance(): NativeScriptApplication;
}
}
}
@NativeClass
class BroadcastReceiver extends android.content.BroadcastReceiver {
private _onReceiveCallback: (
context: android.content.Context,
intent: android.content.Intent
) => void;
constructor(
onReceiveCallback: (
context: android.content.Context,
intent: android.content.Intent
) => void
) {
super();
this._onReceiveCallback = onReceiveCallback;
return global.__native(this);
}
public onReceive(context: android.content.Context, intent: android.content.Intent) {
if (this._onReceiveCallback) {
this._onReceiveCallback(context, intent);
}
}
}
@NativeClass
@JavaProxy('org.nativescript.NativeScriptLifecycleCallbacks')
class NativeScriptLifecycleCallbacks extends android.app.Application
.ActivityLifecycleCallbacks {
private activitiesCount: number = 0;
private nativescriptActivity: androidx.appcompat.app.AppCompatActivity;
@profile
public onActivityCreated(
activity: androidx.appcompat.app.AppCompatActivity,
savedInstanceState: android.os.Bundle
): void {
// console.log('NativeScriptLifecycleCallbacks onActivityCreated');
this.setThemeOnLaunch(activity);
if (!Application.android.startActivity) {
Application.android.setStartActivity(activity);
}
if (!this.nativescriptActivity && 'isNativeScriptActivity' in activity) {
this.nativescriptActivity = activity;
}
this.notifyActivityCreated(activity, savedInstanceState);
if (Application.hasListeners(Application.displayedEvent)) {
this.subscribeForGlobalLayout(activity);
}
}
@profile
public onActivityDestroyed(activity: androidx.appcompat.app.AppCompatActivity): void {
// console.log('NativeScriptLifecycleCallbacks onActivityDestroyed');
if (activity === Application.android.foregroundActivity) {
Application.android.setForegroundActivity(undefined);
}
if (activity === this.nativescriptActivity) {
this.nativescriptActivity = undefined;
}
if (activity === Application.android.startActivity) {
Application.android.setStartActivity(undefined);
// Fallback for start activity when it is destroyed but we have a known nativescript activity
if (this.nativescriptActivity) {
Application.android.setStartActivity(this.nativescriptActivity);
}
}
Application.android.notify({
eventName: Application.android.activityDestroyedEvent,
object: Application.android,
activity,
} as AndroidActivityEventData);
// TODO: This is a temporary workaround to force the V8's Garbage Collector, which will force the related Java Object to be collected.
// gc();
}
@profile
public onActivityPaused(activity: androidx.appcompat.app.AppCompatActivity): void {
// console.log('NativeScriptLifecycleCallbacks onActivityPaused');
if ('isNativeScriptActivity' in activity) {
Application.android.notify({
eventName: Application.suspendEvent,
object: Application.android,
android: activity,
} as ApplicationEventData);
}
Application.android.notify({
eventName: Application.android.activityPausedEvent,
object: Application.android,
activity,
} as AndroidActivityEventData);
}
@profile
public onActivityResumed(activity: androidx.appcompat.app.AppCompatActivity): void {
// console.log('NativeScriptLifecycleCallbacks onActivityResumed');
Application.android.setForegroundActivity(activity);
Application.android.notify({
eventName: Application.android.activityResumedEvent,
object: Application.android,
activity,
} as AndroidActivityEventData);
}
@profile
public onActivitySaveInstanceState(
activity: androidx.appcompat.app.AppCompatActivity,
bundle: android.os.Bundle
): void {
// console.log('NativeScriptLifecycleCallbacks onActivitySaveInstanceState');
Application.android.notify({
eventName: Application.android.saveActivityStateEvent,
object: Application.android,
activity,
bundle,
} as AndroidActivityBundleEventData);
}
@profile
public onActivityStarted(activity: androidx.appcompat.app.AppCompatActivity): void {
// console.log('NativeScriptLifecycleCallbacks onActivityStarted');
this.activitiesCount++;
if (this.activitiesCount === 1) {
Application.android.notify({
eventName: Application.foregroundEvent,
object: Application.android,
android: activity,
} as ApplicationEventData);
}
Application.android.notify({
eventName: Application.android.activityStartedEvent,
object: Application.android,
activity,
} as AndroidActivityEventData);
}
@profile
public onActivityStopped(activity: androidx.appcompat.app.AppCompatActivity): void {
// console.log('NativeScriptLifecycleCallbacks onActivityStopped');
this.activitiesCount--;
if (this.activitiesCount === 0) {
Application.android.notify({
eventName: Application.backgroundEvent,
object: Application.android,
android: activity,
} as ApplicationEventData);
}
Application.android.notify({
eventName: Application.android.activityStoppedEvent,
object: Application.android,
activity,
} as AndroidActivityEventData);
}
@profile
setThemeOnLaunch(activity: androidx.appcompat.app.AppCompatActivity) {
// Set app theme after launch screen was used during startup
const activityInfo = activity
.getPackageManager()
.getActivityInfo(
activity.getComponentName(),
android.content.pm.PackageManager.GET_META_DATA
);
if (activityInfo.metaData) {
const setThemeOnLaunch = activityInfo.metaData.getInt('SET_THEME_ON_LAUNCH', -1);
if (setThemeOnLaunch !== -1) {
activity.setTheme(setThemeOnLaunch);
}
}
}
@profile
notifyActivityCreated(
activity: androidx.appcompat.app.AppCompatActivity,
bundle: android.os.Bundle
) {
Application.android.notify({
eventName: Application.android.activityCreatedEvent,
object: Application.android,
activity,
bundle,
} as AndroidActivityBundleEventData);
}
@profile
subscribeForGlobalLayout(activity: androidx.appcompat.app.AppCompatActivity) {
const rootView = activity.getWindow().getDecorView().getRootView();
// store the listener not to trigger GC collection before collecting the method
global.onGlobalLayoutListener =
new android.view.ViewTreeObserver.OnGlobalLayoutListener({
onGlobalLayout() {
Application.android.notify({
eventName: Application.android.displayedEvent,
object: Application,
android: Application.android,
activity,
});
const viewTreeObserver = rootView.getViewTreeObserver();
viewTreeObserver.removeOnGlobalLayoutListener(global.onGlobalLayoutListener);
},
});
rootView
.getViewTreeObserver()
.addOnGlobalLayoutListener(global.onGlobalLayoutListener);
}
}
@NativeClass
@JavaProxy('org.nativescript.NativeScriptComponentCallbacks')
class NativeScriptComponentCallbacks extends android.content.ComponentCallbacks2 {
@profile
public onLowMemory(): void {
gc();
java.lang.System.gc();
Application.notify({
eventName: Application.lowMemoryEvent,
object: Application,
android: this,
} as ApplicationEventData);
}
@profile
public onTrimMemory(level: number): void {
// TODO: This is skipped for now, test carefully for OutOfMemory exceptions
}
@profile
public onConfigurationChanged(
newConfiguration: android.content.res.Configuration
): void {
Application.android.onConfigurationChanged(newConfiguration);
}
}
export class AndroidApplication extends ApplicationCommon implements IAndroidApplication {
readonly activityCreatedEvent = 'activityCreated';
readonly activityDestroyedEvent = 'activityDestroyed';
readonly activityStartedEvent = 'activityStarted';
readonly activityPausedEvent = 'activityPaused';
readonly activityResumedEvent = 'activityResumed';
readonly activityStoppedEvent = 'activityStopped';
readonly saveActivityStateEvent = 'saveActivityState';
readonly activityResultEvent = 'activityResult';
readonly activityBackPressedEvent = 'activityBackPressed';
readonly activityNewIntentEvent = 'activityNewIntent';
readonly activityRequestPermissionsEvent = 'activityRequestPermissions';
private _nativeApp: android.app.Application;
// we are using these property to store the callbacks to avoid early GC collection which would trigger MarkReachableObjects
private lifecycleCallbacks: NativeScriptLifecycleCallbacks;
private componentCallbacks: NativeScriptComponentCallbacks;
constructor() {
super();
}
init(nativeApp: android.app.Application): void {
if (this.nativeApp === nativeApp) {
return;
}
if (this.nativeApp) {
throw new Error('Application.android already initialized.');
}
this._nativeApp = nativeApp;
// we store those callbacks and add a function for clearing them later so that the objects will be eligable for GC
this.lifecycleCallbacks = new NativeScriptLifecycleCallbacks();
this.nativeApp.registerActivityLifecycleCallbacks(this.lifecycleCallbacks);
this.componentCallbacks = new NativeScriptComponentCallbacks();
this.nativeApp.registerComponentCallbacks(this.componentCallbacks);
this._registerPendingReceivers();
}
private _registeredReceivers = {};
private _pendingReceiverRegistrations = new Array<
(context: android.content.Context) => void
>();
private _registerPendingReceivers() {
this._pendingReceiverRegistrations.forEach((func) => func(this.context));
this._pendingReceiverRegistrations.length = 0;
}
onConfigurationChanged(configuration: android.content.res.Configuration): void {
this.setOrientation(this.getOrientationValue(configuration));
this.setSystemAppearance(this.getSystemAppearanceValue(configuration));
}
getNativeApplication() {
let nativeApp = this.nativeApp;
if (nativeApp) {
return nativeApp;
}
// Try getting it from module - check whether application.android.init has been explicitly called
// check whether the com.tns.NativeScriptApplication type exists
if (com.tns.NativeScriptApplication) {
nativeApp = com.tns.NativeScriptApplication.getInstance();
}
// the getInstance might return null if com.tns.NativeScriptApplication exists but is not the starting app type
if (!nativeApp) {
// TODO: Should we handle the case when a custom application type is provided and the user has not explicitly initialized the application module?
const clazz = java.lang.Class.forName(
'androidx.appcompat.app.AppCompatActivityThread'
);
if (clazz) {
const method = clazz.getMethod('currentApplication', null);
if (method) {
nativeApp = method.invoke(null, null);
}
}
}
// we cannot work without having the app instance
if (!nativeApp) {
throw new Error(
"Failed to retrieve native Android Application object. If you have a custom android.app.Application type implemented make sure that you've called the 'Application.android.init' method."
);
}
return nativeApp;
}
get nativeApp(): android.app.Application {
return this._nativeApp;
}
run(entry?: string | NavigationEntry): void {
if (this.started) {
throw new Error('Application is already started.');
}
this.started = true;
this.mainEntry = typeof entry === 'string' ? { moduleName: entry } : entry;
if (!this.nativeApp) {
const nativeApp = this.getNativeApplication();
this.init(nativeApp);
}
initAccessibilityCssHelper();
initAccessibilityFontScale();
}
/**
* todo: check if true, cause there's no such thing in Utils.android
* @deprecated Use Utils.android.getPackageName() instead.
*/
get packageName() {
return this.nativeApp.getPackageName();
}
private _startActivity: androidx.appcompat.app.AppCompatActivity;
private _foregroundActivity: androidx.appcompat.app.AppCompatActivity;
get startActivity() {
return this._startActivity;
}
get foregroundActivity() {
return this._foregroundActivity;
}
setStartActivity(value: androidx.appcompat.app.AppCompatActivity) {
this._startActivity = value;
}
setForegroundActivity(value: androidx.appcompat.app.AppCompatActivity) {
this._foregroundActivity = value;
}
get context() {
return this.nativeApp.getApplicationContext();
}
public registerBroadcastReceiver(
intentFilter: string,
onReceiveCallback: (
context: android.content.Context,
intent: android.content.Intent
) => void
): void {
const registerFunc = (context: android.content.Context) => {
const receiver: android.content.BroadcastReceiver = new BroadcastReceiver(
onReceiveCallback
);
context.registerReceiver(receiver, new android.content.IntentFilter(intentFilter));
this._registeredReceivers[intentFilter] = receiver;
};
if (this.context) {
registerFunc(this.context);
} else {
this._pendingReceiverRegistrations.push(registerFunc);
}
}
public unregisterBroadcastReceiver(intentFilter: string): void {
const receiver = this._registeredReceivers[intentFilter];
if (receiver) {
this.context.unregisterReceiver(receiver);
this._registeredReceivers[intentFilter] = undefined;
delete this._registeredReceivers[intentFilter];
}
}
public getRegisteredBroadcastReceiver(
intentFilter: string
): android.content.BroadcastReceiver | undefined {
return this._registeredReceivers[intentFilter];
}
getRootView(): View {
const activity = this.foregroundActivity || this.startActivity;
if (!activity) {
return undefined;
}
const callbacks: AndroidActivityCallbacks = activity['_callbacks'];
return callbacks ? callbacks.getRootView() : undefined;
}
resetRootView(entry?: NavigationEntry | string): void {
super.resetRootView(entry);
const activity = this.foregroundActivity || this.startActivity;
if (!activity) {
throw new Error('Cannot find android activity.');
}
// this.mainEntry = typeof entry === 'string' ? { moduleName: entry } : entry;
const callbacks: AndroidActivityCallbacks = activity['_callbacks'];
if (!callbacks) {
throw new Error('Cannot find android activity callbacks.');
}
callbacks.resetActivityContent(activity);
}
getSystemAppearance(): 'light' | 'dark' {
const resources = this.context.getResources();
const configuration = resources.getConfiguration();
return this.getSystemAppearanceValue(configuration);
}
// https://developer.android.com/guide/topics/ui/look-and-feel/darktheme#configuration_changes
private getSystemAppearanceValue(
configuration: android.content.res.Configuration
): 'dark' | 'light' {
const systemAppearance =
configuration.uiMode & android.content.res.Configuration.UI_MODE_NIGHT_MASK;
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';
}
}
getOrientation() {
const resources = this.context.getResources();
const configuration = <android.content.res.Configuration>resources.getConfiguration();
return this.getOrientationValue(configuration);
}
private getOrientationValue(
configuration: android.content.res.Configuration
): 'portrait' | 'landscape' | 'unknown' {
const orientation = configuration.orientation;
switch (orientation) {
case android.content.res.Configuration.ORIENTATION_LANDSCAPE:
return 'landscape';
case android.content.res.Configuration.ORIENTATION_PORTRAIT:
return 'portrait';
default:
return 'unknown';
}
}
get android() {
// ensures Application.android is defined when running on iOS
return this;
}
}
export * from './application-common';
export const Application = new AndroidApplication();
export const iOSApplication = undefined;

View File

@@ -0,0 +1,57 @@
import { ApplicationCommon } from './application-common';
export * from './application-common';
export * from './application-interfaces';
export const Application: ApplicationCommon;
export interface AndroidApplication extends ApplicationCommon {
readonly activityCreatedEvent;
readonly activityDestroyedEvent;
readonly activityStartedEvent;
readonly activityPausedEvent;
readonly activityResumedEvent;
readonly activityStoppedEvent;
readonly saveActivityStateEvent;
readonly activityResultEvent;
readonly activityBackPressedEvent;
readonly activityNewIntentEvent;
readonly activityRequestPermissionsEvent;
getNativeApplication(): android.app.Application;
init(nativeApp: android.app.Application): void;
get nativeApp(): android.app.Application;
get packageName(): string;
get startActivity(): androidx.appcompat.app.AppCompatActivity;
get foregroundActivity(): androidx.appcompat.app.AppCompatActivity;
get context(): android.content.Context;
registerBroadcastReceiver(
intentFilter: string,
onReceiveCallback: (
context: android.content.Context,
intent: android.content.Intent
) => void
): void;
unregisterBroadcastReceiver(intentFilter: string): void;
}
export interface iOSApplication extends ApplicationCommon {
get rootController(): UIViewController;
get nativeApp(): UIApplication;
get window(): UIWindow;
get delegate(): UIApplicationDelegate;
set delegate(value: UIApplicationDelegate | unknown);
getNativeApplication(): UIApplication;
addNotificationObserver(
notificationName: string,
onReceiveCallback: (notification: NSNotification) => void
): NotificationObserver;
removeNotificationObserver(observer: any, notificationName: string);
}

View File

@@ -0,0 +1,554 @@
import { initAccessibilityCssHelper } from '../accessibility/accessibility-css-helper';
import { initAccessibilityFontScale } from '../accessibility/font-scale';
import { profile } from '../profiling';
import { View } from '../ui';
import { Builder } from '../ui/builder';
import { IOSHelper } from '../ui/core/view/view-helper';
import { NavigationEntry } from '../ui/frame/frame-interfaces';
import * as Utils from '../utils';
import type { iOSApplication as IiOSApplication } from './';
import { ApplicationCommon } from './application-common';
import {
ApplicationEventData,
LaunchEventData,
LoadAppCSSEventData,
} from './application-interfaces';
@NativeClass
class CADisplayLinkTarget extends NSObject {
private _owner: WeakRef<iOSApplication>;
static initWithOwner(owner: WeakRef<iOSApplication>): CADisplayLinkTarget {
const target = <CADisplayLinkTarget>CADisplayLinkTarget.new();
target._owner = owner;
return target;
}
onDisplayed(link: CADisplayLink) {
link.invalidate();
const owner = this._owner.deref();
if (!owner) {
return;
}
owner.displayedOnce = true;
owner.notify(<ApplicationEventData>{
eventName: owner.displayedEvent,
object: owner,
ios: owner.ios,
});
owner.displayedLinkTarget = null;
owner.displayedLink = null;
}
public static ObjCExposedMethods = {
onDisplayed: { returns: interop.types.void, params: [CADisplayLink] },
};
}
@NativeClass
class NotificationObserver extends NSObject {
private _onReceiveCallback: (notification: NSNotification) => void;
public static initWithCallback(
onReceiveCallback: (notification: NSNotification) => void
): NotificationObserver {
const observer = <NotificationObserver>super.new();
observer._onReceiveCallback = onReceiveCallback;
return observer;
}
public onReceive(notification: NSNotification): void {
this._onReceiveCallback(notification);
}
public static ObjCExposedMethods = {
onReceive: { returns: interop.types.void, params: [NSNotification] },
};
}
@NativeClass
class Responder extends UIResponder implements UIApplicationDelegate {
get window(): UIWindow {
return Application.ios.window;
}
set window(value: UIWindow) {
// NOOP
}
static ObjCProtocols = [UIApplicationDelegate];
}
export class iOSApplication extends ApplicationCommon implements IiOSApplication {
private _delegate: UIApplicationDelegate;
private _window: UIWindow;
private _notificationObservers: NotificationObserver[] = [];
private _rootView: View;
displayedOnce = false;
displayedLinkTarget: CADisplayLinkTarget;
displayedLink: CADisplayLink;
constructor() {
super();
this.addNotificationObserver(
UIApplicationDidFinishLaunchingNotification,
this.didFinishLaunchingWithOptions.bind(this)
);
this.addNotificationObserver(
UIApplicationDidBecomeActiveNotification,
this.didBecomeActive.bind(this)
);
this.addNotificationObserver(
UIApplicationDidEnterBackgroundNotification,
this.didEnterBackground.bind(this)
);
this.addNotificationObserver(
UIApplicationWillTerminateNotification,
this.willTerminate.bind(this)
);
this.addNotificationObserver(
UIApplicationDidReceiveMemoryWarningNotification,
this.didReceiveMemoryWarning.bind(this)
);
this.addNotificationObserver(
UIApplicationDidChangeStatusBarOrientationNotification,
this.didChangeStatusBarOrientation.bind(this)
);
}
getRootView(): View {
return this._rootView;
}
resetRootView(view?: View) {
super.resetRootView(view);
this.setWindowContent();
}
run(entry?: string | NavigationEntry): void {
console.log('run in iOSApplication', entry);
this.mainEntry = typeof entry === 'string' ? { moduleName: entry } : entry;
this.started = true;
if (this.nativeApp) {
this.runAsEmbeddedApp();
} else {
this.runAsMainApp();
}
initAccessibilityCssHelper();
initAccessibilityFontScale();
}
private runAsMainApp() {
console.log('runAsMainApp', this.delegate);
UIApplicationMain(
0,
null,
null,
this.delegate
? NSStringFromClass(this.delegate as any)
: NSStringFromClass(Responder)
);
}
private runAsEmbeddedApp() {
// TODO: this rootView should be held alive until rootController dismissViewController is called.
const rootView = this.createRootView();
if (!rootView) {
return;
}
// Attach to the existing iOS app
const window =
this.nativeApp.keyWindow ||
(this.nativeApp.windows.count > 0 && this.nativeApp.windows[0]);
if (!window) {
return;
}
const rootController = window.rootViewController;
if (!rootController) {
return;
}
const controller = this.getViewController(rootView);
const embedderDelegate = NativeScriptEmbedder.sharedInstance().delegate;
rootView._setupAsRootView({});
rootView.on(IOSHelper.traitCollectionColorAppearanceChangedEvent, () => {
const userInterfaceStyle = controller.traitCollection.userInterfaceStyle;
const newSystemAppearance = this.getSystemAppearanceValue(userInterfaceStyle);
this.setSystemAppearance(newSystemAppearance);
});
if (embedderDelegate) {
this.setViewControllerView(rootView);
embedderDelegate.presentNativeScriptApp(controller);
} else {
const visibleVC = Utils.ios.getVisibleViewController(rootController);
visibleVC.presentViewControllerAnimatedCompletion(controller, true, null);
}
// this.setRootViewsSystemAppearanceCssClass(rootView);
this.notifyAppStarted();
}
private createRootView(v?: View) {
let rootView = v;
if (!rootView) {
console.log('createRootView mainEntry', this.mainEntry);
// try to navigate to the mainEntry (if specified)
if (!this.mainEntry) {
throw new Error(
'Main entry is missing. App cannot be started. Verify app bootstrap.'
);
} else {
// console.log('createRootView mainEntry:', mainEntry);
rootView = Builder.createViewFromEntry(this.mainEntry);
}
}
// console.log('createRootView rootView:', rootView);
// setRootViewsCssClasses(rootView);
return rootView;
}
private getViewController(rootView: View): UIViewController {
let viewController: UIViewController = rootView.viewController || rootView.ios;
if (!(viewController instanceof UIViewController)) {
// We set UILayoutViewController dynamically to the root view if it doesn't have a view controller
// At the moment the root view doesn't have its native view created. We set it in the setViewControllerView func
viewController = IOSHelper.UILayoutViewController.initWithOwner(
new WeakRef(rootView)
) as UIViewController;
rootView.viewController = viewController;
}
return viewController;
}
private setViewControllerView(view: View): void {
const viewController: UIViewController = view.viewController || view.ios;
const nativeView = view.ios || view.nativeViewProtected;
if (!nativeView || !viewController) {
throw new Error('Root should be either UIViewController or UIView');
}
if (viewController instanceof IOSHelper.UILayoutViewController) {
viewController.view.addSubview(nativeView);
}
}
setMaxRefreshRate(options?: { min?: number; max?: number; preferred?: number }): void {
const adjustRefreshRate = () => {
if (!this.displayedLink) {
return;
}
const minFrameRateDisabled = NSBundle.mainBundle.objectForInfoDictionaryKey(
'CADisableMinimumFrameDurationOnPhone'
);
if (minFrameRateDisabled) {
let max = 120;
const deviceMaxFrames = UIScreen.mainScreen?.maximumFramesPerSecond;
if (options?.max) {
if (deviceMaxFrames) {
// iOS 10.3
max = options.max <= deviceMaxFrames ? options.max : deviceMaxFrames;
} else if (this.displayedLink.preferredFramesPerSecond) {
// iOS 10.0
max =
options.max <= this.displayedLink.preferredFramesPerSecond
? options.max
: this.displayedLink.preferredFramesPerSecond;
}
}
if (Utils.ios.MajorVersion >= 15) {
const min = options?.min || max / 2;
const preferred = options?.preferred || max;
this.displayedLink.preferredFrameRateRange = CAFrameRateRangeMake(
min,
max,
preferred
);
} else {
this.displayedLink.preferredFramesPerSecond = max;
}
}
};
if (this.displayedOnce) {
adjustRefreshRate();
return;
}
this.displayedLinkTarget = CADisplayLinkTarget.initWithOwner(new WeakRef(this));
this.displayedLink = CADisplayLink.displayLinkWithTargetSelector(
this.displayedLinkTarget,
'onDisplayed'
);
adjustRefreshRate();
this.displayedLink.addToRunLoopForMode(NSRunLoop.mainRunLoop, NSDefaultRunLoopMode);
this.displayedLink.addToRunLoopForMode(NSRunLoop.mainRunLoop, UITrackingRunLoopMode);
}
get rootController() {
return this.window.rootViewController;
}
get nativeApp() {
return UIApplication.sharedApplication;
}
get window(): UIWindow {
if (NativeScriptEmbedder.sharedInstance().delegate && !this._window) {
this._window = UIApplication.sharedApplication.keyWindow;
}
return this._window;
}
get delegate(): UIApplicationDelegate {
return this._delegate;
}
set delegate(value: UIApplicationDelegate | unknown) {
if (this._delegate !== value) {
this._delegate = value as UIApplicationDelegate;
}
}
getNativeApplication() {
return this.nativeApp;
}
addNotificationObserver(
notificationName: string,
onReceiveCallback: (notification: NSNotification) => void
) {
const observer = NotificationObserver.initWithCallback(onReceiveCallback);
NSNotificationCenter.defaultCenter.addObserverSelectorNameObject(
observer,
'onReceive',
notificationName,
null
);
this._notificationObservers.push(observer);
return observer;
}
removeNotificationObserver(observer: any, notificationName: string) {
const index = this._notificationObservers.indexOf(observer);
if (index >= 0) {
this._notificationObservers.splice(index, 1);
NSNotificationCenter.defaultCenter.removeObserverNameObject(
observer,
notificationName,
null
);
}
}
protected getSystemAppearance(): 'light' | 'dark' {
// userInterfaceStyle is available on UITraitCollection since iOS 12.
if (Utils.ios.MajorVersion <= 11) {
return undefined;
}
const userInterfaceStyle = this.rootController.traitCollection.userInterfaceStyle;
return this.getSystemAppearanceValue(userInterfaceStyle);
}
private getSystemAppearanceValue(userInterfaceStyle: number): 'dark' | 'light' {
switch (userInterfaceStyle) {
case UIUserInterfaceStyle.Dark:
return 'dark';
case UIUserInterfaceStyle.Light:
case UIUserInterfaceStyle.Unspecified:
return 'light';
}
}
protected getOrientation() {
const statusBarOrientation = UIApplication.sharedApplication.statusBarOrientation;
return this.getOrientationValue(statusBarOrientation);
}
private getOrientationValue(orientation: number): 'portrait' | 'landscape' | 'unknown' {
switch (orientation) {
case UIInterfaceOrientation.LandscapeRight:
case UIInterfaceOrientation.LandscapeLeft:
return 'landscape';
case UIInterfaceOrientation.PortraitUpsideDown:
case UIInterfaceOrientation.Portrait:
return 'portrait';
case UIInterfaceOrientation.Unknown:
return 'unknown';
}
}
private notifyAppStarted(notification?: NSNotification) {
const args: LaunchEventData = {
eventName: this.launchEvent,
object: this,
ios:
notification?.userInfo?.objectForKey(
'UIApplicationLaunchOptionsLocalNotificationKey'
) || null,
};
this.notify(args);
this.notify(<LoadAppCSSEventData>{
eventName: 'loadAppCss',
object: this,
cssFile: this.getCssFileName(),
});
if (this._window) {
if (args.root !== null && !NativeScriptEmbedder.sharedInstance().delegate) {
this.setWindowContent(args.root);
}
} else {
this._window = UIApplication.sharedApplication.keyWindow;
}
}
private setWindowContent(view?: View): void {
if (this._rootView) {
// if we already have a root view, we reset it.
this._rootView._onRootViewReset();
}
const rootView = this.createRootView(view);
const controller = this.getViewController(rootView);
this._rootView = rootView;
// setup view as styleScopeHost
rootView._setupAsRootView({});
this.setViewControllerView(rootView);
const haveController = this._window.rootViewController !== null;
this._window.rootViewController = controller;
this.setRootViewsSystemAppearanceCssClass(rootView);
if (!haveController) {
this._window.makeKeyAndVisible();
}
rootView.on(IOSHelper.traitCollectionColorAppearanceChangedEvent, () => {
const userInterfaceStyle = controller.traitCollection.userInterfaceStyle;
const newSystemAppearance = this.getSystemAppearanceValue(userInterfaceStyle);
this.setSystemAppearance(newSystemAppearance);
});
}
// Observers
@profile
private didFinishLaunchingWithOptions(notification: NSNotification) {
this.setMaxRefreshRate();
this._window = UIWindow.alloc().initWithFrame(UIScreen.mainScreen.bounds);
// TODO: Expose Window module so that it can we styled from XML & CSS
this._window.backgroundColor =
Utils.ios.MajorVersion <= 12 || !UIColor.systemBackgroundColor
? UIColor.whiteColor
: UIColor.systemBackgroundColor;
this.notifyAppStarted(notification);
}
@profile
private didBecomeActive(notification: NSNotification) {
// const ios = UIApplication.sharedApplication;
// const object = this;
// setInBackground(false);
// setSuspended(false);
this.notify(<ApplicationEventData>{
eventName: this.resumeEvent,
object: this,
ios: this.ios,
});
this.notify(<ApplicationEventData>{
eventName: this.foregroundEvent,
object: this,
ios: this.ios,
});
// const rootView = this._rootView;
// if (rootView && !rootView.isLoaded) {
// rootView.callLoaded();
// }
}
private didEnterBackground(notification: NSNotification) {
// const ios = UIApplication.sharedApplication;
// const object = this;
// setInBackground(true);
// setSuspended(true);
this.notify(<ApplicationEventData>{
eventName: this.suspendEvent,
object: this,
ios: this.ios,
});
this.notify(<ApplicationEventData>{
eventName: this.backgroundEvent,
object: this,
ios: this.ios,
});
// const rootView = this._rootView;
// if (rootView && rootView.isLoaded) {
// rootView.callUnloaded();
// }
}
private willTerminate(notification: NSNotification) {
this.notify(<ApplicationEventData>{
eventName: this.exitEvent,
object: this,
ios: this.ios,
});
// const rootView = this._rootView;
// if (rootView && rootView.isLoaded) {
// rootView.callUnloaded();
// }
}
private didReceiveMemoryWarning(notification: NSNotification) {
this.notify(<ApplicationEventData>{
eventName: this.lowMemoryEvent,
object: this,
ios: this.ios,
});
}
private didChangeStatusBarOrientation(notification: NSNotification) {
const statusBarOrientation = UIApplication.sharedApplication.statusBarOrientation;
const newOrientation = this.getOrientationValue(statusBarOrientation);
this.setOrientation(newOrientation);
}
get ios() {
// ensures Application.ios is defined when running on iOS
return this;
}
}
export * from './application-common';
export const Application = new iOSApplication();
export const AndroidApplication = undefined;

View File

@@ -1,527 +1,2 @@
import { initAccessibilityCssHelper } from '../accessibility/accessibility-css-helper';
import { initAccessibilityFontScale } from '../accessibility/font-scale';
import { profile } from '../profiling';
import { View } from '../ui';
import { AndroidActivityCallbacks, NavigationEntry } from '../ui/frame/frame-common';
import type {
AndroidActivityBundleEventData,
AndroidActivityEventData,
ApplicationEventData,
AndroidApplication as IAndroidApplication,
} from './';
import { ApplicationCommon } from './application-common';
declare namespace com {
namespace tns {
class NativeScriptApplication extends android.app.Application {
static getInstance(): NativeScriptApplication;
}
}
}
@NativeClass
class BroadcastReceiver extends android.content.BroadcastReceiver {
private _onReceiveCallback: (
context: android.content.Context,
intent: android.content.Intent
) => void;
constructor(
onReceiveCallback: (
context: android.content.Context,
intent: android.content.Intent
) => void
) {
super();
this._onReceiveCallback = onReceiveCallback;
return global.__native(this);
}
public onReceive(context: android.content.Context, intent: android.content.Intent) {
if (this._onReceiveCallback) {
this._onReceiveCallback(context, intent);
}
}
}
@NativeClass
@JavaProxy('org.nativescript.NativeScriptLifecycleCallbacks')
class NativeScriptLifecycleCallbacks extends android.app.Application
.ActivityLifecycleCallbacks {
private activitiesCount: number = 0;
private nativescriptActivity: android.app.Activity;
@profile
public onActivityCreated(
activity: android.app.Activity,
savedInstanceState: android.os.Bundle
): void {
// console.log('NativeScriptLifecycleCallbacks onActivityCreated');
this.setThemeOnLaunch(activity);
if (!Application.android.startActivity) {
Application.android.setStartActivity(activity);
}
if (!this.nativescriptActivity && 'isNativeScriptActivity' in activity) {
this.nativescriptActivity = activity;
}
this.notifyActivityCreated(activity, savedInstanceState);
if (Application.hasListeners(Application.displayedEvent)) {
this.subscribeForGlobalLayout(activity);
}
}
@profile
public onActivityDestroyed(activity: android.app.Activity): void {
// console.log('NativeScriptLifecycleCallbacks onActivityDestroyed');
if (activity === Application.android.foregroundActivity) {
Application.android.setForegroundActivity(undefined);
}
if (activity === this.nativescriptActivity) {
this.nativescriptActivity = undefined;
}
if (activity === Application.android.startActivity) {
Application.android.setStartActivity(undefined);
// Fallback for start activity when it is destroyed but we have a known nativescript activity
if (this.nativescriptActivity) {
Application.android.setStartActivity(this.nativescriptActivity);
}
}
Application.android.notify({
eventName: Application.android.activityDestroyedEvent,
object: Application.android,
activity,
} as AndroidActivityEventData);
// TODO: This is a temporary workaround to force the V8's Garbage Collector, which will force the related Java Object to be collected.
// gc();
}
@profile
public onActivityPaused(activity: android.app.Activity): void {
// console.log('NativeScriptLifecycleCallbacks onActivityPaused');
if ('isNativeScriptActivity' in activity) {
Application.android.notify({
eventName: Application.suspendEvent,
object: Application.android,
android: activity,
} as ApplicationEventData);
}
Application.android.notify({
eventName: Application.android.activityPausedEvent,
object: Application.android,
activity,
} as AndroidActivityEventData);
}
@profile
public onActivityResumed(activity: android.app.Activity): void {
// console.log('NativeScriptLifecycleCallbacks onActivityResumed');
Application.android.setForegroundActivity(activity);
Application.android.notify({
eventName: Application.android.activityResumedEvent,
object: Application.android,
activity,
} as AndroidActivityEventData);
}
@profile
public onActivitySaveInstanceState(
activity: android.app.Activity,
bundle: android.os.Bundle
): void {
// console.log('NativeScriptLifecycleCallbacks onActivitySaveInstanceState');
Application.android.notify({
eventName: Application.android.saveActivityStateEvent,
object: Application.android,
activity,
bundle,
} as AndroidActivityBundleEventData);
}
@profile
public onActivityStarted(activity: android.app.Activity): void {
// console.log('NativeScriptLifecycleCallbacks onActivityStarted');
this.activitiesCount++;
if (this.activitiesCount === 1) {
Application.android.notify({
eventName: Application.foregroundEvent,
object: Application.android,
android: activity,
} as ApplicationEventData);
}
Application.android.notify({
eventName: Application.android.activityStartedEvent,
object: Application.android,
activity,
} as AndroidActivityEventData);
}
@profile
public onActivityStopped(activity: android.app.Activity): void {
// console.log('NativeScriptLifecycleCallbacks onActivityStopped');
this.activitiesCount--;
if (this.activitiesCount === 0) {
Application.android.notify({
eventName: Application.backgroundEvent,
object: Application.android,
android: activity,
} as ApplicationEventData);
}
Application.android.notify({
eventName: Application.android.activityStoppedEvent,
object: Application.android,
activity,
} as AndroidActivityEventData);
}
@profile
setThemeOnLaunch(activity: android.app.Activity) {
// Set app theme after launch screen was used during startup
const activityInfo = activity
.getPackageManager()
.getActivityInfo(
activity.getComponentName(),
android.content.pm.PackageManager.GET_META_DATA
);
if (activityInfo.metaData) {
const setThemeOnLaunch = activityInfo.metaData.getInt('SET_THEME_ON_LAUNCH', -1);
if (setThemeOnLaunch !== -1) {
activity.setTheme(setThemeOnLaunch);
}
}
}
@profile
notifyActivityCreated(activity: android.app.Activity, bundle: android.os.Bundle) {
Application.android.notify({
eventName: Application.android.activityCreatedEvent,
object: Application.android,
activity,
bundle,
} as AndroidActivityBundleEventData);
}
@profile
subscribeForGlobalLayout(activity: android.app.Activity) {
const rootView = activity.getWindow().getDecorView().getRootView();
// store the listener not to trigger GC collection before collecting the method
global.onGlobalLayoutListener =
new android.view.ViewTreeObserver.OnGlobalLayoutListener({
onGlobalLayout() {
Application.android.notify({
eventName: Application.android.displayedEvent,
object: Application,
android: Application.android,
activity,
});
const viewTreeObserver = rootView.getViewTreeObserver();
viewTreeObserver.removeOnGlobalLayoutListener(global.onGlobalLayoutListener);
},
});
rootView
.getViewTreeObserver()
.addOnGlobalLayoutListener(global.onGlobalLayoutListener);
}
}
@NativeClass
@JavaProxy('org.nativescript.NativeScriptComponentCallbacks')
class NativeScriptComponentCallbacks extends android.content.ComponentCallbacks2 {
@profile
public onLowMemory(): void {
gc();
java.lang.System.gc();
Application.notify({
eventName: Application.lowMemoryEvent,
object: Application,
android: this,
} as ApplicationEventData);
}
@profile
public onTrimMemory(level: number): void {
// TODO: This is skipped for now, test carefully for OutOfMemory exceptions
}
@profile
public onConfigurationChanged(
newConfiguration: android.content.res.Configuration
): void {
Application.android.onConfigurationChanged(newConfiguration);
}
}
export class AndroidApplication extends ApplicationCommon implements IAndroidApplication {
readonly activityCreatedEvent = 'activityCreated';
readonly activityDestroyedEvent = 'activityDestroyed';
readonly activityStartedEvent = 'activityStarted';
readonly activityPausedEvent = 'activityPaused';
readonly activityResumedEvent = 'activityResumed';
readonly activityStoppedEvent = 'activityStopped';
readonly saveActivityStateEvent = 'saveActivityState';
readonly activityResultEvent = 'activityResult';
readonly activityBackPressedEvent = 'activityBackPressed';
readonly activityNewIntentEvent = 'activityNewIntent';
readonly activityRequestPermissionsEvent = 'activityRequestPermissions';
private _nativeApp: android.app.Application;
// we are using these property to store the callbacks to avoid early GC collection which would trigger MarkReachableObjects
private lifecycleCallbacks: NativeScriptLifecycleCallbacks;
private componentCallbacks: NativeScriptComponentCallbacks;
constructor() {
super();
}
init(nativeApp: android.app.Application): void {
if (this.nativeApp === nativeApp) {
return;
}
if (this.nativeApp) {
throw new Error('Application.android already initialized.');
}
this._nativeApp = nativeApp;
// we store those callbacks and add a function for clearing them later so that the objects will be eligable for GC
this.lifecycleCallbacks = new NativeScriptLifecycleCallbacks();
this.nativeApp.registerActivityLifecycleCallbacks(this.lifecycleCallbacks);
this.componentCallbacks = new NativeScriptComponentCallbacks();
this.nativeApp.registerComponentCallbacks(this.componentCallbacks);
this._registerPendingReceivers();
}
private _registeredReceivers = {};
private _pendingReceiverRegistrations = new Array<
(context: android.content.Context) => void
>();
private _registerPendingReceivers() {
this._pendingReceiverRegistrations.forEach((func) => func(this.context));
this._pendingReceiverRegistrations.length = 0;
}
onConfigurationChanged(configuration: android.content.res.Configuration): void {
this.setOrientation(this.getOrientationValue(configuration));
this.setSystemAppearance(this.getSystemAppearanceValue(configuration));
}
getNativeApplication() {
let nativeApp = this.nativeApp;
if (nativeApp) {
return nativeApp;
}
// Try getting it from module - check whether application.android.init has been explicitly called
// check whether the com.tns.NativeScriptApplication type exists
if (com.tns.NativeScriptApplication) {
nativeApp = com.tns.NativeScriptApplication.getInstance();
}
// the getInstance might return null if com.tns.NativeScriptApplication exists but is not the starting app type
if (!nativeApp) {
// TODO: Should we handle the case when a custom application type is provided and the user has not explicitly initialized the application module?
const clazz = java.lang.Class.forName('android.app.ActivityThread');
if (clazz) {
const method = clazz.getMethod('currentApplication', null);
if (method) {
nativeApp = method.invoke(null, null);
}
}
}
// we cannot work without having the app instance
if (!nativeApp) {
throw new Error(
"Failed to retrieve native Android Application object. If you have a custom android.app.Application type implemented make sure that you've called the 'Application.android.init' method."
);
}
return nativeApp;
}
get nativeApp(): android.app.Application {
return this._nativeApp;
}
run(entry?: string | NavigationEntry): void {
if (this.started) {
throw new Error('Application is already started.');
}
this.started = true;
this.mainEntry = typeof entry === 'string' ? { moduleName: entry } : entry;
if (!this.nativeApp) {
const nativeApp = this.getNativeApplication();
this.init(nativeApp);
}
initAccessibilityCssHelper();
initAccessibilityFontScale();
}
/**
* todo: check if true, cause there's no such thing in Utils.android
* @deprecated Use Utils.android.getPackageName() instead.
*/
get packageName() {
return this.nativeApp.getPackageName();
}
private _startActivity: android.app.Activity;
private _foregroundActivity: android.app.Activity;
get startActivity() {
return this._startActivity;
}
get foregroundActivity() {
return this._foregroundActivity;
}
setStartActivity(value: android.app.Activity) {
this._startActivity = value;
}
setForegroundActivity(value: android.app.Activity) {
this._foregroundActivity = value;
}
get context() {
return this.nativeApp.getApplicationContext();
}
public registerBroadcastReceiver(
intentFilter: string,
onReceiveCallback: (
context: android.content.Context,
intent: android.content.Intent
) => void
): void {
const registerFunc = (context: android.content.Context) => {
const receiver: android.content.BroadcastReceiver = new BroadcastReceiver(
onReceiveCallback
);
context.registerReceiver(receiver, new android.content.IntentFilter(intentFilter));
this._registeredReceivers[intentFilter] = receiver;
};
if (this.context) {
registerFunc(this.context);
} else {
this._pendingReceiverRegistrations.push(registerFunc);
}
}
public unregisterBroadcastReceiver(intentFilter: string): void {
const receiver = this._registeredReceivers[intentFilter];
if (receiver) {
this.context.unregisterReceiver(receiver);
this._registeredReceivers[intentFilter] = undefined;
delete this._registeredReceivers[intentFilter];
}
}
public getRegisteredBroadcastReceiver(
intentFilter: string
): android.content.BroadcastReceiver | undefined {
return this._registeredReceivers[intentFilter];
}
getRootView(): View {
const activity = this.foregroundActivity || this.startActivity;
if (!activity) {
return undefined;
}
const callbacks: AndroidActivityCallbacks = activity['_callbacks'];
return callbacks ? callbacks.getRootView() : undefined;
}
resetRootView(entry?: NavigationEntry | string): void {
super.resetRootView(entry);
const activity = this.foregroundActivity || this.startActivity;
if (!activity) {
throw new Error('Cannot find android activity.');
}
// this.mainEntry = typeof entry === 'string' ? { moduleName: entry } : entry;
const callbacks: AndroidActivityCallbacks = activity['_callbacks'];
if (!callbacks) {
throw new Error('Cannot find android activity callbacks.');
}
callbacks.resetActivityContent(activity);
}
getSystemAppearance(): 'light' | 'dark' {
const resources = this.context.getResources();
const configuration = resources.getConfiguration();
return this.getSystemAppearanceValue(configuration);
}
// https://developer.android.com/guide/topics/ui/look-and-feel/darktheme#configuration_changes
private getSystemAppearanceValue(
configuration: android.content.res.Configuration
): 'dark' | 'light' {
const systemAppearance =
configuration.uiMode & android.content.res.Configuration.UI_MODE_NIGHT_MASK;
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';
}
}
getOrientation() {
const resources = this.context.getResources();
const configuration = <android.content.res.Configuration>resources.getConfiguration();
return this.getOrientationValue(configuration);
}
private getOrientationValue(
configuration: android.content.res.Configuration
): 'portrait' | 'landscape' | 'unknown' {
const orientation = configuration.orientation;
switch (orientation) {
case android.content.res.Configuration.ORIENTATION_LANDSCAPE:
return 'landscape';
case android.content.res.Configuration.ORIENTATION_PORTRAIT:
return 'portrait';
default:
return 'unknown';
}
}
get android() {
// ensures Application.android is defined when running on iOS
return this;
}
}
export * from './application-common';
export const Application = new AndroidApplication();
export const iOSApplication = undefined;
export * from './application';
export * from './application-shims';

View File

@@ -1,57 +1,2 @@
import { ApplicationCommon } from './application-common';
export * from './application-common';
export * from './application-interfaces';
export const Application: ApplicationCommon;
export interface AndroidApplication extends ApplicationCommon {
readonly activityCreatedEvent;
readonly activityDestroyedEvent;
readonly activityStartedEvent;
readonly activityPausedEvent;
readonly activityResumedEvent;
readonly activityStoppedEvent;
readonly saveActivityStateEvent;
readonly activityResultEvent;
readonly activityBackPressedEvent;
readonly activityNewIntentEvent;
readonly activityRequestPermissionsEvent;
getNativeApplication(): android.app.Application;
init(nativeApp: android.app.Application): void;
get nativeApp(): android.app.Application;
get packageName(): string;
get startActivity(): android.app.Activity;
get foregroundActivity(): android.app.Activity;
get context(): android.content.Context;
registerBroadcastReceiver(
intentFilter: string,
onReceiveCallback: (
context: android.content.Context,
intent: android.content.Intent
) => void
): void;
unregisterBroadcastReceiver(intentFilter: string): void;
}
export interface iOSApplication extends ApplicationCommon {
get rootController(): UIViewController;
get nativeApp(): UIApplication;
get window(): UIWindow;
get delegate(): UIApplicationDelegate;
set delegate(value: UIApplicationDelegate | unknown);
getNativeApplication(): UIApplication;
addNotificationObserver(
notificationName: string,
onReceiveCallback: (notification: NSNotification) => void
): NotificationObserver;
removeNotificationObserver(observer: any, notificationName: string);
}
export * from './application';
export * from './application-shims';

View File

@@ -1,554 +1,2 @@
import { initAccessibilityCssHelper } from '../accessibility/accessibility-css-helper';
import { initAccessibilityFontScale } from '../accessibility/font-scale';
import { profile } from '../profiling';
import { View } from '../ui';
import { Builder } from '../ui/builder';
import { IOSHelper } from '../ui/core/view/view-helper';
import { NavigationEntry } from '../ui/frame/frame-interfaces';
import * as Utils from '../utils';
import type { iOSApplication as IiOSApplication } from './';
import { ApplicationCommon } from './application-common';
import {
ApplicationEventData,
LaunchEventData,
LoadAppCSSEventData,
} from './application-interfaces';
@NativeClass
class CADisplayLinkTarget extends NSObject {
private _owner: WeakRef<iOSApplication>;
static initWithOwner(owner: WeakRef<iOSApplication>): CADisplayLinkTarget {
const target = <CADisplayLinkTarget>CADisplayLinkTarget.new();
target._owner = owner;
return target;
}
onDisplayed(link: CADisplayLink) {
link.invalidate();
const owner = this._owner.deref();
if (!owner) {
return;
}
owner.displayedOnce = true;
owner.notify(<ApplicationEventData>{
eventName: owner.displayedEvent,
object: owner,
ios: owner.ios,
});
owner.displayedLinkTarget = null;
owner.displayedLink = null;
}
public static ObjCExposedMethods = {
onDisplayed: { returns: interop.types.void, params: [CADisplayLink] },
};
}
@NativeClass
class NotificationObserver extends NSObject {
private _onReceiveCallback: (notification: NSNotification) => void;
public static initWithCallback(
onReceiveCallback: (notification: NSNotification) => void
): NotificationObserver {
const observer = <NotificationObserver>super.new();
observer._onReceiveCallback = onReceiveCallback;
return observer;
}
public onReceive(notification: NSNotification): void {
this._onReceiveCallback(notification);
}
public static ObjCExposedMethods = {
onReceive: { returns: interop.types.void, params: [NSNotification] },
};
}
@NativeClass
class Responder extends UIResponder implements UIApplicationDelegate {
get window(): UIWindow {
return Application.ios.window;
}
set window(value: UIWindow) {
// NOOP
}
static ObjCProtocols = [UIApplicationDelegate];
}
export class iOSApplication extends ApplicationCommon implements IiOSApplication {
private _delegate: UIApplicationDelegate;
private _window: UIWindow;
private _notificationObservers: NotificationObserver[] = [];
private _rootView: View;
displayedOnce = false;
displayedLinkTarget: CADisplayLinkTarget;
displayedLink: CADisplayLink;
constructor() {
super();
this.addNotificationObserver(
UIApplicationDidFinishLaunchingNotification,
this.didFinishLaunchingWithOptions.bind(this)
);
this.addNotificationObserver(
UIApplicationDidBecomeActiveNotification,
this.didBecomeActive.bind(this)
);
this.addNotificationObserver(
UIApplicationDidEnterBackgroundNotification,
this.didEnterBackground.bind(this)
);
this.addNotificationObserver(
UIApplicationWillTerminateNotification,
this.willTerminate.bind(this)
);
this.addNotificationObserver(
UIApplicationDidReceiveMemoryWarningNotification,
this.didReceiveMemoryWarning.bind(this)
);
this.addNotificationObserver(
UIApplicationDidChangeStatusBarOrientationNotification,
this.didChangeStatusBarOrientation.bind(this)
);
}
getRootView(): View {
return this._rootView;
}
resetRootView(view?: View) {
super.resetRootView(view);
this.setWindowContent();
}
run(entry?: string | NavigationEntry): void {
console.log('run in iOSApplication', entry);
this.mainEntry = typeof entry === 'string' ? { moduleName: entry } : entry;
this.started = true;
if (this.nativeApp) {
this.runAsEmbeddedApp();
} else {
this.runAsMainApp();
}
initAccessibilityCssHelper();
initAccessibilityFontScale();
}
private runAsMainApp() {
console.log('runAsMainApp', this.delegate);
UIApplicationMain(
0,
null,
null,
this.delegate
? NSStringFromClass(this.delegate as any)
: NSStringFromClass(Responder)
);
}
private runAsEmbeddedApp() {
// TODO: this rootView should be held alive until rootController dismissViewController is called.
const rootView = this.createRootView();
if (!rootView) {
return;
}
// Attach to the existing iOS app
const window =
this.nativeApp.keyWindow ||
(this.nativeApp.windows.count > 0 && this.nativeApp.windows[0]);
if (!window) {
return;
}
const rootController = window.rootViewController;
if (!rootController) {
return;
}
const controller = this.getViewController(rootView);
const embedderDelegate = NativeScriptEmbedder.sharedInstance().delegate;
rootView._setupAsRootView({});
rootView.on(IOSHelper.traitCollectionColorAppearanceChangedEvent, () => {
const userInterfaceStyle = controller.traitCollection.userInterfaceStyle;
const newSystemAppearance = this.getSystemAppearanceValue(userInterfaceStyle);
this.setSystemAppearance(newSystemAppearance);
});
if (embedderDelegate) {
this.setViewControllerView(rootView);
embedderDelegate.presentNativeScriptApp(controller);
} else {
const visibleVC = Utils.ios.getVisibleViewController(rootController);
visibleVC.presentViewControllerAnimatedCompletion(controller, true, null);
}
// this.setRootViewsSystemAppearanceCssClass(rootView);
this.notifyAppStarted();
}
private createRootView(v?: View) {
let rootView = v;
if (!rootView) {
console.log('createRootView mainEntry', this.mainEntry);
// try to navigate to the mainEntry (if specified)
if (!this.mainEntry) {
throw new Error(
'Main entry is missing. App cannot be started. Verify app bootstrap.'
);
} else {
// console.log('createRootView mainEntry:', mainEntry);
rootView = Builder.createViewFromEntry(this.mainEntry);
}
}
// console.log('createRootView rootView:', rootView);
// setRootViewsCssClasses(rootView);
return rootView;
}
private getViewController(rootView: View): UIViewController {
let viewController: UIViewController = rootView.viewController || rootView.ios;
if (!(viewController instanceof UIViewController)) {
// We set UILayoutViewController dynamically to the root view if it doesn't have a view controller
// At the moment the root view doesn't have its native view created. We set it in the setViewControllerView func
viewController = IOSHelper.UILayoutViewController.initWithOwner(
new WeakRef(rootView)
) as UIViewController;
rootView.viewController = viewController;
}
return viewController;
}
private setViewControllerView(view: View): void {
const viewController: UIViewController = view.viewController || view.ios;
const nativeView = view.ios || view.nativeViewProtected;
if (!nativeView || !viewController) {
throw new Error('Root should be either UIViewController or UIView');
}
if (viewController instanceof IOSHelper.UILayoutViewController) {
viewController.view.addSubview(nativeView);
}
}
setMaxRefreshRate(options?: { min?: number; max?: number; preferred?: number }): void {
const adjustRefreshRate = () => {
if (!this.displayedLink) {
return;
}
const minFrameRateDisabled = NSBundle.mainBundle.objectForInfoDictionaryKey(
'CADisableMinimumFrameDurationOnPhone'
);
if (minFrameRateDisabled) {
let max = 120;
const deviceMaxFrames = UIScreen.mainScreen?.maximumFramesPerSecond;
if (options?.max) {
if (deviceMaxFrames) {
// iOS 10.3
max = options.max <= deviceMaxFrames ? options.max : deviceMaxFrames;
} else if (this.displayedLink.preferredFramesPerSecond) {
// iOS 10.0
max =
options.max <= this.displayedLink.preferredFramesPerSecond
? options.max
: this.displayedLink.preferredFramesPerSecond;
}
}
if (Utils.ios.MajorVersion >= 15) {
const min = options?.min || max / 2;
const preferred = options?.preferred || max;
this.displayedLink.preferredFrameRateRange = CAFrameRateRangeMake(
min,
max,
preferred
);
} else {
this.displayedLink.preferredFramesPerSecond = max;
}
}
};
if (this.displayedOnce) {
adjustRefreshRate();
return;
}
this.displayedLinkTarget = CADisplayLinkTarget.initWithOwner(new WeakRef(this));
this.displayedLink = CADisplayLink.displayLinkWithTargetSelector(
this.displayedLinkTarget,
'onDisplayed'
);
adjustRefreshRate();
this.displayedLink.addToRunLoopForMode(NSRunLoop.mainRunLoop, NSDefaultRunLoopMode);
this.displayedLink.addToRunLoopForMode(NSRunLoop.mainRunLoop, UITrackingRunLoopMode);
}
get rootController() {
return this.window.rootViewController;
}
get nativeApp() {
return UIApplication.sharedApplication;
}
get window(): UIWindow {
if (NativeScriptEmbedder.sharedInstance().delegate && !this._window) {
this._window = UIApplication.sharedApplication.keyWindow;
}
return this._window;
}
get delegate(): UIApplicationDelegate {
return this._delegate;
}
set delegate(value: UIApplicationDelegate | unknown) {
if (this._delegate !== value) {
this._delegate = value as UIApplicationDelegate;
}
}
getNativeApplication() {
return this.nativeApp;
}
addNotificationObserver(
notificationName: string,
onReceiveCallback: (notification: NSNotification) => void
) {
const observer = NotificationObserver.initWithCallback(onReceiveCallback);
NSNotificationCenter.defaultCenter.addObserverSelectorNameObject(
observer,
'onReceive',
notificationName,
null
);
this._notificationObservers.push(observer);
return observer;
}
removeNotificationObserver(observer: any, notificationName: string) {
const index = this._notificationObservers.indexOf(observer);
if (index >= 0) {
this._notificationObservers.splice(index, 1);
NSNotificationCenter.defaultCenter.removeObserverNameObject(
observer,
notificationName,
null
);
}
}
protected getSystemAppearance(): 'light' | 'dark' {
// userInterfaceStyle is available on UITraitCollection since iOS 12.
if (Utils.ios.MajorVersion <= 11) {
return undefined;
}
const userInterfaceStyle = this.rootController.traitCollection.userInterfaceStyle;
return this.getSystemAppearanceValue(userInterfaceStyle);
}
private getSystemAppearanceValue(userInterfaceStyle: number): 'dark' | 'light' {
switch (userInterfaceStyle) {
case UIUserInterfaceStyle.Dark:
return 'dark';
case UIUserInterfaceStyle.Light:
case UIUserInterfaceStyle.Unspecified:
return 'light';
}
}
protected getOrientation() {
const statusBarOrientation = UIApplication.sharedApplication.statusBarOrientation;
return this.getOrientationValue(statusBarOrientation);
}
private getOrientationValue(orientation: number): 'portrait' | 'landscape' | 'unknown' {
switch (orientation) {
case UIInterfaceOrientation.LandscapeRight:
case UIInterfaceOrientation.LandscapeLeft:
return 'landscape';
case UIInterfaceOrientation.PortraitUpsideDown:
case UIInterfaceOrientation.Portrait:
return 'portrait';
case UIInterfaceOrientation.Unknown:
return 'unknown';
}
}
private notifyAppStarted(notification?: NSNotification) {
const args: LaunchEventData = {
eventName: this.launchEvent,
object: this,
ios:
notification?.userInfo?.objectForKey(
'UIApplicationLaunchOptionsLocalNotificationKey'
) || null,
};
this.notify(args);
this.notify(<LoadAppCSSEventData>{
eventName: 'loadAppCss',
object: this,
cssFile: this.getCssFileName(),
});
if (this._window) {
if (args.root !== null && !NativeScriptEmbedder.sharedInstance().delegate) {
this.setWindowContent(args.root);
}
} else {
this._window = UIApplication.sharedApplication.keyWindow;
}
}
private setWindowContent(view?: View): void {
if (this._rootView) {
// if we already have a root view, we reset it.
this._rootView._onRootViewReset();
}
const rootView = this.createRootView(view);
const controller = this.getViewController(rootView);
this._rootView = rootView;
// setup view as styleScopeHost
rootView._setupAsRootView({});
this.setViewControllerView(rootView);
const haveController = this._window.rootViewController !== null;
this._window.rootViewController = controller;
this.setRootViewsSystemAppearanceCssClass(rootView);
if (!haveController) {
this._window.makeKeyAndVisible();
}
rootView.on(IOSHelper.traitCollectionColorAppearanceChangedEvent, () => {
const userInterfaceStyle = controller.traitCollection.userInterfaceStyle;
const newSystemAppearance = this.getSystemAppearanceValue(userInterfaceStyle);
this.setSystemAppearance(newSystemAppearance);
});
}
// Observers
@profile
private didFinishLaunchingWithOptions(notification: NSNotification) {
this.setMaxRefreshRate();
this._window = UIWindow.alloc().initWithFrame(UIScreen.mainScreen.bounds);
// TODO: Expose Window module so that it can we styled from XML & CSS
this._window.backgroundColor =
Utils.ios.MajorVersion <= 12 || !UIColor.systemBackgroundColor
? UIColor.whiteColor
: UIColor.systemBackgroundColor;
this.notifyAppStarted(notification);
}
@profile
private didBecomeActive(notification: NSNotification) {
// const ios = UIApplication.sharedApplication;
// const object = this;
// setInBackground(false);
// setSuspended(false);
this.notify(<ApplicationEventData>{
eventName: this.resumeEvent,
object: this,
ios: this.ios,
});
this.notify(<ApplicationEventData>{
eventName: this.foregroundEvent,
object: this,
ios: this.ios,
});
// const rootView = this._rootView;
// if (rootView && !rootView.isLoaded) {
// rootView.callLoaded();
// }
}
private didEnterBackground(notification: NSNotification) {
// const ios = UIApplication.sharedApplication;
// const object = this;
// setInBackground(true);
// setSuspended(true);
this.notify(<ApplicationEventData>{
eventName: this.suspendEvent,
object: this,
ios: this.ios,
});
this.notify(<ApplicationEventData>{
eventName: this.backgroundEvent,
object: this,
ios: this.ios,
});
// const rootView = this._rootView;
// if (rootView && rootView.isLoaded) {
// rootView.callUnloaded();
// }
}
private willTerminate(notification: NSNotification) {
this.notify(<ApplicationEventData>{
eventName: this.exitEvent,
object: this,
ios: this.ios,
});
// const rootView = this._rootView;
// if (rootView && rootView.isLoaded) {
// rootView.callUnloaded();
// }
}
private didReceiveMemoryWarning(notification: NSNotification) {
this.notify(<ApplicationEventData>{
eventName: this.lowMemoryEvent,
object: this,
ios: this.ios,
});
}
private didChangeStatusBarOrientation(notification: NSNotification) {
const statusBarOrientation = UIApplication.sharedApplication.statusBarOrientation;
const newOrientation = this.getOrientationValue(statusBarOrientation);
this.setOrientation(newOrientation);
}
get ios() {
// ensures Application.ios is defined when running on iOS
return this;
}
}
export * from './application-common';
export const Application = new iOSApplication();
export const AndroidApplication = undefined;
export * from './application';
export * from './application-shims';