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