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.assertNotNull; 23 import static org.junit.Assert.assertTrue; 24 import static org.mockito.Mockito.atLeast; 25 import static org.mockito.Mockito.mock; 26 import static org.mockito.Mockito.timeout; 27 import static org.mockito.Mockito.times; 28 import static org.mockito.Mockito.verify; 29 30 import android.animation.Animator; 31 import android.animation.Animator.AnimatorListener; 32 import android.animation.AnimatorListenerAdapter; 33 import android.animation.ObjectAnimator; 34 import android.animation.PropertyValuesHolder; 35 import android.animation.TimeInterpolator; 36 import android.animation.TypeEvaluator; 37 import android.animation.ValueAnimator; 38 import android.animation.ValueAnimator.AnimatorUpdateListener; 39 import android.graphics.Color; 40 import android.graphics.PointF; 41 import android.os.SystemClock; 42 import android.util.Range; 43 import android.view.animation.AccelerateInterpolator; 44 import android.view.animation.LinearInterpolator; 45 46 import androidx.test.InstrumentationRegistry; 47 import androidx.test.annotation.UiThreadTest; 48 import androidx.test.filters.LargeTest; 49 import androidx.test.rule.ActivityTestRule; 50 import androidx.test.runner.AndroidJUnit4; 51 52 import com.android.compatibility.common.util.AdoptShellPermissionsRule; 53 54 import org.junit.AfterClass; 55 import org.junit.Before; 56 import org.junit.BeforeClass; 57 import org.junit.Rule; 58 import org.junit.Test; 59 import org.junit.runner.RunWith; 60 61 import java.util.concurrent.CountDownLatch; 62 import java.util.concurrent.TimeUnit; 63 64 @LargeTest 65 @RunWith(AndroidJUnit4.class) 66 public class ValueAnimatorTest { 67 private static final float EPSILON = 0.0001f; 68 private static float sPreviousAnimatorScale = 1.0f; 69 70 private AnimationActivity mActivity; 71 private ValueAnimator mValueAnimator; 72 private final long mDuration = 2000; 73 74 @Rule(order = 0) 75 public AdoptShellPermissionsRule mAdoptShellPermissionsRule = 76 new AdoptShellPermissionsRule( 77 androidx.test.platform.app.InstrumentationRegistry 78 .getInstrumentation().getUiAutomation(), 79 android.Manifest.permission.START_ACTIVITIES_FROM_SDK_SANDBOX); 80 81 @Rule(order = 1) 82 public ActivityTestRule<AnimationActivity> mActivityRule = 83 new ActivityTestRule<>(AnimationActivity.class); 84 85 @Before setup()86 public void setup() { 87 InstrumentationRegistry.getInstrumentation().setInTouchMode(false); 88 mActivity = mActivityRule.getActivity(); 89 mValueAnimator = mActivity.createAnimatorWithDuration(mDuration); 90 } 91 92 @BeforeClass beforeClass()93 public static void beforeClass() { 94 sPreviousAnimatorScale = ValueAnimator.getDurationScale(); 95 ValueAnimator.setDurationScale(1.0f); 96 } 97 98 @AfterClass afterClass()99 public static void afterClass() { 100 ValueAnimator.setDurationScale(sPreviousAnimatorScale); 101 } 102 103 @Test testDuration()104 public void testDuration() throws Throwable { 105 ValueAnimator valueAnimatorLocal = mActivity.createAnimatorWithDuration(mDuration); 106 startAnimation(valueAnimatorLocal); 107 assertEquals(mDuration, valueAnimatorLocal.getDuration()); 108 } 109 110 @Test testIsRunning()111 public void testIsRunning() throws Throwable { 112 assertFalse(mValueAnimator.isRunning()); 113 startAnimation(mValueAnimator); 114 ValueAnimator valueAnimatorReturned = mActivity.view.bounceYAnimator; 115 assertTrue(valueAnimatorReturned.isRunning()); 116 } 117 118 @Test testIsStarted()119 public void testIsStarted() throws Throwable { 120 assertFalse(mValueAnimator.isRunning()); 121 assertFalse(mValueAnimator.isStarted()); 122 long startDelay = 10000; 123 mValueAnimator.setStartDelay(startDelay); 124 startAnimation(mValueAnimator); 125 assertFalse(mValueAnimator.isRunning()); 126 assertTrue(mValueAnimator.isStarted()); 127 } 128 129 @Test testRepeatMode()130 public void testRepeatMode() throws Throwable { 131 ValueAnimator mValueAnimator = mActivity.createAnimatorWithRepeatMode( 132 ValueAnimator.RESTART); 133 startAnimation(mValueAnimator); 134 assertEquals(ValueAnimator.RESTART, mValueAnimator.getRepeatMode()); 135 } 136 137 @Test testRepeatCount()138 public void testRepeatCount() throws Throwable { 139 int repeatCount = 2; 140 ValueAnimator mValueAnimator = mActivity.createAnimatorWithRepeatCount(repeatCount); 141 startAnimation(mValueAnimator); 142 assertEquals(repeatCount, mValueAnimator.getRepeatCount()); 143 } 144 145 @Test testStartDelay()146 public void testStartDelay() { 147 long startDelay = 1000; 148 mValueAnimator.setStartDelay(startDelay); 149 assertEquals(startDelay, mValueAnimator.getStartDelay()); 150 } 151 152 /** 153 * Verify that an animator with start delay will have its listener's onAnimationStart(...) 154 * and onAnimationEnd(...) called at the right time. 155 */ 156 @Test testListenerCallbackWithStartDelay()157 public void testListenerCallbackWithStartDelay() throws Throwable { 158 final ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f); 159 anim.setStartDelay(300); 160 anim.setDuration(300); 161 AnimatorListener listener = mock(AnimatorListenerAdapter.class); 162 anim.addListener(listener); 163 mActivityRule.runOnUiThread(() -> { 164 anim.start(); 165 }); 166 167 verify(listener, timeout(450).times(1)).onAnimationStart(anim, false); 168 verify(listener, timeout(450).times(1)).onAnimationEnd(anim, false); 169 } 170 171 @Test testGetCurrentPlayTime()172 public void testGetCurrentPlayTime() throws Throwable { 173 startAnimation(mValueAnimator); 174 SystemClock.sleep(100); 175 long currentPlayTime = mValueAnimator.getCurrentPlayTime(); 176 assertTrue(currentPlayTime > 0); 177 } 178 179 @Test testSetCurrentPlayTime()180 public void testSetCurrentPlayTime() throws Throwable { 181 final ValueAnimator anim = ValueAnimator.ofFloat(0, 100).setDuration(mDuration); 182 final ValueAnimator delayedAnim = ValueAnimator.ofFloat(0, 100).setDuration(mDuration); 183 delayedAnim.setStartDelay(mDuration); 184 final long proposedCurrentPlayTime = mDuration / 2; 185 mActivityRule.runOnUiThread(() -> { 186 anim.setCurrentPlayTime(mDuration / 2); 187 long currentPlayTime = anim.getCurrentPlayTime(); 188 float currentFraction = anim.getAnimatedFraction(); 189 float currentValue = (Float) anim.getAnimatedValue(); 190 assertEquals(proposedCurrentPlayTime, currentPlayTime); 191 assertEquals(.5f, currentFraction, EPSILON); 192 assertEquals(50, currentValue, EPSILON); 193 194 delayedAnim.setCurrentPlayTime(mDuration / 2); 195 currentPlayTime = delayedAnim.getCurrentPlayTime(); 196 currentFraction = delayedAnim.getAnimatedFraction(); 197 currentValue = (Float) delayedAnim.getAnimatedValue(); 198 assertEquals(proposedCurrentPlayTime, currentPlayTime); 199 assertEquals(.5f, currentFraction, EPSILON); 200 assertEquals(50, currentValue, EPSILON); 201 }); 202 // Now make sure that it's still true a little later, to test that we're 203 // getting a result based on the seek time, not the wall clock time 204 SystemClock.sleep(100); 205 long currentPlayTime = anim.getCurrentPlayTime(); 206 float currentFraction = anim.getAnimatedFraction(); 207 float currentValue = (Float) anim.getAnimatedValue(); 208 assertEquals(proposedCurrentPlayTime, currentPlayTime); 209 assertEquals(.5f, currentFraction, EPSILON); 210 assertEquals(50, currentValue, EPSILON); 211 212 currentPlayTime = delayedAnim.getCurrentPlayTime(); 213 currentFraction = delayedAnim.getAnimatedFraction(); 214 currentValue = (Float) delayedAnim.getAnimatedValue(); 215 assertEquals(proposedCurrentPlayTime, currentPlayTime); 216 assertEquals(.5f, currentFraction, EPSILON); 217 assertEquals(50, currentValue, EPSILON); 218 219 // Finally, start() the delayed animation and check that the play time was 220 // not affected by playing during the delay 221 mActivityRule.runOnUiThread(() -> { 222 delayedAnim.start(); 223 assertEquals(proposedCurrentPlayTime, delayedAnim.getCurrentPlayTime()); 224 assertEquals(.5f, delayedAnim.getAnimatedFraction(), EPSILON); 225 assertEquals(50, (float) delayedAnim.getAnimatedValue(), EPSILON); 226 }); 227 228 SystemClock.sleep(100); 229 currentPlayTime = delayedAnim.getCurrentPlayTime(); 230 currentFraction = delayedAnim.getAnimatedFraction(); 231 currentValue = (Float) delayedAnim.getAnimatedValue(); 232 assertTrue(currentPlayTime > proposedCurrentPlayTime); 233 assertTrue(currentFraction > 0.5f); 234 assertTrue(currentValue > 50); 235 236 mActivityRule.runOnUiThread(delayedAnim::cancel); 237 } 238 239 @Test testPauseListener()240 public void testPauseListener() throws Throwable { 241 // Adds two pause listeners to the animator, and remove one after the animator is paused. 242 Animator.AnimatorPauseListener l1 = mock(Animator.AnimatorPauseListener.class); 243 Animator.AnimatorPauseListener l2 = mock(Animator.AnimatorPauseListener.class); 244 ValueAnimator a1 = ValueAnimator.ofFloat(0, 1f); 245 a1.addPauseListener(l1); 246 a1.addPauseListener(l2); 247 mActivityRule.runOnUiThread(() -> { 248 a1.start(); 249 a1.pause(); 250 verify(l1, times(1)).onAnimationPause(a1); 251 verify(l2, times(1)).onAnimationPause(a1); 252 a1.removePauseListener(l2); 253 a1.resume(); 254 }); 255 256 // Check that the pause listener that is removed doesn't have resume called. 257 verify(l1, times(1)).onAnimationResume(a1); 258 verify(l2, times(0)).onAnimationResume(a1); 259 } 260 261 @Test testSetCurrentPlayTimeAfterStart()262 public void testSetCurrentPlayTimeAfterStart() throws Throwable { 263 // This test sets current play time right after start() is called on a non-delayed animation 264 final long duration = 100; 265 final float seekFraction = 0.2f; 266 final CountDownLatch frameUpdateLatch = new CountDownLatch(1); 267 268 final AnimatorSetTest.MyListener myListener = new AnimatorSetTest.MyListener(); 269 final ValueAnimator anim = ValueAnimator.ofFloat(0, 1).setDuration(duration); 270 anim.setInterpolator(null); 271 final Animator.AnimatorListener listener = mock(Animator.AnimatorListener.class); 272 anim.addListener(listener); 273 anim.addListener(myListener); 274 mActivityRule.runOnUiThread(() -> { 275 anim.start(); 276 anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 277 float fractionOnFirstFrame = -1f; 278 279 @Override 280 public void onAnimationUpdate(ValueAnimator animation) { 281 if (fractionOnFirstFrame < 0) { 282 // First frame: 283 fractionOnFirstFrame = animation.getAnimatedFraction(); 284 assertEquals(seekFraction, fractionOnFirstFrame, EPSILON); 285 frameUpdateLatch.countDown(); 286 } else { 287 assertTrue(animation.getAnimatedFraction() >= fractionOnFirstFrame); 288 } 289 } 290 }); 291 long currentPlayTime = (long) (seekFraction * (float) duration); 292 anim.setCurrentPlayTime(currentPlayTime); 293 }); 294 assertTrue(frameUpdateLatch.await(100, TimeUnit.MILLISECONDS)); 295 verify(listener, within(200)).onAnimationEnd(anim, false); 296 // Also make sure the onAnimationEnd(anim) is called. 297 assertTrue(myListener.mEndIsCalled); 298 } 299 300 @Test testSetCurrentFraction()301 public void testSetCurrentFraction() throws Throwable { 302 final ValueAnimator anim = ValueAnimator.ofFloat(0, 100).setDuration(mDuration); 303 final long proposedCurrentPlayTime = mDuration / 2; 304 mActivityRule.runOnUiThread(() -> { 305 anim.setCurrentFraction(.5f); 306 long currentPlayTime = anim.getCurrentPlayTime(); 307 float currentFraction = anim.getAnimatedFraction(); 308 float currentValue = (Float) anim.getAnimatedValue(); 309 assertEquals(proposedCurrentPlayTime, currentPlayTime); 310 assertEquals(.5f, currentFraction, EPSILON); 311 assertEquals(50, currentValue, EPSILON); 312 }); 313 // Now make sure that it's still true a little later, to test that we're 314 // getting a result based on the seek time, not the wall clock time 315 SystemClock.sleep(100); 316 long currentPlayTime = anim.getCurrentPlayTime(); 317 float currentFraction = anim.getAnimatedFraction(); 318 float currentValue = (Float) anim.getAnimatedValue(); 319 assertEquals(proposedCurrentPlayTime, currentPlayTime); 320 assertEquals(.5f, currentFraction, EPSILON); 321 assertEquals(50, currentValue, EPSILON); 322 } 323 324 @UiThreadTest 325 @Test testReverseRightAfterStart()326 public void testReverseRightAfterStart() { 327 // Reverse() right after start() should trigger immediate end() at fraction 0. 328 final ValueAnimator anim = ValueAnimator.ofFloat(0, 100).setDuration(mDuration); 329 anim.start(); 330 assertTrue(anim.isStarted()); 331 anim.reverse(); 332 assertFalse(anim.isStarted()); 333 assertEquals(0f, anim.getAnimatedFraction(), 0.0f); 334 } 335 336 @Test testGetFrameDelay()337 public void testGetFrameDelay() throws Throwable { 338 final long frameDelay = 10; 339 mActivityRule.runOnUiThread(() -> mValueAnimator.setFrameDelay(frameDelay)); 340 startAnimation(mValueAnimator); 341 SystemClock.sleep(100); 342 mActivityRule.runOnUiThread(() -> { 343 long actualFrameDelay = mValueAnimator.getFrameDelay(); 344 assertEquals(frameDelay, actualFrameDelay); 345 }); 346 } 347 348 @Test testUpdateListeners()349 public void testUpdateListeners() throws Throwable { 350 final AnimatorSetTest.MyListener myListener = new AnimatorSetTest.MyListener(); 351 ValueAnimator.AnimatorUpdateListener l1 = mock(ValueAnimator.AnimatorUpdateListener.class); 352 ValueAnimator.AnimatorUpdateListener l2 = mock(ValueAnimator.AnimatorUpdateListener.class); 353 ValueAnimator.AnimatorUpdateListener l3 = mock(ValueAnimator.AnimatorUpdateListener.class); 354 ValueAnimator.AnimatorUpdateListener l4 = mock(ValueAnimator.AnimatorUpdateListener.class); 355 356 AnimatorListenerAdapter listener = mock(AnimatorListenerAdapter.class); 357 358 ValueAnimator a1 = ValueAnimator.ofFloat(0, 1f); 359 a1.setDuration(50); 360 a1.addUpdateListener(l1); 361 a1.addUpdateListener(l2); 362 a1.removeAllUpdateListeners(); 363 364 a1.addUpdateListener(l3); 365 a1.addUpdateListener(l4); 366 a1.removeUpdateListener(l3); 367 368 a1.addListener(listener); 369 a1.addListener(myListener); 370 371 mActivityRule.runOnUiThread(() -> { 372 a1.start(); 373 }); 374 375 // Wait for the anim to finish. 376 verify(listener, within(200)).onAnimationEnd(a1, false); 377 // Also make sure the onAnimationEnd(anim) is called. 378 assertTrue(myListener.mEndIsCalled); 379 380 verify(l1, times(0)).onAnimationUpdate(a1); 381 verify(l2, times(0)).onAnimationUpdate(a1); 382 verify(l3, times(0)).onAnimationUpdate(a1); 383 verify(l4, atLeast(1)).onAnimationUpdate(a1); 384 } 385 386 @Test testValuesSetterAndGetter()387 public void testValuesSetterAndGetter() throws Throwable { 388 389 final AnimatorSetTest.MyListener myListener = new AnimatorSetTest.MyListener(); 390 ValueAnimator a2 = ValueAnimator.ofPropertyValuesHolder(); 391 PropertyValuesHolder p1 = PropertyValuesHolder.ofFloat("scaleX", 0f, 1f); 392 PropertyValuesHolder p2 = PropertyValuesHolder.ofFloat("scaleY", 1f, 2f); 393 a2.setValues(p1, p2); 394 PropertyValuesHolder[] holders = a2.getValues(); 395 assertEquals(2, holders.length); 396 397 // Use the PropertyValueHolders returned from the getter to initialize the animator, in 398 // order to test the getter. 399 ValueAnimator a1 = ValueAnimator.ofPropertyValuesHolder(holders); 400 a1.setDuration(50); 401 a1.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 402 @Override 403 public void onAnimationUpdate(ValueAnimator animation) { 404 float scaleX = (Float) animation.getAnimatedValue("scaleX"); 405 float scaleY = (Float) animation.getAnimatedValue("scaleY"); 406 assertTrue(scaleX >= 0f && scaleX <= 1f); 407 assertTrue(scaleY >= 1f && scaleY <= 2f); 408 } 409 }); 410 AnimatorListenerAdapter l1 = mock(AnimatorListenerAdapter.class); 411 a1.addListener(l1); 412 a1.addListener(myListener); 413 414 mActivityRule.runOnUiThread(() -> { 415 a1.start(); 416 }); 417 418 verify(l1, within(200)).onAnimationEnd(a1, false); 419 // Also make sure the onAnimationEnd(anim) is called. 420 assertTrue(myListener.mEndIsCalled); 421 } 422 423 @Test testSetObjectValues()424 public void testSetObjectValues() throws Throwable { 425 TypeEvaluator<PointF> eval = new TypeEvaluator<PointF>() { 426 PointF tmpValue = new PointF(); 427 @Override 428 public PointF evaluate(float fraction, PointF startValue, PointF endValue) { 429 tmpValue.x = fraction * startValue.x + (1f - fraction) * endValue.x; 430 tmpValue.y = fraction * startValue.y + (1f - fraction) * endValue.y; 431 return tmpValue; 432 } 433 }; 434 435 final AnimatorSetTest.MyListener myListener = new AnimatorSetTest.MyListener(); 436 ValueAnimator a1 = new ValueAnimator(); 437 a1.setDuration(50); 438 a1.setObjectValues(new PointF(0, 0), new PointF(1, 1)); 439 a1.setEvaluator(eval); 440 a1.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 441 @Override 442 public void onAnimationUpdate(ValueAnimator animation) { 443 PointF point = (PointF) animation.getAnimatedValue(); 444 assertTrue(point.x >= 0f && point.x <= 1f); 445 assertTrue(point.y >= 0f && point.y <= 1f); 446 } 447 }); 448 AnimatorListenerAdapter l1 = mock(AnimatorListenerAdapter.class); 449 a1.addListener(l1); 450 a1.addListener(myListener); 451 mActivityRule.runOnUiThread(() -> { 452 a1.start(); 453 }); 454 455 verify(l1, within(200)).onAnimationEnd(a1, false); 456 // Also make sure the onAnimationEnd(anim) is called. 457 assertTrue(myListener.mEndIsCalled); 458 } 459 460 @Test testSetInterpolator()461 public void testSetInterpolator() throws Throwable { 462 AccelerateInterpolator interpolator = new AccelerateInterpolator(); 463 ValueAnimator mValueAnimator = mActivity.createAnimatorWithInterpolator(interpolator); 464 startAnimation(mValueAnimator); 465 assertTrue(interpolator.equals(mValueAnimator.getInterpolator())); 466 } 467 468 @Test testCancel()469 public void testCancel() throws Throwable { 470 startAnimation(mValueAnimator); 471 SystemClock.sleep(100); 472 cancelAnimation(mValueAnimator); 473 assertFalse(mValueAnimator.isRunning()); 474 } 475 476 @Test testEnd()477 public void testEnd() throws Throwable { 478 Object object = mActivity.view.newBall; 479 String property = "y"; 480 float startY = mActivity.mStartY; 481 float endY = mActivity.mStartY + mActivity.mDeltaY; 482 ObjectAnimator objAnimator = ObjectAnimator.ofFloat(object, property, startY, endY); 483 objAnimator.setDuration(mDuration); 484 objAnimator.setRepeatCount(ValueAnimator.INFINITE); 485 objAnimator.setInterpolator(new AccelerateInterpolator()); 486 objAnimator.setRepeatMode(ValueAnimator.REVERSE); 487 startAnimation(objAnimator); 488 SystemClock.sleep(100); 489 endAnimation(objAnimator); 490 float y = mActivity.view.newBall.getY(); 491 assertEquals(y, endY, 0.0f); 492 } 493 494 @Test testGetAnimatedFraction()495 public void testGetAnimatedFraction() throws Throwable { 496 ValueAnimator animator = ValueAnimator.ofFloat(0f, 1f); 497 assertNotNull(animator); 498 animator.setDuration(200); 499 animator.addUpdateListener(new AnimatorUpdateListener() { 500 public float lastFraction = 0; 501 @Override 502 public void onAnimationUpdate(ValueAnimator animation) { 503 float currentFraction = animation.getAnimatedFraction(); 504 assertTrue( 505 "Last fraction = " + lastFraction + "current fraction = " + currentFraction, 506 animation.getAnimatedFraction() >= lastFraction); 507 lastFraction = currentFraction; 508 assertTrue(currentFraction <= 1f); 509 } 510 }); 511 CountDownLatch latch = new CountDownLatch(1); 512 animator.addListener(new AnimatorListenerAdapter() { 513 @Override 514 public void onAnimationEnd(Animator animation) { 515 latch.countDown(); 516 } 517 }); 518 mActivityRule.runOnUiThread(() -> { 519 animator.start(); 520 }); 521 522 latch.await(1000, TimeUnit.MILLISECONDS); 523 524 assertEquals(1.0f, animator.getAnimatedFraction(), EPSILON); 525 } 526 527 class TestInterpolator implements TimeInterpolator { 528 529 @Override getInterpolation(float input)530 public float getInterpolation(float input) { 531 return input * input; 532 } 533 } 534 535 @Test testGetAnimatedValue()536 public void testGetAnimatedValue() { 537 ValueAnimator animator = ValueAnimator.ofFloat(0f, 1f); 538 assertNotNull(animator); 539 TimeInterpolator myInterpolator = new TestInterpolator(); 540 animator.setInterpolator(myInterpolator); 541 int sliceNum = 10; 542 for (int i = 0; i <= sliceNum; i++) { 543 float fraction = i / (float) sliceNum; 544 animator.setCurrentFraction(fraction); 545 assertEquals(myInterpolator.getInterpolation(fraction), 546 (float) animator.getAnimatedValue(), EPSILON); 547 548 } 549 } 550 551 @Test testGetAnimatedValue_PropertyName()552 public void testGetAnimatedValue_PropertyName() { 553 PropertyValuesHolder pvhX = PropertyValuesHolder.ofFloat("x", 100f, -100f); 554 PropertyValuesHolder pvhY = PropertyValuesHolder.ofFloat("y", 0f, 1f); 555 ValueAnimator animator = ValueAnimator.ofPropertyValuesHolder(pvhX, pvhY); 556 assertNotNull(animator); 557 TimeInterpolator myInterpolator = new TestInterpolator(); 558 animator.setInterpolator(myInterpolator); 559 int sliceNum = 10; 560 for (int i = 0; i <= sliceNum; i++) { 561 float fraction = i / (float) sliceNum; 562 animator.setCurrentFraction(fraction); 563 assertEquals(myInterpolator.getInterpolation(fraction), 564 (float) animator.getAnimatedValue("y"), EPSILON); 565 566 } 567 } 568 569 @Test testOfFloat()570 public void testOfFloat() throws Throwable { 571 float start = 0.0f; 572 float end = 1.0f; 573 float[] values = {start, end}; 574 final ValueAnimator valueAnimatorLocal = ValueAnimator.ofFloat(values); 575 valueAnimatorLocal.setDuration(mDuration); 576 valueAnimatorLocal.setRepeatCount(ValueAnimator.INFINITE); 577 valueAnimatorLocal.setInterpolator(new AccelerateInterpolator()); 578 valueAnimatorLocal.setRepeatMode(ValueAnimator.RESTART); 579 580 mActivityRule.runOnUiThread(valueAnimatorLocal::start); 581 SystemClock.sleep(100); 582 boolean isRunning = valueAnimatorLocal.isRunning(); 583 assertTrue(isRunning); 584 585 Float animatedValue = (Float) valueAnimatorLocal.getAnimatedValue(); 586 assertTrue(animatedValue >= start); 587 assertTrue(animatedValue <= end); 588 } 589 590 @Test testOfInt()591 public void testOfInt() throws Throwable { 592 int start = 0; 593 int end = 10; 594 int[] values = {start, end}; 595 final ValueAnimator valueAnimatorLocal = ValueAnimator.ofInt(values); 596 valueAnimatorLocal.setDuration(mDuration); 597 valueAnimatorLocal.setRepeatCount(ValueAnimator.INFINITE); 598 valueAnimatorLocal.setInterpolator(new AccelerateInterpolator()); 599 valueAnimatorLocal.setRepeatMode(ValueAnimator.RESTART); 600 601 mActivityRule.runOnUiThread(valueAnimatorLocal::start); 602 SystemClock.sleep(100); 603 boolean isRunning = valueAnimatorLocal.isRunning(); 604 assertTrue(isRunning); 605 606 Integer animatedValue = (Integer) valueAnimatorLocal.getAnimatedValue(); 607 assertTrue(animatedValue >= start); 608 assertTrue(animatedValue <= end); 609 } 610 611 @Test testOfArgb()612 public void testOfArgb() throws Throwable { 613 int start = 0xffff0000; 614 int end = 0xff0000ff; 615 int[] values = {start, end}; 616 int startRed = Color.red(start); 617 int startBlue = Color.blue(start); 618 int endRed = Color.red(end); 619 int endBlue = Color.blue(end); 620 final ValueAnimator valueAnimatorLocal = ValueAnimator.ofArgb(values); 621 valueAnimatorLocal.setDuration(mDuration); 622 623 final CountDownLatch latch = new CountDownLatch(1); 624 valueAnimatorLocal.addUpdateListener((ValueAnimator animation) -> { 625 if (animation.getAnimatedFraction() > .05f) { 626 latch.countDown(); 627 } 628 }); 629 630 mActivityRule.runOnUiThread(valueAnimatorLocal::start); 631 boolean isRunning = valueAnimatorLocal.isRunning(); 632 assertTrue(isRunning); 633 634 assertTrue(latch.await(500, TimeUnit.MILLISECONDS)); 635 636 Integer animatedValue = (Integer) valueAnimatorLocal.getAnimatedValue(); 637 int alpha = Color.alpha(animatedValue); 638 int red = Color.red(animatedValue); 639 int green = Color.green(animatedValue); 640 int blue = Color.blue(animatedValue); 641 assertTrue(red < startRed); 642 assertTrue(red > endRed); 643 assertTrue(blue > startBlue); 644 assertTrue(blue < endBlue); 645 assertEquals(255, alpha); 646 assertEquals(0, green); 647 648 mActivityRule.runOnUiThread(valueAnimatorLocal::cancel); 649 } 650 651 @Test 652 public void testNoDelayOnSeekAnimation() throws Throwable { 653 ValueAnimator animator = ValueAnimator.ofFloat(0, 1); 654 animator.setInterpolator(new LinearInterpolator()); 655 animator.setStartDelay(1000); 656 animator.setDuration(300); 657 animator.setCurrentPlayTime(150); 658 final Animator.AnimatorListener watcher = mock(Animator.AnimatorListener.class); 659 animator.addListener(watcher); 660 mActivityRule.runOnUiThread(animator::start); 661 verify(watcher, times(1)).onAnimationStart(animator, false); 662 assertTrue(((Float)animator.getAnimatedValue()) >= 0.5f); 663 assertTrue(animator.getAnimatedFraction() >= 0.5f); 664 mActivityRule.runOnUiThread(animator::cancel); 665 } 666 667 @Test testNotifiesAfterEnd()668 public void testNotifiesAfterEnd() throws Throwable { 669 final ValueAnimator animator = ValueAnimator.ofFloat(0, 1); 670 animator.addListener(new AnimatorListenerAdapter() { 671 @Override 672 public void onAnimationStart(Animator animation) { 673 assertTrue(animation.isStarted()); 674 assertTrue(animation.isRunning()); 675 } 676 677 @Override 678 public void onAnimationEnd(Animator animation) { 679 assertFalse(animation.isRunning()); 680 assertFalse(animation.isStarted()); 681 super.onAnimationEnd(animation); 682 } 683 }); 684 mActivityRule.runOnUiThread(() -> { 685 animator.start(); 686 animator.end(); 687 }); 688 } 689 690 @Test testAnimatorsEnabled()691 public void testAnimatorsEnabled() throws Throwable { 692 float currentDurationScale = ValueAnimator.getDurationScale(); 693 try { 694 testAnimatorsEnabledImpl(true); 695 testAnimatorsEnabledImpl(false); 696 } finally { 697 // restore scale value to avoid messing up future tests 698 ValueAnimator.setDurationScale(currentDurationScale); 699 } 700 } 701 702 @Test testAnimationDurationNoShortenByTinkeredScale()703 public void testAnimationDurationNoShortenByTinkeredScale() throws Throwable { 704 final long expectedDurationMs = 1000L; 705 final long minDurationMs = expectedDurationMs; 706 final long maxDurationMs = expectedDurationMs + 200L; 707 final Range<Long> durationRange = new Range<>(minDurationMs, maxDurationMs); 708 709 final CountDownLatch endLatch = new CountDownLatch(1); 710 long[] startAnimationTime = new long[1]; 711 long[] endAnimationTime = new long[1]; 712 713 final float durationScale = 1.0f; 714 float currentDurationScale = ValueAnimator.getDurationScale(); 715 try { 716 ValueAnimator.setDurationScale(durationScale); 717 assertTrue("The duration scale of ValueAnimator should be 1.0f," 718 + " actual=" + ValueAnimator.getDurationScale(), 719 ValueAnimator.getDurationScale() == durationScale); 720 721 ValueAnimator animator = ValueAnimator.ofFloat(0, 1); 722 animator.setInterpolator(new LinearInterpolator()); 723 animator.setDuration(expectedDurationMs); 724 assertEquals(animator.getDuration(), expectedDurationMs); 725 726 animator.addListener(new AnimatorListenerAdapter() { 727 @Override 728 public void onAnimationEnd(Animator animation) { 729 endAnimationTime[0] = SystemClock.uptimeMillis(); 730 endLatch.countDown(); 731 } 732 }); 733 734 // Start the animation and verify if the actual animation duration is in the range. 735 mActivityRule.runOnUiThread(() -> { 736 startAnimationTime[0] = SystemClock.uptimeMillis(); 737 animator.start(); 738 }); 739 endLatch.await(2, TimeUnit.SECONDS); 740 final long totalTime = endAnimationTime[0] - startAnimationTime[0]; 741 assertTrue("ValueAnimator the duration should be in the range " 742 + "<" + minDurationMs + ", " + maxDurationMs + "> ms, " 743 + "actual=" + totalTime, durationRange.contains(totalTime)); 744 } finally { 745 // restore scale value to avoid messing up future tests 746 ValueAnimator.setDurationScale(currentDurationScale); 747 } 748 } 749 testAnimatorsEnabledImpl(boolean enabled)750 private void testAnimatorsEnabledImpl(boolean enabled) throws Throwable { 751 final CountDownLatch startLatch = new CountDownLatch(1); 752 final CountDownLatch endLatch = new CountDownLatch(1); 753 final ValueAnimator animator = ValueAnimator.ofFloat(0, 1); 754 755 animator.setDuration(1000); 756 animator.addListener(new AnimatorListenerAdapter() { 757 @Override 758 public void onAnimationEnd(Animator animation) { 759 endLatch.countDown(); 760 } 761 }); 762 mActivityRule.runOnUiThread(() -> { 763 animator.start(); 764 startLatch.countDown(); 765 }); 766 767 assertTrue(startLatch.await(200, TimeUnit.MILLISECONDS)); 768 769 float durationScale = enabled ? 1 : 0; 770 ValueAnimator.setDurationScale(durationScale); 771 772 if (enabled) { 773 assertTrue("Animators not enabled with duration scale 1", 774 ValueAnimator.areAnimatorsEnabled()); 775 assertFalse("Animator ended too early when animators enabled = ", 776 endLatch.await(100, TimeUnit.MILLISECONDS)); 777 } else { 778 assertFalse("Animators enabled with duration scale 0", 779 ValueAnimator.areAnimatorsEnabled()); 780 assertTrue("Animator did not end when animators enabled = ", 781 endLatch.await(100, TimeUnit.MILLISECONDS)); 782 } 783 mActivityRule.runOnUiThread(() -> { 784 animator.end(); 785 }); 786 } 787 788 @Test testRegisterAndUnregisterDurationScaleListener()789 public void testRegisterAndUnregisterDurationScaleListener() { 790 ValueAnimator.DurationScaleChangeListener listener = scale -> { 791 return; 792 }; 793 assertTrue("Listener not registered", 794 ValueAnimator.registerDurationScaleChangeListener(listener)); 795 assertFalse("Listener was registered again", 796 ValueAnimator.registerDurationScaleChangeListener(listener)); 797 assertTrue("Listener not unregistered", 798 ValueAnimator.unregisterDurationScaleChangeListener(listener)); 799 assertFalse("Listener was unregistered again", 800 ValueAnimator.unregisterDurationScaleChangeListener(listener)); 801 } 802 803 @Test testGetDurationScale()804 public void testGetDurationScale() { 805 float currentDurationScale = ValueAnimator.getDurationScale(); 806 try { 807 ValueAnimator.setDurationScale(0f); 808 assertEquals(0f, ValueAnimator.getDurationScale(), 0.0f); 809 } finally { 810 // restore scale value to avoid messing up future tests 811 ValueAnimator.setDurationScale(currentDurationScale); 812 } 813 814 } 815 816 @Test testDurationScaleListenerOnChange()817 public void testDurationScaleListenerOnChange() throws InterruptedException { 818 float currentDurationScale = ValueAnimator.getDurationScale(); 819 820 ValueAnimator.setDurationScale(1f); 821 final CountDownLatch durationScaleUpdateLatch = new CountDownLatch(1); 822 ValueAnimator.DurationScaleChangeListener listener = scale -> { 823 assertEquals(0f, ValueAnimator.getDurationScale(), 0.0f); 824 durationScaleUpdateLatch.countDown(); 825 }; 826 827 try { 828 ValueAnimator.registerDurationScaleChangeListener(listener); 829 ValueAnimator.setDurationScale(0f); 830 assertTrue(durationScaleUpdateLatch.await(100, TimeUnit.MILLISECONDS)); 831 } finally { 832 ValueAnimator.unregisterDurationScaleChangeListener(listener); 833 // restore scale value to avoid messing up future tests 834 ValueAnimator.setDurationScale(currentDurationScale); 835 } 836 } 837 getAnimator()838 private ValueAnimator getAnimator() { 839 Object object = mActivity.view.newBall; 840 String property = "y"; 841 float startY = mActivity.mStartY; 842 float endY = mActivity.mStartY + mActivity.mDeltaY; 843 ValueAnimator objAnimator = ObjectAnimator.ofFloat(object, property, startY, endY); 844 objAnimator.setDuration(mDuration); 845 objAnimator.setRepeatCount(ValueAnimator.INFINITE); 846 objAnimator.setInterpolator(new AccelerateInterpolator()); 847 objAnimator.setRepeatMode(ValueAnimator.REVERSE); 848 return objAnimator; 849 } 850 startAnimation(final ValueAnimator animator)851 private void startAnimation(final ValueAnimator animator) throws Throwable { 852 mActivityRule.runOnUiThread(() -> mActivity.startAnimation(animator)); 853 } 854 endAnimation(final ValueAnimator animator)855 private void endAnimation(final ValueAnimator animator) throws Throwable { 856 mActivityRule.runOnUiThread(animator::end); 857 } 858 cancelAnimation(final ValueAnimator animator)859 private void cancelAnimation(final ValueAnimator animator) throws Throwable { 860 mActivityRule.runOnUiThread(animator::cancel); 861 } 862 errorMessage(float[] values)863 private String errorMessage(float[] values) { 864 StringBuilder message = new StringBuilder(); 865 for (int i = 0; i < values.length; i++) { 866 message.append(values[i]).append(" "); 867 } 868 return message.toString(); 869 } 870 } 871