mirror of
https://github.com/NativeScript/NativeScript.git
synced 2025-11-05 13:26:48 +08:00
fix: multiple android navigation chashes (#5173)
* Fixes multiple android navigation chashes * Fix page.ios unloaded event when navigated to. * calling loaded/unloaded from application suspend resume on the rootview
This commit is contained in:
committed by
Svetoslav
parent
60773e7545
commit
f6907bed2b
@@ -105,6 +105,6 @@ export var test_Resolve_Fragment_ForPage = function () {
|
||||
frame.navigate(pageFactory);
|
||||
TKUnit.waitUntilReady(() => frame.navigationQueueIsEmpty());
|
||||
|
||||
const fragment = frame.android.fragmentForPage(testPage);
|
||||
const fragment = frame.android.fragmentForPage(frame._currentEntry);
|
||||
TKUnit.assertNotNull(fragment, "Failed to resolve native fragment for page");
|
||||
}
|
||||
@@ -154,6 +154,11 @@ class IOSApplication implements IOSApplicationDefinition {
|
||||
let ios = utils.ios.getter(UIApplication, UIApplication.sharedApplication);
|
||||
let object = this;
|
||||
notify(<ApplicationEventData>{ eventName: resumeEvent, object, ios });
|
||||
const content = this._window.content;
|
||||
if (content && !content.isLoaded) {
|
||||
content.onLoaded();
|
||||
}
|
||||
|
||||
if (!displayedOnce) {
|
||||
notify(<ApplicationEventData>{ eventName: displayedEvent, object, ios });
|
||||
displayedOnce = true;
|
||||
@@ -161,10 +166,18 @@ class IOSApplication implements IOSApplicationDefinition {
|
||||
}
|
||||
|
||||
private didEnterBackground(notification: NSNotification) {
|
||||
const content = this._window.content;
|
||||
if (content && content.isLoaded) {
|
||||
content.onUnloaded();
|
||||
}
|
||||
notify(<ApplicationEventData>{ eventName: suspendEvent, object: this, ios: utils.ios.getter(UIApplication, UIApplication.sharedApplication) });
|
||||
}
|
||||
|
||||
private willTerminate(notification: NSNotification) {
|
||||
const content = this._window.content;
|
||||
if (content && content.isLoaded) {
|
||||
content.onUnloaded();
|
||||
}
|
||||
notify(<ApplicationEventData>{ eventName: exitEvent, object: this, ios: utils.ios.getter(UIApplication, UIApplication.sharedApplication) });
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,10 @@ class FragmentClass extends android.app.Fragment {
|
||||
return result;
|
||||
}
|
||||
|
||||
public onStop(): void {
|
||||
this._callbacks.onStop(this, super.onStop);
|
||||
}
|
||||
|
||||
public onCreate(savedInstanceState: android.os.Bundle) {
|
||||
if (!this._callbacks) {
|
||||
setFragmentCallbacks(this);
|
||||
|
||||
@@ -13,27 +13,10 @@ import lazy from "../../utils/lazy";
|
||||
|
||||
import { isEnabled as traceEnabled, write as traceWrite, categories as traceCategories } from "../../trace";
|
||||
|
||||
// SAME as frame.android.ts!!! Not imported because we don't want cycle reference.
|
||||
const CALLBACKS = "_callbacks";
|
||||
|
||||
const sdkVersion = lazy(() => parseInt(device.sdkVersion));
|
||||
const intEvaluator = lazy(() => new android.animation.IntEvaluator());
|
||||
const defaultInterpolator = lazy(() => new android.view.animation.AccelerateDecelerateInterpolator());
|
||||
|
||||
const waitingQueue = new Set<android.app.Fragment>();
|
||||
|
||||
interface TransitionListener {
|
||||
new(entry: ExpandedEntry, transition: android.transition.Transition): ExpandedTransitionListener;
|
||||
}
|
||||
|
||||
let TransitionListener: TransitionListener;
|
||||
let AnimationListener: android.animation.Animator.AnimatorListener;
|
||||
let loadAnimatorMethod: java.lang.reflect.Method;
|
||||
let reflectionDone: boolean;
|
||||
let defaultEnterAnimatorStatic: android.animation.Animator;
|
||||
let defaultExitAnimatorStatic: android.animation.Animator;
|
||||
let fragmentCompleted: android.app.Fragment;
|
||||
|
||||
interface ExpandedAnimator extends android.animation.Animator {
|
||||
entry: ExpandedEntry;
|
||||
transitionType?: string;
|
||||
@@ -55,8 +38,12 @@ interface ExpandedEntry extends BackstackEntry {
|
||||
popEnterAnimator: ExpandedAnimator;
|
||||
popExitAnimator: ExpandedAnimator;
|
||||
|
||||
defaultEnterAnimator: ExpandedAnimator;
|
||||
defaultExitAnimator: ExpandedAnimator;
|
||||
|
||||
transition: Transition;
|
||||
transitionName: string;
|
||||
frameId: number
|
||||
}
|
||||
|
||||
interface FragmentCallbacks {
|
||||
@@ -64,18 +51,38 @@ interface FragmentCallbacks {
|
||||
entry: ExpandedEntry;
|
||||
}
|
||||
|
||||
const sdkVersion = lazy(() => parseInt(device.sdkVersion));
|
||||
const intEvaluator = lazy(() => new android.animation.IntEvaluator());
|
||||
const defaultInterpolator = lazy(() => new android.view.animation.AccelerateDecelerateInterpolator());
|
||||
|
||||
export const waitingQueue = new Map<number, Set<ExpandedEntry>>();
|
||||
export const completedEntries = new Map<number, ExpandedEntry>();
|
||||
|
||||
let TransitionListener: TransitionListener;
|
||||
let AnimationListener: android.animation.Animator.AnimatorListener;
|
||||
let loadAnimatorMethod: java.lang.reflect.Method;
|
||||
let reflectionDone: boolean;
|
||||
let defaultEnterAnimatorStatic: android.animation.Animator;
|
||||
let defaultExitAnimatorStatic: android.animation.Animator;
|
||||
|
||||
export function _setAndroidFragmentTransitions(
|
||||
animated: boolean,
|
||||
navigationTransition: NavigationTransition,
|
||||
currentFragment: android.app.Fragment,
|
||||
newFragment: android.app.Fragment,
|
||||
currentEntry: ExpandedEntry,
|
||||
newEntry: ExpandedEntry,
|
||||
fragmentTransaction: android.app.FragmentTransaction,
|
||||
manager: android.app.FragmentManager): void {
|
||||
manager: android.app.FragmentManager,
|
||||
frameId: number): void {
|
||||
|
||||
if (waitingQueue.size > 0) {
|
||||
throw new Error('Calling navigation before previous queue completes.');
|
||||
const currentFragment: android.app.Fragment = currentEntry ? currentEntry.fragment : null;
|
||||
const newFragment: android.app.Fragment = newEntry.fragment;
|
||||
const entries = waitingQueue.get(frameId);
|
||||
if (entries && entries.size > 0) {
|
||||
throw new Error('Calling navigation before previous navigation finish.');
|
||||
}
|
||||
|
||||
initDefaultAnimations(manager);
|
||||
|
||||
if (sdkVersion() >= 21) {
|
||||
allowTransitionOverlap(currentFragment);
|
||||
allowTransitionOverlap(newFragment);
|
||||
@@ -101,9 +108,6 @@ export function _setAndroidFragmentTransitions(
|
||||
name = 'default';
|
||||
}
|
||||
|
||||
const callbacks = getFragmentCallbacks(newFragment);
|
||||
const newEntry = callbacks.entry;
|
||||
const currentEntry = currentFragment ? getFragmentCallbacks(currentFragment).entry : null;
|
||||
let currentFragmentNeedsDifferentAnimation = false;
|
||||
if (currentEntry) {
|
||||
_updateTransitions(currentEntry);
|
||||
@@ -117,7 +121,6 @@ export function _setAndroidFragmentTransitions(
|
||||
if (name === 'none') {
|
||||
transition = new NoTransition(0, null);
|
||||
} else if (name === 'default') {
|
||||
initDefaultAnimations(manager);
|
||||
transition = new DefaultTransition(0, null);
|
||||
} else if (useLollipopTransition) {
|
||||
// setEnterTransition: Enter
|
||||
@@ -172,24 +175,50 @@ export function _setAndroidFragmentTransitions(
|
||||
}
|
||||
}
|
||||
|
||||
initDefaultAnimations(manager);
|
||||
setupDefaultAnimations(newEntry, new DefaultTransition(0, null));
|
||||
|
||||
printTransitions(currentEntry);
|
||||
printTransitions(newEntry);
|
||||
}
|
||||
|
||||
export function _onFragmentCreateAnimator(fragment: android.app.Fragment, nextAnim: number): android.animation.Animator {
|
||||
const entry = getFragmentCallbacks(fragment).entry;
|
||||
export function _onFragmentCreateAnimator(entry: ExpandedEntry, fragment: android.app.Fragment, nextAnim: number, enter: boolean): android.animation.Animator {
|
||||
let animator: android.animation.Animator;
|
||||
switch (nextAnim) {
|
||||
case AnimationType.enterFakeResourceId:
|
||||
return entry.enterAnimator;
|
||||
animator = entry.enterAnimator;
|
||||
break;
|
||||
|
||||
case AnimationType.exitFakeResourceId:
|
||||
return entry.exitAnimator;
|
||||
animator = entry.exitAnimator;
|
||||
break;
|
||||
|
||||
case AnimationType.popEnterFakeResourceId:
|
||||
return entry.popEnterAnimator;
|
||||
animator = entry.popEnterAnimator;
|
||||
break;
|
||||
|
||||
case AnimationType.popExitFakeResourceId:
|
||||
return entry.popExitAnimator;
|
||||
animator = entry.popExitAnimator;
|
||||
break;
|
||||
}
|
||||
|
||||
return null;
|
||||
if (!animator && sdkVersion() >= 21) {
|
||||
const view = fragment.getView();
|
||||
const jsParent = entry.resolvedPage.parent;
|
||||
const parent = view.getParent() || (jsParent && jsParent.nativeViewProtected);
|
||||
const animatedEntries = _getAnimatedEntries(entry.frameId);
|
||||
if (!animatedEntries || !animatedEntries.has(entry)) {
|
||||
if (parent && !(<any>parent).isLaidOut()) {
|
||||
animator = enter ? entry.defaultEnterAnimator : entry.defaultExitAnimator;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return animator;
|
||||
}
|
||||
|
||||
export function _getAnimatedEntries(frameId: number): Set<BackstackEntry> {
|
||||
return waitingQueue.get(frameId);
|
||||
}
|
||||
|
||||
export function _updateTransitions(entry: ExpandedEntry): void {
|
||||
@@ -241,10 +270,6 @@ export function _reverseTransitions(previousEntry: ExpandedEntry, currentEntry:
|
||||
return transitionUsed;
|
||||
}
|
||||
|
||||
function getFragmentCallbacks(fragment: android.app.Fragment): FragmentCallbacks {
|
||||
return fragment[CALLBACKS] as FragmentCallbacks;
|
||||
}
|
||||
|
||||
// Transition listener can't be static because
|
||||
// android is cloning transitions and we can't expand them :(
|
||||
function getTransitionListener(entry: ExpandedEntry, transition: android.transition.Transition): ExpandedTransitionListener {
|
||||
@@ -257,40 +282,38 @@ function getTransitionListener(entry: ExpandedEntry, transition: android.transit
|
||||
}
|
||||
|
||||
public onTransitionStart(transition: android.transition.Transition): void {
|
||||
const fragment = this.entry.fragment;
|
||||
waitingQueue.add(fragment);
|
||||
const entry = this.entry;
|
||||
addToWaitingQueue(entry);
|
||||
if (traceEnabled()) {
|
||||
traceWrite(`START ${toShortString(transition)} transition for ${fragment}`, traceCategories.Transition);
|
||||
traceWrite(`START ${toShortString(transition)} transition for ${entry.fragmentTag}`, traceCategories.Transition);
|
||||
}
|
||||
}
|
||||
|
||||
onTransitionEnd(transition: android.transition.Transition): void {
|
||||
const fragment = this.entry.fragment;
|
||||
const entry = this.entry;
|
||||
if (traceEnabled()) {
|
||||
traceWrite(`END ${toShortString(transition)} transition for ${fragment}`, traceCategories.Transition);
|
||||
traceWrite(`END ${toShortString(transition)} transition for ${entry.fragmentTag}`, traceCategories.Transition);
|
||||
}
|
||||
|
||||
transitionOrAnimationCompleted(fragment);
|
||||
transitionOrAnimationCompleted(entry);
|
||||
}
|
||||
|
||||
onTransitionResume(transition: android.transition.Transition): void {
|
||||
if (traceEnabled()) {
|
||||
const fragment = this.entry.fragment;
|
||||
const fragment = this.entry.fragmentTag;
|
||||
traceWrite(`RESUME ${toShortString(transition)} transition for ${fragment}`, traceCategories.Transition);
|
||||
}
|
||||
}
|
||||
|
||||
onTransitionPause(transition: android.transition.Transition): void {
|
||||
if (traceEnabled()) {
|
||||
const fragment = this.entry.fragment;
|
||||
traceWrite(`PAUSE ${toShortString(transition)} transition for ${fragment}`, traceCategories.Transition);
|
||||
traceWrite(`PAUSE ${toShortString(transition)} transition for ${this.entry.fragmentTag}`, traceCategories.Transition);
|
||||
}
|
||||
}
|
||||
|
||||
onTransitionCancel(transition: android.transition.Transition): void {
|
||||
if (traceEnabled()) {
|
||||
const fragment = this.entry.fragment;
|
||||
traceWrite(`CANCEL ${toShortString(transition)} transition for ${fragment}`, traceCategories.Transition);
|
||||
traceWrite(`CANCEL ${toShortString(transition)} transition for ${this.entry.fragmentTag}`, traceCategories.Transition);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -311,30 +334,30 @@ function getAnimationListener(): android.animation.Animator.IAnimatorListener {
|
||||
}
|
||||
|
||||
onAnimationStart(animator: ExpandedAnimator): void {
|
||||
const fragment = animator.entry.fragment;
|
||||
waitingQueue.add(fragment);
|
||||
const entry = animator.entry;
|
||||
addToWaitingQueue(entry);
|
||||
if (traceEnabled()) {
|
||||
traceWrite(`START ${animator.transitionType} for ${fragment}`, traceCategories.Transition);
|
||||
traceWrite(`START ${animator.transitionType} for ${entry.fragmentTag}`, traceCategories.Transition);
|
||||
}
|
||||
}
|
||||
|
||||
onAnimationRepeat(animator: ExpandedAnimator): void {
|
||||
if (traceEnabled()) {
|
||||
traceWrite(`REPEAT ${animator.transitionType} for ${animator.entry.fragment}`, traceCategories.Transition);
|
||||
traceWrite(`REPEAT ${animator.transitionType} for ${animator.entry.fragmentTag}`, traceCategories.Transition);
|
||||
}
|
||||
}
|
||||
|
||||
onAnimationEnd(animator: ExpandedAnimator): void {
|
||||
if (traceEnabled()) {
|
||||
traceWrite(`END ${animator.transitionType} for ${animator.entry.fragment}`, traceCategories.Transition);
|
||||
traceWrite(`END ${animator.transitionType} for ${animator.entry.fragmentTag}`, traceCategories.Transition);
|
||||
}
|
||||
|
||||
transitionOrAnimationCompleted(animator.entry.fragment);
|
||||
transitionOrAnimationCompleted(animator.entry);
|
||||
}
|
||||
|
||||
onAnimationCancel(animator: ExpandedAnimator): void {
|
||||
if (traceEnabled()) {
|
||||
traceWrite(`CANCEL ${animator.transitionType} for ${animator.entry.fragment}`, traceCategories.Transition);
|
||||
traceWrite(`CANCEL ${animator.transitionType} for ${animator.entry.fragmentTag}`, traceCategories.Transition);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -345,6 +368,17 @@ function getAnimationListener(): android.animation.Animator.IAnimatorListener {
|
||||
return AnimationListener;
|
||||
}
|
||||
|
||||
function addToWaitingQueue(entry: ExpandedEntry): void {
|
||||
const frameId = entry.frameId;
|
||||
let entries = waitingQueue.get(frameId);
|
||||
if (!entries) {
|
||||
entries = new Set<ExpandedEntry>();
|
||||
waitingQueue.set(frameId, entries);
|
||||
}
|
||||
|
||||
entries.add(entry);
|
||||
}
|
||||
|
||||
function clearAnimationListener(animator: ExpandedAnimator, listener: android.animation.Animator.IAnimatorListener): void {
|
||||
if (!animator) {
|
||||
return;
|
||||
@@ -352,10 +386,9 @@ function clearAnimationListener(animator: ExpandedAnimator, listener: android.an
|
||||
|
||||
animator.removeListener(listener);
|
||||
|
||||
const entry = animator.entry;
|
||||
const fragment = entry.fragment;
|
||||
if (traceEnabled()) {
|
||||
traceWrite(`Clear ${animator.transitionType} - ${entry.transition} for ${fragment}`, traceCategories.Transition);
|
||||
const entry = animator.entry;
|
||||
traceWrite(`Clear ${animator.transitionType} - ${entry.transition} for ${entry.fragmentTag}`, traceCategories.Transition);
|
||||
}
|
||||
|
||||
animator.entry = null;
|
||||
@@ -403,8 +436,9 @@ function clearExitAndReenterTransitions(entry: ExpandedEntry, removeListener: bo
|
||||
}
|
||||
}
|
||||
}
|
||||
export function _clearFragment(fragment: android.app.Fragment): void {
|
||||
clearEntry(getFragmentCallbacks(fragment).entry, false);
|
||||
|
||||
export function _clearFragment(entry: ExpandedEntry): void {
|
||||
clearEntry(entry, false);
|
||||
}
|
||||
|
||||
export function _clearEntry(entry: ExpandedEntry): void {
|
||||
@@ -633,6 +667,22 @@ function setupAllAnimation(entry: ExpandedEntry, transition: Transition): void {
|
||||
entry.popExitAnimator = popExitAnimator;
|
||||
}
|
||||
|
||||
function setupDefaultAnimations(entry: ExpandedEntry, transition: Transition): void {
|
||||
const listener = getAnimationListener();
|
||||
|
||||
const enterAnimator = <ExpandedAnimator>transition.createAndroidAnimator(AndroidTransitionType.enter);
|
||||
enterAnimator.transitionType = AndroidTransitionType.enter;
|
||||
enterAnimator.entry = entry;
|
||||
enterAnimator.addListener(listener);
|
||||
entry.defaultEnterAnimator = enterAnimator;
|
||||
|
||||
const exitAnimator = <ExpandedAnimator>transition.createAndroidAnimator(AndroidTransitionType.exit);
|
||||
exitAnimator.transitionType = AndroidTransitionType.exit;
|
||||
exitAnimator.entry = entry;
|
||||
exitAnimator.addListener(listener);
|
||||
entry.defaultExitAnimator = exitAnimator;
|
||||
}
|
||||
|
||||
function setUpNativeTransition(navigationTransition: NavigationTransition, nativeTransition: android.transition.Transition) {
|
||||
if (navigationTransition.duration) {
|
||||
nativeTransition.setDuration(navigationTransition.duration);
|
||||
@@ -648,22 +698,28 @@ function addNativeTransitionListener(entry: ExpandedEntry, nativeTransition: and
|
||||
return listener;
|
||||
}
|
||||
|
||||
function transitionOrAnimationCompleted(fragment: android.app.Fragment): void {
|
||||
waitingQueue.delete(fragment);
|
||||
if (waitingQueue.size === 0) {
|
||||
const callbacks = getFragmentCallbacks(fragment);
|
||||
const entry = callbacks.entry;
|
||||
const frame = callbacks.frame;
|
||||
const setAsCurrent = frame.isCurrent(entry) ? fragmentCompleted : fragment;
|
||||
function transitionOrAnimationCompleted(entry: ExpandedEntry): void {
|
||||
const frameId = entry.frameId;
|
||||
const entries = waitingQueue.get(frameId);
|
||||
entries.delete(entry);
|
||||
if (entries.size === 0) {
|
||||
const frame = entry.resolvedPage.frame;
|
||||
// We have 0 or 1 entry per frameId in completedEntries
|
||||
// So there is no need to make it to Set like waitingQueue
|
||||
const previousCompletedAnimationEntry = completedEntries.get(frameId);
|
||||
completedEntries.delete(frameId);
|
||||
waitingQueue.delete(frameId);
|
||||
|
||||
fragmentCompleted = null;
|
||||
let current = frame.isCurrent(entry) ? previousCompletedAnimationEntry : entry;
|
||||
current = current || entry;
|
||||
// Will be null if Frame is shown modally...
|
||||
// AnimationCompleted fires again (probably bug in android).
|
||||
if (setAsCurrent) {
|
||||
setTimeout(() => frame.setCurrent(getFragmentCallbacks(setAsCurrent).entry));
|
||||
if (current) {
|
||||
const isBack = frame._isBack;
|
||||
setTimeout(() => frame.setCurrent(current, isBack));
|
||||
}
|
||||
} else {
|
||||
fragmentCompleted = fragment;
|
||||
completedEntries.set(frameId, entry);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -719,8 +775,7 @@ function createDummyZeroDurationAnimator(): android.animation.Animator {
|
||||
|
||||
function printTransitions(entry: ExpandedEntry) {
|
||||
if (entry && traceEnabled()) {
|
||||
const fragment = entry.fragment;
|
||||
let result = `${fragment} Transitions:`;
|
||||
let result = `${entry.fragmentTag} Transitions:`;
|
||||
if (entry.transitionName) {
|
||||
result += `transitionName=${entry.transitionName}, `;
|
||||
}
|
||||
@@ -732,6 +787,7 @@ function printTransitions(entry: ExpandedEntry) {
|
||||
result += `popExitAnimator=${entry.popExitAnimator}, `;
|
||||
}
|
||||
if (sdkVersion() >= 21) {
|
||||
const fragment = entry.fragment;
|
||||
result += `${fragment.getEnterTransition() ? " enter=" + toShortString(fragment.getEnterTransition()) : ""}`;
|
||||
result += `${fragment.getExitTransition() ? " exit=" + toShortString(fragment.getExitTransition()) : ""}`;
|
||||
result += `${fragment.getReenterTransition() ? " popEnter=" + toShortString(fragment.getReenterTransition()) : ""}`;
|
||||
|
||||
@@ -21,14 +21,19 @@ export const enum AnimationType {
|
||||
export function _setAndroidFragmentTransitions(
|
||||
animated: boolean,
|
||||
navigationTransition: NavigationTransition,
|
||||
currentFragment: any,
|
||||
newFragment: any,
|
||||
currentEntry: BackstackEntry,
|
||||
newEntry: BackstackEntry,
|
||||
fragmentTransaction: any,
|
||||
manager: any /* android.app.FragmentManager */): void;
|
||||
manager: any /* android.app.FragmentManager */,
|
||||
frameId: number): void;
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
export function _onFragmentCreateAnimator(fragment: any, nextAnim: number): any;
|
||||
export function _onFragmentCreateAnimator(entry: BackstackEntry, fragment: any, nextAnim: number, enter: boolean): any;
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
export function _getAnimatedEntries(frameId: number): Set<BackstackEntry>;
|
||||
/**
|
||||
* @private
|
||||
* Called once fragment is recreated after it was destroyed.
|
||||
@@ -54,9 +59,9 @@ export function _clearEntry(entry: BackstackEntry): void;
|
||||
* Removes all animations and transitions but keeps them on the entry
|
||||
* in order to reapply them when new fragment is created for the same entry.
|
||||
*/
|
||||
export function _clearFragment(fragment: any): void;
|
||||
export function _clearFragment(entry: BackstackEntry): void;
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
export function _createIOSAnimatedTransitioning(navigationTransition: NavigationTransition, nativeCurve: any, operation: number, fromVC: any, toVC: any): any;
|
||||
//@endprivate
|
||||
//@endprivate
|
||||
|
||||
@@ -165,38 +165,89 @@ export class FrameBase extends CustomLayoutView implements FrameDefinition {
|
||||
public static androidOptionSelectedEvent = "optionSelected";
|
||||
|
||||
private _animated: boolean;
|
||||
public _currentEntry: BackstackEntry;
|
||||
private _transition: NavigationTransition;
|
||||
private _backStack = new Array<BackstackEntry>();
|
||||
private _navigationQueue = new Array<NavigationContext>();
|
||||
|
||||
public _currentEntry: BackstackEntry;
|
||||
public _executingEntry: BackstackEntry;
|
||||
public _isInFrameStack = false;
|
||||
public static defaultAnimatedNavigation = true;
|
||||
public static defaultTransition: NavigationTransition;
|
||||
|
||||
// TODO: Currently our navigation will not be synchronized in case users directly call native navigation methods like Activity.startActivity.
|
||||
|
||||
public _addChildFromBuilder(name: string, value: any) {
|
||||
if (value instanceof Page) {
|
||||
this.navigate({ create: () => value });
|
||||
}
|
||||
}
|
||||
|
||||
@profile
|
||||
public onLoaded() {
|
||||
super.onLoaded();
|
||||
this._processNextNavigationEntry();
|
||||
}
|
||||
|
||||
public canGoBack(): boolean {
|
||||
return this._backStack.length > 0;
|
||||
let backstack = this._backStack.length;
|
||||
let previousForwardNotInBackstack = false;
|
||||
this._navigationQueue.forEach(item => {
|
||||
const entry = item.entry;
|
||||
if (item.isBackNavigation) {
|
||||
previousForwardNotInBackstack = false;
|
||||
if (!entry) {
|
||||
backstack--;
|
||||
} else {
|
||||
const backstackIndex = this._backStack.indexOf(entry);
|
||||
if (backstackIndex !== -1) {
|
||||
backstack = backstackIndex;
|
||||
} else {
|
||||
// NOTE: We don't search for entries in navigationQueue because there is no way for
|
||||
// developer to get reference to BackstackEntry unless transition is completed.
|
||||
// At that point the entry is put in the backstack array.
|
||||
// If we start to return Backstack entry from navigate method then
|
||||
// here we should check also navigationQueue as well.
|
||||
backstack--;
|
||||
}
|
||||
}
|
||||
} else if (entry.entry.clearHistory) {
|
||||
previousForwardNotInBackstack = false;
|
||||
backstack = 0;
|
||||
} else {
|
||||
backstack++;
|
||||
if (previousForwardNotInBackstack) {
|
||||
backstack--;
|
||||
}
|
||||
|
||||
previousForwardNotInBackstack = entry.entry.backstackVisible === false;
|
||||
}
|
||||
});
|
||||
|
||||
// this is our first navigation which is not completed yet.
|
||||
if (this._navigationQueue.length > 0 && !this._currentEntry) {
|
||||
backstack--;
|
||||
}
|
||||
|
||||
return backstack > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigates to the previous entry (if any) in the back stack.
|
||||
* @param to The backstack entry to navigate back to.
|
||||
*/
|
||||
public goBack(backstackEntry?: BackstackEntry) {
|
||||
public goBack(backstackEntry?: BackstackEntry): void {
|
||||
if (traceEnabled()) {
|
||||
traceWrite(`GO BACK`, traceCategories.Navigation);
|
||||
}
|
||||
|
||||
if (!this.canGoBack()) {
|
||||
// TODO: Do we need to throw an error?
|
||||
return;
|
||||
}
|
||||
|
||||
if (backstackEntry) {
|
||||
const backIndex = this._backStack.indexOf(backstackEntry);
|
||||
if (backIndex < 0) {
|
||||
const index = this._backStack.indexOf(backstackEntry);
|
||||
if (index < 0) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -207,19 +258,18 @@ export class FrameBase extends CustomLayoutView implements FrameDefinition {
|
||||
}
|
||||
|
||||
this._navigationQueue.push(navigationContext);
|
||||
|
||||
if (this._navigationQueue.length === 1) {
|
||||
this._processNavigationContext(navigationContext);
|
||||
}
|
||||
else {
|
||||
if (traceEnabled()) {
|
||||
traceWrite(`Going back scheduled`, traceCategories.Navigation);
|
||||
}
|
||||
}
|
||||
this._processNextNavigationEntry();
|
||||
}
|
||||
|
||||
public _removeBackstackEntries(removed: BackstackEntry[]): void {
|
||||
// Handled in android.
|
||||
public _removeEntry(removed: BackstackEntry): void {
|
||||
const page = removed.resolvedPage;
|
||||
const frame = page.frame;
|
||||
(<any>page)._frame = null;
|
||||
if (frame) {
|
||||
frame._removeView(page);
|
||||
} else {
|
||||
page._tearDownUI(true);
|
||||
}
|
||||
}
|
||||
|
||||
// Attempts to implement https://github.com/NativeScript/NativeScript/issues/1311
|
||||
@@ -249,7 +299,7 @@ export class FrameBase extends CustomLayoutView implements FrameDefinition {
|
||||
}
|
||||
|
||||
const entry = buildEntryFromArgs(param);
|
||||
const page = resolvePageFromEntry(entry);
|
||||
const page = resolvePageFromEntry(entry) as Page;
|
||||
|
||||
// Attempts to implement https://github.com/NativeScript/NativeScript/issues/1311
|
||||
// if (page["isBiOrientational"] && entry.moduleName && !this._subscribedToOrientationChangedEvent){
|
||||
@@ -276,22 +326,59 @@ export class FrameBase extends CustomLayoutView implements FrameDefinition {
|
||||
}
|
||||
|
||||
this._navigationQueue.push(navigationContext);
|
||||
|
||||
if (this._navigationQueue.length === 1) {
|
||||
this._processNavigationContext(navigationContext);
|
||||
} else {
|
||||
if (traceEnabled()) {
|
||||
traceWrite(`Navigation scheduled`, traceCategories.Navigation);
|
||||
}
|
||||
}
|
||||
this._processNextNavigationEntry();
|
||||
}
|
||||
|
||||
public isCurrent(entry: BackstackEntry): boolean {
|
||||
return this._currentEntry === entry;
|
||||
}
|
||||
|
||||
public setCurrent(entry: BackstackEntry): void {
|
||||
public setCurrent(entry: BackstackEntry, isBack: boolean): void {
|
||||
const newPage = entry.resolvedPage;
|
||||
// In case we navigated forward to a page that was in the backstack
|
||||
// with clearHistory: true
|
||||
if (!newPage.frame) {
|
||||
this._addView(newPage);
|
||||
(<any>newPage)._frame = this;
|
||||
}
|
||||
|
||||
this._currentEntry = entry;
|
||||
this._executingEntry = null;
|
||||
newPage.onNavigatedTo(isBack);
|
||||
}
|
||||
|
||||
public _updateBackstack(entry: BackstackEntry, isBack: boolean): void {
|
||||
this.raiseCurrentPageNavigatedEvents(isBack);
|
||||
const current = this._currentEntry;
|
||||
|
||||
if (isBack) {
|
||||
const index = this._backStack.indexOf(entry);
|
||||
this._backStack.splice(index + 1).forEach(e => this._removeEntry(e));
|
||||
this._backStack.pop();
|
||||
} else {
|
||||
if (entry.entry.clearHistory) {
|
||||
this._backStack.forEach(e => this._removeEntry(e));
|
||||
this._backStack.length = 0;
|
||||
} else if (FrameBase._isEntryBackstackVisible(current)) {
|
||||
this._backStack.push(current);
|
||||
}
|
||||
}
|
||||
|
||||
if (current && this._backStack.indexOf(current) < 0) {
|
||||
this._removeEntry(current);
|
||||
}
|
||||
}
|
||||
|
||||
private raiseCurrentPageNavigatedEvents(isBack: boolean) {
|
||||
const page = this.currentPage;
|
||||
if (page) {
|
||||
if (page.isLoaded) {
|
||||
// Forward navigation does not remove page from frame so we raise unloaded manually.
|
||||
page.onUnloaded();
|
||||
}
|
||||
|
||||
page.onNavigatedFrom(isBack);
|
||||
}
|
||||
}
|
||||
|
||||
public _processNavigationQueue(page: Page) {
|
||||
@@ -300,8 +387,8 @@ export class FrameBase extends CustomLayoutView implements FrameDefinition {
|
||||
return;
|
||||
}
|
||||
|
||||
let entry = this._navigationQueue[0].entry;
|
||||
let currentNavigationPage = entry.resolvedPage;
|
||||
const entry = this._navigationQueue[0].entry;
|
||||
const currentNavigationPage = entry.resolvedPage;
|
||||
if (page !== currentNavigationPage) {
|
||||
// If the page is not the one that requested navigation - skip it.
|
||||
return;
|
||||
@@ -309,12 +396,7 @@ export class FrameBase extends CustomLayoutView implements FrameDefinition {
|
||||
|
||||
// remove completed operation.
|
||||
this._navigationQueue.shift();
|
||||
|
||||
if (this._navigationQueue.length > 0) {
|
||||
let navigationContext = this._navigationQueue[0];
|
||||
this._processNavigationContext(navigationContext);
|
||||
}
|
||||
|
||||
this._processNextNavigationEntry();
|
||||
this._updateActionBar();
|
||||
}
|
||||
|
||||
@@ -353,29 +435,25 @@ export class FrameBase extends CustomLayoutView implements FrameDefinition {
|
||||
//traceWrite("calling _updateActionBar on Frame", traceCategories.Navigation);
|
||||
}
|
||||
|
||||
protected _processNavigationContext(navigationContext: NavigationContext) {
|
||||
if (navigationContext.isBackNavigation) {
|
||||
this.performGoBack(navigationContext);
|
||||
} else {
|
||||
this.performNavigation(navigationContext);
|
||||
protected _processNextNavigationEntry() {
|
||||
if (!this.isLoaded || this._executingEntry) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
public _clearBackStack(): void {
|
||||
this._backStack.length = 0;
|
||||
if (this._navigationQueue.length > 0) {
|
||||
const navigationContext = this._navigationQueue[0];
|
||||
if (navigationContext.isBackNavigation) {
|
||||
this.performGoBack(navigationContext);
|
||||
} else {
|
||||
this.performNavigation(navigationContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@profile
|
||||
private performNavigation(navigationContext: NavigationContext) {
|
||||
let navContext = navigationContext.entry;
|
||||
|
||||
// TODO: This should happen once navigation is completed.
|
||||
if (navigationContext.entry.entry.clearHistory) {
|
||||
// Don't clear backstack immediately or we can't remove pages from frame.
|
||||
} else if (FrameBase._isEntryBackstackVisible(this._currentEntry)) {
|
||||
this._backStack.push(this._currentEntry);
|
||||
}
|
||||
|
||||
const navContext = navigationContext.entry;
|
||||
this._executingEntry = navContext;
|
||||
this._onNavigatingTo(navContext, navigationContext.isBackNavigation);
|
||||
this._navigateCore(navContext);
|
||||
}
|
||||
@@ -383,16 +461,13 @@ export class FrameBase extends CustomLayoutView implements FrameDefinition {
|
||||
@profile
|
||||
private performGoBack(navigationContext: NavigationContext) {
|
||||
let backstackEntry = navigationContext.entry;
|
||||
const backstack = this._backStack;
|
||||
if (!backstackEntry) {
|
||||
backstackEntry = this._backStack.pop();
|
||||
backstackEntry = backstack[backstack.length - 1];
|
||||
navigationContext.entry = backstackEntry;
|
||||
} else {
|
||||
const index = this._backStack.indexOf(backstackEntry);
|
||||
const removed = this._backStack.splice(index + 1);
|
||||
this._backStack.pop();
|
||||
this._removeBackstackEntries(removed);
|
||||
}
|
||||
|
||||
|
||||
this._executingEntry = backstackEntry;
|
||||
this._onNavigatingTo(backstackEntry, true);
|
||||
this._goBackCore(backstackEntry);
|
||||
}
|
||||
@@ -485,8 +560,9 @@ export class FrameBase extends CustomLayoutView implements FrameDefinition {
|
||||
}
|
||||
|
||||
public eachChildView(callback: (child: View) => boolean) {
|
||||
if (this.currentPage) {
|
||||
callback(this.currentPage);
|
||||
const page = this.currentPage;
|
||||
if (page) {
|
||||
callback(page);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -588,7 +664,7 @@ export function topmost(): FrameBase {
|
||||
|
||||
export function goBack(): boolean {
|
||||
const top = topmost();
|
||||
if (top.canGoBack()) {
|
||||
if (top && top.canGoBack()) {
|
||||
top.goBack();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1,16 +1,19 @@
|
||||
// Definitions.
|
||||
import {
|
||||
AndroidFrame as AndroidFrameDefinition, BackstackEntry,
|
||||
AndroidFrame as AndroidFrameDefinition, BackstackEntry, NavigationEntry,
|
||||
NavigationTransition, AndroidFragmentCallbacks, AndroidActivityCallbacks
|
||||
} from ".";
|
||||
import { Page } from "../page";
|
||||
|
||||
// Types.
|
||||
import * as application from "../../application";
|
||||
import { FrameBase, NavigationContext, stack, goBack, View, Observable, traceEnabled, traceWrite, traceCategories } from "./frame-common";
|
||||
import { DIALOG_FRAGMENT_TAG } from "../page/constants";
|
||||
import {
|
||||
_setAndroidFragmentTransitions, _onFragmentCreateAnimator,
|
||||
FrameBase, NavigationContext, stack, goBack, View, Observable, topmost,
|
||||
traceEnabled, traceWrite, traceCategories
|
||||
} from "./frame-common";
|
||||
|
||||
import {
|
||||
_setAndroidFragmentTransitions, _onFragmentCreateAnimator, _getAnimatedEntries,
|
||||
_updateTransitions, _reverseTransitions, _clearEntry, _clearFragment, AnimationType
|
||||
} from "./fragment.transitions";
|
||||
|
||||
@@ -21,9 +24,10 @@ export * from "./frame-common";
|
||||
const INTENT_EXTRA = "com.tns.activity";
|
||||
const FRAMEID = "_frameId";
|
||||
const CALLBACKS = "_callbacks";
|
||||
|
||||
let navDepth = -1;
|
||||
let fragmentId = -1;
|
||||
let activityInitialized: boolean;
|
||||
export let moduleLoaded: boolean;
|
||||
|
||||
if (global && global.__inspector) {
|
||||
const devtools = require("tns-core-modules/debugger/devtools-elements");
|
||||
@@ -35,6 +39,7 @@ export class Frame extends FrameBase {
|
||||
private _android: AndroidFrame;
|
||||
private _delayedNavigationEntry: BackstackEntry;
|
||||
private _containerViewId: number = -1;
|
||||
private _tearDownPending = false;
|
||||
public _isBack: boolean = true;
|
||||
|
||||
constructor() {
|
||||
@@ -64,9 +69,57 @@ export class Frame extends FrameBase {
|
||||
return this._android;
|
||||
}
|
||||
|
||||
public _getFragmentManager(): android.app.FragmentManager {
|
||||
const activity = this._android.activity;
|
||||
return activity && activity.getFragmentManager();
|
||||
}
|
||||
|
||||
protected _processNextNavigationEntry(): void {
|
||||
// In case activity was destroyed because of back button pressed (e.g. app exit)
|
||||
// and application is restored from recent apps, current fragment isn't recreated.
|
||||
// In this case call _navigateCore in order to recreate the current fragment.
|
||||
// Don't call navigate because it will fire navigation events.
|
||||
// As JS instances are alive it is already done for the current page.
|
||||
if (!this.isLoaded) {
|
||||
return;
|
||||
}
|
||||
|
||||
const animatedEntries = _getAnimatedEntries(this._android.frameId);
|
||||
if (animatedEntries) {
|
||||
// // recreate UI on the animated fragments because we have new context.
|
||||
// // We need to recreate the UI because it Frame will do it only for currentPage.
|
||||
// // Once currentPage is changed due to transition end we will have no UI on the
|
||||
// // new Page.
|
||||
// animatedEntries.forEach(entry => {
|
||||
// const page = entry.resolvedPage;
|
||||
// if (page._context !== this._context) {
|
||||
// page._tearDownUI(true);
|
||||
// page._setupUI(this._context);
|
||||
// }
|
||||
// });
|
||||
|
||||
// Wait until animations are completed.
|
||||
if (animatedEntries.size > 0) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const manager = this._getFragmentManager();
|
||||
const entry = this._currentEntry;
|
||||
if (entry && manager && !manager.findFragmentByTag(entry.fragmentTag)) {
|
||||
// Simulate first navigation (e.g. no animations or transitions)
|
||||
this._currentEntry = null;
|
||||
// NavigateCore will eventually call _processNextNavigationEntry again.
|
||||
this._navigateCore(entry);
|
||||
this._currentEntry = entry;
|
||||
} else {
|
||||
super._processNextNavigationEntry();
|
||||
}
|
||||
}
|
||||
|
||||
private createFragment(backstackEntry: BackstackEntry, fragmentTag: string): android.app.Fragment {
|
||||
ensureFragmentClass();
|
||||
const newFragment: android.app.Fragment = new fragmentClass();
|
||||
const newFragment = new fragmentClass();
|
||||
const args = new android.os.Bundle();
|
||||
args.putInt(FRAMEID, this._android.frameId);
|
||||
newFragment.setArguments(args);
|
||||
@@ -84,42 +137,72 @@ export class Frame extends FrameBase {
|
||||
return newFragment;
|
||||
}
|
||||
|
||||
public setCurrent(entry: BackstackEntry): void {
|
||||
this.changeCurrentPage(entry);
|
||||
this._currentEntry = entry;
|
||||
this._isBack = true;
|
||||
this._processNavigationQueue(entry.resolvedPage);
|
||||
}
|
||||
|
||||
private changeCurrentPage(entry: BackstackEntry) {
|
||||
const isBack = this._isBack;
|
||||
let page: Page = this.currentPage;
|
||||
if (page) {
|
||||
if (page.frame === this &&
|
||||
(isBack || this.backStack.indexOf(this._currentEntry) < 0)) {
|
||||
// If going back or navigate forward but current entry is not backstack visible.
|
||||
removeEntry(this._currentEntry, page, this, true);
|
||||
}
|
||||
|
||||
if (page.isLoaded) {
|
||||
// Forward navigation does not remove page from frame so we raise unloaded manually.
|
||||
page.onUnloaded();
|
||||
}
|
||||
|
||||
page.onNavigatedFrom(isBack);
|
||||
public setCurrent(entry: BackstackEntry, isBack: boolean): void {
|
||||
const current = this._currentEntry;
|
||||
const currentEntryChanged = current !== entry;
|
||||
if (currentEntryChanged) {
|
||||
this._updateBackstack(entry, isBack);
|
||||
}
|
||||
|
||||
const newPage = entry.resolvedPage;
|
||||
newPage._fragmentTag = entry.fragmentTag;
|
||||
this._currentEntry = entry;
|
||||
newPage.onNavigatedTo(isBack);
|
||||
if (currentEntryChanged) {
|
||||
// If activity was destroyed we need to destroy fragment and UI
|
||||
// of current and new entries.
|
||||
if (this._tearDownPending) {
|
||||
this._tearDownPending = false;
|
||||
if (!entry.recreated) {
|
||||
clearEntry(entry);
|
||||
}
|
||||
|
||||
if (current && !current.recreated) {
|
||||
clearEntry(current);
|
||||
}
|
||||
|
||||
// If we have context activity was recreated. Create new fragment
|
||||
// and UI for the new current page.
|
||||
const context = this._context;
|
||||
if (context && !entry.recreated) {
|
||||
entry.fragment = this.createFragment(entry, entry.fragmentTag);
|
||||
entry.resolvedPage._setupUI(context);
|
||||
}
|
||||
|
||||
entry.recreated = false;
|
||||
current.recreated = false;
|
||||
}
|
||||
|
||||
super.setCurrent(entry, isBack);
|
||||
|
||||
// If we had real navigation process queue.
|
||||
this._processNavigationQueue(entry.resolvedPage);
|
||||
} else {
|
||||
// Otherwise currentPage was recreated so this wasn't real navigation.
|
||||
// Continue with next item in the queue.
|
||||
this._processNextNavigationEntry();
|
||||
}
|
||||
}
|
||||
|
||||
public _onBackPressed(): boolean {
|
||||
if (this.canGoBack()) {
|
||||
this.goBack();
|
||||
return true;
|
||||
} else if (!this.navigationQueueIsEmpty()) {
|
||||
const manager = this._getFragmentManager();
|
||||
if (manager) {
|
||||
manager.executePendingTransactions();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@profile
|
||||
public _navigateCore(backstackEntry: BackstackEntry) {
|
||||
super._navigateCore(backstackEntry);
|
||||
public _navigateCore(newEntry: BackstackEntry) {
|
||||
super._navigateCore(newEntry);
|
||||
this._isBack = false;
|
||||
|
||||
// set frameId here so that we could use it in fragment.transitions
|
||||
newEntry.frameId = this._android.frameId;
|
||||
|
||||
const activity = this._android.activity;
|
||||
if (!activity) {
|
||||
// Activity not associated. In this case we have two execution paths:
|
||||
@@ -130,15 +213,13 @@ export class Frame extends FrameBase {
|
||||
startActivity(currentActivity, this._android.frameId);
|
||||
}
|
||||
|
||||
this._delayedNavigationEntry = backstackEntry;
|
||||
this._delayedNavigationEntry = newEntry;
|
||||
return;
|
||||
}
|
||||
|
||||
const manager = activity.getFragmentManager();
|
||||
|
||||
// Current Fragment
|
||||
const currentFragment = this._currentEntry ? manager.findFragmentByTag(this._currentEntry.fragmentTag) : null;
|
||||
const clearHistory = backstackEntry.entry.clearHistory;
|
||||
const manager: android.app.FragmentManager = this._getFragmentManager();
|
||||
const clearHistory = newEntry.entry.clearHistory;
|
||||
const currentEntry = this._currentEntry;
|
||||
|
||||
// New Fragment
|
||||
if (clearHistory) {
|
||||
@@ -148,22 +229,21 @@ export class Frame extends FrameBase {
|
||||
navDepth++;
|
||||
fragmentId++;
|
||||
const newFragmentTag = `fragment${fragmentId}[${navDepth}]`;
|
||||
const newFragment = this.createFragment(backstackEntry, newFragmentTag);
|
||||
const newFragment = this.createFragment(newEntry, newFragmentTag);
|
||||
const transaction = manager.beginTransaction();
|
||||
const animated = this._getIsAnimatedNavigation(backstackEntry.entry);
|
||||
const animated = this._getIsAnimatedNavigation(newEntry.entry);
|
||||
// NOTE: Don't use transition for the initial nagivation (same as on iOS)
|
||||
// On API 21+ transition won't be triggered unless there was at least one
|
||||
// layout pass so we will wait forever for transitionCompleted handler...
|
||||
// https://github.com/NativeScript/NativeScript/issues/4895
|
||||
const navigationTransition = this._currentEntry ? this._getNavigationTransition(backstackEntry.entry) : null;
|
||||
const navigationTransition = this._currentEntry ? this._getNavigationTransition(newEntry.entry) : null;
|
||||
|
||||
_setAndroidFragmentTransitions(animated, navigationTransition, currentFragment, newFragment, transaction, manager);
|
||||
if (clearHistory) {
|
||||
destroyPages(this.backStack, true);
|
||||
this._clearBackStack();
|
||||
}
|
||||
_setAndroidFragmentTransitions(animated, navigationTransition, currentEntry, newEntry, transaction, manager, this._android.frameId);
|
||||
// if (clearHistory) {
|
||||
// deleteEntries(this.backStack);
|
||||
// }
|
||||
|
||||
if (currentFragment && animated && !navigationTransition) {
|
||||
if (currentEntry && animated && !navigationTransition) {
|
||||
transaction.setTransition(android.app.FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
|
||||
}
|
||||
|
||||
@@ -172,11 +252,11 @@ export class Frame extends FrameBase {
|
||||
}
|
||||
|
||||
public _goBackCore(backstackEntry: BackstackEntry) {
|
||||
this._isBack = true;
|
||||
super._goBackCore(backstackEntry);
|
||||
navDepth = backstackEntry.navDepth;
|
||||
|
||||
const activity = this._android.activity;
|
||||
const manager = activity.getFragmentManager();
|
||||
const manager: android.app.FragmentManager = this._getFragmentManager();
|
||||
const transaction = manager.beginTransaction();
|
||||
|
||||
if (!backstackEntry.fragment) {
|
||||
@@ -198,26 +278,46 @@ export class Frame extends FrameBase {
|
||||
transaction.commit();
|
||||
}
|
||||
|
||||
public _removeBackstackEntries(removed: BackstackEntry[]): void {
|
||||
destroyPages(removed, true);
|
||||
public _removeEntry(removed: BackstackEntry): void {
|
||||
super._removeEntry(removed);
|
||||
|
||||
if (removed.fragment) {
|
||||
_clearEntry(removed);
|
||||
}
|
||||
|
||||
removed.fragment = null;
|
||||
removed.viewSavedState = null;
|
||||
}
|
||||
|
||||
public createNativeView() {
|
||||
const root = new org.nativescript.widgets.ContentLayout(this._context);
|
||||
if (this._containerViewId < 0) {
|
||||
this._containerViewId = android.view.View.generateViewId();
|
||||
}
|
||||
return root;
|
||||
return new org.nativescript.widgets.ContentLayout(this._context);
|
||||
}
|
||||
|
||||
public initNativeView(): void {
|
||||
super.initNativeView();
|
||||
this._android.rootViewGroup = this.nativeViewProtected;
|
||||
if (this._containerViewId < 0) {
|
||||
this._containerViewId = android.view.View.generateViewId();
|
||||
}
|
||||
this._android.rootViewGroup.setId(this._containerViewId);
|
||||
}
|
||||
|
||||
public disposeNativeView() {
|
||||
// we should keep the reference to underlying native object, since frame can contain many pages.
|
||||
this._tearDownPending = !!this._executingEntry;
|
||||
const current = this._currentEntry;
|
||||
|
||||
this.backStack.forEach(entry => {
|
||||
// Don't destroy current and executing entries or UI will look blank.
|
||||
// We will do it in setCurrent.
|
||||
if (entry !== this._executingEntry) {
|
||||
clearEntry(entry);
|
||||
}
|
||||
});
|
||||
|
||||
if (current && !this._executingEntry) {
|
||||
clearEntry(current);
|
||||
}
|
||||
|
||||
this._android.rootViewGroup = null;
|
||||
super.disposeNativeView();
|
||||
}
|
||||
@@ -233,20 +333,6 @@ export class Frame extends FrameBase {
|
||||
}
|
||||
}
|
||||
|
||||
public _printNativeBackStack() {
|
||||
if (!this._android.activity) {
|
||||
return;
|
||||
}
|
||||
const manager = this._android.activity.getFragmentManager();
|
||||
const length = manager.getBackStackEntryCount();
|
||||
let i = length - 1;
|
||||
console.log(`Fragment Manager Back Stack: `);
|
||||
while (i >= 0) {
|
||||
const fragment = manager.findFragmentByTag(manager.getBackStackEntryAt(i--).getName());
|
||||
console.log(`\t${fragment}`);
|
||||
}
|
||||
}
|
||||
|
||||
public _getNavBarVisible(page: Page): boolean {
|
||||
if (page.actionBarHidden !== undefined) {
|
||||
return !page.actionBarHidden;
|
||||
@@ -271,83 +357,19 @@ export class Frame extends FrameBase {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected _processNavigationContext(navigationContext: NavigationContext) {
|
||||
let activity = this._android.activity;
|
||||
if (activity) {
|
||||
let isForegroundActivity = activity === application.android.foregroundActivity;
|
||||
let isPaused = application.android.paused;
|
||||
|
||||
if (activity && !isForegroundActivity || (isForegroundActivity && isPaused)) {
|
||||
let weakActivity = new WeakRef(activity);
|
||||
let resume = (args: application.AndroidActivityEventData) => {
|
||||
let weakActivityInstance = weakActivity.get();
|
||||
let isCurrent = args.activity === weakActivityInstance;
|
||||
if (!weakActivityInstance) {
|
||||
if (traceEnabled()) {
|
||||
traceWrite(`Frame _processNavigationContext: Drop For Activity GC-ed`, traceCategories.Navigation);
|
||||
}
|
||||
unsubscribe();
|
||||
return;
|
||||
}
|
||||
if (isCurrent) {
|
||||
if (traceEnabled()) {
|
||||
traceWrite(`Frame _processNavigationContext: Activity.Resumed, Continue`, traceCategories.Navigation);
|
||||
}
|
||||
super._processNavigationContext(navigationContext);
|
||||
unsubscribe();
|
||||
}
|
||||
};
|
||||
let unsubscribe = () => {
|
||||
if (traceEnabled()) {
|
||||
traceWrite(`Frame _processNavigationContext: Unsubscribe from Activity.Resumed`, traceCategories.Navigation);
|
||||
}
|
||||
application.android.off(application.AndroidApplication.activityResumedEvent, resume);
|
||||
application.android.off(application.AndroidApplication.activityStoppedEvent, unsubscribe);
|
||||
application.android.off(application.AndroidApplication.activityDestroyedEvent, unsubscribe);
|
||||
};
|
||||
|
||||
if (traceEnabled()) {
|
||||
traceWrite(`Frame._processNavigationContext: Subscribe for Activity.Resumed`, traceCategories.Navigation);
|
||||
}
|
||||
application.android.on(application.AndroidApplication.activityResumedEvent, resume);
|
||||
application.android.on(application.AndroidApplication.activityStoppedEvent, unsubscribe);
|
||||
application.android.on(application.AndroidApplication.activityDestroyedEvent, unsubscribe);
|
||||
return;
|
||||
}
|
||||
}
|
||||
super._processNavigationContext(navigationContext);
|
||||
}
|
||||
}
|
||||
|
||||
function removeEntry(entry: BackstackEntry, page: Page, frame: View, entryUnused: boolean): void {
|
||||
function clearEntry(entry: BackstackEntry): void {
|
||||
if (entry.fragment) {
|
||||
if (entryUnused) {
|
||||
_clearEntry(entry);
|
||||
} else {
|
||||
_clearFragment(entry.fragment);
|
||||
}
|
||||
_clearFragment(entry);
|
||||
}
|
||||
|
||||
entry.recreated = false;
|
||||
entry.fragment = null;
|
||||
if (entryUnused) {
|
||||
entry.viewSavedState = null;
|
||||
const page = entry.resolvedPage;
|
||||
if (page._context) {
|
||||
entry.resolvedPage._tearDownUI(true);
|
||||
}
|
||||
|
||||
if (frame) {
|
||||
frame._removeView(page);
|
||||
}
|
||||
}
|
||||
|
||||
function destroyPages(backStack: Array<BackstackEntry>, entryUnused: boolean): void {
|
||||
if (traceEnabled()) {
|
||||
traceWrite(`CLEAR HISTORY`, traceCategories.Navigation);
|
||||
}
|
||||
|
||||
backStack.forEach((entry) => {
|
||||
const page = entry.resolvedPage;
|
||||
removeEntry(entry, page, page.frame, entryUnused);
|
||||
});
|
||||
}
|
||||
|
||||
let framesCounter = 0;
|
||||
@@ -445,15 +467,10 @@ class AndroidFrame extends Observable implements AndroidFrameDefinition {
|
||||
return this.activity.getIntent().getAction() !== android.content.Intent.ACTION_MAIN;
|
||||
}
|
||||
|
||||
public fragmentForPage(page: Page): any {
|
||||
if (!page) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
let tag = page._fragmentTag;
|
||||
public fragmentForPage(entry: BackstackEntry): any {
|
||||
const tag = entry && entry.fragmentTag;
|
||||
if (tag) {
|
||||
let manager = this.activity.getFragmentManager();
|
||||
return manager.findFragmentByTag(tag);
|
||||
return this.owner._getFragmentManager().findFragmentByTag(tag);
|
||||
}
|
||||
|
||||
return undefined;
|
||||
@@ -466,12 +483,21 @@ function findPageForFragment(fragment: android.app.Fragment, frame: Frame) {
|
||||
traceWrite(`Finding page for ${fragmentTag}.`, traceCategories.NativeLifecycle);
|
||||
}
|
||||
|
||||
if (fragmentTag === DIALOG_FRAGMENT_TAG) {
|
||||
return;
|
||||
let entry: BackstackEntry;
|
||||
const current = frame._currentEntry;
|
||||
const navigating = frame._executingEntry;
|
||||
if (current && current.fragmentTag === fragmentTag) {
|
||||
entry = current;
|
||||
} else if (navigating && navigating.fragmentTag === fragmentTag) {
|
||||
entry = navigating;
|
||||
}
|
||||
|
||||
let page: Page;
|
||||
if (entry) {
|
||||
entry.recreated = true;
|
||||
page = entry.resolvedPage;
|
||||
}
|
||||
|
||||
const entry = frame._findEntryForTag(fragmentTag);
|
||||
const page = entry ? entry.resolvedPage : undefined;
|
||||
if (page) {
|
||||
const callbacks: FragmentCallbacksImplementation = fragment[CALLBACKS];
|
||||
callbacks.frame = frame;
|
||||
@@ -549,7 +575,7 @@ class FragmentCallbacksImplementation implements AndroidFragmentCallbacks {
|
||||
case -40: nextAnimString = "popExit"; break;
|
||||
}
|
||||
|
||||
let animator = _onFragmentCreateAnimator(fragment, nextAnim);
|
||||
let animator = _onFragmentCreateAnimator(this.entry, fragment, nextAnim, enter);
|
||||
if (!animator) {
|
||||
animator = superFunc.call(fragment, transit, enter, nextAnim);
|
||||
}
|
||||
@@ -570,7 +596,8 @@ class FragmentCallbacksImplementation implements AndroidFragmentCallbacks {
|
||||
// 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) {
|
||||
const frameId = fragment.getArguments().getInt(FRAMEID);
|
||||
const args = fragment.getArguments();
|
||||
const frameId = args.getInt(FRAMEID);
|
||||
const frame = getFrameById(frameId);
|
||||
if (!frame) {
|
||||
throw new Error(`Cannot find Frame for ${fragment}`);
|
||||
@@ -588,19 +615,26 @@ class FragmentCallbacksImplementation implements AndroidFragmentCallbacks {
|
||||
|
||||
const entry = this.entry;
|
||||
const page = entry.resolvedPage;
|
||||
try {
|
||||
const frame = this.frame;
|
||||
if (page.parent === frame) {
|
||||
if (frame.isLoaded && !page.isLoaded) {
|
||||
page.onLoaded();
|
||||
}
|
||||
} else {
|
||||
this.frame._addView(page);
|
||||
const frame = this.frame;
|
||||
if (page.parent === frame) {
|
||||
// If we are navigating to a page that was destroyed
|
||||
// reinitialize its UI.
|
||||
if (!page._context) {
|
||||
const context = container && container.getContext() || inflater && inflater.getContext();
|
||||
page._setupUI(context);
|
||||
}
|
||||
} catch (ex) {
|
||||
const label = new android.widget.TextView(container.getContext());
|
||||
label.setText(ex.message + ", " + ex.stackTrace);
|
||||
return label;
|
||||
} else {
|
||||
this.frame._addView(page);
|
||||
}
|
||||
|
||||
// Load page here even if root view is not loaded yet.
|
||||
// Otherwiaw it will show as blank,
|
||||
// The case is Tab->Frame->Page activity recreated, fragments are
|
||||
// created before Tab loads its items.
|
||||
// TODO: addCheck if the fragment is visible so we don't load pages
|
||||
// that are not in the selectedIndex of the Tab!!!!!!
|
||||
if (!page.isLoaded) {
|
||||
page.onLoaded();
|
||||
}
|
||||
|
||||
const savedState = entry.viewSavedState;
|
||||
@@ -636,14 +670,25 @@ class FragmentCallbacksImplementation implements AndroidFragmentCallbacks {
|
||||
superFunc.call(fragment);
|
||||
}
|
||||
|
||||
@profile
|
||||
public onStop(fragment: android.app.Fragment, superFunc: Function): void {
|
||||
superFunc.call(fragment);
|
||||
this.entry.resolvedPage.onUnloaded();
|
||||
}
|
||||
|
||||
@profile
|
||||
public toStringOverride(fragment: android.app.Fragment, superFunc: Function): string {
|
||||
return `${fragment.getTag()}<${this.entry ? this.entry.resolvedPage : ""}>`;
|
||||
const entry = this.entry;
|
||||
if (entry) {
|
||||
return `${entry.fragmentTag}<${entry.resolvedPage}>`;
|
||||
} else {
|
||||
return "NO ENTRY, " + superFunc.call(fragment);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ActivityCallbacksImplementation implements AndroidActivityCallbacks {
|
||||
private _rootView: View;
|
||||
public _rootView: View;
|
||||
|
||||
@profile
|
||||
public onCreate(activity: android.app.Activity, savedInstanceState: android.os.Bundle, superFunc: Function): void {
|
||||
@@ -693,10 +738,10 @@ class ActivityCallbacksImplementation implements AndroidActivityCallbacks {
|
||||
|
||||
// If there is savedInstanceState this call will recreate all fragments that were previously in the navigation.
|
||||
// We take care of associating them with a Page from our backstack in the onAttachFragment callback.
|
||||
// If there is savedInstanceState and activityInitialized is false we are restarted but process was killed.
|
||||
// If there is savedInstanceState and moduleLoaded is false we are restarted but process was killed.
|
||||
// For now we treat it like first run (e.g. we are not passing savedInstanceState so no fragments are being restored).
|
||||
// When we add support for application save/load state - revise this logic.
|
||||
let isRestart = !!savedInstanceState && activityInitialized;
|
||||
let isRestart = !!savedInstanceState && moduleLoaded;
|
||||
superFunc.call(activity, isRestart ? savedInstanceState : null);
|
||||
|
||||
this._rootView = rootView;
|
||||
@@ -709,7 +754,7 @@ class ActivityCallbacksImplementation implements AndroidActivityCallbacks {
|
||||
frame.navigate(navParam);
|
||||
}
|
||||
|
||||
activityInitialized = true;
|
||||
moduleLoaded = true;
|
||||
}
|
||||
|
||||
@profile
|
||||
@@ -760,23 +805,19 @@ class ActivityCallbacksImplementation implements AndroidActivityCallbacks {
|
||||
|
||||
@profile
|
||||
public onDestroy(activity: any, superFunc: Function): void {
|
||||
const rootView = this._rootView;
|
||||
if (rootView && rootView._context) {
|
||||
rootView._tearDownUI(true);
|
||||
}
|
||||
|
||||
superFunc.call(activity);
|
||||
|
||||
if (traceEnabled()) {
|
||||
traceWrite("NativeScriptActivity.onDestroy();", traceCategories.NativeLifecycle);
|
||||
}
|
||||
|
||||
const rootView = this._rootView;
|
||||
if (rootView) {
|
||||
rootView._tearDownUI(true);
|
||||
}
|
||||
|
||||
const exitArgs = { eventName: application.exitEvent, object: application.android, android: activity };
|
||||
application.notify(exitArgs);
|
||||
|
||||
if (rootView instanceof Frame) {
|
||||
destroyPages(rootView.backStack, false);
|
||||
}
|
||||
superFunc.call(activity);
|
||||
}
|
||||
|
||||
@profile
|
||||
@@ -792,12 +833,29 @@ class ActivityCallbacksImplementation implements AndroidActivityCallbacks {
|
||||
cancel: false,
|
||||
};
|
||||
application.android.notify(args);
|
||||
|
||||
if (args.cancel) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!goBack()) {
|
||||
const view = this._rootView;
|
||||
let callSuper = false;
|
||||
if (view instanceof Frame) {
|
||||
callSuper = !view._onBackPressed();
|
||||
} else {
|
||||
const viewArgs = <application.AndroidActivityBackPressedEventData>{
|
||||
eventName: "activityBackPressed",
|
||||
object: view,
|
||||
activity: activity,
|
||||
cancel: false,
|
||||
};
|
||||
view.notify(viewArgs);
|
||||
|
||||
if (!viewArgs.cancel) { //&& !view._onBackPressed()) {
|
||||
callSuper = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (callSuper) {
|
||||
superFunc.call(activity);
|
||||
}
|
||||
}
|
||||
|
||||
20
tns-core-modules/ui/frame/frame.d.ts
vendored
20
tns-core-modules/ui/frame/frame.d.ts
vendored
@@ -110,8 +110,9 @@ export class Frame extends View {
|
||||
/**
|
||||
* @private
|
||||
* @param entry to set as current
|
||||
* @param isBack true when we set current because of back navigation.
|
||||
*/
|
||||
setCurrent(entry: BackstackEntry): void;
|
||||
setCurrent(entry: BackstackEntry, isBack: boolean): void;
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
@@ -120,6 +121,10 @@ export class Frame extends View {
|
||||
* @private
|
||||
*/
|
||||
navigationBarHeight: number;
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_currentEntry: BackstackEntry;
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
@@ -139,7 +144,7 @@ export class Frame extends View {
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_clearBackStack(): void;
|
||||
_updateBackstack(entry: BackstackEntry, isBack: boolean): void;
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
@@ -300,6 +305,14 @@ export interface BackstackEntry {
|
||||
* @private
|
||||
*/
|
||||
viewSavedState?: any;
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
frameId?: number;
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
recreated?: boolean;
|
||||
//@endprivate
|
||||
}
|
||||
|
||||
@@ -360,7 +373,7 @@ export interface AndroidFrame extends Observable {
|
||||
* Finds the native android.app.Fragment instance created for the specified Page.
|
||||
* @param page The Page instance to search for.
|
||||
*/
|
||||
fragmentForPage(page: Page): any;
|
||||
fragmentForPage(entry: BackstackEntry): any;
|
||||
}
|
||||
|
||||
export interface AndroidActivityCallbacks {
|
||||
@@ -382,6 +395,7 @@ export interface AndroidFragmentCallbacks {
|
||||
onSaveInstanceState(fragment: any, outState: any, superFunc: Function): void;
|
||||
onDestroyView(fragment: any, superFunc: Function): void;
|
||||
onDestroy(fragment: any, superFunc: Function): void;
|
||||
onStop(fragment: any, superFunc: Function): void;
|
||||
toStringOverride(fragment: any, superFunc: Function): string;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Definitions.
|
||||
import { iOSFrame as iOSFrameDefinition, BackstackEntry, NavigationTransition } from ".";
|
||||
import { iOSFrame as iOSFrameDefinition, BackstackEntry, NavigationTransition, NavigationEntry } from ".";
|
||||
import { Page } from "../page";
|
||||
import { profile } from "../../profiling";
|
||||
|
||||
@@ -54,12 +54,11 @@ function handleNotification(notification: NSNotification): void {
|
||||
}
|
||||
|
||||
export class Frame extends FrameBase {
|
||||
public viewController: UINavigationControllerImpl;
|
||||
private _ios: iOSFrame;
|
||||
private _paramToNavigate: any;
|
||||
public _animatedDelegate = <UINavigationControllerDelegate>UINavigationControllerAnimatedDelegate.new();
|
||||
|
||||
public _shouldSkipNativePop: boolean = false;
|
||||
public _navigateToEntry: BackstackEntry;
|
||||
public _widthMeasureSpec: number;
|
||||
public _heightMeasureSpec: number;
|
||||
public _right: number;
|
||||
@@ -69,27 +68,12 @@ export class Frame extends FrameBase {
|
||||
constructor() {
|
||||
super();
|
||||
this._ios = new iOSFrame(this);
|
||||
this.viewController = this._ios.controller;
|
||||
this.nativeViewProtected = this._ios.controller.view;
|
||||
}
|
||||
|
||||
@profile
|
||||
public onLoaded() {
|
||||
super.onLoaded();
|
||||
|
||||
if (this._paramToNavigate) {
|
||||
this.navigate(this._paramToNavigate);
|
||||
this._paramToNavigate = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
public navigate(param: any) {
|
||||
if (this.isLoaded) {
|
||||
super.navigate(param);
|
||||
this._isInitialNavigation = false;
|
||||
}
|
||||
else {
|
||||
this._paramToNavigate = param;
|
||||
}
|
||||
public get ios(): iOSFrame {
|
||||
return this._ios;
|
||||
}
|
||||
|
||||
@profile
|
||||
@@ -103,7 +87,6 @@ export class Frame extends FrameBase {
|
||||
|
||||
let clearHistory = backstackEntry.entry.clearHistory;
|
||||
if (clearHistory) {
|
||||
this._clearBackStack();
|
||||
navDepth = -1;
|
||||
}
|
||||
navDepth++;
|
||||
@@ -264,10 +247,6 @@ export class Frame extends FrameBase {
|
||||
}
|
||||
}
|
||||
|
||||
public get ios(): iOSFrame {
|
||||
return this._ios;
|
||||
}
|
||||
|
||||
public static get defaultAnimatedNavigation(): boolean {
|
||||
return FrameBase.defaultAnimatedNavigation;
|
||||
}
|
||||
@@ -302,12 +281,6 @@ export class Frame extends FrameBase {
|
||||
this._heightMeasureSpec = heightMeasureSpec;
|
||||
|
||||
let result = this.measurePage(this.currentPage);
|
||||
if (this._navigateToEntry && this.currentPage) {
|
||||
let newPageSize = this.measurePage(this._navigateToEntry.resolvedPage);
|
||||
result.measuredWidth = Math.max(result.measuredWidth, newPageSize.measuredWidth);
|
||||
result.measuredHeight = Math.max(result.measuredHeight, newPageSize.measuredHeight);
|
||||
}
|
||||
|
||||
let widthAndState = View.resolveSizeAndState(result.measuredWidth, width, widthMode, 0);
|
||||
let heightAndState = View.resolveSizeAndState(result.measuredHeight, height, heightMode, 0);
|
||||
|
||||
@@ -332,9 +305,6 @@ export class Frame extends FrameBase {
|
||||
this._bottom = bottom;
|
||||
this._handleHigherInCallStatusBarIfNeeded();
|
||||
this.layoutPage(this.currentPage);
|
||||
if (this._navigateToEntry && this.currentPage) {
|
||||
this.layoutPage(this._navigateToEntry.resolvedPage);
|
||||
}
|
||||
}
|
||||
|
||||
public layoutPage(page: Page): void {
|
||||
@@ -506,12 +476,21 @@ class UINavigationControllerImpl extends UINavigationController {
|
||||
@profile
|
||||
public viewWillAppear(animated: boolean): void {
|
||||
super.viewWillAppear(animated);
|
||||
let owner = this._owner.get();
|
||||
if (owner && (!owner.isLoaded && !owner.parent)) {
|
||||
const owner = this._owner.get();
|
||||
if (owner && !owner.isLoaded && !owner.parent) {
|
||||
owner.onLoaded();
|
||||
}
|
||||
}
|
||||
|
||||
@profile
|
||||
public viewDidDisappear(animated: boolean): void {
|
||||
super.viewDidDisappear(animated);
|
||||
const owner = this._owner.get();
|
||||
if (owner && owner.isLoaded && !owner.parent) {
|
||||
owner.onUnloaded();
|
||||
}
|
||||
}
|
||||
|
||||
@profile
|
||||
public viewDidLayoutSubviews(): void {
|
||||
let owner = this._owner.get();
|
||||
|
||||
@@ -19,16 +19,15 @@ const ENTRY = "_entry";
|
||||
const DELEGATE = "_delegate";
|
||||
|
||||
function isBackNavigationTo(page: Page, entry): boolean {
|
||||
let frame = page.frame;
|
||||
const frame = page.frame;
|
||||
if (!frame) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (frame.navigationQueueIsEmpty()) {
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
let navigationQueue = (<any>frame)._navigationQueue;
|
||||
} else {
|
||||
const navigationQueue = (<any>frame)._navigationQueue;
|
||||
for (let i = 0; i < navigationQueue.length; i++) {
|
||||
if (navigationQueue[i].entry === entry) {
|
||||
return navigationQueue[i].isBackNavigation;
|
||||
@@ -65,7 +64,7 @@ class UIViewControllerImpl extends UIViewController {
|
||||
public shown: boolean;
|
||||
|
||||
public static initWithOwner(owner: WeakRef<Page>): UIViewControllerImpl {
|
||||
let controller = <UIViewControllerImpl>UIViewControllerImpl.new();
|
||||
const controller = <UIViewControllerImpl>UIViewControllerImpl.new();
|
||||
controller._owner = owner;
|
||||
controller.automaticallyAdjustsScrollViewInsets = false;
|
||||
controller.shown = false;
|
||||
@@ -152,7 +151,7 @@ class UIViewControllerImpl extends UIViewController {
|
||||
|
||||
// Don't raise event if currentPage was showing modal page.
|
||||
if (!page._presentedViewController && newEntry && (!frame || frame.currentPage !== page)) {
|
||||
let isBack = isBackNavigationTo(page, newEntry);
|
||||
const isBack = isBackNavigationTo(page, newEntry);
|
||||
page.onNavigatingTo(newEntry.entry.context, isBack, newEntry.entry.bindingContext);
|
||||
}
|
||||
|
||||
@@ -165,18 +164,14 @@ class UIViewControllerImpl extends UIViewController {
|
||||
|
||||
if (frame) {
|
||||
if (!page.parent) {
|
||||
if (!frame._currentEntry) {
|
||||
frame._currentEntry = newEntry;
|
||||
} else {
|
||||
frame._navigateToEntry = newEntry;
|
||||
}
|
||||
|
||||
frame._addView(page);
|
||||
frame.remeasureFrame();
|
||||
} else if (page.parent !== frame) {
|
||||
throw new Error("Page is already shown on another frame.");
|
||||
}
|
||||
|
||||
frame.measurePage(page);
|
||||
frame.layoutPage(page);
|
||||
|
||||
page.actionBar.update();
|
||||
}
|
||||
|
||||
@@ -206,7 +201,8 @@ class UIViewControllerImpl extends UIViewController {
|
||||
//https://github.com/NativeScript/NativeScript/issues/1201
|
||||
page._viewWillDisappear = false;
|
||||
|
||||
let frame = this.navigationController ? (<any>this.navigationController).owner : null;
|
||||
const navigationController = this.navigationController;
|
||||
const frame = navigationController ? (<any>navigationController).owner : null;
|
||||
// Skip navigation events if modal page is shown.
|
||||
if (!page._presentedViewController && frame) {
|
||||
let newEntry = this[ENTRY];
|
||||
@@ -216,25 +212,24 @@ class UIViewControllerImpl extends UIViewController {
|
||||
isBack = false;
|
||||
}
|
||||
|
||||
frame._navigateToEntry = null;
|
||||
frame._currentEntry = newEntry;
|
||||
frame._updateBackstack(newEntry, isBack);
|
||||
frame.setCurrent(newEntry, isBack);
|
||||
frame.remeasureFrame();
|
||||
frame._updateActionBar(page);
|
||||
|
||||
page.onNavigatedTo(isBack);
|
||||
|
||||
// If page was shown with custom animation - we need to set the navigationController.delegate to the animatedDelegate.
|
||||
frame.ios.controller.delegate = this[DELEGATE];
|
||||
|
||||
frame._processNavigationQueue(page);
|
||||
|
||||
// _processNavigationQueue will shift navigationQueue. Check canGoBack after that.
|
||||
// Workaround for disabled backswipe on second custom native transition
|
||||
if (frame.canGoBack()) {
|
||||
this.navigationController.interactivePopGestureRecognizer.delegate = this.navigationController;
|
||||
this.navigationController.interactivePopGestureRecognizer.enabled = page.enableSwipeBackNavigation;
|
||||
navigationController.interactivePopGestureRecognizer.delegate = navigationController;
|
||||
navigationController.interactivePopGestureRecognizer.enabled = page.enableSwipeBackNavigation;
|
||||
} else {
|
||||
this.navigationController.interactivePopGestureRecognizer.enabled = false;
|
||||
navigationController.interactivePopGestureRecognizer.enabled = false;
|
||||
}
|
||||
|
||||
frame._processNavigationQueue(page);
|
||||
}
|
||||
|
||||
if (!this.presentedViewController) {
|
||||
@@ -243,7 +238,7 @@ class UIViewControllerImpl extends UIViewController {
|
||||
// If we clean it when we have viewController then once presented VC is dismissed then
|
||||
page._presentedViewController = null;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@profile
|
||||
public viewWillDisappear(animated: boolean): void {
|
||||
@@ -299,34 +294,38 @@ class UIViewControllerImpl extends UIViewController {
|
||||
|
||||
// Manually pop backStack when Back button is pressed or navigating back with edge swipe.
|
||||
// Don't pop if we are hiding modally shown page.
|
||||
let frame = page.frame;
|
||||
// const frame = page.frame;
|
||||
// We are not modal page, have frame with backstack and navigation queue is empty and currentPage is closed
|
||||
// then pop our backstack.
|
||||
if (!modalParent && frame && frame.backStack.length > 0 && frame.navigationQueueIsEmpty() && frame.currentPage === page) {
|
||||
(<any>frame)._backStack.pop();
|
||||
}
|
||||
// If we are in frame wich is in tab and tab.selectedControler is not the frame
|
||||
// skip navigation.
|
||||
// const tab = this.tabBarController;
|
||||
// const fireNavigationEvents = !tab
|
||||
// || tab.selectedViewController === this.navigationController;
|
||||
|
||||
// Remove from parent if page was in frame and we navigated back or
|
||||
// navigate forward but current entry is not backstack visible.
|
||||
// Showing page modally will not pass isBack check so currentPage won't be removed from Frame.
|
||||
// const isBack = isBackNavigationFrom(this, page);
|
||||
// if (frame && page.frame === frame &&
|
||||
// (isBack || !frame._isCurrentEntryBackstackVisible)) {
|
||||
// // Remove parent when navigating back.
|
||||
// frame._removeBackstackEntries([_removeBackstackEntries])
|
||||
// frame._removeView(page);
|
||||
// page._frame = null;
|
||||
// }
|
||||
|
||||
page._enableLoadedEvents = true;
|
||||
|
||||
// Remove from parent if page was in frame and we navigated back.
|
||||
// Showing page modally will not pass isBack check so currentPage won't be removed from Frame.
|
||||
let isBack = isBackNavigationFrom(this, page);
|
||||
if (isBack) {
|
||||
// Remove parent when navigating back.
|
||||
frame._removeView(page);
|
||||
}
|
||||
|
||||
// Forward navigation does not remove page from frame so we raise unloaded manually.
|
||||
if (page.isLoaded) {
|
||||
page.onUnloaded();
|
||||
}
|
||||
|
||||
page._enableLoadedEvents = false;
|
||||
|
||||
if (!modalParent) {
|
||||
// Last raise onNavigatedFrom event if we are not modally shown.
|
||||
page.onNavigatedFrom(isBack);
|
||||
}
|
||||
// if (!modalParent && fireNavigationEvents) {
|
||||
// // Last raise onNavigatedFrom event if we are not modally shown.
|
||||
// page.onNavigatedFrom(isBack);
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -461,10 +460,10 @@ export class Page extends PageBase {
|
||||
public _updateStatusBarStyle(value?: string) {
|
||||
const frame = this.frame;
|
||||
if (this.frame && value) {
|
||||
let navigationController = frame.ios.controller;
|
||||
let navigationBar = navigationController.navigationBar;
|
||||
const navigationController: UINavigationController = frame.ios.controller;
|
||||
const navigationBar = navigationController.navigationBar;
|
||||
|
||||
navigationBar.barStyle = value === "dark" ? 1 : 0;
|
||||
navigationBar.barStyle = value === "dark" ? UIBarStyle.Black : UIBarStyle.Default;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -587,9 +586,6 @@ export class Page extends PageBase {
|
||||
super._removeViewFromNativeVisualTree(view);
|
||||
}
|
||||
|
||||
[actionBarHiddenProperty.getDefault](): boolean {
|
||||
return undefined;
|
||||
}
|
||||
[actionBarHiddenProperty.setNative](value: boolean) {
|
||||
this._updateEnableSwipeBackNavigation(value);
|
||||
if (this.isLoaded) {
|
||||
@@ -602,9 +598,9 @@ export class Page extends PageBase {
|
||||
return UIBarStyle.Default;
|
||||
}
|
||||
[statusBarStyleProperty.setNative](value: string | UIBarStyle) {
|
||||
let frame = this.frame;
|
||||
const frame = this.frame;
|
||||
if (frame) {
|
||||
let navigationBar = (<UINavigationController>frame.ios.controller).navigationBar;
|
||||
const navigationBar = (<UINavigationController>frame.ios.controller).navigationBar;
|
||||
if (typeof value === "string") {
|
||||
navigationBar.barStyle = value === "dark" ? UIBarStyle.Black : UIBarStyle.Default;
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user