1 /* 2 * Copyright (C) 2009 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 17 package android.view.animation.cts; 18 19 import static org.junit.Assert.assertEquals; 20 import static org.junit.Assert.assertFalse; 21 import static org.junit.Assert.assertNotNull; 22 import static org.junit.Assert.assertNotSame; 23 import static org.junit.Assert.assertNull; 24 import static org.junit.Assert.assertSame; 25 import static org.junit.Assert.assertTrue; 26 import static org.mockito.Mockito.atLeastOnce; 27 import static org.mockito.Mockito.mock; 28 import static org.mockito.Mockito.never; 29 import static org.mockito.Mockito.reset; 30 import static org.mockito.Mockito.times; 31 import static org.mockito.Mockito.verify; 32 import static org.mockito.Mockito.verifyZeroInteractions; 33 34 import android.app.Activity; 35 import android.app.Instrumentation; 36 import android.content.res.XmlResourceParser; 37 import android.os.SystemClock; 38 import android.util.AttributeSet; 39 import android.util.Xml; 40 import android.view.View; 41 import android.view.animation.AccelerateDecelerateInterpolator; 42 import android.view.animation.AccelerateInterpolator; 43 import android.view.animation.Animation; 44 import android.view.animation.Animation.AnimationListener; 45 import android.view.animation.AnimationUtils; 46 import android.view.animation.DecelerateInterpolator; 47 import android.view.animation.Interpolator; 48 import android.view.animation.Transformation; 49 import android.view.cts.R; 50 51 import androidx.test.InstrumentationRegistry; 52 import androidx.test.filters.LargeTest; 53 import androidx.test.filters.MediumTest; 54 import androidx.test.rule.ActivityTestRule; 55 import androidx.test.runner.AndroidJUnit4; 56 57 import com.android.compatibility.common.util.PollingCheck; 58 59 import org.junit.Before; 60 import org.junit.Rule; 61 import org.junit.Test; 62 import org.junit.runner.RunWith; 63 64 import java.util.concurrent.CountDownLatch; 65 import java.util.concurrent.TimeUnit; 66 67 /** 68 * Test {@link Animation}. 69 */ 70 @MediumTest 71 @RunWith(AndroidJUnit4.class) 72 public class AnimationTest { 73 private static final float COMPARISON_DELTA = 0.001f; 74 75 /** It is defined in R.anim.accelerate_alpha */ 76 private static final int ACCELERATE_ALPHA_DURATION = 1000; 77 78 /** It is defined in R.anim.decelerate_alpha */ 79 private static final int DECELERATE_ALPHA_DURATION = 2000; 80 81 private static final int CANCELATION_TIMEOUT = 5000; 82 83 private Instrumentation mInstrumentation; 84 private Activity mActivity; 85 86 @Rule 87 public ActivityTestRule<AnimationTestCtsActivity> mActivityRule = 88 new ActivityTestRule<>(AnimationTestCtsActivity.class); 89 90 @Before setup()91 public void setup() { 92 mInstrumentation = InstrumentationRegistry.getInstrumentation(); 93 mActivity = mActivityRule.getActivity(); 94 } 95 96 @Test testConstructor()97 public void testConstructor() { 98 XmlResourceParser parser = mActivity.getResources().getAnimation(R.anim.alpha); 99 AttributeSet attrs = Xml.asAttributeSet(parser); 100 new Animation(mActivity, attrs) { 101 }; 102 103 new Animation() { 104 }; 105 } 106 107 @Test testAccessInterpolator()108 public void testAccessInterpolator() { 109 // check default interpolator 110 MyAnimation myAnimation = new MyAnimation(); 111 Interpolator interpolator = myAnimation.getInterpolator(); 112 assertTrue(interpolator instanceof AccelerateDecelerateInterpolator); // issue 1561186. 113 114 myAnimation.ensureInterpolator(); 115 assertSame(interpolator, myAnimation.getInterpolator()); 116 117 myAnimation.setInterpolator(null); 118 assertNull(myAnimation.getInterpolator()); 119 myAnimation.ensureInterpolator(); 120 interpolator = myAnimation.getInterpolator(); 121 assertTrue(interpolator instanceof AccelerateDecelerateInterpolator); 122 123 Animation animation = AnimationUtils.loadAnimation(mActivity, R.anim.decelerate_alpha); 124 interpolator = animation.getInterpolator(); 125 assertNotNull(interpolator); 126 assertTrue(interpolator instanceof DecelerateInterpolator); 127 128 animation = AnimationUtils.loadAnimation(mActivity, R.anim.accelerate_alpha); 129 interpolator = animation.getInterpolator(); 130 assertNotNull(interpolator); 131 assertTrue(interpolator instanceof AccelerateInterpolator); 132 } 133 134 @Test testDefaultFill()135 public void testDefaultFill() { 136 Animation animation = new Animation() { 137 }; 138 assertTrue(animation.getFillBefore()); 139 assertFalse(animation.getFillAfter()); 140 } 141 142 @Test testAccessFill()143 public void testAccessFill() throws Throwable { 144 View animWindow = mActivity.findViewById(R.id.anim_window); 145 // XML file of R.anim.accelerate_alpha 146 // <alpha xmlns:android="http://schemas.android.com/apk/res/android" 147 // android:interpolator="@android:anim/accelerate_interpolator" 148 // android:fromAlpha="0.1" 149 // android:toAlpha="0.9" 150 // android:duration="1000" /> 151 Animation animation = AnimationUtils.loadAnimation(mActivity, R.anim.accelerate_alpha); 152 assertFalse(animation.isFillEnabled()); 153 assertTrue(animation.getFillBefore()); 154 assertFalse(animation.getFillAfter()); 155 156 AnimationTestUtils.assertRunAnimation(mInstrumentation, mActivityRule, animWindow, 157 animation); 158 159 // fillBefore and fillAfter are ignored when fillEnabled is false 160 Transformation transformation = new Transformation(); 161 // check alpha before start 162 animation.getTransformation(animation.getStartTime() - 1, transformation); 163 float alpha = transformation.getAlpha(); 164 assertEquals(0.1f, alpha, COMPARISON_DELTA); // issue 1698355 165 166 transformation = new Transformation(); 167 // check alpha after the end 168 animation.getTransformation(animation.getStartTime() + animation.getDuration() + 1, 169 transformation); 170 alpha = transformation.getAlpha(); 171 assertEquals(0.9f, alpha, COMPARISON_DELTA); // issue 1698355 172 173 animation.setFillEnabled(true); 174 animation.setFillBefore(false); 175 assertTrue(animation.isFillEnabled()); 176 assertFalse(animation.getFillBefore()); 177 assertFalse(animation.getFillAfter()); 178 AnimationTestUtils.assertRunAnimation(mInstrumentation, mActivityRule, animWindow, 179 animation); 180 181 transformation = new Transformation(); 182 animation.getTransformation(animation.getStartTime() - 1, transformation); 183 alpha = transformation.getAlpha(); 184 assertEquals(1.0f, alpha, COMPARISON_DELTA); 185 186 transformation = new Transformation(); 187 animation.getTransformation(animation.getStartTime() + animation.getDuration() + 1, 188 transformation); 189 alpha = transformation.getAlpha(); 190 assertEquals(1.0f, alpha, COMPARISON_DELTA); 191 192 animation.setFillBefore(true); 193 animation.setFillAfter(true); 194 assertTrue(animation.isFillEnabled()); 195 assertTrue(animation.getFillBefore()); 196 assertTrue(animation.getFillAfter()); 197 AnimationTestUtils.assertRunAnimation(mInstrumentation, mActivityRule, animWindow, 198 animation); 199 200 transformation = new Transformation(); 201 animation.getTransformation(animation.getStartTime() - 1, transformation); 202 alpha = transformation.getAlpha(); 203 assertEquals(0.1f, alpha, COMPARISON_DELTA); 204 205 transformation = new Transformation(); 206 animation.getTransformation(animation.getStartTime() + animation.getDuration() + 1, 207 transformation); 208 alpha = transformation.getAlpha(); 209 assertEquals(0.9f, alpha, COMPARISON_DELTA); 210 } 211 212 @Test testComputeDurationHint()213 public void testComputeDurationHint() { 214 // start offset is 0, duration is 2000, repeat count is 0. 215 Animation animation = AnimationUtils.loadAnimation(mActivity, R.anim.decelerate_alpha); 216 assertEquals(2000, animation.computeDurationHint()); 217 218 // start offset is 0, duration is 2000, repeat count is 2. 219 animation.setRepeatCount(2); 220 assertEquals(6000, animation.computeDurationHint()); 221 222 // start offset is 800, duration is 2000, repeat count is 2. 223 animation.setStartOffset(800); 224 assertEquals(8400, animation.computeDurationHint()); 225 } 226 227 @Test testRepeatAnimation()228 public void testRepeatAnimation() throws Throwable { 229 // check default repeatMode 230 Animation animation = new Animation() { 231 }; 232 assertEquals(Animation.RESTART, animation.getRepeatMode()); 233 234 final View animWindow = mActivity.findViewById(R.id.anim_window); 235 236 // XML file of R.anim.decelerate_alpha 237 // <alpha xmlns:android="http://schemas.android.com/apk/res/android" 238 // android:interpolator="@android:anim/decelerate_interpolator" 239 // android:fromAlpha="0.0" 240 // android:toAlpha="1.0" 241 // android:duration="2000" /> 242 final Animation anim = AnimationUtils.loadAnimation(mActivity, R.anim.decelerate_alpha); 243 assertEquals(Animation.RESTART, animation.getRepeatMode()); 244 long duration = anim.getDuration(); 245 assertEquals(DECELERATE_ALPHA_DURATION, duration); 246 // repeat count is 0, repeat mode does not make sense. 247 AnimationTestUtils.assertRunAnimation(mInstrumentation, mActivityRule, animWindow, anim); 248 249 // test repeat mode REVERSE 250 anim.setRepeatCount(1); 251 anim.setRepeatMode(Animation.REVERSE); 252 // we have to PollingCheck the animation status on test thread, 253 // it cannot be done on UI thread, so we invoke runOnUiThread method here. 254 mActivityRule.runOnUiThread(() -> animWindow.startAnimation(anim)); 255 256 // check whether animation has started 257 PollingCheck.waitFor(anim::hasStarted); 258 259 Transformation transformation = new Transformation(); 260 long startTime = anim.getStartTime(); 261 anim.getTransformation(startTime, transformation); 262 float alpha1 = transformation.getAlpha(); 263 assertEquals(0.0f, alpha1, COMPARISON_DELTA); 264 265 anim.getTransformation(startTime + 1000, transformation); 266 float alpha2 = transformation.getAlpha(); 267 268 anim.getTransformation(startTime + 2000, transformation); 269 float alpha3 = transformation.getAlpha(); 270 assertEquals(1.0f, alpha3, COMPARISON_DELTA); 271 272 // wait for animation has ended. 273 // timeout is larger than duration, in case the system is sluggish 274 PollingCheck.waitFor(duration * 2 + 1000, anim::hasEnded); 275 276 // get start time of reversing. 277 startTime = anim.getStartTime(); 278 anim.getTransformation(startTime + 3000, transformation); 279 float alpha4 = transformation.getAlpha(); 280 281 anim.getTransformation(startTime + 4000, transformation); 282 float alpha5 = transformation.getAlpha(); 283 assertEquals(0.0f, alpha5, COMPARISON_DELTA); 284 285 // check decelerating delta alpha when reverse. alpha should change form 0.0f to 1.0f 286 // and then from 1.0f to 0.0f 287 float delta1 = alpha2 - alpha1; 288 float delta2 = alpha3 - alpha2; 289 // the animation plays backward 290 float delta3 = alpha3 - alpha4; 291 float delta4 = alpha4 - alpha5; 292 assertTrue(delta1 > delta2); 293 assertTrue(delta3 > delta4); 294 295 // test repeat mode RESTART 296 anim.setRepeatMode(Animation.RESTART); 297 // we have to PollingCheck the animation status on test thread, 298 // it cannot be done on UI thread, so we invoke runOnUiThread method here. 299 mActivityRule.runOnUiThread(() -> animWindow.startAnimation(anim)); 300 301 // check whether animation has started 302 PollingCheck.waitFor(anim::hasStarted); 303 304 transformation = new Transformation(); 305 startTime = anim.getStartTime(); 306 anim.getTransformation(startTime, transformation); 307 alpha1 = transformation.getAlpha(); 308 assertEquals(0.0f, alpha1, COMPARISON_DELTA); 309 310 anim.getTransformation(startTime + 1000, transformation); 311 alpha2 = transformation.getAlpha(); 312 313 anim.getTransformation(startTime + 2000, transformation); 314 alpha3 = transformation.getAlpha(); 315 assertEquals(1.0f, alpha3, COMPARISON_DELTA); 316 317 // wait for animation has ended. 318 // timeout is larger than duration, in case the system is sluggish 319 PollingCheck.waitFor(duration * 2 + 1000, anim::hasEnded); 320 321 // get start time of restarting. 322 startTime = anim.getStartTime(); 323 anim.getTransformation(startTime + 3000, transformation); 324 alpha4 = transformation.getAlpha(); 325 326 anim.getTransformation(startTime + 4000, transformation); 327 alpha5 = transformation.getAlpha(); 328 assertEquals(1.0f, alpha5, COMPARISON_DELTA); 329 330 // check decelerating delta alpha when restart. alpha should change form 0.0f to 1.0f 331 // and then from 0.0f to 1.0f again 332 delta1 = alpha2 - 0.0f; 333 delta2 = alpha3 - alpha2; 334 // the animation restarts from the beginning 335 delta3 = alpha4 - 0.0f; 336 delta4 = alpha5 - alpha4; 337 assertTrue(delta1 > delta2); 338 assertTrue(delta3 > delta4); 339 } 340 341 @Test testAccessStartOffset()342 public void testAccessStartOffset() throws Throwable { 343 final long startOffset = 800; 344 // check default startOffset 345 Animation animation = new Animation() { 346 }; 347 assertEquals(0, animation.getStartOffset()); 348 349 View animWindow = mActivity.findViewById(R.id.anim_window); 350 // XML file of R.anim.accelerate_alpha 351 // <alpha xmlns:android="http://schemas.android.com/apk/res/android" 352 // android:interpolator="@android:anim/accelerate_interpolator" 353 // android:fromAlpha="0.1" 354 // android:toAlpha="0.9" 355 // android:duration="1000" /> 356 animation = AnimationUtils.loadAnimation(mActivity, R.anim.accelerate_alpha); 357 animation.setStartOffset(startOffset); 358 assertEquals(startOffset, animation.getStartOffset()); 359 360 AnimationTestUtils.assertRunAnimation(mInstrumentation, mActivityRule, animWindow, 361 animation, ACCELERATE_ALPHA_DURATION + startOffset); 362 363 Transformation transformation = new Transformation(); 364 long startTime = animation.getStartTime(); 365 animation.getTransformation(startTime, transformation); 366 float alpha1 = transformation.getAlpha(); 367 assertEquals(0.1f, alpha1, COMPARISON_DELTA); 368 369 animation.getTransformation(startTime + 400, transformation); 370 float alpha2 = transformation.getAlpha(); 371 // alpha is 0.1f during start offset 372 assertEquals(0.1f, alpha2, COMPARISON_DELTA); 373 374 animation.getTransformation(startTime + startOffset, transformation); 375 float alpha3 = transformation.getAlpha(); 376 // alpha is 0.1f during start offset 377 assertEquals(0.1f, alpha3, COMPARISON_DELTA); 378 379 animation.getTransformation(startTime + startOffset + 1, transformation); 380 float alpha4 = transformation.getAlpha(); 381 // alpha is lager than 0.1f after start offset 382 assertTrue(alpha4 > 0.1f); 383 } 384 385 @Test testRunAccelerateAlpha()386 public void testRunAccelerateAlpha() throws Throwable { 387 // check default startTime 388 Animation animation = new Animation() { 389 }; 390 assertEquals(Animation.START_ON_FIRST_FRAME, animation.getStartTime()); 391 392 long currentTime = AnimationUtils.currentAnimationTimeMillis(); 393 animation.setStartTime(currentTime); 394 assertEquals(currentTime, animation.getStartTime()); 395 396 View animWindow = mActivity.findViewById(R.id.anim_window); 397 398 // XML file of R.anim.accelerate_alpha 399 // <alpha xmlns:android="http://schemas.android.com/apk/res/android" 400 // android:interpolator="@android:anim/accelerate_interpolator" 401 // android:fromAlpha="0.1" 402 // android:toAlpha="0.9" 403 // android:duration="1000" /> 404 Animation anim = AnimationUtils.loadAnimation(mActivity, R.anim.accelerate_alpha); 405 assertEquals(Animation.START_ON_FIRST_FRAME, anim.getStartTime()); 406 assertFalse(anim.hasStarted()); 407 AnimationTestUtils.assertRunAnimation(mInstrumentation, mActivityRule, animWindow, anim); 408 } 409 410 @Test testGetTransformation()411 public void testGetTransformation() throws Throwable { 412 final View animWindow = mActivity.findViewById(R.id.anim_window); 413 414 // XML file of R.anim.accelerate_alpha 415 // <alpha xmlns:android="http://schemas.android.com/apk/res/android" 416 // android:interpolator="@android:anim/accelerate_interpolator" 417 // android:fromAlpha="0.1" 418 // android:toAlpha="0.9" 419 // android:duration="1000" /> 420 final Animation anim = AnimationUtils.loadAnimation(mActivity, R.anim.accelerate_alpha); 421 assertFalse(anim.hasStarted()); 422 423 // we have to PollingCheck the animation status on test thread, 424 // it cannot be done on UI thread, so we invoke runOnUiThread method here. 425 mActivityRule.runOnUiThread(() -> animWindow.startAnimation(anim)); 426 427 // check whether animation has started 428 PollingCheck.waitFor(anim::hasStarted); 429 430 // check transformation objects that is provided by the 431 // caller and will be filled in by the animation. 432 Transformation transformation = new Transformation(); 433 long startTime = anim.getStartTime(); 434 assertTrue(anim.getTransformation(startTime, transformation)); 435 float alpha1 = transformation.getAlpha(); 436 assertEquals(0.1f, alpha1, COMPARISON_DELTA); 437 438 assertTrue(anim.getTransformation(startTime + 250, transformation)); 439 float alpha2 = transformation.getAlpha(); 440 441 assertTrue(anim.getTransformation(startTime + 500, transformation)); 442 float alpha3 = transformation.getAlpha(); 443 444 assertTrue(anim.getTransformation(startTime + 750, transformation)); 445 float alpha4 = transformation.getAlpha(); 446 447 // wait for animation has ended. 448 // timeout is larger than duration, in case the system is sluggish 449 PollingCheck.waitFor(2000, anim::hasEnded); 450 451 assertFalse(anim.getTransformation(startTime + 1000, transformation)); 452 float alpha5 = transformation.getAlpha(); 453 assertEquals(0.9f, alpha5, COMPARISON_DELTA); 454 455 // check decelerating delta alpha 456 float delta1 = alpha2 - alpha1; 457 float delta2 = alpha3 - alpha2; 458 float delta3 = alpha4 - alpha3; 459 float delta4 = alpha5 - alpha4; 460 assertTrue(delta1 < delta2); 461 assertTrue(delta2 < delta3); 462 assertTrue(delta3 < delta4); 463 } 464 465 @Test 466 public void testAccessZAdjustment() { 467 // check default zAdjustment 468 Animation animation = new Animation() { 469 }; 470 assertEquals(Animation.ZORDER_NORMAL, animation.getZAdjustment()); 471 472 animation = AnimationUtils.loadAnimation(mActivity, R.anim.accelerate_alpha); 473 assertEquals(Animation.ZORDER_NORMAL, animation.getZAdjustment()); 474 475 animation.setZAdjustment(Animation.ZORDER_TOP); 476 assertEquals(Animation.ZORDER_TOP, animation.getZAdjustment()); 477 478 animation.setZAdjustment(Animation.ZORDER_BOTTOM); 479 assertEquals(Animation.ZORDER_BOTTOM, animation.getZAdjustment()); 480 } 481 482 @Test 483 public void testInitialize() { 484 Animation animation = new Animation() { 485 }; 486 487 assertFalse(animation.isInitialized()); 488 animation.initialize(320, 480, 320, 480); 489 assertTrue(animation.isInitialized()); 490 } 491 492 @Test 493 public void testResolveSize() { 494 MyAnimation myAnimation = new MyAnimation(); 495 496 assertEquals(1.0f, myAnimation.resolveSize(Animation.ABSOLUTE, 1.0f, 0, 0), 497 COMPARISON_DELTA); 498 assertEquals(2.0f, myAnimation.resolveSize(Animation.ABSOLUTE, 2.0f, 0, 0), 499 COMPARISON_DELTA); 500 501 assertEquals(6.0f, myAnimation.resolveSize(Animation.RELATIVE_TO_SELF, 3.0f, 2, 0), 502 COMPARISON_DELTA); 503 assertEquals(9.0f, myAnimation.resolveSize(Animation.RELATIVE_TO_SELF, 3.0f, 3, 0), 504 COMPARISON_DELTA); 505 506 assertEquals(18.0f, myAnimation.resolveSize(Animation.RELATIVE_TO_PARENT, 3.0f, 0, 6), 507 COMPARISON_DELTA); 508 assertEquals(12.0f, myAnimation.resolveSize(Animation.RELATIVE_TO_PARENT, 3.0f, 0, 4), 509 COMPARISON_DELTA); 510 511 int unknownType = 7; 512 assertEquals(8.0f, myAnimation.resolveSize(unknownType, 8.0f, 3, 4), COMPARISON_DELTA); 513 assertEquals(10.0f, myAnimation.resolveSize(unknownType, 10.0f, 3, 4), COMPARISON_DELTA); 514 } 515 516 @Test 517 public void testRestrictDuration() { 518 Animation animation = new Animation() { 519 }; 520 521 animation.setStartOffset(1000); 522 animation.restrictDuration(500); 523 assertEquals(500, animation.getStartOffset()); 524 assertEquals(0, animation.getDuration()); 525 assertEquals(0, animation.getRepeatCount()); 526 527 animation.setStartOffset(1000); 528 animation.setDuration(1000); 529 animation.restrictDuration(1500); 530 assertEquals(500, animation.getDuration()); 531 532 animation.setStartOffset(1000); 533 animation.setDuration(1000); 534 animation.setRepeatCount(3); 535 animation.restrictDuration(4500); 536 assertEquals(1, animation.getRepeatCount()); 537 } 538 539 @Test 540 public void testScaleCurrentDuration() { 541 Animation animation = new Animation() { 542 }; 543 544 animation.setDuration(10); 545 animation.scaleCurrentDuration(0); 546 assertEquals(0, animation.getDuration()); 547 548 animation.setDuration(10); 549 animation.scaleCurrentDuration(2); 550 assertEquals(20, animation.getDuration()); 551 552 animation.setDuration(10); 553 animation.scaleCurrentDuration(-1); 554 assertEquals(-10, animation.getDuration()); 555 } 556 557 @LargeTest 558 @Test 559 public void testSetAnimationListener() throws Throwable { 560 final View animWindow = mActivity.findViewById(R.id.anim_window); 561 562 // XML file of R.anim.accelerate_alpha 563 // <alpha xmlns:android="http://schemas.android.com/apk/res/android" 564 // android:interpolator="@android:anim/accelerate_interpolator" 565 // android:fromAlpha="0.1" 566 // android:toAlpha="0.9" 567 // android:duration="1000" /> 568 final Animation anim = AnimationUtils.loadAnimation(mActivity, R.anim.accelerate_alpha); 569 final AnimationListener listener = mock(AnimationListener.class); 570 anim.setAnimationListener(listener); 571 verifyZeroInteractions(listener); 572 573 AnimationTestUtils.assertRunAnimation(mInstrumentation, mActivityRule, animWindow, anim); 574 verify(listener, times(1)).onAnimationStart(anim); 575 verify(listener, times(1)).onAnimationEnd(anim); 576 verify(listener, never()).onAnimationRepeat(anim); 577 578 reset(listener); 579 anim.setRepeatCount(2); 580 anim.setRepeatMode(Animation.REVERSE); 581 582 AnimationTestUtils.assertRunAnimation(mInstrumentation, mActivityRule, animWindow, anim, 583 3 * ACCELERATE_ALPHA_DURATION); 584 verify(listener, times(1)).onAnimationStart(anim); 585 verify(listener, times(2)).onAnimationRepeat(anim); 586 verify(listener, times(1)).onAnimationEnd(anim); 587 588 reset(listener); 589 // onAnimationEnd will not be invoked and animation should not end 590 anim.setRepeatCount(Animation.INFINITE); 591 592 mActivityRule.runOnUiThread(() -> animWindow.startAnimation(anim)); 593 // Verify that our animation doesn't call listener's onAnimationEnd even after a long 594 // period of time. We just sleep and then verify what's happened with the listener. 595 SystemClock.sleep(4 * ACCELERATE_ALPHA_DURATION); 596 597 verify(listener, times(1)).onAnimationStart(anim); 598 verify(listener, atLeastOnce()).onAnimationRepeat(anim); 599 verify(listener, never()).onAnimationEnd(anim); 600 } 601 602 @Test 603 public void testStart() { 604 Animation animation = AnimationUtils.loadAnimation(mActivity, R.anim.accelerate_alpha); 605 animation.setStartTime(0); 606 607 animation.start(); 608 assertEquals(Animation.START_ON_FIRST_FRAME, animation.getStartTime()); 609 } 610 611 @Test 612 public void testStartNow() { 613 Animation animation = AnimationUtils.loadAnimation(mActivity, R.anim.accelerate_alpha); 614 animation.setStartTime(0); 615 616 long currentTime = AnimationUtils.currentAnimationTimeMillis(); 617 animation.startNow(); 618 assertEquals(currentTime, animation.getStartTime(), 100); 619 } 620 621 @Test 622 public void testWillChangeBounds() { 623 Animation animation = new Animation() { 624 }; 625 626 assertTrue(animation.willChangeBounds()); 627 } 628 629 @Test 630 public void testWillChangeTransformationMatrix() { 631 Animation animation = new Animation() { 632 }; 633 634 assertTrue(animation.willChangeTransformationMatrix()); 635 } 636 637 @Test 638 public void testClone() throws CloneNotSupportedException { 639 MyAnimation myAnimation = new MyAnimation(); 640 myAnimation.setDuration(3000); 641 myAnimation.setFillAfter(true); 642 myAnimation.setFillBefore(false); 643 myAnimation.setFillEnabled(true); 644 myAnimation.setStartTime(1000); 645 myAnimation.setRepeatCount(10); 646 myAnimation.setRepeatMode(Animation.REVERSE); 647 648 Animation cloneAnimation = myAnimation.clone(); 649 assertNotSame(myAnimation, cloneAnimation); 650 assertEquals(myAnimation.getDuration(), cloneAnimation.getDuration()); 651 assertEquals(myAnimation.getFillAfter(), cloneAnimation.getFillAfter()); 652 assertEquals(myAnimation.getFillBefore(), cloneAnimation.getFillBefore()); 653 assertEquals(myAnimation.isFillEnabled(), cloneAnimation.isFillEnabled()); 654 assertEquals(myAnimation.getStartOffset(), cloneAnimation.getStartOffset()); 655 assertEquals(myAnimation.getRepeatCount(), cloneAnimation.getRepeatCount()); 656 assertEquals(myAnimation.getRepeatMode(), cloneAnimation.getRepeatMode()); 657 } 658 659 @Test 660 public void testCancelImmediately() throws Throwable { 661 MyAnimation anim = new MyAnimation(); 662 final CountDownLatch latch1 = new CountDownLatch(1); 663 runCanceledAnimation(anim, latch1, false, false); 664 assertTrue(latch1.await(CANCELATION_TIMEOUT, TimeUnit.MILLISECONDS)); 665 assertFalse(anim.isStillAnimating()); 666 } 667 668 @Test 669 public void testRepeatingCancelImmediately() throws Throwable { 670 MyAnimation anim = new MyAnimation(); 671 final CountDownLatch latch2 = new CountDownLatch(1); 672 runCanceledAnimation(anim, latch2, true, false); 673 assertTrue(latch2.await(CANCELATION_TIMEOUT, TimeUnit.MILLISECONDS)); 674 assertFalse(anim.isStillAnimating()); 675 } 676 677 @Test 678 public void testCancelDelayed() throws Throwable { 679 MyAnimation anim = new MyAnimation(); 680 final CountDownLatch latch3 = new CountDownLatch(1); 681 runCanceledAnimation(anim, latch3, false, true); 682 assertTrue(latch3.await(CANCELATION_TIMEOUT, TimeUnit.MILLISECONDS)); 683 assertFalse(anim.isStillAnimating()); 684 } 685 686 @Test 687 public void testRepeatingCancelDelayed() throws Throwable { 688 MyAnimation anim = new MyAnimation(); 689 final CountDownLatch latch4 = new CountDownLatch(1); 690 runCanceledAnimation(anim, latch4, true, true); 691 assertTrue(latch4.await(CANCELATION_TIMEOUT, TimeUnit.MILLISECONDS)); 692 assertFalse(anim.isStillAnimating()); 693 } 694 695 private void runCanceledAnimation(final MyAnimation anim, final CountDownLatch latch, 696 final boolean repeating, final boolean delayed) throws Throwable { 697 // The idea behind this test is that canceling an Animation should result in 698 // it ending, which means not having its getTransformation() method called 699 // anymore. The trick is that cancel() will still allow one more frame to run, 700 // so we have to insert some delay between when we cancel and when we can check 701 // whether it is still animating. 702 final View view = mActivity.findViewById(R.id.anim_window); 703 mActivityRule.runOnUiThread(() -> { 704 anim.setDuration(delayed ? 300 : 200); 705 if (repeating) { 706 anim.setRepeatCount(Animation.INFINITE); 707 } 708 view.startAnimation(anim); 709 if (!delayed) { 710 anim.cancel(); 711 } else { 712 view.postDelayed(anim::cancel, 100); 713 } 714 view.postDelayed(() -> { 715 anim.setStillAnimating(false); 716 view.postDelayed(latch::countDown, 200); 717 }, delayed ? 300 : 200); 718 }); 719 } 720 721 private class MyAnimation extends Animation { 722 boolean mStillAnimating = false; 723 724 @Override ensureInterpolator()725 protected void ensureInterpolator() { 726 super.ensureInterpolator(); 727 } 728 729 @Override resolveSize(int type, float value, int size, int parentSize)730 protected float resolveSize(int type, float value, int size, int parentSize) { 731 return super.resolveSize(type, value, size, parentSize); 732 } 733 734 @Override clone()735 protected Animation clone() throws CloneNotSupportedException { 736 return super.clone(); 737 } 738 setStillAnimating(boolean value)739 public void setStillAnimating(boolean value) { 740 mStillAnimating = value; 741 } 742 isStillAnimating()743 public boolean isStillAnimating() { 744 return mStillAnimating; 745 } 746 747 @Override getTransformation(long currentTime, Transformation outTransformation)748 public boolean getTransformation(long currentTime, Transformation outTransformation) { 749 mStillAnimating = true; 750 return super.getTransformation(currentTime, outTransformation); 751 } 752 } 753 } 754