From cc19b400b96ea8d374da193d588d601a01facde2 Mon Sep 17 00:00:00 2001 From: Manol Donev Date: Thu, 2 Aug 2018 16:01:52 +0300 Subject: [PATCH] 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) --- .../ui/frame/fragment.transitions.android.ts | 76 +------------------ .../ui/frame/fragment.transitions.d.ts | 1 - tns-core-modules/ui/frame/frame.android.ts | 5 +- 3 files changed, 3 insertions(+), 79 deletions(-) diff --git a/tns-core-modules/ui/frame/fragment.transitions.android.ts b/tns-core-modules/ui/frame/fragment.transitions.android.ts index 74eee202b..df2792234 100644 --- a/tns-core-modules/ui/frame/fragment.transitions.android.ts +++ b/tns-core-modules/ui/frame/fragment.transitions.android.ts @@ -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>(); export const completedEntries = new Map(); 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[]) { - 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); - } - } -} diff --git a/tns-core-modules/ui/frame/fragment.transitions.d.ts b/tns-core-modules/ui/frame/fragment.transitions.d.ts index f94e9fcc5..bf864da7c 100644 --- a/tns-core-modules/ui/frame/fragment.transitions.d.ts +++ b/tns-core-modules/ui/frame/fragment.transitions.d.ts @@ -24,7 +24,6 @@ export function _setAndroidFragmentTransitions( currentEntry: BackstackEntry, newEntry: BackstackEntry, fragmentTransaction: any, - manager: any /* android.support.v4.app.FragmentManager */, frameId: number): void; /** * @private diff --git a/tns-core-modules/ui/frame/frame.android.ts b/tns-core-modules/ui/frame/frame.android.ts index b3d080eb9..ab83e1b1e 100644 --- a/tns-core-modules/ui/frame/frame.android.ts +++ b/tns-core-modules/ui/frame/frame.android.ts @@ -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);