Merge pull request #2244 from NativeScript/atanasovg/remove-app-activity-extends

Remove the android.app.Application extend from the core modules
This commit is contained in:
Georgi Atanasov
2016-06-06 15:36:50 +03:00
3 changed files with 114 additions and 80 deletions

View File

@ -7,40 +7,9 @@ import * as typesModule from "utils/types";
global.moduleMerge(appModule, exports); global.moduleMerge(appModule, exports);
var typedExports: typeof definition = exports; var typedExports: typeof definition = exports;
@JavaProxy("com.tns.NativeScriptApplication") function initLifecycleCallbacks() {
class NativeScriptApplication extends android.app.Application {
constructor() {
super();
return global.__native(this);
}
public onCreate(): void {
androidApp.init(this);
setupOrientationListener(androidApp);
}
public onLowMemory(): void {
gc();
java.lang.System.gc();
super.onLowMemory();
typedExports.notify(<definition.ApplicationEventData>{ eventName: typedExports.lowMemoryEvent, object: this, android: this });
}
public onTrimMemory(level: number): void {
gc();
java.lang.System.gc();
super.onTrimMemory(level);
}
}
// 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"
function initEvents() {
// TODO: Verify whether the logic for triggerring application-wide events based on Activity callbacks is working properly // TODO: Verify whether the logic for triggerring application-wide events based on Activity callbacks is working properly
var lifecycleCallbacks = new android.app.Application.ActivityLifecycleCallbacks({ let lifecycleCallbacks = new android.app.Application.ActivityLifecycleCallbacks({
onActivityCreated: function (activity: any, bundle: any) { onActivityCreated: function (activity: any, bundle: any) {
if (!androidApp.startActivity) { if (!androidApp.startActivity) {
androidApp.startActivity = activity; androidApp.startActivity = activity;
@ -55,7 +24,6 @@ function initEvents() {
}, },
onActivityDestroyed: function (activity: any) { onActivityDestroyed: function (activity: any) {
// Clear the current activity reference to prevent leak // Clear the current activity reference to prevent leak
if (activity === androidApp.foregroundActivity) { if (activity === androidApp.foregroundActivity) {
androidApp.foregroundActivity = undefined; androidApp.foregroundActivity = undefined;
@ -150,6 +118,54 @@ function initEvents() {
return lifecycleCallbacks; return lifecycleCallbacks;
} }
let currentOrientation: number;
function initComponentCallbacks() {
let componentCallbacks = new android.content.ComponentCallbacks2({
onLowMemory: function() {
gc();
java.lang.System.gc();
typedExports.notify(<definition.ApplicationEventData>{ eventName: typedExports.lowMemoryEvent, object: this, android: this });
},
onTrimMemory: function(level: number) {
// TODO: This is skipped for now, test carefully for OutOfMemory exceptions
},
onConfigurationChanged: function(newConfig: android.content.res.Configuration) {
let newOrientation = newConfig.orientation;
if(newOrientation === currentOrientation) {
return;
}
currentOrientation = newOrientation;
let enums = require("ui/enums");
let newValue;
switch (orientation) {
case android.content.res.Configuration.ORIENTATION_LANDSCAPE:
newValue = enums.DeviceOrientation.landscape;
break;
case android.content.res.Configuration.ORIENTATION_PORTRAIT:
newValue = enums.DeviceOrientation.portrait;
break;
default:
newValue = enums.DeviceOrientation.unknown;
break;
}
typedExports.notify(<definition.OrientationChangedEventData>{
eventName: typedExports.orientationChangedEvent,
android: androidApp.nativeApp,
newValue: newValue,
object: typedExports.android,
});
}
});
return componentCallbacks;
}
export class AndroidApplication extends observable.Observable implements definition.AndroidApplication { export class AndroidApplication extends observable.Observable implements definition.AndroidApplication {
public static activityCreatedEvent = "activityCreated"; public static activityCreatedEvent = "activityCreated";
public static activityDestroyedEvent = "activityDestroyed"; public static activityDestroyedEvent = "activityDestroyed";
@ -187,15 +203,20 @@ export class AndroidApplication extends observable.Observable implements definit
public onActivityResult: (requestCode: number, resultCode: number, data: android.content.Intent) => void; public onActivityResult: (requestCode: number, resultCode: number, data: android.content.Intent) => void;
private _eventsToken: any;
public init(nativeApp: any) { public init(nativeApp: any) {
if(this.nativeApp) {
throw new Error("application.android already initialized.")
}
this.nativeApp = nativeApp; this.nativeApp = nativeApp;
this.packageName = nativeApp.getPackageName(); this.packageName = nativeApp.getPackageName();
this.context = nativeApp.getApplicationContext(); this.context = nativeApp.getApplicationContext();
this._eventsToken = initEvents(); let lifecycleCallbacks = initLifecycleCallbacks();
this.nativeApp.registerActivityLifecycleCallbacks(this._eventsToken); let componentCallbacks = initComponentCallbacks();
this.nativeApp.registerActivityLifecycleCallbacks(lifecycleCallbacks);
this.nativeApp.registerComponentCallbacks(componentCallbacks);
this._registerPendingReceivers(); this._registerPendingReceivers();
} }
@ -240,11 +261,11 @@ export class AndroidApplication extends observable.Observable implements definit
} }
} }
var androidApp = new AndroidApplication(); let androidApp = new AndroidApplication();
// use the exports object instead of 'export var' due to global namespace collision // use the exports object instead of 'export var' due to global namespace collision
typedExports.android = androidApp; typedExports.android = androidApp;
var BroadcastReceiverClass; let BroadcastReceiverClass;
function ensureBroadCastReceiverClass() { function ensureBroadCastReceiverClass() {
if (BroadcastReceiverClass) { if (BroadcastReceiverClass) {
return; return;
@ -269,12 +290,19 @@ function ensureBroadCastReceiverClass() {
BroadcastReceiverClass = BroadcastReceiver; BroadcastReceiverClass = BroadcastReceiver;
} }
var started = false; let started = false;
export function start(entry?: frame.NavigationEntry) { export function start(entry?: frame.NavigationEntry) {
if (started) { if (started) {
throw new Error("Application is already started."); throw new Error("Application is already started.");
} }
if(!androidApp.nativeApp) {
// we are still not initialized, this is possible if no 'androidApp.init' call has been made
let utils = require("utils/utils");
let nativeApp = utils.ad.getApplication();
androidApp.init(nativeApp);
}
started = true; started = true;
if (entry) { if (entry) {
typedExports.mainEntry = entry; typedExports.mainEntry = entry;
@ -283,42 +311,6 @@ export function start(entry?: frame.NavigationEntry) {
loadCss(); loadCss();
} }
var currentOrientation: number;
function setupOrientationListener(androidApp: AndroidApplication) {
androidApp.registerBroadcastReceiver(android.content.Intent.ACTION_CONFIGURATION_CHANGED, onConfigurationChanged);
currentOrientation = androidApp.context.getResources().getConfiguration().orientation;
}
function onConfigurationChanged(context: android.content.Context, intent: android.content.Intent) {
var orientation = context.getResources().getConfiguration().orientation;
if (currentOrientation !== orientation) {
currentOrientation = orientation;
var enums = require("ui/enums");
var newValue;
switch (orientation) {
case android.content.res.Configuration.ORIENTATION_LANDSCAPE:
newValue = enums.DeviceOrientation.landscape;
break;
case android.content.res.Configuration.ORIENTATION_PORTRAIT:
newValue = enums.DeviceOrientation.portrait;
break;
default:
newValue = enums.DeviceOrientation.unknown;
break;
}
typedExports.notify(<definition.OrientationChangedEventData>{
eventName: typedExports.orientationChangedEvent,
android: context,
newValue: newValue,
object: typedExports.android,
});
}
}
function loadCss() { function loadCss() {
//HACK: identical to application.ios.ts //HACK: identical to application.ios.ts
typedExports.appSelectors = typedExports.loadCss(typedExports.cssFile) || []; typedExports.appSelectors = typedExports.loadCss(typedExports.cssFile) || [];

View File

@ -377,6 +377,13 @@ declare module "application" {
*/ */
paused: boolean; paused: boolean;
/**
* Initialized the android-specific application object with the native android.app.Application instance.
* This is useful when creating custom application types.
* @param nativeApp - the android.app.Application instance that started the app.
*/
init: (nativeApp) => void;
/** /**
* [Deprecated. Please use the respective event instead.] Direct handler of the [onActivityCreated method](http://developer.android.com/reference/android/app/Application.ActivityLifecycleCallbacks.html). * [Deprecated. Please use the respective event instead.] Direct handler of the [onActivityCreated method](http://developer.android.com/reference/android/app/Application.ActivityLifecycleCallbacks.html).
*/ */

View File

@ -148,10 +148,45 @@ export module ad {
view.setEllipsize(value === enums.WhiteSpace.nowrap ? android.text.TextUtils.TruncateAt.END : null); view.setEllipsize(value === enums.WhiteSpace.nowrap ? android.text.TextUtils.TruncateAt.END : null);
} }
let nativeApp: android.app.Application;
declare var com;
export function getApplication() { export function getApplication() {
return <android.app.Application>(<any>com.tns).NativeScriptApplication.getInstance(); if(!nativeApp) {
// check whether the com.tns.NativeScriptApplication type exists
if(com.tns.NativeScriptApplication) {
nativeApp = com.tns.NativeScriptApplication.getInstance();
}
// the getInstance might return null if com.tns.NativeScriptApplication exists but is not the starting app type
if(!nativeApp) {
// check whether application.android.init has been explicitly called
let application = require("application");
nativeApp = application.android.nativeApp;
if(!nativeApp) {
// TODO: Should we handle the case when a custom application type is provided and the user has not explicitly initialized the application module?
let clazz = java.lang.Class.forName("android.app.ActivityThread");
if(clazz) {
let method = clazz.getMethod("currentApplication", null);
if(method) {
nativeApp = method.invoke(null, null);
}
}
}
}
// we cannot work without having the app instance
if(!nativeApp) {
throw new Error("Failed to retrieve native Android Application object. If you have a custom android.app.Application type implemented make sure that you've called the '<application-module>.android.init' method.")
}
}
return nativeApp;
}
export function getApplicationContext() {
let app = getApplication();
return app.getApplicationContext();
} }
export function getApplicationContext() { return <android.content.Context>getApplication().getApplicationContext(); }
var inputMethodManager: android.view.inputmethod.InputMethodManager; var inputMethodManager: android.view.inputmethod.InputMethodManager;
export function getInputMethodManager() { export function getInputMethodManager() {