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