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