diff --git a/tests/app/application-tests.android.ts b/tests/app/application-tests.android.ts index 4d196eae0..064a831e2 100644 --- a/tests/app/application-tests.android.ts +++ b/tests/app/application-tests.android.ts @@ -41,7 +41,8 @@ if (app.android) { export var testAndroidApplicationInitialized = function () { TKUnit.assert(app.android, "Android application not initialized."); TKUnit.assert(app.android.context, "Android context not initialized."); - TKUnit.assert(app.android.foregroundActivity, "Android currentActivity not initialized."); + TKUnit.assert(app.android.currentContext, "Android currentContext not initialized."); + TKUnit.assert(app.android.foregroundActivity, "Android foregroundActivity not initialized."); TKUnit.assert(app.android.startActivity, "Android startActivity not initialized."); TKUnit.assert(app.android.nativeApp, "Android nativeApp not initialized."); TKUnit.assert(app.android.packageName, "Android packageName not initialized."); diff --git a/tns-core-modules/application/application.android.ts b/tns-core-modules/application/application.android.ts index c9ade5fe8..9b356031b 100644 --- a/tns-core-modules/application/application.android.ts +++ b/tns-core-modules/application/application.android.ts @@ -4,16 +4,16 @@ import frame = require("ui/frame"); import observable = require("data/observable"); import * as typesModule from "utils/types"; import * as enumsModule from "ui/enums"; +import { isNativeScriptActivity } from "ui/frame/activity"; let enums: typeof enumsModule; global.moduleMerge(appModule, exports); -var typedExports: typeof definition = exports; +const typedExports: typeof definition = exports; function initLifecycleCallbacks() { // TODO: Verify whether the logic for triggerring application-wide events based on Activity callbacks is working properly let lifecycleCallbacks = new android.app.Application.ActivityLifecycleCallbacks({ onActivityCreated: function (activity: any, bundle: any) { - // Set app theme after launch screen was used during startup let activityInfo = activity.getPackageManager().getActivityInfo(activity.getComponentName(), android.content.pm.PackageManager.GET_META_DATA); if (activityInfo.metaData) { @@ -25,38 +25,20 @@ function initLifecycleCallbacks() { 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; + androidApp.notify({ eventName: "activityCreated", object: androidApp, activity: activity, bundle: bundle }); + if (androidApp.onActivityCreated) { + androidApp.onActivityCreated(activity, bundle); + } }, 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 (typedExports.onExit) { - typedExports.onExit(); - } - - typedExports.notify({ eventName: typedExports.exitEvent, object: androidApp, android: activity }); - androidApp.startActivity = undefined; } androidApp.notify({ eventName: "activityDestroyed", object: androidApp, activity: activity }); - if (androidApp.onActivityDestroyed) { androidApp.onActivityDestroyed(activity); } @@ -66,35 +48,38 @@ function initLifecycleCallbacks() { }, onActivityPaused: function (activity: any) { - androidApp.paused = true; - if (activity === androidApp.foregroundActivity) { + androidApp.foregroundActivity = undefined; + } + + if (isNativeScriptActivity(activity)) { + androidApp.paused = true; + if (typedExports.onSuspend) { typedExports.onSuspend(); } - typedExports.notify({ eventName: typedExports.suspendEvent, object: androidApp, android: activity }); } androidApp.notify({ eventName: "activityPaused", object: androidApp, activity: activity }); - if (androidApp.onActivityPaused) { androidApp.onActivityPaused(activity); } }, onActivityResumed: function (activity: any) { - androidApp.paused = false; androidApp.foregroundActivity = activity; - if (typedExports.onResume) { - typedExports.onResume(); + if (isNativeScriptActivity(activity)) { + if (typedExports.onResume) { + typedExports.onResume(); + } + typedExports.notify({ eventName: typedExports.resumeEvent, object: androidApp, android: activity }); + + androidApp.paused = false; } - typedExports.notify({ eventName: typedExports.resumeEvent, object: androidApp, android: activity }); - androidApp.notify({ eventName: "activityResumed", object: androidApp, activity: activity }); - if (androidApp.onActivityResumed) { androidApp.onActivityResumed(activity); } @@ -109,10 +94,7 @@ function initLifecycleCallbacks() { }, onActivityStarted: function (activity: any) { - androidApp.foregroundActivity = activity; - androidApp.notify({ eventName: "activityStarted", object: androidApp, activity: activity }); - if (androidApp.onActivityStarted) { androidApp.onActivityStarted(activity); } @@ -120,7 +102,6 @@ function initLifecycleCallbacks() { onActivityStopped: function (activity: any) { androidApp.notify({ eventName: "activityStopped", object: androidApp, activity: activity }); - if (androidApp.onActivityStopped) { androidApp.onActivityStopped(activity); } @@ -195,12 +176,15 @@ export class AndroidApplication extends observable.Observable implements definit public paused: boolean; 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; + public get currentContext(): android.content.Context { + return this.foregroundActivity; + } + public onActivityCreated: (activity: android.app.Activity, bundle: android.os.Bundle) => void; public onActivityDestroyed: (activity: android.app.Activity) => void; @@ -219,7 +203,7 @@ export class AndroidApplication extends observable.Observable implements definit public init(nativeApp: any) { if (this.nativeApp) { - throw new Error("application.android already initialized.") + throw new Error("application.android already initialized."); } this.nativeApp = nativeApp; @@ -238,10 +222,10 @@ export class AndroidApplication extends observable.Observable implements definit private _pendingReceiverRegistrations = new Array<(context: android.content.Context) => void>(); private _registerPendingReceivers() { if (this._pendingReceiverRegistrations) { - var i = 0; - var length = this._pendingReceiverRegistrations.length; + let i = 0; + const length = this._pendingReceiverRegistrations.length; for (; i < length; i++) { - var registerFunc = this._pendingReceiverRegistrations[i]; + const registerFunc = this._pendingReceiverRegistrations[i]; registerFunc(this.context); } this._pendingReceiverRegistrations = new Array<(context: android.content.Context) => void>(); @@ -250,12 +234,12 @@ export class AndroidApplication extends observable.Observable implements definit public registerBroadcastReceiver(intentFilter: string, onReceiveCallback: (context: android.content.Context, intent: android.content.Intent) => void) { ensureBroadCastReceiverClass(); - var that = this; - var registerFunc = function (context: android.content.Context) { - var receiver: android.content.BroadcastReceiver = new BroadcastReceiverClass(onReceiveCallback); + const that = this; + const registerFunc = function (context: android.content.Context) { + const receiver: android.content.BroadcastReceiver = new BroadcastReceiverClass(onReceiveCallback); context.registerReceiver(receiver, new android.content.IntentFilter(intentFilter)); that._registeredReceivers[intentFilter] = receiver; - } + }; if (this.context) { registerFunc(this.context); @@ -266,7 +250,7 @@ export class AndroidApplication extends observable.Observable implements definit } public unregisterBroadcastReceiver(intentFilter: string) { - var receiver = this._registeredReceivers[intentFilter]; + const receiver = this._registeredReceivers[intentFilter]; if (receiver) { this.context.unregisterReceiver(receiver); this._registeredReceivers[intentFilter] = undefined; @@ -350,10 +334,10 @@ global.__onLiveSync = function () { appModule.__onLiveSync(); loadCss(); -} +}; global.__onUncaughtError = function (error: definition.NativeScriptError) { - var types: typeof typesModule = require("utils/types"); + const types: typeof typesModule = require("utils/types"); // TODO: Obsolete this if (types.isFunction(typedExports.onUncaughtError)) { @@ -361,4 +345,4 @@ global.__onUncaughtError = function (error: definition.NativeScriptError) { } typedExports.notify({ eventName: typedExports.uncaughtErrorEvent, object: appModule.android, android: error }); -} +}; diff --git a/tns-core-modules/application/application.d.ts b/tns-core-modules/application/application.d.ts index 2291bc608..910b7f2da 100644 --- a/tns-core-modules/application/application.d.ts +++ b/tns-core-modules/application/application.d.ts @@ -358,12 +358,12 @@ declare module "application" { foregroundActivity: any /* android.app.Activity */; /** - * The currently active (loaded) Context. This is typically the top-level Activity that is just created. + * [Deprecated. Please use the respective event instead.] Please use foregroundActivity property. */ currentContext: any /* android.content.Context */; /** - * The main (start) Activity for the application. + * [Deprecated. Please use foregroundActivity or activity related events instead.] The main (start) Activity for the application. */ startActivity: any /* android.app.Activity */; @@ -373,7 +373,7 @@ declare module "application" { packageName: string; /** - * True if the application is not running (suspended), false otherwise. + * True if the main application activity is not running (suspended), false otherwise. */ paused: boolean; diff --git a/tns-core-modules/ui/dialogs/dialogs.android.ts b/tns-core-modules/ui/dialogs/dialogs.android.ts index 83edc40be..083d5c15a 100644 --- a/tns-core-modules/ui/dialogs/dialogs.android.ts +++ b/tns-core-modules/ui/dialogs/dialogs.android.ts @@ -9,7 +9,7 @@ import types = require("utils/types"); global.moduleMerge(dialogsCommon, exports); function createAlertDialog(options?: dialogs.DialogOptions): android.app.AlertDialog.Builder { - var alert = new android.app.AlertDialog.Builder(appmodule.android.currentContext); + const alert = new android.app.AlertDialog.Builder(appmodule.android.foregroundActivity); alert.setTitle(options && types.isString(options.title) ? options.title : ""); alert.setMessage(options && types.isString(options.message) ? options.message : ""); if (options && options.cancelable === false) { @@ -19,21 +19,21 @@ function createAlertDialog(options?: dialogs.DialogOptions): android.app.AlertDi } function showDialog(builder: android.app.AlertDialog.Builder) { - var dlg = builder.show(); + const dlg = builder.show(); - var labelColor = dialogsCommon.getLabelColor(); + const labelColor = dialogsCommon.getLabelColor(); if (labelColor) { - var textViewId = dlg.getContext().getResources().getIdentifier("android:id/alertTitle", null, null); + const textViewId = dlg.getContext().getResources().getIdentifier("android:id/alertTitle", null, null); if (textViewId) { - var tv = dlg.findViewById(textViewId); + const tv = dlg.findViewById(textViewId); if (tv) { tv.setTextColor(labelColor.android); } } - var messageTextViewId = dlg.getContext().getResources().getIdentifier("android:id/message", null, null); + const messageTextViewId = dlg.getContext().getResources().getIdentifier("android:id/message", null, null); if (messageTextViewId) { - var messageTextView = dlg.findViewById(messageTextViewId); + const messageTextView = dlg.findViewById(messageTextViewId); if (messageTextView) { messageTextView.setTextColor(labelColor.android); } @@ -98,9 +98,9 @@ function addButtonsToAlertDialog(alert: android.app.AlertDialog.Builder, options export function alert(arg: any): Promise { return new Promise((resolve, reject) => { try { - var options = !dialogsCommon.isDialogOptions(arg) ? { title: dialogsCommon.ALERT, okButtonText: dialogsCommon.OK, message: arg + "" } : arg; + const options = !dialogsCommon.isDialogOptions(arg) ? { title: dialogsCommon.ALERT, okButtonText: dialogsCommon.OK, message: arg + "" } : arg; - var alert = createAlertDialog(options); + const alert = createAlertDialog(options); alert.setPositiveButton(options.okButtonText, new android.content.DialogInterface.OnClickListener({ onClick: function (dialog: android.content.DialogInterface, id: number) { @@ -125,8 +125,8 @@ export function alert(arg: any): Promise { export function confirm(arg: any): Promise { return new Promise((resolve, reject) => { try { - var options = !dialogsCommon.isDialogOptions(arg) ? { title: dialogsCommon.CONFIRM, okButtonText: dialogsCommon.OK, cancelButtonText: dialogsCommon.CANCEL, message: arg + "" } : arg; - var alert = createAlertDialog(options); + const options = !dialogsCommon.isDialogOptions(arg) ? { title: dialogsCommon.CONFIRM, okButtonText: dialogsCommon.OK, cancelButtonText: dialogsCommon.CANCEL, message: arg + "" } : arg; + const alert = createAlertDialog(options); addButtonsToAlertDialog(alert, options, function (result) { resolve(result); }); @@ -139,9 +139,9 @@ export function confirm(arg: any): Promise { } export function prompt(arg: any): Promise { - var options: dialogs.PromptOptions; + let options: dialogs.PromptOptions; - var defaultOptions = { + const defaultOptions = { title: dialogsCommon.PROMPT, okButtonText: dialogsCommon.OK, cancelButtonText: dialogsCommon.CANCEL, @@ -165,9 +165,9 @@ export function prompt(arg: any): Promise { return new Promise((resolve, reject) => { try { - var alert = createAlertDialog(options); + const alert = createAlertDialog(options); - var input = new android.widget.EditText(appmodule.android.currentContext); + const input = new android.widget.EditText(appmodule.android.foregroundActivity); if (options && options.inputType === dialogsCommon.inputType.password) { input.setInputType(android.text.InputType.TYPE_CLASS_TEXT | android.text.InputType.TYPE_TEXT_VARIATION_PASSWORD); @@ -177,7 +177,7 @@ export function prompt(arg: any): Promise { alert.setView(input); - var getText = function () { return input.getText().toString(); }; + const getText = function () { return input.getText().toString(); }; addButtonsToAlertDialog(alert, options, function (r) { resolve({ result: r, text: getText() }); }); @@ -191,9 +191,9 @@ export function prompt(arg: any): Promise { } export function login(arg: any): Promise { - var options: dialogs.LoginOptions; + let options: dialogs.LoginOptions; - var defaultOptions = { title: dialogsCommon.LOGIN, okButtonText: dialogsCommon.OK, cancelButtonText: dialogsCommon.CANCEL }; + const defaultOptions = { title: dialogsCommon.LOGIN, okButtonText: dialogsCommon.OK, cancelButtonText: dialogsCommon.CANCEL }; if (arguments.length === 1) { if (types.isString(arguments[0])) { @@ -219,18 +219,18 @@ export function login(arg: any): Promise { return new Promise((resolve, reject) => { try { - var context = appmodule.android.currentContext; + const context = appmodule.android.foregroundActivity; - var alert = createAlertDialog(options); + const alert = createAlertDialog(options); - var userNameInput = new android.widget.EditText(context); + const userNameInput = new android.widget.EditText(context); userNameInput.setText(options.userName ? options.userName : ""); - var passwordInput = new android.widget.EditText(context); + const passwordInput = new android.widget.EditText(context); passwordInput.setInputType(android.text.InputType.TYPE_CLASS_TEXT | android.text.InputType.TYPE_TEXT_VARIATION_PASSWORD); passwordInput.setText(options.password ? options.password : ""); - var layout = new android.widget.LinearLayout(context); + const layout = new android.widget.LinearLayout(context); layout.setOrientation(1); layout.addView(userNameInput); layout.addView(passwordInput); @@ -255,10 +255,9 @@ export function login(arg: any): Promise { } export function action(arg: any): Promise { + let options: dialogs.ActionOptions; - var options: dialogs.ActionOptions; - - var defaultOptions = { title: null, cancelButtonText: dialogsCommon.CANCEL }; + const defaultOptions = { title: null, cancelButtonText: dialogsCommon.CANCEL }; if (arguments.length === 1) { if (types.isString(arguments[0])) { @@ -284,10 +283,10 @@ export function action(arg: any): Promise { return new Promise((resolve, reject) => { try { - var activity = appmodule.android.foregroundActivity || appmodule.android.startActivity; - var alert = new android.app.AlertDialog.Builder(activity); - var message = options && types.isString(options.message) ? options.message : ""; - var title = options && types.isString(options.title) ? options.title : ""; + const activity = appmodule.android.foregroundActivity || appmodule.android.startActivity; + const alert = new android.app.AlertDialog.Builder(activity); + const message = options && types.isString(options.message) ? options.message : ""; + const title = options && types.isString(options.title) ? options.title : ""; if (options && options.cancelable === false) { alert.setCancelable(false); } diff --git a/tns-core-modules/ui/frame/activity.android.ts b/tns-core-modules/ui/frame/activity.android.ts index 95aa0fcb3..6d9ba2724 100644 --- a/tns-core-modules/ui/frame/activity.android.ts +++ b/tns-core-modules/ui/frame/activity.android.ts @@ -43,4 +43,8 @@ class NativeScriptActivity extends android.app.Activity { protected onActivityResult(requestCode: number, resultCode: number, data: android.content.Intent): void { this._callbacks.onActivityResult(this, requestCode, resultCode, data, super.onActivityResult); } +} + +export function isNativeScriptActivity(activity: android.app.Activity): boolean { + return activity instanceof NativeScriptActivity; } \ No newline at end of file diff --git a/tns-core-modules/ui/frame/activity.d.ts b/tns-core-modules/ui/frame/activity.d.ts new file mode 100644 index 000000000..3afed6e97 --- /dev/null +++ b/tns-core-modules/ui/frame/activity.d.ts @@ -0,0 +1,3 @@ +declare module "ui/frame/activity" { + export function isNativeScriptActivity(activity: any/* android.app.Activity */): boolean; +} \ No newline at end of file diff --git a/tns-core-modules/ui/frame/frame.android.ts b/tns-core-modules/ui/frame/frame.android.ts index 9dcd29241..d2c53b176 100644 --- a/tns-core-modules/ui/frame/frame.android.ts +++ b/tns-core-modules/ui/frame/frame.android.ts @@ -49,7 +49,7 @@ function onFragmentShown(fragment: android.app.Fragment) { } } - var isBack = currentNavigationContext ? currentNavigationContext.isBackNavigation : false; + const isBack = currentNavigationContext ? currentNavigationContext.isBackNavigation : false; frame._addView(page); @@ -286,7 +286,7 @@ export class Frame extends frameCommon.Frame { this._currentEntry.isNavigation = true; } - var manager = this._android.activity.getFragmentManager(); + const manager = this._android.activity.getFragmentManager(); if (manager.getBackStackEntryCount() > 0) { // pop all other fragments up until the named one // this handles cases where user may navigate to an inner page without adding it on the backstack @@ -337,12 +337,12 @@ export class Frame extends frameCommon.Frame { if (!this._android.activity) { return; } - var manager = this._android.activity.getFragmentManager(); - var length = manager.getBackStackEntryCount(); - var i = length - 1; + const manager = this._android.activity.getFragmentManager(); + const length = manager.getBackStackEntryCount(); + let i = length - 1; console.log(`Fragment Manager Back Stack: `); while (i >= 0) { - var fragment = manager.findFragmentByTag(manager.getBackStackEntryAt(i--).getName()); + const fragment = manager.findFragmentByTag(manager.getBackStackEntryAt(i--).getName()); console.log(`\t${fragment}`); } } @@ -375,7 +375,7 @@ export class Frame extends frameCommon.Frame { trace.write(`Frame _processNavigationContext: Drop For Activity GC-ed`, trace.categories.Navigation); } unsubscribe(); - return + return; } if (isCurrent) { if (trace.enabled) { @@ -384,7 +384,7 @@ export class Frame extends frameCommon.Frame { super._processNavigationContext(navigationContext); unsubscribe(); } - } + }; let unsubscribe = () => { if (trace.enabled) { trace.write(`Frame _processNavigationContext: Unsubscribe from Activity.Resumed`, trace.categories.Navigation); @@ -392,7 +392,7 @@ export class Frame extends frameCommon.Frame { application.android.off(application.AndroidApplication.activityResumedEvent, resume); application.android.off(application.AndroidApplication.activityStoppedEvent, unsubscribe); application.android.off(application.AndroidApplication.activityDestroyedEvent, unsubscribe); - } + }; if (trace.enabled) { trace.write(`Frame._processNavigationContext: Subscribe for Activity.Resumed`, trace.categories.Navigation); @@ -563,7 +563,7 @@ function findPageForFragment(fragment: android.app.Fragment, frame: Frame) { } } else { - var backStack = frame.backStack; + const backStack = frame.backStack; for (let i = 0; i < backStack.length; i++) { if (backStack[i].fragmentTag === fragmentTag) { entry = backStack[i]; @@ -590,7 +590,7 @@ function findPageForFragment(fragment: android.app.Fragment, frame: Frame) { function startActivity(activity: android.app.Activity, frameId: number) { // TODO: Implicitly, we will open the same activity type as the current one - var intent = new android.content.Intent(activity, activity.getClass()); + const intent = new android.content.Intent(activity, activity.getClass()); intent.setAction(android.content.Intent.ACTION_DEFAULT); intent.putExtra(INTENT_EXTRA, frameId); @@ -659,7 +659,7 @@ class FragmentCallbacksImplementation implements definition.AndroidFragmentCallb } public onCreateAnimator(fragment: android.app.Fragment, transit: number, enter: boolean, nextAnim: number, superFunc: Function): android.animation.Animator { - var nextAnimString: string; + let nextAnimString: string; switch (nextAnim) { case -10: nextAnimString = "enter"; break; case -20: nextAnimString = "exit"; break; @@ -667,7 +667,7 @@ class FragmentCallbacksImplementation implements definition.AndroidFragmentCallb case -40: nextAnimString = "popExit"; break; } - var animator = transitionModule._onFragmentCreateAnimator(fragment, nextAnim); + let animator = transitionModule._onFragmentCreateAnimator(fragment, nextAnim); if (!animator) { animator = superFunc.call(fragment, transit, enter, nextAnim); @@ -706,8 +706,8 @@ class FragmentCallbacksImplementation implements definition.AndroidFragmentCallb if (trace.enabled) { trace.write(`${fragment}.onCreateView(inflater, container, ${savedInstanceState})`, trace.categories.NativeLifecycle); } - var entry = this.entry; - var page = entry.resolvedPage; + const entry = this.entry; + const page = entry.resolvedPage; if (savedInstanceState && savedInstanceState.getBoolean(HIDDEN, false)) { fragment.getFragmentManager().beginTransaction().hide(fragment).commit(); page._onAttached(fragment.getActivity()); @@ -780,7 +780,7 @@ class ActivityCallbacksImplementation implements definition.AndroidActivityCallb } if (savedInstanceState && frameId < 0) { - frameId = savedInstanceState.getInt(INTENT_EXTRA, -1) + frameId = savedInstanceState.getInt(INTENT_EXTRA, -1); } // If we have frameId from extras - we are starting a new activity from navigation (e.g. new Frame().navigate())) @@ -862,7 +862,7 @@ class ActivityCallbacksImplementation implements definition.AndroidActivityCallb } public onDestroy(activity: any, superFunc: Function): void { - let rootView = this._rootView + let rootView = this._rootView; if (rootView && rootView._context) { rootView._onDetached(true); } @@ -872,6 +872,12 @@ class ActivityCallbacksImplementation implements definition.AndroidActivityCallb if (trace.enabled) { trace.write("NativeScriptActivity.onDestroy();", trace.categories.NativeLifecycle); } + + const exitArgs = { eventName: application.exitEvent, object: application.android, android: activity }; + application.notify(exitArgs); + if (application.onExit) { + application.onExit(); + } } public onBackPressed(activity: any, superFunc: Function): void { @@ -879,7 +885,7 @@ class ActivityCallbacksImplementation implements definition.AndroidActivityCallb trace.write("NativeScriptActivity.onBackPressed;", trace.categories.NativeLifecycle); } - var args = { + const args = { eventName: "activityBackPressed", object: application.android, activity: activity, @@ -917,7 +923,7 @@ class ActivityCallbacksImplementation implements definition.AndroidActivityCallb trace.write(`NativeScriptActivity.onActivityResult(${requestCode}, ${resultCode}, ${data})`, trace.categories.NativeLifecycle); } - var result = application.android.onActivityResult; + const result = application.android.onActivityResult; if (result) { result(requestCode, resultCode, data); }