| /* |
| * 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<Integer> 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<View> 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<String> 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<Class> 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; |
| } |
| |
| } |
| } |