1 /*
2  * Copyright (C) 2012 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package android.animation.cts;
17 
18 import static com.android.compatibility.common.util.CtsMockitoUtils.within;
19 
20 import static org.junit.Assert.assertEquals;
21 import static org.junit.Assert.assertFalse;
22 import static org.junit.Assert.assertNotNull;
23 import static org.junit.Assert.assertTrue;
24 import static org.mockito.Mockito.atLeast;
25 import static org.mockito.Mockito.mock;
26 import static org.mockito.Mockito.timeout;
27 import static org.mockito.Mockito.times;
28 import static org.mockito.Mockito.verify;
29 
30 import android.animation.Animator;
31 import android.animation.Animator.AnimatorListener;
32 import android.animation.AnimatorListenerAdapter;
33 import android.animation.ObjectAnimator;
34 import android.animation.PropertyValuesHolder;
35 import android.animation.TimeInterpolator;
36 import android.animation.TypeEvaluator;
37 import android.animation.ValueAnimator;
38 import android.animation.ValueAnimator.AnimatorUpdateListener;
39 import android.graphics.Color;
40 import android.graphics.PointF;
41 import android.os.SystemClock;
42 import android.util.Range;
43 import android.view.animation.AccelerateInterpolator;
44 import android.view.animation.LinearInterpolator;
45 
46 import androidx.test.InstrumentationRegistry;
47 import androidx.test.annotation.UiThreadTest;
48 import androidx.test.filters.LargeTest;
49 import androidx.test.rule.ActivityTestRule;
50 import androidx.test.runner.AndroidJUnit4;
51 
52 import com.android.compatibility.common.util.AdoptShellPermissionsRule;
53 
54 import org.junit.AfterClass;
55 import org.junit.Before;
56 import org.junit.BeforeClass;
57 import org.junit.Rule;
58 import org.junit.Test;
59 import org.junit.runner.RunWith;
60 
61 import java.util.concurrent.CountDownLatch;
62 import java.util.concurrent.TimeUnit;
63 
64 @LargeTest
65 @RunWith(AndroidJUnit4.class)
66 public class ValueAnimatorTest {
67     private static final float EPSILON = 0.0001f;
68     private static float sPreviousAnimatorScale = 1.0f;
69 
70     private AnimationActivity mActivity;
71     private ValueAnimator mValueAnimator;
72     private final long mDuration = 2000;
73 
74     @Rule(order = 0)
75     public AdoptShellPermissionsRule mAdoptShellPermissionsRule =
76             new AdoptShellPermissionsRule(
77                     androidx.test.platform.app.InstrumentationRegistry
78                             .getInstrumentation().getUiAutomation(),
79                     android.Manifest.permission.START_ACTIVITIES_FROM_SDK_SANDBOX);
80 
81     @Rule(order = 1)
82     public ActivityTestRule<AnimationActivity> mActivityRule =
83             new ActivityTestRule<>(AnimationActivity.class);
84 
85     @Before
setup()86     public void setup() {
87         InstrumentationRegistry.getInstrumentation().setInTouchMode(false);
88         mActivity = mActivityRule.getActivity();
89         mValueAnimator = mActivity.createAnimatorWithDuration(mDuration);
90     }
91 
92     @BeforeClass
beforeClass()93     public static void beforeClass() {
94         sPreviousAnimatorScale = ValueAnimator.getDurationScale();
95         ValueAnimator.setDurationScale(1.0f);
96     }
97 
98     @AfterClass
afterClass()99     public static void afterClass() {
100         ValueAnimator.setDurationScale(sPreviousAnimatorScale);
101     }
102 
103     @Test
testDuration()104     public void testDuration() throws Throwable {
105         ValueAnimator valueAnimatorLocal = mActivity.createAnimatorWithDuration(mDuration);
106         startAnimation(valueAnimatorLocal);
107         assertEquals(mDuration, valueAnimatorLocal.getDuration());
108     }
109 
110     @Test
testIsRunning()111     public void testIsRunning() throws Throwable {
112         assertFalse(mValueAnimator.isRunning());
113         startAnimation(mValueAnimator);
114         ValueAnimator valueAnimatorReturned = mActivity.view.bounceYAnimator;
115         assertTrue(valueAnimatorReturned.isRunning());
116     }
117 
118     @Test
testIsStarted()119     public void testIsStarted() throws Throwable {
120         assertFalse(mValueAnimator.isRunning());
121         assertFalse(mValueAnimator.isStarted());
122         long startDelay = 10000;
123         mValueAnimator.setStartDelay(startDelay);
124         startAnimation(mValueAnimator);
125         assertFalse(mValueAnimator.isRunning());
126         assertTrue(mValueAnimator.isStarted());
127     }
128 
129     @Test
testRepeatMode()130     public void testRepeatMode() throws Throwable {
131         ValueAnimator mValueAnimator = mActivity.createAnimatorWithRepeatMode(
132             ValueAnimator.RESTART);
133         startAnimation(mValueAnimator);
134         assertEquals(ValueAnimator.RESTART, mValueAnimator.getRepeatMode());
135     }
136 
137     @Test
testRepeatCount()138     public void testRepeatCount() throws Throwable {
139         int repeatCount = 2;
140         ValueAnimator mValueAnimator = mActivity.createAnimatorWithRepeatCount(repeatCount);
141         startAnimation(mValueAnimator);
142         assertEquals(repeatCount, mValueAnimator.getRepeatCount());
143     }
144 
145     @Test
testStartDelay()146     public void testStartDelay() {
147         long startDelay = 1000;
148         mValueAnimator.setStartDelay(startDelay);
149         assertEquals(startDelay, mValueAnimator.getStartDelay());
150     }
151 
152     /**
153      * Verify that an animator with start delay will have its listener's onAnimationStart(...)
154      * and onAnimationEnd(...) called at the right time.
155      */
156     @Test
testListenerCallbackWithStartDelay()157     public void testListenerCallbackWithStartDelay() throws Throwable {
158         final ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);
159         anim.setStartDelay(300);
160         anim.setDuration(300);
161         AnimatorListener listener = mock(AnimatorListenerAdapter.class);
162         anim.addListener(listener);
163         mActivityRule.runOnUiThread(() -> {
164             anim.start();
165         });
166 
167         verify(listener, timeout(450).times(1)).onAnimationStart(anim, false);
168         verify(listener, timeout(450).times(1)).onAnimationEnd(anim, false);
169     }
170 
171     @Test
testGetCurrentPlayTime()172     public void testGetCurrentPlayTime() throws Throwable {
173         startAnimation(mValueAnimator);
174         SystemClock.sleep(100);
175         long currentPlayTime = mValueAnimator.getCurrentPlayTime();
176         assertTrue(currentPlayTime  >  0);
177     }
178 
179     @Test
testSetCurrentPlayTime()180     public void testSetCurrentPlayTime() throws Throwable {
181         final ValueAnimator anim = ValueAnimator.ofFloat(0, 100).setDuration(mDuration);
182         final ValueAnimator delayedAnim = ValueAnimator.ofFloat(0, 100).setDuration(mDuration);
183         delayedAnim.setStartDelay(mDuration);
184         final long proposedCurrentPlayTime = mDuration / 2;
185         mActivityRule.runOnUiThread(() -> {
186             anim.setCurrentPlayTime(mDuration / 2);
187             long currentPlayTime = anim.getCurrentPlayTime();
188             float currentFraction = anim.getAnimatedFraction();
189             float currentValue = (Float) anim.getAnimatedValue();
190             assertEquals(proposedCurrentPlayTime, currentPlayTime);
191             assertEquals(.5f, currentFraction, EPSILON);
192             assertEquals(50, currentValue, EPSILON);
193 
194             delayedAnim.setCurrentPlayTime(mDuration / 2);
195             currentPlayTime = delayedAnim.getCurrentPlayTime();
196             currentFraction = delayedAnim.getAnimatedFraction();
197             currentValue = (Float) delayedAnim.getAnimatedValue();
198             assertEquals(proposedCurrentPlayTime, currentPlayTime);
199             assertEquals(.5f, currentFraction, EPSILON);
200             assertEquals(50, currentValue, EPSILON);
201         });
202         // Now make sure that it's still true a little later, to test that we're
203         // getting a result based on the seek time, not the wall clock time
204         SystemClock.sleep(100);
205         long currentPlayTime = anim.getCurrentPlayTime();
206         float currentFraction = anim.getAnimatedFraction();
207         float currentValue = (Float) anim.getAnimatedValue();
208         assertEquals(proposedCurrentPlayTime, currentPlayTime);
209         assertEquals(.5f, currentFraction, EPSILON);
210         assertEquals(50, currentValue, EPSILON);
211 
212         currentPlayTime = delayedAnim.getCurrentPlayTime();
213         currentFraction = delayedAnim.getAnimatedFraction();
214         currentValue = (Float) delayedAnim.getAnimatedValue();
215         assertEquals(proposedCurrentPlayTime, currentPlayTime);
216         assertEquals(.5f, currentFraction, EPSILON);
217         assertEquals(50, currentValue, EPSILON);
218 
219         // Finally, start() the delayed animation and check that the play time was
220         // not affected by playing during the delay
221         mActivityRule.runOnUiThread(() -> {
222             delayedAnim.start();
223             assertEquals(proposedCurrentPlayTime, delayedAnim.getCurrentPlayTime());
224             assertEquals(.5f, delayedAnim.getAnimatedFraction(), EPSILON);
225             assertEquals(50, (float) delayedAnim.getAnimatedValue(), EPSILON);
226         });
227 
228         SystemClock.sleep(100);
229         currentPlayTime = delayedAnim.getCurrentPlayTime();
230         currentFraction = delayedAnim.getAnimatedFraction();
231         currentValue = (Float) delayedAnim.getAnimatedValue();
232         assertTrue(currentPlayTime > proposedCurrentPlayTime);
233         assertTrue(currentFraction > 0.5f);
234         assertTrue(currentValue > 50);
235 
236         mActivityRule.runOnUiThread(delayedAnim::cancel);
237     }
238 
239     @Test
testPauseListener()240     public void testPauseListener() throws Throwable {
241         // Adds two pause listeners to the animator, and remove one after the animator is paused.
242         Animator.AnimatorPauseListener l1 = mock(Animator.AnimatorPauseListener.class);
243         Animator.AnimatorPauseListener l2 = mock(Animator.AnimatorPauseListener.class);
244         ValueAnimator a1 = ValueAnimator.ofFloat(0, 1f);
245         a1.addPauseListener(l1);
246         a1.addPauseListener(l2);
247         mActivityRule.runOnUiThread(() -> {
248             a1.start();
249             a1.pause();
250             verify(l1, times(1)).onAnimationPause(a1);
251             verify(l2, times(1)).onAnimationPause(a1);
252             a1.removePauseListener(l2);
253             a1.resume();
254         });
255 
256         // Check that the pause listener that is removed doesn't have resume called.
257         verify(l1, times(1)).onAnimationResume(a1);
258         verify(l2, times(0)).onAnimationResume(a1);
259     }
260 
261     @Test
testSetCurrentPlayTimeAfterStart()262     public void testSetCurrentPlayTimeAfterStart() throws Throwable {
263         // This test sets current play time right after start() is called on a non-delayed animation
264         final long duration = 100;
265         final float seekFraction = 0.2f;
266         final CountDownLatch frameUpdateLatch = new CountDownLatch(1);
267 
268         final AnimatorSetTest.MyListener myListener = new AnimatorSetTest.MyListener();
269         final ValueAnimator anim  = ValueAnimator.ofFloat(0, 1).setDuration(duration);
270         anim.setInterpolator(null);
271         final Animator.AnimatorListener listener = mock(Animator.AnimatorListener.class);
272         anim.addListener(listener);
273         anim.addListener(myListener);
274         mActivityRule.runOnUiThread(() -> {
275             anim.start();
276             anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
277                 float fractionOnFirstFrame = -1f;
278 
279                 @Override
280                 public void onAnimationUpdate(ValueAnimator animation) {
281                     if (fractionOnFirstFrame < 0) {
282                         // First frame:
283                         fractionOnFirstFrame = animation.getAnimatedFraction();
284                         assertEquals(seekFraction, fractionOnFirstFrame, EPSILON);
285                         frameUpdateLatch.countDown();
286                     } else {
287                         assertTrue(animation.getAnimatedFraction() >= fractionOnFirstFrame);
288                     }
289                 }
290             });
291             long currentPlayTime = (long) (seekFraction * (float) duration);
292             anim.setCurrentPlayTime(currentPlayTime);
293         });
294         assertTrue(frameUpdateLatch.await(100, TimeUnit.MILLISECONDS));
295         verify(listener, within(200)).onAnimationEnd(anim, false);
296         // Also make sure the onAnimationEnd(anim) is called.
297         assertTrue(myListener.mEndIsCalled);
298     }
299 
300     @Test
testSetCurrentFraction()301     public void testSetCurrentFraction() throws Throwable {
302         final ValueAnimator anim = ValueAnimator.ofFloat(0, 100).setDuration(mDuration);
303         final long proposedCurrentPlayTime = mDuration / 2;
304         mActivityRule.runOnUiThread(() -> {
305             anim.setCurrentFraction(.5f);
306             long currentPlayTime = anim.getCurrentPlayTime();
307             float currentFraction = anim.getAnimatedFraction();
308             float currentValue = (Float) anim.getAnimatedValue();
309             assertEquals(proposedCurrentPlayTime, currentPlayTime);
310             assertEquals(.5f, currentFraction, EPSILON);
311             assertEquals(50, currentValue, EPSILON);
312         });
313         // Now make sure that it's still true a little later, to test that we're
314         // getting a result based on the seek time, not the wall clock time
315         SystemClock.sleep(100);
316         long currentPlayTime = anim.getCurrentPlayTime();
317         float currentFraction = anim.getAnimatedFraction();
318         float currentValue = (Float) anim.getAnimatedValue();
319         assertEquals(proposedCurrentPlayTime, currentPlayTime);
320         assertEquals(.5f, currentFraction, EPSILON);
321         assertEquals(50, currentValue, EPSILON);
322     }
323 
324     @UiThreadTest
325     @Test
testReverseRightAfterStart()326     public void testReverseRightAfterStart() {
327         // Reverse() right after start() should trigger immediate end() at fraction 0.
328         final ValueAnimator anim = ValueAnimator.ofFloat(0, 100).setDuration(mDuration);
329         anim.start();
330         assertTrue(anim.isStarted());
331         anim.reverse();
332         assertFalse(anim.isStarted());
333         assertEquals(0f, anim.getAnimatedFraction(), 0.0f);
334     }
335 
336     @Test
testGetFrameDelay()337     public void testGetFrameDelay() throws Throwable {
338         final long frameDelay = 10;
339         mActivityRule.runOnUiThread(() -> mValueAnimator.setFrameDelay(frameDelay));
340         startAnimation(mValueAnimator);
341         SystemClock.sleep(100);
342         mActivityRule.runOnUiThread(() -> {
343             long actualFrameDelay = mValueAnimator.getFrameDelay();
344             assertEquals(frameDelay, actualFrameDelay);
345         });
346     }
347 
348     @Test
testUpdateListeners()349     public void testUpdateListeners() throws Throwable {
350         final AnimatorSetTest.MyListener myListener = new AnimatorSetTest.MyListener();
351         ValueAnimator.AnimatorUpdateListener l1 = mock(ValueAnimator.AnimatorUpdateListener.class);
352         ValueAnimator.AnimatorUpdateListener l2 = mock(ValueAnimator.AnimatorUpdateListener.class);
353         ValueAnimator.AnimatorUpdateListener l3 = mock(ValueAnimator.AnimatorUpdateListener.class);
354         ValueAnimator.AnimatorUpdateListener l4 = mock(ValueAnimator.AnimatorUpdateListener.class);
355 
356         AnimatorListenerAdapter listener = mock(AnimatorListenerAdapter.class);
357 
358         ValueAnimator a1 = ValueAnimator.ofFloat(0, 1f);
359         a1.setDuration(50);
360         a1.addUpdateListener(l1);
361         a1.addUpdateListener(l2);
362         a1.removeAllUpdateListeners();
363 
364         a1.addUpdateListener(l3);
365         a1.addUpdateListener(l4);
366         a1.removeUpdateListener(l3);
367 
368         a1.addListener(listener);
369         a1.addListener(myListener);
370 
371         mActivityRule.runOnUiThread(() -> {
372             a1.start();
373         });
374 
375         // Wait for the anim to finish.
376         verify(listener, within(200)).onAnimationEnd(a1, false);
377         // Also make sure the onAnimationEnd(anim) is called.
378         assertTrue(myListener.mEndIsCalled);
379 
380         verify(l1, times(0)).onAnimationUpdate(a1);
381         verify(l2, times(0)).onAnimationUpdate(a1);
382         verify(l3, times(0)).onAnimationUpdate(a1);
383         verify(l4, atLeast(1)).onAnimationUpdate(a1);
384     }
385 
386     @Test
testValuesSetterAndGetter()387     public void testValuesSetterAndGetter() throws Throwable {
388 
389         final AnimatorSetTest.MyListener myListener = new AnimatorSetTest.MyListener();
390         ValueAnimator a2 = ValueAnimator.ofPropertyValuesHolder();
391         PropertyValuesHolder p1 = PropertyValuesHolder.ofFloat("scaleX", 0f, 1f);
392         PropertyValuesHolder p2 = PropertyValuesHolder.ofFloat("scaleY", 1f, 2f);
393         a2.setValues(p1, p2);
394         PropertyValuesHolder[] holders = a2.getValues();
395         assertEquals(2, holders.length);
396 
397         // Use the PropertyValueHolders returned from the getter to initialize the animator, in
398         // order to test the getter.
399         ValueAnimator a1 = ValueAnimator.ofPropertyValuesHolder(holders);
400         a1.setDuration(50);
401         a1.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
402             @Override
403             public void onAnimationUpdate(ValueAnimator animation) {
404                 float scaleX = (Float) animation.getAnimatedValue("scaleX");
405                 float scaleY = (Float) animation.getAnimatedValue("scaleY");
406                 assertTrue(scaleX >= 0f && scaleX <= 1f);
407                 assertTrue(scaleY >= 1f && scaleY <= 2f);
408             }
409         });
410         AnimatorListenerAdapter l1 = mock(AnimatorListenerAdapter.class);
411         a1.addListener(l1);
412         a1.addListener(myListener);
413 
414         mActivityRule.runOnUiThread(() -> {
415             a1.start();
416         });
417 
418         verify(l1, within(200)).onAnimationEnd(a1, false);
419         // Also make sure the onAnimationEnd(anim) is called.
420         assertTrue(myListener.mEndIsCalled);
421     }
422 
423     @Test
testSetObjectValues()424     public void testSetObjectValues() throws Throwable {
425         TypeEvaluator<PointF> eval = new TypeEvaluator<PointF>() {
426             PointF tmpValue = new PointF();
427             @Override
428             public PointF evaluate(float fraction, PointF startValue, PointF endValue) {
429                 tmpValue.x = fraction * startValue.x + (1f - fraction) * endValue.x;
430                 tmpValue.y = fraction * startValue.y + (1f - fraction) * endValue.y;
431                 return tmpValue;
432             }
433         };
434 
435         final AnimatorSetTest.MyListener myListener = new AnimatorSetTest.MyListener();
436         ValueAnimator a1 = new ValueAnimator();
437         a1.setDuration(50);
438         a1.setObjectValues(new PointF(0, 0), new PointF(1, 1));
439         a1.setEvaluator(eval);
440         a1.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
441             @Override
442             public void onAnimationUpdate(ValueAnimator animation) {
443                 PointF point = (PointF) animation.getAnimatedValue();
444                 assertTrue(point.x >= 0f && point.x <= 1f);
445                 assertTrue(point.y >= 0f && point.y <= 1f);
446             }
447         });
448         AnimatorListenerAdapter l1 = mock(AnimatorListenerAdapter.class);
449         a1.addListener(l1);
450         a1.addListener(myListener);
451         mActivityRule.runOnUiThread(() -> {
452             a1.start();
453         });
454 
455         verify(l1, within(200)).onAnimationEnd(a1, false);
456         // Also make sure the onAnimationEnd(anim) is called.
457         assertTrue(myListener.mEndIsCalled);
458     }
459 
460     @Test
testSetInterpolator()461     public void testSetInterpolator() throws Throwable {
462         AccelerateInterpolator interpolator = new AccelerateInterpolator();
463         ValueAnimator mValueAnimator = mActivity.createAnimatorWithInterpolator(interpolator);
464         startAnimation(mValueAnimator);
465         assertTrue(interpolator.equals(mValueAnimator.getInterpolator()));
466     }
467 
468     @Test
testCancel()469     public void testCancel() throws Throwable {
470         startAnimation(mValueAnimator);
471         SystemClock.sleep(100);
472         cancelAnimation(mValueAnimator);
473         assertFalse(mValueAnimator.isRunning());
474     }
475 
476     @Test
testEnd()477     public void testEnd() throws Throwable {
478         Object object = mActivity.view.newBall;
479         String property = "y";
480         float startY = mActivity.mStartY;
481         float endY = mActivity.mStartY + mActivity.mDeltaY;
482         ObjectAnimator objAnimator = ObjectAnimator.ofFloat(object, property, startY, endY);
483         objAnimator.setDuration(mDuration);
484         objAnimator.setRepeatCount(ValueAnimator.INFINITE);
485         objAnimator.setInterpolator(new AccelerateInterpolator());
486         objAnimator.setRepeatMode(ValueAnimator.REVERSE);
487         startAnimation(objAnimator);
488         SystemClock.sleep(100);
489         endAnimation(objAnimator);
490         float y = mActivity.view.newBall.getY();
491         assertEquals(y, endY, 0.0f);
492     }
493 
494     @Test
testGetAnimatedFraction()495     public void testGetAnimatedFraction() throws Throwable {
496         ValueAnimator animator = ValueAnimator.ofFloat(0f, 1f);
497         assertNotNull(animator);
498         animator.setDuration(200);
499         animator.addUpdateListener(new AnimatorUpdateListener() {
500             public float lastFraction = 0;
501             @Override
502             public void onAnimationUpdate(ValueAnimator animation) {
503                 float currentFraction = animation.getAnimatedFraction();
504                 assertTrue(
505                         "Last fraction = " + lastFraction + "current fraction = " + currentFraction,
506                         animation.getAnimatedFraction() >= lastFraction);
507                 lastFraction = currentFraction;
508                 assertTrue(currentFraction <= 1f);
509             }
510         });
511         CountDownLatch latch = new CountDownLatch(1);
512         animator.addListener(new AnimatorListenerAdapter() {
513             @Override
514             public void onAnimationEnd(Animator animation) {
515                 latch.countDown();
516             }
517         });
518         mActivityRule.runOnUiThread(() -> {
519             animator.start();
520         });
521 
522         latch.await(1000, TimeUnit.MILLISECONDS);
523 
524         assertEquals(1.0f, animator.getAnimatedFraction(), EPSILON);
525     }
526 
527     class TestInterpolator implements TimeInterpolator {
528 
529         @Override
getInterpolation(float input)530         public float getInterpolation(float input) {
531             return input * input;
532         }
533     }
534 
535     @Test
testGetAnimatedValue()536     public void testGetAnimatedValue() {
537         ValueAnimator animator = ValueAnimator.ofFloat(0f, 1f);
538         assertNotNull(animator);
539         TimeInterpolator myInterpolator = new TestInterpolator();
540         animator.setInterpolator(myInterpolator);
541         int sliceNum = 10;
542         for (int i = 0; i <= sliceNum; i++) {
543             float fraction = i / (float) sliceNum;
544             animator.setCurrentFraction(fraction);
545             assertEquals(myInterpolator.getInterpolation(fraction),
546                     (float) animator.getAnimatedValue(), EPSILON);
547 
548         }
549     }
550 
551     @Test
testGetAnimatedValue_PropertyName()552     public void testGetAnimatedValue_PropertyName() {
553         PropertyValuesHolder pvhX = PropertyValuesHolder.ofFloat("x", 100f, -100f);
554         PropertyValuesHolder pvhY = PropertyValuesHolder.ofFloat("y", 0f, 1f);
555         ValueAnimator animator = ValueAnimator.ofPropertyValuesHolder(pvhX, pvhY);
556         assertNotNull(animator);
557         TimeInterpolator myInterpolator = new TestInterpolator();
558         animator.setInterpolator(myInterpolator);
559         int sliceNum = 10;
560         for (int i = 0; i <= sliceNum; i++) {
561             float fraction = i / (float) sliceNum;
562             animator.setCurrentFraction(fraction);
563             assertEquals(myInterpolator.getInterpolation(fraction),
564                     (float) animator.getAnimatedValue("y"), EPSILON);
565 
566         }
567     }
568 
569     @Test
testOfFloat()570     public void testOfFloat() throws Throwable {
571         float start = 0.0f;
572         float end = 1.0f;
573         float[] values = {start, end};
574         final ValueAnimator valueAnimatorLocal = ValueAnimator.ofFloat(values);
575         valueAnimatorLocal.setDuration(mDuration);
576         valueAnimatorLocal.setRepeatCount(ValueAnimator.INFINITE);
577         valueAnimatorLocal.setInterpolator(new AccelerateInterpolator());
578         valueAnimatorLocal.setRepeatMode(ValueAnimator.RESTART);
579 
580         mActivityRule.runOnUiThread(valueAnimatorLocal::start);
581         SystemClock.sleep(100);
582         boolean isRunning = valueAnimatorLocal.isRunning();
583         assertTrue(isRunning);
584 
585         Float animatedValue = (Float) valueAnimatorLocal.getAnimatedValue();
586         assertTrue(animatedValue >= start);
587         assertTrue(animatedValue <= end);
588     }
589 
590     @Test
testOfInt()591     public void testOfInt() throws Throwable {
592         int start = 0;
593         int end = 10;
594         int[] values = {start, end};
595         final ValueAnimator valueAnimatorLocal = ValueAnimator.ofInt(values);
596         valueAnimatorLocal.setDuration(mDuration);
597         valueAnimatorLocal.setRepeatCount(ValueAnimator.INFINITE);
598         valueAnimatorLocal.setInterpolator(new AccelerateInterpolator());
599         valueAnimatorLocal.setRepeatMode(ValueAnimator.RESTART);
600 
601         mActivityRule.runOnUiThread(valueAnimatorLocal::start);
602         SystemClock.sleep(100);
603         boolean isRunning = valueAnimatorLocal.isRunning();
604         assertTrue(isRunning);
605 
606         Integer animatedValue = (Integer) valueAnimatorLocal.getAnimatedValue();
607         assertTrue(animatedValue >= start);
608         assertTrue(animatedValue <= end);
609     }
610 
611     @Test
testOfArgb()612     public void testOfArgb() throws Throwable {
613         int start = 0xffff0000;
614         int end = 0xff0000ff;
615         int[] values = {start, end};
616         int startRed = Color.red(start);
617         int startBlue = Color.blue(start);
618         int endRed = Color.red(end);
619         int endBlue = Color.blue(end);
620         final ValueAnimator valueAnimatorLocal = ValueAnimator.ofArgb(values);
621         valueAnimatorLocal.setDuration(mDuration);
622 
623         final CountDownLatch latch = new CountDownLatch(1);
624         valueAnimatorLocal.addUpdateListener((ValueAnimator animation) -> {
625             if (animation.getAnimatedFraction() > .05f) {
626                 latch.countDown();
627             }
628         });
629 
630         mActivityRule.runOnUiThread(valueAnimatorLocal::start);
631         boolean isRunning = valueAnimatorLocal.isRunning();
632         assertTrue(isRunning);
633 
634         assertTrue(latch.await(500, TimeUnit.MILLISECONDS));
635 
636         Integer animatedValue = (Integer) valueAnimatorLocal.getAnimatedValue();
637         int alpha = Color.alpha(animatedValue);
638         int red = Color.red(animatedValue);
639         int green = Color.green(animatedValue);
640         int blue = Color.blue(animatedValue);
641         assertTrue(red < startRed);
642         assertTrue(red > endRed);
643         assertTrue(blue > startBlue);
644         assertTrue(blue < endBlue);
645         assertEquals(255, alpha);
646         assertEquals(0, green);
647 
648         mActivityRule.runOnUiThread(valueAnimatorLocal::cancel);
649     }
650 
651     @Test
652     public void testNoDelayOnSeekAnimation() throws Throwable {
653         ValueAnimator animator = ValueAnimator.ofFloat(0, 1);
654         animator.setInterpolator(new LinearInterpolator());
655         animator.setStartDelay(1000);
656         animator.setDuration(300);
657         animator.setCurrentPlayTime(150);
658         final Animator.AnimatorListener watcher = mock(Animator.AnimatorListener.class);
659         animator.addListener(watcher);
660         mActivityRule.runOnUiThread(animator::start);
661         verify(watcher, times(1)).onAnimationStart(animator, false);
662         assertTrue(((Float)animator.getAnimatedValue()) >= 0.5f);
663         assertTrue(animator.getAnimatedFraction() >= 0.5f);
664         mActivityRule.runOnUiThread(animator::cancel);
665     }
666 
667     @Test
testNotifiesAfterEnd()668     public void testNotifiesAfterEnd() throws Throwable {
669         final ValueAnimator animator = ValueAnimator.ofFloat(0, 1);
670         animator.addListener(new AnimatorListenerAdapter() {
671             @Override
672             public void onAnimationStart(Animator animation) {
673                 assertTrue(animation.isStarted());
674                 assertTrue(animation.isRunning());
675             }
676 
677             @Override
678             public void onAnimationEnd(Animator animation) {
679                 assertFalse(animation.isRunning());
680                 assertFalse(animation.isStarted());
681                 super.onAnimationEnd(animation);
682             }
683         });
684         mActivityRule.runOnUiThread(() -> {
685             animator.start();
686             animator.end();
687         });
688     }
689 
690     @Test
testAnimatorsEnabled()691     public void testAnimatorsEnabled() throws Throwable {
692         float currentDurationScale = ValueAnimator.getDurationScale();
693         try {
694             testAnimatorsEnabledImpl(true);
695             testAnimatorsEnabledImpl(false);
696         } finally {
697             // restore scale value to avoid messing up future tests
698             ValueAnimator.setDurationScale(currentDurationScale);
699         }
700     }
701 
702     @Test
testAnimationDurationNoShortenByTinkeredScale()703     public void testAnimationDurationNoShortenByTinkeredScale() throws Throwable {
704         final long expectedDurationMs = 1000L;
705         final long minDurationMs = expectedDurationMs;
706         final long maxDurationMs = expectedDurationMs + 200L;
707         final Range<Long> durationRange = new Range<>(minDurationMs, maxDurationMs);
708 
709         final CountDownLatch endLatch = new CountDownLatch(1);
710         long[] startAnimationTime = new long[1];
711         long[] endAnimationTime = new long[1];
712 
713         final float durationScale = 1.0f;
714         float currentDurationScale = ValueAnimator.getDurationScale();
715         try {
716             ValueAnimator.setDurationScale(durationScale);
717             assertTrue("The duration scale of ValueAnimator should be 1.0f,"
718                             + " actual=" + ValueAnimator.getDurationScale(),
719                     ValueAnimator.getDurationScale() == durationScale);
720 
721             ValueAnimator animator = ValueAnimator.ofFloat(0, 1);
722             animator.setInterpolator(new LinearInterpolator());
723             animator.setDuration(expectedDurationMs);
724             assertEquals(animator.getDuration(), expectedDurationMs);
725 
726             animator.addListener(new AnimatorListenerAdapter() {
727                 @Override
728                 public void onAnimationEnd(Animator animation) {
729                     endAnimationTime[0] = SystemClock.uptimeMillis();
730                     endLatch.countDown();
731                 }
732             });
733 
734             // Start the animation and verify if the actual animation duration is in the range.
735             mActivityRule.runOnUiThread(() -> {
736                 startAnimationTime[0] = SystemClock.uptimeMillis();
737                 animator.start();
738             });
739             endLatch.await(2, TimeUnit.SECONDS);
740             final long totalTime = endAnimationTime[0] - startAnimationTime[0];
741             assertTrue("ValueAnimator the duration should be in the range "
742                     + "<" + minDurationMs + ", " + maxDurationMs + "> ms, "
743                     + "actual=" + totalTime, durationRange.contains(totalTime));
744         } finally {
745             // restore scale value to avoid messing up future tests
746             ValueAnimator.setDurationScale(currentDurationScale);
747         }
748     }
749 
testAnimatorsEnabledImpl(boolean enabled)750     private void testAnimatorsEnabledImpl(boolean enabled) throws Throwable {
751         final CountDownLatch startLatch = new CountDownLatch(1);
752         final CountDownLatch endLatch = new CountDownLatch(1);
753         final ValueAnimator animator = ValueAnimator.ofFloat(0, 1);
754 
755         animator.setDuration(1000);
756         animator.addListener(new AnimatorListenerAdapter() {
757             @Override
758             public void onAnimationEnd(Animator animation) {
759                 endLatch.countDown();
760             }
761         });
762         mActivityRule.runOnUiThread(() -> {
763             animator.start();
764             startLatch.countDown();
765         });
766 
767         assertTrue(startLatch.await(200, TimeUnit.MILLISECONDS));
768 
769         float durationScale = enabled ? 1 : 0;
770         ValueAnimator.setDurationScale(durationScale);
771 
772         if (enabled) {
773             assertTrue("Animators not enabled with duration scale 1",
774                     ValueAnimator.areAnimatorsEnabled());
775             assertFalse("Animator ended too early when animators enabled = ",
776                     endLatch.await(100, TimeUnit.MILLISECONDS));
777         } else {
778             assertFalse("Animators enabled with duration scale 0",
779                     ValueAnimator.areAnimatorsEnabled());
780             assertTrue("Animator did not end when animators enabled = ",
781                     endLatch.await(100, TimeUnit.MILLISECONDS));
782         }
783         mActivityRule.runOnUiThread(() -> {
784             animator.end();
785         });
786     }
787 
788     @Test
testRegisterAndUnregisterDurationScaleListener()789     public void testRegisterAndUnregisterDurationScaleListener() {
790         ValueAnimator.DurationScaleChangeListener listener = scale -> {
791             return;
792         };
793         assertTrue("Listener not registered",
794                 ValueAnimator.registerDurationScaleChangeListener(listener));
795         assertFalse("Listener was registered again",
796                 ValueAnimator.registerDurationScaleChangeListener(listener));
797         assertTrue("Listener not unregistered",
798                 ValueAnimator.unregisterDurationScaleChangeListener(listener));
799         assertFalse("Listener was unregistered again",
800                 ValueAnimator.unregisterDurationScaleChangeListener(listener));
801     }
802 
803     @Test
testGetDurationScale()804     public void testGetDurationScale() {
805         float currentDurationScale = ValueAnimator.getDurationScale();
806         try {
807             ValueAnimator.setDurationScale(0f);
808             assertEquals(0f, ValueAnimator.getDurationScale(), 0.0f);
809         } finally {
810             // restore scale value to avoid messing up future tests
811             ValueAnimator.setDurationScale(currentDurationScale);
812         }
813 
814     }
815 
816     @Test
testDurationScaleListenerOnChange()817     public void testDurationScaleListenerOnChange() throws InterruptedException {
818         float currentDurationScale = ValueAnimator.getDurationScale();
819 
820         ValueAnimator.setDurationScale(1f);
821         final CountDownLatch durationScaleUpdateLatch = new CountDownLatch(1);
822         ValueAnimator.DurationScaleChangeListener listener = scale -> {
823             assertEquals(0f, ValueAnimator.getDurationScale(), 0.0f);
824             durationScaleUpdateLatch.countDown();
825         };
826 
827         try {
828             ValueAnimator.registerDurationScaleChangeListener(listener);
829             ValueAnimator.setDurationScale(0f);
830             assertTrue(durationScaleUpdateLatch.await(100, TimeUnit.MILLISECONDS));
831         } finally {
832             ValueAnimator.unregisterDurationScaleChangeListener(listener);
833             // restore scale value to avoid messing up future tests
834             ValueAnimator.setDurationScale(currentDurationScale);
835         }
836     }
837 
getAnimator()838     private ValueAnimator getAnimator() {
839         Object object = mActivity.view.newBall;
840         String property = "y";
841         float startY = mActivity.mStartY;
842         float endY = mActivity.mStartY + mActivity.mDeltaY;
843         ValueAnimator objAnimator = ObjectAnimator.ofFloat(object, property, startY, endY);
844         objAnimator.setDuration(mDuration);
845         objAnimator.setRepeatCount(ValueAnimator.INFINITE);
846         objAnimator.setInterpolator(new AccelerateInterpolator());
847         objAnimator.setRepeatMode(ValueAnimator.REVERSE);
848         return objAnimator;
849     }
850 
startAnimation(final ValueAnimator animator)851     private void startAnimation(final ValueAnimator animator) throws Throwable {
852         mActivityRule.runOnUiThread(() -> mActivity.startAnimation(animator));
853     }
854 
endAnimation(final ValueAnimator animator)855     private void endAnimation(final ValueAnimator animator) throws Throwable {
856         mActivityRule.runOnUiThread(animator::end);
857     }
858 
cancelAnimation(final ValueAnimator animator)859     private void cancelAnimation(final ValueAnimator animator) throws Throwable {
860         mActivityRule.runOnUiThread(animator::cancel);
861     }
862 
errorMessage(float[] values)863     private String errorMessage(float[] values) {
864         StringBuilder message = new StringBuilder();
865         for (int i = 0; i < values.length; i++) {
866             message.append(values[i]).append(" ");
867         }
868         return message.toString();
869     }
870 }
871