diff --git a/CrossPlatformModules.csproj b/CrossPlatformModules.csproj
index 6df402a84..b853675d0 100644
--- a/CrossPlatformModules.csproj
+++ b/CrossPlatformModules.csproj
@@ -83,6 +83,10 @@
data-binding.xml
+
+
+ list-view.xml
+
@@ -135,6 +139,15 @@
+
+ Designer
+
+
+ Always
+
+
+ Designer
+
@@ -2077,6 +2090,9 @@
+
+ PreserveNewest
+
diff --git a/application/application.android.ts b/application/application.android.ts
index 84d213f30..75222ff5c 100644
--- a/application/application.android.ts
+++ b/application/application.android.ts
@@ -191,41 +191,7 @@ export class AndroidApplication extends observable.Observable implements definit
private _eventsToken: any;
public getActivity(intent: android.content.Intent): Object {
- if (intent && intent.getAction() === android.content.Intent.ACTION_MAIN) {
- // application's main activity
- if (typedExports.onLaunch) {
- typedExports.onLaunch(intent);
- }
-
- typedExports.notify({ eventName: typedExports.launchEvent, object: this, android: intent });
-
- setupOrientationListener(this);
-
- /* 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 mainEntry/Module (if specified)
- var navParam = typedExports.mainEntry;
- if (!navParam) {
- navParam = typedExports.mainModule;
- }
-
- if (navParam) {
- topFrame = new frame.Frame();
- topFrame.navigate(navParam);
- } else {
- // TODO: Throw an exception?
- throw new Error("A Frame must be used to navigate to a Page.");
- }
- }
-
- return topFrame.android.onActivityRequested(intent);
+ return frame.getActivity();
}
public init(nativeApp: any) {
@@ -320,7 +286,7 @@ function loadCss() {
}
var started = false;
-export function start (entry?: frame.NavigationEntry) {
+export function start(entry?: frame.NavigationEntry) {
if (started) {
throw new Error("Application is already started.");
}
@@ -340,6 +306,7 @@ export function start (entry?: frame.NavigationEntry) {
onCreate: function () {
androidApp.init(this);
+ setupOrientationListener(androidApp);
}
});
loadCss();
@@ -352,8 +319,9 @@ typedExports.android = androidApp;
var currentOrientation: number;
function setupOrientationListener(androidApp: AndroidApplication) {
androidApp.registerBroadcastReceiver(android.content.Intent.ACTION_CONFIGURATION_CHANGED, onConfigurationChanged);
- currentOrientation = androidApp.context.getResources().getConfiguration().orientation
+ currentOrientation = androidApp.context.getResources().getConfiguration().orientation;
}
+
function onConfigurationChanged(context: android.content.Context, intent: android.content.Intent) {
var orientation = context.getResources().getConfiguration().orientation;
diff --git a/application/application.d.ts b/application/application.d.ts
index 060a13c3f..17c28e783 100644
--- a/application/application.d.ts
+++ b/application/application.d.ts
@@ -5,7 +5,7 @@ declare module "application" {
import cssSelector = require("ui/styling/css-selector");
import observable = require("data/observable");
import frame = require("ui/frame");
-
+ import {View} from "ui/core/view";
/**
* An extended JavaScript Error which will have the nativeError property initialized in case the error is caused by executing platform-specific code.
*/
@@ -76,6 +76,17 @@ declare module "application" {
object: any;
}
+ /**
+ * Event data containing information for launch event.
+ */
+ export interface LaunchEventData extends ApplicationEventData {
+ /**
+ * The root view for this Window on iOS or Activity for Android.
+ * If not set a new Frame will be created as a root view in order to maintain backwards compatibility.
+ */
+ root?: View;
+ }
+
/**
* Event data containing information for orientation changed event.
*/
@@ -130,7 +141,7 @@ declare module "application" {
/**
* The main entry point event. This method is expected to use the root frame to navigate to the main application page.
*/
- export function onLaunch(context: any): void;
+ export function onLaunch(context?: any): void;
/**
* A callback to be used when an uncaught error occurs while the application is running.
@@ -174,7 +185,7 @@ declare module "application" {
* @param callback - Callback function which will be removed.
* @param thisArg - An optional parameter which will be used as `this` context for callback execution.
*/
- export function off(eventNames: string, callback ?: any, thisArg ?: any);
+ export function off(eventNames: string, callback?: any, thisArg?: any);
/**
* Notifies all the registered listeners for the event provided in the data.eventName.
@@ -191,7 +202,7 @@ declare module "application" {
/**
* This event is raised on application launchEvent.
*/
- export function on(event: "launch", callback: (args: ApplicationEventData) => void, thisArg?: any);
+ export function on(event: "launch", callback: (args: LaunchEventData) => void, thisArg?: any);
/**
* This event is raised when the Application is suspended.
diff --git a/application/application.ios.ts b/application/application.ios.ts
index ed0674491..47599e198 100644
--- a/application/application.ios.ts
+++ b/application/application.ios.ts
@@ -1,5 +1,5 @@
import common = require("./application-common");
-import frame = require("ui/frame");
+import {Frame, NavigationEntry, reloadPage} from "ui/frame";
import definition = require("application");
import * as uiUtils from "ui/utils";
import * as typesModule from "utils/types";
@@ -114,32 +114,51 @@ class IOSApplication implements definition.iOSApplication {
typedExports.onLaunch(undefined);
}
- typedExports.notify({
+ let args: definition.LaunchEventData = {
eventName: typedExports.launchEvent,
object: this,
ios: notification.userInfo && notification.userInfo.objectForKey("UIApplicationLaunchOptionsLocalNotificationKey") || null
- });
+ };
- var topFrame = frame.topmost();
- if (!topFrame) {
+ typedExports.notify(args);
+
+ let rootView = args.root;
+ let frame: Frame;
+ let navParam: Object;
+ if (!rootView) {
// try to navigate to the mainEntry/Module (if specified)
- var navParam = typedExports.mainEntry;
+ navParam = typedExports.mainEntry;
if (!navParam) {
navParam = typedExports.mainModule;
}
if (navParam) {
- topFrame = new frame.Frame();
- topFrame.navigate(navParam);
+ frame = new Frame();
+ frame.navigate(navParam);
} else {
// TODO: Throw an exception?
throw new Error("A Frame must be used to navigate to a Page.");
}
+
+ rootView = frame;
}
+
+ this._window.content = rootView;
- this._window.content = topFrame;
-
- this.rootController = this._window.rootViewController = topFrame.ios.controller;
+ if (rootView instanceof Frame) {
+ this.rootController = this._window.rootViewController = rootView.ios.controller;
+ }
+ else if (rootView.ios instanceof UIViewController) {
+ this.rootController = this._window.rootViewController = rootView.ios;
+ }
+ else if (rootView.ios instanceof UIView) {
+ let newController = new UIViewController();
+ newController.view.addSubview(rootView.ios);
+ this.rootController = newController;
+ }
+ else {
+ throw new Error("Root should be either UIViewController or UIView");
+ }
this._window.makeKeyAndVisible();
}
@@ -229,7 +248,7 @@ function loadCss() {
}
var started: boolean = false;
-typedExports.start = function (entry?: frame.NavigationEntry) {
+typedExports.start = function (entry?: NavigationEntry) {
if (!started) {
if (entry) {
exports.mainEntry = entry;
@@ -256,5 +275,5 @@ global.__onLiveSync = function () {
loadCss();
// Reload current page.
- frame.reloadPage();
+ reloadPage();
}
\ No newline at end of file
diff --git a/apps/custom-root-view/app.ts b/apps/custom-root-view/app.ts
new file mode 100644
index 000000000..4c9e6cd1f
--- /dev/null
+++ b/apps/custom-root-view/app.ts
@@ -0,0 +1,14 @@
+import app = require("application");
+import {Frame} from "ui/frame";
+import {TabView} from "ui/tab-view";
+import * as builder from "ui/builder";
+
+app.on("launch", function (args) {
+ var tabView = builder.load((__dirname + "/main-page.xml"));
+ args.root = tabView;
+ var frame = tabView.items[0].view;
+ var basePath = "list-view";
+ frame.navigate(basePath);
+});
+
+app.start();
\ No newline at end of file
diff --git a/apps/custom-root-view/list-view.xml b/apps/custom-root-view/list-view.xml
new file mode 100644
index 000000000..75c7a9c66
--- /dev/null
+++ b/apps/custom-root-view/list-view.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/apps/custom-root-view/main-page.css b/apps/custom-root-view/main-page.css
new file mode 100644
index 000000000..115f58fbc
--- /dev/null
+++ b/apps/custom-root-view/main-page.css
@@ -0,0 +1,29 @@
+.title {
+ font-size: 20;
+ margin: 3;
+}
+
+.author {
+ font-size: 14;
+ horizontal-align: left;
+ vertical-align: bottom;
+ margin: 3;
+}
+
+.comments {
+ color: #10C2B0;
+ font-size: 14;
+ vertical-align: bottom;
+ margin: 3;
+}
+
+.thumbnail {
+ width: 72;
+ height: 72;
+ margin: 3;
+ vertical-align: top;
+}
+
+TabView {
+ background-color: white;
+}
\ No newline at end of file
diff --git a/apps/custom-root-view/main-page.xml b/apps/custom-root-view/main-page.xml
new file mode 100644
index 000000000..7984dc0d6
--- /dev/null
+++ b/apps/custom-root-view/main-page.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/apps/custom-root-view/package.json b/apps/custom-root-view/package.json
new file mode 100644
index 000000000..5daa2ee99
--- /dev/null
+++ b/apps/custom-root-view/package.json
@@ -0,0 +1,4 @@
+{
+ "name": "custom-root-viewcustom-root-view",
+ "main": "app.js"
+}
diff --git a/tsconfig.json b/tsconfig.json
index c59a5946c..482b8cbb0 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -52,6 +52,8 @@
"apps/animations/opacity.ts",
"apps/connectivity-demo/app.ts",
"apps/connectivity-demo/main-page.ts",
+ "apps/custom-root-view/app.ts",
+ "apps/custom-root-view/list-view.ts",
"apps/cuteness.io/app.ts",
"apps/cuteness.io/details-page.ts",
"apps/cuteness.io/main-page.ts",
diff --git a/ui/core/view.android.ts b/ui/core/view.android.ts
index c0f98b18d..fe4024d90 100644
--- a/ui/core/view.android.ts
+++ b/ui/core/view.android.ts
@@ -196,7 +196,7 @@ export class View extends viewCommon.View {
}
if (this._context) {
- this._onDetached();
+ this._onDetached(true);
}
this._context = context;
diff --git a/ui/frame/frame.android.ts b/ui/frame/frame.android.ts
index 1161f3e4e..2ed763d60 100644
--- a/ui/frame/frame.android.ts
+++ b/ui/frame/frame.android.ts
@@ -1,11 +1,12 @@
-import frameCommon = require("./frame-common");
-import definition = require("ui/frame");
+import definition = require("ui/frame");
+import frameCommon = require("./frame-common");
import pages = require("ui/page");
+import {View} from "ui/core/view";
+import {Observable} from "data/observable";
import trace = require("trace");
-import observable = require("data/observable");
import application = require("application");
import * as types from "utils/types";
-import * as utilsModule from "utils/utils";
+import * as utils from "utils/utils";
import transitionModule = require("ui/transition");
global.moduleMerge(frameCommon, exports);
@@ -14,15 +15,25 @@ var TAG = "_fragmentTag";
var OWNER = "_owner";
var HIDDEN = "_hidden";
var INTENT_EXTRA = "com.tns.activity";
-var ANDROID_FRAME = "android_frame";
+var ROOT_VIEW = "_rootView";
var BACKSTACK_TAG = "_backstackTag";
var IS_BACK = "_isBack";
var NAV_DEPTH = "_navDepth";
var CLEARING_HISTORY = "_clearingHistory";
-var activityInitialized = false;
+var FRAMEID = "_frameId";
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) {
@@ -30,72 +41,92 @@ function ensureFragmentClass() {
}
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);
- },
- 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;
- },
+ onCreate: function (savedInstanceState: android.os.Bundle) {
+ trace.write(`${this.getTag()}.onCreate(${savedInstanceState})`, trace.categories.NativeLifecycle);
+ this.super.onCreate(savedInstanceState);
+ this.super.setHasOptionsMenu(true);
- onHiddenChanged: function (hidden: boolean) {
- trace.write(`${this.getTag()}.onHiddenChanged(${hidden})`, trace.categories.NativeLifecycle);
- this.super.onHiddenChanged(hidden);
- if (hidden) {
+ // 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;
}
- 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);
- },
-
- onDestroy: function () {
- trace.write(`${this.getTag()}.onDestroy()`, trace.categories.NativeLifecycle);
- this.super.onDestroy();
-
- var utils: typeof utilsModule = require("utils/utils");
-
- 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) {
@@ -153,13 +184,15 @@ function onFragmentHidden(fragment) {
export class Frame extends frameCommon.Frame {
private _android: AndroidFrame;
private _delayedNavigationEntry: definition.BackstackEntry;
- private _isFirstNavigation = false;
private _containerViewId: number;
-
+ private _listener: android.view.View.OnAttachStateChangeListener;
constructor() {
super();
- this._containerViewId = android.view.View.generateViewId();
this._android = new AndroidFrame(this);
+ this._listener = new android.view.View.OnAttachStateChangeListener({
+ onViewAttachedToWindow: this.onNativeViewAttachedToWindow.bind(this),
+ onViewDetachedFromWindow: this.onNativeViewDetachedToWindow.bind(this)
+ });
}
public static get defaultAnimatedNavigation(): boolean {
@@ -180,7 +213,7 @@ export class Frame extends frameCommon.Frame {
return this._containerViewId;
}
- get android(): definition.AndroidFrame {
+ get android(): AndroidFrame {
return this._android;
}
@@ -191,27 +224,27 @@ export class Frame extends frameCommon.Frame {
public _navigateCore(backstackEntry: definition.BackstackEntry) {
trace.write(`${this}._navigateCore(page: ${backstackEntry.resolvedPage}, backstackVisible: ${this._isEntryBackstackVisible(backstackEntry)}, clearHistory: ${backstackEntry.entry.clearHistory}), navDepth: ${navDepth}`, trace.categories.Navigation);
- var activity = this._android.activity;
+ let activity = this._android.activity;
if (!activity) {
// We do not have an Activity yet associated. In this case we have two execution paths:
// 1. This is the main frame for the application
// 2. This is an inner frame which requires a new Activity
- var currentActivity = this._android.currentActivity;
+ let currentActivity = this._android.currentActivity;
if (currentActivity) {
- startActivity(currentActivity, backstackEntry.entry);
+ startActivity(currentActivity, this._android.frameId);
}
this._delayedNavigationEntry = backstackEntry;
return;
}
- var manager = activity.getFragmentManager();
+ let manager = activity.getFragmentManager();
// Clear history
- if (backstackEntry.entry.clearHistory && !this._isFirstNavigation) {
- var backStackEntryCount = manager.getBackStackEntryCount();
- var i = backStackEntryCount - 1;
- var fragment: android.app.Fragment;
+ if (backstackEntry.entry.clearHistory) {
+ let backStackEntryCount = manager.getBackStackEntryCount();
+ let i = backStackEntryCount - 1;
+ let fragment: android.app.Fragment;
while (i >= 0) {
fragment = manager.findFragmentByTag(manager.getBackStackEntryAt(i--).getName());
trace.write(`${fragment.getTag()}[CLEARING_HISTORY] = true;`, trace.categories.NativeLifecycle);
@@ -228,7 +261,7 @@ export class Frame extends frameCommon.Frame {
}
if (backStackEntryCount) {
- var firstEntryName = manager.getBackStackEntryAt(0).getName();
+ let firstEntryName = manager.getBackStackEntryAt(0).getName();
trace.write(`manager.popBackStack(${firstEntryName}, android.app.FragmentManager.POP_BACK_STACK_INCLUSIVE);`, trace.categories.NativeLifecycle);
manager.popBackStack(firstEntryName, android.app.FragmentManager.POP_BACK_STACK_INCLUSIVE);
}
@@ -238,7 +271,7 @@ export class Frame extends frameCommon.Frame {
navDepth++;
- var fragmentTransaction = manager.beginTransaction();
+ let fragmentTransaction = manager.beginTransaction();
var currentFragmentTag: string;
var currentFragment: android.app.Fragment;
@@ -249,7 +282,11 @@ export class Frame extends frameCommon.Frame {
var newFragmentTag = "fragment" + navDepth;
ensureFragmentClass();
- var newFragment = new FragmentClass();
+ let newFragment = new FragmentClass();
+
+ let args = new android.os.Bundle();
+ args.putInt(FRAMEID, this._android.frameId);
+ newFragment.setArguments(args);
var animated = this._getIsAnimatedNavigation(backstackEntry.entry);
var navigationTransition = this._getNavigationTransition(backstackEntry.entry);
@@ -257,6 +294,7 @@ export class Frame extends frameCommon.Frame {
// There might be transitions left over from previous forward navigations from the current page.
transitionModule._clearForwardTransitions(currentFragment);
}
+
if (animated && navigationTransition) {
transitionModule._setAndroidFragmentTransitions(navigationTransition, currentFragment, newFragment, fragmentTransaction);
}
@@ -270,7 +308,8 @@ export class Frame extends frameCommon.Frame {
// remember the fragment tag at page level so that we can retrieve the fragment associated with a Page instance
backstackEntry.resolvedPage[TAG] = newFragmentTag;
- if (this._isFirstNavigation) {
+ let isFirstNavigation = types.isNullOrUndefined(this._currentEntry);
+ if (isFirstNavigation) {
fragmentTransaction.add(this.containerViewId, newFragment, newFragmentTag);
trace.write(`fragmentTransaction.add(${newFragmentTag});`, trace.categories.NativeLifecycle);
}
@@ -295,14 +334,16 @@ export class Frame extends frameCommon.Frame {
// Add to backStack if needed.
if (this.backStack.length > 0 && this._currentEntry) {
// We add each entry in the backstack to avoid the "Stack corrupted" mismatch
- var backstackTag = this._currentEntry[BACKSTACK_TAG];
+ let backstackTag = this._currentEntry[BACKSTACK_TAG];
fragmentTransaction.addToBackStack(backstackTag);
trace.write(`fragmentTransaction.addToBackStack(${backstackTag});`, trace.categories.NativeLifecycle);
}
}
- if (!this._isFirstNavigation) {
- if (this.android.cachePagesOnNavigate) {
+ if (!isFirstNavigation) {
+ // This bug is fixed on API19+
+ ensureAnimationFixed();
+ if (this.android.cachePagesOnNavigate && animationFixed < 0) {
// Apparently, there is an Android bug with when hiding fragments with animation.
// https://code.google.com/p/android/issues/detail?id=32405
// When bug is fixed use animated variable.
@@ -326,7 +367,7 @@ export class Frame extends frameCommon.Frame {
this._currentEntry[IS_BACK] = true;
}
- trace.write(`${this}._goBackCore(pageId: ${backstackEntry.resolvedPage.id}, backstackVisible: ${this._isEntryBackstackVisible(backstackEntry) }, clearHistory: ${backstackEntry.entry.clearHistory}), navDepth: ${navDepth}`, trace.categories.Navigation);
+ trace.write(`${this}._goBackCore(pageId: ${backstackEntry.resolvedPage.id}, backstackVisible: ${this._isEntryBackstackVisible(backstackEntry)}, clearHistory: ${backstackEntry.entry.clearHistory}), navDepth: ${navDepth}`, trace.categories.Navigation);
var manager = this._android.activity.getFragmentManager();
if (manager.getBackStackEntryCount() > 0) {
@@ -337,27 +378,22 @@ export class Frame extends frameCommon.Frame {
}
public _createUI() {
- // TODO: Implement for nested frames
- // this._android.layout = new android.widget.FrameLayout(this._context);
- // this._android.layout.setId(android.view.View.generateViewId());
+ let root = new org.nativescript.widgets.ContentLayout(this._context);
+ this._containerViewId = android.view.View.generateViewId();
+ this._android.rootViewGroup = root;
+ this._android.rootViewGroup.setId(this._containerViewId);
+ this._android.rootViewGroup.addOnAttachStateChangeListener(this._listener);
}
- public _onActivityCreated(isRestart: boolean) {
- this._onAttached(this._android.activity);
-
- var backstackEntry = this._currentEntry || this._delayedNavigationEntry;
-
- if (isRestart) {
- this._onNavigatingTo(backstackEntry, false);
- this._onNavigatedTo(backstackEntry, false);
- }
- else {
- this._isFirstNavigation = true;
- this._navigateCore(backstackEntry);
- this._isFirstNavigation = false;
+ private onNativeViewAttachedToWindow(view: android.view.View): void {
+ if (this._delayedNavigationEntry) {
+ this._navigateCore(this._delayedNavigationEntry);
+ this._delayedNavigationEntry = undefined;
}
+ }
- this._delayedNavigationEntry = undefined;
+ private onNativeViewDetachedToWindow(view: android.view.View): void {
+ // unused for the moment.
}
public _popFromFrameStack() {
@@ -372,7 +408,9 @@ export class Frame extends frameCommon.Frame {
}
public _clearAndroidReference() {
+ this._android.rootViewGroup.removeOnAttachStateChangeListener(this._listener);
// we should keep the reference to underlying native object, since frame can contain many pages.
+ this._android.rootViewGroup = null;
}
public _printNativeBackStack() {
@@ -416,32 +454,58 @@ export class Frame extends frameCommon.Frame {
var NativeActivity = {
- get frame(): Frame {
- if (this.androidFrame) {
- return this.androidFrame.owner;
- }
- return null;
- },
-
- get androidFrame(): AndroidFrame {
- return this[ANDROID_FRAME];
+ get rootView(): View {
+ return this[ROOT_VIEW];
},
onCreate: function (savedInstanceState: android.os.Bundle) {
- trace.write(`NativeActivity.onCreate(${savedInstanceState})`, trace.categories.NativeLifecycle);
+ trace.write(`NativeScriptActivity.onCreate(${savedInstanceState})`, trace.categories.NativeLifecycle);
- // Find the frame for this activity.
- var frameId = this.getIntent().getExtras().getInt(INTENT_EXTRA);
- for (var i = 0; i < framesCache.length; i++) {
- var aliveFrame = framesCache[i].get();
- if (aliveFrame && aliveFrame.frameId === frameId) {
- this[ANDROID_FRAME] = aliveFrame;
- break;
- }
+ let app = application.android;
+ let activity: android.app.Activity = this;
+ let intent = activity.getIntent();
+ if (application.onLaunch) {
+ application.onLaunch(intent);
}
- if (!this.androidFrame) {
- throw new Error("Could not find AndroidFrame for Activity");
+ 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.
@@ -452,24 +516,33 @@ var NativeActivity = {
var isRestart = !!savedInstanceState && activityInitialized;
this.super.onCreate(isRestart ? savedInstanceState : null);
- this.androidFrame.setActivity(this);
+ this[ROOT_VIEW] = rootView;
- // Create and set content container.
- var root = new org.nativescript.widgets.ContentLayout(this);
+ // 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);
+ }
- this.androidFrame.rootViewGroup = root;
- this.androidFrame.rootViewGroup.setId(this.frame.containerViewId);
- this.setContentView(this.androidFrame.rootViewGroup, new org.nativescript.widgets.CommonLayoutParams());
-
- // If there is no instance state - we call navigateCore from here since Activity is created AFTER the navigate call and navigateCore will fail.
-
activityInitialized = true;
- this.frame._onActivityCreated(isRestart);
+ // 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(`NativeActivity.onActivityResult(${requestCode}, ${resultCode}, ${data})`, trace.categories.NativeLifecycle);
+ trace.write(`NativeScriptActivity.onActivityResult(${requestCode}, ${resultCode}, ${data})`, trace.categories.NativeLifecycle);
var result = application.android.onActivityResult;
if (result) {
@@ -486,66 +559,36 @@ var NativeActivity = {
});
},
- onAttachFragment: function (fragment: android.app.Fragment) {
- trace.write(`NativeActivity.onAttachFragment(${fragment.getTag()})`, trace.categories.NativeLifecycle);
- this.super.onAttachFragment(fragment);
-
- if (!(fragment).entry) {
- // 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.
- findPageForFragment(fragment, this.frame);
- }
- },
-
onStart: function () {
this.super.onStart();
- trace.write("NativeActivity.onStart()", trace.categories.NativeLifecycle);
- if (!this.frame.isLoaded) {
- this.frame.onLoaded();
+ 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("NativeActivity.onStop()", trace.categories.NativeLifecycle);
- this.frame.onUnloaded();
+ trace.write("NativeScriptActivity.onStop();", trace.categories.NativeLifecycle);
+ let rootView: View = this.rootView
+ if (rootView && rootView.isLoaded) {
+ rootView.onUnloaded();
+ }
},
onDestroy: function () {
- trace.write("NativeActivity.onDestroy()", trace.categories.NativeLifecycle);
- // TODO: Implement uninitialized(detached) routine
- var frame = this.frame;
- frame._onDetached(true);
-
- // There might be cached pages in the backstack - force detach them too.
- for (var i = 0; i < frame.backStack.length; i++) {
- frame.backStack[i].resolvedPage._onDetached(true);
+ let rootView: View = this.rootView
+ if (rootView) {
+ rootView._onDetached(true);
}
- this.androidFrame.reset();
-
this.super.onDestroy();
- },
-
- onOptionsItemSelected: function (menuItem: android.view.IMenuItem) {
- trace.write(`NativeActivity.onOptionsItemSelected(${menuItem})`, trace.categories.NativeLifecycle);
- if (!this.androidFrame.hasListeners(frameCommon.Frame.androidOptionSelectedEvent)) {
- return false;
- }
-
- var data: definition.AndroidOptionEventData = {
- handled: false,
- eventName: frameCommon.Frame.androidOptionSelectedEvent,
- item: menuItem,
- object: this.androidFrame
- }
-
- this.androidFrame.notify(data);
- return data.handled;
+ trace.write("NativeScriptActivity.onDestroy();", trace.categories.NativeLifecycle);
},
onBackPressed: function () {
- trace.write("NativeActivity.onBackPressed()", trace.categories.NativeLifecycle);
+ trace.write("NativeScriptActivity.onBackPressed;", trace.categories.NativeLifecycle);
var args = {
eventName: "activityBackPressed",
@@ -565,7 +608,7 @@ var NativeActivity = {
},
onLowMemory: function () {
- trace.write("NativeActivity.onLowMemory()", trace.categories.NativeLifecycle);
+ trace.write("NativeScriptActivity.onLowMemory()", trace.categories.NativeLifecycle);
gc();
java.lang.System.gc();
this.super.onLowMemory();
@@ -574,7 +617,7 @@ var NativeActivity = {
},
onTrimMemory: function (level: number) {
- trace.write(`NativeActivity.onTrimMemory(${level})`, trace.categories.NativeLifecycle);
+ trace.write(`NativeScriptActivity.onTrimMemory(${level})`, trace.categories.NativeLifecycle);
gc();
java.lang.System.gc();
this.super.onTrimMemory(level);
@@ -584,13 +627,12 @@ var NativeActivity = {
var framesCounter = 0;
var framesCache: Array> = new Array>();
-class AndroidFrame extends observable.Observable implements definition.AndroidFrame {
+class AndroidFrame extends Observable implements definition.AndroidFrame {
public rootViewGroup: android.view.ViewGroup;
public hasOwnActivity = false;
public frameId;
private _showActionBar = true;
- private _activity: android.app.Activity;
private _owner: Frame;
private _cachePagesOnNavigate: boolean;
@@ -615,12 +657,13 @@ class AndroidFrame extends observable.Observable implements definition.AndroidFr
}
public get activity(): android.app.Activity {
- if (this._activity) {
- return this._activity;
+ let activity: android.app.Activity = this.owner._context;
+ if (activity) {
+ return activity;
}
// traverse the parent chain for an ancestor Frame
- var currView = this._owner.parent;
+ let currView = this._owner.parent;
while (currView) {
if (currView instanceof Frame) {
return (currView).android.activity;
@@ -633,12 +676,12 @@ class AndroidFrame extends observable.Observable implements definition.AndroidFr
}
public get actionBar(): android.app.ActionBar {
- var activity = this.currentActivity;
+ let activity = this.currentActivity;
if (!activity) {
return undefined;
}
- var bar = activity.getActionBar();
+ let bar = activity.getActionBar();
if (!bar) {
return undefined;
}
@@ -647,12 +690,12 @@ class AndroidFrame extends observable.Observable implements definition.AndroidFr
}
public get currentActivity(): android.app.Activity {
- var activity = this.activity;
+ let activity = this.activity;
if (activity) {
return activity;
}
- var stack = frameCommon.stack(),
+ let stack = frameCommon.stack(),
length = stack.length,
i = length - 1,
frame: definition.Frame;
@@ -686,39 +729,13 @@ class AndroidFrame extends observable.Observable implements definition.AndroidFr
}
}
- public onActivityRequested(intent: android.content.Intent): Object {
- if (this.activity) {
- throw new Error("Frame already attached to an Activity");
- }
-
- intent.putExtra(INTENT_EXTRA, this.frameId);
- this.hasOwnActivity = true;
- return this.createActivity(intent);
- }
-
public canGoBack() {
- if (!this._activity) {
+ if (!this.activity) {
return false;
}
// can go back only if it is not the main one.
- return this._activity.getIntent().getAction() !== android.content.Intent.ACTION_MAIN;
- }
-
- public reset() {
- // TODO: Cleanup, do we need more?
- delete this.rootViewGroup[OWNER];
- this._activity = undefined;
- this.rootViewGroup = undefined;
- }
-
- public setActivity(value: android.app.Activity) {
- this._activity = value;
- }
-
- private createActivity(intent: android.content.Intent) {
- // TODO: check intent
- return NativeActivity;
+ return this.activity.getIntent().getAction() !== android.content.Intent.ACTION_MAIN;
}
}
@@ -763,9 +780,27 @@ function findPageForFragment(fragment: android.app.Fragment, frame: Frame) {
}
}
-function startActivity(activity: android.app.Activity, entry: definition.NavigationEntry) {
+function startActivity(activity: android.app.Activity, frameId: number) {
var intent = new android.content.Intent(activity, (com).tns.NativeScriptActivity.class);
intent.setAction(android.content.Intent.ACTION_DEFAULT);
+ intent.putExtra(INTENT_EXTRA, frameId);
+
// TODO: Put the navigation context (if any) in the intent
activity.startActivity(intent);
+}
+
+function getFrameById(frameId: number): Frame {
+ // Find the frame for this activity.
+ for (let i = 0; i < framesCache.length; i++) {
+ let aliveFrame = framesCache[i].get();
+ if (aliveFrame && aliveFrame.frameId === frameId) {
+ return aliveFrame.owner;
+ }
+ }
+
+ return null;
+}
+
+export function getActivity(): Object {
+ return NativeActivity;
}
\ No newline at end of file
diff --git a/ui/frame/frame.d.ts b/ui/frame/frame.d.ts
index 2b4a28490..177243dbc 100644
--- a/ui/frame/frame.d.ts
+++ b/ui/frame/frame.d.ts
@@ -13,6 +13,7 @@ declare module "ui/frame" {
*/
export class Frame extends view.View {
/**
+ * Deprecated.
* String value used when hooking to androidOptionSelected event (prefix `android` states that this event is available only in Android).
*/
public static androidOptionSelectedEvent: string;
@@ -247,12 +248,6 @@ declare module "ui/frame" {
*/
actionBar: any /* android.app.ActionBar */;
- /**
- * A function called by the Runtime whenever a new Activity is about to be opened.
- * @param intent The native [android Intent](http://developer.android.com/reference/android/content/Intent.html) object passed to the Activity's onCreate method.
- */
- onActivityRequested(intent: any /* android.content.Intent */): Object;
-
/**
* Determines whether the Activity associated with this Frame will display an action bar or not.
*/
@@ -286,5 +281,6 @@ declare module "ui/frame" {
//@private
function reloadPage(): void;
function resolvePageFromEntry(entry: NavigationEntry): pages.Page;
+ function getActivity(): Object;
//@endprivate
}
\ No newline at end of file
diff --git a/ui/tab-view/tab-view.android.ts b/ui/tab-view/tab-view.android.ts
index aa495e120..5eec24a2c 100644
--- a/ui/tab-view/tab-view.android.ts
+++ b/ui/tab-view/tab-view.android.ts
@@ -77,17 +77,17 @@ function ensurePagerAdapterClass() {
if (this[VIEWS_STATES]) {
trace.write("TabView.PagerAdapter.instantiateItem; restoreHierarchyState: " + item.view, common.traceCategory);
- item.view.android.restoreHierarchyState(this[VIEWS_STATES]);
+ item.view._nativeView.restoreHierarchyState(this[VIEWS_STATES]);
}
- container.addView(item.view.android);
- return item.view.android;
+ container.addView(item.view._nativeView);
+ return item.view._nativeView;
}
destroyItem(container: android.view.ViewGroup, index: number, _object: any) {
trace.write("TabView.PagerAdapter.destroyItem; container: " + container + "; index: " + index + "; _object: " + _object, common.traceCategory);
var item = this.items[index];
- var nativeView = item.view.android;
+ var nativeView = item.view._nativeView;
if (nativeView.toString() !== _object.toString()) {
throw new Error("Expected " + nativeView.toString() + " to equal " + _object.toString());
@@ -100,7 +100,7 @@ function ensurePagerAdapterClass() {
container.removeView(nativeView);
- // Note: this.owner._removeView will clear item.view.android.
+ // Note: this.owner._removeView will clear item.view._nativeView.
// So call this after the native instance is removed form the container.
if (item.view.parent === this.owner) {
this.owner._removeView(item.view);
@@ -124,7 +124,7 @@ function ensurePagerAdapterClass() {
}
var viewStates = this[VIEWS_STATES];
var childCallback = function (view: view.View): boolean {
- var nativeView: android.view.View = view.android;
+ var nativeView: android.view.View = view._nativeView;
if (nativeView && nativeView.isSaveFromParentEnabled && nativeView.isSaveFromParentEnabled()) {
nativeView.saveHierarchyState(viewStates);
}
@@ -173,7 +173,7 @@ function ensurePageChangedListenerClass() {
function selectedColorPropertyChanged(data: dependencyObservable.PropertyChangeData) {
var tabLayout = (data.object)._getAndroidTabView();
if (tabLayout && data.newValue instanceof color.Color) {
- tabLayout.setSelectedIndicatorColors([data.newValue.android]);
+ tabLayout.setSelectedIndicatorColors([data.newValue._nativeView]);
}
}
(common.TabView.selectedColorProperty.metadata).onSetNativeValue = selectedColorPropertyChanged;
@@ -181,7 +181,7 @@ function selectedColorPropertyChanged(data: dependencyObservable.PropertyChangeD
function tabsBackgroundColorPropertyChanged(data: dependencyObservable.PropertyChangeData) {
var tabLayout = (data.object)._getAndroidTabView();
if (tabLayout && data.newValue instanceof color.Color) {
- tabLayout.setBackgroundColor(data.newValue.android);
+ tabLayout.setBackgroundColor(data.newValue._nativeView);
}
}
(common.TabView.tabsBackgroundColorProperty.metadata).onSetNativeValue = tabsBackgroundColorPropertyChanged;