Merge pull request #2885 from NativeScript/android-current-context

Application and ActivityEvents improvements
This commit is contained in:
Alexander Vakrilov
2016-10-13 11:47:25 +03:00
committed by GitHub
7 changed files with 101 additions and 104 deletions

View File

@ -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.");

View File

@ -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(<definition.AndroidActivityBundleEventData>{ eventName: "activityCreated", object: androidApp, activity: activity, bundle: bundle });
if (androidApp.onActivityCreated) {
androidApp.onActivityCreated(activity, bundle);
}
}
androidApp.currentContext = activity;
androidApp.notify(<definition.AndroidActivityBundleEventData>{ 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(<definition.ApplicationEventData>{ eventName: typedExports.exitEvent, object: androidApp, android: activity });
androidApp.startActivity = undefined;
}
androidApp.notify(<definition.AndroidActivityEventData>{ 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(<definition.ApplicationEventData>{ eventName: typedExports.suspendEvent, object: androidApp, android: activity });
}
androidApp.notify(<definition.AndroidActivityEventData>{ 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(<definition.ApplicationEventData>{ eventName: typedExports.resumeEvent, object: androidApp, android: activity });
androidApp.paused = false;
}
typedExports.notify(<definition.ApplicationEventData>{ eventName: typedExports.resumeEvent, object: androidApp, android: activity });
androidApp.notify(<definition.AndroidActivityEventData>{ 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(<definition.AndroidActivityEventData>{ eventName: "activityStarted", object: androidApp, activity: activity });
if (androidApp.onActivityStarted) {
androidApp.onActivityStarted(activity);
}
@ -120,7 +102,6 @@ function initLifecycleCallbacks() {
onActivityStopped: function (activity: any) {
androidApp.notify(<definition.AndroidActivityEventData>{ 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 });
}
};

View File

@ -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;

View File

@ -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 = <android.widget.TextView>dlg.findViewById(textViewId);
const tv = <android.widget.TextView>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 = <android.widget.TextView>dlg.findViewById(messageTextViewId);
const messageTextView = <android.widget.TextView>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<void> {
return new Promise<void>((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<void> {
export function confirm(arg: any): Promise<boolean> {
return new Promise<boolean>((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<boolean> {
}
export function prompt(arg: any): Promise<dialogs.PromptResult> {
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<dialogs.PromptResult> {
return new Promise<dialogs.PromptResult>((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<dialogs.PromptResult> {
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<dialogs.PromptResult> {
}
export function login(arg: any): Promise<dialogs.LoginResult> {
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<dialogs.LoginResult> {
return new Promise<dialogs.LoginResult>((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<dialogs.LoginResult> {
}
export function action(arg: any): Promise<string> {
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<string> {
return new Promise<string>((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);
}

View File

@ -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;
}

View File

@ -0,0 +1,3 @@
declare module "ui/frame/activity" {
export function isNativeScriptActivity(activity: any/* android.app.Activity */): boolean;
}

View File

@ -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 = <application.AndroidActivityBackPressedEventData>{
const args = <application.AndroidActivityBackPressedEventData>{
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);
}