mirror of
https://github.com/NativeScript/NativeScript.git
synced 2025-08-14 10:01:08 +08:00
fix(android): improved handling for nested frames (#10713)
This commit is contained in:

committed by
GitHub

parent
2377b6ae98
commit
86524229bc
@ -117,12 +117,13 @@ export interface ShowModalOptions {
|
|||||||
* @param criterion - The type of ancestor view we are looking for. Could be a string containing a class name or an actual type.
|
* @param criterion - The type of ancestor view we are looking for. Could be a string containing a class name or an actual type.
|
||||||
* Returns an instance of a view (if found), otherwise undefined.
|
* Returns an instance of a view (if found), otherwise undefined.
|
||||||
*/
|
*/
|
||||||
export function getAncestor(view: ViewBaseDefinition, criterion: string | { new () }): ViewBaseDefinition {
|
export function getAncestor<T extends ViewBaseDefinition = ViewBaseDefinition>(view: T, criterion: string | { new () }): T {
|
||||||
let matcher: (view: ViewBaseDefinition) => boolean = null;
|
let matcher: (view: ViewBaseDefinition) => view is T;
|
||||||
|
|
||||||
if (typeof criterion === 'string') {
|
if (typeof criterion === 'string') {
|
||||||
matcher = (view: ViewBaseDefinition) => view.typeName === criterion;
|
matcher = (view: ViewBaseDefinition): view is T => view.typeName === criterion;
|
||||||
} else {
|
} else {
|
||||||
matcher = (view: ViewBaseDefinition) => view instanceof criterion;
|
matcher = (view: ViewBaseDefinition): view is T => view instanceof criterion;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let parent = view.parent; parent != null; parent = parent.parent) {
|
for (let parent = view.parent; parent != null; parent = parent.parent) {
|
||||||
|
@ -5,8 +5,6 @@ import { AndroidActivityBackPressedEventData, AndroidActivityNewIntentEventData,
|
|||||||
import { Trace } from '../../../trace';
|
import { Trace } from '../../../trace';
|
||||||
import { View } from '../../core/view';
|
import { View } from '../../core/view';
|
||||||
|
|
||||||
import { _clearEntry, _clearFragment, _getAnimatedEntries, _reverseTransitions, _setAndroidFragmentTransitions, _updateTransitions } from '../fragment.transitions';
|
|
||||||
|
|
||||||
import { profile } from '../../../profiling';
|
import { profile } from '../../../profiling';
|
||||||
import { isEmbedded, setEmbeddedView } from '../../embedding';
|
import { isEmbedded, setEmbeddedView } from '../../embedding';
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
// Definitions.
|
// Definitions.
|
||||||
import { NavigationType } from './frame-common';
|
import { NavigationType, TransitionState } from './frame-common';
|
||||||
import { NavigationTransition, BackstackEntry } from '.';
|
import { NavigationTransition, BackstackEntry } from '.';
|
||||||
|
|
||||||
// Types.
|
// Types.
|
||||||
@ -152,7 +152,7 @@ export function _setAndroidFragmentTransitions(animated: boolean, navigationTran
|
|||||||
setupCurrentFragmentExplodeTransition(navigationTransition, currentEntry);
|
setupCurrentFragmentExplodeTransition(navigationTransition, currentEntry);
|
||||||
}
|
}
|
||||||
} else if (name.indexOf('flip') === 0) {
|
} else if (name.indexOf('flip') === 0) {
|
||||||
const direction = name.substr('flip'.length) || 'right'; //Extract the direction from the string
|
const direction = name.substring('flip'.length) || 'right'; //Extract the direction from the string
|
||||||
const flipTransition = new FlipTransition(direction, navigationTransition.duration, navigationTransition.curve);
|
const flipTransition = new FlipTransition(direction, navigationTransition.duration, navigationTransition.curve);
|
||||||
|
|
||||||
setupNewFragmentCustomTransition(navigationTransition, newEntry, flipTransition);
|
setupNewFragmentCustomTransition(navigationTransition, newEntry, flipTransition);
|
||||||
@ -282,23 +282,28 @@ export function _getAnimatedEntries(frameId: number): Set<BackstackEntry> {
|
|||||||
|
|
||||||
export function _updateTransitions(entry: ExpandedEntry): void {
|
export function _updateTransitions(entry: ExpandedEntry): void {
|
||||||
const fragment = entry.fragment;
|
const fragment = entry.fragment;
|
||||||
|
|
||||||
|
if (!fragment) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const enterTransitionListener = entry.enterTransitionListener;
|
const enterTransitionListener = entry.enterTransitionListener;
|
||||||
if (enterTransitionListener && fragment) {
|
if (enterTransitionListener) {
|
||||||
fragment.setEnterTransition(enterTransitionListener.transition);
|
fragment.setEnterTransition(enterTransitionListener.transition);
|
||||||
}
|
}
|
||||||
|
|
||||||
const exitTransitionListener = entry.exitTransitionListener;
|
const exitTransitionListener = entry.exitTransitionListener;
|
||||||
if (exitTransitionListener && fragment) {
|
if (exitTransitionListener) {
|
||||||
fragment.setExitTransition(exitTransitionListener.transition);
|
fragment.setExitTransition(exitTransitionListener.transition);
|
||||||
}
|
}
|
||||||
|
|
||||||
const reenterTransitionListener = entry.reenterTransitionListener;
|
const reenterTransitionListener = entry.reenterTransitionListener;
|
||||||
if (reenterTransitionListener && fragment) {
|
if (reenterTransitionListener) {
|
||||||
fragment.setReenterTransition(reenterTransitionListener.transition);
|
fragment.setReenterTransition(reenterTransitionListener.transition);
|
||||||
}
|
}
|
||||||
|
|
||||||
const returnTransitionListener = entry.returnTransitionListener;
|
const returnTransitionListener = entry.returnTransitionListener;
|
||||||
if (returnTransitionListener && fragment) {
|
if (returnTransitionListener) {
|
||||||
fragment.setReturnTransition(returnTransitionListener.transition);
|
fragment.setReturnTransition(returnTransitionListener.transition);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -428,6 +433,16 @@ function addToWaitingQueue(entry: ExpandedEntry): void {
|
|||||||
entries.add(entry);
|
entries.add(entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function cloneExpandedTransitionListener(expandedTransitionListener: ExpandedTransitionListener) {
|
||||||
|
if (!expandedTransitionListener) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const cloneTransition = expandedTransitionListener.transition.clone();
|
||||||
|
|
||||||
|
return addNativeTransitionListener(expandedTransitionListener.entry, cloneTransition);
|
||||||
|
}
|
||||||
|
|
||||||
function clearExitAndReenterTransitions(entry: ExpandedEntry, removeListener: boolean): void {
|
function clearExitAndReenterTransitions(entry: ExpandedEntry, removeListener: boolean): void {
|
||||||
const fragment: androidx.fragment.app.Fragment = entry.fragment;
|
const fragment: androidx.fragment.app.Fragment = entry.fragment;
|
||||||
const exitListener = entry.exitTransitionListener;
|
const exitListener = entry.exitTransitionListener;
|
||||||
@ -469,15 +484,56 @@ function clearExitAndReenterTransitions(entry: ExpandedEntry, removeListener: bo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function _getTransitionState(entry: ExpandedEntry): TransitionState {
|
||||||
|
let transitionState: TransitionState;
|
||||||
|
|
||||||
|
if (entry.enterTransitionListener && entry.exitTransitionListener) {
|
||||||
|
transitionState = {
|
||||||
|
enterTransitionListener: cloneExpandedTransitionListener(entry.enterTransitionListener),
|
||||||
|
exitTransitionListener: cloneExpandedTransitionListener(entry.exitTransitionListener),
|
||||||
|
reenterTransitionListener: cloneExpandedTransitionListener(entry.reenterTransitionListener),
|
||||||
|
returnTransitionListener: cloneExpandedTransitionListener(entry.returnTransitionListener),
|
||||||
|
transitionName: entry.transitionName,
|
||||||
|
entry,
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
transitionState = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return transitionState;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function _restoreTransitionState(snapshot: TransitionState): void {
|
||||||
|
const entry = snapshot.entry as ExpandedEntry;
|
||||||
|
|
||||||
|
if (snapshot.enterTransitionListener) {
|
||||||
|
entry.enterTransitionListener = snapshot.enterTransitionListener;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (snapshot.exitTransitionListener) {
|
||||||
|
entry.exitTransitionListener = snapshot.exitTransitionListener;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (snapshot.reenterTransitionListener) {
|
||||||
|
entry.reenterTransitionListener = snapshot.reenterTransitionListener;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (snapshot.returnTransitionListener) {
|
||||||
|
entry.returnTransitionListener = snapshot.returnTransitionListener;
|
||||||
|
}
|
||||||
|
|
||||||
|
entry.transitionName = snapshot.transitionName;
|
||||||
|
}
|
||||||
|
|
||||||
export function _clearFragment(entry: ExpandedEntry): void {
|
export function _clearFragment(entry: ExpandedEntry): void {
|
||||||
clearEntry(entry, false);
|
clearTransitions(entry, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function _clearEntry(entry: ExpandedEntry): void {
|
export function _clearEntry(entry: ExpandedEntry): void {
|
||||||
clearEntry(entry, true);
|
clearTransitions(entry, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
function clearEntry(entry: ExpandedEntry, removeListener: boolean): void {
|
function clearTransitions(entry: ExpandedEntry, removeListener: boolean): void {
|
||||||
clearExitAndReenterTransitions(entry, removeListener);
|
clearExitAndReenterTransitions(entry, removeListener);
|
||||||
|
|
||||||
const fragment: androidx.fragment.app.Fragment = entry.fragment;
|
const fragment: androidx.fragment.app.Fragment = entry.fragment;
|
||||||
@ -569,7 +625,7 @@ function setReturnTransition(navigationTransition: NavigationTransition, entry:
|
|||||||
|
|
||||||
function setupNewFragmentSlideTransition(navTransition: NavigationTransition, entry: ExpandedEntry, name: string): void {
|
function setupNewFragmentSlideTransition(navTransition: NavigationTransition, entry: ExpandedEntry, name: string): void {
|
||||||
setupCurrentFragmentSlideTransition(navTransition, entry, name);
|
setupCurrentFragmentSlideTransition(navTransition, entry, name);
|
||||||
const direction = name.substr('slide'.length) || 'left'; //Extract the direction from the string
|
const direction = name.substring('slide'.length) || 'left'; //Extract the direction from the string
|
||||||
switch (direction) {
|
switch (direction) {
|
||||||
case 'left':
|
case 'left':
|
||||||
setEnterTransition(navTransition, entry, new androidx.transition.Slide(android.view.Gravity.RIGHT));
|
setEnterTransition(navTransition, entry, new androidx.transition.Slide(android.view.Gravity.RIGHT));
|
||||||
@ -594,7 +650,7 @@ function setupNewFragmentSlideTransition(navTransition: NavigationTransition, en
|
|||||||
}
|
}
|
||||||
|
|
||||||
function setupCurrentFragmentSlideTransition(navTransition: NavigationTransition, entry: ExpandedEntry, name: string): void {
|
function setupCurrentFragmentSlideTransition(navTransition: NavigationTransition, entry: ExpandedEntry, name: string): void {
|
||||||
const direction = name.substr('slide'.length) || 'left'; //Extract the direction from the string
|
const direction = name.substring('slide'.length) || 'left'; //Extract the direction from the string
|
||||||
switch (direction) {
|
switch (direction) {
|
||||||
case 'left':
|
case 'left':
|
||||||
setExitTransition(navTransition, entry, new androidx.transition.Slide(android.view.Gravity.LEFT));
|
setExitTransition(navTransition, entry, new androidx.transition.Slide(android.view.Gravity.LEFT));
|
||||||
|
10
packages/core/ui/frame/fragment.transitions.d.ts
vendored
10
packages/core/ui/frame/fragment.transitions.d.ts
vendored
@ -1,4 +1,4 @@
|
|||||||
import { NavigationTransition, BackstackEntry } from '.';
|
import { NavigationTransition, BackstackEntry, TransitionState } from '.';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
@ -20,6 +20,14 @@ export function _updateTransitions(entry: BackstackEntry): void;
|
|||||||
* Reverse transitions from entry to fragment if any.
|
* Reverse transitions from entry to fragment if any.
|
||||||
*/
|
*/
|
||||||
export function _reverseTransitions(previousEntry: BackstackEntry, currentEntry: BackstackEntry): boolean;
|
export function _reverseTransitions(previousEntry: BackstackEntry, currentEntry: BackstackEntry): boolean;
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
export function _getTransitionState(entry: BackstackEntry): TransitionState;
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
export function _restoreTransitionState(snapshot: TransitionState): void;
|
||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
* Called when entry is removed from backstack (either back navigation or
|
* Called when entry is removed from backstack (either back navigation or
|
||||||
|
@ -76,13 +76,13 @@ export class FrameBase extends CustomLayoutView {
|
|||||||
return true;
|
return true;
|
||||||
} else if (top) {
|
} else if (top) {
|
||||||
let parentFrameCanGoBack = false;
|
let parentFrameCanGoBack = false;
|
||||||
let parentFrame = <FrameBase>getAncestor(top, 'Frame');
|
let parentFrame = getAncestor(top, 'Frame');
|
||||||
|
|
||||||
while (parentFrame && !parentFrameCanGoBack) {
|
while (parentFrame && !parentFrameCanGoBack) {
|
||||||
if (parentFrame && parentFrame.canGoBack()) {
|
if (parentFrame && parentFrame.canGoBack()) {
|
||||||
parentFrameCanGoBack = true;
|
parentFrameCanGoBack = true;
|
||||||
} else {
|
} else {
|
||||||
parentFrame = <FrameBase>getAncestor(parentFrame, 'Frame');
|
parentFrame = getAncestor(parentFrame, 'Frame');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -122,7 +122,6 @@ export class FrameBase extends CustomLayoutView {
|
|||||||
@profile
|
@profile
|
||||||
public onLoaded() {
|
public onLoaded() {
|
||||||
super.onLoaded();
|
super.onLoaded();
|
||||||
|
|
||||||
this._processNextNavigationEntry();
|
this._processNextNavigationEntry();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -323,9 +322,10 @@ export class FrameBase extends CustomLayoutView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private isNestedWithin(parentFrameCandidate: FrameBase): boolean {
|
private isNestedWithin(parentFrameCandidate: FrameBase): boolean {
|
||||||
let frameAncestor: FrameBase = this;
|
let frameAncestor = this as FrameBase;
|
||||||
|
|
||||||
while (frameAncestor) {
|
while (frameAncestor) {
|
||||||
frameAncestor = <FrameBase>getAncestor(frameAncestor, FrameBase);
|
frameAncestor = getAncestor(frameAncestor, FrameBase);
|
||||||
if (frameAncestor === parentFrameCandidate) {
|
if (frameAncestor === parentFrameCandidate) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,7 @@ import { Trace } from '../../trace';
|
|||||||
import { View } from '../core/view';
|
import { View } from '../core/view';
|
||||||
import { _stack, FrameBase, NavigationType } from './frame-common';
|
import { _stack, FrameBase, NavigationType } from './frame-common';
|
||||||
|
|
||||||
import { _clearEntry, _clearFragment, _getAnimatedEntries, _reverseTransitions, _setAndroidFragmentTransitions, _updateTransitions, addNativeTransitionListener } from './fragment.transitions';
|
import { _clearEntry, _clearFragment, _getAnimatedEntries, _getTransitionState, _restoreTransitionState, _reverseTransitions, _setAndroidFragmentTransitions, _updateTransitions, addNativeTransitionListener } from './fragment.transitions';
|
||||||
|
|
||||||
import { profile } from '../../profiling';
|
import { profile } from '../../profiling';
|
||||||
import { android as androidUtils } from '../../utils/native-helper';
|
import { android as androidUtils } from '../../utils/native-helper';
|
||||||
@ -28,6 +28,7 @@ const FRAMEID = '_frameId';
|
|||||||
const CALLBACKS = '_callbacks';
|
const CALLBACKS = '_callbacks';
|
||||||
|
|
||||||
const ownerSymbol = Symbol('_owner');
|
const ownerSymbol = Symbol('_owner');
|
||||||
|
const isPendingDetachSymbol = Symbol('_isPendingDetach');
|
||||||
|
|
||||||
let navDepth = -1;
|
let navDepth = -1;
|
||||||
let fragmentId = -1;
|
let fragmentId = -1;
|
||||||
@ -58,6 +59,12 @@ function getAttachListener(): android.view.View.OnAttachStateChangeListener {
|
|||||||
if (owner) {
|
if (owner) {
|
||||||
owner._onDetachedFromWindow();
|
owner._onDetachedFromWindow();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (view[isPendingDetachSymbol]) {
|
||||||
|
delete view[isPendingDetachSymbol];
|
||||||
|
view.removeOnAttachStateChangeListener(this);
|
||||||
|
view[ownerSymbol] = null;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -73,7 +80,10 @@ export class Frame extends FrameBase {
|
|||||||
private _containerViewId = -1;
|
private _containerViewId = -1;
|
||||||
private _tearDownPending = false;
|
private _tearDownPending = false;
|
||||||
private _attachedToWindow = false;
|
private _attachedToWindow = false;
|
||||||
private _wasReset = false;
|
/**
|
||||||
|
* This property indicates that the view is to be reused as a root view or has been previously disposed.
|
||||||
|
*/
|
||||||
|
private _isReset = false;
|
||||||
private _cachedTransitionState: TransitionState;
|
private _cachedTransitionState: TransitionState;
|
||||||
private _frameCreateTimeout: NodeJS.Timeout;
|
private _frameCreateTimeout: NodeJS.Timeout;
|
||||||
|
|
||||||
@ -143,8 +153,9 @@ export class Frame extends FrameBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this._attachedToWindow = true;
|
this._attachedToWindow = true;
|
||||||
this._wasReset = false;
|
this._isReset = false;
|
||||||
this._processNextNavigationEntry();
|
this._processNextNavigationEntry();
|
||||||
|
this._ensureEntryFragment();
|
||||||
}
|
}
|
||||||
|
|
||||||
_onDetachedFromWindow(): void {
|
_onDetachedFromWindow(): void {
|
||||||
@ -162,12 +173,12 @@ export class Frame extends FrameBase {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// in case the activity is "reset" using resetRootView we must wait for
|
// in case the activity is "reset" using resetRootView or disposed we must wait for
|
||||||
// the attachedToWindow event to make the first navigation or it will crash
|
// the attachedToWindow event to make the first navigation or it will crash
|
||||||
// https://github.com/NativeScript/NativeScript/commit/9dd3e1a8076e5022e411f2f2eeba34aabc68d112
|
// https://github.com/NativeScript/NativeScript/commit/9dd3e1a8076e5022e411f2f2eeba34aabc68d112
|
||||||
// though we should not do it on app "start"
|
// though we should not do it on app "start"
|
||||||
// or it will create a "flash" to activity background color
|
// or it will create a "flash" to activity background color
|
||||||
if (this._wasReset && !this._attachedToWindow) {
|
if (this._isReset && !this._attachedToWindow) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -194,7 +205,7 @@ export class Frame extends FrameBase {
|
|||||||
// simulated navigation (NoTransition, zero duration animator) and thus the fragment immediately disappears;
|
// simulated navigation (NoTransition, zero duration animator) and thus the fragment immediately disappears;
|
||||||
// the user only sees the animation of the entering fragment as per its specific enter animation settings.
|
// the user only sees the animation of the entering fragment as per its specific enter animation settings.
|
||||||
// NOTE: we are restoring the animation settings in Frame.setCurrent(...) as navigation completes asynchronously
|
// NOTE: we are restoring the animation settings in Frame.setCurrent(...) as navigation completes asynchronously
|
||||||
const cachedTransitionState = getTransitionState(this._currentEntry);
|
const cachedTransitionState = _getTransitionState(this._currentEntry);
|
||||||
|
|
||||||
if (cachedTransitionState) {
|
if (cachedTransitionState) {
|
||||||
this._cachedTransitionState = cachedTransitionState;
|
this._cachedTransitionState = cachedTransitionState;
|
||||||
@ -228,7 +239,7 @@ export class Frame extends FrameBase {
|
|||||||
public _onRootViewReset(): void {
|
public _onRootViewReset(): void {
|
||||||
super._onRootViewReset();
|
super._onRootViewReset();
|
||||||
// used to handle the "first" navigate differently on first run and on reset
|
// used to handle the "first" navigate differently on first run and on reset
|
||||||
this._wasReset = true;
|
this._isReset = true;
|
||||||
// call this AFTER the super call to ensure descendants apply their rootview-reset logic first
|
// call this AFTER the super call to ensure descendants apply their rootview-reset logic first
|
||||||
// i.e. in a scenario with nested frames / frame with tabview let the descendandt cleanup the inner
|
// i.e. in a scenario with nested frames / frame with tabview let the descendandt cleanup the inner
|
||||||
// fragments first, and then cleanup the parent fragments
|
// fragments first, and then cleanup the parent fragments
|
||||||
@ -241,6 +252,33 @@ export class Frame extends FrameBase {
|
|||||||
this.backgroundColor = this._originalBackground;
|
this.backgroundColor = this._originalBackground;
|
||||||
this._originalBackground = null;
|
this._originalBackground = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this._ensureEntryFragment();
|
||||||
|
super.onLoaded();
|
||||||
|
}
|
||||||
|
|
||||||
|
onUnloaded() {
|
||||||
|
super.onUnloaded();
|
||||||
|
|
||||||
|
if (typeof this._frameCreateTimeout === 'number') {
|
||||||
|
clearTimeout(this._frameCreateTimeout);
|
||||||
|
this._frameCreateTimeout = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO: Check if this fragment precaution is still needed
|
||||||
|
*/
|
||||||
|
private _ensureEntryFragment(): void {
|
||||||
|
// in case the activity is "reset" using resetRootView or disposed we must wait for
|
||||||
|
// the attachedToWindow event to make the first navigation or it will crash
|
||||||
|
// https://github.com/NativeScript/NativeScript/commit/9dd3e1a8076e5022e411f2f2eeba34aabc68d112
|
||||||
|
// though we should not do it on app "start"
|
||||||
|
// or it will create a "flash" to activity background color
|
||||||
|
if (this._isReset && !this._attachedToWindow) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this._frameCreateTimeout = setTimeout(() => {
|
this._frameCreateTimeout = setTimeout(() => {
|
||||||
// there's a bug with nested frames where sometimes the nested fragment is not recreated at all
|
// there's a bug with nested frames where sometimes the nested fragment is not recreated at all
|
||||||
// so we manually check on loaded event if the fragment is not recreated and recreate it
|
// so we manually check on loaded event if the fragment is not recreated and recreate it
|
||||||
@ -255,17 +293,9 @@ export class Frame extends FrameBase {
|
|||||||
transaction.commitAllowingStateLoss();
|
transaction.commitAllowingStateLoss();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, 0);
|
|
||||||
|
|
||||||
super.onLoaded();
|
|
||||||
}
|
|
||||||
|
|
||||||
onUnloaded() {
|
|
||||||
super.onUnloaded();
|
|
||||||
if (typeof this._frameCreateTimeout === 'number') {
|
|
||||||
clearTimeout(this._frameCreateTimeout);
|
|
||||||
this._frameCreateTimeout = null;
|
this._frameCreateTimeout = null;
|
||||||
}
|
}, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
private disposeCurrentFragment(): void {
|
private disposeCurrentFragment(): void {
|
||||||
@ -352,7 +382,7 @@ export class Frame extends FrameBase {
|
|||||||
|
|
||||||
// restore cached animation settings if we just completed simulated first navigation (no animation)
|
// restore cached animation settings if we just completed simulated first navigation (no animation)
|
||||||
if (this._cachedTransitionState) {
|
if (this._cachedTransitionState) {
|
||||||
restoreTransitionState(this._currentEntry, this._cachedTransitionState);
|
_restoreTransitionState(this._cachedTransitionState);
|
||||||
this._cachedTransitionState = null;
|
this._cachedTransitionState = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -391,7 +421,7 @@ export class Frame extends FrameBase {
|
|||||||
// HACK: This @profile decorator creates a circular dependency
|
// HACK: This @profile decorator creates a circular dependency
|
||||||
// HACK: because the function parameter type is evaluated with 'typeof'
|
// HACK: because the function parameter type is evaluated with 'typeof'
|
||||||
@profile
|
@profile
|
||||||
public _navigateCore(newEntry: any) {
|
public _navigateCore(newEntry: BackstackEntry) {
|
||||||
// should be (newEntry: BackstackEntry)
|
// should be (newEntry: BackstackEntry)
|
||||||
super._navigateCore(newEntry);
|
super._navigateCore(newEntry);
|
||||||
|
|
||||||
@ -487,19 +517,19 @@ export class Frame extends FrameBase {
|
|||||||
|
|
||||||
if (removed.fragment) {
|
if (removed.fragment) {
|
||||||
_clearEntry(removed);
|
_clearEntry(removed);
|
||||||
|
removed.fragment = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
removed.fragment = null;
|
|
||||||
removed.viewSavedState = null;
|
removed.viewSavedState = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected _disposeBackstackEntry(entry: BackstackEntry): void {
|
protected _disposeBackstackEntry(entry: BackstackEntry): void {
|
||||||
if (entry.fragment) {
|
if (entry.fragment) {
|
||||||
_clearFragment(entry);
|
_clearFragment(entry);
|
||||||
|
entry.fragment = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
entry.recreated = false;
|
entry.recreated = false;
|
||||||
entry.fragment = null;
|
|
||||||
|
|
||||||
super._disposeBackstackEntry(entry);
|
super._disposeBackstackEntry(entry);
|
||||||
}
|
}
|
||||||
@ -518,9 +548,12 @@ export class Frame extends FrameBase {
|
|||||||
public initNativeView(): void {
|
public initNativeView(): void {
|
||||||
super.initNativeView();
|
super.initNativeView();
|
||||||
const listener = getAttachListener();
|
const listener = getAttachListener();
|
||||||
this.nativeViewProtected.addOnAttachStateChangeListener(listener);
|
const nativeView = this.nativeViewProtected as android.view.ViewGroup;
|
||||||
this.nativeViewProtected[ownerSymbol] = this;
|
|
||||||
this._android.rootViewGroup = this.nativeViewProtected;
|
nativeView.addOnAttachStateChangeListener(listener);
|
||||||
|
nativeView[ownerSymbol] = this;
|
||||||
|
|
||||||
|
this._android.rootViewGroup = nativeView;
|
||||||
if (this._containerViewId < 0) {
|
if (this._containerViewId < 0) {
|
||||||
this._containerViewId = android.view.View.generateViewId();
|
this._containerViewId = android.view.View.generateViewId();
|
||||||
}
|
}
|
||||||
@ -528,12 +561,23 @@ export class Frame extends FrameBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public disposeNativeView() {
|
public disposeNativeView() {
|
||||||
|
const nativeView = this.nativeViewProtected as android.view.ViewGroup;
|
||||||
const listener = getAttachListener();
|
const listener = getAttachListener();
|
||||||
this.nativeViewProtected.removeOnAttachStateChangeListener(listener);
|
|
||||||
this.nativeViewProtected[ownerSymbol] = null;
|
// There are cases like root view when detach listener is not called upon removing view from view-tree
|
||||||
|
// so mark those views as pending and remove listener once the view is detached
|
||||||
|
if (nativeView.isAttachedToWindow()) {
|
||||||
|
nativeView[isPendingDetachSymbol] = true;
|
||||||
|
} else {
|
||||||
|
nativeView.removeOnAttachStateChangeListener(listener);
|
||||||
|
nativeView[ownerSymbol] = null;
|
||||||
|
}
|
||||||
|
|
||||||
this._tearDownPending = !!this._executingContext;
|
this._tearDownPending = !!this._executingContext;
|
||||||
|
|
||||||
const current = this._currentEntry;
|
const current = this._currentEntry;
|
||||||
const executingEntry = this._executingContext ? this._executingContext.entry : null;
|
const executingEntry = this._executingContext ? this._executingContext.entry : null;
|
||||||
|
|
||||||
this.backStack.forEach((entry) => {
|
this.backStack.forEach((entry) => {
|
||||||
// Don't destroy current and executing entries or UI will look blank.
|
// Don't destroy current and executing entries or UI will look blank.
|
||||||
// We will do it in setCurrent.
|
// We will do it in setCurrent.
|
||||||
@ -546,6 +590,12 @@ export class Frame extends FrameBase {
|
|||||||
this._disposeBackstackEntry(current);
|
this._disposeBackstackEntry(current);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Dispose cached transition and store it again if view ever gets re-used
|
||||||
|
this._cachedTransitionState = null;
|
||||||
|
|
||||||
|
// Mark as reset in order to properly re-initialize fragments if view ever gets re-used
|
||||||
|
this._isReset = true;
|
||||||
|
|
||||||
this._android.rootViewGroup = null;
|
this._android.rootViewGroup = null;
|
||||||
this._removeFromFrameStack();
|
this._removeFromFrameStack();
|
||||||
super.disposeNativeView();
|
super.disposeNativeView();
|
||||||
@ -603,55 +653,6 @@ export function reloadPage(context?: ModuleContext): void {
|
|||||||
// attach on global, so it can be overwritten in NativeScript Angular
|
// attach on global, so it can be overwritten in NativeScript Angular
|
||||||
global.__onLiveSyncCore = Frame.reloadPage;
|
global.__onLiveSyncCore = Frame.reloadPage;
|
||||||
|
|
||||||
function cloneExpandedTransitionListener(expandedTransitionListener: any) {
|
|
||||||
if (!expandedTransitionListener) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const cloneTransition = expandedTransitionListener.transition.clone();
|
|
||||||
|
|
||||||
return addNativeTransitionListener(expandedTransitionListener.entry, cloneTransition);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getTransitionState(entry: BackstackEntry): TransitionState {
|
|
||||||
const expandedEntry = <any>entry;
|
|
||||||
const transitionState = <TransitionState>{};
|
|
||||||
|
|
||||||
if (expandedEntry.enterTransitionListener && expandedEntry.exitTransitionListener) {
|
|
||||||
transitionState.enterTransitionListener = cloneExpandedTransitionListener(expandedEntry.enterTransitionListener);
|
|
||||||
transitionState.exitTransitionListener = cloneExpandedTransitionListener(expandedEntry.exitTransitionListener);
|
|
||||||
transitionState.reenterTransitionListener = cloneExpandedTransitionListener(expandedEntry.reenterTransitionListener);
|
|
||||||
transitionState.returnTransitionListener = cloneExpandedTransitionListener(expandedEntry.returnTransitionListener);
|
|
||||||
transitionState.transitionName = expandedEntry.transitionName;
|
|
||||||
transitionState.entry = entry;
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return transitionState;
|
|
||||||
}
|
|
||||||
|
|
||||||
function restoreTransitionState(entry: BackstackEntry, snapshot: TransitionState): void {
|
|
||||||
const expandedEntry = <any>entry;
|
|
||||||
if (snapshot.enterTransitionListener) {
|
|
||||||
expandedEntry.enterTransitionListener = snapshot.enterTransitionListener;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (snapshot.exitTransitionListener) {
|
|
||||||
expandedEntry.exitTransitionListener = snapshot.exitTransitionListener;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (snapshot.reenterTransitionListener) {
|
|
||||||
expandedEntry.reenterTransitionListener = snapshot.reenterTransitionListener;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (snapshot.returnTransitionListener) {
|
|
||||||
expandedEntry.returnTransitionListener = snapshot.returnTransitionListener;
|
|
||||||
}
|
|
||||||
|
|
||||||
expandedEntry.transitionName = snapshot.transitionName;
|
|
||||||
}
|
|
||||||
|
|
||||||
let framesCounter = 0;
|
let framesCounter = 0;
|
||||||
const framesCache = new Array<WeakRef<AndroidFrame>>();
|
const framesCache = new Array<WeakRef<AndroidFrame>>();
|
||||||
|
|
||||||
|
@ -97,7 +97,7 @@ export class Frame extends FrameBase {
|
|||||||
// !!! THIS PROFILE DECORATOR CREATES A CIRCULAR DEPENDENCY
|
// !!! THIS PROFILE DECORATOR CREATES A CIRCULAR DEPENDENCY
|
||||||
// !!! BECAUSE THE PARAMETER TYPE IS EVALUATED WITH TYPEOF
|
// !!! BECAUSE THE PARAMETER TYPE IS EVALUATED WITH TYPEOF
|
||||||
@profile
|
@profile
|
||||||
public _navigateCore(backstackEntry: any) {
|
public _navigateCore(backstackEntry: BackstackEntry) {
|
||||||
super._navigateCore(backstackEntry);
|
super._navigateCore(backstackEntry);
|
||||||
|
|
||||||
const viewController: UIViewController = backstackEntry.resolvedPage.ios;
|
const viewController: UIViewController = backstackEntry.resolvedPage.ios;
|
||||||
@ -507,7 +507,7 @@ class UINavigationControllerImpl extends UINavigationController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private animateWithDuration(navigationTransition: NavigationTransition, nativeTransition: UIViewAnimationTransition, transitionType: string, baseCallback: Function): void {
|
private animateWithDuration(navigationTransition: NavigationTransition, nativeTransition: UIViewAnimationTransition, transitionType: string, baseCallback: () => void): void {
|
||||||
const duration = navigationTransition.duration ? navigationTransition.duration / 1000 : CORE_ANIMATION_DEFAULTS.duration;
|
const duration = navigationTransition.duration ? navigationTransition.duration / 1000 : CORE_ANIMATION_DEFAULTS.duration;
|
||||||
const curve = _getNativeCurve(navigationTransition);
|
const curve = _getNativeCurve(navigationTransition);
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user