diff --git a/application/application-common.ts b/application/application-common.ts index b1e97ef97..a305cff7b 100644 --- a/application/application-common.ts +++ b/application/application-common.ts @@ -3,6 +3,22 @@ import definition = require("application"); import fs = require("file-system"); import fileSystemAccess = require("file-system/file-system-access"); import styleScope = require("ui/styling/style-scope"); +import observable = require("data/observable"); + +var events = new observable.Observable(); +require("utils/module-merge").merge(events, exports); + +export var launchEvent = "launchEvent"; + +export var uncaughtErrorEvent = "uncaughtErrorEvent"; + +export var suspendEvent = "suspendEvent"; + +export var resumeEvent = "resumeEvent"; + +export var exitEvent = "exitEvent"; + +export var lowMemoryEvent = "lowMemoryEvent"; export var cssFile: string = "app.css" diff --git a/application/application.android.ts b/application/application.android.ts index a1eba504b..9de127b80 100644 --- a/application/application.android.ts +++ b/application/application.android.ts @@ -43,6 +43,9 @@ var initEvents = function () { if (exports.onExit) { exports.onExit(); } + + exports.notify({ eventName: dts.exitEvent, object: androidApp, android: activity }); + androidApp.startActivity = undefined; } @@ -58,6 +61,9 @@ var initEvents = function () { if (exports.onSuspend) { exports.onSuspend(); } + + exports.notify({ eventName: dts.suspendEvent, object: androidApp, android: activity }); + } if (androidApp.onActivityPaused) { @@ -69,6 +75,9 @@ var initEvents = function () { if (exports.onResume) { exports.onResume(); } + + exports.notify({ eventName: dts.resumeEvent, object: androidApp, android: activity }); + } if (androidApp.onActivityResumed) { @@ -183,6 +192,8 @@ class AndroidApplication implements dts.AndroidApplication { exports.onLaunch(intent); } + exports.notify({ eventName: dts.launchEvent, object: this, android: intent }); + /* In the onLaunch event we expect the following setup, which ensures a root frame: * var frame = require("ui/frame"); * var rootFrame = new frame.Frame(); @@ -224,6 +235,8 @@ global.__onUncaughtError = function (error: Error) { } exports.onUncaughtError(nsError); + + exports.notify({ eventName: dts.uncaughtErrorEvent, object: appModule.android, android: nsError }); } exports.start = function () { diff --git a/application/application.d.ts b/application/application.d.ts index 5f736b23d..f650ac148 100644 --- a/application/application.d.ts +++ b/application/application.d.ts @@ -3,6 +3,7 @@ */ declare module "application" { import cssSelector = require("ui/styling/css-selector"); + import observable = require("data/observable"); /** * An extended JavaScript Error which will have the nativeError property initialized in case the error is caused by executing platform-specific code. @@ -14,19 +15,64 @@ declare module "application" { nativeError: any; } - /** - * The main page path (without the file extension) for the application starting from the application root. - * For example if you have page called "main.js" in a folder called "subFolder" and your root folder is "app" you can specify mainModule like this: - * var application = require("application"); - * application.mainModule = "app/subFolder/main"; - * application.start(); - */ + /** + * String value used when hooking to launch event. + */ + export var launchEvent: string; + + /** + * String value used when hooking to uncaughtError event. + */ + export var uncaughtErrorEvent: string; + + /** + * String value used when hooking to suspend event. + */ + export var suspendEvent: string; + + /** + * String value used when hooking to resume event. + */ + export var resumeEvent: string; + + /** + * String value used when hooking to exitevent. + */ + export var exitEvent: string; + + /** + * String value used when hooking to lowMemory event. + */ + export var lowMemoryEvent: string; + + /** + * Event data containing information for the application events. + */ + export interface ApplicationEventData extends observable.EventData { + /** + * Gets the native iOS event arguments. Valid only when running on iOS. + */ + ios?: any; + + /** + * Gets the native Android event arguments. Valid only when running on Android. + */ + android?: any; + } + + /** + * The main page path (without the file extension) for the application starting from the application root. + * For example if you have page called "main.js" in a folder called "subFolder" and your root folder is "app" you can specify mainModule like this: + * var application = require("application"); + * application.mainModule = "app/subFolder/main"; + * application.start(); + */ export var mainModule: string; /** * An application level static resources. */ - export var resources: any; + export var resources: any; /** * The application level css file name (starting from the application root). Used to set css across all pages. @@ -44,14 +90,14 @@ declare module "application" { */ export function loadCss(): void; - /** - * Call this method to start the application. Important: All code after this method call will not be executed! - */ + /** + * Call this method to start the application. Important: All code after this method call will not be executed! + */ export function start(); - /** - * The main entry point event. This method is expected to use the root frame to navigate to the main application page. - */ + /** + * The main entry point event. This method is expected to use the root frame to navigate to the main application page. + */ export function onLaunch(context: any): void; /** @@ -62,136 +108,186 @@ declare module "application" { */ export function onUncaughtError(error: NativeScriptError): void; - /** - * This method will be called when the Application is suspended. - */ + /** + * This method will be called when the Application is suspended. + */ export function onSuspend(); - /** - * This method will be called when the Application is resumed after it has been suspended. - */ + /** + * This method will be called when the Application is resumed after it has been suspended. + */ export function onResume(); - /** - * This method will be called when the Application is about to exit. - */ + /** + * This method will be called when the Application is about to exitEvent. + */ export function onExit(); - /** - * This method will be called when there is low memory on the target device. - */ + /** + * This method will be called when there is low memory on the target device. + */ export function onLowMemory(); - /** - * This is the Android-specific application object instance. - * Encapsulates methods and properties specific to the Android platform. - * Will be undefined when TargetOS is iOS. - */ + /** + * A basic method signature to hook an event listener (shortcut alias to the addEventListener method). + * @param eventNames - String corresponding to events (e.g. "onLaunch"). Optionally could be used more events separated by `,` (e.g. "onLaunch", "onSuspend"). + * @param callback - Callback function which will be executed when event is raised. + * @param thisArg - An optional parameter which will be used as `this` context for callback execution. + */ + export function on(eventNames: string, callback: (data: any) => void, thisArg?: any); + + /** + * Notifies all the registered listeners for the event provided in the data.eventName. + * @param data The data associated with the event. + */ + export function notify(data: ApplicationEventData): void; + + /** + * Checks whether a listener is registered for the specified event name. + * @param eventName The name of the event to check for. + */ + export function hasListeners(eventName: string): boolean; + + /** + * This event is raised on application launchEvent. + */ + export function on(event: "onLaunch", callback: (args: any) => void, thisArg?: any); + + /** + * This event is raised when an uncaught error occurs while the application is running. + */ + export function on(event: "onUncaughtError", callback: (args: any) => void, thisArg?: any); + + /** + * This event is raised when the Application is suspended. + */ + export function on(event: "onSuspend", callback: (args: any) => void, thisArg?: any); + + /** + * This event is raised when the Application is resumed after it has been suspended. + */ + export function on(event: "onResume", callback: (args: any) => void, thisArg?: any); + + /** + * This event is raised when the Application is about to exitEvent. + */ + export function on(event: "onExit", callback: (args: any) => void, thisArg?: any); + + /** + * This event is raised when there is low memory on the target device. + */ + export function on(event: "onLowMemory", callback: (args: any) => void, thisArg?: any); + + /** + * This is the Android-specific application object instance. + * Encapsulates methods and properties specific to the Android platform. + * Will be undefined when TargetOS is iOS. + */ export var android: AndroidApplication; - /** - * This is the iOS-specific application object instance. - * Encapsulates methods and properties specific to the iOS platform. - * Will be undefined when TargetOS is Android. - */ + /** + * This is the iOS-specific application object instance. + * Encapsulates methods and properties specific to the iOS platform. + * Will be undefined when TargetOS is Android. + */ export var ios: iOSApplication; - /** - * The abstraction of an Android-specific application object. - */ + /** + * The abstraction of an Android-specific application object. + */ export interface AndroidApplication { - /** - * The [android Application](http://developer.android.com/reference/android/app/Application.html) object instance provided to the init of the module. - */ + /** + * The [android Application](http://developer.android.com/reference/android/app/Application.html) object instance provided to the init of the module. + */ nativeApp: android.app.Application; - /** - * The application's [android Context](http://developer.android.com/reference/android/content/Context.html) object instance. - */ + /** + * The application's [android Context](http://developer.android.com/reference/android/content/Context.html) object instance. + */ context: android.content.Context; - /** - * The currently active (loaded) [android Activity](http://developer.android.com/reference/android/app/Activity.html). This property is automatically updated upon Activity events. - */ + /** + * The currently active (loaded) [android Activity](http://developer.android.com/reference/android/app/Activity.html). This property is automatically updated upon Activity events. + */ foregroundActivity: android.app.Activity; - /** - * The currently active (loaded) Context. This is typically the top-level Activity that is just created. - */ + /** + * The currently active (loaded) Context. This is typically the top-level Activity that is just created. + */ currentContext: android.content.Context; - /** - * The main (start) Activity for the application. - */ + /** + * The main (start) Activity for the application. + */ startActivity: android.app.Activity; - /** - * The name of the application package. - */ + /** + * The name of the application package. + */ packageName: string; - /** - * This method is called by the JavaScript Bridge when navigation to a new activity is triggered. - * @param intent - Native (android) intent used to create the activity. - * Returns com.tns.NativeScriptActivity.extend implementation. - */ + /** + * This method is called by the JavaScript Bridge when navigation to a new activity is triggered. + * @param intent - Native (android) intent used to create the activity. + * Returns com.tns.NativeScriptActivity.extend implementation. + */ getActivity(intent: android.content.Intent): any; - /** - * Direct handler of the [onActivityCreated method](http://developer.android.com/reference/android/app/Application.ActivityLifecycleCallbacks.html). - */ + /** + * Direct handler of the [onActivityCreated method](http://developer.android.com/reference/android/app/Application.ActivityLifecycleCallbacks.html). + */ onActivityCreated: (activity: android.app.Activity, bundle: android.os.Bundle) => void; - /** - * Direct handler of the [onActivityDestroyed method](http://developer.android.com/reference/android/app/Application.ActivityLifecycleCallbacks.html). - */ + /** + * Direct handler of the [onActivityDestroyed method](http://developer.android.com/reference/android/app/Application.ActivityLifecycleCallbacks.html). + */ onActivityDestroyed: (activity: android.app.Activity) => void; - /** - * Direct handler of the [onActivityDestroyed method](http://developer.android.com/reference/android/app/Application.ActivityLifecycleCallbacks.html). - */ + /** + * Direct handler of the [onActivityDestroyed method](http://developer.android.com/reference/android/app/Application.ActivityLifecycleCallbacks.html). + */ onActivityStarted: (activity: android.app.Activity) => void; - /** - * Direct handler of the [onActivityPaused method](http://developer.android.com/reference/android/app/Application.ActivityLifecycleCallbacks.html). - */ + /** + * Direct handler of the [onActivityPaused method](http://developer.android.com/reference/android/app/Application.ActivityLifecycleCallbacks.html). + */ onActivityPaused: (activity: android.app.Activity) => void; - /** - * Direct handler of the [onActivityResumed method](http://developer.android.com/reference/android/app/Application.ActivityLifecycleCallbacks.html). - */ + /** + * Direct handler of the [onActivityResumed method](http://developer.android.com/reference/android/app/Application.ActivityLifecycleCallbacks.html). + */ onActivityResumed: (activity: android.app.Activity) => void; - /** - * Direct handler of the [onActivityStopped method](http://developer.android.com/reference/android/app/Application.ActivityLifecycleCallbacks.html). - */ + /** + * Direct handler of the [onActivityStopped method](http://developer.android.com/reference/android/app/Application.ActivityLifecycleCallbacks.html). + */ onActivityStopped: (activity: android.app.Activity) => void; - /** - * Direct handler of the [onActivitySaveInstanceState method](http://developer.android.com/reference/android/app/Application.ActivityLifecycleCallbacks.html). - */ + /** + * Direct handler of the [onActivitySaveInstanceState method](http://developer.android.com/reference/android/app/Application.ActivityLifecycleCallbacks.html). + */ onSaveActivityState: (activity: android.app.Activity, bundle: android.os.Bundle) => void; - /** - * Direct handler of the onActivityResult method. - */ + /** + * Direct handler of the onActivityResult method. + */ onActivityResult: (requestCode: number, resultCode: number, data: android.content.Intent) => void } -/* tslint:disable */ - /** - * The abstraction of an iOS-specific application object. - */ + /* tslint:disable */ + /** + * The abstraction of an iOS-specific application object. + */ export interface iOSApplication { -/* tslint:enable */ - /** - * The root view controller for the application. - */ + /* tslint:enable */ + /** + * The root view controller for the application. + */ rootController: UIViewController; - /** - * The [UIApplication](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIApplication_Class/index.html) object instance provided to the init of the module. - */ + /** + * The [UIApplication](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIApplication_Class/index.html) object instance provided to the init of the module. + */ nativeApp: UIApplication; } } \ No newline at end of file diff --git a/application/application.ios.ts b/application/application.ios.ts index e5c652935..52edffd17 100644 --- a/application/application.ios.ts +++ b/application/application.ios.ts @@ -14,7 +14,7 @@ export var mainModule: string; class Window extends UIWindow { private _content: view.View; - + initWithFrame(frame: CGRect): UIWindow { var window = super.initWithFrame(frame); if (window) { @@ -50,6 +50,8 @@ class TNSAppDelegate extends UIResponder implements UIApplicationDelegate { exports.onLaunch(); } + exports.notify({ eventName: definition.launchEvent, object: this, ios: launchOptions }); + var topFrame = frame.topmost(); if (!topFrame) { if (mainModule) { @@ -74,6 +76,8 @@ class TNSAppDelegate extends UIResponder implements UIApplicationDelegate { if (exports.onResume) { exports.onResume(); } + + exports.notify({ eventName: definition.resumeEvent, object: this, ios: application }); } applicationWillResignActive(application: UIApplication) { @@ -84,6 +88,8 @@ class TNSAppDelegate extends UIResponder implements UIApplicationDelegate { if (exports.onSuspend) { exports.onSuspend(); } + + exports.notify({ eventName: definition.suspendEvent, object: this, ios: application }); } applicationWillEnterForeground(application: UIApplication) { @@ -94,12 +100,16 @@ class TNSAppDelegate extends UIResponder implements UIApplicationDelegate { if (exports.onExit) { exports.onExit(); } + + exports.notify({ eventName: definition.exitEvent, object: this, ios: application }); } applicationDidReceiveMemoryWarning(application: UIApplication) { if (exports.onLowMemory) { exports.onLowMemory(); } + + exports.notify({ eventName: definition.lowMemoryEvent, object: this, android: undefined, ios: application }); } applicationOpenURLSourceApplicationAnnotation(application: UIApplication, url: NSURL, sourceApplication: string, annotation: any): boolean { @@ -147,5 +157,7 @@ exports.start = function () { } exports.onUncaughtError(error); + + definition.notify({ eventName: definition.uncaughtErrorEvent, object: definition.ios, ios: error }); } } \ No newline at end of file diff --git a/apps/tests/app/app.ts b/apps/tests/app/app.ts index ee60fa462..e6e205b10 100644 --- a/apps/tests/app/app.ts +++ b/apps/tests/app/app.ts @@ -1,3 +1,30 @@ import application = require("application"); application.mainModule = "app/mainPage"; + +application.on(application.launchEvent, function (args: application.ApplicationEventData) { + console.log("launchEvent, iOS: " + args.ios + ", Android: " + args.android); +}); + +application.on(application.uncaughtErrorEvent, function (args: application.ApplicationEventData) { + console.log("uncaughtErrorEvent, iOS: " + args.ios + ", Android: " + args.android); +}); + +application.on(application.suspendEvent, function (args: application.ApplicationEventData) { + console.log("suspendEvent, iOS: " + args.ios + ", Android: " + args.android); + +}); + +application.on(application.resumeEvent, function (args: application.ApplicationEventData) { + console.log("resumeEvent, iOS: " + args.ios + ", Android: " + args.android); +}); + +application.on(application.exitEvent, function (args: application.ApplicationEventData) { + console.log("exitEvent, iOS: " + args.ios + ", Android: " + args.android); +}); + +application.on(application.lowMemoryEvent, function (args: application.ApplicationEventData) { + console.log("exitEvent, iOS: " + args.ios + ", Android: " + args.android); + +}); + application.start(); diff --git a/ui/frame/frame.android.ts b/ui/frame/frame.android.ts index b7f0f447e..fdf205011 100644 --- a/ui/frame/frame.android.ts +++ b/ui/frame/frame.android.ts @@ -382,7 +382,7 @@ var NativeActivity = { return this[ANDROID_FRAME]; }, - onCreate: function(savedInstanceState: android.os.Bundle) { + onCreate: function (savedInstanceState: android.os.Bundle) { trace.write("NativeScriptActivity.onCreate(); savedInstanceState: " + savedInstanceState, trace.categories.NativeLifecycle); // Find the frame for this activity. @@ -418,7 +418,7 @@ var NativeActivity = { this.frame._onActivityCreated(isRestart); }, - onActivityResult: function(requestCode: number, resultCode: number, data: android.content.Intent) { + onActivityResult: function (requestCode: number, resultCode: number, data: android.content.Intent) { this.super.onActivityResult(requestCode, resultCode, data); trace.write("NativeScriptActivity.onActivityResult();", trace.categories.NativeLifecycle); @@ -428,7 +428,7 @@ var NativeActivity = { } }, - onAttachFragment: function(fragment: android.app.Fragment) { + onAttachFragment: function (fragment: android.app.Fragment) { trace.write("NativeScriptActivity.onAttachFragment() : " + fragment.getTag(), trace.categories.NativeLifecycle); this.super.onAttachFragment(fragment); @@ -439,19 +439,19 @@ var NativeActivity = { } }, - onStart: function() { + onStart: function () { this.super.onStart(); trace.write("NativeScriptActivity.onStart();", trace.categories.NativeLifecycle); this.frame.onLoaded(); }, - onStop: function() { + onStop: function () { this.super.onStop(); trace.write("NativeScriptActivity.onStop();", trace.categories.NativeLifecycle); this.frame.onUnloaded(); }, - onDestroy: function() { + onDestroy: function () { // TODO: Implement uninitialized(detached) routine var frame = this.frame; frame._onDetached(true); @@ -467,7 +467,7 @@ var NativeActivity = { trace.write("NativeScriptActivity.onDestroy();", trace.categories.NativeLifecycle); }, - onOptionsItemSelected: function(menuItem: android.view.IMenuItem) { + onOptionsItemSelected: function (menuItem: android.view.IMenuItem) { if (!this.androidFrame.hasListeners(frameCommon.Frame.androidOptionSelectedEvent)) { return false; } @@ -483,20 +483,22 @@ var NativeActivity = { return data.handled; }, - onBackPressed: function() { + onBackPressed: function () { trace.write("NativeScriptActivity.onBackPressed;", trace.categories.NativeLifecycle); if (!frameCommon.goBack()) { this.super.onBackPressed(); } }, - onLowMemory: function() { + onLowMemory: function () { gc(); java.lang.System.gc(); this.super.onLowMemory(); + + application.notify({ eventName: application.lowMemoryEvent, object: this, android: this }); }, - onTrimMemory: function(level: number) { + onTrimMemory: function (level: number) { gc(); java.lang.System.gc(); this.super.onTrimMemory(level); @@ -668,7 +670,7 @@ function findPageForFragment(fragment: android.app.Fragment, frame: Frame) { } function startActivity(activity: android.app.Activity, entry: definition.NavigationEntry) { - var intent = new android.content.Intent(activity, (com).tns.NativeScriptActivity.class); + var intent = new android.content.Intent(activity,(com).tns.NativeScriptActivity.class); intent.setAction(android.content.Intent.ACTION_DEFAULT); // TODO: Put the navigation context (if any) in the intent activity.startActivity(intent);