/* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.support.transition; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.not; import static org.hamcrest.CoreMatchers.nullValue; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.hasItem; import static org.hamcrest.Matchers.sameInstance; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.fail; import static org.mockito.Matchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import android.animation.Animator; import android.animation.ObjectAnimator; import android.animation.TimeInterpolator; import android.graphics.Rect; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.test.annotation.UiThreadTest; import android.support.test.filters.MediumTest; import android.support.transition.test.R; import android.support.v4.view.ViewCompat; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.animation.LinearInterpolator; import android.widget.Button; import android.widget.FrameLayout; import android.widget.ImageView; import org.junit.Before; import org.junit.Test; import java.util.List; @MediumTest public class TransitionTest extends BaseTest { private Scene[] mScenes = new Scene[2]; private View[] mViews = new View[3]; @Before public void prepareScenes() { TransitionActivity activity = rule.getActivity(); ViewGroup root = activity.getRoot(); mScenes[0] = Scene.getSceneForLayout(root, R.layout.support_scene0, activity); mScenes[1] = Scene.getSceneForLayout(root, R.layout.support_scene1, activity); } @Test public void testName() { Transition transition = new EmptyTransition(); assertThat(transition.getName(), is(equalTo("android.support.transition.TransitionTest$EmptyTransition"))); } @Test public void testDuration() { Transition transition = new EmptyTransition(); long duration = 12345; assertThat(transition.setDuration(duration), is(sameInstance(transition))); assertThat(transition.getDuration(), is(duration)); } @Test public void testInterpolator() { Transition transition = new EmptyTransition(); TimeInterpolator interpolator = new LinearInterpolator(); assertThat(transition.setInterpolator(interpolator), is(sameInstance(transition))); assertThat(transition.getInterpolator(), is(interpolator)); } @Test public void testStartDelay() { Transition transition = new EmptyTransition(); long startDelay = 12345; assertThat(transition.setStartDelay(startDelay), is(sameInstance(transition))); assertThat(transition.getStartDelay(), is(startDelay)); } @Test public void testTargetIds() { Transition transition = new EmptyTransition(); assertThat(transition.addTarget(R.id.view0), is(sameInstance(transition))); assertThat(transition.addTarget(R.id.view1), is(sameInstance(transition))); List targetIds = transition.getTargetIds(); assertThat(targetIds.size(), is(2)); assertThat(targetIds, hasItem(R.id.view0)); assertThat(targetIds, hasItem(R.id.view1)); assertThat(transition.removeTarget(R.id.view0), is(sameInstance(transition))); targetIds = transition.getTargetIds(); assertThat(targetIds.size(), is(1)); assertThat(targetIds, not(hasItem(R.id.view0))); assertThat(targetIds, hasItem(R.id.view1)); } @Test @UiThreadTest public void testTargetView() { // Set up views TransitionActivity activity = rule.getActivity(); ViewGroup root = activity.getRoot(); View container = LayoutInflater.from(activity) .inflate(R.layout.support_scene0, root, false); root.addView(container); View view0 = container.findViewById(R.id.view0); View view1 = container.findViewById(R.id.view1); // Test transition targets Transition transition = new EmptyTransition(); assertThat(transition.addTarget(view0), is(sameInstance(transition))); assertThat(transition.addTarget(view1), is(sameInstance(transition))); List targets = transition.getTargets(); assertThat(targets.size(), is(2)); assertThat(targets, hasItem(sameInstance(view0))); assertThat(targets, hasItem(sameInstance(view1))); assertThat(transition.removeTarget(view0), is(sameInstance(transition))); targets = transition.getTargets(); assertThat(targets.size(), is(1)); assertThat(targets, not(hasItem(sameInstance(view0)))); assertThat(targets, hasItem(sameInstance(view1))); } @Test public void testTargetName() { Transition transition = new EmptyTransition(); assertThat(transition.addTarget("a"), is(sameInstance(transition))); assertThat(transition.addTarget("b"), is(sameInstance(transition))); List targetNames = transition.getTargetNames(); assertNotNull(targetNames); assertThat(targetNames.size(), is(2)); assertThat(targetNames, hasItem("a")); assertThat(targetNames, hasItem("b")); transition.removeTarget("a"); assertThat(targetNames.size(), is(1)); assertThat(targetNames, not(hasItem("a"))); assertThat(targetNames, hasItem("b")); } @Test public void testTargetType() { Transition transition = new EmptyTransition(); assertThat(transition.addTarget(Button.class), is(sameInstance(transition))); assertThat(transition.addTarget(ImageView.class), is(sameInstance(transition))); List targetTypes = transition.getTargetTypes(); assertNotNull(targetTypes); assertThat(targetTypes.size(), is(2)); assertThat(targetTypes, hasItem(Button.class)); assertThat(targetTypes, hasItem(ImageView.class)); transition.removeTarget(Button.class); assertThat(targetTypes.size(), is(1)); assertThat(targetTypes, not(hasItem(Button.class))); assertThat(targetTypes, hasItem(ImageView.class)); } @Test public void testExcludeTargetId() throws Throwable { showInitialScene(); Transition transition = new EmptyTransition(); transition.addTarget(R.id.view0); transition.addTarget(R.id.view1); View view0 = rule.getActivity().findViewById(R.id.view0); View view1 = rule.getActivity().findViewById(R.id.view1); assertThat(transition.isValidTarget(view0), is(true)); assertThat(transition.isValidTarget(view1), is(true)); transition.excludeTarget(R.id.view0, true); assertThat(transition.isValidTarget(view0), is(false)); assertThat(transition.isValidTarget(view1), is(true)); } @Test public void testExcludeTargetView() throws Throwable { showInitialScene(); Transition transition = new EmptyTransition(); View view0 = rule.getActivity().findViewById(R.id.view0); View view1 = rule.getActivity().findViewById(R.id.view1); transition.addTarget(view0); transition.addTarget(view1); assertThat(transition.isValidTarget(view0), is(true)); assertThat(transition.isValidTarget(view1), is(true)); transition.excludeTarget(view0, true); assertThat(transition.isValidTarget(view0), is(false)); assertThat(transition.isValidTarget(view1), is(true)); } @Test public void testExcludeTargetName() throws Throwable { showInitialScene(); Transition transition = new EmptyTransition(); View view0 = rule.getActivity().findViewById(R.id.view0); View view1 = rule.getActivity().findViewById(R.id.view1); ViewCompat.setTransitionName(view0, "zero"); ViewCompat.setTransitionName(view1, "one"); transition.addTarget("zero"); transition.addTarget("one"); assertThat(transition.isValidTarget(view0), is(true)); assertThat(transition.isValidTarget(view1), is(true)); transition.excludeTarget("zero", true); assertThat(transition.isValidTarget(view0), is(false)); assertThat(transition.isValidTarget(view1), is(true)); } @Test public void testExcludeTargetType() throws Throwable { showInitialScene(); Transition transition = new EmptyTransition(); FrameLayout container = (FrameLayout) rule.getActivity().findViewById(R.id.container); View view0 = rule.getActivity().findViewById(R.id.view0); transition.addTarget(View.class); assertThat(transition.isValidTarget(container), is(true)); assertThat(transition.isValidTarget(view0), is(true)); transition.excludeTarget(FrameLayout.class, true); assertThat(transition.isValidTarget(container), is(false)); assertThat(transition.isValidTarget(view0), is(true)); } @Test public void testListener() { Transition transition = new EmptyTransition(); Transition.TransitionListener listener = new EmptyTransitionListener(); assertThat(transition.addListener(listener), is(sameInstance(transition))); assertThat(transition.removeListener(listener), is(sameInstance(transition))); } @Test public void testMatchOrder() throws Throwable { showInitialScene(); final Transition transition = new ChangeBounds() { @Nullable @Override public Animator createAnimator(@NonNull ViewGroup sceneRoot, @Nullable TransitionValues startValues, @Nullable TransitionValues endValues) { if (startValues != null && endValues != null) { fail("Match by View ID should be prevented"); } return super.createAnimator(sceneRoot, startValues, endValues); } }; transition.setDuration(0); // This prevents matches between start and end scenes because they have different set of // View instances. They will be regarded as independent views even though they share the // same View IDs. transition.setMatchOrder(Transition.MATCH_INSTANCE); SyncRunnable enter1 = new SyncRunnable(); mScenes[1].setEnterAction(enter1); goToScene(mScenes[1], transition); if (!enter1.await()) { fail("Timed out while waiting for scene change"); } } @Test public void testExcludedTransitionAnimator() throws Throwable { showInitialScene(); final Animator.AnimatorListener animatorListener = mock(Animator.AnimatorListener.class); final DummyTransition transition = new DummyTransition(animatorListener); final SyncTransitionListener transitionListener = new SyncTransitionListener( SyncTransitionListener.EVENT_END); transition.addListener(transitionListener); transition.addTarget(mViews[0]); transition.excludeTarget(mViews[0], true); rule.runOnUiThread(new Runnable() { @Override public void run() { TransitionManager.beginDelayedTransition(rule.getActivity().getRoot(), transition); mViews[0].setTranslationX(3.f); } }); if (!transitionListener.await()) { fail("Timed out waiting for the TransitionListener"); } verify(animatorListener, never()).onAnimationStart(any(Animator.class)); } @Test public void testEpicenter() throws Throwable { final Transition transition = new EmptyTransition(); final Transition.EpicenterCallback epicenterCallback = new Transition.EpicenterCallback() { private Rect mRect = new Rect(); @Override public Rect onGetEpicenter(@NonNull Transition t) { assertThat(t, is(sameInstance(transition))); mRect.set(1, 2, 3, 4); return mRect; } }; transition.setEpicenterCallback(epicenterCallback); assertThat(transition.getEpicenterCallback(), is(sameInstance(transition.getEpicenterCallback()))); Rect rect = transition.getEpicenter(); assertNotNull(rect); assertThat(rect.left, is(1)); assertThat(rect.top, is(2)); assertThat(rect.right, is(3)); assertThat(rect.bottom, is(4)); } @Test public void testSetPropagation() throws Throwable { final Transition transition = new EmptyTransition(); assertThat(transition.getPropagation(), is(nullValue())); final TransitionPropagation propagation = new CircularPropagation(); transition.setPropagation(propagation); assertThat(propagation, is(sameInstance(propagation))); } @Test public void testIsTransitionRequired() throws Throwable { final EmptyTransition transition = new EmptyTransition(); assertThat(transition.isTransitionRequired(null, null), is(false)); final TransitionValues start = new TransitionValues(); final String propname = "android:transition:dummy"; start.values.put(propname, 1); final TransitionValues end = new TransitionValues(); end.values.put(propname, 1); assertThat(transition.isTransitionRequired(start, end), is(false)); end.values.put(propname, 2); assertThat(transition.isTransitionRequired(start, end), is(true)); } private void showInitialScene() throws Throwable { SyncRunnable enter0 = new SyncRunnable(); mScenes[0].setEnterAction(enter0); AutoTransition transition1 = new AutoTransition(); transition1.setDuration(0); goToScene(mScenes[0], transition1); if (!enter0.await()) { fail("Timed out while waiting for scene change"); } mViews[0] = rule.getActivity().findViewById(R.id.view0); mViews[1] = rule.getActivity().findViewById(R.id.view1); mViews[2] = rule.getActivity().findViewById(R.id.view2); } private void goToScene(final Scene scene, final Transition transition) throws Throwable { rule.runOnUiThread(new Runnable() { @Override public void run() { TransitionManager.go(scene, transition); } }); } public static class EmptyTransition extends Transition { @Override public void captureEndValues(@NonNull TransitionValues transitionValues) { } @Override public void captureStartValues(@NonNull TransitionValues transitionValues) { } @Override public Animator createAnimator(@NonNull ViewGroup sceneRoot, @Nullable TransitionValues startValues, @Nullable TransitionValues endValues) { return null; } } public static class EmptyTransitionListener implements Transition.TransitionListener { @Override public void onTransitionStart(@NonNull Transition transition) { } @Override public void onTransitionEnd(@NonNull Transition transition) { } @Override public void onTransitionCancel(@NonNull Transition transition) { } @Override public void onTransitionPause(@NonNull Transition transition) { } @Override public void onTransitionResume(@NonNull Transition transition) { } } /** * A dummy transition for monitoring use of its animator by the Transition framework. */ private static class DummyTransition extends Transition { private final Animator.AnimatorListener mListener; DummyTransition(Animator.AnimatorListener listener) { mListener = listener; } @Override public void captureStartValues(@NonNull TransitionValues transitionValues) { transitionValues.values.put("state", 1); } @Override public void captureEndValues(@NonNull TransitionValues transitionValues) { transitionValues.values.put("state", 2); } @Override public Animator createAnimator(@NonNull ViewGroup sceneRoot, TransitionValues startValues, TransitionValues endValues) { if (startValues == null || endValues == null) { return null; } final ObjectAnimator animator = ObjectAnimator .ofFloat(startValues.view, "translationX", 1.f, 2.f); animator.addListener(mListener); return animator; } } }