Skip to content

Commit cc19b40

Browse files
authored
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)
1 parent 7ebac7c commit cc19b40

File tree

3 files changed

+3
-79
lines changed

3 files changed

+3
-79
lines changed

tns-core-modules/ui/frame/fragment.transitions.android.ts

Lines changed: 2 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -50,25 +50,19 @@ interface ExpandedEntry extends BackstackEntry {
5050

5151
const sdkVersion = lazy(() => parseInt(device.sdkVersion));
5252
const defaultInterpolator = lazy(() => new android.view.animation.AccelerateDecelerateInterpolator());
53-
const isAndroidP = lazy(() => sdkVersion() > 27);
5453

5554
export const waitingQueue = new Map<number, Set<ExpandedEntry>>();
5655
export const completedEntries = new Map<number, ExpandedEntry>();
5756

5857
let TransitionListener: TransitionListener;
5958
let AnimationListener: android.view.animation.Animation.AnimationListener;
60-
let loadAnimationMethod: java.lang.reflect.Method;
61-
let reflectionDone: boolean;
62-
let defaultEnterAnimationStatic: android.view.animation.Animation;
63-
let defaultExitAnimationStatic: android.view.animation.Animation;
6459

6560
export function _setAndroidFragmentTransitions(
6661
animated: boolean,
6762
navigationTransition: NavigationTransition,
6863
currentEntry: ExpandedEntry,
6964
newEntry: ExpandedEntry,
7065
fragmentTransaction: android.support.v4.app.FragmentTransaction,
71-
manager: android.support.v4.app.FragmentManager,
7266
frameId: number): void {
7367

7468
const currentFragment: android.support.v4.app.Fragment = currentEntry ? currentEntry.fragment : null;
@@ -78,10 +72,6 @@ export function _setAndroidFragmentTransitions(
7872
throw new Error("Calling navigation before previous navigation finish.");
7973
}
8074

81-
if (!isAndroidP()) {
82-
initDefaultAnimations(manager);
83-
}
84-
8575
if (sdkVersion() >= 21) {
8676
allowTransitionOverlap(currentFragment);
8777
allowTransitionOverlap(newFragment);
@@ -120,11 +110,7 @@ export function _setAndroidFragmentTransitions(
120110
if (name === "none") {
121111
transition = new NoTransition(0, null);
122112
} else if (name === "default") {
123-
if (isAndroidP()) {
124-
transition = new FadeTransition(150, null);
125-
} else {
126-
transition = new DefaultTransition(0, null);
127-
}
113+
transition = new FadeTransition(150, null);
128114
} else if (useLollipopTransition) {
129115
// setEnterTransition: Enter
130116
// setExitTransition: Exit
@@ -178,11 +164,7 @@ export function _setAndroidFragmentTransitions(
178164
}
179165
}
180166

181-
if (isAndroidP()) {
182-
setupDefaultAnimations(newEntry, new FadeTransition(150, null));
183-
} else {
184-
setupDefaultAnimations(newEntry, new DefaultTransition(0, null));
185-
}
167+
setupDefaultAnimations(newEntry, new FadeTransition(150, null));
186168

187169
printTransitions(currentEntry);
188170
printTransitions(newEntry);
@@ -739,46 +721,6 @@ function toShortString(nativeTransition: android.transition.Transition): string
739721
return `${nativeTransition.getClass().getSimpleName()}@${nativeTransition.hashCode().toString(16)}`;
740722
}
741723

742-
function javaObjectArray(...params: java.lang.Object[]) {
743-
const nativeArray = Array.create(java.lang.Object, params.length);
744-
params.forEach((value, i) => nativeArray[i] = value);
745-
return nativeArray;
746-
}
747-
748-
function javaClassArray(...params: java.lang.Class<any>[]) {
749-
const nativeArray = Array.create(java.lang.Class, params.length);
750-
params.forEach((value, i) => nativeArray[i] = value);
751-
return nativeArray;
752-
}
753-
754-
function initDefaultAnimations(manager: android.support.v4.app.FragmentManager): void {
755-
if (reflectionDone) {
756-
return;
757-
}
758-
759-
reflectionDone = true;
760-
761-
loadAnimationMethod = manager.getClass().getDeclaredMethod("loadAnimation", javaClassArray(android.support.v4.app.Fragment.class, java.lang.Integer.TYPE, java.lang.Boolean.TYPE, java.lang.Integer.TYPE));
762-
if (loadAnimationMethod != null) {
763-
loadAnimationMethod.setAccessible(true);
764-
765-
const fragment_open = java.lang.Integer.valueOf(android.support.v4.app.FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
766-
const zero = java.lang.Integer.valueOf(0);
767-
const fragment = new android.support.v4.app.Fragment();
768-
769-
// Get default enter transition.
770-
defaultEnterAnimationStatic = loadAnimationMethod.invoke(manager, javaObjectArray(fragment, fragment_open, java.lang.Boolean.TRUE, zero));
771-
772-
// Get default exit transition.
773-
defaultExitAnimationStatic = loadAnimationMethod.invoke(manager, javaObjectArray(fragment, fragment_open, java.lang.Boolean.FALSE, zero));
774-
}
775-
}
776-
777-
function getDefaultAnimation(enter: boolean): android.view.animation.Animation {
778-
const defaultAnimation = enter ? defaultEnterAnimationStatic : defaultExitAnimationStatic;
779-
return defaultAnimation ? defaultAnimation.clone() : null;
780-
}
781-
782724
function createDummyZeroDurationAnimation(): android.view.animation.Animation {
783725
// NOTE: returning the dummy AlphaAnimation directly does not work for some reason;
784726
// animationEnd is fired first, then some animationStart (but for a different animation?)
@@ -817,17 +759,3 @@ class NoTransition extends Transition {
817759
return createDummyZeroDurationAnimation();
818760
}
819761
}
820-
821-
class DefaultTransition extends Transition {
822-
public createAndroidAnimation(transitionType: string): android.view.animation.Animation {
823-
switch (transitionType) {
824-
case AndroidTransitionType.enter:
825-
case AndroidTransitionType.popEnter:
826-
return getDefaultAnimation(true);
827-
828-
case AndroidTransitionType.popExit:
829-
case AndroidTransitionType.exit:
830-
return getDefaultAnimation(false);
831-
}
832-
}
833-
}

tns-core-modules/ui/frame/fragment.transitions.d.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ export function _setAndroidFragmentTransitions(
2424
currentEntry: BackstackEntry,
2525
newEntry: BackstackEntry,
2626
fragmentTransaction: any,
27-
manager: any /* android.support.v4.app.FragmentManager */,
2827
frameId: number): void;
2928
/**
3029
* @private

tns-core-modules/ui/frame/frame.android.ts

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -337,10 +337,7 @@ export class Frame extends FrameBase {
337337
// https://github.com/NativeScript/NativeScript/issues/4895
338338
const navigationTransition = this._currentEntry ? this._getNavigationTransition(newEntry.entry) : null;
339339

340-
_setAndroidFragmentTransitions(animated, navigationTransition, currentEntry, newEntry, transaction, manager, this._android.frameId);
341-
// if (clearHistory) {
342-
// deleteEntries(this.backStack);
343-
// }
340+
_setAndroidFragmentTransitions(animated, navigationTransition, currentEntry, newEntry, transaction, this._android.frameId);
344341

345342
if (currentEntry && animated && !navigationTransition) {
346343
transaction.setTransition(android.support.v4.app.FragmentTransaction.TRANSIT_FRAGMENT_OPEN);

0 commit comments

Comments
 (0)