diff --git a/packages/core/ui/frame/fragment.transitions.android.ts b/packages/core/ui/frame/fragment.transitions.android.ts index 434c69ce85..1ce888d22d 100644 --- a/packages/core/ui/frame/fragment.transitions.android.ts +++ b/packages/core/ui/frame/fragment.transitions.android.ts @@ -23,17 +23,87 @@ const animFadeOut = 17432577; // android.R.anim.fade_out export const waitingQueue = new Map>(); export const completedEntries = new Map(); -let TransitionListener: TransitionListener; +let TransitionListener = lazy(() => { + @NativeClass + @Interfaces([(androidx).transition.Transition.TransitionListener]) + class TransitionListenerImpl extends java.lang.Object implements androidx.transition.Transition.TransitionListener { + public entry: WeakRef; + constructor( + entry: ExpandedEntry, + public transition: androidx.transition.Transition, + ) { + super(); + this.entry = new WeakRef(entry); + + return global.__native(this); + } + + public onTransitionStart(transition: androidx.transition.Transition): void { + const entry = this.entry.deref(); + if (!entry) { + return; + } + entry.isAnimationRunning = true; + addToWaitingQueue(entry); + if (Trace.isEnabled()) { + Trace.write(`START ${toShortString(transition)} transition for ${entry.fragmentTag}`, Trace.categories.Transition); + } + if (entry?.transition) { + notifySharedTransition(entry.transition?.id, SharedTransition.startedEvent); + } + } + + onTransitionEnd(transition: androidx.transition.Transition): void { + const entry = this.entry.deref(); + if (!entry) { + return; + } + if (Trace.isEnabled()) { + Trace.write(`END ${toShortString(transition)} transition for ${entry.fragmentTag}`, Trace.categories.Transition); + } + entry.isAnimationRunning = false; + transitionOrAnimationCompleted(entry, undefined); + if (entry?.transition) { + notifySharedTransition(entry.transition?.id, SharedTransition.finishedEvent); + } + } + + onTransitionResume(transition: androidx.transition.Transition): void { + if (Trace.isEnabled()) { + const fragment = this.entry.deref()?.fragmentTag; + Trace.write(`RESUME ${toShortString(transition)} transition for ${fragment}`, Trace.categories.Transition); + } + } + + onTransitionPause(transition: androidx.transition.Transition): void { + if (Trace.isEnabled()) { + Trace.write(`PAUSE ${toShortString(transition)} transition for ${this.entry.deref()?.fragmentTag}`, Trace.categories.Transition); + } + } + + onTransitionCancel(transition: androidx.transition.Transition): void { + const entry = this.entry.deref(); + if (!entry) { + return; + } + entry.isAnimationRunning = false; + if (Trace.isEnabled()) { + Trace.write(`CANCEL ${toShortString(transition)} transition for ${entry.fragmentTag}`, Trace.categories.Transition); + } + } + } + return TransitionListenerImpl; +}); let AnimationListener: android.animation.Animator.AnimatorListener; interface ExpandedTransitionListener extends androidx.transition.Transition.TransitionListener { - entry: ExpandedEntry; + entry: WeakRef; transition: androidx.transition.Transition; } interface ExpandedAnimator extends android.animation.Animator { - entry: ExpandedEntry; - backEntry?: BackstackEntry; + entry: WeakRef; + backEntry?: WeakRef; transitionType?: string; } @@ -187,13 +257,13 @@ function setupAllAnimation(entry: ExpandedEntry, transition: Transition): void { // need to clearAnimationListener for enter & popExit animators. const enterAnimator = transition.createAndroidAnimator(Transition.AndroidTransitionType.enter); enterAnimator.transitionType = Transition.AndroidTransitionType.enter; - enterAnimator.entry = entry; + enterAnimator.entry = new WeakRef(entry); enterAnimator.addListener(listener); entry.enterAnimator = enterAnimator; const popExitAnimator = transition.createAndroidAnimator(Transition.AndroidTransitionType.popExit); popExitAnimator.transitionType = Transition.AndroidTransitionType.popExit; - popExitAnimator.entry = entry; + popExitAnimator.entry = new WeakRef(entry); popExitAnimator.addListener(listener); entry.popExitAnimator = popExitAnimator; } @@ -207,13 +277,13 @@ function setupExitAndPopEnterAnimation(entry: ExpandedEntry, transition: Transit const exitAnimator = transition.createAndroidAnimator(Transition.AndroidTransitionType.exit); exitAnimator.transitionType = Transition.AndroidTransitionType.exit; - exitAnimator.entry = entry; + exitAnimator.entry = new WeakRef(entry); exitAnimator.addListener(listener); entry.exitAnimator = exitAnimator; const popEnterAnimator = transition.createAndroidAnimator(Transition.AndroidTransitionType.popEnter); popEnterAnimator.transitionType = Transition.AndroidTransitionType.popEnter; - popEnterAnimator.entry = entry; + popEnterAnimator.entry = new WeakRef(entry); popEnterAnimator.addListener(listener); entry.popEnterAnimator = popEnterAnimator; } @@ -230,33 +300,44 @@ function getAnimationListener(): android.animation.Animator.AnimatorListener { } onAnimationStart(animator: ExpandedAnimator): void { - const entry = animator.entry; - addToWaitingQueue(entry); + const entry = animator.entry.deref(); if (Trace.isEnabled()) { - Trace.write(`START ${animator.transitionType} for ${entry.fragmentTag}`, Trace.categories.Transition); + Trace.write(`START ${animator.transitionType} for ${entry?.fragmentTag}`, Trace.categories.Transition); + } + if (!entry) { + return; } + addToWaitingQueue(entry); entry.isAnimationRunning = true; } onAnimationRepeat(animator: ExpandedAnimator): void { if (Trace.isEnabled()) { - Trace.write(`REPEAT ${animator.transitionType} for ${animator.entry.fragmentTag}`, Trace.categories.Transition); + Trace.write(`REPEAT ${animator.transitionType} for ${animator.entry.deref()?.fragmentTag}`, Trace.categories.Transition); } } onAnimationEnd(animator: ExpandedAnimator): void { if (Trace.isEnabled()) { - Trace.write(`END ${animator.transitionType} for ${animator.entry.fragmentTag}`, Trace.categories.Transition); + Trace.write(`END ${animator.transitionType} for ${animator.entry.deref()?.fragmentTag}`, Trace.categories.Transition); + } + const entry = animator.entry.deref(); + if (!entry) { + return; } - animator.entry.isAnimationRunning = false; - transitionOrAnimationCompleted(animator.entry, animator.backEntry); + entry.isAnimationRunning = false; + transitionOrAnimationCompleted(entry, animator.backEntry?.deref()); } onAnimationCancel(animator: ExpandedAnimator): void { if (Trace.isEnabled()) { - Trace.write(`CANCEL ${animator.transitionType} for ${animator.entry.fragmentTag}`, Trace.categories.Transition); + Trace.write(`CANCEL ${animator.transitionType} for ${animator.entry.deref()?.fragmentTag}`, Trace.categories.Transition); + } + const entry = animator.entry.deref(); + if (!entry) { + return; } - animator.entry.isAnimationRunning = false; + entry.isAnimationRunning = false; } } @@ -273,8 +354,8 @@ function clearAnimationListener(animator: ExpandedAnimator, listener: android.an animator.removeListener(listener); - if (animator.entry && Trace.isEnabled()) { - const entry = animator.entry; + if (animator.entry?.deref() && Trace.isEnabled()) { + const entry = animator.entry.deref(); Trace.write(`Clear ${animator.transitionType} - ${entry.transition} for ${entry.fragmentTag}`, Trace.categories.Transition); } @@ -361,70 +442,7 @@ function notifySharedTransition(id: number, eventName: string) { // Transition listener can't be static because // android is cloning transitions and we can't expand them :( function getTransitionListener(entry: ExpandedEntry, transition: androidx.transition.Transition): ExpandedTransitionListener { - if (!TransitionListener) { - @NativeClass - @Interfaces([(androidx).transition.Transition.TransitionListener]) - class TransitionListenerImpl extends java.lang.Object implements androidx.transition.Transition.TransitionListener { - public backEntry?: BackstackEntry; - constructor( - public entry: ExpandedEntry, - public transition: androidx.transition.Transition, - ) { - super(); - - return global.__native(this); - } - - public onTransitionStart(transition: androidx.transition.Transition): void { - const entry = this.entry; - entry.isAnimationRunning = true; - addToWaitingQueue(entry); - if (Trace.isEnabled()) { - Trace.write(`START ${toShortString(transition)} transition for ${entry.fragmentTag}`, Trace.categories.Transition); - } - if (entry?.transition) { - notifySharedTransition(entry.transition?.id, SharedTransition.startedEvent); - } - } - - onTransitionEnd(transition: androidx.transition.Transition): void { - const entry = this.entry; - if (Trace.isEnabled()) { - Trace.write(`END ${toShortString(transition)} transition for ${entry.fragmentTag}`, Trace.categories.Transition); - } - entry.isAnimationRunning = false; - transitionOrAnimationCompleted(entry, this.backEntry); - if (entry?.transition) { - notifySharedTransition(entry.transition?.id, SharedTransition.finishedEvent); - } - } - - onTransitionResume(transition: androidx.transition.Transition): void { - if (Trace.isEnabled()) { - const fragment = this.entry.fragmentTag; - Trace.write(`RESUME ${toShortString(transition)} transition for ${fragment}`, Trace.categories.Transition); - } - } - - onTransitionPause(transition: androidx.transition.Transition): void { - if (Trace.isEnabled()) { - Trace.write(`PAUSE ${toShortString(transition)} transition for ${this.entry.fragmentTag}`, Trace.categories.Transition); - } - } - - onTransitionCancel(transition: androidx.transition.Transition): void { - const entry = this.entry; - entry.isAnimationRunning = false; - if (Trace.isEnabled()) { - Trace.write(`CANCEL ${toShortString(transition)} transition for ${this.entry.fragmentTag}`, Trace.categories.Transition); - } - } - } - - TransitionListener = TransitionListenerImpl; - } - - return new TransitionListener(entry, transition); + return new (TransitionListener())(entry, transition); } function addToWaitingQueue(entry: ExpandedEntry): void { @@ -445,7 +463,7 @@ function cloneExpandedTransitionListener(expandedTransitionListener: ExpandedTra const cloneTransition = expandedTransitionListener.transition.clone(); - return addNativeTransitionListener(expandedTransitionListener.entry, cloneTransition); + return addNativeTransitionListener(expandedTransitionListener.entry.deref(), cloneTransition); } function clearExitAndReenterTransitions(entry: ExpandedEntry, removeListener: boolean): void {