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