Skip to content

Commit b113b00

Browse files
MartoYankovAlexander Vakrilov
authored andcommitted
feat: Add methods to get the root view and set a different root view at run time (#5386)
* feat: add option to set a different root view at run time * feat: expose application getRootView method * refactor: Introduce ViewEntry interface * fix: Respect root view rturned from launch event in Android * refactor: getRootView() code + caching root view per activity. * refactor: add app-root.xml in apps * refactor: http test made async
1 parent 0c8275f commit b113b00

11 files changed

Lines changed: 219 additions & 115 deletions

File tree

apps/app/ui-tests-app/app-root.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<Frame defaultPage="ui-tests-app/main-page" />

apps/app/ui-tests-app/app.ts

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,11 @@
1-
console.log("####### ------ APP MODULES START ")
1+
console.log("####### ------ APP MODULES START ");
22

33
import * as application from "tns-core-modules/application";
44
import * as trace from "tns-core-modules/trace";
5+
trace.addCategories(trace.categories.NativeLifecycle);
6+
trace.addCategories(trace.categories.Navigation);
7+
trace.addCategories(trace.categories.Transition);
58
trace.enable();
6-
trace.setCategories(trace.categories.concat(
7-
trace.categories.NativeLifecycle,
8-
trace.categories.Navigation,
9-
trace.categories.Transition
10-
));
119

1210
var countResume = 0;
1311
var countSuspend = 0;
@@ -25,7 +23,7 @@ application.on("uncaughtError", args => {
2523
}
2624
});
2725

28-
application.on(application.launchEvent, function (args: application.ApplicationEventData) {
26+
application.on(application.launchEvent, function(args: application.LaunchEventData) {
2927
if (args.android) {
3028
// For Android applications, args.android is an android.content.Intent class.
3129
console.log("### Launched application with: " + args.android + ".");
@@ -35,7 +33,7 @@ application.on(application.launchEvent, function (args: application.ApplicationE
3533
}
3634
});
3735

38-
application.on(application.suspendEvent, function (args: application.ApplicationEventData) {
36+
application.on(application.suspendEvent, function(args: application.ApplicationEventData) {
3937
if (args.android) {
4038
// For Android applications, args.android is an android activity class.
4139
console.log("#" + ++countSuspend + "# SuspendEvent Activity: " + args.android);
@@ -45,7 +43,7 @@ application.on(application.suspendEvent, function (args: application.Application
4543
}
4644
});
4745

48-
application.on(application.resumeEvent, function (args: application.ApplicationEventData) {
46+
application.on(application.resumeEvent, function(args: application.ApplicationEventData) {
4947
if (args.android) {
5048
// For Android applications, args.android is an android activity class.
5149
console.log("#" + ++countResume + "# ResumeEvent Activity: " + args.android);
@@ -55,7 +53,7 @@ application.on(application.resumeEvent, function (args: application.ApplicationE
5553
}
5654
});
5755

58-
application.on(application.exitEvent, function (args: application.ApplicationEventData) {
56+
application.on(application.exitEvent, function(args: application.ApplicationEventData) {
5957
if (args.android) {
6058
// For Android applications, args.android is an android activity class.
6159
console.log("### ExitEvent Activity: " + args.android);
@@ -65,7 +63,7 @@ application.on(application.exitEvent, function (args: application.ApplicationEve
6563
}
6664
});
6765

68-
application.on(application.lowMemoryEvent, function (args: application.ApplicationEventData) {
66+
application.on(application.lowMemoryEvent, function(args: application.ApplicationEventData) {
6967
if (args.android) {
7068
// For Android applications, args.android is an android activity class.
7169
console.log("### LowMemoryEvent Activity: " + args.android);
@@ -75,12 +73,15 @@ application.on(application.lowMemoryEvent, function (args: application.Applicati
7573
}
7674
});
7775

78-
application.on(application.uncaughtErrorEvent, function (args: application.UnhandledErrorEventData) {
79-
console.log("### NativeScriptError: " + args.error);
80-
console.log("### nativeException: " + (<any>args.error).nativeException);
81-
console.log("### stackTace: " + (<any>args.error).stackTrace);
82-
console.log("### stack: " + args.error.stack);
76+
application.on(application.uncaughtErrorEvent, function(args: application.UnhandledErrorEventData) {
77+
console.log("### NativeScriptError: " + args.error);
78+
console.log("### nativeException: " + (<any>args.error).nativeException);
79+
console.log("### stackTrace: " + (<any>args.error).stackTrace);
80+
console.log("### stack: " + args.error.stack);
8381
});
8482

8583
application.setCssFileName("ui-tests-app/app.css");
84+
8685
application.start({ moduleName: "ui-tests-app/main-page" });
86+
// application.run({ moduleName: "ui-tests-app/app-root" });
87+
// application.run();

tests/app/http/http-tests.ts

Lines changed: 9 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -634,20 +634,12 @@ export var test_request_jsonAsContentSentAndReceivedProperly = function (done) {
634634
};
635635

636636
declare var Worker: any;
637-
export var test_getString_WorksProperlyInWorker = function () {
638-
var ready;
639-
640-
var worker = new Worker("./http-string-worker");
641-
642-
worker.onmessage = function (msg) {
643-
TKUnit.assert(typeof msg.data === "string", "Result from getString() should be valid string object!");
644-
ready = true;
645-
}
646-
647-
worker.onerror = function (e) {
648-
ready = true;
649-
throw e;
650-
}
651-
652-
TKUnit.waitUntilReady(() => ready);
653-
}
637+
export var test_getString_WorksProperlyInWorker = function(done) {
638+
let worker = new Worker("./http-string-worker");
639+
worker.onmessage = function(msg) {
640+
done();
641+
};
642+
worker.onerror = function(e) {
643+
done(e);
644+
};
645+
};

tns-core-modules/application/application-common.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
require("globals");
33

44
import { Observable, EventData } from "../data/observable";
5+
// types
6+
import { View } from "../ui/core/view";
57
import {
68
trace as profilingTrace,
79
time,

tns-core-modules/application/application.android.ts

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ import { profile } from "../profiling";
1313
// First reexport so that app module is initialized.
1414
export * from "./application-common";
1515

16-
import { NavigationEntry } from "../ui/frame";
16+
// types
17+
import { NavigationEntry, View, AndroidActivityCallbacks } from "../ui/frame";
1718

1819
const ActivityCreated = "activityCreated";
1920
const ActivityDestroyed = "activityDestroyed";
@@ -53,7 +54,7 @@ export class AndroidApplication extends Observable implements AndroidApplication
5354
if (this.nativeApp === nativeApp) {
5455
return;
5556
}
56-
57+
5758
if (this.nativeApp) {
5859
throw new Error("application.android already initialized.");
5960
}
@@ -148,10 +149,35 @@ export function run(entry?: NavigationEntry | string) {
148149
start(entry);
149150
}
150151

152+
const CALLBACKS = "_callbacks";
153+
154+
export function _resetRootView(entry?: NavigationEntry | string) {
155+
const activity = androidApp.foregroundActivity;
156+
if (!activity) {
157+
throw new Error("Cannot find android activity.");
158+
}
159+
160+
mainEntry = typeof entry === "string" ? { moduleName: entry } : entry;
161+
const callbacks: AndroidActivityCallbacks = activity[CALLBACKS];
162+
callbacks.resetActivityContent(activity);
163+
}
164+
151165
export function getMainEntry() {
152166
return mainEntry;
153167
}
154168

169+
export function getRootView() {
170+
// Use start activity as a backup when foregroundActivity is still not set
171+
// in cases when we are getting the root view before activity.onResumed event is fired
172+
const activity = androidApp.foregroundActivity || androidApp.startActivity;
173+
if (!activity) {
174+
return undefined;
175+
}
176+
const callbacks: AndroidActivityCallbacks = activity[CALLBACKS];
177+
178+
return callbacks ? callbacks.getRootView() : undefined;
179+
}
180+
155181
export function getNativeApplication(): android.app.Application {
156182
// Try getting it from module - check whether application.android.init has been explicitly called
157183
let nativeApp = androidApp.nativeApp;
@@ -202,11 +228,11 @@ function initLifecycleCallbacks() {
202228
}
203229
});
204230

205-
const notifyActivityCreated = profile("notifyActivityCreated", function(activity: android.app.Activity, savedInstanceState: android.os.Bundle) {
231+
const notifyActivityCreated = profile("notifyActivityCreated", function (activity: android.app.Activity, savedInstanceState: android.os.Bundle) {
206232
androidApp.notify(<AndroidActivityBundleEventData>{ eventName: ActivityCreated, object: androidApp, activity, bundle: savedInstanceState });
207233
});
208234

209-
const subscribeForGlobalLayout = profile("subscribeForGlobalLayout", function(activity: android.app.Activity) {
235+
const subscribeForGlobalLayout = profile("subscribeForGlobalLayout", function (activity: android.app.Activity) {
210236
const rootView = activity.getWindow().getDecorView().getRootView();
211237
let onGlobalLayoutListener = new android.view.ViewTreeObserver.OnGlobalLayoutListener({
212238
onGlobalLayout() {

tns-core-modules/application/application.d.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,11 @@ export interface CssChangedEventData extends EventData {
116116
*/
117117
export function getMainEntry(): NavigationEntry;
118118

119+
/**
120+
* Get current application root view.
121+
*/
122+
export function getRootView(): View;
123+
119124
/**
120125
* Get application level static resources.
121126
*/
@@ -178,6 +183,12 @@ export function start(entry?: NavigationEntry | string);
178183
*/
179184
export function run(entry?: NavigationEntry | string);
180185

186+
/**
187+
* Call this method to change the root view of your application. Important: Your application must already be running.
188+
* This method won't create Frame as root view.
189+
*/
190+
export function _resetRootView(entry?: NavigationEntry | string);
191+
181192
//@private
182193
/**
183194
* Internal method use to check if a root Frame should be automatically created as root view.

tns-core-modules/application/application.ios.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,10 @@ class IOSApplication implements IOSApplicationDefinition {
100100
}
101101
}
102102

103+
get rootView() : View {
104+
return this._rootView;
105+
}
106+
103107
public addNotificationObserver(notificationName: string, onReceiveCallback: (notification: NSNotification) => void): NotificationObserver {
104108
const observer = NotificationObserver.initWithCallback(onReceiveCallback);
105109
utils.ios.getter(NSNotificationCenter, NSNotificationCenter.defaultCenter).addObserverSelectorNameObject(observer, "onReceive", notificationName, null);
@@ -263,6 +267,10 @@ export function getMainEntry() {
263267
return mainEntry;
264268
}
265269

270+
export function getRootView() {
271+
return iosApp.rootView;
272+
}
273+
266274
// NOTE: for backwards compatibility. Remove for 4.0.0.
267275
let createRootFrame = true;
268276
let started: boolean = false;
@@ -296,6 +304,12 @@ export function run(entry?: string | NavigationEntry) {
296304
start(entry);
297305
}
298306

307+
export function _resetRootView(entry?: NavigationEntry | string) {
308+
createRootFrame = false;
309+
mainEntry = typeof entry === "string" ? { moduleName: entry } : entry;
310+
iosApp.setWindowContent();
311+
}
312+
299313
export function getNativeApplication(): UIApplication {
300314
return iosApp.nativeApp;
301315
}

tns-core-modules/ui/builder/builder.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// Definitions.
22
import { LoadOptions } from ".";
33
import { View, ViewBase, Template, KeyedTemplate } from "../core/view";
4-
import { NavigationEntry } from "../frame";
4+
import { ViewEntry } from "../frame";
55

66
// Types.
77
import { debug, ScopeError, SourceError, Source } from "../../utils/debug";
@@ -62,7 +62,7 @@ export function loadPage(moduleNamePath: string, fileName: string, context?: any
6262
return componentModule && componentModule.component;
6363
}
6464

65-
const loadModule = profile("loadModule", (moduleNamePath: string, entry: NavigationEntry): ModuleExports => {
65+
const loadModule = profile("loadModule", (moduleNamePath: string, entry: ViewEntry): ModuleExports => {
6666
// web-pack case where developers register their page JS file manually.
6767
if (global.moduleExists(entry.moduleName)) {
6868
return global.loadModule(entry.moduleName);
@@ -94,7 +94,7 @@ const viewFromBuilder = profile("viewFromBuilder", (moduleNamePath: string, modu
9494
return null;
9595
})
9696

97-
export const createViewFromEntry = profile("createViewFromEntry", (entry: NavigationEntry): View => {
97+
export const createViewFromEntry = profile("createViewFromEntry", (entry: ViewEntry): View => {
9898
if (entry.create) {
9999
return createView(entry);
100100
} else if (entry.moduleName) {
@@ -116,7 +116,7 @@ export const createViewFromEntry = profile("createViewFromEntry", (entry: Naviga
116116
throw new Error("Failed to load page XML file for module: " + entry.moduleName);
117117
});
118118

119-
const createView = profile("entry.create", (entry: NavigationEntry): View => {
119+
const createView = profile("entry.create", (entry: ViewEntry): View => {
120120
const view = entry.create();
121121
if (!view) {
122122
throw new Error("Failed to create Page with entry.create() function.");

tns-core-modules/ui/frame/activity.android.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ class NativeScriptActivity extends android.app.Activity {
1919
appModule.android.init(this.getApplication());
2020

2121
// Set isNativeScriptActivity in onCreate.
22-
// The JS construcotr might not be called beacuse the activity is created from Andoird.
22+
// The JS constructor might not be called because the activity is created from Android.
2323
this.isNativeScriptActivity = true;
2424
if (!this._callbacks) {
2525
setActivityCallbacks(this);

0 commit comments

Comments
 (0)