1 package org.robolectric.shadows; 2 3 import static android.os.Build.VERSION_CODES.N; 4 import static org.robolectric.util.reflector.Reflector.reflector; 5 6 import android.animation.AnimationHandler; 7 import android.animation.ValueAnimator; 8 import android.app.UiAutomation; 9 import org.robolectric.RuntimeEnvironment; 10 import org.robolectric.annotation.Implementation; 11 import org.robolectric.annotation.Implements; 12 import org.robolectric.annotation.RealObject; 13 import org.robolectric.annotation.Resetter; 14 import org.robolectric.util.ReflectionHelpers; 15 import org.robolectric.util.reflector.Accessor; 16 import org.robolectric.util.reflector.Direct; 17 import org.robolectric.util.reflector.ForType; 18 import org.robolectric.util.reflector.Static; 19 20 @Implements(ValueAnimator.class) 21 public class ShadowValueAnimator { 22 23 @RealObject private ValueAnimator realObject; 24 25 private int actualRepeatCount; 26 27 @Resetter reset()28 public static void reset() { 29 /* ValueAnimator.sAnimationHandler is a static thread local that otherwise would survive between 30 * tests. The AnimationHandler.mAnimationScheduled is set to true when the scheduleAnimation() is 31 * called and the reset to false when run() is called by the Choreographer. If an animation is 32 * already scheduled, it will not post to the Choreographer. This is a problem if a previous 33 * test leaves animations on the Choreographers callback queue without running them as it will 34 * cause the AnimationHandler not to post a callback. We reset the thread local here so a new 35 * one will be created for each test with a fresh state. 36 */ 37 if (RuntimeEnvironment.getApiLevel() >= N) { 38 ThreadLocal<AnimationHandler> animatorHandlerTL = 39 ReflectionHelpers.getStaticField(AnimationHandler.class, "sAnimatorHandler"); 40 animatorHandlerTL.remove(); 41 } else { 42 ReflectionHelpers.callStaticMethod(ValueAnimator.class, "clearAllAnimations"); 43 ThreadLocal<AnimationHandler> animatorHandlerTL = 44 ReflectionHelpers.getStaticField(ValueAnimator.class, "sAnimationHandler"); 45 animatorHandlerTL.remove(); 46 } 47 48 setDurationScale(1); 49 } 50 51 @Implementation setRepeatCount(int count)52 protected void setRepeatCount(int count) { 53 actualRepeatCount = count; 54 if (count == ValueAnimator.INFINITE) { 55 count = 1; 56 } 57 reflector(ValueAnimatorReflector.class, realObject).setRepeatCount(count); 58 } 59 60 /** 61 * Returns the value that was set as the repeat count. This is otherwise the same as 62 * getRepeatCount(), except when the count was set to infinite. 63 * 64 * @return Repeat count. 65 */ getActualRepeatCount()66 public int getActualRepeatCount() { 67 return actualRepeatCount; 68 } 69 70 /** 71 * Sets the duration scale for value animator. To set this value use {@link 72 * UiAutomation#setAnimationScale(float)} or {@link 73 * ShadowUiAutomation#setAnimationScaleCompat(float)}. 74 */ 75 @Implementation setDurationScale(float duration)76 protected static void setDurationScale(float duration) { 77 reflector(ValueAnimatorReflector.class, null).setDurationScale(duration); 78 } 79 80 @ForType(ValueAnimator.class) 81 interface ValueAnimatorReflector { 82 83 @Direct setRepeatCount(int count)84 void setRepeatCount(int count); 85 86 @Static 87 @Accessor("sDurationScale") setDurationScale(float duration)88 void setDurationScale(float duration); 89 } 90 } 91