import appModule = require("application/application-common"); import dts = require("application"); import frame = require("ui/frame"); import types = require("utils/types"); import observable = require("data/observable"); // merge the exports of the application_common file with the exports of this file declare var exports; require("utils/module-merge").merge(appModule, exports); export var mainModule: string; // We are using the exports object for the common events since we merge the appModule with this module's exports, which is what users will receive when require("application") is called; // TODO: This is kind of hacky and is "pure JS in TypeScript" var initEvents = function () { var androidApp: dts.AndroidApplication = exports.android; // TODO: Verify whether the logic for triggerring application-wide events based on Activity callbacks is working properly var lifecycleCallbacks = new android.app.Application.ActivityLifecycleCallbacks({ onActivityCreated: function (activity: any, bundle: any) { if (!androidApp.startActivity) { androidApp.startActivity = activity; androidApp.notify({ eventName: "activityCreated", object: androidApp, activity: activity, bundle: bundle }); if (androidApp.onActivityCreated) { androidApp.onActivityCreated(activity, bundle); } } androidApp.currentContext = activity; }, onActivityDestroyed: function (activity: any) { // Clear the current activity reference to prevent leak if (activity === androidApp.foregroundActivity) { androidApp.foregroundActivity = undefined; } if (activity === androidApp.currentContext) { androidApp.currentContext = undefined; } if (activity === androidApp.startActivity) { if (exports.onExit) { exports.onExit(); } exports.notify({ eventName: dts.exitEvent, object: androidApp, android: activity }); androidApp.startActivity = undefined; } androidApp.notify({ eventName: "activityDestroyed", object: androidApp, activity: activity }); if (androidApp.onActivityDestroyed) { androidApp.onActivityDestroyed(activity); } // TODO: This is a temporary workaround to force the V8's Garbage Collector, which will force the related Java Object to be collected. gc(); }, onActivityPaused: function (activity: any) { if (activity === androidApp.foregroundActivity) { if (exports.onSuspend) { exports.onSuspend(); } exports.notify({ eventName: dts.suspendEvent, object: androidApp, android: activity }); } androidApp.notify({ eventName: "activityPaused", object: androidApp, activity: activity }); if (androidApp.onActivityPaused) { androidApp.onActivityPaused(activity); } }, onActivityResumed: function (activity: any) { if (activity === androidApp.foregroundActivity) { if (exports.onResume) { exports.onResume(); } exports.notify({ eventName: dts.resumeEvent, object: androidApp, android: activity }); } androidApp.notify({ eventName: "activityResumed", object: androidApp, activity: activity }); if (androidApp.onActivityResumed) { androidApp.onActivityResumed(activity); } }, onActivitySaveInstanceState: function (activity: any, bundle: any) { androidApp.notify({ eventName: "saveActivityState", object: androidApp, activity: activity, bundle: bundle }); if (androidApp.onSaveActivityState) { androidApp.onSaveActivityState(activity, bundle); } }, onActivityStarted: function (activity: any) { androidApp.foregroundActivity = activity; androidApp.notify({ eventName: "activityStarted", object: androidApp, activity: activity }); if (androidApp.onActivityStarted) { androidApp.onActivityStarted(activity); } }, onActivityStopped: function (activity: any) { androidApp.notify({ eventName: "activityStopped", object: androidApp, activity: activity }); if (androidApp.onActivityStopped) { androidApp.onActivityStopped(activity); } } }); return lifecycleCallbacks; } app.init({ getActivity: function (activity: android.app.Activity) { var intent = activity.getIntent() return exports.android.getActivity(intent); }, onCreate: function () { exports.android.init(this); } }); export class AndroidApplication extends observable.Observable implements dts.AndroidApplication { public static activityCreatedEvent = "activityCreated"; public static activityDestroyedEvent = "activityDestroyed"; public static activityStartedEvent = "activityStarted"; public static activityPausedEvent = "activityPaused"; public static activityResumedEvent = "activityResumed"; public static activityStoppedEvent = "activityStopped"; public static saveActivityStateEvent = "saveActivityState"; public static activityResultEvent = "activityResult"; public static activityBackPressedEvent = "activityBackPressed"; public nativeApp: android.app.Application; public context: android.content.Context; public currentContext: android.content.Context; public foregroundActivity: android.app.Activity; public startActivity: android.app.Activity; public packageName: string; public hasActionBar: boolean; /* tslint:disable */ @Deprecated /* tslint:enable */ public onActivityCreated: (activity: android.app.Activity, bundle: android.os.Bundle) => void; /* tslint:disable */ @Deprecated /* tslint:enable */ public onActivityDestroyed: (activity: android.app.Activity) => void; /* tslint:disable */ @Deprecated /* tslint:enable */ public onActivityStarted: (activity: android.app.Activity) => void; /* tslint:disable */ @Deprecated /* tslint:enable */ public onActivityPaused: (activity: android.app.Activity) => void; /* tslint:disable */ @Deprecated /* tslint:enable */ public onActivityResumed: (activity: android.app.Activity) => void; /* tslint:disable */ @Deprecated /* tslint:enable */ public onActivityStopped: (activity: android.app.Activity) => void; /* tslint:disable */ @Deprecated /* tslint:enable */ public onSaveActivityState: (activity: android.app.Activity, bundle: android.os.Bundle) => void; /* tslint:disable */ @Deprecated /* tslint:enable */ public onActivityResult: (requestCode: number, resultCode: number, data: android.content.Intent) => void; private _eventsToken: any; public getActivity(intent: android.content.Intent): Object { if (intent && intent.getAction() === android.content.Intent.ACTION_MAIN) { // application's main activity if (exports.onLaunch) { 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(); * rootFrame.navigate({ pageModuleName: "mainPage" }); */ } var topFrame = frame.topmost(); if (!topFrame) { // try to navigate to the mainModule (if specified) if (mainModule) { topFrame = new frame.Frame(); topFrame.navigate(mainModule); } else { // TODO: Throw an exception? throw new Error("A Frame must be used to navigate to a Page."); } } return topFrame.android.onActivityRequested(intent); } public init(nativeApp: any) { this.nativeApp = nativeApp; this.packageName = nativeApp.getPackageName(); this.context = nativeApp.getApplicationContext(); this._eventsToken = initEvents(); this.nativeApp.registerActivityLifecycleCallbacks(this._eventsToken); this.context = this.nativeApp.getApplicationContext(); } } global.__onUncaughtError = function (error: Error) { if (!types.isFunction(exports.onUncaughtError)) { return; } var nsError = { message: error.message, name: error.name, nativeError: (error).nativeException } exports.onUncaughtError(nsError); exports.notify({ eventName: dts.uncaughtErrorEvent, object: appModule.android, android: nsError }); } exports.start = function () { dts.loadCss(); } exports.android = new AndroidApplication();