From c6549343bc9b2952ad75e6e03bf3c1abc7d19f7f Mon Sep 17 00:00:00 2001 From: Vladimir Enchev Date: Mon, 1 Jun 2015 15:10:47 +0300 Subject: [PATCH] application module events added --- application/application-common.ts | 4 + application/application.android.ts | 13 ++ application/application.d.ts | 260 ++++++++++++++++++----------- application/application.ios.ts | 14 +- apps/tests/app/app.ts | 25 +++ ui/frame/frame.android.ts | 24 +-- 6 files changed, 231 insertions(+), 109 deletions(-) diff --git a/application/application-common.ts b/application/application-common.ts index b1e97ef97..e6a460936 100644 --- a/application/application-common.ts +++ b/application/application-common.ts @@ -3,6 +3,10 @@ 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 cssFile: string = "app.css" diff --git a/application/application.android.ts b/application/application.android.ts index a1eba504b..a535cf63a 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: "onExit", object: androidApp, android: activity, ios: undefined }); + androidApp.startActivity = undefined; } @@ -58,6 +61,9 @@ var initEvents = function () { if (exports.onSuspend) { exports.onSuspend(); } + + exports.notify({ eventName: "onSuspend", object: androidApp, android: activity, ios: undefined }); + } if (androidApp.onActivityPaused) { @@ -69,6 +75,9 @@ var initEvents = function () { if (exports.onResume) { exports.onResume(); } + + exports.notify({ eventName: "onResume", object: androidApp, android: activity, ios: undefined }); + } if (androidApp.onActivityResumed) { @@ -183,6 +192,8 @@ class AndroidApplication implements dts.AndroidApplication { exports.onLaunch(intent); } + exports.notify({ eventName: "onLaunch", object: this, android: intent, ios: undefined }); + /* 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: "onUncaughtError", object: appModule.android, android: error, ios: undefined }); } exports.start = function () { diff --git a/application/application.d.ts b/application/application.d.ts index 5f736b23d..352d52276 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,34 @@ 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(); - */ + /** + * 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 +60,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 +78,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 exit. + */ 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 launch. + */ + 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 exit. + */ + 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..f4ffd6866 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: "onLaunch", object: this, android: undefined, 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: "onResume", object: this, android: undefined, ios: application }); } applicationWillResignActive(application: UIApplication) { @@ -84,6 +88,8 @@ class TNSAppDelegate extends UIResponder implements UIApplicationDelegate { if (exports.onSuspend) { exports.onSuspend(); } + + exports.notify({ eventName: "onSuspend", object: this, android: undefined, ios: application }); } applicationWillEnterForeground(application: UIApplication) { @@ -94,12 +100,16 @@ class TNSAppDelegate extends UIResponder implements UIApplicationDelegate { if (exports.onExit) { exports.onExit(); } + + exports.notify({ eventName: "onExit", object: this, android: undefined, ios: application }); } applicationDidReceiveMemoryWarning(application: UIApplication) { if (exports.onLowMemory) { exports.onLowMemory(); } + + exports.notify({ eventName: "onLowMemory", 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: "onUncaughtError", object: definition.ios, android: undefined, ios: error }); } } \ No newline at end of file diff --git a/apps/tests/app/app.ts b/apps/tests/app/app.ts index ee60fa462..62b564eba 100644 --- a/apps/tests/app/app.ts +++ b/apps/tests/app/app.ts @@ -1,3 +1,28 @@ import application = require("application"); application.mainModule = "app/mainPage"; + +application.on("onLaunch", function (args) { + console.log("onLaunch: " + args); +}); + +application.on("onUncaughtError", function (args) { + console.log("onUncaughtError: " + args); +}); + +application.on("onSuspend", function (args) { + console.log("onSuspend: " + args); +}); + +application.on("onResume", function (args) { + console.log("onResume: " + args); +}); + +application.on("onExit", function (args) { + console.log("onExit: " + args); +}); + +application.on("onLowMemory", function (args) { + console.log("onLowMemory: " + args); +}); + application.start(); diff --git a/ui/frame/frame.android.ts b/ui/frame/frame.android.ts index b7f0f447e..570df03ad 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: "onLowMemory", object: this, android: undefined, ios: undefined }); }, - 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);