Home | History | Annotate | Download | only in transition
      1 /*
      2  * Copyright (C) 2016 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package androidx.transition;
     18 
     19 
     20 import static org.hamcrest.CoreMatchers.equalTo;
     21 import static org.hamcrest.CoreMatchers.is;
     22 import static org.hamcrest.CoreMatchers.not;
     23 import static org.hamcrest.CoreMatchers.nullValue;
     24 import static org.hamcrest.MatcherAssert.assertThat;
     25 import static org.hamcrest.Matchers.hasItem;
     26 import static org.hamcrest.Matchers.sameInstance;
     27 import static org.junit.Assert.assertNotNull;
     28 import static org.junit.Assert.fail;
     29 import static org.mockito.Matchers.any;
     30 import static org.mockito.Mockito.mock;
     31 import static org.mockito.Mockito.never;
     32 import static org.mockito.Mockito.verify;
     33 
     34 import android.animation.Animator;
     35 import android.animation.ObjectAnimator;
     36 import android.animation.TimeInterpolator;
     37 import android.graphics.Rect;
     38 import android.support.test.annotation.UiThreadTest;
     39 import android.support.test.filters.MediumTest;
     40 import android.view.LayoutInflater;
     41 import android.view.View;
     42 import android.view.ViewGroup;
     43 import android.view.animation.LinearInterpolator;
     44 import android.widget.Button;
     45 import android.widget.FrameLayout;
     46 import android.widget.ImageView;
     47 
     48 import androidx.annotation.NonNull;
     49 import androidx.annotation.Nullable;
     50 import androidx.core.view.ViewCompat;
     51 import androidx.transition.test.R;
     52 
     53 import org.junit.Before;
     54 import org.junit.Test;
     55 
     56 import java.util.List;
     57 
     58 @MediumTest
     59 public class TransitionTest extends BaseTest {
     60 
     61     private Scene[] mScenes = new Scene[2];
     62     private View[] mViews = new View[3];
     63 
     64     @Before
     65     public void prepareScenes() {
     66         TransitionActivity activity = rule.getActivity();
     67         ViewGroup root = activity.getRoot();
     68         mScenes[0] = Scene.getSceneForLayout(root, R.layout.support_scene0, activity);
     69         mScenes[1] = Scene.getSceneForLayout(root, R.layout.support_scene1, activity);
     70     }
     71 
     72     @Test
     73     public void testName() {
     74         Transition transition = new EmptyTransition();
     75         assertThat(transition.getName(),
     76                 is(equalTo("androidx.transition.TransitionTest$EmptyTransition")));
     77     }
     78 
     79     @Test
     80     public void testDuration() {
     81         Transition transition = new EmptyTransition();
     82         long duration = 12345;
     83         assertThat(transition.setDuration(duration), is(sameInstance(transition)));
     84         assertThat(transition.getDuration(), is(duration));
     85     }
     86 
     87     @Test
     88     public void testInterpolator() {
     89         Transition transition = new EmptyTransition();
     90         TimeInterpolator interpolator = new LinearInterpolator();
     91         assertThat(transition.setInterpolator(interpolator), is(sameInstance(transition)));
     92         assertThat(transition.getInterpolator(), is(interpolator));
     93     }
     94 
     95     @Test
     96     public void testStartDelay() {
     97         Transition transition = new EmptyTransition();
     98         long startDelay = 12345;
     99         assertThat(transition.setStartDelay(startDelay), is(sameInstance(transition)));
    100         assertThat(transition.getStartDelay(), is(startDelay));
    101     }
    102 
    103     @Test
    104     public void testTargetIds() {
    105         Transition transition = new EmptyTransition();
    106         assertThat(transition.addTarget(R.id.view0), is(sameInstance(transition)));
    107         assertThat(transition.addTarget(R.id.view1), is(sameInstance(transition)));
    108         List<Integer> targetIds = transition.getTargetIds();
    109         assertThat(targetIds.size(), is(2));
    110         assertThat(targetIds, hasItem(R.id.view0));
    111         assertThat(targetIds, hasItem(R.id.view1));
    112         assertThat(transition.removeTarget(R.id.view0), is(sameInstance(transition)));
    113         targetIds = transition.getTargetIds();
    114         assertThat(targetIds.size(), is(1));
    115         assertThat(targetIds, not(hasItem(R.id.view0)));
    116         assertThat(targetIds, hasItem(R.id.view1));
    117     }
    118 
    119     @Test
    120     @UiThreadTest
    121     public void testTargetView() {
    122         // Set up views
    123         TransitionActivity activity = rule.getActivity();
    124         ViewGroup root = activity.getRoot();
    125         View container = LayoutInflater.from(activity)
    126                 .inflate(R.layout.support_scene0, root, false);
    127         root.addView(container);
    128         View view0 = container.findViewById(R.id.view0);
    129         View view1 = container.findViewById(R.id.view1);
    130         // Test transition targets
    131         Transition transition = new EmptyTransition();
    132         assertThat(transition.addTarget(view0), is(sameInstance(transition)));
    133         assertThat(transition.addTarget(view1), is(sameInstance(transition)));
    134         List<View> targets = transition.getTargets();
    135         assertThat(targets.size(), is(2));
    136         assertThat(targets, hasItem(sameInstance(view0)));
    137         assertThat(targets, hasItem(sameInstance(view1)));
    138         assertThat(transition.removeTarget(view0), is(sameInstance(transition)));
    139         targets = transition.getTargets();
    140         assertThat(targets.size(), is(1));
    141         assertThat(targets, not(hasItem(sameInstance(view0))));
    142         assertThat(targets, hasItem(sameInstance(view1)));
    143     }
    144 
    145     @Test
    146     public void testTargetName() {
    147         Transition transition = new EmptyTransition();
    148         assertThat(transition.addTarget("a"), is(sameInstance(transition)));
    149         assertThat(transition.addTarget("b"), is(sameInstance(transition)));
    150         List<String> targetNames = transition.getTargetNames();
    151         assertNotNull(targetNames);
    152         assertThat(targetNames.size(), is(2));
    153         assertThat(targetNames, hasItem("a"));
    154         assertThat(targetNames, hasItem("b"));
    155         transition.removeTarget("a");
    156         assertThat(targetNames.size(), is(1));
    157         assertThat(targetNames, not(hasItem("a")));
    158         assertThat(targetNames, hasItem("b"));
    159     }
    160 
    161     @Test
    162     public void testTargetType() {
    163         Transition transition = new EmptyTransition();
    164         assertThat(transition.addTarget(Button.class), is(sameInstance(transition)));
    165         assertThat(transition.addTarget(ImageView.class), is(sameInstance(transition)));
    166         List<Class> targetTypes = transition.getTargetTypes();
    167         assertNotNull(targetTypes);
    168         assertThat(targetTypes.size(), is(2));
    169         assertThat(targetTypes, hasItem(Button.class));
    170         assertThat(targetTypes, hasItem(ImageView.class));
    171         transition.removeTarget(Button.class);
    172         assertThat(targetTypes.size(), is(1));
    173         assertThat(targetTypes, not(hasItem(Button.class)));
    174         assertThat(targetTypes, hasItem(ImageView.class));
    175     }
    176 
    177     @Test
    178     public void testExcludeTargetId() throws Throwable {
    179         showInitialScene();
    180         Transition transition = new EmptyTransition();
    181         transition.addTarget(R.id.view0);
    182         transition.addTarget(R.id.view1);
    183         View view0 = rule.getActivity().findViewById(R.id.view0);
    184         View view1 = rule.getActivity().findViewById(R.id.view1);
    185         assertThat(transition.isValidTarget(view0), is(true));
    186         assertThat(transition.isValidTarget(view1), is(true));
    187         transition.excludeTarget(R.id.view0, true);
    188         assertThat(transition.isValidTarget(view0), is(false));
    189         assertThat(transition.isValidTarget(view1), is(true));
    190     }
    191 
    192     @Test
    193     public void testExcludeTargetView() throws Throwable {
    194         showInitialScene();
    195         Transition transition = new EmptyTransition();
    196         View view0 = rule.getActivity().findViewById(R.id.view0);
    197         View view1 = rule.getActivity().findViewById(R.id.view1);
    198         transition.addTarget(view0);
    199         transition.addTarget(view1);
    200         assertThat(transition.isValidTarget(view0), is(true));
    201         assertThat(transition.isValidTarget(view1), is(true));
    202         transition.excludeTarget(view0, true);
    203         assertThat(transition.isValidTarget(view0), is(false));
    204         assertThat(transition.isValidTarget(view1), is(true));
    205     }
    206 
    207     @Test
    208     public void testExcludeTargetName() throws Throwable {
    209         showInitialScene();
    210         Transition transition = new EmptyTransition();
    211         View view0 = rule.getActivity().findViewById(R.id.view0);
    212         View view1 = rule.getActivity().findViewById(R.id.view1);
    213         ViewCompat.setTransitionName(view0, "zero");
    214         ViewCompat.setTransitionName(view1, "one");
    215         transition.addTarget("zero");
    216         transition.addTarget("one");
    217         assertThat(transition.isValidTarget(view0), is(true));
    218         assertThat(transition.isValidTarget(view1), is(true));
    219         transition.excludeTarget("zero", true);
    220         assertThat(transition.isValidTarget(view0), is(false));
    221         assertThat(transition.isValidTarget(view1), is(true));
    222     }
    223 
    224     @Test
    225     public void testExcludeTargetType() throws Throwable {
    226         showInitialScene();
    227         Transition transition = new EmptyTransition();
    228         FrameLayout container = (FrameLayout) rule.getActivity().findViewById(R.id.container);
    229         View view0 = rule.getActivity().findViewById(R.id.view0);
    230         transition.addTarget(View.class);
    231         assertThat(transition.isValidTarget(container), is(true));
    232         assertThat(transition.isValidTarget(view0), is(true));
    233         transition.excludeTarget(FrameLayout.class, true);
    234         assertThat(transition.isValidTarget(container), is(false));
    235         assertThat(transition.isValidTarget(view0), is(true));
    236     }
    237 
    238     @Test
    239     public void testListener() {
    240         Transition transition = new EmptyTransition();
    241         Transition.TransitionListener listener = new EmptyTransitionListener();
    242         assertThat(transition.addListener(listener), is(sameInstance(transition)));
    243         assertThat(transition.removeListener(listener), is(sameInstance(transition)));
    244     }
    245 
    246     @Test
    247     public void testMatchOrder() throws Throwable {
    248         showInitialScene();
    249         final Transition transition = new ChangeBounds() {
    250             @Nullable
    251             @Override
    252             public Animator createAnimator(@NonNull ViewGroup sceneRoot,
    253                     @Nullable TransitionValues startValues, @Nullable TransitionValues endValues) {
    254                 if (startValues != null && endValues != null) {
    255                     fail("Match by View ID should be prevented");
    256                 }
    257                 return super.createAnimator(sceneRoot, startValues, endValues);
    258             }
    259         };
    260         transition.setDuration(0);
    261         // This prevents matches between start and end scenes because they have different set of
    262         // View instances. They will be regarded as independent views even though they share the
    263         // same View IDs.
    264         transition.setMatchOrder(Transition.MATCH_INSTANCE);
    265         SyncRunnable enter1 = new SyncRunnable();
    266         mScenes[1].setEnterAction(enter1);
    267         goToScene(mScenes[1], transition);
    268         if (!enter1.await()) {
    269             fail("Timed out while waiting for scene change");
    270         }
    271     }
    272 
    273     @Test
    274     public void testExcludedTransitionAnimator() throws Throwable {
    275         showInitialScene();
    276         final Animator.AnimatorListener animatorListener = mock(Animator.AnimatorListener.class);
    277         final DummyTransition transition = new DummyTransition(animatorListener);
    278         final SyncTransitionListener transitionListener = new SyncTransitionListener(
    279                 SyncTransitionListener.EVENT_END);
    280         transition.addListener(transitionListener);
    281         transition.addTarget(mViews[0]);
    282         transition.excludeTarget(mViews[0], true);
    283         rule.runOnUiThread(new Runnable() {
    284             @Override
    285             public void run() {
    286                 TransitionManager.beginDelayedTransition(rule.getActivity().getRoot(), transition);
    287                 mViews[0].setTranslationX(3.f);
    288             }
    289         });
    290         if (!transitionListener.await()) {
    291             fail("Timed out waiting for the TransitionListener");
    292         }
    293         verify(animatorListener, never()).onAnimationStart(any(Animator.class));
    294     }
    295 
    296     @Test
    297     public void testEpicenter() throws Throwable {
    298         final Transition transition = new EmptyTransition();
    299         final Transition.EpicenterCallback epicenterCallback = new Transition.EpicenterCallback() {
    300             private Rect mRect = new Rect();
    301 
    302             @Override
    303             public Rect onGetEpicenter(@NonNull Transition t) {
    304                 assertThat(t, is(sameInstance(transition)));
    305                 mRect.set(1, 2, 3, 4);
    306                 return mRect;
    307             }
    308         };
    309         transition.setEpicenterCallback(epicenterCallback);
    310         assertThat(transition.getEpicenterCallback(),
    311                 is(sameInstance(transition.getEpicenterCallback())));
    312         Rect rect = transition.getEpicenter();
    313         assertNotNull(rect);
    314         assertThat(rect.left, is(1));
    315         assertThat(rect.top, is(2));
    316         assertThat(rect.right, is(3));
    317         assertThat(rect.bottom, is(4));
    318     }
    319 
    320     @Test
    321     public void testSetPropagation() throws Throwable {
    322         final Transition transition = new EmptyTransition();
    323         assertThat(transition.getPropagation(), is(nullValue()));
    324         final TransitionPropagation propagation = new CircularPropagation();
    325         transition.setPropagation(propagation);
    326         assertThat(propagation, is(sameInstance(propagation)));
    327     }
    328 
    329     @Test
    330     public void testIsTransitionRequired() throws Throwable {
    331         final EmptyTransition transition = new EmptyTransition();
    332         assertThat(transition.isTransitionRequired(null, null), is(false));
    333         final TransitionValues start = new TransitionValues();
    334         final String propname = "android:transition:dummy";
    335         start.values.put(propname, 1);
    336         final TransitionValues end = new TransitionValues();
    337         end.values.put(propname, 1);
    338         assertThat(transition.isTransitionRequired(start, end), is(false));
    339         end.values.put(propname, 2);
    340         assertThat(transition.isTransitionRequired(start, end), is(true));
    341     }
    342 
    343     private void showInitialScene() throws Throwable {
    344         SyncRunnable enter0 = new SyncRunnable();
    345         mScenes[0].setEnterAction(enter0);
    346         AutoTransition transition1 = new AutoTransition();
    347         transition1.setDuration(0);
    348         goToScene(mScenes[0], transition1);
    349         if (!enter0.await()) {
    350             fail("Timed out while waiting for scene change");
    351         }
    352         mViews[0] = rule.getActivity().findViewById(R.id.view0);
    353         mViews[1] = rule.getActivity().findViewById(R.id.view1);
    354         mViews[2] = rule.getActivity().findViewById(R.id.view2);
    355     }
    356 
    357     private void goToScene(final Scene scene, final Transition transition) throws Throwable {
    358         rule.runOnUiThread(new Runnable() {
    359             @Override
    360             public void run() {
    361                 TransitionManager.go(scene, transition);
    362             }
    363         });
    364     }
    365 
    366     public static class EmptyTransition extends Transition {
    367 
    368         @Override
    369         public void captureEndValues(@NonNull TransitionValues transitionValues) {
    370         }
    371 
    372         @Override
    373         public void captureStartValues(@NonNull TransitionValues transitionValues) {
    374         }
    375 
    376         @Override
    377         public Animator createAnimator(@NonNull ViewGroup sceneRoot,
    378                 @Nullable TransitionValues startValues,
    379                 @Nullable TransitionValues endValues) {
    380             return null;
    381         }
    382 
    383     }
    384 
    385     public static class EmptyTransitionListener implements Transition.TransitionListener {
    386 
    387         @Override
    388         public void onTransitionStart(@NonNull Transition transition) {
    389         }
    390 
    391         @Override
    392         public void onTransitionEnd(@NonNull Transition transition) {
    393         }
    394 
    395         @Override
    396         public void onTransitionCancel(@NonNull Transition transition) {
    397         }
    398 
    399         @Override
    400         public void onTransitionPause(@NonNull Transition transition) {
    401         }
    402 
    403         @Override
    404         public void onTransitionResume(@NonNull Transition transition) {
    405         }
    406 
    407     }
    408 
    409     /**
    410      * A dummy transition for monitoring use of its animator by the Transition framework.
    411      */
    412     private static class DummyTransition extends Transition {
    413 
    414         private final Animator.AnimatorListener mListener;
    415 
    416         DummyTransition(Animator.AnimatorListener listener) {
    417             mListener = listener;
    418         }
    419 
    420         @Override
    421         public void captureStartValues(@NonNull TransitionValues transitionValues) {
    422             transitionValues.values.put("state", 1);
    423         }
    424 
    425         @Override
    426         public void captureEndValues(@NonNull TransitionValues transitionValues) {
    427             transitionValues.values.put("state", 2);
    428         }
    429 
    430         @Override
    431         public Animator createAnimator(@NonNull ViewGroup sceneRoot, TransitionValues startValues,
    432                 TransitionValues endValues) {
    433             if (startValues == null || endValues == null) {
    434                 return null;
    435             }
    436             final ObjectAnimator animator = ObjectAnimator
    437                     .ofFloat(startValues.view, "translationX", 1.f, 2.f);
    438             animator.addListener(mListener);
    439             return animator;
    440         }
    441 
    442     }
    443 }
    444