From 6273bb8d2f7fe7343bc14418d9901e28872d29ce Mon Sep 17 00:00:00 2001 From: Hristo Hristov Date: Mon, 29 Feb 2016 09:57:45 +0200 Subject: [PATCH] FrameClass implemented as TypeScript class NativeActivity is now NativeScriptActivity and is real class Added NativeScriptApplication class Added JavaProxy attribute to FrameClass, NativeScriptActivity & NativeScriptApplication --- CrossPlatformModules.csproj | 2 +- application/application.android.ts | 114 +++--- application/application.d.ts | 7 - declarations.d.ts | 6 + globals/globals.ts | 2 +- ui/frame/frame.android.ts | 542 ++++++++++++++--------------- ui/frame/frame.d.ts | 1 - 7 files changed, 316 insertions(+), 358 deletions(-) diff --git a/CrossPlatformModules.csproj b/CrossPlatformModules.csproj index cd1bee5b7..42f6374cc 100644 --- a/CrossPlatformModules.csproj +++ b/CrossPlatformModules.csproj @@ -3,7 +3,7 @@ 12.0 $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) - 1.7 + 1.8 {2313F1BF-1F2D-4F11-806A-87927FA6A7C0} {349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc} Library diff --git a/application/application.android.ts b/application/application.android.ts index 917f1c9b8..e700e98fc 100644 --- a/application/application.android.ts +++ b/application/application.android.ts @@ -8,6 +8,34 @@ import * as fileResolverModule from "file-system/file-name-resolver"; global.moduleMerge(appModule, exports); var typedExports: typeof definition = exports; +@JavaProxy("com.tns.NativeScriptApplication") +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({ 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" @@ -15,13 +43,8 @@ function initEvents() { // 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 (!(activity instanceof (com).tns.NativeScriptActivity)) { - return; - } - if (!androidApp.startActivity) { androidApp.startActivity = activity; - androidApp.notify({ eventName: "activityCreated", object: androidApp, activity: activity, bundle: bundle }); if (androidApp.onActivityCreated) { @@ -33,9 +56,6 @@ function initEvents() { }, onActivityDestroyed: function (activity: any) { - if (!(activity instanceof (com).tns.NativeScriptActivity)) { - return; - } // Clear the current activity reference to prevent leak if (activity === androidApp.foregroundActivity) { @@ -67,10 +87,6 @@ function initEvents() { }, onActivityPaused: function (activity: any) { - if (!(activity instanceof (com).tns.NativeScriptActivity)) { - return; - } - androidApp.paused = true; if (activity === androidApp.foregroundActivity) { @@ -89,10 +105,6 @@ function initEvents() { }, onActivityResumed: function (activity: any) { - if (!(activity instanceof (com).tns.NativeScriptActivity)) { - return; - } - androidApp.paused = false; if (activity === androidApp.foregroundActivity) { @@ -111,10 +123,6 @@ function initEvents() { }, onActivitySaveInstanceState: function (activity: any, bundle: any) { - if (!(activity instanceof (com).tns.NativeScriptActivity)) { - return; - } - androidApp.notify({ eventName: "saveActivityState", object: androidApp, activity: activity, bundle: bundle }); if (androidApp.onSaveActivityState) { @@ -123,10 +131,6 @@ function initEvents() { }, onActivityStarted: function (activity: any) { - if (!(activity instanceof (com).tns.NativeScriptActivity)) { - return; - } - androidApp.foregroundActivity = activity; androidApp.notify({ eventName: "activityStarted", object: androidApp, activity: activity }); @@ -137,10 +141,6 @@ function initEvents() { }, onActivityStopped: function (activity: any) { - if (!(activity instanceof (com).tns.NativeScriptActivity)) { - return; - } - androidApp.notify({ eventName: "activityStopped", object: androidApp, activity: activity }); if (androidApp.onActivityStopped) { @@ -152,7 +152,7 @@ function initEvents() { return lifecycleCallbacks; } -export class AndroidApplication extends observable.Observable implements definition.AndroidApplication { +class AndroidApplication extends observable.Observable implements definition.AndroidApplication { public static activityCreatedEvent = "activityCreated"; public static activityDestroyedEvent = "activityDestroyed"; public static activityStartedEvent = "activityStarted"; @@ -190,10 +190,6 @@ export class AndroidApplication extends observable.Observable implements definit private _eventsToken: any; - public getActivity(intent: android.content.Intent): Object { - return frame.getActivity(); - } - public init(nativeApp: any) { this.nativeApp = nativeApp; this.packageName = nativeApp.getPackageName(); @@ -245,6 +241,10 @@ export class AndroidApplication extends observable.Observable implements definit } } +var androidApp = new AndroidApplication(); +// use the exports object instead of 'export var' due to global namespace collision +typedExports.android = androidApp; + var BroadcastReceiverClass; function ensureBroadCastReceiverClass() { if (BroadcastReceiverClass) { @@ -270,21 +270,6 @@ function ensureBroadCastReceiverClass() { BroadcastReceiverClass = BroadcastReceiver; } -global.__onUncaughtError = function (error: definition.NativeScriptError) { - var types: typeof typesModule = require("utils/types"); - - // TODO: Obsolete this - if (types.isFunction(typedExports.onUncaughtError)) { - typedExports.onUncaughtError(error); - } - - typedExports.notify({ eventName: typedExports.uncaughtErrorEvent, object: appModule.android, android: error }); -} - -function loadCss() { - typedExports.cssSelectorsCache = typedExports.loadCss(typedExports.cssFile); -} - var started = false; export function start(entry?: frame.NavigationEntry) { if (started) { @@ -292,31 +277,13 @@ export function start(entry?: frame.NavigationEntry) { } started = true; - if (entry) { typedExports.mainEntry = entry; } - // this should be the first call, to avoid issues when someone accesses the Application singleton prior to extending its onCreate method - app.init({ - getActivity: function (activity: android.app.Activity) { - var intent = activity.getIntent() - return androidApp.getActivity(intent); - }, - - onCreate: function () { - androidApp.init(this); - setupOrientationListener(androidApp); - } - }); - loadCss(); } -var androidApp = new AndroidApplication(); -// use the exports object instead of 'export var' due to global namespace collision -typedExports.android = androidApp; - var currentOrientation: number; function setupOrientationListener(androidApp: AndroidApplication) { androidApp.registerBroadcastReceiver(android.content.Intent.ACTION_CONFIGURATION_CHANGED, onConfigurationChanged); @@ -353,6 +320,10 @@ function onConfigurationChanged(context: android.content.Context, intent: androi } } +function loadCss() { + typedExports.cssSelectorsCache = typedExports.loadCss(typedExports.cssFile); +} + global.__onLiveSync = function () { if (typedExports.android && typedExports.android.paused) { return; @@ -368,4 +339,15 @@ global.__onLiveSync = function () { // Reload current page. frame.reloadPage(); +} + +global.__onUncaughtError = function (error: definition.NativeScriptError) { + var types: typeof typesModule = require("utils/types"); + + // TODO: Obsolete this + if (types.isFunction(typedExports.onUncaughtError)) { + typedExports.onUncaughtError(error); + } + + typedExports.notify({ eventName: typedExports.uncaughtErrorEvent, object: appModule.android, android: error }); } \ No newline at end of file diff --git a/application/application.d.ts b/application/application.d.ts index 17c28e783..c720bacb1 100644 --- a/application/application.d.ts +++ b/application/application.d.ts @@ -347,13 +347,6 @@ declare module "application" { */ paused: boolean; - /** - * 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: any /* android.content.Intent */): any; - /** * [Deprecated. Please use the respective event instead.] Direct handler of the [onActivityCreated method](http://developer.android.com/reference/android/app/Application.ActivityLifecycleCallbacks.html). */ diff --git a/declarations.d.ts b/declarations.d.ts index ed61cfc82..00eef4ad2 100644 --- a/declarations.d.ts +++ b/declarations.d.ts @@ -105,6 +105,12 @@ declare var require: NativeScriptRequire; declare function Deprecated(target: Object, key?: string | symbol, value?: any): void; declare function Experimental(target: Object, key?: string | symbol, value?: any): void; +/** + * Decorates class that extends native Java class + * @param nativeClassName The name of the newly generated class. Must be unique in the application. + */ +declare function JavaProxy(nativeClassName: string): ClassDecorator; + declare function Log(data: any): void; declare function log(data: any): void; declare function float(num: number): any; diff --git a/globals/globals.ts b/globals/globals.ts index 58fb5837c..b0f51aba0 100644 --- a/globals/globals.ts +++ b/globals/globals.ts @@ -102,7 +102,7 @@ if (platform.device.os === platform.platformNames.android) { if (typeof global.__decorate !== "function") { global.__decorate = function (decorators, target, key, desc) { - var c = arguments.length + var c = arguments.length; var r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof global.Reflect === "object" && typeof global.Reflect.decorate === "function") { diff --git a/ui/frame/frame.android.ts b/ui/frame/frame.android.ts index 073522be0..1ab8b836e 100644 --- a/ui/frame/frame.android.ts +++ b/ui/frame/frame.android.ts @@ -25,110 +25,7 @@ var navDepth = -1; var activityInitialized = false; -var animationFixed; -function ensureAnimationFixed() { - if (!animationFixed) { - animationFixed = android.os.Build.VERSION.SDK_INT >= 19 // android.os.Build.VERSION.KITKAT but we don't have definition for it - ? 1 : -1; - } -} - -var FragmentClass; -function ensureFragmentClass() { - if (FragmentClass) { - return; - } - - FragmentClass = (android.app.Fragment).extend({ - - onCreate: function (savedInstanceState: android.os.Bundle) { - trace.write(`${this.getTag()}.onCreate(${savedInstanceState})`, trace.categories.NativeLifecycle); - this.super.onCreate(savedInstanceState); - this.super.setHasOptionsMenu(true); - - // There is no entry set to the fragment, so this must be destroyed fragment that was recreated by Android. - // We should find its corresponding page in our backstack and set it manually. - if (!(this).entry) { - let frameId = (this).getArguments().getInt(FRAMEID); - let frame = getFrameById(frameId); - if (frame) { - this.frame = frame; - } - else { - throw new Error(`Cannot find Frame for ${this}`); - } - - findPageForFragment(this, this.frame); - } - }, - - onCreateView: function (inflater: android.view.LayoutInflater, container: android.view.ViewGroup, savedInstanceState: android.os.Bundle): android.view.View { - trace.write(`${this.getTag()}.onCreateView(inflater, container, ${savedInstanceState})`, trace.categories.NativeLifecycle); - var entry = this.entry; - var page = entry.resolvedPage; - if (savedInstanceState && savedInstanceState.getBoolean(HIDDEN, false)) { - this.super.getFragmentManager().beginTransaction().hide(this).commit(); - page._onAttached(this.getActivity()); - } - else { - onFragmentShown(this); - } - - return page._nativeView; - }, - - onHiddenChanged: function (hidden: boolean) { - trace.write(`${this.getTag()}.onHiddenChanged(${hidden})`, trace.categories.NativeLifecycle); - this.super.onHiddenChanged(hidden); - if (hidden) { - onFragmentHidden(this); - } - else { - onFragmentShown(this); - } - }, - - onSaveInstanceState: function (outState: android.os.Bundle) { - trace.write(`${this.getTag()}.onSaveInstanceState(${outState})`, trace.categories.NativeLifecycle); - this.super.onSaveInstanceState(outState); - if (this.isHidden()) { - outState.putBoolean(HIDDEN, true); - } - }, - - onDestroyView: function () { - trace.write(`${this.getTag()}.onDestroyView()`, trace.categories.NativeLifecycle); - this.super.onDestroyView(); - onFragmentHidden(this); - - // When Fragment is destroyed we detach page even if cachePagesOnNavigate is true. - let entry: definition.BackstackEntry = this.entry; - let page = entry.resolvedPage; - if (page._context) { - page._onDetached(true); - } - }, - - onDestroy: function () { - trace.write(`${this.getTag()}.onDestroy()`, trace.categories.NativeLifecycle); - this.super.onDestroy(); - utils.GC(); - }, - - onCreateAnimator: function (transit: number, enter: boolean, nextAnim: number): android.animation.Animator { - var animator = transitionModule._onFragmentCreateAnimator(this, nextAnim); - - if (!animator) { - animator = this.super.onCreateAnimator(transit, enter, nextAnim); - } - - trace.write(`${this.getTag()}.onCreateAnimator(${transit}, ${enter}, ${nextAnim}): ${animator}`, trace.categories.NativeLifecycle); - return animator; - } - }); -} - -function onFragmentShown(fragment) { +function onFragmentShown(fragment: FragmentClass) { trace.write(`SHOWN ${fragment.getTag()}`, trace.categories.NativeLifecycle); if (fragment[CLEARING_HISTORY]) { trace.write(`${fragment.getTag()} has been shown, but we are currently clearing history. Returning.`, trace.categories.NativeLifecycle); @@ -142,7 +39,7 @@ function onFragmentShown(fragment) { var page: pages.Page = entry.resolvedPage; let currentNavigationContext; - let navigationQueue = frame._navigationQueue; + let navigationQueue = (frame)._navigationQueue; for (let i = 0; i < navigationQueue.length; i++) { if (navigationQueue[i].entry === entry) { currentNavigationContext = navigationQueue[i]; @@ -165,7 +62,7 @@ function onFragmentShown(fragment) { transitionModule._onFragmentShown(fragment, isBack); } -function onFragmentHidden(fragment) { +function onFragmentHidden(fragment: FragmentClass) { trace.write(`HIDDEN ${fragment.getTag()}`, trace.categories.NativeLifecycle); if (fragment[CLEARING_HISTORY]) { @@ -175,7 +72,7 @@ function onFragmentHidden(fragment) { var isBack = fragment.entry[IS_BACK]; fragment.entry[IS_BACK] = undefined; - + // Handle page transitions. transitionModule._onFragmentHidden(fragment, isBack); } @@ -281,7 +178,6 @@ export class Frame extends frameCommon.Frame { } var newFragmentTag = "fragment" + navDepth; - ensureFragmentClass(); let newFragment = new FragmentClass(); let args = new android.os.Bundle(); @@ -454,178 +350,6 @@ export class Frame extends frameCommon.Frame { } } -var NativeActivity = { - - get rootView(): View { - return this[ROOT_VIEW]; - }, - - onCreate: function (savedInstanceState: android.os.Bundle) { - trace.write(`NativeScriptActivity.onCreate(${savedInstanceState})`, trace.categories.NativeLifecycle); - - let app = application.android; - let activity: android.app.Activity = this; - let intent = activity.getIntent(); - if (application.onLaunch) { - application.onLaunch(intent); - } - - let args: application.LaunchEventData = { eventName: application.launchEvent, object: app, android: intent }; - application.notify(args); - - let frameId = -1; - let rootView = args.root; - let extras = intent.getExtras(); - - // We have extras when we call - new Frame().navigate(); - // savedInstanceState is used when activity is recreated. - if (extras) { - frameId = extras.getInt(INTENT_EXTRA, -1); - } - else if (savedInstanceState) { - 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())) - // Then we check if we have frameId from savedInstanceState - this happens when Activity is destroyed but app was not (e.g. suspend) - // Only then we fallback to the view returned from the event. This is done in order to have backwards compatibility (remove it for 2.0.0). - let frame: Frame; - let navParam; - if (frameId >= 0) { - rootView = getFrameById(frameId); - } - else if (!rootView) { - navParam = application.mainEntry; - if (!navParam) { - navParam = application.mainModule; - } - - if (navParam) { - frame = new Frame(); - } else { - // TODO: Throw an exception? - throw new Error("A Frame must be used to navigate to a Page."); - } - - rootView = frame; - } - - // If there is savedInstanceState this call will recreate all fragments that were previously in the navigation. - // We take care of associating them with a Page from our backstack in the onAttachFragment callback. - // If there is savedInstanceState and activityInitialized is false we are restarted but process was killed. - // For now we treat it like first run (e.g. we are not passing savedInstanceState so no fragments are being restored). - // When we add support for application save/load state - revise this logic. - var isRestart = !!savedInstanceState && activityInitialized; - this.super.onCreate(isRestart ? savedInstanceState : null); - - this[ROOT_VIEW] = rootView; - - // Initialize native visual tree; - rootView._onAttached(this); - this.setContentView(rootView._nativeView, new org.nativescript.widgets.CommonLayoutParams()); - // frameId is negative w - if (frame) { - frame.navigate(navParam); - } - - activityInitialized = true; - // TODO: If the above fails because we call fragmentManager.beginTransition().commit() before - // we are added as content to activity - add if (rootview instanceof Frame) -> call navigate - //this.frame._onActivityCreated(isRestart); - }, - - onSaveInstanceState(outState: android.os.Bundle): void { - this.super.onSaveInstanceState(outState); - let view = this.rootView; - if (view instanceof Frame) { - outState.putInt(INTENT_EXTRA, (view).android.frameId); - } - }, - - onActivityResult: function (requestCode: number, resultCode: number, data: android.content.Intent) { - this.super.onActivityResult(requestCode, resultCode, data); - trace.write(`NativeScriptActivity.onActivityResult(${requestCode}, ${resultCode}, ${data})`, trace.categories.NativeLifecycle); - - var result = application.android.onActivityResult; - if (result) { - result(requestCode, resultCode, data); - } - - application.android.notify({ - eventName: "activityResult", - object: application.android, - activity: this, - requestCode: requestCode, - resultCode: resultCode, - intent: data - }); - }, - - onStart: function () { - this.super.onStart(); - trace.write("NativeScriptActivity.onStart();", trace.categories.NativeLifecycle); - let rootView: View = this.rootView - if (rootView && !rootView.isLoaded) { - rootView.onLoaded(); - } - }, - - onStop: function () { - this.super.onStop(); - trace.write("NativeScriptActivity.onStop();", trace.categories.NativeLifecycle); - let rootView: View = this.rootView - if (rootView && rootView.isLoaded) { - rootView.onUnloaded(); - } - }, - - onDestroy: function () { - let rootView: View = this.rootView - if (rootView && rootView._context) { - rootView._onDetached(true); - } - - this.super.onDestroy(); - trace.write("NativeScriptActivity.onDestroy();", trace.categories.NativeLifecycle); - }, - - onBackPressed: function () { - trace.write("NativeScriptActivity.onBackPressed;", trace.categories.NativeLifecycle); - - var args = { - eventName: "activityBackPressed", - object: application.android, - activity: this, - cancel: false, - }; - application.android.notify(args); - - if (args.cancel) { - return; - } - - if (!frameCommon.goBack()) { - this.super.onBackPressed(); - } - }, - - onLowMemory: function () { - trace.write("NativeScriptActivity.onLowMemory()", trace.categories.NativeLifecycle); - gc(); - java.lang.System.gc(); - this.super.onLowMemory(); - - application.notify({ eventName: application.lowMemoryEvent, object: this, android: this }); - }, - - onTrimMemory: function (level: number) { - trace.write(`NativeScriptActivity.onTrimMemory(${level})`, trace.categories.NativeLifecycle); - gc(); - java.lang.System.gc(); - this.super.onTrimMemory(level); - } -}; - var framesCounter = 0; var framesCache: Array> = new Array>(); @@ -803,6 +527,260 @@ function getFrameById(frameId: number): Frame { return null; } -export function getActivity(): Object { - return NativeActivity; +var animationFixed; +function ensureAnimationFixed() { + if (!animationFixed) { + // android.os.Build.VERSION.KITKAT but we don't have definition for it + animationFixed = android.os.Build.VERSION.SDK_INT >= 19 ? 1 : -1; + } } + +@JavaProxy("com.tns.FragmentClass") +class FragmentClass extends android.app.Fragment { + public frame: Frame; + public entry: definition.BackstackEntry; + + constructor() { + super(); + return global.__native(this); + } + + public onHiddenChanged(hidden: boolean): void { + trace.write(`${this.getTag()}.onHiddenChanged(${hidden})`, trace.categories.NativeLifecycle); + super.onHiddenChanged(hidden); + if (hidden) { + onFragmentHidden(this); + } + else { + onFragmentShown(this); + } + } + + public onCreateAnimator(transit: number, enter: boolean, nextAnim: number): android.animation.Animator { + var animator = transitionModule._onFragmentCreateAnimator(this, nextAnim); + if (!animator) { + animator = super.onCreateAnimator(transit, enter, nextAnim); + } + + trace.write(`${this.getTag()}.onCreateAnimator(${transit}, ${enter}, ${nextAnim}): ${animator}`, trace.categories.NativeLifecycle); + return animator; + } + + public onCreate(savedInstanceState: android.os.Bundle): void { + trace.write(`${this.getTag()}.onCreate(${savedInstanceState})`, trace.categories.NativeLifecycle); + super.onCreate(savedInstanceState); + super.setHasOptionsMenu(true); + + // There is no entry set to the fragment, so this must be destroyed fragment that was recreated by Android. + // We should find its corresponding page in our backstack and set it manually. + if (!this.entry) { + let frameId = this.getArguments().getInt(FRAMEID); + let frame = getFrameById(frameId); + if (frame) { + this.frame = frame; + } + else { + throw new Error(`Cannot find Frame for ${this}`); + } + + findPageForFragment(this, this.frame); + } + } + + public onCreateView(inflater: android.view.LayoutInflater, container: android.view.ViewGroup, savedInstanceState: android.os.Bundle): android.view.View { + trace.write(`${this.getTag()}.onCreateView(inflater, container, ${savedInstanceState})`, trace.categories.NativeLifecycle); + var entry = this.entry; + var page = entry.resolvedPage; + if (savedInstanceState && savedInstanceState.getBoolean(HIDDEN, false)) { + this.getFragmentManager().beginTransaction().hide(this).commit(); + page._onAttached(this.getActivity()); + } + else { + onFragmentShown(this); + } + + return page._nativeView; + } + + public onSaveInstanceState(outState: android.os.Bundle): void { + trace.write(`${this.getTag()}.onSaveInstanceState(${outState})`, trace.categories.NativeLifecycle); + super.onSaveInstanceState(outState); + if (this.isHidden()) { + outState.putBoolean(HIDDEN, true); + } + } + + public onDestroyView(): void { + trace.write(`${this.getTag()}.onDestroyView()`, trace.categories.NativeLifecycle); + super.onDestroyView(); + onFragmentHidden(this); + + // When Fragment is destroyed we detach page even if cachePagesOnNavigate is true. + let entry = this.entry; + let page = entry.resolvedPage; + if (page._context) { + page._onDetached(true); + } + } + + public onDestroy(): void { + trace.write(`${this.getTag()}.onDestroy()`, trace.categories.NativeLifecycle); + super.onDestroy(); + utils.GC(); + } +} + +@JavaProxy("com.tns.NativeScriptActivity") +class NativeScriptActivity extends android.app.Activity { + private rootView: View; + + constructor() { + super(); + return global.__native(this); + } + + protected onCreate(savedInstanceState: android.os.Bundle): void { + trace.write(`NativeScriptActivity.onCreate(${savedInstanceState})`, trace.categories.NativeLifecycle); + + let app = application.android; + let intent = this.getIntent(); + if (application.onLaunch) { + application.onLaunch(intent); + } + + let args: application.LaunchEventData = { eventName: application.launchEvent, object: app, android: intent }; + application.notify(args); + + let frameId = -1; + let rootView = args.root; + let extras = intent.getExtras(); + + // We have extras when we call - new Frame().navigate(); + // savedInstanceState is used when activity is recreated. + if (extras) { + frameId = extras.getInt(INTENT_EXTRA, -1); + } + else if (savedInstanceState) { + 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())) + // Then we check if we have frameId from savedInstanceState - this happens when Activity is destroyed but app was not (e.g. suspend) + // Only then we fallback to the view returned from the event. This is done in order to have backwards compatibility (remove it for 2.0.0). + let frame: Frame; + let navParam; + if (frameId >= 0) { + rootView = getFrameById(frameId); + } + else if (!rootView) { + navParam = application.mainEntry; + if (!navParam) { + navParam = application.mainModule; + } + + if (navParam) { + frame = new Frame(); + } else { + // TODO: Throw an exception? + throw new Error("A Frame must be used to navigate to a Page."); + } + + rootView = frame; + } + + // If there is savedInstanceState this call will recreate all fragments that were previously in the navigation. + // We take care of associating them with a Page from our backstack in the onAttachFragment callback. + // If there is savedInstanceState and activityInitialized is false we are restarted but process was killed. + // For now we treat it like first run (e.g. we are not passing savedInstanceState so no fragments are being restored). + // When we add support for application save/load state - revise this logic. + var isRestart = !!savedInstanceState && activityInitialized; + super.onCreate(isRestart ? savedInstanceState : null); + + this[ROOT_VIEW] = rootView; + + // Initialize native visual tree; + rootView._onAttached(this); + this.setContentView(rootView._nativeView, new org.nativescript.widgets.CommonLayoutParams()); + // frameId is negative w + if (frame) { + frame.navigate(navParam); + } + + activityInitialized = true; + } + + protected onSaveInstanceState(outState: android.os.Bundle): void { + super.onSaveInstanceState(outState); + let view = this.rootView; + if (view instanceof Frame) { + outState.putInt(INTENT_EXTRA, view.android.frameId); + } + } + + protected onStart(): void { + super.onStart(); + trace.write("NativeScriptActivity.onStart();", trace.categories.NativeLifecycle); + let rootView = this.rootView + if (rootView && !rootView.isLoaded) { + rootView.onLoaded(); + } + } + + protected onStop(): void { + super.onStop(); + trace.write("NativeScriptActivity.onStop();", trace.categories.NativeLifecycle); + let rootView = this.rootView + if (rootView && rootView.isLoaded) { + rootView.onUnloaded(); + } + } + + protected onDestroy(): void { + let rootView = this.rootView + if (rootView && rootView._context) { + rootView._onDetached(true); + } + + super.onDestroy(); + trace.write("NativeScriptActivity.onDestroy();", trace.categories.NativeLifecycle); + } + + public onBackPressed(): void { + trace.write("NativeScriptActivity.onBackPressed;", trace.categories.NativeLifecycle); + + var args = { + eventName: "activityBackPressed", + object: application.android, + activity: this, + cancel: false, + }; + application.android.notify(args); + + if (args.cancel) { + return; + } + + if (!frameCommon.goBack()) { + super.onBackPressed(); + } + } + + protected onActivityResult(requestCode: number, resultCode: number, data: android.content.Intent): void { + super.onActivityResult(requestCode, resultCode, data); + trace.write(`NativeScriptActivity.onActivityResult(${requestCode}, ${resultCode}, ${data})`, trace.categories.NativeLifecycle); + + var result = application.android.onActivityResult; + if (result) { + result(requestCode, resultCode, data); + } + + application.android.notify({ + eventName: "activityResult", + object: application.android, + activity: this, + requestCode: requestCode, + resultCode: resultCode, + intent: data + }); + } +} \ No newline at end of file diff --git a/ui/frame/frame.d.ts b/ui/frame/frame.d.ts index e4c8048e9..50095f2c9 100644 --- a/ui/frame/frame.d.ts +++ b/ui/frame/frame.d.ts @@ -310,6 +310,5 @@ declare module "ui/frame" { //@private function reloadPage(): void; function resolvePageFromEntry(entry: NavigationEntry): pages.Page; - function getActivity(): Object; //@endprivate } \ No newline at end of file