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