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