Home | History | Annotate | Download | only in cts
      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
     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
     90     public void tearDown() {
     91         ValueAnimator.setDurationScale(mPreviousDurationScale);
     92     }
     93 
     94     @Test
     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      */
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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 
    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 
    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 
    647     private void startAnimation(final AnimatorSet animatorSet) throws Throwable {
    648         mActivityRule.runOnUiThread(() -> mActivity.startAnimatorSet(animatorSet));
    649     }
    650 
    651     private void assertUnique(Object object) {
    652         assertUnique(object, "");
    653     }
    654 
    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
    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
    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
    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
    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
    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
    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
    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
    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 
    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 
    993         public void onAnimationStart(Animator animation) {
    994             mStartIsCalled = true;
    995         }
    996 
    997         public void onAnimationEnd(Animator animation) {
    998             mEndIsCalled = true;
    999         }
   1000     }
   1001 
   1002     static class MyValueAnimator extends ValueAnimator {
   1003         boolean mStartCalled = false;
   1004         @Override
   1005         public void start() {
   1006             // Do not call super intentionally.
   1007             mStartCalled = true;
   1008         }
   1009     }
   1010 }
   1011