diff --git a/packages/core/ui/frame/fragment.android.ts b/packages/core/ui/frame/fragment.android.ts index a7233f115..2a01119f1 100644 --- a/packages/core/ui/frame/fragment.android.ts +++ b/packages/core/ui/frame/fragment.android.ts @@ -19,6 +19,10 @@ const FragmentClass = (org.nativescript.widgets.FragmentBase).extend('com.t this._callbacks.onPause(this, superProto.onPause); }, + onResume(): void { + this._callbacks.onResume(this, superProto.onResume); + }, + onCreate(savedInstanceState: android.os.Bundle) { if (!this._callbacks) { setFragmentCallbacks(this); diff --git a/packages/core/ui/frame/fragment.transitions.android.ts b/packages/core/ui/frame/fragment.transitions.android.ts index 6fdc9bb13..638c3297e 100644 --- a/packages/core/ui/frame/fragment.transitions.android.ts +++ b/packages/core/ui/frame/fragment.transitions.android.ts @@ -51,6 +51,7 @@ export interface ExpandedEntry extends BackstackEntry { frameId: number; isNestedDefaultTransition: boolean; + isAnimationRunning: boolean; } export function _setAndroidFragmentTransitions(animated: boolean, navigationTransition: NavigationTransition, currentEntry: ExpandedEntry, newEntry: ExpandedEntry, frameId: number, fragmentTransaction: androidx.fragment.app.FragmentTransaction, isNestedDefaultTransition?: boolean): void { @@ -60,6 +61,7 @@ export function _setAndroidFragmentTransitions(animated: boolean, navigationTran if (entries && entries.size > 0) { throw new Error('Calling navigation before previous navigation finish.'); } + newEntry.isAnimationRunning = false; allowTransitionOverlap(currentFragment); allowTransitionOverlap(newFragment); @@ -227,6 +229,7 @@ function getAnimationListener(): android.animation.Animator.AnimatorListener { if (Trace.isEnabled()) { Trace.write(`START ${animator.transitionType} for ${entry.fragmentTag}`, Trace.categories.Transition); } + entry.isAnimationRunning = true; } onAnimationRepeat(animator: ExpandedAnimator): void { @@ -239,6 +242,7 @@ function getAnimationListener(): android.animation.Animator.AnimatorListener { if (Trace.isEnabled()) { Trace.write(`END ${animator.transitionType} for ${animator.entry.fragmentTag}`, Trace.categories.Transition); } + animator.entry.isAnimationRunning = false; transitionOrAnimationCompleted(animator.entry, animator.backEntry); } @@ -246,6 +250,7 @@ function getAnimationListener(): android.animation.Animator.AnimatorListener { if (Trace.isEnabled()) { Trace.write(`CANCEL ${animator.transitionType} for ${animator.entry.fragmentTag}`, Trace.categories.Transition); } + animator.entry.isAnimationRunning = false; } } diff --git a/packages/core/ui/frame/index.android.ts b/packages/core/ui/frame/index.android.ts index 177d2b4c5..aff09c4d1 100644 --- a/packages/core/ui/frame/index.android.ts +++ b/packages/core/ui/frame/index.android.ts @@ -237,6 +237,21 @@ export class Frame extends FrameBase { this.backgroundColor = this._originalBackground; this._originalBackground = null; } + 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(); + } + } + }, 0); super.onLoaded(); } @@ -1002,6 +1017,19 @@ class FragmentCallbacksImplementation implements AndroidFragmentCallbacks { } } + @profile + public onResume(fragment: org.nativescript.widgets.FragmentBase, superFunc: Function): void { + const frame = this.entry.resolvedPage.frame; + // on some cases during the first navigation on nested frames the animation doesn't trigger + // we depend on the animation (even None animation) to set the entry as the current entry + // animation should start between start and resume, so if we have an executing navigation here it probably means the animation was skipped + // so we manually set the entry + if (frame._executingContext && !(this.entry).isAnimationRunning) { + frame.setCurrent(this.entry, frame._executingContext.navigationType); + } + superFunc.call(fragment); + } + @profile public onStop(fragment: androidx.fragment.app.Fragment, superFunc: Function): void { superFunc.call(fragment); diff --git a/packages/core/ui/frame/index.d.ts b/packages/core/ui/frame/index.d.ts index 4b13d6926..e541f4040 100644 --- a/packages/core/ui/frame/index.d.ts +++ b/packages/core/ui/frame/index.d.ts @@ -476,6 +476,7 @@ export interface AndroidFragmentCallbacks { onDestroyView(fragment: any, superFunc: Function): void; onDestroy(fragment: any, superFunc: Function): void; onPause(fragment: any, superFunc: Function): void; + onResume(fragment: any, superFunc: Function): void; onStop(fragment: any, superFunc: Function): void; toStringOverride(fragment: any, superFunc: Function): string; }