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