refactor: additional cleanup and code reuse

This commit is contained in:
Igor Randjelovic
2023-05-12 16:51:29 +02:00
parent 5b4624a308
commit f7d5a8e8a6
9 changed files with 544 additions and 353 deletions

View File

@ -16,6 +16,8 @@ import {
AndroidActivityNewIntentEventData, AndroidActivityNewIntentEventData,
AndroidActivityResultEventData, AndroidActivityResultEventData,
AndroidActivityBackPressedEventData, AndroidActivityBackPressedEventData,
Label,
LaunchEventData,
} from '@nativescript/core'; } from '@nativescript/core';
// import * as Application from "@nativescript/core/application"; // import * as Application from "@nativescript/core/application";
@ -25,7 +27,7 @@ if (Application.ios) {
Application.ios.addNotificationObserver( Application.ios.addNotificationObserver(
UIApplicationDidFinishLaunchingNotification, UIApplicationDidFinishLaunchingNotification,
(notification: NSNotification) => { (notification: NSNotification) => {
console.log('UIApplicationDidFinishLaunchingNotification: ' + notification); console.log('UIApplicationDidFinishLaunchingNotification:', notification);
} }
); );
} }
@ -35,77 +37,75 @@ Application.on(Application.displayedEvent, function (args: ApplicationEventData)
global.isDisplayedEventFired = true; global.isDisplayedEventFired = true;
if (args.android) { if (args.android) {
// For Android applications, args.android is an Android activity class. // For Android applications, args.activity is an Android activity class.
console.log('Displayed Activity: ' + args.android); console.log('Displayed Activity:', (args as AndroidActivityEventData).activity);
} else if (args.ios) { } else if (args.ios) {
// For iOS applications, args.ios is UIApplication. // For iOS applications, args.ios is UIApplication.
console.log('Displayed UIApplication: ' + args.ios); console.log('Displayed UIApplication:', args.ios);
} }
}); });
Application.on(Application.launchEvent, function (args: ApplicationEventData) { Application.on(Application.launchEvent, function (args: LaunchEventData) {
if (args.android) { if (args.android) {
// For Android applications, args.android is an android.content.Intent class. // For Android applications, args.android is an android.content.Intent class.
console.log( console.log('Launched Android application with the following intent:', args.android);
'Launched Android application with the following intent: ' + args.android + '.'
);
} else if (args.ios !== undefined) { } else if (args.ios !== undefined) {
// For iOS applications, args.ios is NSDictionary (launchOptions). // For iOS applications, args.ios is NSDictionary (launchOptions).
console.log('Launched iOS application with options: ' + args.ios); console.log('Launched iOS application with options:', args.ios);
} }
}); });
Application.on(Application.suspendEvent, function (args: ApplicationEventData) { Application.on(Application.suspendEvent, function (args: ApplicationEventData) {
if (args.android) { if (args.android) {
// For Android applications, args.android is an Android activity class. // For Android applications, args.android is an Android activity class.
console.log('Suspend Activity: ' + args.android); console.log('Suspend Activity:', args.android);
} else if (args.ios) { } else if (args.ios) {
// For iOS applications, args.ios is UIApplication. // For iOS applications, args.ios is UIApplication.
console.log('Suspend UIApplication: ' + args.ios); console.log('Suspend UIApplication:', args.ios);
} }
}); });
Application.on(Application.resumeEvent, function (args: ApplicationEventData) { Application.on(Application.resumeEvent, function (args: ApplicationEventData) {
if (args.android) { if (args.android) {
// For Android applications, args.android is an Android activity class. // For Android applications, args.android is an Android activity class.
console.log('Resume Activity: ' + args.android); console.log('Resume Activity:', args.android);
} else if (args.ios) { } else if (args.ios) {
// For iOS applications, args.ios is UIApplication. // For iOS applications, args.ios is UIApplication.
console.log('Resume UIApplication: ' + args.ios); console.log('Resume UIApplication:', args.ios);
} }
}); });
Application.on(Application.exitEvent, function (args: ApplicationEventData) { Application.on(Application.exitEvent, function (args: ApplicationEventData) {
if (args.android) { if (args.android) {
// For Android applications, args.android is an Android activity class. // For Android applications, args.android is an Android activity class.
console.log('Exit Activity: ' + args.android); console.log('Exit Activity:', args.android);
} else if (args.ios) { } else if (args.ios) {
// For iOS applications, args.ios is UIApplication. // For iOS applications, args.ios is UIApplication.
console.log('Exit UIApplication: ' + args.ios); console.log('Exit UIApplication:', args.ios);
} }
}); });
Application.on(Application.lowMemoryEvent, function (args: ApplicationEventData) { Application.on(Application.lowMemoryEvent, function (args: ApplicationEventData) {
if (args.android) { if (args.android) {
// For Android applications, args.android is an Android activity class. // For Android applications, args.android is an Android activity class.
console.log('Low Memory: ' + args.android); console.log('Low Memory:', args.android);
} else if (args.ios) { } else if (args.ios) {
// For iOS applications, args.ios is UIApplication. // For iOS applications, args.ios is UIApplication.
console.log('Low Memory: ' + args.ios); console.log('Low Memory:', args.ios);
} }
}); });
// Error events. // Error events.
Application.on(Application.uncaughtErrorEvent, function (args: UnhandledErrorEventData) { Application.on(Application.uncaughtErrorEvent, function (args: UnhandledErrorEventData) {
console.log('NativeScriptError: ' + args.error); console.log('NativeScriptError:', args.error);
console.log((<any>args.error).nativeException || (<any>args.error).nativeError); console.log((<any>args.error).nativeException ?? (<any>args.error).nativeError);
console.log((<any>args.error).stackTrace || (<any>args.error).stack); console.log((<any>args.error).stackTrace ?? (<any>args.error).stack);
}); });
Application.on(Application.discardedErrorEvent, function (args: DiscardedErrorEventData) { Application.on(Application.discardedErrorEvent, function (args: DiscardedErrorEventData) {
console.log('[Discarded] NativeScriptError: ' + args.error); console.log('[Discarded] NativeScriptError:', args.error);
console.log((<any>args.error).nativeException || (<any>args.error).nativeError); console.log((<any>args.error).nativeException ?? (<any>args.error).nativeError);
console.log((<any>args.error).stackTrace || (<any>args.error).stack); console.log((<any>args.error).stackTrace ?? (<any>args.error).stack);
}); });
// Android activity events. // Android activity events.
@ -114,12 +114,10 @@ if (Application.android) {
Application.android.activityCreatedEvent, Application.android.activityCreatedEvent,
function (args: AndroidActivityBundleEventData) { function (args: AndroidActivityBundleEventData) {
console.log( console.log(
'Event: ' + 'Event: ' + args.eventName + ', Activity:',
args.eventName + args.activity,
', Activity: ' + ', Bundle:',
args.activity + args.bundle
', Bundle: ' +
args.bundle
); );
} }
); );
@ -127,35 +125,35 @@ if (Application.android) {
Application.android.on( Application.android.on(
Application.android.activityDestroyedEvent, Application.android.activityDestroyedEvent,
function (args: AndroidActivityEventData) { function (args: AndroidActivityEventData) {
console.log('Event: ' + args.eventName + ', Activity: ' + args.activity); console.log('Event: ' + args.eventName + ', Activity: ', args.activity);
} }
); );
Application.android.on( Application.android.on(
Application.android.activityStartedEvent, Application.android.activityStartedEvent,
function (args: AndroidActivityEventData) { function (args: AndroidActivityEventData) {
console.log('Event: ' + args.eventName + ', Activity: ' + args.activity); console.log('Event: ' + args.eventName + ', Activity:', args.activity);
} }
); );
Application.android.on( Application.android.on(
Application.android.activityPausedEvent, Application.android.activityPausedEvent,
function (args: AndroidActivityEventData) { function (args: AndroidActivityEventData) {
console.log('Event: ' + args.eventName + ', Activity: ' + args.activity); console.log('Event: ' + args.eventName + ', Activity:', args.activity);
} }
); );
Application.android.on( Application.android.on(
Application.android.activityResumedEvent, Application.android.activityResumedEvent,
function (args: AndroidActivityEventData) { function (args: AndroidActivityEventData) {
console.log('Event: ' + args.eventName + ', Activity: ' + args.activity); console.log('Event: ' + args.eventName + ', Activity:', args.activity);
} }
); );
Application.android.on( Application.android.on(
Application.android.activityStoppedEvent, Application.android.activityStoppedEvent,
function (args: AndroidActivityEventData) { function (args: AndroidActivityEventData) {
console.log('Event: ' + args.eventName + ', Activity: ' + args.activity); console.log('Event: ' + args.eventName + ', Activity:', args.activity);
} }
); );
@ -163,12 +161,10 @@ if (Application.android) {
Application.android.saveActivityStateEvent, Application.android.saveActivityStateEvent,
function (args: AndroidActivityBundleEventData) { function (args: AndroidActivityBundleEventData) {
console.log( console.log(
'Event: ' + 'Event: ' + args.eventName + ', Activity:',
args.eventName + args.activity,
', Activity: ' + ', Bundle:',
args.activity + args.bundle
', Bundle: ' +
args.bundle
); );
} }
); );
@ -177,16 +173,16 @@ if (Application.android) {
Application.android.activityResultEvent, Application.android.activityResultEvent,
function (args: AndroidActivityResultEventData) { function (args: AndroidActivityResultEventData) {
console.log( console.log(
'Event: ' + 'Event:',
args.eventName + args.eventName,
', Activity: ' + ', Activity:',
args.activity + args.activity,
', requestCode: ' + ', requestCode: ',
args.requestCode + args.requestCode,
', resultCode: ' + ', resultCode: ',
args.resultCode + args.resultCode,
', Intent: ' + ', Intent: ',
args.intent args.intent
); );
} }
); );
@ -194,7 +190,7 @@ if (Application.android) {
Application.android.on( Application.android.on(
Application.android.activityBackPressedEvent, Application.android.activityBackPressedEvent,
function (args: AndroidActivityBackPressedEventData) { function (args: AndroidActivityBackPressedEventData) {
console.log('Event: ' + args.eventName + ', Activity: ' + args.activity); console.log('Event:', args.eventName, ', Activity:', args.activity);
// Set args.cancel = true to cancel back navigation and do something custom. // Set args.cancel = true to cancel back navigation and do something custom.
} }
); );
@ -203,12 +199,12 @@ if (Application.android) {
Application.android.activityNewIntentEvent, Application.android.activityNewIntentEvent,
function (args: AndroidActivityNewIntentEventData) { function (args: AndroidActivityNewIntentEventData) {
console.log( console.log(
'Event: ' + 'Event: ',
args.eventName + args.eventName,
', Activity: ' + ', Activity:',
args.activity + args.activity,
', Intent: ' + ', Intent:',
args.intent args.intent
); );
} }
); );

View File

@ -1,19 +1,28 @@
// Require globals first so that snapshot takes __extends function. import { initAccessibilityCssHelper } from '../accessibility/accessibility-css-helper';
// import '../globals'; import { initAccessibilityFontScale } from '../accessibility/font-scale';
import { Observable } from '../data/observable';
import { CoreTypes } from '../core-types'; import { CoreTypes } from '../core-types';
import { CSSUtils } from '../css/system-classes'; import { CSSUtils } from '../css/system-classes';
import { Observable } from '../data/observable';
import { Device } from '../platform';
import { profile } from '../profiling';
import { Trace } from '../trace'; import { Trace } from '../trace';
import { Builder } from '../ui/builder';
import * as bindableResources from '../ui/core/bindable/bindable-resources'; import * as bindableResources from '../ui/core/bindable/bindable-resources';
import { View } from '../ui/core/view'; import type { View } from '../ui/core/view';
import type { Frame } from '../ui/frame';
import { NavigationEntry } from '../ui/frame/frame-interfaces'; import { NavigationEntry } from '../ui/frame/frame-interfaces';
import type { StyleScope } from '../ui/styling/style-scope';
import type { import type {
AndroidApplication as IAndroidApplication, AndroidApplication as IAndroidApplication,
iOSApplication as IiOSApplication, iOSApplication as IiOSApplication,
} from './'; } from './';
import { CssChangedEventData, LoadAppCSSEventData } from './application-interfaces';
import { import {
ApplicationEventData,
CssChangedEventData,
DiscardedErrorEventData,
LaunchEventData,
LoadAppCSSEventData,
NativeScriptError,
OrientationChangedEventData, OrientationChangedEventData,
SystemAppearanceChangedEventData, SystemAppearanceChangedEventData,
} from './application-interfaces'; } from './application-interfaces';
@ -43,22 +52,75 @@ export class ApplicationCommon extends Observable {
readonly orientationChangedEvent = 'orientationChanged'; readonly orientationChangedEvent = 'orientationChanged';
readonly systemAppearanceChangedEvent = 'systemAppearanceChanged'; readonly systemAppearanceChangedEvent = 'systemAppearanceChanged';
readonly fontScaleChangedEvent = 'fontScaleChanged'; readonly fontScaleChangedEvent = 'fontScaleChanged';
readonly livesyncEvent = 'livesync';
constructor() { constructor() {
super(); super();
global.NativeScriptGlobals.appInstanceReady = true; global.NativeScriptGlobals.appInstanceReady = true;
global.__onUncaughtError = (error: NativeScriptError) => {
this.notify({
eventName: this.uncaughtErrorEvent,
object: this,
android: error,
ios: error,
error: error,
} as DiscardedErrorEventData);
};
global.__onDiscardedError = (error: NativeScriptError) => {
this.notify({
eventName: this.discardedErrorEvent,
object: this,
error: error,
} as DiscardedErrorEventData);
};
global.__onLiveSync = (context?: ModuleContext) => {
if (this.suspended) {
return;
}
const rootView = this.getRootView();
this.livesync(rootView, context);
};
}
livesync(rootView: View, context?: ModuleContext) {
this.notify({ eventName: this.livesyncEvent, object: this });
const liveSyncCore = global.__onLiveSyncCore;
let reapplyAppStyles = false;
// ModuleContext is available only for Hot Module Replacement
if (context && context.path) {
const styleExtensions = ['css', 'scss'];
const appStylesFullFileName = this.getCssFileName();
const appStylesFileName = appStylesFullFileName.substring(
0,
appStylesFullFileName.lastIndexOf('.') + 1
);
reapplyAppStyles = styleExtensions.some(
(ext) => context.path === appStylesFileName.concat(ext)
);
}
// Handle application styles
if (rootView && reapplyAppStyles) {
rootView._onCssStateChange();
} else if (liveSyncCore) {
liveSyncCore(context);
}
} }
/**
* Ensure css-class is set on rootView
*/
applyCssClass(rootView: View, cssClasses: string[], newCssClass: string): void { applyCssClass(rootView: View, cssClasses: string[], newCssClass: string): void {
if (!rootView.cssClasses.has(newCssClass)) { if (!rootView.cssClasses.has(newCssClass)) {
cssClasses.forEach((cssClass) => this.removeCssClass(rootView, cssClass)); cssClasses.forEach((cssClass) => this.removeCssClass(rootView, cssClass));
this.addCssClass(rootView, newCssClass); this.addCssClass(rootView, newCssClass);
this.increaseStyleScopeApplicationCssSelectorVersion(rootView); this.increaseStyleScopeApplicationCssSelectorVersion(rootView);
rootView._onCssStateChange(); rootView._onCssStateChange();
console.log('APPLY ROOT CSS CLASSES', ...rootView.cssClasses);
} }
} }
@ -73,15 +135,41 @@ export class ApplicationCommon extends Observable {
} }
private increaseStyleScopeApplicationCssSelectorVersion(rootView: View) { private increaseStyleScopeApplicationCssSelectorVersion(rootView: View) {
const styleScope = const styleScope: StyleScope =
rootView._styleScope || rootView._styleScope ?? (rootView as Frame)?.currentPage?._styleScope;
((<any>rootView).currentPage && (<any>rootView).currentPage._styleScope);
if (styleScope) { if (styleScope) {
styleScope._increaseApplicationCssSelectorVersion(); styleScope._increaseApplicationCssSelectorVersion();
} }
} }
protected setRootViewCSSClasses(rootView: View): void {
const platform = Device.os.toLowerCase();
const deviceType = Device.deviceType.toLowerCase();
if (platform) {
CSSUtils.pushToSystemCssClasses(`${CSSUtils.CLASS_PREFIX}${platform}`);
}
if (deviceType) {
CSSUtils.pushToSystemCssClasses(`${CSSUtils.CLASS_PREFIX}${deviceType}`);
}
if (this.orientation) {
CSSUtils.pushToSystemCssClasses(`${CSSUtils.CLASS_PREFIX}${this.orientation}`);
}
if (this.systemAppearance) {
CSSUtils.pushToSystemCssClasses(`${CSSUtils.CLASS_PREFIX}${this.systemAppearance}`);
}
rootView.cssClasses.add(CSSUtils.ROOT_VIEW_CSS_CLASS);
const rootViewCssClasses = CSSUtils.getSystemCssClasses();
rootViewCssClasses.forEach((c) => rootView.cssClasses.add(c));
console.log('ROOT CSS CLASSES', ...rootView.cssClasses);
}
/** /**
* iOS Only * iOS Only
* Dynamically change the preferred frame rate * Dynamically change the preferred frame rate
@ -89,12 +177,14 @@ export class ApplicationCommon extends Observable {
* For devices (iOS < 15), you can specify the max frame rate * For devices (iOS < 15), you can specify the max frame rate
* see: https://developer.apple.com/documentation/quartzcore/optimizing_promotion_refresh_rates_for_iphone_13_pro_and_ipad_pro * see: https://developer.apple.com/documentation/quartzcore/optimizing_promotion_refresh_rates_for_iphone_13_pro_and_ipad_pro
* To use, ensure your Info.plist has: * To use, ensure your Info.plist has:
* ```xml
* <key>CADisableMinimumFrameDurationOnPhone</key> * <key>CADisableMinimumFrameDurationOnPhone</key>
* <true/> * <true/>
* ```
* @param options { min?: number; max?: number; preferred?: number } * @param options { min?: number; max?: number; preferred?: number }
*/ */
setMaxRefreshRate(options?: { min?: number; max?: number; preferred?: number }) { setMaxRefreshRate(options?: { min?: number; max?: number; preferred?: number }) {
// // implement in platform specific files (iOS only for now)
} }
mainEntry: NavigationEntry; mainEntry: NavigationEntry;
@ -102,15 +192,62 @@ export class ApplicationCommon extends Observable {
return this.mainEntry; return this.mainEntry;
} }
@profile
protected notifyLaunch(additionalLanchEventData?: any): View | null {
const launchArgs: LaunchEventData = {
eventName: this.launchEvent,
object: this,
ios: this.ios,
android: this.android,
...additionalLanchEventData,
};
this.notify(launchArgs);
this.loadAppCss();
return launchArgs.root;
}
createRootView(view?: View, fireLaunchEvent = false, additionalLanchEventData?: any) {
let rootView = view;
if (!rootView) {
if (fireLaunchEvent) {
rootView = this.notifyLaunch(additionalLanchEventData);
// useful for integrations that would like to set rootView asynchronously after app launch
if (rootView === null) {
return null;
}
}
if (!rootView) {
// 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.'
);
}
rootView = Builder.createViewFromEntry(this.mainEntry);
}
}
return rootView;
}
getRootView(): View { getRootView(): View {
// ensureNativeApplication();
// return iosApp.rootView;
// return this.rootView;
throw new Error('getRootView() Not implemented.'); throw new Error('getRootView() Not implemented.');
} }
resetRootView(entry?: NavigationEntry | string) { resetRootView(entry?: NavigationEntry | string) {
this.mainEntry = typeof entry === 'string' ? { moduleName: entry } : entry; this.mainEntry = typeof entry === 'string' ? { moduleName: entry } : entry;
// rest of implementation is platform specific
}
initRootView() {
this.setRootViewCSSClasses(this.getRootView());
initAccessibilityCssHelper();
initAccessibilityFontScale();
} }
/** /**
@ -157,7 +294,9 @@ export class ApplicationCommon extends Observable {
try { try {
this.notify(<LoadAppCSSEventData>{ this.notify(<LoadAppCSSEventData>{
eventName: 'loadAppCss', eventName: 'loadAppCss',
object: this, // app, object: this,
ios: this.ios,
android: this.android,
cssFile: this.getCssFileName(), cssFile: this.getCssFileName(),
}); });
} catch (e) { } catch (e) {
@ -174,7 +313,7 @@ export class ApplicationCommon extends Observable {
addCss(cssText: string, attributeScoped?: boolean): void { addCss(cssText: string, attributeScoped?: boolean): void {
this.notify(<CssChangedEventData>{ this.notify(<CssChangedEventData>{
eventName: 'cssChanged', eventName: 'cssChanged',
object: this, // <any>iosApp, object: this,
cssText: cssText, cssText: cssText,
}); });
if (!attributeScoped) { if (!attributeScoped) {
@ -250,9 +389,9 @@ export class ApplicationCommon extends Observable {
return global.NativeScriptGlobals && global.NativeScriptGlobals.launched; return global.NativeScriptGlobals && global.NativeScriptGlobals.launched;
} }
private _systemAppearance: 'dark' | 'light'; private _systemAppearance: 'dark' | 'light' | null;
protected getSystemAppearance(): 'dark' | 'light' { protected getSystemAppearance(): 'dark' | 'light' | null {
// override in platform specific Application class // override in platform specific Application class
throw new Error('getSystemAppearance() not implemented'); throw new Error('getSystemAppearance() not implemented');
} }
@ -262,7 +401,6 @@ export class ApplicationCommon extends Observable {
return; return;
} }
this._systemAppearance = value; this._systemAppearance = value;
this.setRootViewsSystemAppearanceCssClass(this.getRootView());
this.systemAppearanceChanged(this.getRootView(), value); this.systemAppearanceChanged(this.getRootView(), value);
this.notify(<SystemAppearanceChangedEventData>{ this.notify(<SystemAppearanceChangedEventData>{
eventName: this.systemAppearanceChangedEvent, eventName: this.systemAppearanceChangedEvent,
@ -273,14 +411,7 @@ export class ApplicationCommon extends Observable {
}); });
} }
setRootViewsSystemAppearanceCssClass(rootView: View): void { get systemAppearance(): 'dark' | 'light' | null {
const systemAppearance = this.systemAppearance;
const systemAppearanceCssClass = `${CSSUtils.CLASS_PREFIX}${systemAppearance}`;
CSSUtils.pushToSystemCssClasses(systemAppearanceCssClass);
rootView.cssClasses.add(systemAppearanceCssClass);
}
get systemAppearance(): 'dark' | 'light' {
// return cached value, or get it from the platform specific override // return cached value, or get it from the platform specific override
return (this._systemAppearance ??= this.getSystemAppearance()); return (this._systemAppearance ??= this.getSystemAppearance());
} }
@ -314,16 +445,55 @@ export class ApplicationCommon extends Observable {
newSystemAppearanceCssClass newSystemAppearanceCssClass
); );
const rootModalViews = <Array<View>>rootView._getRootModalViews(); const rootModalViews = rootView._getRootModalViews();
rootModalViews.forEach((rootModalView) => { rootModalViews.forEach((rootModalView) => {
this.applyCssClass( this.applyCssClass(
rootModalView, rootModalView as View,
SYSTEM_APPEARANCE_CSS_CLASSES, SYSTEM_APPEARANCE_CSS_CLASSES,
newSystemAppearanceCssClass newSystemAppearanceCssClass
); );
}); });
} }
private _inBackground: boolean = false;
get inBackground() {
return this._inBackground;
}
setInBackground(value: boolean, additonalData?: any) {
this._inBackground = value;
this.notify(<ApplicationEventData>{
eventName: value ? this.backgroundEvent : this.foregroundEvent,
object: this,
ios: this.ios,
...additonalData,
});
}
private _suspended: boolean = false;
get suspended() {
return this._suspended;
}
setSuspended(value: boolean, additonalData?: any) {
this._suspended = value;
this.notify(<ApplicationEventData>{
eventName: value ? this.suspendEvent : this.resumeEvent,
object: this,
ios: this.ios,
android: this.android,
...additonalData,
});
}
public started = false;
get android(): IAndroidApplication { get android(): IAndroidApplication {
return undefined; return undefined;
} }
@ -332,18 +502,6 @@ export class ApplicationCommon extends Observable {
return undefined; return undefined;
} }
get inBackground() {
return false;
}
get suspended() {
return false;
}
setSuspended(suspended: boolean) {
// TODO
}
get AndroidApplication() { get AndroidApplication() {
return this.android; return this.android;
} }
@ -351,8 +509,6 @@ export class ApplicationCommon extends Observable {
get iOSApplication() { get iOSApplication() {
return this.ios; return this.ios;
} }
public started = false;
} }
// export const AndroidApplication: IAndroidApplication = undefined; // export const AndroidApplication: IAndroidApplication = undefined;

View File

@ -84,7 +84,7 @@ export interface FontScaleChangedEventData extends ApplicationEventData {
/** /**
* Event data containing information about unhandled application errors. * Event data containing information about unhandled application errors.
*/ */
export interface UnhandledErrorEventData extends EventData { export interface UnhandledErrorEventData extends ApplicationEventData {
ios?: NativeScriptError; ios?: NativeScriptError;
android?: NativeScriptError; android?: NativeScriptError;
error: NativeScriptError; error: NativeScriptError;
@ -93,7 +93,7 @@ export interface UnhandledErrorEventData extends EventData {
/** /**
* Event data containing information about discarded application errors. * Event data containing information about discarded application errors.
*/ */
export interface DiscardedErrorEventData extends EventData { export interface DiscardedErrorEventData extends ApplicationEventData {
error: NativeScriptError; error: NativeScriptError;
} }

View File

@ -1,15 +1,13 @@
import { initAccessibilityCssHelper } from '../accessibility/accessibility-css-helper';
import { initAccessibilityFontScale } from '../accessibility/font-scale';
import { profile } from '../profiling'; import { profile } from '../profiling';
import { View } from '../ui'; import { View } from '../ui';
import { AndroidActivityCallbacks, NavigationEntry } from '../ui/frame/frame-common'; import { AndroidActivityCallbacks, NavigationEntry } from '../ui/frame/frame-common';
import type { AndroidApplication as IAndroidApplication } from './application';
import { ApplicationCommon } from './application-common';
import type { import type {
AndroidActivityBundleEventData, AndroidActivityBundleEventData,
AndroidActivityEventData, AndroidActivityEventData,
ApplicationEventData, ApplicationEventData,
AndroidApplication as IAndroidApplication, } from './application-interfaces';
} from '.';
import { ApplicationCommon } from './application-common';
declare namespace com { declare namespace com {
namespace tns { namespace tns {
@ -102,18 +100,18 @@ class NativeScriptLifecycleCallbacks extends android.app.Application
} as AndroidActivityEventData); } 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. // TODO: This is a temporary workaround to force the V8's Garbage Collector, which will force the related Java Object to be collected.
// gc(); gc();
} }
@profile @profile
public onActivityPaused(activity: androidx.appcompat.app.AppCompatActivity): void { public onActivityPaused(activity: androidx.appcompat.app.AppCompatActivity): void {
// console.log('NativeScriptLifecycleCallbacks onActivityPaused'); // console.log('NativeScriptLifecycleCallbacks onActivityPaused');
if ('isNativeScriptActivity' in activity) { if ('isNativeScriptActivity' in activity) {
Application.android.notify({ Application.setSuspended(true, {
eventName: Application.suspendEvent, // todo: deprecate event.android in favor of event.activity
object: Application.android,
android: activity, android: activity,
} as ApplicationEventData); activity,
});
} }
Application.android.notify({ Application.android.notify({
@ -128,6 +126,9 @@ class NativeScriptLifecycleCallbacks extends android.app.Application
// console.log('NativeScriptLifecycleCallbacks onActivityResumed'); // console.log('NativeScriptLifecycleCallbacks onActivityResumed');
Application.android.setForegroundActivity(activity); Application.android.setForegroundActivity(activity);
// NOTE: setSuspended(false) is called in frame/index.android.ts inside onPostResume
// This is done to ensure proper timing for the event to be raised
Application.android.notify({ Application.android.notify({
eventName: Application.android.activityResumedEvent, eventName: Application.android.activityResumedEvent,
object: Application.android, object: Application.android,
@ -227,11 +228,11 @@ class NativeScriptLifecycleCallbacks extends android.app.Application
new android.view.ViewTreeObserver.OnGlobalLayoutListener({ new android.view.ViewTreeObserver.OnGlobalLayoutListener({
onGlobalLayout() { onGlobalLayout() {
Application.android.notify({ Application.android.notify({
eventName: Application.android.displayedEvent, eventName: Application.displayedEvent,
object: Application, object: Application,
android: Application.android, android: Application.android,
activity, activity,
}); } as AndroidActivityEventData);
const viewTreeObserver = rootView.getViewTreeObserver(); const viewTreeObserver = rootView.getViewTreeObserver();
viewTreeObserver.removeOnGlobalLayoutListener(global.onGlobalLayoutListener); viewTreeObserver.removeOnGlobalLayoutListener(global.onGlobalLayoutListener);
}, },
@ -380,17 +381,6 @@ export class AndroidApplication extends ApplicationCommon implements IAndroidApp
const nativeApp = this.getNativeApplication(); const nativeApp = this.getNativeApplication();
this.init(nativeApp); 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 _startActivity: androidx.appcompat.app.AppCompatActivity;
@ -412,10 +402,34 @@ export class AndroidApplication extends ApplicationCommon implements IAndroidApp
this._foregroundActivity = value; this._foregroundActivity = value;
} }
/**
* @deprecated Use `Application.suspended` instead.
*/
get paused(): boolean {
return this.suspended;
}
/**
* @deprecated Use `Application.inBackground` instead.
*/
get backgrounded(): boolean {
return this.inBackground;
}
/**
* @deprecated Use `Utils.android.getApplicationContext()` instead.
*/
get context() { get context() {
return this.nativeApp.getApplicationContext(); return this.nativeApp.getApplicationContext();
} }
/**
* @deprecated Use `Utils.android.getPackageName()` instead.
*/
get packageName() {
return this.nativeApp.getPackageName();
}
public registerBroadcastReceiver( public registerBroadcastReceiver(
intentFilter: string, intentFilter: string,
onReceiveCallback: ( onReceiveCallback: (

View File

@ -1,12 +1,9 @@
import { initAccessibilityCssHelper } from '../accessibility/accessibility-css-helper';
import { initAccessibilityFontScale } from '../accessibility/font-scale';
import { profile } from '../profiling'; import { profile } from '../profiling';
import { View } from '../ui'; import { View } from '../ui';
import { Builder } from '../ui/builder';
import { IOSHelper } from '../ui/core/view/view-helper'; import { IOSHelper } from '../ui/core/view/view-helper';
import { NavigationEntry } from '../ui/frame/frame-interfaces'; import { NavigationEntry } from '../ui/frame/frame-interfaces';
import * as Utils from '../utils'; import * as Utils from '../utils';
import type { iOSApplication as IiOSApplication } from './'; import type { iOSApplication as IiOSApplication } from './application';
import { ApplicationCommon } from './application-common'; import { ApplicationCommon } from './application-common';
import { import {
ApplicationEventData, ApplicationEventData,
@ -35,7 +32,7 @@ class CADisplayLinkTarget extends NSObject {
owner.notify(<ApplicationEventData>{ owner.notify(<ApplicationEventData>{
eventName: owner.displayedEvent, eventName: owner.displayedEvent,
object: owner, object: owner,
ios: owner.ios, ios: UIApplication.sharedApplication,
}); });
owner.displayedLinkTarget = null; owner.displayedLinkTarget = null;
owner.displayedLink = null; owner.displayedLink = null;
@ -130,8 +127,6 @@ export class iOSApplication extends ApplicationCommon implements IiOSApplication
} }
run(entry?: string | NavigationEntry): void { run(entry?: string | NavigationEntry): void {
console.log('run in iOSApplication', entry);
this.mainEntry = typeof entry === 'string' ? { moduleName: entry } : entry; this.mainEntry = typeof entry === 'string' ? { moduleName: entry } : entry;
this.started = true; this.started = true;
@ -140,9 +135,6 @@ export class iOSApplication extends ApplicationCommon implements IiOSApplication
} else { } else {
this.runAsMainApp(); this.runAsMainApp();
} }
initAccessibilityCssHelper();
initAccessibilityFontScale();
} }
private runAsMainApp() { private runAsMainApp() {
@ -195,31 +187,10 @@ export class iOSApplication extends ApplicationCommon implements IiOSApplication
visibleVC.presentViewControllerAnimatedCompletion(controller, true, null); visibleVC.presentViewControllerAnimatedCompletion(controller, true, null);
} }
// this.setRootViewsSystemAppearanceCssClass(rootView); this.initRootView();
this.notifyAppStarted(); 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 { private getViewController(rootView: View): UIViewController {
let viewController: UIViewController = rootView.viewController || rootView.ios; let viewController: UIViewController = rootView.viewController || rootView.ios;
@ -362,8 +333,8 @@ export class iOSApplication extends ApplicationCommon implements IiOSApplication
protected getSystemAppearance(): 'light' | 'dark' { protected getSystemAppearance(): 'light' | 'dark' {
// userInterfaceStyle is available on UITraitCollection since iOS 12. // userInterfaceStyle is available on UITraitCollection since iOS 12.
if (Utils.ios.MajorVersion <= 11) { if (Utils.ios.MajorVersion <= 11 || !this.rootController) {
return undefined; return null;
} }
const userInterfaceStyle = this.rootController.traitCollection.userInterfaceStyle; const userInterfaceStyle = this.rootController.traitCollection.userInterfaceStyle;
@ -399,25 +370,16 @@ export class iOSApplication extends ApplicationCommon implements IiOSApplication
} }
private notifyAppStarted(notification?: NSNotification) { private notifyAppStarted(notification?: NSNotification) {
const args: LaunchEventData = { const root = this.notifyLaunch({
eventName: this.launchEvent,
object: this,
ios: ios:
notification?.userInfo?.objectForKey( notification?.userInfo?.objectForKey(
'UIApplicationLaunchOptionsLocalNotificationKey' 'UIApplicationLaunchOptionsLocalNotificationKey'
) || null, ) ?? null,
};
this.notify(args);
this.notify(<LoadAppCSSEventData>{
eventName: 'loadAppCss',
object: this,
cssFile: this.getCssFileName(),
}); });
if (this._window) { if (this._window) {
if (args.root !== null && !NativeScriptEmbedder.sharedInstance().delegate) { if (root !== null && !NativeScriptEmbedder.sharedInstance().delegate) {
this.setWindowContent(args.root); this.setWindowContent(root);
} }
} else { } else {
this._window = UIApplication.sharedApplication.keyWindow; this._window = UIApplication.sharedApplication.keyWindow;
@ -442,12 +404,12 @@ export class iOSApplication extends ApplicationCommon implements IiOSApplication
const haveController = this._window.rootViewController !== null; const haveController = this._window.rootViewController !== null;
this._window.rootViewController = controller; this._window.rootViewController = controller;
this.setRootViewsSystemAppearanceCssClass(rootView);
if (!haveController) { if (!haveController) {
this._window.makeKeyAndVisible(); this._window.makeKeyAndVisible();
} }
this.initRootView();
rootView.on(IOSHelper.traitCollectionColorAppearanceChangedEvent, () => { rootView.on(IOSHelper.traitCollectionColorAppearanceChangedEvent, () => {
const userInterfaceStyle = controller.traitCollection.userInterfaceStyle; const userInterfaceStyle = controller.traitCollection.userInterfaceStyle;
const newSystemAppearance = this.getSystemAppearanceValue(userInterfaceStyle); const newSystemAppearance = this.getSystemAppearanceValue(userInterfaceStyle);
@ -474,47 +436,29 @@ export class iOSApplication extends ApplicationCommon implements IiOSApplication
@profile @profile
private didBecomeActive(notification: NSNotification) { private didBecomeActive(notification: NSNotification) {
// const ios = UIApplication.sharedApplication; const additionalData = {
// const object = this; ios: UIApplication.sharedApplication,
// setInBackground(false); };
// setSuspended(false); this.setInBackground(false, additionalData);
this.notify(<ApplicationEventData>{ this.setSuspended(false, additionalData);
eventName: this.resumeEvent,
object: this, const rootView = this._rootView;
ios: this.ios, if (rootView && !rootView.isLoaded) {
}); rootView.callLoaded();
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) { private didEnterBackground(notification: NSNotification) {
// const ios = UIApplication.sharedApplication; const additionalData = {
// const object = this; ios: UIApplication.sharedApplication,
// setInBackground(true); };
// setSuspended(true); this.setInBackground(true, additionalData);
this.setSuspended(true, additionalData);
this.notify(<ApplicationEventData>{ const rootView = this._rootView;
eventName: this.suspendEvent, if (rootView && rootView.isLoaded) {
object: this, rootView.callUnloaded();
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) { private willTerminate(notification: NSNotification) {

View File

@ -14,9 +14,6 @@ import {
AndroidActivityRequestPermissionsEventData, AndroidActivityRequestPermissionsEventData,
AndroidActivityResultEventData, AndroidActivityResultEventData,
Application, Application,
ApplicationEventData,
LaunchEventData,
LoadAppCSSEventData,
} from '../../application'; } from '../../application';
import { _stack, FrameBase, NavigationType } from './frame-common'; import { _stack, FrameBase, NavigationType } from './frame-common';
@ -35,17 +32,9 @@ import {
addNativeTransitionListener, addNativeTransitionListener,
} from './fragment.transitions'; } from './fragment.transitions';
// TODO: Remove this and get it from global to decouple builder for angular
import { Builder } from '../builder';
import { CSSUtils } from '../../css/system-classes';
import { Device } from '../../platform';
import { profile } from '../../profiling'; import { profile } from '../../profiling';
import { ad } from '../../utils/native-helper'; import { android as androidUtils } from '../../utils/native-helper';
import type { ExpandedEntry } from './fragment.transitions.android'; import type { ExpandedEntry } from './fragment.transitions.android';
import {
SharedTransition,
SharedTransitionAnimationType,
} from '../transition/shared-transition';
import { import {
AndroidActivityBackPressedEventData, AndroidActivityBackPressedEventData,
AndroidActivityNewIntentEventData, AndroidActivityNewIntentEventData,
@ -53,8 +42,6 @@ import {
export * from './frame-common'; export * from './frame-common';
const ANDROID_PLATFORM = 'android';
const INTENT_EXTRA = 'com.tns.activity'; const INTENT_EXTRA = 'com.tns.activity';
const ROOT_VIEW_ID_EXTRA = 'com.tns.activity.rootViewId'; const ROOT_VIEW_ID_EXTRA = 'com.tns.activity.rootViewId';
const FRAMEID = '_frameId'; const FRAMEID = '_frameId';
@ -123,7 +110,7 @@ export class Frame extends FrameBase {
} }
public static reloadPage(context?: ModuleContext): void { public static reloadPage(context?: ModuleContext): void {
const activity = ad.getCurrentActivity(); const activity = androidUtils.getCurrentActivity();
const callbacks: AndroidActivityCallbacks = activity[CALLBACKS]; const callbacks: AndroidActivityCallbacks = activity[CALLBACKS];
if (callbacks) { if (callbacks) {
const rootView: View = callbacks.getRootView(); const rootView: View = callbacks.getRootView();
@ -180,7 +167,7 @@ export class Frame extends FrameBase {
// _onAttachedToWindow called from OS again after it was detach // _onAttachedToWindow called from OS again after it was detach
// still happens with androidx.fragment:1.3.2 // still happens with androidx.fragment:1.3.2
const activity = ad.getCurrentActivity(); const activity = androidUtils.getCurrentActivity();
const lifecycleState = const lifecycleState =
activity?.getLifecycle?.()?.getCurrentState() || activity?.getLifecycle?.()?.getCurrentState() ||
androidx.lifecycle.Lifecycle.State.CREATED; androidx.lifecycle.Lifecycle.State.CREATED;
@ -1413,13 +1400,11 @@ class ActivityCallbacksImplementation implements AndroidActivityCallbacks {
// and raising the application resume event there causes issues like // and raising the application resume event there causes issues like
// https://github.com/NativeScript/NativeScript/issues/6708 // https://github.com/NativeScript/NativeScript/issues/6708
if ((<any>activity).isNativeScriptActivity) { if ((<any>activity).isNativeScriptActivity) {
Application.setSuspended(false); Application.setSuspended(false, {
const args = <ApplicationEventData>{ // todo: deprecate in favor of using event.activity instead.
eventName: Application.resumeEvent,
object: Application.android,
android: activity, android: activity,
}; activity,
Application.notify(args); });
} }
} }
@ -1585,52 +1570,21 @@ class ActivityCallbacksImplementation implements AndroidActivityCallbacks {
); );
} }
const intent = activity.getIntent();
rootView = Application.createRootView(rootView, fireLaunchEvent, {
// todo: deprecate in favor of args.intent?
android: intent,
intent,
savedInstanceState,
});
if (!rootView) { if (!rootView) {
const mainEntry = Application.getMainEntry(); // no root view created
const intent = activity.getIntent(); return;
// useful for integrations that would like to set rootView asynchronously after app launch
let shouldRootViewBeEmpty = false;
if (fireLaunchEvent) {
// entry point for Angular and Vue frameworks
rootView = notifyLaunch(intent, <any>savedInstanceState, null);
shouldRootViewBeEmpty = rootView === null;
}
if (!rootView) {
if (shouldRootViewBeEmpty) {
return;
}
// entry point for NS Core
if (!mainEntry) {
// Also handles scenarios with Angular and Vue where the notifyLaunch didn't return a root view.
throw new Error(
'Main entry is missing. App cannot be started. Verify app bootstrap.'
);
}
rootView = Builder.createViewFromEntry(mainEntry);
}
this._rootView = rootView;
activityRootViewsMap.set(rootView._domId, new WeakRef(rootView));
const deviceType = Device.deviceType.toLowerCase();
CSSUtils.pushToSystemCssClasses(`${CSSUtils.CLASS_PREFIX}${ANDROID_PLATFORM}`);
CSSUtils.pushToSystemCssClasses(`${CSSUtils.CLASS_PREFIX}${deviceType}`);
CSSUtils.pushToSystemCssClasses(
`${CSSUtils.CLASS_PREFIX}${Application.android.orientation}`
);
CSSUtils.pushToSystemCssClasses(
`${CSSUtils.CLASS_PREFIX}${Application.android.systemAppearance}`
);
this._rootView.cssClasses.add(CSSUtils.ROOT_VIEW_CSS_CLASS);
const rootViewCssClasses = CSSUtils.getSystemCssClasses();
rootViewCssClasses.forEach((c) => this._rootView.cssClasses.add(c));
} }
activityRootViewsMap.set(rootView._domId, new WeakRef(rootView));
// setup view as styleScopeHost // setup view as styleScopeHost
rootView._setupAsRootView(activity); rootView._setupAsRootView(activity);
@ -1638,34 +1592,14 @@ class ActivityCallbacksImplementation implements AndroidActivityCallbacks {
rootView.nativeViewProtected, rootView.nativeViewProtected,
new org.nativescript.widgets.CommonLayoutParams() new org.nativescript.widgets.CommonLayoutParams()
); );
this._rootView = rootView;
// sets root classes once rootView is ready...
Application.initRootView();
} }
} }
const notifyLaunch = profile(
'notifyLaunch',
function notifyLaunch(
intent: android.content.Intent,
savedInstanceState: android.os.Bundle
): View {
const launchArgs: LaunchEventData = {
eventName: Application.launchEvent,
object: Application,
android: intent,
savedInstanceState,
};
Application.notify(launchArgs);
Application.notify(<LoadAppCSSEventData>{
eventName: 'loadAppCss',
object: Application,
android: Application.android,
cssFile: Application.getCssFileName(),
});
return launchArgs.root;
}
);
export function setActivityCallbacks( export function setActivityCallbacks(
activity: androidx.appcompat.app.AppCompatActivity activity: androidx.appcompat.app.AppCompatActivity
): void { ): void {

View File

@ -5,7 +5,7 @@ import { Page } from '../page';
import { View } from '../core/view'; import { View } from '../core/view';
import { IOSHelper } from '../core/view/view-helper'; import { IOSHelper } from '../core/view/view-helper';
import { profile } from '../../profiling'; import { profile } from '../../profiling';
import { CORE_ANIMATION_DEFAULTS, iOSNativeHelper, layout } from '../../utils'; import { CORE_ANIMATION_DEFAULTS, ios as iOSUtils, layout } from '../../utils';
import { Trace } from '../../trace'; import { Trace } from '../../trace';
import type { PageTransition } from '../transition/page-transition'; import type { PageTransition } from '../transition/page-transition';
import { SlideTransition } from '../transition/slide-transition'; import { SlideTransition } from '../transition/slide-transition';
@ -14,7 +14,7 @@ import { SharedTransition } from '../transition/shared-transition';
export * from './frame-common'; export * from './frame-common';
const majorVersion = iOSNativeHelper.MajorVersion; const majorVersion = iOSUtils.MajorVersion;
const ENTRY = '_entry'; const ENTRY = '_entry';
const DELEGATE = '_delegate'; const DELEGATE = '_delegate';
@ -82,13 +82,17 @@ export class Frame extends FrameBase {
navDepth = -1; navDepth = -1;
} }
const isReplace = this._executingContext && this._executingContext.navigationType === NavigationType.replace; const isReplace =
this._executingContext &&
this._executingContext.navigationType === NavigationType.replace;
if (!isReplace) { if (!isReplace) {
navDepth++; navDepth++;
} }
let navigationTransition: NavigationTransition; let navigationTransition: NavigationTransition;
const animated = this.currentPage ? this._getIsAnimatedNavigation(backstackEntry.entry) : false; const animated = this.currentPage
? this._getIsAnimatedNavigation(backstackEntry.entry)
: false;
if (animated) { if (animated) {
navigationTransition = this._getNavigationTransition(backstackEntry.entry); navigationTransition = this._getNavigationTransition(backstackEntry.entry);
if (navigationTransition) { if (navigationTransition) {
@ -102,7 +106,9 @@ export class Frame extends FrameBase {
const nativeTransition = _getNativeTransition(navigationTransition, true); const nativeTransition = _getNativeTransition(navigationTransition, true);
if (!nativeTransition && navigationTransition) { if (!nativeTransition && navigationTransition) {
if (!this._animatedDelegate) { if (!this._animatedDelegate) {
this._animatedDelegate = <UINavigationControllerDelegate>UINavigationControllerAnimatedDelegate.initWithOwner(new WeakRef(this)); this._animatedDelegate = <UINavigationControllerDelegate>(
UINavigationControllerAnimatedDelegate.initWithOwner(new WeakRef(this))
);
} }
this._ios.controller.delegate = this._animatedDelegate; this._ios.controller.delegate = this._animatedDelegate;
viewController[DELEGATE] = this._animatedDelegate; viewController[DELEGATE] = this._animatedDelegate;
@ -128,7 +134,12 @@ export class Frame extends FrameBase {
if (!animated && majorVersion > 10) { if (!animated && majorVersion > 10) {
// Reset back button title before pushing view controller to prevent // Reset back button title before pushing view controller to prevent
// displaying default 'back' title (when NavigaitonButton custom title is set). // displaying default 'back' title (when NavigaitonButton custom title is set).
const barButtonItem = UIBarButtonItem.alloc().initWithTitleStyleTargetAction('', UIBarButtonItemStyle.Plain, null, null); const barButtonItem = UIBarButtonItem.alloc().initWithTitleStyleTargetAction(
'',
UIBarButtonItemStyle.Plain,
null,
null
);
viewController.navigationItem.backBarButtonItem = barButtonItem; viewController.navigationItem.backBarButtonItem = barButtonItem;
} }
@ -138,9 +149,16 @@ export class Frame extends FrameBase {
this._updateActionBar(backstackEntry.resolvedPage, true); this._updateActionBar(backstackEntry.resolvedPage, true);
// Core defaults modalPresentationStyle to 1 for standard frame navigation // Core defaults modalPresentationStyle to 1 for standard frame navigation
// for all others, it's modal presentation // for all others, it's modal presentation
this.pushViewControllerAnimated(viewController, animated, this._ios?.controller?.modalPresentationStyle !== 1); this.pushViewControllerAnimated(
viewController,
animated,
this._ios?.controller?.modalPresentationStyle !== 1
);
if (Trace.isEnabled()) { if (Trace.isEnabled()) {
Trace.write(`${this}.pushViewControllerAnimated(${viewController}, ${animated}); depth = ${navDepth}`, Trace.categories.Navigation); Trace.write(
`${this}.pushViewControllerAnimated(${viewController}, ${animated}); depth = ${navDepth}`,
Trace.categories.Navigation
);
} }
return; return;
@ -160,7 +178,10 @@ export class Frame extends FrameBase {
this._ios.controller.setViewControllersAnimated(newControllers, animated); this._ios.controller.setViewControllersAnimated(newControllers, animated);
if (Trace.isEnabled()) { if (Trace.isEnabled()) {
Trace.write(`${this}.setViewControllersAnimated([${viewController}], ${animated}); depth = ${navDepth}`, Trace.categories.Navigation); Trace.write(
`${this}.setViewControllersAnimated([${viewController}], ${animated}); depth = ${navDepth}`,
Trace.categories.Navigation
);
} }
return; return;
@ -169,7 +190,9 @@ export class Frame extends FrameBase {
// We should hide the current entry from the back stack. // We should hide the current entry from the back stack.
// This is the case for HMR when NavigationType.replace. // This is the case for HMR when NavigationType.replace.
if (!Frame._isEntryBackstackVisible(this._currentEntry) || isReplace) { if (!Frame._isEntryBackstackVisible(this._currentEntry) || isReplace) {
const newControllers = NSMutableArray.alloc<UIViewController>().initWithArray(this._ios.controller.viewControllers); const newControllers = NSMutableArray.alloc<UIViewController>().initWithArray(
this._ios.controller.viewControllers
);
if (newControllers.count === 0) { if (newControllers.count === 0) {
throw new Error('Wrong controllers count.'); throw new Error('Wrong controllers count.');
} }
@ -187,7 +210,10 @@ export class Frame extends FrameBase {
// replace the controllers instead of pushing directly // replace the controllers instead of pushing directly
this._ios.controller.setViewControllersAnimated(newControllers, animated); this._ios.controller.setViewControllersAnimated(newControllers, animated);
if (Trace.isEnabled()) { if (Trace.isEnabled()) {
Trace.write(`${this}.setViewControllersAnimated([originalControllers - lastController + ${viewController}], ${animated}); depth = ${navDepth}`, Trace.categories.Navigation); Trace.write(
`${this}.setViewControllersAnimated([originalControllers - lastController + ${viewController}], ${animated}); depth = ${navDepth}`,
Trace.categories.Navigation
);
} }
return; return;
@ -196,11 +222,18 @@ export class Frame extends FrameBase {
// General case. // General case.
this._ios.controller.pushViewControllerAnimated(viewController, animated); this._ios.controller.pushViewControllerAnimated(viewController, animated);
if (Trace.isEnabled()) { if (Trace.isEnabled()) {
Trace.write(`${this}.pushViewControllerAnimated(${viewController}, ${animated}); depth = ${navDepth}`, Trace.categories.Navigation); Trace.write(
`${this}.pushViewControllerAnimated(${viewController}, ${animated}); depth = ${navDepth}`,
Trace.categories.Navigation
);
} }
} }
private pushViewControllerAnimated(viewController: UIViewController, animated: boolean, isModal: boolean) { private pushViewControllerAnimated(
viewController: UIViewController,
animated: boolean,
isModal: boolean
) {
const transitionCoordinator = this._ios.controller.transitionCoordinator; const transitionCoordinator = this._ios.controller.transitionCoordinator;
if (!isModal && transitionCoordinator) { if (!isModal && transitionCoordinator) {
transitionCoordinator.animateAlongsideTransitionCompletion(null, () => { transitionCoordinator.animateAlongsideTransitionCompletion(null, () => {
@ -217,12 +250,17 @@ export class Frame extends FrameBase {
navDepth = backstackEntry[NAV_DEPTH]; navDepth = backstackEntry[NAV_DEPTH];
const controller = backstackEntry.resolvedPage.ios; const controller = backstackEntry.resolvedPage.ios;
const animated = this._currentEntry ? this._getIsAnimatedNavigation(this._currentEntry.entry) : false; const animated = this._currentEntry
? this._getIsAnimatedNavigation(this._currentEntry.entry)
: false;
this._updateActionBar(backstackEntry.resolvedPage); this._updateActionBar(backstackEntry.resolvedPage);
if (Trace.isEnabled()) { if (Trace.isEnabled()) {
Trace.write(`${this}.popToViewControllerAnimated(${controller}, ${animated}); depth = ${navDepth}`, Trace.categories.Navigation); Trace.write(
`${this}.popToViewControllerAnimated(${controller}, ${animated}); depth = ${navDepth}`,
Trace.categories.Navigation
);
} }
this._ios.controller.popToViewControllerAnimated(controller, animated); this._ios.controller.popToViewControllerAnimated(controller, animated);
@ -252,7 +290,8 @@ export class Frame extends FrameBase {
} }
if (this._ios.controller?.navigationBar) { if (this._ios.controller?.navigationBar) {
this._ios.controller.navigationBar.userInteractionEnabled = this.navigationQueueIsEmpty(); this._ios.controller.navigationBar.userInteractionEnabled =
this.navigationQueueIsEmpty();
} }
if (needsPageLayout && page) { if (needsPageLayout && page) {
@ -282,7 +321,9 @@ export class Frame extends FrameBase {
if (page && page.actionBarHidden !== undefined) { if (page && page.actionBarHidden !== undefined) {
newValue = !page.actionBarHidden; newValue = !page.actionBarHidden;
} else { } else {
newValue = this.ios.controller.viewControllers.count > 1 || (page && page.actionBar && !page.actionBar._isEmpty()); newValue =
this.ios.controller.viewControllers.count > 1 ||
(page && page.actionBar && !page.actionBar._isEmpty());
} }
newValue = !!newValue; newValue = !!newValue;
@ -320,7 +361,12 @@ export class Frame extends FrameBase {
this.setMeasuredDimension(widthAndState, heightAndState); this.setMeasuredDimension(widthAndState, heightAndState);
} }
public layoutNativeView(left: number, top: number, right: number, bottom: number): void { public layoutNativeView(
left: number,
top: number,
right: number,
bottom: number
): void {
// //
} }
@ -392,18 +438,28 @@ class TransitionDelegate extends NSObject {
} }
@NativeClass @NativeClass
class UINavigationControllerAnimatedDelegate extends NSObject implements UINavigationControllerDelegate { class UINavigationControllerAnimatedDelegate
extends NSObject
implements UINavigationControllerDelegate
{
public static ObjCProtocols = [UINavigationControllerDelegate]; public static ObjCProtocols = [UINavigationControllerDelegate];
owner: WeakRef<Frame>; owner: WeakRef<Frame>;
transition: PageTransition; transition: PageTransition;
static initWithOwner(owner: WeakRef<Frame>) { static initWithOwner(owner: WeakRef<Frame>) {
const delegate = <UINavigationControllerAnimatedDelegate>UINavigationControllerAnimatedDelegate.new(); const delegate = <UINavigationControllerAnimatedDelegate>(
UINavigationControllerAnimatedDelegate.new()
);
delegate.owner = owner; delegate.owner = owner;
return delegate; return delegate;
} }
navigationControllerAnimationControllerForOperationFromViewControllerToViewController(navigationController: UINavigationController, operation: number, fromVC: UIViewController, toVC: UIViewController): UIViewControllerAnimatedTransitioning { navigationControllerAnimationControllerForOperationFromViewControllerToViewController(
navigationController: UINavigationController,
operation: number,
fromVC: UIViewController,
toVC: UIViewController
): UIViewControllerAnimatedTransitioning {
let viewController: UIViewController; let viewController: UIViewController;
switch (operation) { switch (operation) {
case UINavigationControllerOperation.Push: case UINavigationControllerOperation.Push:
@ -424,7 +480,12 @@ class UINavigationControllerAnimatedDelegate extends NSObject implements UINavig
} }
if (Trace.isEnabled()) { if (Trace.isEnabled()) {
Trace.write(`UINavigationControllerImpl.navigationControllerAnimationControllerForOperationFromViewControllerToViewController(${operation}, ${fromVC}, ${toVC}), transition: ${JSON.stringify(navigationTransition)}`, Trace.categories.NativeLifecycle); Trace.write(
`UINavigationControllerImpl.navigationControllerAnimationControllerForOperationFromViewControllerToViewController(${operation}, ${fromVC}, ${toVC}), transition: ${JSON.stringify(
navigationTransition
)}`,
Trace.categories.NativeLifecycle
);
} }
this.transition = navigationTransition.instance; this.transition = navigationTransition.instance;
@ -434,7 +495,11 @@ class UINavigationControllerAnimatedDelegate extends NSObject implements UINavig
const name = navigationTransition.name.toLowerCase(); const name = navigationTransition.name.toLowerCase();
if (name.indexOf('slide') === 0) { if (name.indexOf('slide') === 0) {
const direction = name.substring('slide'.length) || 'left'; //Extract the direction from the string const direction = name.substring('slide'.length) || 'left'; //Extract the direction from the string
this.transition = new SlideTransition(direction, navigationTransition.duration, curve); this.transition = new SlideTransition(
direction,
navigationTransition.duration,
curve
);
} else if (name === 'fade') { } else if (name === 'fade') {
this.transition = new FadeTransition(navigationTransition.duration, curve); this.transition = new FadeTransition(navigationTransition.duration, curve);
} }
@ -442,12 +507,20 @@ class UINavigationControllerAnimatedDelegate extends NSObject implements UINavig
} }
if (this.transition?.iosNavigatedController) { if (this.transition?.iosNavigatedController) {
return this.transition.iosNavigatedController(navigationController, operation, fromVC, toVC); return this.transition.iosNavigatedController(
navigationController,
operation,
fromVC,
toVC
);
} }
return null; return null;
} }
navigationControllerInteractionControllerForAnimationController(navigationController: UINavigationController, animationController: UIViewControllerAnimatedTransitioning): UIViewControllerInteractiveTransitioning { navigationControllerInteractionControllerForAnimationController(
navigationController: UINavigationController,
animationController: UIViewControllerAnimatedTransitioning
): UIViewControllerInteractiveTransitioning {
const owner = this.owner?.deref(); const owner = this.owner?.deref();
if (owner) { if (owner) {
const state = SharedTransition.getState(owner.transitionId); const state = SharedTransition.getState(owner.transitionId);
@ -496,8 +569,15 @@ class UINavigationControllerImpl extends UINavigationController {
} }
} }
private animateWithDuration(navigationTransition: NavigationTransition, nativeTransition: UIViewAnimationTransition, transitionType: string, baseCallback: Function): void { private animateWithDuration(
const duration = navigationTransition.duration ? navigationTransition.duration / 1000 : CORE_ANIMATION_DEFAULTS.duration; navigationTransition: NavigationTransition,
nativeTransition: UIViewAnimationTransition,
transitionType: string,
baseCallback: Function
): void {
const duration = navigationTransition.duration
? navigationTransition.duration / 1000
: CORE_ANIMATION_DEFAULTS.duration;
const curve = _getNativeCurve(navigationTransition); const curve = _getNativeCurve(navigationTransition);
const transitionTraced = Trace.isCategorySet(Trace.categories.Transition); const transitionTraced = Trace.isCategorySet(Trace.categories.Transition);
@ -521,10 +601,18 @@ class UINavigationControllerImpl extends UINavigationController {
} }
@profile @profile
public pushViewControllerAnimated(viewController: UIViewController, animated: boolean): void { public pushViewControllerAnimated(
viewController: UIViewController,
animated: boolean
): void {
const navigationTransition = <NavigationTransition>viewController[TRANSITION]; const navigationTransition = <NavigationTransition>viewController[TRANSITION];
if (Trace.isEnabled()) { if (Trace.isEnabled()) {
Trace.write(`UINavigationControllerImpl.pushViewControllerAnimated(${viewController}, ${animated}); transition: ${JSON.stringify(navigationTransition)}`, Trace.categories.NativeLifecycle); Trace.write(
`UINavigationControllerImpl.pushViewControllerAnimated(${viewController}, ${animated}); transition: ${JSON.stringify(
navigationTransition
)}`,
Trace.categories.NativeLifecycle
);
} }
const nativeTransition = _getNativeTransition(navigationTransition, true); const nativeTransition = _getNativeTransition(navigationTransition, true);
@ -540,12 +628,20 @@ class UINavigationControllerImpl extends UINavigationController {
} }
@profile @profile
public setViewControllersAnimated(viewControllers: NSArray<any>, animated: boolean): void { public setViewControllersAnimated(
viewControllers: NSArray<any>,
animated: boolean
): void {
const viewController = viewControllers.lastObject; const viewController = viewControllers.lastObject;
const navigationTransition = <NavigationTransition>viewController[TRANSITION]; const navigationTransition = <NavigationTransition>viewController[TRANSITION];
if (Trace.isEnabled()) { if (Trace.isEnabled()) {
Trace.write(`UINavigationControllerImpl.setViewControllersAnimated(${viewControllers}, ${animated}); transition: ${JSON.stringify(navigationTransition)}`, Trace.categories.NativeLifecycle); Trace.write(
`UINavigationControllerImpl.setViewControllersAnimated(${viewControllers}, ${animated}); transition: ${JSON.stringify(
navigationTransition
)}`,
Trace.categories.NativeLifecycle
);
} }
const nativeTransition = _getNativeTransition(navigationTransition, true); const nativeTransition = _getNativeTransition(navigationTransition, true);
@ -564,7 +660,12 @@ class UINavigationControllerImpl extends UINavigationController {
const lastViewController = this.viewControllers.lastObject; const lastViewController = this.viewControllers.lastObject;
const navigationTransition = <NavigationTransition>lastViewController[TRANSITION]; const navigationTransition = <NavigationTransition>lastViewController[TRANSITION];
if (Trace.isEnabled()) { if (Trace.isEnabled()) {
Trace.write(`UINavigationControllerImpl.popViewControllerAnimated(${animated}); transition: ${JSON.stringify(navigationTransition)}`, Trace.categories.NativeLifecycle); Trace.write(
`UINavigationControllerImpl.popViewControllerAnimated(${animated}); transition: ${JSON.stringify(
navigationTransition
)}`,
Trace.categories.NativeLifecycle
);
} }
if (navigationTransition && navigationTransition.name === NON_ANIMATED_TRANSITION) { if (navigationTransition && navigationTransition.name === NON_ANIMATED_TRANSITION) {
@ -584,11 +685,19 @@ class UINavigationControllerImpl extends UINavigationController {
return null; return null;
} }
public popToViewControllerAnimated(viewController: UIViewController, animated: boolean): NSArray<UIViewController> { public popToViewControllerAnimated(
viewController: UIViewController,
animated: boolean
): NSArray<UIViewController> {
const lastViewController = this.viewControllers.lastObject; const lastViewController = this.viewControllers.lastObject;
const navigationTransition = <NavigationTransition>lastViewController[TRANSITION]; const navigationTransition = <NavigationTransition>lastViewController[TRANSITION];
if (Trace.isEnabled()) { if (Trace.isEnabled()) {
Trace.write(`UINavigationControllerImpl.popToViewControllerAnimated(${viewController}, ${animated}); transition: ${JSON.stringify(navigationTransition)}`, Trace.categories.NativeLifecycle); Trace.write(
`UINavigationControllerImpl.popToViewControllerAnimated(${viewController}, ${animated}); transition: ${JSON.stringify(
navigationTransition
)}`,
Trace.categories.NativeLifecycle
);
} }
if (navigationTransition && navigationTransition.name === NON_ANIMATED_TRANSITION) { if (navigationTransition && navigationTransition.name === NON_ANIMATED_TRANSITION) {
@ -614,7 +723,13 @@ class UINavigationControllerImpl extends UINavigationController {
if (majorVersion >= 13) { if (majorVersion >= 13) {
const owner = this._owner?.deref?.(); const owner = this._owner?.deref?.();
if (owner && this.traitCollection.hasDifferentColorAppearanceComparedToTraitCollection && this.traitCollection.hasDifferentColorAppearanceComparedToTraitCollection(previousTraitCollection)) { if (
owner &&
this.traitCollection.hasDifferentColorAppearanceComparedToTraitCollection &&
this.traitCollection.hasDifferentColorAppearanceComparedToTraitCollection(
previousTraitCollection
)
) {
owner.notify({ owner.notify({
eventName: IOSHelper.traitCollectionColorAppearanceChangedEvent, eventName: IOSHelper.traitCollectionColorAppearanceChangedEvent,
object: owner, object: owner,
@ -624,7 +739,10 @@ class UINavigationControllerImpl extends UINavigationController {
} }
} }
function _getTransitionId(nativeTransition: UIViewAnimationTransition, transitionType: string): string { function _getTransitionId(
nativeTransition: UIViewAnimationTransition,
transitionType: string
): string {
let name; let name;
switch (nativeTransition) { switch (nativeTransition) {
case UIViewAnimationTransition.CurlDown: case UIViewAnimationTransition.CurlDown:
@ -647,19 +765,30 @@ function _getTransitionId(nativeTransition: UIViewAnimationTransition, transitio
return `${name} ${transitionType}`; return `${name} ${transitionType}`;
} }
function _getNativeTransition(navigationTransition: NavigationTransition, push: boolean): UIViewAnimationTransition { function _getNativeTransition(
navigationTransition: NavigationTransition,
push: boolean
): UIViewAnimationTransition {
if (navigationTransition && navigationTransition.name) { if (navigationTransition && navigationTransition.name) {
switch (navigationTransition.name.toLowerCase()) { switch (navigationTransition.name.toLowerCase()) {
case 'flip': case 'flip':
case 'flipright': case 'flipright':
return push ? UIViewAnimationTransition.FlipFromRight : UIViewAnimationTransition.FlipFromLeft; return push
? UIViewAnimationTransition.FlipFromRight
: UIViewAnimationTransition.FlipFromLeft;
case 'flipleft': case 'flipleft':
return push ? UIViewAnimationTransition.FlipFromLeft : UIViewAnimationTransition.FlipFromRight; return push
? UIViewAnimationTransition.FlipFromLeft
: UIViewAnimationTransition.FlipFromRight;
case 'curl': case 'curl':
case 'curlup': case 'curlup':
return push ? UIViewAnimationTransition.CurlUp : UIViewAnimationTransition.CurlDown; return push
? UIViewAnimationTransition.CurlUp
: UIViewAnimationTransition.CurlDown;
case 'curldown': case 'curldown':
return push ? UIViewAnimationTransition.CurlDown : UIViewAnimationTransition.CurlUp; return push
? UIViewAnimationTransition.CurlDown
: UIViewAnimationTransition.CurlUp;
} }
} }
@ -671,35 +800,50 @@ export function _getNativeCurve(transition: NavigationTransition): UIViewAnimati
switch (transition.curve) { switch (transition.curve) {
case 'easeIn': case 'easeIn':
if (Trace.isEnabled()) { if (Trace.isEnabled()) {
Trace.write('Transition curve resolved to UIViewAnimationCurve.EaseIn.', Trace.categories.Transition); Trace.write(
'Transition curve resolved to UIViewAnimationCurve.EaseIn.',
Trace.categories.Transition
);
} }
return UIViewAnimationCurve.EaseIn; return UIViewAnimationCurve.EaseIn;
case 'easeOut': case 'easeOut':
if (Trace.isEnabled()) { if (Trace.isEnabled()) {
Trace.write('Transition curve resolved to UIViewAnimationCurve.EaseOut.', Trace.categories.Transition); Trace.write(
'Transition curve resolved to UIViewAnimationCurve.EaseOut.',
Trace.categories.Transition
);
} }
return UIViewAnimationCurve.EaseOut; return UIViewAnimationCurve.EaseOut;
case 'easeInOut': case 'easeInOut':
if (Trace.isEnabled()) { if (Trace.isEnabled()) {
Trace.write('Transition curve resolved to UIViewAnimationCurve.EaseInOut.', Trace.categories.Transition); Trace.write(
'Transition curve resolved to UIViewAnimationCurve.EaseInOut.',
Trace.categories.Transition
);
} }
return UIViewAnimationCurve.EaseInOut; return UIViewAnimationCurve.EaseInOut;
case 'linear': case 'linear':
if (Trace.isEnabled()) { if (Trace.isEnabled()) {
Trace.write('Transition curve resolved to UIViewAnimationCurve.Linear.', Trace.categories.Transition); Trace.write(
'Transition curve resolved to UIViewAnimationCurve.Linear.',
Trace.categories.Transition
);
} }
return UIViewAnimationCurve.Linear; return UIViewAnimationCurve.Linear;
default: default:
if (Trace.isEnabled()) { if (Trace.isEnabled()) {
Trace.write('Transition curve resolved to original: ' + transition.curve, Trace.categories.Transition); Trace.write(
'Transition curve resolved to original: ' + transition.curve,
Trace.categories.Transition
);
} }
return transition.curve; return transition.curve;
@ -737,7 +881,10 @@ class iOSFrame implements iOSFrameDefinition {
public set showNavigationBar(value: boolean) { public set showNavigationBar(value: boolean) {
this._showNavigationBar = value; this._showNavigationBar = value;
if (this._controller) { if (this._controller) {
this._controller.setNavigationBarHiddenAnimated(!value, !this._disableNavBarAnimation); this._controller.setNavigationBarHiddenAnimated(
!value,
!this._disableNavBarAnimation
);
} }
} }

View File

@ -33,7 +33,7 @@ export function getResources() {
return contextResources; return contextResources;
} }
function getPackageName() { export function getPackageName() {
if (!packageName) { if (!packageName) {
packageName = getApplicationContext().getPackageName(); packageName = getApplicationContext().getPackageName();
} }

View File

@ -1,4 +1,4 @@
import { ad } from './native-helper'; import { android as androidHelper } from './native-helper';
export function dispatchToMainThread(func: () => void) { export function dispatchToMainThread(func: () => void) {
const runOnMainThread = (global as any).__runOnMainThread; const runOnMainThread = (global as any).__runOnMainThread;
@ -20,7 +20,7 @@ export function isMainThread(): boolean {
} }
export function dispatchToUIThread(func: () => void) { export function dispatchToUIThread(func: () => void) {
const activity: androidx.appcompat.app.AppCompatActivity = ad.getCurrentActivity(); const activity = androidHelper.getCurrentActivity();
if (activity && func) { if (activity && func) {
activity.runOnUiThread( activity.runOnUiThread(
new java.lang.Runnable({ new java.lang.Runnable({