mirror of
https://github.com/NativeScript/NativeScript.git
synced 2025-11-05 13:26:48 +08:00
feat: add deprecated shims to keep b/w compatibility
This commit is contained in:
@@ -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.'
|
||||
);
|
||||
|
||||
@@ -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.
|
||||
|
||||
393
packages/core/application/application-shims.ts
Normal file
393
packages/core/application/application-shims.ts
Normal 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;
|
||||
532
packages/core/application/application.android.ts
Normal file
532
packages/core/application/application.android.ts
Normal 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;
|
||||
57
packages/core/application/application.d.ts
vendored
Normal file
57
packages/core/application/application.d.ts
vendored
Normal 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);
|
||||
}
|
||||
554
packages/core/application/application.ios.ts
Normal file
554
packages/core/application/application.ios.ts
Normal 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;
|
||||
@@ -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';
|
||||
|
||||
59
packages/core/application/index.d.ts
vendored
59
packages/core/application/index.d.ts
vendored
@@ -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';
|
||||
|
||||
@@ -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';
|
||||
|
||||
Reference in New Issue
Block a user