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