Skip to content

Commit 9dd3e1a

Browse files
Hristo HristovHristo Hristov
authored andcommitted
Fix crash with nested frames navigation when aactivity is recreated. We now check if frame native view is atached to window before running navigation.
Livesync now recreates the main page instead of calling frame.navigate
1 parent 3f2f5f4 commit 9dd3e1a

File tree

4 files changed

+72
-28
lines changed

4 files changed

+72
-28
lines changed

tns-core-modules/ui/core/view/view-common.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -934,6 +934,14 @@ export abstract class ViewCommon extends ViewBase implements ViewDefinition {
934934
public _redrawNativeBackground(value: any): void {
935935
//
936936
}
937+
938+
_onAttachedToWindow(): void {
939+
//
940+
}
941+
942+
_onDetachedFromWindow(): void {
943+
//
944+
}
937945
}
938946

939947
export const automationTextProperty = new Property<ViewCommon, string>({ name: "automationText" });

tns-core-modules/ui/core/view/view.android.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,7 @@ function initializeDialogFragment() {
164164
const window = this.getDialog().getWindow();
165165
const length = android.view.ViewGroup.LayoutParams.MATCH_PARENT;
166166
window.setLayout(length, length);
167+
// This removes the default backgroundDrawable so there are no margins.
167168
window.setBackgroundDrawable(new android.graphics.drawable.ColorDrawable(android.graphics.Color.WHITE));
168169
}
169170

tns-core-modules/ui/core/view/view.d.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -666,6 +666,16 @@ export abstract class View extends ViewBase {
666666
* @param css
667667
*/
668668
_updateStyleScope(cssFileName?: string, cssString?: string, css?: string): void;
669+
670+
/**
671+
* Called in android when native view is attached to window.
672+
*/
673+
_onAttachedToWindow(): void;
674+
675+
/**
676+
* Called in android when native view is dettached from window.
677+
*/
678+
_onDetachedFromWindow(): void;
669679
//@endprivate
670680

671681
/**

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

Lines changed: 53 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ const INTENT_EXTRA = "com.tns.activity";
2828
const FRAMEID = "_frameId";
2929
const CALLBACKS = "_callbacks";
3030

31+
const ownerSymbol = Symbol("_owner");
32+
3133
let navDepth = -1;
3234
let fragmentId = -1;
3335
export let moduleLoaded: boolean;
@@ -38,36 +40,41 @@ if (global && global.__inspector) {
3840
devtools.attachDOMInspectorCommandCallbacks(global.__inspector);
3941
}
4042

41-
export function reloadPage(): void {
42-
const frame = topmost();
43-
if (frame) {
44-
if (frame.currentPage && frame.currentPage.modal) {
45-
frame.currentPage.modal.closeModal();
46-
}
47-
48-
const currentEntry = frame._currentEntry.entry;
49-
const newEntry: NavigationEntry = {
50-
animated: false,
51-
clearHistory: true,
52-
context: currentEntry.context,
53-
create: currentEntry.create,
54-
moduleName: currentEntry.moduleName,
55-
backstackVisible: currentEntry.backstackVisible
56-
}
57-
58-
// If create returns the same page instance we can't recreate it.
59-
// Instead of navigation set activity content.
60-
// This could happen if current page was set in XML as a Page instance.
61-
if (newEntry.create) {
62-
const page = newEntry.create();
63-
if (page === frame.currentPage) {
64-
resetActivityContent(frame.android.activity);
65-
return;
43+
export let attachStateChangeListener: android.view.View.OnAttachStateChangeListener;
44+
45+
function getAttachListener(): android.view.View.OnAttachStateChangeListener {
46+
if (!attachStateChangeListener) {
47+
@Interfaces([android.view.View.OnAttachStateChangeListener])
48+
class AttachListener extends java.lang.Object implements android.view.View.OnAttachStateChangeListener {
49+
constructor() {
50+
super();
51+
return global.__native(this);
52+
}
53+
54+
onViewAttachedToWindow(view: android.view.View): void {
55+
const owner: View = view[ownerSymbol];
56+
if (owner) {
57+
owner._onAttachedToWindow();
58+
}
59+
}
60+
61+
onViewDetachedFromWindow(view: android.view.View): void {
62+
const owner: View = view[ownerSymbol];
63+
if (owner) {
64+
owner._onDetachedFromWindow();
65+
}
6666
}
6767
}
6868

69-
frame.navigate(newEntry);
69+
attachStateChangeListener = new AttachListener();
7070
}
71+
72+
return attachStateChangeListener;
73+
}
74+
75+
export function reloadPage(): void {
76+
// Delete previously cached root view in order to recreate it.
77+
resetActivityContent(application.android.foregroundActivity);
7178
}
7279

7380
// attach on global, so it can be overwritten in NativeScript Angular
@@ -78,6 +85,7 @@ export class Frame extends FrameBase {
7885
private _delayedNavigationEntry: BackstackEntry;
7986
private _containerViewId: number = -1;
8087
private _tearDownPending = false;
88+
private _attachedToWindow = false;
8189
public _isBack: boolean = true;
8290

8391
constructor() {
@@ -107,13 +115,24 @@ export class Frame extends FrameBase {
107115
return this._android;
108116
}
109117

118+
_onAttachedToWindow(): void {
119+
super._onAttachedToWindow();
120+
this._attachedToWindow = true;
121+
this._processNextNavigationEntry();
122+
}
123+
124+
_onDetachedFromWindow(): void {
125+
super._onDetachedFromWindow();
126+
this._attachedToWindow = false;
127+
}
128+
110129
protected _processNextNavigationEntry(): void {
111130
// In case activity was destroyed because of back button pressed (e.g. app exit)
112131
// and application is restored from recent apps, current fragment isn't recreated.
113132
// In this case call _navigateCore in order to recreate the current fragment.
114133
// Don't call navigate because it will fire navigation events.
115134
// As JS instances are alive it is already done for the current page.
116-
if (!this.isLoaded) {
135+
if (!this.isLoaded || !this._attachedToWindow) {
117136
return;
118137
}
119138

@@ -328,6 +347,9 @@ export class Frame extends FrameBase {
328347

329348
public initNativeView(): void {
330349
super.initNativeView();
350+
const listener = getAttachListener();
351+
this.nativeViewProtected.addOnAttachStateChangeListener(listener);
352+
this.nativeViewProtected[ownerSymbol] = this;
331353
this._android.rootViewGroup = this.nativeViewProtected;
332354
if (this._containerViewId < 0) {
333355
this._containerViewId = android.view.View.generateViewId();
@@ -336,6 +358,9 @@ export class Frame extends FrameBase {
336358
}
337359

338360
public disposeNativeView() {
361+
const listener = getAttachListener();
362+
this.nativeViewProtected.removeOnAttachStateChangeListener(listener);
363+
this.nativeViewProtected[ownerSymbol] = null;
339364
this._tearDownPending = !!this._executingEntry;
340365
const current = this._currentEntry;
341366

@@ -661,7 +686,7 @@ class FragmentCallbacksImplementation implements AndroidFragmentCallbacks {
661686
// Make sure page will have styleScope even if parents don't.
662687
page._updateStyleScope();
663688
}
664-
689+
665690
this.frame._addView(page);
666691
}
667692

0 commit comments

Comments
 (0)