Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 16 additions & 16 deletions packages/core/ui/frame/fragment.transitions.android.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,22 +38,22 @@ interface ExpandedAnimator extends android.animation.Animator {
}

export interface ExpandedEntry extends BackstackEntry {
enterTransitionListener: ExpandedTransitionListener;
exitTransitionListener: ExpandedTransitionListener;
reenterTransitionListener: ExpandedTransitionListener;
returnTransitionListener: ExpandedTransitionListener;

enterAnimator: ExpandedAnimator;
exitAnimator: ExpandedAnimator;
popEnterAnimator: ExpandedAnimator;
popExitAnimator: ExpandedAnimator;

transition: Transition;
transitionName: string;
frameId: number;

isNestedDefaultTransition: boolean;
isAnimationRunning: boolean;
enterTransitionListener?: ExpandedTransitionListener;
exitTransitionListener?: ExpandedTransitionListener;
reenterTransitionListener?: ExpandedTransitionListener;
returnTransitionListener?: ExpandedTransitionListener;

enterAnimator?: ExpandedAnimator;
exitAnimator?: ExpandedAnimator;
popEnterAnimator?: ExpandedAnimator;
popExitAnimator?: ExpandedAnimator;

transition?: Transition;
transitionName?: string;
frameId?: number;

isNestedDefaultTransition?: boolean;
isAnimationRunning?: boolean;
}

export function _setAndroidFragmentTransitions(animated: boolean, navigationTransition: NavigationTransition, currentEntry: ExpandedEntry, newEntry: ExpandedEntry, frameId: number, fragmentTransaction: androidx.fragment.app.FragmentTransaction, layoutDirection: CoreTypes.LayoutDirectionType, isNestedDefaultTransition?: boolean): void {
Expand Down
4 changes: 2 additions & 2 deletions packages/core/ui/frame/frame-common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { sanitizeModuleName } from '../../utils/common';
import { profile } from '../../profiling';
import { FRAME_SYMBOL } from './frame-helpers';
import { SharedTransition } from '../transition/shared-transition';
import { NavigationData } from '.';
import { Frame as FrameDefinition, NavigationData } from '.';

export { NavigationType } from './frame-interfaces';
export type { AndroidActivityCallbacks, AndroidFragmentCallbacks, AndroidFrame, BackstackEntry, NavigationContext, NavigationEntry, NavigationTransition, TransitionState, ViewEntry, iOSFrame, NavigationData } from './frame-interfaces';
Expand All @@ -35,7 +35,7 @@ function buildEntryFromArgs(arg: any): NavigationEntry {
}

@CSSType('Frame')
export class FrameBase extends CustomLayoutView {
export class FrameBase extends CustomLayoutView implements FrameDefinition {
public static navigatingToEvent = 'navigatingTo';
public static navigatedToEvent = 'navigatedTo';

Expand Down
32 changes: 20 additions & 12 deletions packages/core/ui/frame/frame-helper-for-android.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import { Trace } from '../../trace';
import { _clearEntry, _clearFragment, _getAnimatedEntries, _reverseTransitions, _setAndroidFragmentTransitions, _updateTransitions } from './fragment.transitions';
import type { BackstackEntry } from '.';
import type { AndroidFrame, BackstackEntry, Frame } from '.';
import { profile } from '../../profiling';
import { getNativeApp } from '../../application/helpers-common';
import { Color } from '../../color';
import type { Page } from '../page';
import type { AndroidFrame as Frame } from '.';
import type { ExpandedEntry } from './fragment.transitions.android';
export const FRAMEID = '_frameId';
export const CALLBACKS = '_callbacks';
export const framesCache = new Array<WeakRef<any>>();
export const framesCache = new Array<WeakRef<AndroidFrame>>();

export interface AndroidFragmentCallbacks {
onHiddenChanged(fragment: any, hidden: boolean, superFunc: Function): void;
Expand Down Expand Up @@ -58,7 +58,7 @@ function findPageForFragment(fragment: androidx.fragment.app.Fragment, frame: Fr

export class FragmentCallbacksImplementation implements AndroidFragmentCallbacks {
public frame: Frame;
public entry: BackstackEntry;
public entry: ExpandedEntry;
private backgroundBitmap: android.graphics.Bitmap = null;

@profile
Expand Down Expand Up @@ -233,11 +233,14 @@ export class FragmentCallbacksImplementation implements AndroidFragmentCallbacks
return null;
}

// [nested frames / fragments] see https://github.com/NativeScript/NativeScript/issues/6629
// retaining reference to a destroyed fragment here somehow causes a cryptic
// "IllegalStateException: Failure saving state: active fragment has cleared index: -1"
// in a specific mixed parent / nested frame navigation scenario
entry.fragment = null;
// Check if entry fragment is still this fragment as the destroy lifecycle might have been called due to replace
if (entry.fragment === fragment) {
// [nested frames / fragments] see https://github.com/NativeScript/NativeScript/issues/6629
// retaining reference to a destroyed fragment here somehow causes a cryptic
// "IllegalStateException: Failure saving state: active fragment has cleared index: -1"
// in a specific mixed parent / nested frame navigation scenario
entry.fragment = null;
}

const page = entry.resolvedPage;
if (!page) {
Expand Down Expand Up @@ -279,8 +282,13 @@ export class FragmentCallbacksImplementation implements AndroidFragmentCallbacks
if (!owner) {
return;
}
if (frame._executingContext && !(<any>owner.entry).isAnimationRunning) {
frame.setCurrent(owner.entry, frame._executingContext.navigationType);
if (!owner.entry.isAnimationRunning) {
if (frame._executingContext) {
frame.setCurrent(owner.entry, frame._executingContext.navigationType);
} else {
// Restore cached animation settings if we just completed simulated first navigation (no animation)
frame._restoreTransitionState?.();
}
}
}, 0);

Expand Down Expand Up @@ -327,7 +335,7 @@ export class FragmentCallbacksImplementation implements AndroidFragmentCallbacks

export function getFrameByNumberId(frameId: number): Frame {
// Find the frame for this activity.
for (let i = 0; i < framesCache.length; i++) {
for (let i = 0, length = framesCache.length; i < length; i++) {
const aliveFrame = framesCache[i].get();
if (aliveFrame && aliveFrame.frameId === frameId) {
return aliveFrame.owner;
Expand Down
97 changes: 26 additions & 71 deletions packages/core/ui/frame/index.android.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import type { AndroidActivityCallbacks, AndroidFrame as AndroidFrameDefinition, NavigationTransition, AndroidFragmentCallbacks } from '.';
import type { AndroidActivityCallbacks, Frame as FrameDefinition, AndroidFrame as AndroidFrameDefinition, NavigationTransition } from '.';
import type { BackstackEntry } from './frame-interfaces';
import type { Page } from '../page';
import { TransitionState } from './frame-common';
import { Observable } from '../../data/observable';
import { Trace } from '../../trace';
import { View } from '../core/view';
import { _stack, FrameBase, NavigationType } from './frame-common';
import { _clearEntry, _clearFragment, _getAnimatedEntries, _getTransitionState, _restoreTransitionState, _reverseTransitions, _setAndroidFragmentTransitions, _updateTransitions, addNativeTransitionListener } from './fragment.transitions';
import { _clearEntry, _clearFragment, _getAnimatedEntries, _getTransitionState, _restoreTransitionState, _reverseTransitions, _setAndroidFragmentTransitions, _updateTransitions } from './fragment.transitions';
import { profile } from '../../profiling';
import { android as androidUtils } from '../../utils/native-helper';
import type { ExpandedEntry } from './fragment.transitions.android';
Expand All @@ -25,7 +25,6 @@ export { setFragmentClass } from './fragment';
const INTENT_EXTRA = 'com.tns.activity';

const ownerSymbol = Symbol('_owner');
const isPendingDetachSymbol = Symbol('_isPendingDetach');

let navDepth = -1;
let fragmentId = -1;
Expand Down Expand Up @@ -54,12 +53,6 @@ function getAttachListener(): android.view.View.OnAttachStateChangeListener {
if (owner) {
owner._onDetachedFromWindow();
}

if (view[isPendingDetachSymbol]) {
delete view[isPendingDetachSymbol];
view.removeOnAttachStateChangeListener(this);
view[ownerSymbol] = null;
}
},
});

Expand All @@ -82,7 +75,6 @@ export class Frame extends FrameBase {
*/
private _isReset = false;
private _cachedTransitionState: TransitionState;
private _frameCreateTimeout: NodeJS.Timeout;

constructor() {
super();
Expand Down Expand Up @@ -153,7 +145,6 @@ export class Frame extends FrameBase {
this._attachedToWindow = true;
this._isReset = false;
this._processNextNavigationEntry();
this._ensureEntryFragment();
}

_onDetachedFromWindow(): void {
Expand Down Expand Up @@ -208,7 +199,7 @@ export class Frame extends FrameBase {
if (cachedTransitionState) {
this._cachedTransitionState = cachedTransitionState;
this._currentEntry = null;
// NavigateCore will eventually call _processNextNavigationEntry again.
// NavigateCore will eventually call _processNextNavigationEntry again
this._navigateCore(entry);
this._currentEntry = entry;
} else {
Expand Down Expand Up @@ -251,51 +242,9 @@ export class Frame extends FrameBase {
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(() => {
// 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
const currentEntry = this._currentEntry || this._executingContext?.entry;
if (currentEntry) {
if (!currentEntry.fragment) {
const manager = this._getFragmentManager();
const transaction = manager.beginTransaction();
currentEntry.fragment = this.createFragment(currentEntry, currentEntry.fragmentTag);
_updateTransitions(currentEntry);
transaction.replace(this.containerViewId, currentEntry.fragment, currentEntry.fragmentTag);
transaction.commitAllowingStateLoss();
}
}

this._frameCreateTimeout = null;
}, 0);
}

private disposeCurrentFragment(): void {
if (!this._currentEntry || !this._currentEntry.fragment || !this._currentEntry.fragment.isAdded()) {
return;
Expand Down Expand Up @@ -378,11 +327,8 @@ export class Frame extends FrameBase {
this._processNextNavigationEntry();
}

// restore cached animation settings if we just completed simulated first navigation (no animation)
if (this._cachedTransitionState) {
_restoreTransitionState(this._cachedTransitionState);
this._cachedTransitionState = null;
}
// Restore cached animation settings if we just completed simulated first navigation (no animation)
this._restoreTransitionState();

// restore original fragment transitions if we just completed replace navigation (hmr)
if (navigationType === NavigationType.replace) {
Expand Down Expand Up @@ -463,7 +409,7 @@ export class Frame extends FrameBase {
// layout pass so we will wait forever for transitionCompleted handler...
// https://github.com/NativeScript/NativeScript/issues/4895
let navigationTransition: NavigationTransition;
if (this._currentEntry) {
if (currentEntry) {
navigationTransition = this._getNavigationTransition(newEntry.entry);
} else {
navigationTransition = null;
Expand Down Expand Up @@ -562,13 +508,13 @@ export class Frame extends FrameBase {
const nativeView = this.nativeViewProtected as android.view.ViewGroup;
const listener = getAttachListener();

// 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;
nativeView.removeOnAttachStateChangeListener(listener);
nativeView[ownerSymbol] = null;

// There are cases like root view when detach listener is not called before the native view gets disposed
// so call detach method directly for these views
if (this._attachedToWindow) {
this._onDetachedFromWindow();
}

this._tearDownPending = !!this._executingContext;
Expand Down Expand Up @@ -628,6 +574,13 @@ export class Frame extends FrameBase {
}
}

public _restoreTransitionState(): void {
if (this._cachedTransitionState) {
_restoreTransitionState(this._cachedTransitionState);
this._cachedTransitionState = null;
}
}

public _saveFragmentsState(): void {
// We save only fragments in backstack.
// Current fragment is saved by FragmentManager.
Expand Down Expand Up @@ -655,12 +608,12 @@ let framesCounter = 0;

class AndroidFrame extends Observable implements AndroidFrameDefinition {
public rootViewGroup: android.view.ViewGroup;
public frameId;
public readonly frameId: number;

private _showActionBar = true;
private _owner: Frame;
private readonly _owner: FrameDefinition;

constructor(owner: Frame) {
constructor(owner: FrameDefinition) {
super();
this._owner = owner;
this.frameId = framesCounter++;
Expand Down Expand Up @@ -730,7 +683,7 @@ class AndroidFrame extends Observable implements AndroidFrameDefinition {
return undefined;
}

public get owner(): Frame {
public get owner(): FrameDefinition {
return this._owner;
}

Expand Down Expand Up @@ -1068,6 +1021,8 @@ export class ActivityCallbacksImplementation implements AndroidActivityCallbacks
const manager = this._rootView._getFragmentManager();
manager.executePendingTransactions();

// Some flavors reuse the same root view, so unload the view in order to load it successfully when needed
this._rootView.callUnloaded();
this._rootView._onRootViewReset();
}
// Delete previously cached root view in order to recreate it.
Expand Down
Loading
Loading