mirror of
https://github.com/NativeScript/NativeScript.git
synced 2025-08-17 21:01:34 +08:00
fix(android): suppress reflection for default animations (#6141)
Fixes `Error: java.lang.CloneNotSupportedException: Class android.support.v4.app.FragmentManagerImpl$AnimationOrAnimator doesn't implement Cloneable` in specific projects. Related to #5785 Related to #6129 BREAKING CHANGE Before: Default fragment enter animation was Android version specific After: Default fragment enter animation is now fade animation for all Android versions You can customise the transition per navigation entry or globally via the [navigation transitions API]( https://docs.nativescript.org/core-concepts/navigation#navigation-transitions)
This commit is contained in:
@ -50,17 +50,12 @@ interface ExpandedEntry extends BackstackEntry {
|
||||
|
||||
const sdkVersion = lazy(() => parseInt(device.sdkVersion));
|
||||
const defaultInterpolator = lazy(() => new android.view.animation.AccelerateDecelerateInterpolator());
|
||||
const isAndroidP = lazy(() => sdkVersion() > 27);
|
||||
|
||||
export const waitingQueue = new Map<number, Set<ExpandedEntry>>();
|
||||
export const completedEntries = new Map<number, ExpandedEntry>();
|
||||
|
||||
let TransitionListener: TransitionListener;
|
||||
let AnimationListener: android.view.animation.Animation.AnimationListener;
|
||||
let loadAnimationMethod: java.lang.reflect.Method;
|
||||
let reflectionDone: boolean;
|
||||
let defaultEnterAnimationStatic: android.view.animation.Animation;
|
||||
let defaultExitAnimationStatic: android.view.animation.Animation;
|
||||
|
||||
export function _setAndroidFragmentTransitions(
|
||||
animated: boolean,
|
||||
@ -68,7 +63,6 @@ export function _setAndroidFragmentTransitions(
|
||||
currentEntry: ExpandedEntry,
|
||||
newEntry: ExpandedEntry,
|
||||
fragmentTransaction: android.support.v4.app.FragmentTransaction,
|
||||
manager: android.support.v4.app.FragmentManager,
|
||||
frameId: number): void {
|
||||
|
||||
const currentFragment: android.support.v4.app.Fragment = currentEntry ? currentEntry.fragment : null;
|
||||
@ -78,10 +72,6 @@ export function _setAndroidFragmentTransitions(
|
||||
throw new Error("Calling navigation before previous navigation finish.");
|
||||
}
|
||||
|
||||
if (!isAndroidP()) {
|
||||
initDefaultAnimations(manager);
|
||||
}
|
||||
|
||||
if (sdkVersion() >= 21) {
|
||||
allowTransitionOverlap(currentFragment);
|
||||
allowTransitionOverlap(newFragment);
|
||||
@ -120,11 +110,7 @@ export function _setAndroidFragmentTransitions(
|
||||
if (name === "none") {
|
||||
transition = new NoTransition(0, null);
|
||||
} else if (name === "default") {
|
||||
if (isAndroidP()) {
|
||||
transition = new FadeTransition(150, null);
|
||||
} else {
|
||||
transition = new DefaultTransition(0, null);
|
||||
}
|
||||
transition = new FadeTransition(150, null);
|
||||
} else if (useLollipopTransition) {
|
||||
// setEnterTransition: Enter
|
||||
// setExitTransition: Exit
|
||||
@ -178,11 +164,7 @@ export function _setAndroidFragmentTransitions(
|
||||
}
|
||||
}
|
||||
|
||||
if (isAndroidP()) {
|
||||
setupDefaultAnimations(newEntry, new FadeTransition(150, null));
|
||||
} else {
|
||||
setupDefaultAnimations(newEntry, new DefaultTransition(0, null));
|
||||
}
|
||||
setupDefaultAnimations(newEntry, new FadeTransition(150, null));
|
||||
|
||||
printTransitions(currentEntry);
|
||||
printTransitions(newEntry);
|
||||
@ -739,46 +721,6 @@ function toShortString(nativeTransition: android.transition.Transition): string
|
||||
return `${nativeTransition.getClass().getSimpleName()}@${nativeTransition.hashCode().toString(16)}`;
|
||||
}
|
||||
|
||||
function javaObjectArray(...params: java.lang.Object[]) {
|
||||
const nativeArray = Array.create(java.lang.Object, params.length);
|
||||
params.forEach((value, i) => nativeArray[i] = value);
|
||||
return nativeArray;
|
||||
}
|
||||
|
||||
function javaClassArray(...params: java.lang.Class<any>[]) {
|
||||
const nativeArray = Array.create(java.lang.Class, params.length);
|
||||
params.forEach((value, i) => nativeArray[i] = value);
|
||||
return nativeArray;
|
||||
}
|
||||
|
||||
function initDefaultAnimations(manager: android.support.v4.app.FragmentManager): void {
|
||||
if (reflectionDone) {
|
||||
return;
|
||||
}
|
||||
|
||||
reflectionDone = true;
|
||||
|
||||
loadAnimationMethod = manager.getClass().getDeclaredMethod("loadAnimation", javaClassArray(android.support.v4.app.Fragment.class, java.lang.Integer.TYPE, java.lang.Boolean.TYPE, java.lang.Integer.TYPE));
|
||||
if (loadAnimationMethod != null) {
|
||||
loadAnimationMethod.setAccessible(true);
|
||||
|
||||
const fragment_open = java.lang.Integer.valueOf(android.support.v4.app.FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
|
||||
const zero = java.lang.Integer.valueOf(0);
|
||||
const fragment = new android.support.v4.app.Fragment();
|
||||
|
||||
// Get default enter transition.
|
||||
defaultEnterAnimationStatic = loadAnimationMethod.invoke(manager, javaObjectArray(fragment, fragment_open, java.lang.Boolean.TRUE, zero));
|
||||
|
||||
// Get default exit transition.
|
||||
defaultExitAnimationStatic = loadAnimationMethod.invoke(manager, javaObjectArray(fragment, fragment_open, java.lang.Boolean.FALSE, zero));
|
||||
}
|
||||
}
|
||||
|
||||
function getDefaultAnimation(enter: boolean): android.view.animation.Animation {
|
||||
const defaultAnimation = enter ? defaultEnterAnimationStatic : defaultExitAnimationStatic;
|
||||
return defaultAnimation ? defaultAnimation.clone() : null;
|
||||
}
|
||||
|
||||
function createDummyZeroDurationAnimation(): android.view.animation.Animation {
|
||||
// NOTE: returning the dummy AlphaAnimation directly does not work for some reason;
|
||||
// animationEnd is fired first, then some animationStart (but for a different animation?)
|
||||
@ -817,17 +759,3 @@ class NoTransition extends Transition {
|
||||
return createDummyZeroDurationAnimation();
|
||||
}
|
||||
}
|
||||
|
||||
class DefaultTransition extends Transition {
|
||||
public createAndroidAnimation(transitionType: string): android.view.animation.Animation {
|
||||
switch (transitionType) {
|
||||
case AndroidTransitionType.enter:
|
||||
case AndroidTransitionType.popEnter:
|
||||
return getDefaultAnimation(true);
|
||||
|
||||
case AndroidTransitionType.popExit:
|
||||
case AndroidTransitionType.exit:
|
||||
return getDefaultAnimation(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -24,7 +24,6 @@ export function _setAndroidFragmentTransitions(
|
||||
currentEntry: BackstackEntry,
|
||||
newEntry: BackstackEntry,
|
||||
fragmentTransaction: any,
|
||||
manager: any /* android.support.v4.app.FragmentManager */,
|
||||
frameId: number): void;
|
||||
/**
|
||||
* @private
|
||||
|
@ -337,10 +337,7 @@ export class Frame extends FrameBase {
|
||||
// https://github.com/NativeScript/NativeScript/issues/4895
|
||||
const navigationTransition = this._currentEntry ? this._getNavigationTransition(newEntry.entry) : null;
|
||||
|
||||
_setAndroidFragmentTransitions(animated, navigationTransition, currentEntry, newEntry, transaction, manager, this._android.frameId);
|
||||
// if (clearHistory) {
|
||||
// deleteEntries(this.backStack);
|
||||
// }
|
||||
_setAndroidFragmentTransitions(animated, navigationTransition, currentEntry, newEntry, transaction, this._android.frameId);
|
||||
|
||||
if (currentEntry && animated && !navigationTransition) {
|
||||
transaction.setTransition(android.support.v4.app.FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
|
||||
|
Reference in New Issue
Block a user