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.assertSame;
23 import static org.junit.Assert.assertTrue;
24 import static org.mockito.Mockito.any;
25 import static org.mockito.Mockito.mock;
26 import static org.mockito.Mockito.never;
27 import static org.mockito.Mockito.timeout;
28 import static org.mockito.Mockito.times;
29 import static org.mockito.Mockito.verify;
30 
31 import android.animation.Animator;
32 import android.animation.AnimatorListenerAdapter;
33 import android.animation.AnimatorSet;
34 import android.animation.ObjectAnimator;
35 import android.animation.TimeInterpolator;
36 import android.animation.ValueAnimator;
37 import android.os.SystemClock;
38 import android.view.View;
39 import android.view.animation.AccelerateDecelerateInterpolator;
40 import android.view.animation.AccelerateInterpolator;
41 import android.view.animation.LinearInterpolator;
42 
43 import androidx.test.InstrumentationRegistry;
44 import androidx.test.filters.MediumTest;
45 import androidx.test.rule.ActivityTestRule;
46 import androidx.test.runner.AndroidJUnit4;
47 
48 import org.junit.After;
49 import org.junit.Before;
50 import org.junit.Rule;
51 import org.junit.Test;
52 import org.junit.runner.RunWith;
53 
54 import java.util.ArrayList;
55 import java.util.HashSet;
56 import java.util.List;
57 import java.util.Set;
58 import java.util.concurrent.CountDownLatch;
59 import java.util.concurrent.TimeUnit;
60 
61 @MediumTest
62 @RunWith(AndroidJUnit4.class)
63 public class AnimatorSetTest {
64     private AnimationActivity mActivity;
65     private AnimatorSet mAnimatorSet;
66     private float mPreviousDurationScale = 1.0f;
67     private long mDuration = 1000;
68     private Object object;
69     private ObjectAnimator yAnimator;
70     private ObjectAnimator xAnimator;
71     Set<Integer> identityHashes = new HashSet<>();
72     private static final float EPSILON = 0.001f;
73 
74     @Rule
75     public ActivityTestRule<AnimationActivity> mActivityRule =
76             new ActivityTestRule<>(AnimationActivity.class);
77 
78     @Before
setup()79     public void setup() {
80         InstrumentationRegistry.getInstrumentation().setInTouchMode(false);
81         mActivity = mActivityRule.getActivity();
82         mPreviousDurationScale = ValueAnimator.getDurationScale();
83         ValueAnimator.setDurationScale(1.0f);
84         object = mActivity.view.newBall;
85         yAnimator = getYAnimator(object);
86         xAnimator = getXAnimator(object);
87     }
88 
89     @After
tearDown()90     public void tearDown() {
91         ValueAnimator.setDurationScale(mPreviousDurationScale);
92     }
93 
94     @Test
testPlaySequentially()95     public void testPlaySequentially() throws Throwable {
96         xAnimator.setRepeatCount(0);
97         yAnimator.setRepeatCount(0);
98         xAnimator.setDuration(50);
99         yAnimator.setDuration(50);
100         List<Animator> animators = new ArrayList<Animator>();
101         animators.add(xAnimator);
102         animators.add(yAnimator);
103         mAnimatorSet = new AnimatorSet();
104         mAnimatorSet.playSequentially(animators);
105         verifySequentialPlayOrder(mAnimatorSet, new Animator[] {xAnimator, yAnimator});
106 
107         ValueAnimator anim1 = ValueAnimator.ofFloat(0f, 1f);
108         ValueAnimator anim2 = ValueAnimator.ofInt(0, 100);
109         anim1.setDuration(50);
110         anim2.setDuration(50);
111         AnimatorSet set = new AnimatorSet();
112         set.playSequentially(anim1, anim2);
113         verifySequentialPlayOrder(set, new Animator[] {anim1, anim2});
114     }
115 
116     /**
117      * Start the animator, and verify the animators are played sequentially in the order that is
118      * defined in the array.
119      *
120      * @param set AnimatorSet to be started and verified
121      * @param animators animators that we put in the AnimatorSet, in the order that they'll play
122      */
verifySequentialPlayOrder(final AnimatorSet set, Animator[] animators)123     private void verifySequentialPlayOrder(final AnimatorSet set, Animator[] animators)
124             throws Throwable {
125 
126         final MyListener[] listeners = new MyListener[animators.length];
127         for (int i = 0; i < animators.length; i++) {
128             if (i == 0) {
129                 listeners[i] = new MyListener();
130             } else {
131                 final int current = i;
132                 listeners[i] = new MyListener() {
133                     @Override
134                     public void onAnimationStart(Animator anim) {
135                         super.onAnimationStart(anim);
136                         // Check that the previous animator has finished.
137                         assertTrue(listeners[current - 1].mEndIsCalled);
138                     }
139                 };
140             }
141             animators[i].addListener(listeners[i]);
142         }
143 
144         final CountDownLatch startLatch = new CountDownLatch(1);
145         final CountDownLatch endLatch = new CountDownLatch(1);
146 
147         set.addListener(new MyListener() {
148             @Override
149             public void onAnimationEnd(Animator anim) {
150                 endLatch.countDown();
151             }
152         });
153 
154         long totalDuration = set.getTotalDuration();
155         assertFalse(set.isRunning());
156         mActivityRule.runOnUiThread(() -> {
157             set.start();
158             startLatch.countDown();
159         });
160 
161         // Set timeout to 100ms, if current count reaches 0 before the timeout, startLatch.await(...)
162         // will return immediately.
163         assertTrue(startLatch.await(100, TimeUnit.MILLISECONDS));
164         assertTrue(set.isRunning());
165         assertTrue(endLatch.await(totalDuration * 2, TimeUnit.MILLISECONDS));
166         // Check that all the animators have finished.
167         for (int i = 0; i < listeners.length; i++) {
168             assertTrue(listeners[i].mEndIsCalled);
169         }
170 
171         // Now reverse the animations and verify whether the play order is reversed.
172         for (int i = 0; i < animators.length; i++) {
173             if (i == animators.length - 1) {
174                 listeners[i] = new MyListener();
175             } else {
176                 final int current = i;
177                 listeners[i] = new MyListener() {
178                     @Override
179                     public void onAnimationStart(Animator anim) {
180                         super.onAnimationStart(anim);
181                         // Check that the previous animator has finished.
182                         assertTrue(listeners[current + 1].mEndIsCalled);
183                     }
184                 };
185             }
186             animators[i].removeAllListeners();
187             animators[i].addListener(listeners[i]);
188         }
189 
190         mActivityRule.runOnUiThread(() -> {
191             set.reverse();
192             startLatch.countDown();
193         });
194 
195         // Set timeout to 100ms, if current count reaches 0 before the timeout, startLatch.await(..)
196         // will return immediately.
197         assertTrue(startLatch.await(100, TimeUnit.MILLISECONDS));
198         assertTrue(set.isRunning());
199         assertTrue(endLatch.await(totalDuration * 2, TimeUnit.MILLISECONDS));
200 
201     }
202 
203     @Test
testPlayTogether()204     public void testPlayTogether() throws Throwable {
205         xAnimator.setRepeatCount(ValueAnimator.INFINITE);
206         Animator[] animatorArray = {xAnimator, yAnimator};
207 
208         mAnimatorSet = new AnimatorSet();
209         mAnimatorSet.playTogether(animatorArray);
210 
211         assertFalse(mAnimatorSet.isRunning());
212         assertFalse(xAnimator.isRunning());
213         assertFalse(yAnimator.isRunning());
214         startAnimation(mAnimatorSet);
215         SystemClock.sleep(100);
216         assertTrue(mAnimatorSet.isRunning());
217         assertTrue(xAnimator.isRunning());
218         assertTrue(yAnimator.isRunning());
219 
220         // Now assemble another animator set
221         ValueAnimator anim1 = ValueAnimator.ofFloat(0f, 100f);
222         ValueAnimator anim2 = ValueAnimator.ofFloat(10f, 100f);
223         AnimatorSet set = new AnimatorSet();
224         set.playTogether(anim1, anim2);
225 
226         assertFalse(set.isRunning());
227         assertFalse(anim1.isRunning());
228         assertFalse(anim2.isRunning());
229         startAnimation(set);
230         SystemClock.sleep(100);
231         assertTrue(set.isRunning());
232         assertTrue(anim1.isRunning());
233         assertTrue(anim2.isRunning());
234     }
235 
236     @Test
testPlayBeforeAfter()237     public void testPlayBeforeAfter() throws Throwable {
238         xAnimator.setRepeatCount(0);
239         yAnimator.setRepeatCount(0);
240         final ValueAnimator zAnimator = ValueAnimator.ofFloat(0f, 100f);
241 
242         xAnimator.setDuration(50);
243         yAnimator.setDuration(50);
244         zAnimator.setDuration(50);
245 
246         AnimatorSet set = new AnimatorSet();
247         set.play(yAnimator).before(zAnimator).after(xAnimator);
248 
249         verifySequentialPlayOrder(set, new Animator[] {xAnimator, yAnimator, zAnimator});
250     }
251 
252     @Test
testListenerCallbackOnEmptySet()253     public void testListenerCallbackOnEmptySet() throws Throwable {
254         // Create an AnimatorSet that only contains one empty AnimatorSet, and checks the callback
255         // sequence by checking the time stamps of the callbacks.
256         final AnimatorSet emptySet = new AnimatorSet();
257         final AnimatorSet set = new AnimatorSet();
258         set.play(emptySet);
259         MyListener listener = new MyListener() {
260             long startTime = 0;
261             long endTime = 0;
262             @Override
263             public void onAnimationStart(Animator animation) {
264                 super.onAnimationStart(animation);
265                 startTime = SystemClock.currentThreadTimeMillis();
266             }
267 
268             @Override
269             public void onAnimationEnd(Animator animation) {
270                 super.onAnimationEnd(animation);
271                 endTime = SystemClock.currentThreadTimeMillis();
272                 assertTrue(endTime >= startTime);
273                 assertTrue(startTime != 0);
274             }
275         };
276         set.addListener(listener);
277         mActivityRule.runOnUiThread(() -> {
278             set.start();
279         });
280         assertTrue(listener.mStartIsCalled);
281         assertTrue(listener.mEndIsCalled);
282     }
283 
284     @Test
testPauseAndResume()285     public void testPauseAndResume() throws Throwable {
286         final AnimatorSet set = new AnimatorSet();
287         ValueAnimator a1 = ValueAnimator.ofFloat(0f, 100f);
288         a1.setDuration(50);
289         ValueAnimator a2 = ValueAnimator.ofFloat(0f, 100f);
290         a2.setDuration(50);
291         a1.addListener(new AnimatorListenerAdapter() {
292             @Override
293             public void onAnimationStart(Animator animation) {
294                 // Pause non-delayed set once the child animator starts
295                 set.pause();
296             }
297         });
298         set.playTogether(a1, a2);
299 
300         final AnimatorSet delayedSet = new AnimatorSet();
301         ValueAnimator a3 = ValueAnimator.ofFloat(0f, 100f);
302         a3.setDuration(50);
303         ValueAnimator a4 = ValueAnimator.ofFloat(0f, 100f);
304         a4.setDuration(50);
305         delayedSet.playSequentially(a3, a4);
306         delayedSet.setStartDelay(50);
307 
308         MyListener l1 = new MyListener();
309         MyListener l2 = new MyListener();
310         set.addListener(l1);
311         delayedSet.addListener(l2);
312 
313         mActivityRule.runOnUiThread(() -> {
314             set.start();
315             delayedSet.start();
316 
317             // Pause the delayed set during start delay
318             delayedSet.pause();
319         });
320 
321         // Sleep long enough so that if the sets are not properly paused, they would have
322         // finished.
323         SystemClock.sleep(300);
324         // Verify that both sets have been paused and *not* finished.
325         assertTrue(set.isPaused());
326         assertTrue(delayedSet.isPaused());
327         assertTrue(l1.mStartIsCalled);
328         assertTrue(l2.mStartIsCalled);
329         assertFalse(l1.mEndIsCalled);
330         assertFalse(l2.mEndIsCalled);
331 
332         mActivityRule.runOnUiThread(() -> {
333             set.resume();
334             delayedSet.resume();
335         });
336         SystemClock.sleep(300);
337 
338         assertFalse(set.isPaused());
339         assertFalse(delayedSet.isPaused());
340         assertTrue(l1.mEndIsCalled);
341         assertTrue(l2.mEndIsCalled);
342     }
343 
344     @Test
testPauseBeforeStart()345     public void testPauseBeforeStart() throws Throwable {
346         final AnimatorSet set = new AnimatorSet();
347         ValueAnimator a1 = ValueAnimator.ofFloat(0f, 100f);
348         a1.setDuration(50);
349         ValueAnimator a2 = ValueAnimator.ofFloat(0f, 100f);
350         a2.setDuration(50);
351         set.setStartDelay(50);
352         set.playSequentially(a1, a2);
353 
354         final MyListener listener = new MyListener();
355         set.addListener(listener);
356 
357         mActivityRule.runOnUiThread(() -> {
358             // Pause animator set before calling start()
359             set.pause();
360             // Verify that pause should have no effect on a not-yet-started animator.
361             assertFalse(set.isPaused());
362             set.start();
363         });
364         SystemClock.sleep(300);
365 
366         // Animator set should finish running by now since it's not paused.
367         assertTrue(listener.mStartIsCalled);
368         assertTrue(listener.mEndIsCalled);
369     }
370 
371     @Test
testSeekAfterPause()372     public void testSeekAfterPause() throws Throwable {
373         final AnimatorSet set = new AnimatorSet();
374         ValueAnimator a1 = ValueAnimator.ofFloat(0f, 50f);
375         a1.setDuration(50);
376         ValueAnimator a2 = ValueAnimator.ofFloat(50, 100f);
377         a2.setDuration(50);
378         set.playSequentially(a1, a2);
379         set.setInterpolator(new LinearInterpolator());
380 
381         mActivityRule.runOnUiThread(() -> {
382             set.start();
383             set.pause();
384             set.setCurrentPlayTime(60);
385             assertEquals((long) set.getCurrentPlayTime(), 60);
386             assertEquals((float) a1.getAnimatedValue(), 50f, EPSILON);
387             assertEquals((float) a2.getAnimatedValue(), 60f, EPSILON);
388 
389             set.setCurrentPlayTime(40);
390             assertEquals((long) set.getCurrentPlayTime(), 40);
391             assertEquals((float) a1.getAnimatedValue(), 40f, EPSILON);
392             assertEquals((float) a2.getAnimatedValue(), 50f, EPSILON);
393 
394             set.cancel();
395         });
396     }
397 
398     @Test
testDuration()399     public void testDuration() throws Throwable {
400         xAnimator.setRepeatCount(ValueAnimator.INFINITE);
401         Animator[] animatorArray = { xAnimator, yAnimator };
402 
403         mAnimatorSet = new AnimatorSet();
404         mAnimatorSet.playTogether(animatorArray);
405         mAnimatorSet.setDuration(1000);
406 
407         startAnimation(mAnimatorSet);
408         SystemClock.sleep(100);
409         assertEquals(mAnimatorSet.getDuration(), 1000);
410     }
411 
412     @Test
testStartDelay()413     public void testStartDelay() throws Throwable {
414         xAnimator.setRepeatCount(ValueAnimator.INFINITE);
415         Animator[] animatorArray = { xAnimator, yAnimator };
416 
417         mAnimatorSet = new AnimatorSet();
418         mAnimatorSet.playTogether(animatorArray);
419         mAnimatorSet.setStartDelay(10);
420 
421         startAnimation(mAnimatorSet);
422         SystemClock.sleep(100);
423         assertEquals(mAnimatorSet.getStartDelay(), 10);
424     }
425 
426     /**
427      * This test sets up an AnimatorSet with start delay. One of the child animators also has
428      * start delay. We then verify that start delay was handled correctly on both AnimatorSet
429      * and individual animator level.
430      */
431     @Test
testReverseWithStartDelay()432     public void testReverseWithStartDelay() throws Throwable {
433         ValueAnimator a1 = ValueAnimator.ofFloat(0f, 1f);
434         a1.setDuration(200);
435         Animator.AnimatorListener listener1 = mock(AnimatorListenerAdapter.class);
436         a1.addListener(listener1);
437 
438         ValueAnimator a2 = ValueAnimator.ofFloat(1f, 2f);
439         a2.setDuration(200);
440         // Set start delay on a2 so that the delay is passed 100ms after a1 is finished.
441         a2.setStartDelay(300);
442         Animator.AnimatorListener listener = mock(AnimatorListenerAdapter.class);
443         a2.addListener(listener);
444 
445         a2.addListener(new AnimatorListenerAdapter() {
446             @Override
447             public void onAnimationEnd(Animator animation, boolean inReverse) {
448                 assertTrue(inReverse);
449                 // By the time a2 finishes reversing, a1 should not have started.
450                 assertFalse(a1.isStarted());
451             }
452         });
453 
454         AnimatorSet set = new AnimatorSet();
455         set.playTogether(a1, a2);
456         set.setStartDelay(1000);
457         Animator.AnimatorListener setListener = mock(AnimatorListenerAdapter.class);
458         set.addListener(setListener);
459         mActivityRule.runOnUiThread(() -> {
460             set.reverse();
461             assertTrue(a2.isStarted());
462             assertTrue(a2.isRunning());
463         });
464 
465         // a2 should finish 200ms after reverse started
466         verify(listener, within(300)).onAnimationEnd(a2, true);
467         // When a2 finishes, a1 should not have started yet
468         verify(listener1, never()).onAnimationStart(a1, true);
469 
470         // The whole set should finish within 500ms, i.e. 300ms after a2 is finished. This verifies
471         // that the AnimatorSet didn't mistakenly use its start delay in the reverse run.
472         verify(setListener, within(400)).onAnimationEnd(set, true);
473         verify(listener1, times(1)).onAnimationEnd(a1, true);
474 
475     }
476 
477     /**
478      * Test that duration scale is handled correctly in the AnimatorSet.
479      */
480     @Test
testZeroDurationScale()481     public void testZeroDurationScale() throws Throwable {
482         ValueAnimator.setDurationScale(0);
483 
484         ValueAnimator a1 = ValueAnimator.ofFloat(0f, 1f);
485         a1.setDuration(200);
486         Animator.AnimatorListener listener1 = mock(AnimatorListenerAdapter.class);
487         a1.addListener(listener1);
488 
489         ValueAnimator a2 = ValueAnimator.ofFloat(1f, 2f);
490         a2.setDuration(200);
491         // Set start delay on a2 so that the delay is passed 100ms after a1 is finished.
492         a2.setStartDelay(300);
493         Animator.AnimatorListener listener2 = mock(AnimatorListenerAdapter.class);
494         a2.addListener(listener2);
495 
496         AnimatorSet set = new AnimatorSet();
497         set.playSequentially(a1, a2);
498         set.setStartDelay(1000);
499         Animator.AnimatorListener setListener = mock(AnimatorListenerAdapter.class);
500         set.addListener(setListener);
501 
502         mActivityRule.runOnUiThread(() -> {
503             set.start();
504             verify(setListener, times(0)).onAnimationEnd(any(AnimatorSet.class),
505                     any(boolean.class));
506         });
507         verify(setListener, within(100)).onAnimationEnd(set, false);
508         verify(listener1, times(1)).onAnimationEnd(a1, false);
509         verify(listener2, times(1)).onAnimationEnd(a2, false);
510     }
511 
512     /**
513      * Test that non-zero duration scale is handled correctly in the AnimatorSet.
514      */
515     @Test
testDurationScale()516     public void testDurationScale() throws Throwable {
517         // Change the duration scale to 3
518         ValueAnimator.setDurationScale(3f);
519 
520         ValueAnimator a1 = ValueAnimator.ofFloat(0f, 1f);
521         a1.setDuration(100);
522         Animator.AnimatorListener listener1 = mock(AnimatorListenerAdapter.class);
523         a1.addListener(listener1);
524 
525         ValueAnimator a2 = ValueAnimator.ofFloat(1f, 2f);
526         a2.setDuration(100);
527         // Set start delay on a2 so that the delay is passed 100ms after a1 is finished.
528         a2.setStartDelay(200);
529         Animator.AnimatorListener listener2 = mock(AnimatorListenerAdapter.class);
530         a2.addListener(listener2);
531 
532         AnimatorSet set = new AnimatorSet();
533         set.playSequentially(a1, a2);
534         Animator.AnimatorListener setListener = mock(AnimatorListenerAdapter.class);
535         set.addListener(setListener);
536         set.setStartDelay(200);
537 
538         mActivityRule.runOnUiThread(() -> {
539             set.start();
540         });
541 
542         // Sleep for part of the start delay and check that no child animator has started, to verify
543         // that the duration scale has been properly scaled.
544         SystemClock.sleep(400);
545         // start delay of the set should be scaled to 600ms
546         verify(listener1, never()).onAnimationStart(a1, false);
547         verify(listener2, never()).onAnimationStart(a2, false);
548 
549         verify(listener1, within(400)).onAnimationStart(a1, false);
550         // Verify that a1 finish in ~300ms (3x its defined duration)
551         verify(listener1, within(500)).onAnimationEnd(a1, false);
552 
553         // a2 should be in the delayed stage after a1 is finished
554         assertTrue(a2.isStarted());
555         assertFalse(a2.isRunning());
556 
557         verify(listener2, within(800)).onAnimationStart(a2, false);
558         assertTrue(a2.isRunning());
559 
560         // Verify that the AnimatorSet has finished within 1650ms since the start of the animation.
561         // The duration of the set is 500ms, duration scale = 3.
562         verify(setListener, within(500)).onAnimationEnd(set, false);
563         verify(listener1, times(1)).onAnimationEnd(a1, false);
564         verify(listener2, times(1)).onAnimationEnd(a2, false);
565     }
566 
567     /**
568      * This test sets up 10 animators playing together. We expect the start time for all animators
569      * to be the same.
570      */
571     @Test
testMultipleAnimatorsPlayTogether()572     public void testMultipleAnimatorsPlayTogether() throws Throwable {
573         Animator[] animators = new Animator[10];
574         for (int i = 0; i < 10; i++) {
575             animators[i] = ValueAnimator.ofFloat(0f, 1f);
576         }
577         AnimatorSet set = new AnimatorSet();
578         set.playTogether(animators);
579         set.setStartDelay(80);
580 
581         Animator.AnimatorListener setListener = mock(AnimatorListenerAdapter.class);
582         set.addListener(setListener);
583         mActivityRule.runOnUiThread(() -> {
584             set.start();
585         });
586         SystemClock.sleep(150);
587         for (int i = 0; i < 10; i++) {
588             assertTrue(animators[i].isRunning());
589         }
590 
591         verify(setListener, within(400)).onAnimationEnd(set, false);
592     }
593 
594     @Test
testGetChildAnimations()595     public void testGetChildAnimations() throws Throwable {
596         Animator[] animatorArray = { xAnimator, yAnimator };
597 
598         mAnimatorSet = new AnimatorSet();
599         mAnimatorSet.getChildAnimations();
600         assertEquals(0, mAnimatorSet.getChildAnimations().size());
601         mAnimatorSet.playSequentially(animatorArray);
602         assertEquals(2, mAnimatorSet.getChildAnimations().size());
603     }
604 
605     @Test
testSetInterpolator()606     public void testSetInterpolator() throws Throwable {
607         xAnimator.setRepeatCount(ValueAnimator.INFINITE);
608         Animator[] animatorArray = {xAnimator, yAnimator};
609         TimeInterpolator interpolator = new AccelerateDecelerateInterpolator();
610         mAnimatorSet = new AnimatorSet();
611         mAnimatorSet.playTogether(animatorArray);
612         mAnimatorSet.setInterpolator(interpolator);
613 
614         assertFalse(mAnimatorSet.isRunning());
615         startAnimation(mAnimatorSet);
616         SystemClock.sleep(100);
617 
618         ArrayList<Animator> animatorList = mAnimatorSet.getChildAnimations();
619         assertEquals(interpolator, animatorList.get(0).getInterpolator());
620         assertEquals(interpolator, animatorList.get(1).getInterpolator());
621     }
622 
getXAnimator(Object object)623     private ObjectAnimator getXAnimator(Object object) {
624         String propertyX = "x";
625         float startX = mActivity.mStartX;
626         float endX = mActivity.mStartX + mActivity.mDeltaX;
627         ObjectAnimator xAnimator = ObjectAnimator.ofFloat(object, propertyX, startX, endX);
628         xAnimator.setDuration(mDuration);
629         xAnimator.setRepeatCount(ValueAnimator.INFINITE);
630         xAnimator.setInterpolator(new AccelerateInterpolator());
631         xAnimator.setRepeatMode(ValueAnimator.REVERSE);
632         return xAnimator;
633     }
634 
getYAnimator(Object object)635     private ObjectAnimator getYAnimator(Object object) {
636          String property = "y";
637          float startY = mActivity.mStartY;
638          float endY = mActivity.mStartY + mActivity.mDeltaY;
639          ObjectAnimator yAnimator = ObjectAnimator.ofFloat(object, property, startY, endY);
640          yAnimator.setDuration(mDuration);
641          yAnimator.setRepeatCount(2);
642          yAnimator.setInterpolator(new AccelerateInterpolator());
643          yAnimator.setRepeatMode(ValueAnimator.REVERSE);
644         return yAnimator;
645     }
646 
startAnimation(final AnimatorSet animatorSet)647     private void startAnimation(final AnimatorSet animatorSet) throws Throwable {
648         mActivityRule.runOnUiThread(() -> mActivity.startAnimatorSet(animatorSet));
649     }
650 
assertUnique(Object object)651     private void assertUnique(Object object) {
652         assertUnique(object, "");
653     }
654 
assertUnique(Object object, String msg)655     private void assertUnique(Object object, String msg) {
656         final int code = System.identityHashCode(object);
657         assertTrue("object should be unique " + msg + ", obj:" + object, identityHashes.add(code));
658 
659     }
660 
661     @Test
testClone()662     public void testClone() throws Throwable {
663         final AnimatorSet set1 = new AnimatorSet();
664         final AnimatorListenerAdapter setListener = new AnimatorListenerAdapter() {};
665         set1.addListener(setListener);
666         ObjectAnimator animator1 = new ObjectAnimator();
667         animator1.setDuration(100);
668         animator1.setPropertyName("x");
669         animator1.setIntValues(5);
670         animator1.setInterpolator(new LinearInterpolator());
671         AnimatorListenerAdapter listener1 = new AnimatorListenerAdapter(){};
672         AnimatorListenerAdapter listener2 = new AnimatorListenerAdapter(){};
673         animator1.addListener(listener1);
674 
675         ObjectAnimator animator2 = new ObjectAnimator();
676         animator2.setDuration(100);
677         animator2.setInterpolator(new LinearInterpolator());
678         animator2.addListener(listener2);
679         animator2.setPropertyName("y");
680         animator2.setIntValues(10);
681 
682         set1.playTogether(animator1, animator2);
683 
684         AnimateObject target = new AnimateObject();
685         set1.setTarget(target);
686         mActivityRule.runOnUiThread(set1::start);
687         assertTrue(set1.isStarted());
688 
689         animator1.getListeners();
690         AnimatorSet set2 = set1.clone();
691         assertFalse(set2.isStarted());
692 
693         assertUnique(set1);
694         assertUnique(animator1);
695         assertUnique(animator2);
696 
697         assertUnique(set2);
698         assertEquals(2, set2.getChildAnimations().size());
699 
700         Animator clone1 = set2.getChildAnimations().get(0);
701         Animator clone2 = set2.getChildAnimations().get(1);
702 
703         for (Animator animator : set2.getChildAnimations()) {
704             assertUnique(animator);
705         }
706 
707         assertTrue(clone1.getListeners().contains(listener1));
708         assertTrue(clone2.getListeners().contains(listener2));
709 
710         assertTrue(set2.getListeners().contains(setListener));
711 
712         for (Animator.AnimatorListener listener : set1.getListeners()) {
713             assertTrue(set2.getListeners().contains(listener));
714         }
715 
716         assertEquals(animator1.getDuration(), clone1.getDuration());
717         assertEquals(animator2.getDuration(), clone2.getDuration());
718         assertSame(animator1.getInterpolator(), clone1.getInterpolator());
719         assertSame(animator2.getInterpolator(), clone2.getInterpolator());
720     }
721 
722     /**
723      * Testing seeking in an AnimatorSet containing sequential animators.
724      */
725     @Test
testSeeking()726     public void testSeeking() throws Throwable {
727         final AnimatorSet set = new AnimatorSet();
728         final ValueAnimator a1 = ValueAnimator.ofFloat(0f, 150f);
729         a1.setDuration(150);
730         final ValueAnimator a2 = ValueAnimator.ofFloat(150f, 250f);
731         a2.setDuration(100);
732         final ValueAnimator a3 = ValueAnimator.ofFloat(250f, 300f);
733         a3.setDuration(50);
734 
735         a1.setInterpolator(null);
736         a2.setInterpolator(null);
737         a3.setInterpolator(null);
738 
739         set.playSequentially(a1, a2, a3);
740 
741         set.setCurrentPlayTime(100);
742         assertEquals(100f, (Float) a1.getAnimatedValue(), EPSILON);
743         assertEquals(150f, (Float) a2.getAnimatedValue(), EPSILON);
744         assertEquals(250f, (Float) a3.getAnimatedValue(), EPSILON);
745 
746         set.setCurrentPlayTime(280);
747         assertEquals(150f, (Float) a1.getAnimatedValue(), EPSILON);
748         assertEquals(250f, (Float) a2.getAnimatedValue(), EPSILON);
749         assertEquals(280f, (Float) a3.getAnimatedValue(), EPSILON);
750 
751         AnimatorListenerAdapter setListener = new AnimatorListenerAdapter() {
752             @Override
753             public void onAnimationEnd(Animator animation) {
754                 super.onAnimationEnd(animation);
755                 assertEquals(150f, (Float) a1.getAnimatedValue(), EPSILON);
756                 assertEquals(250f, (Float) a2.getAnimatedValue(), EPSILON);
757                 assertEquals(300f, (Float) a3.getAnimatedValue(), EPSILON);
758 
759             }
760         };
761         AnimatorListenerAdapter mockListener = mock(AnimatorListenerAdapter.class);
762         set.addListener(setListener);
763         set.addListener(mockListener);
764         mActivityRule.runOnUiThread(() -> {
765             set.start();
766         });
767 
768         verify(mockListener, within(300)).onAnimationEnd(set, false);
769 
770         // Seek after a run to the middle-ish, and verify the first animator is at the end
771         // value and the 3rd at beginning value, and the 2nd animator is at the seeked value.
772         set.setCurrentPlayTime(200);
773         assertEquals(150f, (Float) a1.getAnimatedValue(), EPSILON);
774         assertEquals(200f, (Float) a2.getAnimatedValue(), EPSILON);
775         assertEquals(250f, (Float) a3.getAnimatedValue(), EPSILON);
776     }
777 
778     /**
779      * Testing seeking in an AnimatorSet containing infinite animators.
780      */
781     @Test
testSeekingInfinite()782     public void testSeekingInfinite() {
783         final AnimatorSet set = new AnimatorSet();
784         final ValueAnimator a1 = ValueAnimator.ofFloat(0f, 100f);
785         a1.setDuration(100);
786         final ValueAnimator a2 = ValueAnimator.ofFloat(100f, 200f);
787         a2.setDuration(100);
788         a2.setRepeatCount(ValueAnimator.INFINITE);
789         a2.setRepeatMode(ValueAnimator.RESTART);
790 
791         final ValueAnimator a3 = ValueAnimator.ofFloat(100f, 200f);
792         a3.setDuration(100);
793         a3.setRepeatCount(ValueAnimator.INFINITE);
794         a3.setRepeatMode(ValueAnimator.REVERSE);
795 
796         a1.setInterpolator(null);
797         a2.setInterpolator(null);
798         a3.setInterpolator(null);
799         set.play(a1).before(a2);
800         set.play(a1).before(a3);
801 
802         set.setCurrentPlayTime(50);
803         assertEquals(50f, (Float) a1.getAnimatedValue(), EPSILON);
804         assertEquals(100f, (Float) a2.getAnimatedValue(), EPSILON);
805         assertEquals(100f, (Float) a3.getAnimatedValue(), EPSILON);
806 
807         set.setCurrentPlayTime(100);
808         assertEquals(100f, (Float) a1.getAnimatedValue(), EPSILON);
809         assertEquals(100f, (Float) a2.getAnimatedValue(), EPSILON);
810         assertEquals(100f, (Float) a3.getAnimatedValue(), EPSILON);
811 
812         // Seek to the 1st iteration of the infinite repeat animators, and they should have the
813         // same value.
814         set.setCurrentPlayTime(180);
815         assertEquals(100f, (Float) a1.getAnimatedValue(), EPSILON);
816         assertEquals(180f, (Float) a2.getAnimatedValue(), EPSILON);
817         assertEquals(180f, (Float) a3.getAnimatedValue(), EPSILON);
818 
819         // Seek to the 2nd iteration of the infinite repeat animators, and they should have
820         // different values as they have different repeat mode.
821         set.setCurrentPlayTime(280);
822         assertEquals(100f, (Float) a1.getAnimatedValue(), EPSILON);
823         assertEquals(180f, (Float) a2.getAnimatedValue(), EPSILON);
824         assertEquals(120f, (Float) a3.getAnimatedValue(), EPSILON);
825 
826     }
827 
828     /**
829      * This test verifies that getCurrentPlayTime() returns the right value.
830      */
831     @Test
testGetCurrentPlayTime()832     public void testGetCurrentPlayTime() throws Throwable {
833         // Setup an AnimatorSet with start delay
834         final AnimatorSet set = new AnimatorSet();
835         final ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f).setDuration(300);
836         anim.addListener(new AnimatorListenerAdapter() {
837             @Override
838             public void onAnimationStart(Animator animation, boolean inReverse) {
839                 assertFalse(inReverse);
840                 assertTrue(set.getCurrentPlayTime() >= 200);
841             }
842         });
843         set.play(anim);
844         set.setStartDelay(100);
845 
846         Animator.AnimatorListener setListener = mock(AnimatorListenerAdapter.class);
847         set.addListener(setListener);
848 
849         // Set a seek time and verify, before start
850         set.setCurrentPlayTime(20);
851         assertEquals(20, set.getCurrentPlayTime());
852 
853         // Now start() should start right away from the seeked position, skipping the delay.
854         mActivityRule.runOnUiThread(() -> {
855             set.setCurrentPlayTime(200);
856             set.start();
857             assertEquals(200, set.getCurrentPlayTime());
858         });
859 
860         // When animation is seeked to 200ms, it should take another 100ms to end.
861         verify(setListener, within(200)).onAnimationEnd(set, false);
862     }
863 
864     @Test
testNotifiesAfterEnd()865     public void testNotifiesAfterEnd() throws Throwable {
866         final ValueAnimator animator = ValueAnimator.ofFloat(0, 1);
867         Animator.AnimatorListener listener = new AnimatorListenerAdapter() {
868             @Override
869             public void onAnimationStart(Animator animation) {
870                 assertTrue(animation.isStarted());
871                 assertTrue(animation.isRunning());
872             }
873 
874             @Override
875             public void onAnimationEnd(Animator animation) {
876                 assertFalse(animation.isRunning());
877                 assertFalse(animation.isStarted());
878                 super.onAnimationEnd(animation);
879             }
880         };
881         animator.addListener(listener);
882         final AnimatorSet animatorSet = new AnimatorSet();
883         animatorSet.playTogether(animator);
884         animatorSet.addListener(listener);
885         mActivityRule.runOnUiThread(() -> {
886             animatorSet.start();
887             animator.end();
888             assertFalse(animator.isStarted());
889         });
890     }
891 
892     /**
893      * Test that when a child animator is being manipulated outside of an AnimatorSet, by the time
894      * AnimatorSet starts, it will not be affected, and all the child animators would start at their
895      * scheduled start time.
896      */
897     @Test
testManipulateChildOutsideOfSet()898     public void testManipulateChildOutsideOfSet() throws Throwable {
899         final ValueAnimator fadeIn = ObjectAnimator.ofFloat(mActivity.view, View.ALPHA, 0f, 1f);
900         fadeIn.setDuration(200);
901         final ValueAnimator fadeOut = ObjectAnimator.ofFloat(mActivity.view, View.ALPHA, 1f, 0f);
902         fadeOut.setDuration(200);
903 
904         ValueAnimator.AnimatorUpdateListener listener = mock(
905                 ValueAnimator.AnimatorUpdateListener.class);
906         fadeIn.addUpdateListener(listener);
907 
908         AnimatorSet show = new AnimatorSet();
909         show.play(fadeIn);
910 
911         AnimatorSet hideNShow = new AnimatorSet();
912         hideNShow.play(fadeIn).after(fadeOut);
913 
914         mActivityRule.runOnUiThread(() ->
915                 show.start()
916         );
917 
918         verify(listener, timeout(100).atLeast(2)).onAnimationUpdate(fadeIn);
919 
920         AnimatorListenerAdapter adapter = mock(AnimatorListenerAdapter.class);
921         hideNShow.addListener(adapter);
922         // Start hideNShow after fadeIn is started for 100ms
923         mActivityRule.runOnUiThread(() ->
924                 hideNShow.start()
925         );
926 
927         verify(adapter, timeout(800)).onAnimationEnd(hideNShow, false);
928         // Now that the hideNShow finished we need to check whether the fadeIn animation ran again.
929         assertEquals(1f, mActivity.view.getAlpha(), 0);
930 
931     }
932 
933     /**
934      *
935      * This test verifies that custom ValueAnimators will be start()'ed in a set.
936      */
937     @Test
testChildAnimatorStartCalled()938     public void testChildAnimatorStartCalled() throws Throwable {
939         MyValueAnimator a1 = new MyValueAnimator();
940         MyValueAnimator a2 = new MyValueAnimator();
941         AnimatorSet set = new AnimatorSet();
942         set.playTogether(a1, a2);
943         mActivityRule.runOnUiThread(() -> {
944             set.start();
945             assertTrue(a1.mStartCalled);
946             assertTrue(a2.mStartCalled);
947         });
948 
949     }
950 
951     /**
952      * This test sets up an AnimatorSet that contains two sequential animations. The first animation
953      * is infinite, the second animation therefore has an infinite start time. This test verifies
954      * that the infinite start time is handled correctly.
955      */
956     @Test
testInfiniteStartTime()957     public void testInfiniteStartTime() throws Throwable {
958         ValueAnimator a1 = ValueAnimator.ofFloat(0f, 1f);
959         a1.setRepeatCount(ValueAnimator.INFINITE);
960         ValueAnimator a2 = ValueAnimator.ofFloat(0f, 1f);
961 
962         AnimatorSet set = new AnimatorSet();
963         set.playSequentially(a1, a2);
964 
965         mActivityRule.runOnUiThread(() -> {
966             set.start();
967         });
968 
969         assertEquals(Animator.DURATION_INFINITE, set.getTotalDuration());
970 
971         mActivityRule.runOnUiThread(() -> {
972             set.end();
973         });
974     }
975 
976     static class TargetObj {
977         public float value = 0;
978 
setVal(float value)979         public void setVal(float value) {
980             this.value = value;
981         }
982     }
983 
984     class AnimateObject {
985         int x = 1;
986         int y = 2;
987     }
988 
989     static class MyListener extends AnimatorListenerAdapter {
990         boolean mStartIsCalled = false;
991         boolean mEndIsCalled = false;
992 
onAnimationStart(Animator animation)993         public void onAnimationStart(Animator animation) {
994             mStartIsCalled = true;
995         }
996 
onAnimationEnd(Animator animation)997         public void onAnimationEnd(Animator animation) {
998             mEndIsCalled = true;
999         }
1000     }
1001 
1002     static class MyValueAnimator extends ValueAnimator {
1003         boolean mStartCalled = false;
1004         @Override
start()1005         public void start() {
1006             // Do not call super intentionally.
1007             mStartCalled = true;
1008         }
1009     }
1010 }
1011