1 /* 2 * Copyright (C) 2015 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.transition.cts; 17 18 import static com.android.compatibility.common.util.CtsMockitoUtils.within; 19 20 import static org.hamcrest.CoreMatchers.allOf; 21 import static org.hamcrest.Matchers.greaterThanOrEqualTo; 22 import static org.hamcrest.Matchers.lessThan; 23 import static org.junit.Assert.assertEquals; 24 import static org.junit.Assert.assertFalse; 25 import static org.junit.Assert.assertNotNull; 26 import static org.junit.Assert.assertNull; 27 import static org.junit.Assert.assertSame; 28 import static org.junit.Assert.assertThat; 29 import static org.junit.Assert.assertTrue; 30 import static org.junit.Assert.fail; 31 import static org.mockito.Matchers.any; 32 import static org.mockito.Matchers.eq; 33 import static org.mockito.Mockito.doAnswer; 34 import static org.mockito.Mockito.mock; 35 import static org.mockito.Mockito.never; 36 import static org.mockito.Mockito.times; 37 import static org.mockito.Mockito.verify; 38 39 import android.animation.Animator; 40 import android.graphics.Rect; 41 import android.os.SystemClock; 42 import android.transition.ArcMotion; 43 import android.transition.AutoTransition; 44 import android.transition.ChangeBounds; 45 import android.transition.CircularPropagation; 46 import android.transition.PathMotion; 47 import android.transition.Scene; 48 import android.transition.Transition; 49 import android.transition.Transition.EpicenterCallback; 50 import android.transition.Transition.TransitionListener; 51 import android.transition.TransitionListenerAdapter; 52 import android.transition.TransitionManager; 53 import android.transition.TransitionPropagation; 54 import android.transition.TransitionValues; 55 import android.view.View; 56 import android.view.ViewGroup; 57 import android.view.ViewTreeObserver; 58 import android.view.animation.AccelerateInterpolator; 59 import android.widget.ImageView; 60 import android.widget.RelativeLayout; 61 import android.widget.TextView; 62 63 import androidx.test.filters.MediumTest; 64 import androidx.test.runner.AndroidJUnit4; 65 66 import org.junit.Test; 67 import org.junit.runner.RunWith; 68 import org.mockito.invocation.InvocationOnMock; 69 70 import java.util.HashMap; 71 import java.util.concurrent.CountDownLatch; 72 import java.util.concurrent.TimeUnit; 73 74 @MediumTest 75 @RunWith(AndroidJUnit4.class) 76 public class TransitionTest extends BaseTransitionTest { 77 @Override setup()78 public void setup() { 79 super.setup(); 80 // We want to be able to catch the transition in the middle of running, so 81 // it should be long enough that devices can catch it without trouble. 82 mTransition.setDuration(1000); 83 } 84 85 @Test testAddListener()86 public void testAddListener() throws Throwable { 87 startTransition(R.layout.scene1); 88 waitForStart(); 89 90 final Transition.TransitionListener listener2 = mock(Transition.TransitionListener.class); 91 92 mActivityRule.runOnUiThread(() -> { 93 AutoTransition autoTransition = new AutoTransition(); 94 autoTransition.setDuration(100); 95 autoTransition.addListener(listener2); 96 Scene scene = Scene.getSceneForLayout(mSceneRoot, R.layout.scene2, mActivity); 97 TransitionManager.go(scene, autoTransition); 98 }); 99 100 waitForStart(listener2); 101 102 verify(mListener, times(1)).onTransitionPause(any()); 103 verify(mListener, times(1)).onTransitionResume(any()); 104 verify(mListener, never()).onTransitionCancel(any()); 105 verify(mListener, never()).onTransitionEnd(any()); 106 verify(mListener, times(1)).onTransitionStart(any()); 107 108 verify(listener2, never()).onTransitionPause(any()); 109 verify(listener2, never()).onTransitionResume(any()); 110 verify(listener2, never()).onTransitionCancel(any()); 111 verify(listener2, never()).onTransitionEnd(any()); 112 verify(listener2, times(1)).onTransitionStart(any()); 113 endTransition(); 114 } 115 116 @Test testRemoveListener()117 public void testRemoveListener() throws Throwable { 118 TransitionListener listener = mock(TransitionListener.class); 119 mTransition.addListener(listener); 120 startTransition(R.layout.scene1); 121 waitForStart(); 122 123 mActivityRule.runOnUiThread(() -> mTransition.removeListener(listener)); 124 waitForEnd(2000); 125 mActivityRule.runOnUiThread(() -> verify(listener, never()).onTransitionEnd(any())); 126 } 127 128 @Test testAddTargetId()129 public void testAddTargetId() throws Throwable { 130 enterScene(R.layout.scene4); 131 assertNotNull(mTransition.getTargetIds()); 132 assertTrue(mTransition.getTargetIds().isEmpty()); 133 mTransition.addTarget(R.id.holder); 134 mTransition.addTarget(R.id.hello); 135 assertEquals(2, mTransition.getTargetIds().size()); 136 startTransition(R.layout.scene1); 137 assertEquals(1, mTargets.size()); 138 assertEquals(R.id.hello, mTargets.get(0).getId()); 139 endTransition(); 140 } 141 142 @Test testRemoveTargetId()143 public void testRemoveTargetId() throws Throwable { 144 enterScene(R.layout.scene4); 145 mTransition.addTarget(R.id.holder); 146 mTransition.addTarget(R.id.hello); 147 mTransition.addTarget(R.id.redSquare); 148 assertEquals(3, mTransition.getTargetIds().size()); 149 mTransition.removeTarget(0); // nothing should happen 150 mTransition.removeTarget(R.id.redSquare); 151 assertEquals(2, mTransition.getTargetIds().size()); 152 153 startTransition(R.layout.scene1); 154 assertEquals(1, mTargets.size()); 155 assertEquals(R.id.hello, mTargets.get(0).getId()); 156 endTransition(); 157 } 158 159 @Test testAddTargetClass()160 public void testAddTargetClass() throws Throwable { 161 enterScene(R.layout.scene4); 162 assertNull(mTransition.getTargetTypes()); 163 mTransition.addTarget(RelativeLayout.class); 164 mTransition.addTarget(TextView.class); 165 assertEquals(2, mTransition.getTargetTypes().size()); 166 startTransition(R.layout.scene1); 167 assertEquals(1, mTargets.size()); 168 assertTrue(mTargets.get(0) instanceof TextView); 169 endTransition(); 170 } 171 172 @Test testRemoveTargetClass()173 public void testRemoveTargetClass() throws Throwable { 174 enterScene(R.layout.scene4); 175 mTransition.addTarget(TextView.class); 176 mTransition.addTarget(View.class); 177 mTransition.addTarget(RelativeLayout.class); 178 assertEquals(3, mTransition.getTargetTypes().size()); 179 mTransition.removeTarget(ImageView.class); // should do nothing 180 mTransition.removeTarget(View.class); 181 assertEquals(2, mTransition.getTargetTypes().size()); 182 startTransition(R.layout.scene1); 183 assertEquals(1, mTargets.size()); 184 assertTrue(mTargets.get(0) instanceof TextView); 185 endTransition(); 186 } 187 188 @Test testAddTargetView()189 public void testAddTargetView() throws Throwable { 190 enterScene(R.layout.scene1); 191 192 final View[] target = new View[1]; 193 mActivityRule.runOnUiThread(() -> target[0] = mActivity.findViewById(R.id.hello)); 194 mTransition.addTarget(target[0]); 195 assertEquals(1, mTransition.getTargets().size()); 196 mActivityRule.runOnUiThread(() -> { 197 TransitionManager.beginDelayedTransition(mSceneRoot, mTransition); 198 target[0].setVisibility(View.GONE); 199 }); 200 waitForStart(); 201 assertEquals(1, mTargets.size()); 202 assertEquals(target[0], mTargets.get(0)); 203 endTransition(); 204 } 205 206 @Test testRemoveTargetView()207 public void testRemoveTargetView() throws Throwable { 208 enterScene(R.layout.scene1); 209 210 final View[] target = new View[3]; 211 mActivityRule.runOnUiThread(() -> { 212 target[0] = mActivity.findViewById(R.id.hello); 213 target[1] = mActivity.findViewById(R.id.greenSquare); 214 target[2] = mActivity.findViewById(R.id.redSquare); 215 }); 216 217 mTransition.addTarget(target[0]); 218 mTransition.addTarget(target[1]); 219 assertEquals(2, mTransition.getTargets().size()); 220 mTransition.removeTarget(target[2]); // should do nothing 221 mTransition.removeTarget(target[1]); 222 assertEquals(1, mTransition.getTargets().size()); 223 mActivityRule.runOnUiThread(() -> { 224 TransitionManager.beginDelayedTransition(mSceneRoot, mTransition); 225 target[0].setVisibility(View.GONE); 226 }); 227 waitForStart(); 228 assertEquals(1, mTargets.size()); 229 assertEquals(target[0], mTargets.get(0)); 230 endTransition(); 231 } 232 233 @Test testAddTargetName()234 public void testAddTargetName() throws Throwable { 235 enterScene(R.layout.scene4); 236 assertNull(mTransition.getTargetNames()); 237 mTransition.addTarget("red"); 238 mTransition.addTarget("holder"); 239 assertEquals(2, mTransition.getTargetNames().size()); 240 assertEquals(0, mTargets.size()); 241 startTransition(R.layout.scene2); 242 assertEquals(1, mTargets.size()); 243 assertEquals(R.id.redSquare, mTargets.get(0).getId()); 244 endTransition(); 245 } 246 247 @Test testRemoveTargetName()248 public void testRemoveTargetName() throws Throwable { 249 enterScene(R.layout.scene4); 250 mTransition.addTarget("holder"); 251 mTransition.addTarget("red"); 252 mTransition.addTarget("green"); 253 assertEquals(3, mTransition.getTargetNames().size()); 254 mTransition.removeTarget("purple"); // should do nothing 255 // try to force a different String instance 256 String greenName = new StringBuilder("gre").append("en").toString(); 257 mTransition.removeTarget(greenName); 258 assertEquals(2, mTransition.getTargetNames().size()); 259 startTransition(R.layout.scene1); 260 assertEquals(1, mTargets.size()); 261 assertEquals(R.id.redSquare, mTargets.get(0).getId()); 262 endTransition(); 263 } 264 265 @Test testIsTransitionRequired()266 public void testIsTransitionRequired() throws Throwable { 267 enterScene(R.layout.scene1); 268 mTransition = new NotRequiredTransition(); 269 assertFalse(mTransition.isTransitionRequired(null, null)); 270 resetListener(); 271 mActivityRule.runOnUiThread(() -> { 272 TransitionManager.beginDelayedTransition(mSceneRoot, mTransition); 273 mActivity.findViewById(R.id.hello).setVisibility(View.GONE); 274 }); 275 waitForStart(); 276 assertEquals(0, mTargets.size()); 277 endTransition(); 278 } 279 280 @Test testCanRemoveViews()281 public void testCanRemoveViews() throws Throwable { 282 enterScene(R.layout.scene1); 283 assertFalse(mTransition.canRemoveViews()); 284 mTransition.addListener(new TransitionListener() { 285 @Override 286 public void onTransitionStart(Transition transition) { 287 assertTrue(transition.canRemoveViews()); 288 } 289 290 @Override 291 public void onTransitionEnd(Transition transition) { 292 } 293 294 @Override 295 public void onTransitionCancel(Transition transition) { 296 } 297 298 @Override 299 public void onTransitionPause(Transition transition) { 300 } 301 302 @Override 303 public void onTransitionResume(Transition transition) { 304 } 305 }); 306 startTransition(R.layout.scene2); 307 } 308 309 @Test testExcludeChildrenView()310 public void testExcludeChildrenView() throws Throwable { 311 View layout1 = loadLayout(R.layout.scene1); 312 Scene scene1 = loadScene(layout1); 313 enterScene(scene1); 314 View holder1 = layout1.findViewById(R.id.holder); 315 mTransition.excludeChildren(holder1, true); 316 View layout2 = loadLayout(R.layout.scene2); 317 Scene scene2 = loadScene(layout2); 318 View holder2 = layout2.findViewById(R.id.holder); 319 mTransition.excludeChildren(holder2, true); 320 startTransition(scene2); 321 mActivityRule.runOnUiThread(() -> { 322 // Should already be ended, since no children are transitioning 323 verify(mListener, times(1)).onTransitionEnd(any()); 324 }); 325 326 mTransition.excludeChildren(holder1, false); // remove it 327 mTransition.excludeChildren(holder2, false); // remove it 328 resetListener(); 329 startTransition(scene1); 330 verify(mListener, never()).onTransitionEnd(any()); // it is running as expected 331 endTransition(); 332 } 333 334 @Test testExcludeChildrenId()335 public void testExcludeChildrenId() throws Throwable { 336 enterScene(R.layout.scene1); 337 mTransition.excludeChildren(R.id.holder, true); 338 startTransition(R.layout.scene2); 339 mActivityRule.runOnUiThread(() -> { 340 // Should already be ended, since no children are transitioning 341 verify(mListener, times(1)).onTransitionEnd(any()); 342 }); 343 344 resetListener(); 345 mTransition.excludeChildren(R.id.holder, false); // remove it 346 startTransition(R.layout.scene1); 347 verify(mListener, never()).onTransitionEnd(any()); // it is running as expected 348 endTransition(); 349 } 350 351 @Test testExcludeChildrenClass()352 public void testExcludeChildrenClass() throws Throwable { 353 enterScene(R.layout.scene1); 354 mTransition.excludeChildren(RelativeLayout.class, true); 355 startTransition(R.layout.scene2); 356 mActivityRule.runOnUiThread(() -> { 357 // Should already be ended, since no children are transitioning 358 verify(mListener, times(1)).onTransitionEnd(any()); 359 }); 360 361 resetListener(); 362 mTransition.excludeChildren(RelativeLayout.class, false); // remove it 363 startTransition(R.layout.scene1); 364 verify(mListener, never()).onTransitionEnd(any()); // it is running as expected 365 endTransition(); 366 } 367 368 @Test testExcludeTargetView()369 public void testExcludeTargetView() throws Throwable { 370 View layout1 = loadLayout(R.layout.scene1); 371 Scene scene1 = loadScene(layout1); 372 enterScene(R.layout.scene1); 373 View redSquare1 = layout1.findViewById(R.id.redSquare); 374 mTransition.excludeTarget(redSquare1, true); 375 startTransition(R.layout.scene7); 376 waitForEnd(2000); 377 378 mTransition.excludeTarget(redSquare1, false); // remove it 379 resetListener(); 380 startTransition(scene1); 381 verify(mListener, never()).onTransitionEnd(any()); // it is running as expected 382 endTransition(); 383 } 384 385 @Test testExcludeTargetId()386 public void testExcludeTargetId() throws Throwable { 387 enterScene(R.layout.scene1); 388 mTransition.excludeTarget(R.id.redSquare, true); 389 startTransition(R.layout.scene7); 390 mActivityRule.runOnUiThread(() -> { 391 // Should already be ended, since no children are transitioning 392 verify(mListener, times(1)).onTransitionEnd(any()); 393 }); 394 395 resetListener(); 396 mTransition.excludeTarget(R.id.redSquare, false); // remove it 397 startTransition(R.layout.scene1); 398 verify(mListener, never()).onTransitionEnd(any()); // it is running as expected 399 endTransition(); 400 } 401 402 @Test testExcludeTargetClass()403 public void testExcludeTargetClass() throws Throwable { 404 enterScene(R.layout.scene1); 405 mTransition.excludeTarget(TextView.class, true); 406 startTransition(R.layout.scene3); 407 mActivityRule.runOnUiThread(() -> { 408 // Should already be ended, since no children are transitioning 409 verify(mListener, times(1)).onTransitionEnd(any()); 410 }); 411 412 resetListener(); 413 mTransition.excludeTarget(TextView.class, false); // remove it 414 startTransition(R.layout.scene1); 415 verify(mListener, never()).onTransitionEnd(any()); // it is running as expected 416 endTransition(); 417 } 418 419 @Test testExcludeTargetName()420 public void testExcludeTargetName() throws Throwable { 421 enterScene(R.layout.scene1); 422 mTransition.excludeTarget("hello", true); 423 startTransition(R.layout.scene3); 424 mActivityRule.runOnUiThread(() -> { 425 // Should already be ended, since no children are transitioning 426 verify(mListener, times(1)).onTransitionEnd(any()); 427 }); 428 429 resetListener(); 430 mTransition.excludeTarget("hello", false); // remove it 431 startTransition(R.layout.scene1); 432 verify(mListener, never()).onTransitionEnd(any()); // it is running as expected 433 endTransition(); 434 } 435 436 @Test testDuration()437 public void testDuration() throws Throwable { 438 Transition transition = new AutoTransition(); 439 assertEquals(-1, transition.getDuration()); 440 enterScene(R.layout.scene1); 441 mTransition.setDuration(1000); 442 assertEquals(1000, mTransition.getDuration()); 443 DurationListener durationListener = new DurationListener(); 444 mTransition.addListener(durationListener); 445 startTransition(R.layout.scene3); 446 waitForEnd(5000); 447 // We can't be certain that the onTransitionStart() and onTransitionEnd() 448 // are going to be called exactly 1000ms apart. There could be more of a 449 // delay at the beginning than the end. So, we give it some room at the 450 // minimum. It can also take a lot longer on the larger side because of 451 // slow devices. 452 assertThat(durationListener.getDuration(), 453 allOf(greaterThanOrEqualTo(500L), lessThan(2000L))); 454 } 455 456 @Test testEpicenter()457 public void testEpicenter() throws Throwable { 458 assertNull(mTransition.getEpicenter()); 459 EpicenterCallback callback = new EpicenterCallback() { 460 @Override 461 public Rect onGetEpicenter(Transition transition) { 462 return new Rect(0, 0, 1, 1); 463 } 464 }; 465 mTransition.setEpicenterCallback(callback); 466 assertSame(callback, mTransition.getEpicenterCallback()); 467 assertEquals(new Rect(0, 0, 1, 1), mTransition.getEpicenter()); 468 } 469 470 @Test testInterpolator()471 public void testInterpolator() throws Throwable { 472 enterScene(R.layout.scene1); 473 View redSquare = mActivity.findViewById(R.id.redSquare); 474 CaptureAnimatorTransition transition = new CaptureAnimatorTransition(); 475 assertNull(transition.getInterpolator()); 476 AccelerateInterpolator interpolator = new AccelerateInterpolator(); 477 transition.setInterpolator(interpolator); 478 assertSame(interpolator, transition.getInterpolator()); 479 mTransition = transition; 480 resetListener(); 481 startTransition(R.layout.scene4); 482 assertFalse(transition.animators.isEmpty()); 483 Animator animator = transition.animators.get(redSquare); 484 Animator.AnimatorListener listener = transition.listeners.get(redSquare); 485 verify(listener, within(1000)).onAnimationStart(any(), eq(false)); 486 assertSame(interpolator, animator.getInterpolator()); 487 endTransition(); 488 } 489 490 @Test testName()491 public void testName() throws Throwable { 492 assertEquals("android.transition.cts.BaseTransitionTest$TestTransition", 493 mTransition.getName()); 494 } 495 496 @Test testPathMotion()497 public void testPathMotion() throws Throwable { 498 PathMotion pathMotion = new ArcMotion(); 499 mTransition.setPathMotion(pathMotion); 500 assertEquals(pathMotion, mTransition.getPathMotion()); 501 } 502 503 @Test testPropagation()504 public void testPropagation() throws Throwable { 505 enterScene(R.layout.scene1); 506 CaptureAnimatorTransition transition = new CaptureAnimatorTransition(); 507 mTransition = transition; 508 TransitionPropagation yPropagation = new TransitionPropagation() { 509 private static final String TOP = "top value"; 510 private final String[] PROPERTIES = {TOP}; 511 512 @Override 513 public long getStartDelay(ViewGroup viewGroup, Transition transition, 514 TransitionValues startValues, TransitionValues endValues) { 515 int startTop = startValues == null ? 0 : (Integer) startValues.values.get(TOP); 516 int endTop = endValues == null ? 0 : (Integer) endValues.values.get(TOP); 517 return (startTop == 0) ? endTop : startTop; 518 } 519 520 @Override 521 public void captureValues(TransitionValues transitionValues) { 522 if (transitionValues.view != null) { 523 transitionValues.values.put(TOP, transitionValues.view.getTop()); 524 } 525 } 526 527 @Override 528 public String[] getPropagationProperties() { 529 return PROPERTIES; 530 } 531 }; 532 mTransition.setPropagation(yPropagation); 533 resetListener(); 534 535 View redSquare = mActivity.findViewById(R.id.redSquare); 536 View greenSquare = mActivity.findViewById(R.id.greenSquare); 537 int diffTop = greenSquare.getTop() - redSquare.getTop(); 538 startTransition(R.layout.scene4); 539 Animator redSquareAnimator = transition.animators.get(redSquare); 540 Animator greenSquareAnimator = transition.animators.get(greenSquare); 541 Animator.AnimatorListener listener = transition.listeners.get(redSquare); 542 verify(listener, within(1000)).onAnimationStart(any(), eq(false)); 543 assertEquals(0, redSquareAnimator.getStartDelay()); 544 assertEquals(diffTop, greenSquareAnimator.getStartDelay()); 545 endTransition(); 546 } 547 548 @Test testSetPropagation()549 public void testSetPropagation() throws Throwable { 550 Transition transition = new ChangeBounds(); 551 assertNull(transition.getPropagation()); 552 TransitionPropagation propagation = new CircularPropagation(); 553 transition.setPropagation(propagation); 554 assertSame(propagation, transition.getPropagation()); 555 } 556 557 @Test testForceToEndTransitionsDependedEachOther()558 public void testForceToEndTransitionsDependedEachOther() throws Throwable { 559 final Transition.TransitionListener listener1 = mock(Transition.TransitionListener.class); 560 final Transition.TransitionListener listener2 = mock(Transition.TransitionListener.class); 561 562 Scene scene1 = loadScene(R.layout.scene1); 563 Scene scene2 = loadScene(R.layout.scene2); 564 565 final ViewGroup scene1Root = scene1.getSceneRoot(); 566 final ViewTreeObserver.OnPreDrawListener transition1OnPreDrawListener = 567 new ViewTreeObserver.OnPreDrawListener() { 568 public boolean onPreDraw() { 569 // Start the 2nd transition after the 1st transiton starts and does predraw. 570 Transition transition2 = new TestTransition(); 571 transition2.setDuration(1000); 572 transition2.addListener(listener2); 573 TransitionManager.go(scene2, transition2); 574 scene1Root.getViewTreeObserver().removeOnPreDrawListener(this); 575 return true; 576 } 577 }; 578 579 // Start the 1st transition. 580 mActivityRule.runOnUiThread( 581 () -> { 582 Transition transition1 = new TestTransition(); 583 transition1.setDuration(1000); 584 585 transition1.addListener(listener1); 586 TransitionManager.go(scene1, transition1); 587 scene1Root 588 .getViewTreeObserver() 589 .addOnPreDrawListener(transition1OnPreDrawListener); 590 }); 591 592 // When the 1st transition ends, end the other (2nd) transition if it is still alive. 593 doAnswer( 594 (InvocationOnMock invocation) -> { 595 TransitionManager.endTransitions( 596 mActivity.findViewById(R.id.container)); 597 return null; 598 }) 599 .when(listener1) 600 .onTransitionEnd(any()); 601 602 // When the 2st transition ends, end the other (1nd) transition if it is still alive. 603 doAnswer( 604 (InvocationOnMock invocation) -> { 605 TransitionManager.endTransitions( 606 mActivity.findViewById(R.id.container)); 607 return null; 608 }) 609 .when(listener2) 610 .onTransitionEnd(any()); 611 612 verify(listener1, within(4000)).onTransitionStart(any()); 613 verify(listener2, within(4000)).onTransitionStart(any()); 614 615 // End both transitions forcibly. 616 mActivityRule.runOnUiThread( 617 () -> { 618 TransitionManager.endTransitions(mActivity.findViewById(R.id.container)); 619 }); 620 621 verify(listener1, within(4000)).onTransitionEnd(any()); 622 verify(listener2, within(4000)).onTransitionEnd(any()); 623 } 624 625 @Test testStartDelay()626 public void testStartDelay() throws Throwable { 627 CaptureAnimatorTransition transition = new CaptureAnimatorTransition(); 628 mTransition = transition; 629 resetListener(); 630 enterScene(R.layout.scene1); 631 View redSquare = mActivity.findViewById(R.id.redSquare); 632 633 assertEquals(-1, mTransition.getStartDelay()); 634 mTransition.setStartDelay(200); 635 assertEquals(200, mTransition.getStartDelay()); 636 637 startTransition(R.layout.scene4); 638 639 Animator animator = transition.animators.get(redSquare); 640 assertFalse(animator.isRunning()); 641 Animator.AnimatorListener listener = transition.listeners.get(redSquare); 642 verify(listener, within(1000)).onAnimationStart(any(), eq(false)); 643 endTransition(); 644 } 645 646 @Test testTransitionValues()647 public void testTransitionValues() throws Throwable { 648 enterScene(R.layout.scene1); 649 CheckTransitionValuesTransition transition = new CheckTransitionValuesTransition(); 650 mTransition = transition; 651 mTransition.setDuration(10); 652 resetListener(); 653 startTransition(R.layout.scene2); 654 assertTrue(transition.onDisappearCalled.await(2000, TimeUnit.MILLISECONDS)); 655 assertTrue(transition.onAppearCalled.await(2000, TimeUnit.MILLISECONDS)); 656 // The transition has all the asserts in it, so we can just end it now. 657 endTransition(); 658 } 659 660 @Test testMatchOrder()661 public void testMatchOrder() throws Throwable { 662 mTransition = new ChangeBounds(); 663 resetListener(); 664 enterScene(R.layout.scene1); 665 startTransition(R.layout.scene8); 666 667 // scene 8 swaps the ids, but not the names. No transition should happen. 668 waitForEnd(2000); 669 670 // now change the match order to prefer the id 671 mTransition.setMatchOrder(new int[] {Transition.MATCH_ID, Transition.MATCH_NAME}); 672 673 resetListener(); 674 startTransition(R.layout.scene1); 675 verify(mListener, never()).onTransitionEnd(any()); // it is running as expected 676 waitForEnd(2000); 677 } 678 679 @Test testGetTransitionProperties()680 public void testGetTransitionProperties() throws Throwable { 681 enterScene(R.layout.scene1); 682 // Make the transition make changes to properties in getTransitionProperties. 683 TransitionPropertiesTransition transition = new TransitionPropertiesTransition(false); 684 mTransition = transition; 685 resetListener(); 686 startTransition(R.layout.scene2); 687 assertTrue(transition.latch.await(2000, TimeUnit.MILLISECONDS)); 688 endTransition(); 689 690 // Now make the transition only make changes to unimportant properties. 691 transition = new TransitionPropertiesTransition(true); 692 mTransition = transition; 693 resetListener(); 694 startTransition(R.layout.scene1); 695 verify(mListener, within(2000)).onTransitionEnd(any()); 696 // createAnimator shouldn't have been called. 697 assertEquals(1, transition.latch.getCount()); 698 699 assertNotNull(transition.getTransitionProperties()); 700 assertEquals(1, transition.getTransitionProperties().length); 701 } 702 703 @Test testGoWithNullParameter()704 public void testGoWithNullParameter() throws Throwable { 705 final View layout1 = loadLayout(R.layout.scene1); 706 final Scene scene1 = loadScene(layout1); 707 708 final View layout3 = loadLayout(R.layout.scene3); 709 final Scene scene3 = loadScene(layout3); 710 711 enterScene(scene1); 712 713 mActivityRule.runOnUiThread(() -> { 714 // scene1 715 assertSame(layout1, mActivity.findViewById(R.id.holder)); 716 assertNotNull(mActivity.findViewById(R.id.hello)); 717 718 TransitionManager.go(scene3, null); 719 // now at scene3 720 assertSame(layout3, mActivity.findViewById(R.id.holder)); 721 assertNull(mActivity.findViewById(R.id.hello)); 722 723 TransitionManager.go(scene1, null); 724 725 // now at scene1 726 assertSame(layout1, mActivity.findViewById(R.id.holder)); 727 assertNotNull(mActivity.findViewById(R.id.hello)); 728 }); 729 } 730 731 private class NotRequiredTransition extends TestTransition { 732 @Override isTransitionRequired(TransitionValues startValues, TransitionValues newValues)733 public boolean isTransitionRequired(TransitionValues startValues, 734 TransitionValues newValues) { 735 return false; 736 } 737 } 738 739 private class CaptureAnimatorTransition extends TestTransition { 740 public HashMap<View, Animator> animators = new HashMap<>(); 741 public HashMap<View, Animator.AnimatorListener> listeners = new HashMap<>(); 742 743 @Override onAppear(ViewGroup sceneRoot, View view, TransitionValues startValues, TransitionValues endValues)744 public Animator onAppear(ViewGroup sceneRoot, View view, TransitionValues startValues, 745 TransitionValues endValues) { 746 return setupAnimator(super.onAppear(sceneRoot, view, startValues, endValues), 747 endValues.view); 748 } 749 750 @Override onDisappear(ViewGroup sceneRoot, View view, TransitionValues startValues, TransitionValues endValues)751 public Animator onDisappear(ViewGroup sceneRoot, View view, TransitionValues startValues, 752 TransitionValues endValues) { 753 return setupAnimator(super.onDisappear(sceneRoot, view, startValues, endValues), 754 startValues.view); 755 } 756 setupAnimator(Animator animator, View view)757 private Animator setupAnimator(Animator animator, View view) { 758 animators.put(view, animator); 759 Animator.AnimatorListener listener = mock(Animator.AnimatorListener.class); 760 animator.addListener(listener); 761 listeners.put(view, listener); 762 return animator; 763 } 764 } 765 766 private class CheckTransitionValuesTransition extends TestTransition { 767 public CountDownLatch onAppearCalled = new CountDownLatch(1); 768 public CountDownLatch onDisappearCalled = new CountDownLatch(1); 769 @Override onAppear(ViewGroup sceneRoot, View view, TransitionValues startValues, TransitionValues endValues)770 public Animator onAppear(ViewGroup sceneRoot, View view, TransitionValues startValues, 771 TransitionValues endValues) { 772 onAppearCalled.countDown(); 773 assertNull(getTransitionValues(endValues.view, true)); 774 assertEquals(endValues, getTransitionValues(endValues.view, false)); 775 return super.onAppear(sceneRoot, view, startValues, endValues); 776 } 777 778 @Override onDisappear(ViewGroup sceneRoot, View view, TransitionValues startValues, TransitionValues endValues)779 public Animator onDisappear(ViewGroup sceneRoot, View view, TransitionValues startValues, 780 TransitionValues endValues) { 781 onDisappearCalled.countDown(); 782 assertNull(getTransitionValues(startValues.view, false)); 783 assertEquals(startValues, getTransitionValues(startValues.view, true)); 784 return super.onDisappear(sceneRoot, view, startValues, endValues); 785 } 786 } 787 788 private static class TransitionPropertiesTransition extends Transition { 789 private static final String SIDE_PROP = "prop1"; 790 private static final String IMPORTANT_PROP = "prop2"; 791 private static final String[] PROPERTIES = { 792 IMPORTANT_PROP 793 }; 794 795 private boolean mOnlyUnimportant; 796 public CountDownLatch latch = new CountDownLatch(1); 797 TransitionPropertiesTransition(boolean onlyUnimportant)798 public TransitionPropertiesTransition(boolean onlyUnimportant) { 799 mOnlyUnimportant = onlyUnimportant; 800 } 801 802 @Override getTransitionProperties()803 public String[] getTransitionProperties() { 804 return PROPERTIES; 805 } 806 807 @Override captureStartValues(TransitionValues transitionValues)808 public void captureStartValues(TransitionValues transitionValues) { 809 transitionValues.values.put(SIDE_PROP, 1); 810 transitionValues.values.put(IMPORTANT_PROP, 1); 811 } 812 813 @Override captureEndValues(TransitionValues transitionValues)814 public void captureEndValues(TransitionValues transitionValues) { 815 transitionValues.values.put(SIDE_PROP, 2); 816 int val = mOnlyUnimportant ? 1 : 2; 817 transitionValues.values.put(IMPORTANT_PROP, val); 818 } 819 820 @Override createAnimator(ViewGroup sceneRoot, TransitionValues startValues, TransitionValues endValues)821 public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues, 822 TransitionValues endValues) { 823 if (startValues != null && endValues != null) { 824 latch.countDown(); 825 } 826 827 return null; 828 } 829 } 830 831 private static class DurationListener extends TransitionListenerAdapter { 832 833 private long mElapsedMillisStart = -1; 834 private long mDuration = -1; 835 836 @Override onTransitionStart(Transition transition)837 public void onTransitionStart(Transition transition) { 838 mElapsedMillisStart = SystemClock.elapsedRealtime(); 839 } 840 841 @Override onTransitionEnd(Transition transition)842 public void onTransitionEnd(Transition transition) { 843 mDuration = SystemClock.elapsedRealtime() - mElapsedMillisStart; 844 } 845 getDuration()846 public long getDuration() { 847 if (mDuration < 0) { 848 fail("Requested duration measurement of an incomplete transition."); 849 } 850 return mDuration; 851 } 852 } 853 } 854