fix(android): rewrote the transition system

to allow it work correctly with GLSurfaceView and GLTextureView
This commit is contained in:
Martin Guillon
2021-02-20 21:28:09 +01:00
parent 41e6792aed
commit a4d7674aba
4 changed files with 64 additions and 65 deletions

View File

@ -11,6 +11,10 @@ const FragmentClass = (<any>org.nativescript.widgets.FragmentBase).extend('com.t
return this._callbacks.onCreateAnimator(this, transit, enter, nextAnim, superProto.onCreateAnimator);
},
onCreateAnimation(transit: number, enter: boolean, nextAnim: number): android.view.animation.Animation {
return this._callbacks.onCreateAnimation(this, transit, enter, nextAnim, superProto.onCreateAnimation);
},
onStop(): void {
this._callbacks.onStop(this, superProto.onStop);
},

View File

@ -8,6 +8,8 @@ import { FlipTransition } from '../transition/flip-transition';
import { _resolveAnimationCurve } from '../animation';
import lazy from '../../utils/lazy';
import { Trace } from '../../trace';
import { FadeTransition } from 'ui/transition/fade-transition.android';
import { SlideTransition } from 'ui/transition/slide-transition.android';
interface TransitionListener {
new (entry: ExpandedEntry, transition: androidx.transition.Transition): ExpandedTransitionListener;
@ -66,43 +68,39 @@ export function _setAndroidFragmentTransitions(animated: boolean, navigationTran
allowTransitionOverlap(newFragment);
let name = '';
let transition: Transition;
let customTransition: Transition;
if (navigationTransition) {
transition = navigationTransition.instance;
customTransition = navigationTransition.instance;
name = navigationTransition.name ? navigationTransition.name.toLowerCase() : '';
}
if (!animated) {
name = 'none';
} else if (transition) {
} else if (customTransition) {
name = 'custom';
} else if (name.indexOf('slide') !== 0 && name !== 'fade' && name.indexOf('flip') !== 0 && name.indexOf('explode') !== 0) {
} else if (name.indexOf('slide') !== 0 && name !== 'fade' && name.indexOf('flip') !== 0) {
// If we are given name that doesn't match any of ours - fallback to default.
name = 'default';
}
let currentFragmentNeedsDifferentAnimation = false;
if (currentEntry) {
_updateTransitions(currentEntry);
if (currentEntry.transitionName !== name || currentEntry.transition !== transition || isNestedDefaultTransition) {
if (currentEntry.transitionName !== name || currentEntry.transition !== customTransition || isNestedDefaultTransition) {
clearExitAndReenterTransitions(currentEntry, true);
currentFragmentNeedsDifferentAnimation = true;
}
}
let transition: Transition;
if (name === 'none') {
const noTransition = new NoTransition(0, null);
// Setup empty/immediate animator when transitioning to nested frame for first time.
// Also setup empty/immediate transition to be executed when navigating back to this page.
// TODO: Consider removing empty/immediate animator when migrating to official androidx.fragment.app.Fragment:1.2.
if (isNestedDefaultTransition) {
fragmentTransaction.setCustomAnimations(animFadeIn, animFadeOut);
setupAllAnimation(newEntry, noTransition);
setupNewFragmentCustomTransition({ duration: 0, curve: null }, newEntry, noTransition);
} else {
setupNewFragmentCustomTransition({ duration: 0, curve: null }, newEntry, noTransition);
// with androidx.fragment 1.3.0 the first fragment animation is not triggered
// so let's simulate a transition end
setTimeout(() => {
addToWaitingQueue(newEntry);
transitionOrAnimationCompleted(newEntry, null);
});
}
newEntry.isNestedDefaultTransition = isNestedDefaultTransition;
@ -110,52 +108,40 @@ export function _setAndroidFragmentTransitions(animated: boolean, navigationTran
if (currentFragmentNeedsDifferentAnimation) {
setupCurrentFragmentCustomTransition({ duration: 0, curve: null }, currentEntry, noTransition);
}
} else if (name === 'custom') {
} else if (customTransition) {
setupNewFragmentCustomTransition(
{
duration: transition.getDuration(),
curve: transition.getCurve(),
duration: customTransition.getDuration(),
curve: customTransition.getCurve(),
},
newEntry,
transition
customTransition
);
if (currentFragmentNeedsDifferentAnimation) {
setupCurrentFragmentCustomTransition(
{
duration: transition.getDuration(),
curve: transition.getCurve(),
duration: customTransition.getDuration(),
curve: customTransition.getCurve(),
},
currentEntry,
transition
customTransition
);
}
} else if (name === 'default') {
setupNewFragmentFadeTransition({ duration: 150, curve: null }, newEntry);
if (currentFragmentNeedsDifferentAnimation) {
setupCurrentFragmentFadeTransition({ duration: 150, curve: null }, currentEntry);
}
transition = new FadeTransition(150, null);
} else if (name.indexOf('slide') === 0) {
setupNewFragmentSlideTransition(navigationTransition, newEntry, name);
if (currentFragmentNeedsDifferentAnimation) {
setupCurrentFragmentSlideTransition(navigationTransition, currentEntry, name);
}
const direction = name.substr('slide'.length) || 'left'; //Extract the direction from the string
transition = new SlideTransition(direction, navigationTransition.duration, navigationTransition.curve);
} else if (name === 'fade') {
setupNewFragmentFadeTransition(navigationTransition, newEntry);
if (currentFragmentNeedsDifferentAnimation) {
setupCurrentFragmentFadeTransition(navigationTransition, currentEntry);
}
} else if (name === 'explode') {
setupNewFragmentExplodeTransition(navigationTransition, newEntry);
if (currentFragmentNeedsDifferentAnimation) {
setupCurrentFragmentExplodeTransition(navigationTransition, currentEntry);
}
transition = new FadeTransition(navigationTransition.duration, navigationTransition.curve);
} else if (name.indexOf('flip') === 0) {
const direction = name.substr('flip'.length) || 'right'; //Extract the direction from the string
const flipTransition = new FlipTransition(direction, navigationTransition.duration, navigationTransition.curve);
setupNewFragmentCustomTransition(navigationTransition, newEntry, flipTransition);
transition = new FlipTransition(direction, navigationTransition.duration, navigationTransition.curve);
}
if (transition) {
setupNewFragmentCustomTransition(navigationTransition, newEntry, transition);
if (currentFragmentNeedsDifferentAnimation) {
setupCurrentFragmentCustomTransition(navigationTransition, currentEntry, flipTransition);
setupCurrentFragmentCustomTransition(navigationTransition, currentEntry, transition);
}
}

View File

@ -88,6 +88,7 @@ export interface AndroidActivityCallbacks {
export interface AndroidFragmentCallbacks {
onHiddenChanged(fragment: any, hidden: boolean, superFunc: Function): void;
onCreateAnimator(fragment: any, transit: number, enter: boolean, nextAnim: number, superFunc: Function): any;
onCreateAnimation(fragment: any, transit: number, enter: boolean, nextAnim: number): any;
onCreate(fragment: any, savedInstanceState: any, superFunc: Function): void;
onCreateView(fragment: any, inflater: any, container: any, savedInstanceState: any, superFunc: Function): any;
onSaveInstanceState(fragment: any, outState: any, superFunc: Function): void;

View File

@ -476,10 +476,10 @@ export class Frame extends FrameBase {
_reverseTransitions(backstackEntry, this._currentEntry);
const currentIndex =this.backStack.length;
const currentIndex = this.backStack.length;
const goBackToIndex = this.backStack.indexOf(backstackEntry);
// the order is important so that the transition listener called be
// the order is important so that the transition listener called be
// the one from the current entry we are going back from
if (this._currentEntry !== backstackEntry) {
const entry = this._currentEntry as ExpandedEntry;
@ -489,8 +489,9 @@ export class Frame extends FrameBase {
if (entry.returnTransitionListener) {
entry.returnTransitionListener.backEntry = backstackEntry;
}
transaction.remove((this._currentEntry).fragment);
// we only did hide the fragment to fix some black blick issues with GLSurfaceView and GLTextureView
transaction.hide(this._currentEntry.fragment);
}
for (let index = goBackToIndex + 1; index < currentIndex; index++) {
transaction.remove(this.backStack[index].fragment);
@ -501,8 +502,13 @@ export class Frame extends FrameBase {
public _removeEntry(removed: BackstackEntry): void {
super._removeEntry(removed);
if (removed.fragment) {
// we only did hide the fragment to fix some black blick issues with GLSurfaceView and GLTextureView
// so lets remove it
const manager: androidx.fragment.app.FragmentManager = this._getFragmentManager();
const transaction = manager.beginTransaction();
transaction.remove(removed.fragment);
transaction.commitAllowingStateLoss();
_clearEntry(removed);
}
@ -788,11 +794,11 @@ function findPageForFragment(fragment: androidx.fragment.app.Fragment, frame: Fr
} else if (executingContext && executingContext.entry && executingContext.entry.fragmentTag === fragmentTag) {
entry = executingContext.entry;
} else {
frame.backStack.forEach(e=>{
frame.backStack.forEach((e) => {
if (e && e.fragmentTag === fragmentTag) {
entry = e;
}
})
});
}
let page: Page;
@ -873,15 +879,24 @@ class FragmentCallbacksImplementation implements AndroidFragmentCallbacks {
public onCreateAnimator(fragment: androidx.fragment.app.Fragment, transit: number, enter: boolean, nextAnim: number, superFunc: Function): android.animation.Animator {
let animator = null;
const entry = <any>this.entry;
if (Trace.isEnabled()) {
Trace.write(`${fragment}.onCreateAnimator(${transit},${enter}, ${nextAnim})`, Trace.categories.Animation);
}
// Return enterAnimator only when new (no current entry) nested transition.
if (enter && entry.isNestedDefaultTransition) {
animator = entry.enterAnimator;
entry.isNestedDefaultTransition = false;
}
return animator || superFunc.call(fragment, transit, enter, nextAnim);
}
@profile
public onCreateAnimation(fragment: androidx.fragment.app.Fragment, transit: number, enter: boolean, nextAnim: number, superFunc: Function): globalAndroid.view.animation.Animation {
if (Trace.isEnabled()) {
Trace.write(`${fragment}.onCreateAnimation(${transit},${enter}, ${nextAnim})`, Trace.categories.Animation);
}
return superFunc.call(fragment, transit, enter, nextAnim);
}
@profile
public onCreate(fragment: androidx.fragment.app.Fragment, savedInstanceState: android.os.Bundle, superFunc: Function): void {
@ -988,11 +1003,11 @@ class FragmentCallbacksImplementation implements AndroidFragmentCallbacks {
@profile
public onDestroyView(fragment: org.nativescript.widgets.FragmentBase, superFunc: Function): void {
if (Trace.isEnabled()) {
Trace.write(`${fragment}.onDestroyView()`, Trace.categories.NativeLifecycle);
}
superFunc.call(fragment);
if (Trace.isEnabled()) {
Trace.write(`${fragment}.onDestroyView()`, Trace.categories.NativeLifecycle);
}
superFunc.call(fragment);
}
@profile
public onDestroy(fragment: androidx.fragment.app.Fragment, superFunc: Function): void {
@ -1014,18 +1029,11 @@ class FragmentCallbacksImplementation implements AndroidFragmentCallbacks {
// "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) {
Trace.error(`${fragment}.onDestroy: entry has no resolvedPage`);
return null;
}
}
@profile
public onPause(fragment: org.nativescript.widgets.FragmentBase, superFunc: Function): void {
superFunc.call(fragment);
superFunc.call(fragment);
}
@profile