1 /* 2 * Copyright (C) 2021 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 com.android.wm.shell.transition; 18 19 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; 20 import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS; 21 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; 22 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; 23 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; 24 import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; 25 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; 26 import static android.view.Display.DEFAULT_DISPLAY; 27 import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_ROTATE; 28 import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS; 29 import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_UNSPECIFIED; 30 import static android.view.WindowManager.TRANSIT_CHANGE; 31 import static android.view.WindowManager.TRANSIT_CLOSE; 32 import static android.view.WindowManager.TRANSIT_FIRST_CUSTOM; 33 import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY; 34 import static android.view.WindowManager.TRANSIT_OPEN; 35 import static android.view.WindowManager.TRANSIT_SLEEP; 36 import static android.view.WindowManager.TRANSIT_TO_BACK; 37 import static android.view.WindowManager.TRANSIT_TO_FRONT; 38 import static android.window.TransitionInfo.FLAG_DISPLAY_HAS_ALERT_WINDOWS; 39 import static android.window.TransitionInfo.FLAG_IS_DISPLAY; 40 import static android.window.TransitionInfo.FLAG_MOVED_TO_TOP; 41 import static android.window.TransitionInfo.FLAG_SYNC; 42 import static android.window.TransitionInfo.FLAG_TRANSLUCENT; 43 44 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; 45 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; 46 import static com.android.wm.shell.transition.TransitionAnimationHelper.getTransitionTypeFromInfo; 47 48 import static org.junit.Assert.assertEquals; 49 import static org.junit.Assert.assertFalse; 50 import static org.junit.Assert.assertNotNull; 51 import static org.junit.Assert.assertNull; 52 import static org.junit.Assert.assertTrue; 53 import static org.mockito.ArgumentMatchers.any; 54 import static org.mockito.ArgumentMatchers.anyBoolean; 55 import static org.mockito.ArgumentMatchers.anyInt; 56 import static org.mockito.ArgumentMatchers.eq; 57 import static org.mockito.ArgumentMatchers.isA; 58 import static org.mockito.ArgumentMatchers.isNull; 59 import static org.mockito.Mockito.clearInvocations; 60 import static org.mockito.Mockito.doAnswer; 61 import static org.mockito.Mockito.inOrder; 62 import static org.mockito.Mockito.mock; 63 import static org.mockito.Mockito.times; 64 import static org.mockito.Mockito.verify; 65 66 import android.app.ActivityManager.RunningTaskInfo; 67 import android.app.IApplicationThread; 68 import android.app.PendingIntent; 69 import android.content.ComponentName; 70 import android.content.Context; 71 import android.content.Intent; 72 import android.os.Binder; 73 import android.os.Bundle; 74 import android.os.Handler; 75 import android.os.IBinder; 76 import android.os.Looper; 77 import android.os.RemoteException; 78 import android.platform.test.annotations.DisableFlags; 79 import android.platform.test.annotations.EnableFlags; 80 import android.util.ArraySet; 81 import android.util.Pair; 82 import android.view.Surface; 83 import android.view.SurfaceControl; 84 import android.window.IRemoteTransition; 85 import android.window.IRemoteTransitionFinishedCallback; 86 import android.window.IWindowContainerToken; 87 import android.window.RemoteTransition; 88 import android.window.RemoteTransitionStub; 89 import android.window.TransitionFilter; 90 import android.window.TransitionInfo; 91 import android.window.TransitionRequestInfo; 92 import android.window.WindowAnimationState; 93 import android.window.WindowContainerToken; 94 import android.window.WindowContainerTransaction; 95 96 import androidx.annotation.NonNull; 97 import androidx.annotation.Nullable; 98 import androidx.test.ext.junit.runners.AndroidJUnit4; 99 import androidx.test.filters.SmallTest; 100 import androidx.test.platform.app.InstrumentationRegistry; 101 102 import com.android.internal.R; 103 import com.android.internal.policy.TransitionAnimation; 104 import com.android.systemui.shared.Flags; 105 import com.android.wm.shell.RootTaskDisplayAreaOrganizer; 106 import com.android.wm.shell.ShellTaskOrganizer; 107 import com.android.wm.shell.ShellTestCase; 108 import com.android.wm.shell.TestShellExecutor; 109 import com.android.wm.shell.common.DisplayController; 110 import com.android.wm.shell.common.DisplayInsetsController; 111 import com.android.wm.shell.common.DisplayLayout; 112 import com.android.wm.shell.common.ShellExecutor; 113 import com.android.wm.shell.recents.IRecentsAnimationRunner; 114 import com.android.wm.shell.recents.RecentTasksController; 115 import com.android.wm.shell.recents.RecentsTransitionHandler; 116 import com.android.wm.shell.shared.IShellTransitions; 117 import com.android.wm.shell.shared.TransactionPool; 118 import com.android.wm.shell.sysui.ShellController; 119 import com.android.wm.shell.sysui.ShellInit; 120 import com.android.wm.shell.util.StubTransaction; 121 122 import org.junit.Before; 123 import org.junit.Test; 124 import org.junit.runner.RunWith; 125 import org.mockito.Answers; 126 import org.mockito.InOrder; 127 128 import java.util.ArrayList; 129 import java.util.function.Function; 130 131 /** 132 * Tests for the shell transitions. 133 * 134 * Build/Install/Run: 135 * atest WMShellUnitTests:ShellTransitionTests 136 */ 137 @SmallTest 138 @RunWith(AndroidJUnit4.class) 139 public class ShellTransitionTests extends ShellTestCase { 140 141 private final ShellTaskOrganizer mOrganizer = mock(ShellTaskOrganizer.class); 142 private final TransactionPool mTransactionPool = mock(TransactionPool.class); 143 private final Context mContext = 144 InstrumentationRegistry.getInstrumentation().getTargetContext(); 145 private final TestShellExecutor mMainExecutor = new TestShellExecutor(); 146 private final ShellExecutor mAnimExecutor = new TestShellExecutor(); 147 private final TestTransitionHandler mDefaultHandler = new TestTransitionHandler(); 148 private final Handler mMainHandler = new Handler(Looper.getMainLooper()); 149 private final Handler mAnimHandler = mock(Handler.class); 150 private final DisplayInsetsController mDisplayInsets = 151 mock(DisplayInsetsController.class); 152 153 @Before setUp()154 public void setUp() { 155 doAnswer(invocation -> new Binder()) 156 .when(mOrganizer).startNewTransition(anyInt(), any()); 157 } 158 159 @Test instantiate_addInitCallback()160 public void instantiate_addInitCallback() { 161 ShellInit shellInit = mock(ShellInit.class); 162 final Transitions t = new Transitions(mContext, shellInit, mock(ShellController.class), 163 mOrganizer, mTransactionPool, createTestDisplayController(), mDisplayInsets, 164 mMainExecutor, mMainHandler, mAnimExecutor, mAnimHandler, 165 mock(HomeTransitionObserver.class), mock(FocusTransitionObserver.class)); 166 // One from Transitions, one from RootTaskDisplayAreaOrganizer 167 verify(shellInit).addInitCallback(any(), eq(t)); 168 verify(shellInit).addInitCallback(any(), isA(RootTaskDisplayAreaOrganizer.class)); 169 } 170 171 @Test instantiateController_addExternalInterface()172 public void instantiateController_addExternalInterface() { 173 ShellInit shellInit = new ShellInit(mMainExecutor); 174 ShellController shellController = mock(ShellController.class); 175 final Transitions t = new Transitions(mContext, shellInit, shellController, 176 mOrganizer, mTransactionPool, createTestDisplayController(), mDisplayInsets, 177 mMainExecutor, mMainHandler, mAnimExecutor, mAnimHandler, 178 mock(HomeTransitionObserver.class), mock(FocusTransitionObserver.class)); 179 shellInit.init(); 180 verify(shellController, times(1)).addExternalInterface( 181 eq(IShellTransitions.DESCRIPTOR), any(), any()); 182 } 183 184 @Test testBasicTransitionFlow()185 public void testBasicTransitionFlow() { 186 Transitions transitions = createTestTransitions(); 187 transitions.replaceDefaultHandlerForTest(mDefaultHandler); 188 189 IBinder transitToken = new Binder(); 190 transitions.requestStartTransition(transitToken, 191 new TransitionRequestInfo(TRANSIT_OPEN, null /* trigger */, null /* remote */)); 192 verify(mOrganizer, times(1)).startTransition(eq(transitToken), any()); 193 TransitionInfo info = new TransitionInfoBuilder(TRANSIT_OPEN) 194 .addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build(); 195 transitions.onTransitionReady(transitToken, info, new StubTransaction(), 196 new StubTransaction()); 197 assertEquals(1, mDefaultHandler.activeCount()); 198 mDefaultHandler.finishAll(); 199 mMainExecutor.flushAll(); 200 verify(mOrganizer, times(1)).finishTransition(eq(transitToken), any()); 201 } 202 203 @Test testNonDefaultHandler()204 public void testNonDefaultHandler() { 205 Transitions transitions = createTestTransitions(); 206 transitions.replaceDefaultHandlerForTest(mDefaultHandler); 207 208 final WindowContainerTransaction handlerWCT = new WindowContainerTransaction(); 209 // Make a test handler that only responds to multi-window triggers AND only animates 210 // Change transitions. 211 TestTransitionHandler testHandler = new TestTransitionHandler() { 212 @Override 213 public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info, 214 @NonNull SurfaceControl.Transaction startTransaction, 215 @NonNull SurfaceControl.Transaction finishTransaction, 216 @NonNull Transitions.TransitionFinishCallback finishCallback) { 217 for (TransitionInfo.Change chg : info.getChanges()) { 218 if (chg.getMode() == TRANSIT_CHANGE) { 219 return super.startAnimation(transition, info, startTransaction, 220 finishTransaction, finishCallback); 221 } 222 } 223 return false; 224 } 225 226 @Nullable 227 @Override 228 public WindowContainerTransaction handleRequest(@NonNull IBinder transition, 229 @NonNull TransitionRequestInfo request) { 230 final RunningTaskInfo task = request.getTriggerTask(); 231 return (task != null && task.getWindowingMode() == WINDOWING_MODE_MULTI_WINDOW) 232 ? handlerWCT : null; 233 } 234 }; 235 transitions.addHandler(testHandler); 236 237 IBinder transitToken = new Binder(); 238 TransitionInfo open = new TransitionInfoBuilder(TRANSIT_OPEN) 239 .addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build(); 240 241 // Make a request that will be rejected by the testhandler. 242 transitions.requestStartTransition(transitToken, 243 new TransitionRequestInfo(TRANSIT_OPEN, null /* trigger */, null /* remote */)); 244 verify(mOrganizer, times(1)).startTransition(eq(transitToken), isNull()); 245 transitions.onTransitionReady(transitToken, open, new StubTransaction(), 246 new StubTransaction()); 247 assertEquals(1, mDefaultHandler.activeCount()); 248 assertEquals(0, testHandler.activeCount()); 249 mDefaultHandler.finishAll(); 250 mMainExecutor.flushAll(); 251 252 // Make a request that will be handled by testhandler but not animated by it. 253 RunningTaskInfo mwTaskInfo = 254 createTaskInfo(1, WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD); 255 // Make the wct non-empty. 256 handlerWCT.setFocusable(new WindowContainerToken(mock(IWindowContainerToken.class)), true); 257 transitions.requestStartTransition(transitToken, 258 new TransitionRequestInfo(TRANSIT_OPEN, mwTaskInfo, null /* remote */)); 259 verify(mOrganizer, times(1)).startTransition( 260 eq(transitToken), eq(handlerWCT)); 261 transitions.onTransitionReady(transitToken, open, new StubTransaction(), 262 new StubTransaction()); 263 assertEquals(1, mDefaultHandler.activeCount()); 264 assertEquals(0, testHandler.activeCount()); 265 mDefaultHandler.finishAll(); 266 mMainExecutor.flushAll(); 267 268 // Make a request that will be handled AND animated by testhandler. 269 // Add an aggressive handler (doesn't handle but always animates) on top to make sure that 270 // the test handler gets first shot at animating since it claimed to handle it. 271 TestTransitionHandler topHandler = new TestTransitionHandler(); 272 transitions.addHandler(topHandler); 273 transitions.requestStartTransition(transitToken, 274 new TransitionRequestInfo(TRANSIT_CHANGE, mwTaskInfo, null /* remote */)); 275 verify(mOrganizer, times(2)).startTransition( 276 eq(transitToken), eq(handlerWCT)); 277 TransitionInfo change = new TransitionInfoBuilder(TRANSIT_CHANGE) 278 .addChange(TRANSIT_CHANGE).build(); 279 transitions.onTransitionReady(transitToken, change, new StubTransaction(), 280 new StubTransaction()); 281 assertEquals(0, mDefaultHandler.activeCount()); 282 assertEquals(1, testHandler.activeCount()); 283 assertEquals(0, topHandler.activeCount()); 284 testHandler.finishAll(); 285 mMainExecutor.flushAll(); 286 } 287 288 @Test testRequestRemoteTransition()289 public void testRequestRemoteTransition() { 290 Transitions transitions = createTestTransitions(); 291 transitions.replaceDefaultHandlerForTest(mDefaultHandler); 292 293 final boolean[] remoteCalled = new boolean[]{false}; 294 final WindowContainerTransaction remoteFinishWCT = new WindowContainerTransaction(); 295 IRemoteTransition testRemote = new RemoteTransitionStub() { 296 @Override 297 public void startAnimation(IBinder token, TransitionInfo info, 298 SurfaceControl.Transaction t, 299 IRemoteTransitionFinishedCallback finishCallback) throws RemoteException { 300 remoteCalled[0] = true; 301 finishCallback.onTransitionFinished(remoteFinishWCT, null /* sct */); 302 } 303 }; 304 IBinder transitToken = new Binder(); 305 transitions.requestStartTransition(transitToken, 306 new TransitionRequestInfo(TRANSIT_OPEN, null /* trigger */, 307 new RemoteTransition(testRemote, "Test"))); 308 verify(mOrganizer, times(1)).startTransition(eq(transitToken), any()); 309 TransitionInfo info = new TransitionInfoBuilder(TRANSIT_OPEN) 310 .addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build(); 311 transitions.onTransitionReady(transitToken, info, new StubTransaction(), 312 new StubTransaction()); 313 assertEquals(0, mDefaultHandler.activeCount()); 314 assertTrue(remoteCalled[0]); 315 mDefaultHandler.finishAll(); 316 mMainExecutor.flushAll(); 317 verify(mOrganizer, times(1)).finishTransition(eq(transitToken), eq(remoteFinishWCT)); 318 } 319 320 @Test testTransitionFilterActivityType()321 public void testTransitionFilterActivityType() { 322 TransitionFilter filter = new TransitionFilter(); 323 filter.mRequirements = 324 new TransitionFilter.Requirement[]{new TransitionFilter.Requirement()}; 325 filter.mRequirements[0].mActivityType = ACTIVITY_TYPE_HOME; 326 filter.mRequirements[0].mModes = new int[]{TRANSIT_OPEN, TRANSIT_TO_FRONT}; 327 328 final TransitionInfo openHome = new TransitionInfoBuilder(TRANSIT_OPEN) 329 .addChange(TRANSIT_OPEN, 330 createTaskInfo(1, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME)).build(); 331 assertTrue(filter.matches(openHome)); 332 333 final TransitionInfo openStd = new TransitionInfoBuilder(TRANSIT_OPEN) 334 .addChange(TRANSIT_OPEN, createTaskInfo( 335 1, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD)).build(); 336 assertFalse(filter.matches(openStd)); 337 } 338 339 @Test testTransitionFilterTaskFragmentToken()340 public void testTransitionFilterTaskFragmentToken() { 341 final IBinder taskFragmentToken = new Binder(); 342 343 TransitionFilter filter = new TransitionFilter(); 344 filter.mRequirements = 345 new TransitionFilter.Requirement[]{new TransitionFilter.Requirement()}; 346 filter.mRequirements[0].mModes = new int[]{TRANSIT_OPEN, TRANSIT_TO_FRONT}; 347 filter.mRequirements[0].mTaskFragmentToken = taskFragmentToken; 348 349 // Transition with the same token should match. 350 final TransitionInfo infoHasTaskFragmentToken = new TransitionInfoBuilder(TRANSIT_OPEN) 351 .addChange(TRANSIT_OPEN, taskFragmentToken).build(); 352 assertTrue(filter.matches(infoHasTaskFragmentToken)); 353 354 // Transition with a different token should not match. 355 final IBinder differentTaskFragmentToken = new Binder(); 356 final TransitionInfo infoDifferentTaskFragmentToken = 357 new TransitionInfoBuilder(TRANSIT_OPEN) 358 .addChange(TRANSIT_OPEN, differentTaskFragmentToken).build(); 359 assertFalse(filter.matches(infoDifferentTaskFragmentToken)); 360 361 // Transition without a token should not match. 362 final TransitionInfo infoNoTaskFragmentToken = new TransitionInfoBuilder(TRANSIT_OPEN) 363 .addChange(TRANSIT_OPEN, createTaskInfo( 364 1, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD)).build(); 365 assertFalse(filter.matches(infoNoTaskFragmentToken)); 366 } 367 368 @Test testTransitionFilterWindowingMode()369 public void testTransitionFilterWindowingMode() { 370 TransitionFilter filter = new TransitionFilter(); 371 filter.mRequirements = 372 new TransitionFilter.Requirement[]{new TransitionFilter.Requirement()}; 373 filter.mRequirements[0].mWindowingMode = WINDOWING_MODE_FREEFORM; 374 filter.mRequirements[0].mModes = new int[]{TRANSIT_OPEN, TRANSIT_TO_FRONT}; 375 376 final TransitionInfo fullscreenStd = new TransitionInfoBuilder(TRANSIT_OPEN) 377 .addChange(TRANSIT_OPEN, createTaskInfo( 378 1, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD)).build(); 379 assertFalse(filter.matches(fullscreenStd)); 380 381 final TransitionInfo freeformStd = new TransitionInfoBuilder(TRANSIT_OPEN) 382 .addChange(TRANSIT_OPEN, createTaskInfo( 383 1, WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD)).build(); 384 assertTrue(filter.matches(freeformStd)); 385 } 386 387 @Test testTransitionFilterMultiRequirement()388 public void testTransitionFilterMultiRequirement() { 389 // filter that requires at-least one opening and one closing app 390 TransitionFilter filter = new TransitionFilter(); 391 filter.mRequirements = new TransitionFilter.Requirement[]{ 392 new TransitionFilter.Requirement(), new TransitionFilter.Requirement()}; 393 filter.mRequirements[0].mModes = new int[]{TRANSIT_OPEN, TRANSIT_TO_FRONT}; 394 filter.mRequirements[1].mModes = new int[]{TRANSIT_CLOSE, TRANSIT_TO_BACK}; 395 396 final TransitionInfo openOnly = new TransitionInfoBuilder(TRANSIT_OPEN) 397 .addChange(TRANSIT_OPEN).build(); 398 assertFalse(filter.matches(openOnly)); 399 400 final TransitionInfo openClose = new TransitionInfoBuilder(TRANSIT_OPEN) 401 .addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build(); 402 assertTrue(filter.matches(openClose)); 403 } 404 405 @Test testTransitionFilterNotRequirement()406 public void testTransitionFilterNotRequirement() { 407 // filter that requires one opening and NO translucent apps 408 TransitionFilter filter = new TransitionFilter(); 409 filter.mRequirements = new TransitionFilter.Requirement[]{ 410 new TransitionFilter.Requirement(), new TransitionFilter.Requirement()}; 411 filter.mRequirements[0].mModes = new int[]{TRANSIT_OPEN, TRANSIT_TO_FRONT}; 412 filter.mRequirements[1].mFlags = FLAG_TRANSLUCENT; 413 filter.mRequirements[1].mNot = true; 414 415 final TransitionInfo openOnly = new TransitionInfoBuilder(TRANSIT_OPEN) 416 .addChange(TRANSIT_OPEN).build(); 417 assertTrue(filter.matches(openOnly)); 418 419 final TransitionInfo openAndTranslucent = new TransitionInfoBuilder(TRANSIT_OPEN) 420 .addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build(); 421 openAndTranslucent.getChanges().get(1).setFlags(FLAG_TRANSLUCENT); 422 assertFalse(filter.matches(openAndTranslucent)); 423 } 424 425 @Test testTransitionFilterChecksTypeSet()426 public void testTransitionFilterChecksTypeSet() { 427 TransitionFilter filter = new TransitionFilter(); 428 filter.mTypeSet = new int[]{TRANSIT_OPEN, TRANSIT_TO_FRONT}; 429 430 final TransitionInfo openOnly = new TransitionInfoBuilder(TRANSIT_OPEN) 431 .addChange(TRANSIT_OPEN).build(); 432 assertTrue(filter.matches(openOnly)); 433 434 final TransitionInfo toFrontOnly = new TransitionInfoBuilder(TRANSIT_TO_FRONT) 435 .addChange(TRANSIT_TO_FRONT).build(); 436 assertTrue(filter.matches(toFrontOnly)); 437 438 final TransitionInfo closeOnly = new TransitionInfoBuilder(TRANSIT_CLOSE) 439 .addChange(TRANSIT_CLOSE).build(); 440 assertFalse(filter.matches(closeOnly)); 441 } 442 443 @Test testTransitionFilterChecksFlags()444 public void testTransitionFilterChecksFlags() { 445 TransitionFilter filter = new TransitionFilter(); 446 filter.mFlags = TRANSIT_FLAG_KEYGUARD_GOING_AWAY; 447 448 final TransitionInfo withFlag = new TransitionInfoBuilder(TRANSIT_TO_BACK, 449 TRANSIT_FLAG_KEYGUARD_GOING_AWAY) 450 .addChange(TRANSIT_TO_BACK).build(); 451 assertTrue(filter.matches(withFlag)); 452 453 final TransitionInfo withoutFlag = new TransitionInfoBuilder(TRANSIT_OPEN) 454 .addChange(TRANSIT_OPEN).build(); 455 assertFalse(filter.matches(withoutFlag)); 456 } 457 458 @Test testTransitionFilterChecksNotFlags()459 public void testTransitionFilterChecksNotFlags() { 460 TransitionFilter filter = new TransitionFilter(); 461 filter.mNotFlags = TRANSIT_FLAG_KEYGUARD_GOING_AWAY; 462 463 final TransitionInfo withFlag = new TransitionInfoBuilder(TRANSIT_TO_BACK, 464 TRANSIT_FLAG_KEYGUARD_GOING_AWAY) 465 .addChange(TRANSIT_TO_BACK).build(); 466 assertFalse(filter.matches(withFlag)); 467 468 final TransitionInfo withoutFlag = new TransitionInfoBuilder(TRANSIT_OPEN) 469 .addChange(TRANSIT_OPEN).build(); 470 assertTrue(filter.matches(withoutFlag)); 471 } 472 473 @Test testTransitionFilterActivityComponent()474 public void testTransitionFilterActivityComponent() { 475 TransitionFilter filter = new TransitionFilter(); 476 ComponentName cmpt = new ComponentName("testpak", "testcls"); 477 filter.mRequirements = 478 new TransitionFilter.Requirement[]{new TransitionFilter.Requirement()}; 479 filter.mRequirements[0].mTopActivity = cmpt; 480 filter.mRequirements[0].mModes = new int[]{TRANSIT_OPEN, TRANSIT_TO_FRONT}; 481 482 final RunningTaskInfo taskInf = createTaskInfo(1); 483 final TransitionInfo openTask = new TransitionInfoBuilder(TRANSIT_OPEN) 484 .addChange(TRANSIT_OPEN, taskInf).build(); 485 assertFalse(filter.matches(openTask)); 486 487 taskInf.topActivity = cmpt; 488 final TransitionInfo openTaskCmpt = new TransitionInfoBuilder(TRANSIT_OPEN) 489 .addChange(TRANSIT_OPEN, taskInf).build(); 490 assertTrue(filter.matches(openTaskCmpt)); 491 492 final TransitionInfo openAct = new TransitionInfoBuilder(TRANSIT_OPEN) 493 .addChange(TRANSIT_OPEN, cmpt).build(); 494 assertTrue(filter.matches(openAct)); 495 } 496 497 @Test testTransitionFilterAnimOverride()498 public void testTransitionFilterAnimOverride() { 499 TransitionFilter filter = new TransitionFilter(); 500 filter.mRequirements = 501 new TransitionFilter.Requirement[]{new TransitionFilter.Requirement()}; 502 filter.mRequirements[0].mCustomAnimation = true; 503 filter.mRequirements[0].mModes = new int[]{TRANSIT_OPEN, TRANSIT_TO_FRONT}; 504 505 final RunningTaskInfo taskInf = createTaskInfo(1); 506 final TransitionInfo openTask = new TransitionInfoBuilder(TRANSIT_OPEN) 507 .addChange(TRANSIT_OPEN, taskInf).build(); 508 assertFalse(filter.matches(openTask)); 509 510 final TransitionInfo.AnimationOptions overOpts = 511 TransitionInfo.AnimationOptions.makeCustomAnimOptions("pakname", 0, 0, 0, true); 512 final TransitionInfo openTaskOpts = new TransitionInfoBuilder(TRANSIT_OPEN) 513 .addChange(TRANSIT_OPEN, taskInf).build(); 514 openTaskOpts.getChanges().get(0).setAnimationOptions(overOpts); 515 assertTrue(filter.matches(openTaskOpts)); 516 } 517 518 @Test testRegisteredRemoteTransition()519 public void testRegisteredRemoteTransition() { 520 Transitions transitions = createTestTransitions(); 521 transitions.replaceDefaultHandlerForTest(mDefaultHandler); 522 523 final boolean[] remoteCalled = new boolean[]{false}; 524 IRemoteTransition testRemote = new RemoteTransitionStub() { 525 @Override 526 public void startAnimation(IBinder token, TransitionInfo info, 527 SurfaceControl.Transaction t, 528 IRemoteTransitionFinishedCallback finishCallback) throws RemoteException { 529 remoteCalled[0] = true; 530 finishCallback.onTransitionFinished(null /* wct */, null /* sct */); 531 } 532 }; 533 534 TransitionFilter filter = new TransitionFilter(); 535 filter.mRequirements = 536 new TransitionFilter.Requirement[]{new TransitionFilter.Requirement()}; 537 filter.mRequirements[0].mModes = new int[]{TRANSIT_OPEN, TRANSIT_TO_FRONT}; 538 539 transitions.registerRemote(filter, new RemoteTransition(testRemote, "Test")); 540 mMainExecutor.flushAll(); 541 542 IBinder transitToken = new Binder(); 543 transitions.requestStartTransition(transitToken, 544 new TransitionRequestInfo(TRANSIT_OPEN, null /* trigger */, null /* remote */)); 545 verify(mOrganizer, times(1)).startTransition(eq(transitToken), any()); 546 TransitionInfo info = new TransitionInfoBuilder(TRANSIT_OPEN) 547 .addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build(); 548 transitions.onTransitionReady(transitToken, info, new StubTransaction(), 549 new StubTransaction()); 550 assertEquals(0, mDefaultHandler.activeCount()); 551 assertTrue(remoteCalled[0]); 552 mDefaultHandler.finishAll(); 553 mMainExecutor.flushAll(); 554 verify(mOrganizer, times(1)).finishTransition(eq(transitToken), any()); 555 } 556 557 @Test 558 @DisableFlags(Flags.FLAG_RETURN_ANIMATION_FRAMEWORK_LONG_LIVED) testRegisteredRemoteTransitionTakeover_flagDisabled()559 public void testRegisteredRemoteTransitionTakeover_flagDisabled() { 560 Transitions transitions = createTestTransitions(); 561 transitions.replaceDefaultHandlerForTest(mDefaultHandler); 562 563 IRemoteTransition testRemote = new RemoteTransitionStub() { 564 @Override 565 public void startAnimation(IBinder token, TransitionInfo info, 566 SurfaceControl.Transaction t, 567 IRemoteTransitionFinishedCallback finishCallback) throws RemoteException { 568 final Transitions.TransitionHandler takeoverHandler = 569 transitions.getHandlerForTakeover(token, info); 570 571 if (takeoverHandler == null) { 572 finishCallback.onTransitionFinished(null /* wct */, null /* sct */); 573 return; 574 } 575 576 takeoverHandler.takeOverAnimation(token, info, new SurfaceControl.Transaction(), 577 wct -> { 578 try { 579 finishCallback.onTransitionFinished(wct, null /* sct */); 580 } catch (RemoteException e) { 581 // Fail 582 } 583 }, new WindowAnimationState[info.getChanges().size()]); 584 } 585 }; 586 final boolean[] takeoverRemoteCalled = new boolean[]{false}; 587 IRemoteTransition testTakeoverRemote = new RemoteTransitionStub() { 588 @Override 589 public void startAnimation(IBinder token, TransitionInfo info, 590 SurfaceControl.Transaction t, 591 IRemoteTransitionFinishedCallback finishCallback) {} 592 593 @Override 594 public void takeOverAnimation(IBinder transition, TransitionInfo info, 595 SurfaceControl.Transaction startTransaction, 596 IRemoteTransitionFinishedCallback finishCallback, WindowAnimationState[] states) 597 throws RemoteException { 598 takeoverRemoteCalled[0] = true; 599 finishCallback.onTransitionFinished(null /* wct */, null /* sct */); 600 } 601 }; 602 603 TransitionFilter filter = new TransitionFilter(); 604 filter.mRequirements = 605 new TransitionFilter.Requirement[]{new TransitionFilter.Requirement()}; 606 filter.mRequirements[0].mModes = new int[]{TRANSIT_OPEN, TRANSIT_TO_FRONT}; 607 608 transitions.registerRemote(filter, new RemoteTransition(testRemote, "Test")); 609 transitions.registerRemoteForTakeover( 610 filter, new RemoteTransition(testTakeoverRemote, "Test")); 611 mMainExecutor.flushAll(); 612 613 // Takeover shouldn't happen when the flag is disabled. 614 IBinder transitToken = new Binder(); 615 transitions.requestStartTransition(transitToken, 616 new TransitionRequestInfo(TRANSIT_OPEN, null /* trigger */, null /* remote */)); 617 TransitionInfo info = new TransitionInfoBuilder(TRANSIT_OPEN) 618 .addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build(); 619 transitions.onTransitionReady(transitToken, info, new StubTransaction(), 620 new StubTransaction()); 621 assertEquals(0, mDefaultHandler.activeCount()); 622 assertFalse(takeoverRemoteCalled[0]); 623 mDefaultHandler.finishAll(); 624 mMainExecutor.flushAll(); 625 verify(mOrganizer, times(1)).finishTransition(eq(transitToken), any()); 626 } 627 628 @Test 629 @EnableFlags(Flags.FLAG_RETURN_ANIMATION_FRAMEWORK_LONG_LIVED) testRegisteredRemoteTransitionTakeover_flagEnabled()630 public void testRegisteredRemoteTransitionTakeover_flagEnabled() { 631 Transitions transitions = createTestTransitions(); 632 transitions.replaceDefaultHandlerForTest(mDefaultHandler); 633 634 IRemoteTransition testRemote = new RemoteTransitionStub() { 635 @Override 636 public void startAnimation(IBinder token, TransitionInfo info, 637 SurfaceControl.Transaction t, 638 IRemoteTransitionFinishedCallback finishCallback) throws RemoteException { 639 final Transitions.TransitionHandler takeoverHandler = 640 transitions.getHandlerForTakeover(token, info); 641 642 if (takeoverHandler == null) { 643 finishCallback.onTransitionFinished(null /* wct */, null /* sct */); 644 return; 645 } 646 647 takeoverHandler.takeOverAnimation(token, info, new SurfaceControl.Transaction(), 648 wct -> { 649 try { 650 finishCallback.onTransitionFinished(wct, null /* sct */); 651 } catch (RemoteException e) { 652 // Fail 653 } 654 }, new WindowAnimationState[info.getChanges().size()]); 655 } 656 }; 657 final boolean[] takeoverRemoteCalled = new boolean[]{false}; 658 IRemoteTransition testTakeoverRemote = new RemoteTransitionStub() { 659 @Override 660 public void startAnimation(IBinder token, TransitionInfo info, 661 SurfaceControl.Transaction t, 662 IRemoteTransitionFinishedCallback finishCallback) {} 663 664 @Override 665 public void takeOverAnimation(IBinder transition, TransitionInfo info, 666 SurfaceControl.Transaction startTransaction, 667 IRemoteTransitionFinishedCallback finishCallback, WindowAnimationState[] states) 668 throws RemoteException { 669 takeoverRemoteCalled[0] = true; 670 finishCallback.onTransitionFinished(null /* wct */, null /* sct */); 671 } 672 }; 673 674 TransitionFilter filter = new TransitionFilter(); 675 filter.mRequirements = 676 new TransitionFilter.Requirement[]{new TransitionFilter.Requirement()}; 677 filter.mRequirements[0].mModes = new int[]{TRANSIT_OPEN, TRANSIT_TO_FRONT}; 678 679 transitions.registerRemote(filter, new RemoteTransition(testRemote, "Test")); 680 transitions.registerRemoteForTakeover( 681 filter, new RemoteTransition(testTakeoverRemote, "Test")); 682 mMainExecutor.flushAll(); 683 684 // Takeover should happen when the flag is enabled. 685 IBinder transitToken = new Binder(); 686 transitions.requestStartTransition(transitToken, 687 new TransitionRequestInfo(TRANSIT_OPEN, null /* trigger */, null /* remote */)); 688 TransitionInfo info = new TransitionInfoBuilder(TRANSIT_OPEN) 689 .addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build(); 690 transitions.onTransitionReady(transitToken, info, new StubTransaction(), 691 new StubTransaction()); 692 assertEquals(0, mDefaultHandler.activeCount()); 693 assertTrue(takeoverRemoteCalled[0]); 694 mDefaultHandler.finishAll(); 695 mMainExecutor.flushAll(); 696 verify(mOrganizer, times(1)).finishTransition(eq(transitToken), any()); 697 } 698 699 @Test testOneShotRemoteHandler()700 public void testOneShotRemoteHandler() { 701 Transitions transitions = createTestTransitions(); 702 transitions.replaceDefaultHandlerForTest(mDefaultHandler); 703 704 final boolean[] remoteCalled = new boolean[]{false}; 705 final boolean[] takeoverRemoteCalled = new boolean[]{false}; 706 final WindowContainerTransaction remoteFinishWCT = new WindowContainerTransaction(); 707 IRemoteTransition testRemote = new RemoteTransitionStub() { 708 @Override 709 public void startAnimation(IBinder token, TransitionInfo info, 710 SurfaceControl.Transaction t, 711 IRemoteTransitionFinishedCallback finishCallback) throws RemoteException { 712 remoteCalled[0] = true; 713 finishCallback.onTransitionFinished(remoteFinishWCT, null /* sct */); 714 } 715 716 @Override 717 public void takeOverAnimation(IBinder transition, TransitionInfo info, 718 SurfaceControl.Transaction startTransaction, 719 IRemoteTransitionFinishedCallback finishCallback, WindowAnimationState[] states) 720 throws RemoteException { 721 takeoverRemoteCalled[0] = true; 722 finishCallback.onTransitionFinished(remoteFinishWCT, null /* sct */); 723 } 724 }; 725 726 final int transitType = TRANSIT_FIRST_CUSTOM + 1; 727 728 OneShotRemoteHandler oneShot = new OneShotRemoteHandler(mMainExecutor, 729 new RemoteTransition(testRemote, "Test")); 730 731 // Verify that it responds to the remote but not other things. 732 IBinder transitToken = new Binder(); 733 assertNotNull(oneShot.handleRequest(transitToken, 734 new TransitionRequestInfo(transitType, null, 735 new RemoteTransition(testRemote, "Test")))); 736 assertNull(oneShot.handleRequest(transitToken, 737 new TransitionRequestInfo(transitType, null, null))); 738 739 Transitions.TransitionFinishCallback testFinish = 740 mock(Transitions.TransitionFinishCallback.class); 741 742 // Verify that it responds to animation properly 743 oneShot.setTransition(transitToken); 744 IBinder anotherToken = new Binder(); 745 assertFalse(oneShot.startAnimation(anotherToken, new TransitionInfo(transitType, 0), 746 new StubTransaction(), new StubTransaction(), 747 testFinish)); 748 assertTrue(oneShot.startAnimation(transitToken, new TransitionInfo(transitType, 0), 749 new StubTransaction(), new StubTransaction(), 750 testFinish)); 751 assertTrue(remoteCalled[0]); 752 753 // Verify that it handles takeovers properly 754 IBinder newToken = new Binder(); 755 oneShot.setTransition(newToken); 756 assertFalse(oneShot.takeOverAnimation(transitToken, new TransitionInfo(transitType, 0), 757 new StubTransaction(), testFinish, new WindowAnimationState[0])); 758 assertTrue(oneShot.takeOverAnimation(newToken, new TransitionInfo(transitType, 0), 759 new StubTransaction(), testFinish, new WindowAnimationState[0])); 760 assertTrue(takeoverRemoteCalled[0]); 761 } 762 763 @Test testTransitionQueueing()764 public void testTransitionQueueing() { 765 Transitions transitions = createTestTransitions(); 766 transitions.replaceDefaultHandlerForTest(mDefaultHandler); 767 768 IBinder transitToken1 = new Binder(); 769 transitions.requestStartTransition(transitToken1, 770 new TransitionRequestInfo(TRANSIT_OPEN, null /* trigger */, null /* remote */)); 771 TransitionInfo info1 = new TransitionInfoBuilder(TRANSIT_OPEN) 772 .addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build(); 773 transitions.onTransitionReady(transitToken1, info1, new StubTransaction(), 774 new StubTransaction()); 775 assertEquals(1, mDefaultHandler.activeCount()); 776 777 IBinder transitToken2 = new Binder(); 778 transitions.requestStartTransition(transitToken2, 779 new TransitionRequestInfo(TRANSIT_CLOSE, null /* trigger */, null /* remote */)); 780 TransitionInfo info2 = new TransitionInfoBuilder(TRANSIT_CLOSE) 781 .addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build(); 782 transitions.onTransitionReady(transitToken2, info2, new StubTransaction(), 783 new StubTransaction()); 784 // default handler doesn't merge by default, so it shouldn't increment active count. 785 assertEquals(1, mDefaultHandler.activeCount()); 786 assertEquals(0, mDefaultHandler.mergeCount()); 787 verify(mOrganizer, times(0)).finishTransition(eq(transitToken1), any()); 788 verify(mOrganizer, times(0)).finishTransition(eq(transitToken2), any()); 789 790 mDefaultHandler.finishAll(); 791 mMainExecutor.flushAll(); 792 // first transition finished 793 verify(mOrganizer, times(1)).finishTransition(eq(transitToken1), any()); 794 verify(mOrganizer, times(0)).finishTransition(eq(transitToken2), any()); 795 // But now the "queued" transition is running 796 assertEquals(1, mDefaultHandler.activeCount()); 797 798 mDefaultHandler.finishAll(); 799 mMainExecutor.flushAll(); 800 verify(mOrganizer, times(1)).finishTransition(eq(transitToken2), any()); 801 } 802 803 @Test testTransitionMerging()804 public void testTransitionMerging() { 805 Transitions transitions = createTestTransitions(); 806 mDefaultHandler.setSimulateMerge(true); 807 transitions.replaceDefaultHandlerForTest(mDefaultHandler); 808 809 IBinder transitToken1 = new Binder(); 810 transitions.requestStartTransition(transitToken1, 811 new TransitionRequestInfo(TRANSIT_OPEN, null /* trigger */, null /* remote */)); 812 TransitionInfo info1 = new TransitionInfoBuilder(TRANSIT_OPEN) 813 .addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build(); 814 transitions.onTransitionReady(transitToken1, info1, new StubTransaction(), 815 new StubTransaction()); 816 assertEquals(1, mDefaultHandler.activeCount()); 817 818 IBinder transitToken2 = new Binder(); 819 transitions.requestStartTransition(transitToken2, 820 new TransitionRequestInfo(TRANSIT_CLOSE, null /* trigger */, null /* remote */)); 821 TransitionInfo info2 = new TransitionInfoBuilder(TRANSIT_CLOSE) 822 .addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build(); 823 transitions.onTransitionReady(transitToken2, info2, new StubTransaction(), 824 new StubTransaction()); 825 // it should still only have 1 active, but then show 1 merged 826 assertEquals(1, mDefaultHandler.activeCount()); 827 assertEquals(1, mDefaultHandler.mergeCount()); 828 verify(mOrganizer, times(0)).finishTransition(eq(transitToken1), any()); 829 // We don't tell organizer it is finished yet (since we still want to maintain ordering) 830 verify(mOrganizer, times(0)).finishTransition(eq(transitToken2), any()); 831 832 mDefaultHandler.finishAll(); 833 mMainExecutor.flushAll(); 834 // transition + merged all finished. 835 verify(mOrganizer, times(1)).finishTransition(eq(transitToken1), any()); 836 verify(mOrganizer, times(1)).finishTransition(eq(transitToken2), any()); 837 // Make sure nothing was queued 838 assertEquals(0, mDefaultHandler.activeCount()); 839 } 840 841 842 @Test testTransitionMergingOnFinish()843 public void testTransitionMergingOnFinish() { 844 final Transitions transitions = createTestTransitions(); 845 transitions.replaceDefaultHandlerForTest(mDefaultHandler); 846 847 // The current transition. 848 final IBinder transitToken1 = new Binder(); 849 requestStartTransition(transitions, transitToken1); 850 onTransitionReady(transitions, transitToken1); 851 852 // The next ready transition. 853 final IBinder transitToken2 = new Binder(); 854 requestStartTransition(transitions, transitToken2); 855 onTransitionReady(transitions, transitToken2); 856 857 // The non-ready merge candidate. 858 final IBinder transitTokenNotReady = new Binder(); 859 requestStartTransition(transitions, transitTokenNotReady); 860 861 mDefaultHandler.setSimulateMerge(true); 862 mDefaultHandler.mFinishes.get(0).second.onTransitionFinished(null /* wct */); 863 864 // Make sure that the non-ready transition is not merged. 865 assertEquals(0, mDefaultHandler.mergeCount()); 866 } 867 868 @Test testInterleavedMerging()869 public void testInterleavedMerging() { 870 Transitions transitions = createTestTransitions(); 871 transitions.replaceDefaultHandlerForTest(mDefaultHandler); 872 873 Function<Boolean, IBinder> startATransition = (doMerge) -> { 874 IBinder token = new Binder(); 875 if (doMerge) { 876 mDefaultHandler.setShouldMerge(token); 877 } 878 transitions.requestStartTransition(token, 879 new TransitionRequestInfo(TRANSIT_OPEN, null /* trigger */, null /* remote */)); 880 TransitionInfo info = new TransitionInfoBuilder(TRANSIT_OPEN) 881 .addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build(); 882 transitions.onTransitionReady(token, info, new StubTransaction(), 883 new StubTransaction()); 884 return token; 885 }; 886 887 IBinder transitToken1 = startATransition.apply(false); 888 // merge first one 889 IBinder transitToken2 = startATransition.apply(true); 890 assertEquals(1, mDefaultHandler.activeCount()); 891 assertEquals(1, mDefaultHandler.mergeCount()); 892 893 // don't merge next one 894 IBinder transitToken3 = startATransition.apply(false); 895 // make sure nothing happened (since it wasn't merged) 896 assertEquals(1, mDefaultHandler.activeCount()); 897 assertEquals(1, mDefaultHandler.mergeCount()); 898 899 // make a mergable 900 IBinder transitToken4 = startATransition.apply(true); 901 // make sure nothing happened since there is a non-mergable pending. 902 assertEquals(1, mDefaultHandler.activeCount()); 903 assertEquals(1, mDefaultHandler.mergeCount()); 904 905 // Queue up another mergable 906 IBinder transitToken5 = startATransition.apply(true); 907 908 // Queue up a non-mergable 909 IBinder transitToken6 = startATransition.apply(false); 910 911 // Our active now looks like: [playing, merged] 912 // and ready queue: [non-mergable, mergable, mergable, non-mergable] 913 // finish the playing one 914 mDefaultHandler.finishOne(); 915 mMainExecutor.flushAll(); 916 // Now we should have the non-mergable playing now with 2 merged: 917 // active: [playing, merged, merged] queue: [non-mergable] 918 assertEquals(1, mDefaultHandler.activeCount()); 919 assertEquals(2, mDefaultHandler.mergeCount()); 920 921 mDefaultHandler.finishOne(); 922 mMainExecutor.flushAll(); 923 assertEquals(1, mDefaultHandler.activeCount()); 924 assertEquals(0, mDefaultHandler.mergeCount()); 925 926 mDefaultHandler.finishOne(); 927 mMainExecutor.flushAll(); 928 } 929 930 @Test testTransitionOrderMatchesCore()931 public void testTransitionOrderMatchesCore() { 932 Transitions transitions = createTestTransitions(); 933 transitions.replaceDefaultHandlerForTest(mDefaultHandler); 934 935 IBinder transitToken = new Binder(); 936 IBinder shellInit = transitions.startTransition(TRANSIT_CLOSE, 937 new WindowContainerTransaction(), null /* handler */); 938 // make sure we are testing the "New" API. 939 verify(mOrganizer, times(1)).startNewTransition(eq(TRANSIT_CLOSE), any()); 940 // WMCore may not receive the new transition before requesting its own. 941 transitions.requestStartTransition(transitToken, 942 new TransitionRequestInfo(TRANSIT_OPEN, null /* trigger */, null /* remote */)); 943 verify(mOrganizer, times(1)).startTransition(eq(transitToken), any()); 944 945 // At this point, WM is working on its transition (the shell-initialized one is still 946 // queued), so continue the transition lifecycle for that. 947 TransitionInfo info = new TransitionInfoBuilder(TRANSIT_OPEN) 948 .addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build(); 949 transitions.onTransitionReady(transitToken, info, new StubTransaction(), 950 new StubTransaction()); 951 // At this point, if things are not working, we'd get an NPE due to attempting to merge 952 // into the shellInit transition which hasn't started yet. 953 assertEquals(1, mDefaultHandler.activeCount()); 954 } 955 956 @Test testShouldRotateSeamlessly()957 public void testShouldRotateSeamlessly() throws Exception { 958 final RunningTaskInfo taskInfo = 959 createTaskInfo(1, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD); 960 final RunningTaskInfo taskInfoPip = 961 createTaskInfo(1, WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD); 962 963 final DisplayController displays = createTestDisplayController(); 964 final DisplayLayout displayLayout = displays.getDisplayLayout(DEFAULT_DISPLAY); 965 final @Surface.Rotation int upsideDown = displayLayout.getUpsideDownRotation(); 966 967 TransitionInfo.Change displayChange = new ChangeBuilder(TRANSIT_CHANGE) 968 .setFlags(FLAG_IS_DISPLAY).setRotate().build(); 969 // Set non-square display so nav bar won't be allowed to move. 970 displayChange.getStartAbsBounds().set(0, 0, 1000, 2000); 971 final TransitionInfo normalDispRotate = new TransitionInfoBuilder(TRANSIT_CHANGE) 972 .addChange(displayChange) 973 .addChange(new ChangeBuilder(TRANSIT_CHANGE).setTask(taskInfo).setRotate().build()) 974 .build(); 975 assertEquals(ROTATION_ANIMATION_ROTATE, DefaultTransitionHandler.getRotationAnimationHint( 976 displayChange, normalDispRotate, displays)); 977 978 // Seamless if all tasks are seamless 979 final TransitionInfo rotateSeamless = new TransitionInfoBuilder(TRANSIT_CHANGE) 980 .addChange(displayChange) 981 .addChange(new ChangeBuilder(TRANSIT_CHANGE).setTask(taskInfo) 982 .setRotate(ROTATION_ANIMATION_SEAMLESS).build()) 983 .build(); 984 assertEquals(ROTATION_ANIMATION_SEAMLESS, DefaultTransitionHandler.getRotationAnimationHint( 985 displayChange, rotateSeamless, displays)); 986 987 // Not seamless if there is PiP (or any other non-seamless task) 988 final TransitionInfo pipDispRotate = new TransitionInfoBuilder(TRANSIT_CHANGE) 989 .addChange(displayChange) 990 .addChange(new ChangeBuilder(TRANSIT_CHANGE).setTask(taskInfo) 991 .setRotate(ROTATION_ANIMATION_SEAMLESS).build()) 992 .addChange(new ChangeBuilder(TRANSIT_CHANGE).setTask(taskInfoPip) 993 .setRotate().build()) 994 .build(); 995 assertEquals(ROTATION_ANIMATION_ROTATE, DefaultTransitionHandler.getRotationAnimationHint( 996 displayChange, pipDispRotate, displays)); 997 998 // Not seamless if there is no changed task. 999 final TransitionInfo noTask = new TransitionInfoBuilder(TRANSIT_CHANGE) 1000 .addChange(displayChange) 1001 .build(); 1002 assertEquals(ROTATION_ANIMATION_ROTATE, DefaultTransitionHandler.getRotationAnimationHint( 1003 displayChange, noTask, displays)); 1004 1005 // Not seamless if the nav bar cares rotation and one of rotations is upside-down. 1006 doReturn(false).when(displayLayout).allowSeamlessRotationDespiteNavBarMoving(); 1007 displayChange = new ChangeBuilder(TRANSIT_CHANGE).setFlags(FLAG_IS_DISPLAY) 1008 .setRotate(upsideDown, ROTATION_ANIMATION_UNSPECIFIED).build(); 1009 final TransitionInfo seamlessUpsideDown = new TransitionInfoBuilder(TRANSIT_CHANGE) 1010 .addChange(displayChange) 1011 .addChange(new ChangeBuilder(TRANSIT_CHANGE).setTask(taskInfo) 1012 .setRotate(upsideDown, ROTATION_ANIMATION_SEAMLESS).build()) 1013 .build(); 1014 assertEquals(ROTATION_ANIMATION_ROTATE, DefaultTransitionHandler.getRotationAnimationHint( 1015 displayChange, seamlessUpsideDown, displays)); 1016 1017 // Not seamless if system alert windows 1018 displayChange = new ChangeBuilder(TRANSIT_CHANGE) 1019 .setFlags(FLAG_IS_DISPLAY | FLAG_DISPLAY_HAS_ALERT_WINDOWS).setRotate().build(); 1020 final TransitionInfo seamlessButAlert = new TransitionInfoBuilder(TRANSIT_CHANGE) 1021 .addChange(displayChange) 1022 .addChange(new ChangeBuilder(TRANSIT_CHANGE).setTask(taskInfo) 1023 .setRotate(ROTATION_ANIMATION_SEAMLESS).build()) 1024 .build(); 1025 assertEquals(ROTATION_ANIMATION_ROTATE, DefaultTransitionHandler.getRotationAnimationHint( 1026 displayChange, seamlessButAlert, displays)); 1027 1028 // Seamless if display is explicitly seamless. 1029 displayChange = new ChangeBuilder(TRANSIT_CHANGE).setFlags(FLAG_IS_DISPLAY) 1030 .setRotate(ROTATION_ANIMATION_SEAMLESS).build(); 1031 final TransitionInfo seamlessDisplay = new TransitionInfoBuilder(TRANSIT_CHANGE) 1032 .addChange(displayChange) 1033 // The animation hint of task will be ignored. 1034 .addChange(new ChangeBuilder(TRANSIT_CHANGE).setTask(taskInfo) 1035 .setRotate(ROTATION_ANIMATION_ROTATE).build()) 1036 .build(); 1037 assertEquals(ROTATION_ANIMATION_SEAMLESS, DefaultTransitionHandler.getRotationAnimationHint( 1038 displayChange, seamlessDisplay, displays)); 1039 } 1040 1041 @Test testRunWhenIdle()1042 public void testRunWhenIdle() { 1043 Transitions transitions = createTestTransitions(); 1044 transitions.replaceDefaultHandlerForTest(mDefaultHandler); 1045 1046 Runnable runnable1 = mock(Runnable.class); 1047 Runnable runnable2 = mock(Runnable.class); 1048 Runnable runnable3 = mock(Runnable.class); 1049 Runnable runnable4 = mock(Runnable.class); 1050 1051 transitions.runOnIdle(runnable1); 1052 1053 // runnable1 is executed immediately because there are no active transitions. 1054 verify(runnable1, times(1)).run(); 1055 1056 clearInvocations(runnable1); 1057 1058 IBinder transitToken1 = new Binder(); 1059 transitions.requestStartTransition(transitToken1, 1060 new TransitionRequestInfo(TRANSIT_OPEN, null /* trigger */, null /* remote */)); 1061 TransitionInfo info1 = new TransitionInfoBuilder(TRANSIT_OPEN) 1062 .addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build(); 1063 transitions.onTransitionReady(transitToken1, info1, new StubTransaction(), 1064 new StubTransaction()); 1065 assertEquals(1, mDefaultHandler.activeCount()); 1066 1067 transitions.runOnIdle(runnable2); 1068 transitions.runOnIdle(runnable3); 1069 1070 // runnable2 and runnable3 aren't executed immediately because there is an active 1071 // transaction. 1072 1073 IBinder transitToken2 = new Binder(); 1074 transitions.requestStartTransition(transitToken2, 1075 new TransitionRequestInfo(TRANSIT_CLOSE, null /* trigger */, null /* remote */)); 1076 TransitionInfo info2 = new TransitionInfoBuilder(TRANSIT_CLOSE) 1077 .addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build(); 1078 transitions.onTransitionReady(transitToken2, info2, new StubTransaction(), 1079 new StubTransaction()); 1080 assertEquals(1, mDefaultHandler.activeCount()); 1081 1082 mDefaultHandler.finishAll(); 1083 mMainExecutor.flushAll(); 1084 // first transition finished 1085 verify(mOrganizer, times(1)).finishTransition(eq(transitToken1), any()); 1086 verify(mOrganizer, times(0)).finishTransition(eq(transitToken2), any()); 1087 // But now the "queued" transition is running 1088 assertEquals(1, mDefaultHandler.activeCount()); 1089 1090 // runnable2 and runnable3 are still not executed because the second transition is still 1091 // active. 1092 verify(runnable2, times(0)).run(); 1093 verify(runnable3, times(0)).run(); 1094 1095 mDefaultHandler.finishAll(); 1096 mMainExecutor.flushAll(); 1097 verify(mOrganizer, times(1)).finishTransition(eq(transitToken2), any()); 1098 1099 // runnable2 and runnable3 are executed after the second transition finishes because there 1100 // are no other active transitions, runnable1 isn't executed again. 1101 verify(runnable1, times(0)).run(); 1102 verify(runnable2, times(1)).run(); 1103 verify(runnable3, times(1)).run(); 1104 1105 clearInvocations(runnable2); 1106 clearInvocations(runnable3); 1107 1108 transitions.runOnIdle(runnable4); 1109 1110 // runnable4 is executed immediately because there are no active transitions, all other 1111 // runnables aren't executed again. 1112 verify(runnable1, times(0)).run(); 1113 verify(runnable2, times(0)).run(); 1114 verify(runnable3, times(0)).run(); 1115 verify(runnable4, times(1)).run(); 1116 } 1117 1118 @Test testObserverLifecycle_basicTransitionFlow()1119 public void testObserverLifecycle_basicTransitionFlow() { 1120 Transitions transitions = createTestTransitions(); 1121 Transitions.TransitionObserver observer = mock(Transitions.TransitionObserver.class); 1122 transitions.registerObserver(observer); 1123 transitions.replaceDefaultHandlerForTest(mDefaultHandler); 1124 1125 IBinder transitToken = new Binder(); 1126 transitions.requestStartTransition(transitToken, 1127 new TransitionRequestInfo(TRANSIT_OPEN, null /* trigger */, null /* remote */)); 1128 TransitionInfo info = new TransitionInfoBuilder(TRANSIT_OPEN) 1129 .addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build(); 1130 SurfaceControl.Transaction startT = new StubTransaction(); 1131 SurfaceControl.Transaction finishT = new StubTransaction(); 1132 transitions.onTransitionReady(transitToken, info, startT, finishT); 1133 1134 InOrder observerOrder = inOrder(observer); 1135 observerOrder.verify(observer).onTransitionReady(transitToken, info, startT, finishT); 1136 observerOrder.verify(observer).onTransitionStarting(transitToken); 1137 verify(observer, times(0)).onTransitionFinished(eq(transitToken), anyBoolean()); 1138 mDefaultHandler.finishAll(); 1139 mMainExecutor.flushAll(); 1140 verify(observer).onTransitionFinished(transitToken, false); 1141 } 1142 1143 @Test testObserverLifecycle_queueing()1144 public void testObserverLifecycle_queueing() { 1145 Transitions transitions = createTestTransitions(); 1146 Transitions.TransitionObserver observer = mock(Transitions.TransitionObserver.class); 1147 transitions.registerObserver(observer); 1148 transitions.replaceDefaultHandlerForTest(mDefaultHandler); 1149 1150 IBinder transitToken1 = new Binder(); 1151 transitions.requestStartTransition(transitToken1, 1152 new TransitionRequestInfo(TRANSIT_OPEN, null /* trigger */, null /* remote */)); 1153 TransitionInfo info1 = new TransitionInfoBuilder(TRANSIT_OPEN) 1154 .addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build(); 1155 SurfaceControl.Transaction startT1 = new StubTransaction(); 1156 SurfaceControl.Transaction finishT1 = new StubTransaction(); 1157 transitions.onTransitionReady(transitToken1, info1, startT1, finishT1); 1158 verify(observer).onTransitionReady(transitToken1, info1, startT1, finishT1); 1159 1160 IBinder transitToken2 = new Binder(); 1161 transitions.requestStartTransition(transitToken2, 1162 new TransitionRequestInfo(TRANSIT_CLOSE, null /* trigger */, null /* remote */)); 1163 TransitionInfo info2 = new TransitionInfoBuilder(TRANSIT_CLOSE) 1164 .addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build(); 1165 SurfaceControl.Transaction startT2 = new StubTransaction(); 1166 SurfaceControl.Transaction finishT2 = new StubTransaction(); 1167 transitions.onTransitionReady(transitToken2, info2, startT2, finishT2); 1168 verify(observer, times(1)).onTransitionReady(transitToken2, info2, startT2, finishT2); 1169 verify(observer, times(0)).onTransitionStarting(transitToken2); 1170 verify(observer, times(0)).onTransitionFinished(eq(transitToken1), anyBoolean()); 1171 verify(observer, times(0)).onTransitionFinished(eq(transitToken2), anyBoolean()); 1172 1173 mDefaultHandler.finishAll(); 1174 mMainExecutor.flushAll(); 1175 // first transition finished 1176 verify(observer, times(1)).onTransitionFinished(transitToken1, false); 1177 verify(observer, times(1)).onTransitionStarting(transitToken2); 1178 verify(observer, times(0)).onTransitionFinished(eq(transitToken2), anyBoolean()); 1179 1180 mDefaultHandler.finishAll(); 1181 mMainExecutor.flushAll(); 1182 verify(observer, times(1)).onTransitionFinished(transitToken2, false); 1183 } 1184 1185 1186 @Test testObserverLifecycle_merging()1187 public void testObserverLifecycle_merging() { 1188 Transitions transitions = createTestTransitions(); 1189 Transitions.TransitionObserver observer = mock(Transitions.TransitionObserver.class); 1190 transitions.registerObserver(observer); 1191 mDefaultHandler.setSimulateMerge(true); 1192 transitions.replaceDefaultHandlerForTest(mDefaultHandler); 1193 1194 IBinder transitToken1 = new Binder(); 1195 transitions.requestStartTransition(transitToken1, 1196 new TransitionRequestInfo(TRANSIT_OPEN, null /* trigger */, null /* remote */)); 1197 TransitionInfo info1 = new TransitionInfoBuilder(TRANSIT_OPEN) 1198 .addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build(); 1199 SurfaceControl.Transaction startT1 = new StubTransaction(); 1200 SurfaceControl.Transaction finishT1 = new StubTransaction(); 1201 transitions.onTransitionReady(transitToken1, info1, startT1, finishT1); 1202 1203 IBinder transitToken2 = new Binder(); 1204 transitions.requestStartTransition(transitToken2, 1205 new TransitionRequestInfo(TRANSIT_CLOSE, null /* trigger */, null /* remote */)); 1206 TransitionInfo info2 = new TransitionInfoBuilder(TRANSIT_CLOSE) 1207 .addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build(); 1208 SurfaceControl.Transaction startT2 = new StubTransaction(); 1209 SurfaceControl.Transaction finishT2 = new StubTransaction(); 1210 transitions.onTransitionReady(transitToken2, info2, startT2, finishT2); 1211 1212 InOrder observerOrder = inOrder(observer); 1213 observerOrder.verify(observer).onTransitionReady(transitToken2, info2, startT2, finishT2); 1214 observerOrder.verify(observer).onTransitionMerged(transitToken2, transitToken1); 1215 verify(observer, times(0)).onTransitionFinished(eq(transitToken1), anyBoolean()); 1216 1217 mDefaultHandler.finishAll(); 1218 mMainExecutor.flushAll(); 1219 // transition + merged all finished. 1220 verify(observer, times(1)).onTransitionFinished(transitToken1, false); 1221 // Merged transition won't receive any lifecycle calls beyond ready 1222 verify(observer, times(0)).onTransitionStarting(transitToken2); 1223 verify(observer, times(0)).onTransitionFinished(eq(transitToken2), anyBoolean()); 1224 } 1225 1226 @Test testObserverLifecycle_mergingAfterQueueing()1227 public void testObserverLifecycle_mergingAfterQueueing() { 1228 Transitions transitions = createTestTransitions(); 1229 Transitions.TransitionObserver observer = mock(Transitions.TransitionObserver.class); 1230 transitions.registerObserver(observer); 1231 mDefaultHandler.setSimulateMerge(true); 1232 transitions.replaceDefaultHandlerForTest(mDefaultHandler); 1233 1234 // Make a test handler that only responds to multi-window triggers AND only animates 1235 // Change transitions. 1236 final WindowContainerTransaction handlerWCT = new WindowContainerTransaction(); 1237 TestTransitionHandler testHandler = new TestTransitionHandler() { 1238 @Override 1239 public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info, 1240 @NonNull SurfaceControl.Transaction startTransaction, 1241 @NonNull SurfaceControl.Transaction finishTransaction, 1242 @NonNull Transitions.TransitionFinishCallback finishCallback) { 1243 for (TransitionInfo.Change chg : info.getChanges()) { 1244 if (chg.getMode() == TRANSIT_CHANGE) { 1245 return super.startAnimation(transition, info, startTransaction, 1246 finishTransaction, finishCallback); 1247 } 1248 } 1249 return false; 1250 } 1251 1252 @Nullable 1253 @Override 1254 public WindowContainerTransaction handleRequest(@NonNull IBinder transition, 1255 @NonNull TransitionRequestInfo request) { 1256 final RunningTaskInfo task = request.getTriggerTask(); 1257 return (task != null && task.getWindowingMode() == WINDOWING_MODE_MULTI_WINDOW) 1258 ? handlerWCT : null; 1259 } 1260 }; 1261 transitions.addHandler(testHandler); 1262 1263 // Use test handler to play an animation 1264 IBinder transitToken1 = new Binder(); 1265 RunningTaskInfo mwTaskInfo = 1266 createTaskInfo(1, WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD); 1267 transitions.requestStartTransition(transitToken1, 1268 new TransitionRequestInfo(TRANSIT_CHANGE, mwTaskInfo, null /* remote */)); 1269 TransitionInfo change = new TransitionInfoBuilder(TRANSIT_CHANGE) 1270 .addChange(TRANSIT_CHANGE).build(); 1271 SurfaceControl.Transaction startT1 = new StubTransaction(); 1272 SurfaceControl.Transaction finishT1 = new StubTransaction(); 1273 transitions.onTransitionReady(transitToken1, change, startT1, finishT1); 1274 1275 // Request the second transition that should be handled by the default handler 1276 IBinder transitToken2 = new Binder(); 1277 TransitionInfo open = new TransitionInfoBuilder(TRANSIT_OPEN) 1278 .addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build(); 1279 transitions.requestStartTransition(transitToken2, 1280 new TransitionRequestInfo(TRANSIT_OPEN, null /* trigger */, null /* remote */)); 1281 SurfaceControl.Transaction startT2 = new StubTransaction(); 1282 SurfaceControl.Transaction finishT2 = new StubTransaction(); 1283 transitions.onTransitionReady(transitToken2, open, startT2, finishT2); 1284 verify(observer).onTransitionReady(transitToken2, open, startT2, finishT2); 1285 verify(observer, times(0)).onTransitionStarting(transitToken2); 1286 1287 // Request the third transition that should be merged into the second one 1288 IBinder transitToken3 = new Binder(); 1289 transitions.requestStartTransition(transitToken3, 1290 new TransitionRequestInfo(TRANSIT_OPEN, null /* trigger */, null /* remote */)); 1291 SurfaceControl.Transaction startT3 = new StubTransaction(); 1292 SurfaceControl.Transaction finishT3 = new StubTransaction(); 1293 transitions.onTransitionReady(transitToken3, open, startT3, finishT3); 1294 verify(observer, times(0)).onTransitionStarting(transitToken2); 1295 verify(observer).onTransitionReady(transitToken3, open, startT3, finishT3); 1296 verify(observer, times(0)).onTransitionStarting(transitToken3); 1297 1298 testHandler.finishAll(); 1299 mMainExecutor.flushAll(); 1300 1301 verify(observer).onTransitionFinished(transitToken1, false); 1302 1303 mDefaultHandler.finishAll(); 1304 mMainExecutor.flushAll(); 1305 1306 InOrder observerOrder = inOrder(observer); 1307 observerOrder.verify(observer).onTransitionStarting(transitToken2); 1308 observerOrder.verify(observer).onTransitionMerged(transitToken3, transitToken2); 1309 observerOrder.verify(observer).onTransitionFinished(transitToken2, false); 1310 1311 // Merged transition won't receive any lifecycle calls beyond ready 1312 verify(observer, times(0)).onTransitionStarting(transitToken3); 1313 verify(observer, times(0)).onTransitionFinished(eq(transitToken3), anyBoolean()); 1314 } 1315 1316 @Test testTransitSleep_squashesRecents()1317 public void testTransitSleep_squashesRecents() { 1318 ShellInit shellInit = new ShellInit(mMainExecutor); 1319 final Transitions transitions = 1320 new Transitions(mContext, shellInit, mock(ShellController.class), mOrganizer, 1321 mTransactionPool, createTestDisplayController(), mDisplayInsets, 1322 mMainExecutor, mMainHandler, mAnimExecutor, mAnimHandler, 1323 mock(HomeTransitionObserver.class), 1324 mock(FocusTransitionObserver.class)); 1325 final RecentTasksController mockRecentsTaskController = mock(RecentTasksController.class); 1326 doReturn(mContext).when(mockRecentsTaskController).getContext(); 1327 final RecentsTransitionHandler recentsHandler = 1328 new RecentsTransitionHandler(shellInit, mock(ShellTaskOrganizer.class), transitions, 1329 mockRecentsTaskController, mock(HomeTransitionObserver.class)); 1330 transitions.replaceDefaultHandlerForTest(mDefaultHandler); 1331 shellInit.init(); 1332 1333 Transitions.TransitionObserver observer = mock(Transitions.TransitionObserver.class); 1334 transitions.registerObserver(observer); 1335 1336 RunningTaskInfo task1 = createTaskInfo(1, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_RECENTS); 1337 RunningTaskInfo task2 = createTaskInfo(2); 1338 1339 // Start an open transition for the purpose of occupying the ready queue 1340 final IBinder transitOpen1 = new Binder("transitOpen1"); 1341 final TransitionInfo infoOpen1 = 1342 new TransitionInfoBuilder(TRANSIT_OPEN) 1343 .addChange(TRANSIT_OPEN, task1) 1344 .build(); 1345 mMainExecutor.execute(() -> { 1346 transitions.requestStartTransition(transitOpen1, new TransitionRequestInfo( 1347 TRANSIT_OPEN, task1 /* trigger */, null /* remote */)); 1348 onTransitionReady(transitions, transitOpen1, infoOpen1); 1349 }); 1350 1351 // First transition on the queue should start immediately. 1352 mMainExecutor.flushAll(); 1353 verify(observer).onTransitionReady(eq(transitOpen1), any(), any(), any()); 1354 verify(observer).onTransitionStarting(eq(transitOpen1)); 1355 1356 // Start recents 1357 final IRecentsAnimationRunner recentsListener = 1358 mock(IRecentsAnimationRunner.class, Answers.RETURNS_DEEP_STUBS); 1359 final IBinder transitRecents = recentsHandler.startRecentsTransition( 1360 mock(PendingIntent.class) /* intent */, 1361 mock(Intent.class) /* fillIn */, 1362 new Bundle() /* options */, 1363 mock(IApplicationThread.class) /* appThread */, 1364 recentsListener); 1365 final TransitionInfo infoRecents = 1366 new TransitionInfoBuilder(TRANSIT_TO_FRONT) 1367 .addChange(TRANSIT_TO_FRONT, task1) 1368 .addChange(TRANSIT_CLOSE, task2) 1369 .build(); 1370 onTransitionReady(transitions, transitRecents, infoRecents); 1371 1372 // Start another open transition during recents 1373 final IBinder transitOpen2 = new Binder("transitOpen2"); 1374 final TransitionInfo infoOpen2 = 1375 new TransitionInfoBuilder(TRANSIT_OPEN) 1376 .addChange(TRANSIT_OPEN, task2) 1377 .addChange(TRANSIT_TO_BACK, task1) 1378 .build(); 1379 mMainExecutor.execute(() -> { 1380 transitions.requestStartTransition(transitOpen2, new TransitionRequestInfo( 1381 TRANSIT_OPEN, task2 /* trigger */, null /* remote */)); 1382 onTransitionReady(transitions, transitOpen2, infoOpen2); 1383 }); 1384 1385 // Finish testOpen1 to start processing the other transitions 1386 mMainExecutor.execute(() -> { 1387 mDefaultHandler.finishOne(); 1388 }); 1389 mMainExecutor.flushAll(); 1390 1391 // Recents transition SHOULD start, and merge the open transition, which should NOT start. 1392 verify(observer).onTransitionFinished(eq(transitOpen1), eq(false) /* aborted */); 1393 verify(observer).onTransitionReady(eq(transitRecents), any(), any(), any()); 1394 verify(observer).onTransitionStarting(eq(transitRecents)); 1395 verify(observer).onTransitionReady(eq(transitOpen2), any(), any(), any()); 1396 verify(observer).onTransitionMerged(eq(transitOpen2), eq(transitRecents)); 1397 // verify(observer).onTransitionFinished(eq(transitOpen2), eq(true) /* aborted */); 1398 1399 // Go to sleep 1400 final IBinder transitSleep = new Binder("transitSleep"); 1401 final TransitionInfo infoSleep = new TransitionInfoBuilder(TRANSIT_SLEEP).build(); 1402 mMainExecutor.execute(() -> { 1403 transitions.requestStartTransition(transitSleep, new TransitionRequestInfo( 1404 TRANSIT_SLEEP, null /* trigger */, null /* remote */)); 1405 onTransitionReady(transitions, transitSleep, infoSleep); 1406 }); 1407 mMainExecutor.flushAll(); 1408 1409 // Recents transition should finish itself when it sees the sleep transition coming. 1410 verify(observer).onTransitionFinished(eq(transitRecents), eq(false)); 1411 verify(observer).onTransitionFinished(eq(transitSleep), eq(false)); 1412 } 1413 onTransitionReady(Transitions transitions, IBinder token, TransitionInfo info)1414 private void onTransitionReady(Transitions transitions, IBinder token, TransitionInfo info) { 1415 transitions.onTransitionReady(token, info, new StubTransaction(), 1416 new StubTransaction()); 1417 } 1418 1419 @Test testEmptyTransition_withKeyguardGoingAway_plays()1420 public void testEmptyTransition_withKeyguardGoingAway_plays() { 1421 Transitions transitions = createTestTransitions(); 1422 transitions.replaceDefaultHandlerForTest(mDefaultHandler); 1423 1424 IBinder transitToken = new Binder(); 1425 transitions.requestStartTransition(transitToken, 1426 new TransitionRequestInfo(TRANSIT_OPEN, null /* trigger */, null /* remote */)); 1427 1428 // Make a no-op transition 1429 TransitionInfo info = new TransitionInfoBuilder( 1430 TRANSIT_OPEN, TRANSIT_FLAG_KEYGUARD_GOING_AWAY, true /* noOp */).build(); 1431 transitions.onTransitionReady(transitToken, info, new StubTransaction(), 1432 new StubTransaction()); 1433 1434 // If keyguard-going-away flag set, then it shouldn't be aborted. 1435 assertEquals(1, mDefaultHandler.activeCount()); 1436 } 1437 1438 @Test testSleepTransition_withKeyguardGoingAway_plays()1439 public void testSleepTransition_withKeyguardGoingAway_plays(){ 1440 Transitions transitions = createTestTransitions(); 1441 transitions.replaceDefaultHandlerForTest(mDefaultHandler); 1442 1443 IBinder transitToken = new Binder(); 1444 transitions.requestStartTransition(transitToken, 1445 new TransitionRequestInfo(TRANSIT_SLEEP, null /* trigger */, null /* remote */)); 1446 1447 // Make a no-op transition 1448 TransitionInfo info = new TransitionInfoBuilder( 1449 TRANSIT_SLEEP, TRANSIT_FLAG_KEYGUARD_GOING_AWAY, true /* noOp */).build(); 1450 transitions.onTransitionReady(transitToken, info, new StubTransaction(), 1451 new StubTransaction()); 1452 1453 // If keyguard-going-away flag set, then it shouldn't be aborted. 1454 assertEquals(1, mDefaultHandler.activeCount()); 1455 } 1456 1457 @Test testSleepTransition_withChanges_plays()1458 public void testSleepTransition_withChanges_plays(){ 1459 Transitions transitions = createTestTransitions(); 1460 transitions.replaceDefaultHandlerForTest(mDefaultHandler); 1461 1462 IBinder transitToken = new Binder(); 1463 transitions.requestStartTransition(transitToken, 1464 new TransitionRequestInfo(TRANSIT_SLEEP, null /* trigger */, null /* remote */)); 1465 1466 // Make a transition with some changes 1467 TransitionInfo info = new TransitionInfoBuilder(TRANSIT_SLEEP) 1468 .addChange(TRANSIT_OPEN).build(); 1469 info.setTrack(0); 1470 transitions.onTransitionReady(transitToken, info, new StubTransaction(), 1471 new StubTransaction()); 1472 1473 // If there is an actual change, then it shouldn't be aborted. 1474 assertEquals(1, mDefaultHandler.activeCount()); 1475 } 1476 1477 1478 @Test testSleepTransition_empty_SyncBySleepHandler()1479 public void testSleepTransition_empty_SyncBySleepHandler() { 1480 Transitions transitions = createTestTransitions(); 1481 transitions.replaceDefaultHandlerForTest(mDefaultHandler); 1482 1483 IBinder transitToken = new Binder(); 1484 transitions.requestStartTransition(transitToken, 1485 new TransitionRequestInfo(TRANSIT_SLEEP, null /* trigger */, null /* remote */)); 1486 1487 // Make a no-op transition 1488 TransitionInfo info = new TransitionInfoBuilder( 1489 TRANSIT_SLEEP, 0x0, true /* noOp */).build(); 1490 transitions.onTransitionReady(transitToken, info, new StubTransaction(), 1491 new StubTransaction()); 1492 1493 // If there is nothing to actually play, it should not be offered to handlers. 1494 assertEquals(0, mDefaultHandler.activeCount()); 1495 } 1496 1497 @Test testMultipleTracks()1498 public void testMultipleTracks() { 1499 Transitions transitions = createTestTransitions(); 1500 transitions.replaceDefaultHandlerForTest(mDefaultHandler); 1501 TestTransitionHandler alwaysMergeHandler = new TestTransitionHandler(); 1502 alwaysMergeHandler.setSimulateMerge(true); 1503 1504 final boolean[] becameIdle = new boolean[]{false}; 1505 1506 final WindowContainerTransaction emptyWCT = new WindowContainerTransaction(); 1507 final SurfaceControl.Transaction mockSCT = mock(SurfaceControl.Transaction.class); 1508 1509 // Make this always merge so we can ensure that it does NOT get a merge-attempt for a 1510 // different track. 1511 IBinder transitA = transitions.startTransition(TRANSIT_OPEN, emptyWCT, alwaysMergeHandler); 1512 // start tracking idle 1513 transitions.runOnIdle(() -> becameIdle[0] = true); 1514 1515 IBinder transitB = transitions.startTransition(TRANSIT_OPEN, emptyWCT, mDefaultHandler); 1516 IBinder transitC = transitions.startTransition(TRANSIT_CLOSE, emptyWCT, mDefaultHandler); 1517 1518 TransitionInfo infoA = new TransitionInfoBuilder(TRANSIT_OPEN) 1519 .addChange(TRANSIT_OPEN).build(); 1520 infoA.setTrack(0); 1521 TransitionInfo infoB = new TransitionInfoBuilder(TRANSIT_OPEN) 1522 .addChange(TRANSIT_OPEN).build(); 1523 infoB.setTrack(1); 1524 TransitionInfo infoC = new TransitionInfoBuilder(TRANSIT_CLOSE) 1525 .addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build(); 1526 infoC.setTrack(1); 1527 1528 transitions.onTransitionReady(transitA, infoA, mockSCT, mockSCT); 1529 assertEquals(1, alwaysMergeHandler.activeCount()); 1530 transitions.onTransitionReady(transitB, infoB, mockSCT, mockSCT); 1531 // should now be running in parallel 1532 assertEquals(1, mDefaultHandler.activeCount()); 1533 assertEquals(1, alwaysMergeHandler.activeCount()); 1534 // make sure we didn't try to merge into a different track. 1535 assertEquals(0, alwaysMergeHandler.mergeCount()); 1536 1537 // This should be queued-up since it is on track 1 (same as B) 1538 transitions.onTransitionReady(transitC, infoC, mockSCT, mockSCT); 1539 assertEquals(1, mDefaultHandler.activeCount()); 1540 assertEquals(1, alwaysMergeHandler.activeCount()); 1541 1542 // Now finish B and make sure C starts 1543 mDefaultHandler.finishOne(); 1544 mMainExecutor.flushAll(); 1545 1546 // Now C and A running in parallel 1547 assertEquals(1, mDefaultHandler.activeCount()); 1548 assertEquals(1, alwaysMergeHandler.activeCount()); 1549 assertEquals(0, alwaysMergeHandler.mergeCount()); 1550 1551 // Finish A 1552 alwaysMergeHandler.finishOne(); 1553 mMainExecutor.flushAll(); 1554 1555 // C still running 1556 assertEquals(0, alwaysMergeHandler.activeCount()); 1557 assertEquals(1, mDefaultHandler.activeCount()); 1558 assertFalse(becameIdle[0]); 1559 1560 mDefaultHandler.finishOne(); 1561 mMainExecutor.flushAll(); 1562 1563 assertEquals(0, mDefaultHandler.activeCount()); 1564 assertTrue(becameIdle[0]); 1565 } 1566 1567 @Test testSyncMultipleTracks()1568 public void testSyncMultipleTracks() { 1569 Transitions transitions = createTestTransitions(); 1570 transitions.replaceDefaultHandlerForTest(mDefaultHandler); 1571 TestTransitionHandler secondHandler = new TestTransitionHandler(); 1572 1573 // Disable the forced early-sync-finish so that we can test the ordering mechanics. 1574 transitions.setDisableForceSyncForTest(true); 1575 mDefaultHandler.mFinishOnSync = false; 1576 secondHandler.mFinishOnSync = false; 1577 1578 final WindowContainerTransaction emptyWCT = new WindowContainerTransaction(); 1579 final SurfaceControl.Transaction mockSCT = mock(SurfaceControl.Transaction.class); 1580 1581 // Make this always merge so we can ensure that it does NOT get a merge-attempt for a 1582 // different track. 1583 IBinder transitA = transitions.startTransition(TRANSIT_OPEN, emptyWCT, mDefaultHandler); 1584 IBinder transitB = transitions.startTransition(TRANSIT_OPEN, emptyWCT, secondHandler); 1585 IBinder transitC = transitions.startTransition(TRANSIT_CLOSE, emptyWCT, secondHandler); 1586 IBinder transitSync = transitions.startTransition(TRANSIT_CLOSE, emptyWCT, mDefaultHandler); 1587 IBinder transitD = transitions.startTransition(TRANSIT_OPEN, emptyWCT, secondHandler); 1588 IBinder transitE = transitions.startTransition(TRANSIT_OPEN, emptyWCT, mDefaultHandler); 1589 1590 TransitionInfo infoA = new TransitionInfoBuilder(TRANSIT_OPEN) 1591 .addChange(TRANSIT_OPEN).build(); 1592 infoA.setTrack(0); 1593 TransitionInfo infoB = new TransitionInfoBuilder(TRANSIT_OPEN) 1594 .addChange(TRANSIT_OPEN).build(); 1595 infoB.setTrack(1); 1596 TransitionInfo infoC = new TransitionInfoBuilder(TRANSIT_CLOSE) 1597 .addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build(); 1598 infoC.setTrack(1); 1599 TransitionInfo infoSync = new TransitionInfoBuilder(TRANSIT_CLOSE) 1600 .addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build(); 1601 infoSync.setTrack(0); 1602 infoSync.setFlags(FLAG_SYNC); 1603 TransitionInfo infoD = new TransitionInfoBuilder(TRANSIT_OPEN) 1604 .addChange(TRANSIT_OPEN).build(); 1605 infoD.setTrack(1); 1606 TransitionInfo infoE = new TransitionInfoBuilder(TRANSIT_OPEN) 1607 .addChange(TRANSIT_OPEN).build(); 1608 infoE.setTrack(0); 1609 1610 // Start A B and C where A is track 0, B and C are track 1 (C should be queued) 1611 transitions.onTransitionReady(transitA, infoA, mockSCT, mockSCT); 1612 transitions.onTransitionReady(transitB, infoB, mockSCT, mockSCT); 1613 transitions.onTransitionReady(transitC, infoC, mockSCT, mockSCT); 1614 // should now be running in parallel (with one queued) 1615 assertEquals(1, mDefaultHandler.activeCount()); 1616 assertEquals(1, secondHandler.activeCount()); 1617 1618 // Make the sync ready and the following (D, E) ready. 1619 transitions.onTransitionReady(transitSync, infoSync, mockSCT, mockSCT); 1620 transitions.onTransitionReady(transitD, infoD, mockSCT, mockSCT); 1621 transitions.onTransitionReady(transitE, infoE, mockSCT, mockSCT); 1622 1623 // nothing should have happened yet since the sync is queued and blocking everything. 1624 assertEquals(1, mDefaultHandler.activeCount()); 1625 assertEquals(1, secondHandler.activeCount()); 1626 1627 // Finish A (which is track 0 like the sync). 1628 mDefaultHandler.finishOne(); 1629 mMainExecutor.flushAll(); 1630 1631 // Even though the sync is on track 0 and track 0 became idle, it should NOT be started yet 1632 // because it must wait for everything. Additionally, D/E shouldn't start yet either. 1633 assertEquals(0, mDefaultHandler.activeCount()); 1634 assertEquals(1, secondHandler.activeCount()); 1635 1636 // Now finish B and C -- this should then allow the sync to start and D to run (in parallel) 1637 secondHandler.finishOne(); 1638 secondHandler.finishOne(); 1639 mMainExecutor.flushAll(); 1640 1641 // Now the sync and D (on track 1) should be running 1642 assertEquals(1, mDefaultHandler.activeCount()); 1643 assertEquals(1, secondHandler.activeCount()); 1644 1645 // finish the sync. track 0 still has E 1646 mDefaultHandler.finishOne(); 1647 mMainExecutor.flushAll(); 1648 assertEquals(1, mDefaultHandler.activeCount()); 1649 1650 mDefaultHandler.finishOne(); 1651 secondHandler.finishOne(); 1652 mMainExecutor.flushAll(); 1653 1654 assertEquals(0, mDefaultHandler.activeCount()); 1655 assertEquals(0, secondHandler.activeCount()); 1656 } 1657 1658 @Test testForceSyncTracks()1659 public void testForceSyncTracks() { 1660 Transitions transitions = createTestTransitions(); 1661 transitions.replaceDefaultHandlerForTest(mDefaultHandler); 1662 TestTransitionHandler secondHandler = new TestTransitionHandler(); 1663 1664 final WindowContainerTransaction emptyWCT = new WindowContainerTransaction(); 1665 final SurfaceControl.Transaction mockSCT = mock(SurfaceControl.Transaction.class); 1666 1667 // Make this always merge so we can ensure that it does NOT get a merge-attempt for a 1668 // different track. 1669 IBinder transitA = transitions.startTransition(TRANSIT_OPEN, emptyWCT, mDefaultHandler); 1670 IBinder transitB = transitions.startTransition(TRANSIT_OPEN, emptyWCT, mDefaultHandler); 1671 IBinder transitC = transitions.startTransition(TRANSIT_CLOSE, emptyWCT, secondHandler); 1672 IBinder transitD = transitions.startTransition(TRANSIT_OPEN, emptyWCT, secondHandler); 1673 IBinder transitSync = transitions.startTransition(TRANSIT_CLOSE, emptyWCT, mDefaultHandler); 1674 1675 TransitionInfo infoA = new TransitionInfoBuilder(TRANSIT_OPEN) 1676 .addChange(TRANSIT_OPEN).build(); 1677 infoA.setTrack(0); 1678 TransitionInfo infoB = new TransitionInfoBuilder(TRANSIT_OPEN) 1679 .addChange(TRANSIT_OPEN).build(); 1680 infoB.setTrack(0); 1681 TransitionInfo infoC = new TransitionInfoBuilder(TRANSIT_CLOSE) 1682 .addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build(); 1683 infoC.setTrack(1); 1684 TransitionInfo infoD = new TransitionInfoBuilder(TRANSIT_OPEN) 1685 .addChange(TRANSIT_OPEN).build(); 1686 infoD.setTrack(1); 1687 TransitionInfo infoSync = new TransitionInfoBuilder(TRANSIT_CLOSE) 1688 .addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build(); 1689 infoSync.setTrack(0); 1690 infoSync.setFlags(FLAG_SYNC); 1691 1692 transitions.onTransitionReady(transitA, infoA, mockSCT, mockSCT); 1693 transitions.onTransitionReady(transitB, infoB, mockSCT, mockSCT); 1694 transitions.onTransitionReady(transitC, infoC, mockSCT, mockSCT); 1695 transitions.onTransitionReady(transitD, infoD, mockSCT, mockSCT); 1696 // should now be running in parallel (with one queued in each) 1697 assertEquals(1, mDefaultHandler.activeCount()); 1698 assertEquals(1, secondHandler.activeCount()); 1699 1700 // Make the sync ready. 1701 transitions.onTransitionReady(transitSync, infoSync, mockSCT, mockSCT); 1702 mMainExecutor.flushAll(); 1703 1704 // Everything should be forced-finish now except the sync 1705 assertEquals(1, mDefaultHandler.activeCount()); 1706 assertEquals(0, secondHandler.activeCount()); 1707 1708 mDefaultHandler.finishOne(); 1709 mMainExecutor.flushAll(); 1710 1711 assertEquals(0, mDefaultHandler.activeCount()); 1712 } 1713 1714 @Test testCloseTransitAnimationWhenClosingChangesExists()1715 public void testCloseTransitAnimationWhenClosingChangesExists() { 1716 Transitions transitions = createTestTransitions(); 1717 Transitions.TransitionObserver observer = mock(Transitions.TransitionObserver.class); 1718 transitions.registerObserver(observer); 1719 transitions.replaceDefaultHandlerForTest(mDefaultHandler); 1720 final TransitionAnimation transitionAnimation = new TransitionAnimation(mContext, false, 1721 Transitions.TAG); 1722 spyOn(transitionAnimation); 1723 1724 // Creating a transition by the app hooking the back key event to start the 1725 // previous activity with FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_RESET_TASK_IF_NEEDED 1726 // flags in order to clear the top activity and bring the exist previous activity to front. 1727 // Expects the activity transition should playing the close animation instead the initiated 1728 // open animation made by startActivity. 1729 IBinder transitToken = new Binder(); 1730 transitions.requestStartTransition(transitToken, 1731 new TransitionRequestInfo(TRANSIT_OPEN, null /* trigger */, null /* remote */)); 1732 TransitionInfo info = new TransitionInfoBuilder(TRANSIT_OPEN) 1733 .addChange(TRANSIT_CLOSE).addChange(TRANSIT_TO_FRONT).build(); 1734 transitions.onTransitionReady(transitToken, info, new StubTransaction(), 1735 new StubTransaction()); 1736 1737 final int type = getTransitionTypeFromInfo(info); 1738 assertEquals(TRANSIT_CLOSE, type); 1739 1740 TransitionAnimationHelper.loadAttributeAnimation(type, info, info.getChanges().get(0), 0, 1741 transitionAnimation, false); 1742 verify(transitionAnimation).loadDefaultAnimationAttr( 1743 eq(R.styleable.WindowAnimation_activityCloseExitAnimation), anyBoolean()); 1744 1745 TransitionAnimationHelper.loadAttributeAnimation(type, info, info.getChanges().get(1), 0, 1746 transitionAnimation, false); 1747 verify(transitionAnimation).loadDefaultAnimationAttr( 1748 eq(R.styleable.WindowAnimation_activityCloseEnterAnimation), anyBoolean()); 1749 } 1750 1751 @Test testTransientHideWithMoveToTop()1752 public void testTransientHideWithMoveToTop() { 1753 Transitions transitions = createTestTransitions(); 1754 transitions.replaceDefaultHandlerForTest(mDefaultHandler); 1755 final TransitionAnimation transitionAnimation = new TransitionAnimation(mContext, false, 1756 Transitions.TAG); 1757 spyOn(transitionAnimation); 1758 1759 // Prepare for a TO_BACK transition 1760 final RunningTaskInfo taskInfo = createTaskInfo(1); 1761 final IBinder closeTransition = new Binder(); 1762 final SurfaceControl.Transaction closeTransitionFinishT = 1763 mock(SurfaceControl.Transaction.class); 1764 1765 // Start a TO_BACK transition 1766 transitions.requestStartTransition(closeTransition, 1767 new TransitionRequestInfo(TRANSIT_TO_BACK, null /* trigger */, null /* remote */)); 1768 TransitionInfo closeInfo = new TransitionInfoBuilder(TRANSIT_TO_BACK) 1769 .addChange(TRANSIT_TO_BACK, taskInfo) 1770 .build(); 1771 transitions.onTransitionReady(closeTransition, closeInfo, new StubTransaction(), 1772 closeTransitionFinishT); 1773 1774 // Verify that the transition hides the task surface in the finish transaction 1775 verify(closeTransitionFinishT).hide(any()); 1776 1777 // Prepare for a CHANGE transition 1778 final IBinder changeTransition = new Binder(); 1779 final SurfaceControl.Transaction changeTransitionFinishT = 1780 mock(SurfaceControl.Transaction.class); 1781 1782 // Start a CHANGE transition w/ MOVE_TO_FRONT that is merged into the TO_BACK 1783 mDefaultHandler.setShouldMerge(changeTransition); 1784 transitions.requestStartTransition(changeTransition, 1785 new TransitionRequestInfo(TRANSIT_OPEN, null /* trigger */, null /* remote */)); 1786 TransitionInfo changeInfo = new TransitionInfoBuilder(TRANSIT_OPEN) 1787 .addChange(TRANSIT_CHANGE, FLAG_MOVED_TO_TOP, taskInfo) 1788 .build(); 1789 transitions.onTransitionReady(changeTransition, changeInfo, new StubTransaction(), 1790 changeTransitionFinishT); 1791 1792 // Verify that the transition shows the task surface in the finish transaction so that the 1793 // when the original transition finishes, the finish transaction does not clobber the 1794 // visibility of the merged transition 1795 verify(changeTransitionFinishT).show(any()); 1796 } 1797 1798 class TestTransitionHandler implements Transitions.TransitionHandler { 1799 ArrayList<Pair<IBinder, Transitions.TransitionFinishCallback>> mFinishes = 1800 new ArrayList<>(); 1801 final ArrayList<IBinder> mMerged = new ArrayList<>(); 1802 boolean mSimulateMerge = false; 1803 boolean mFinishOnSync = true; 1804 final ArraySet<IBinder> mShouldMerge = new ArraySet<>(); 1805 1806 @Override startAnimation(@onNull IBinder transition, @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction startTransaction, @NonNull SurfaceControl.Transaction finishTransaction, @NonNull Transitions.TransitionFinishCallback finishCallback)1807 public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info, 1808 @NonNull SurfaceControl.Transaction startTransaction, 1809 @NonNull SurfaceControl.Transaction finishTransaction, 1810 @NonNull Transitions.TransitionFinishCallback finishCallback) { 1811 mFinishes.add(new Pair<>(transition, finishCallback)); 1812 return true; 1813 } 1814 1815 @Override mergeAnimation(@onNull IBinder transition, @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction startT, @NonNull SurfaceControl.Transaction finishT, @NonNull IBinder mergeTarget, @NonNull Transitions.TransitionFinishCallback finishCallback)1816 public void mergeAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info, 1817 @NonNull SurfaceControl.Transaction startT, 1818 @NonNull SurfaceControl.Transaction finishT, 1819 @NonNull IBinder mergeTarget, 1820 @NonNull Transitions.TransitionFinishCallback finishCallback) { 1821 if (mFinishOnSync && info.getType() == TRANSIT_SLEEP) { 1822 for (int i = 0; i < mFinishes.size(); ++i) { 1823 if (mFinishes.get(i).first != mergeTarget) continue; 1824 mFinishes.remove(i).second.onTransitionFinished(null); 1825 return; 1826 } 1827 } 1828 if (!(mSimulateMerge || mShouldMerge.contains(transition))) return; 1829 mMerged.add(transition); 1830 finishCallback.onTransitionFinished(null /* wct */); 1831 } 1832 1833 @Nullable 1834 @Override handleRequest(@onNull IBinder transition, @NonNull TransitionRequestInfo request)1835 public WindowContainerTransaction handleRequest(@NonNull IBinder transition, 1836 @NonNull TransitionRequestInfo request) { 1837 return null; 1838 } 1839 setSimulateMerge(boolean sim)1840 void setSimulateMerge(boolean sim) { 1841 mSimulateMerge = sim; 1842 } 1843 setShouldMerge(IBinder toMerge)1844 void setShouldMerge(IBinder toMerge) { 1845 mShouldMerge.add(toMerge); 1846 } 1847 finishAll()1848 void finishAll() { 1849 final ArrayList<Pair<IBinder, Transitions.TransitionFinishCallback>> finishes = 1850 mFinishes; 1851 mFinishes = new ArrayList<>(); 1852 for (int i = finishes.size() - 1; i >= 0; --i) { 1853 finishes.get(i).second.onTransitionFinished(null /* wct */); 1854 } 1855 mShouldMerge.clear(); 1856 } 1857 finishOne()1858 void finishOne() { 1859 Pair<IBinder, Transitions.TransitionFinishCallback> fin = mFinishes.remove(0); 1860 mMerged.clear(); 1861 fin.second.onTransitionFinished(null /* wct */); 1862 } 1863 activeCount()1864 int activeCount() { 1865 return mFinishes.size(); 1866 } 1867 mergeCount()1868 int mergeCount() { 1869 return mMerged.size(); 1870 } 1871 } 1872 requestStartTransition(Transitions transitions, IBinder token)1873 private static void requestStartTransition(Transitions transitions, IBinder token) { 1874 transitions.requestStartTransition(token, 1875 new TransitionRequestInfo(TRANSIT_OPEN, null /* trigger */, null /* remote */)); 1876 } 1877 onTransitionReady(Transitions transitions, IBinder token)1878 private static void onTransitionReady(Transitions transitions, IBinder token) { 1879 transitions.onTransitionReady(token, createTransitionInfo(), 1880 new StubTransaction(), new StubTransaction()); 1881 } 1882 createTransitionInfo()1883 private static TransitionInfo createTransitionInfo() { 1884 return new TransitionInfoBuilder(TRANSIT_OPEN) 1885 .addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build(); 1886 } 1887 createTaskInfo(int taskId, int windowingMode, int activityType)1888 private static RunningTaskInfo createTaskInfo(int taskId, int windowingMode, int activityType) { 1889 RunningTaskInfo taskInfo = new RunningTaskInfo(); 1890 taskInfo.taskId = taskId; 1891 taskInfo.topActivityType = activityType; 1892 taskInfo.configuration.windowConfiguration.setWindowingMode(windowingMode); 1893 taskInfo.configuration.windowConfiguration.setActivityType(activityType); 1894 taskInfo.token = mock(WindowContainerToken.class); 1895 taskInfo.baseIntent = mock(Intent.class); 1896 return taskInfo; 1897 } 1898 createTaskInfo(int taskId)1899 private static RunningTaskInfo createTaskInfo(int taskId) { 1900 return createTaskInfo(taskId, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD); 1901 } 1902 createTestDisplayController()1903 private DisplayController createTestDisplayController() { 1904 DisplayLayout displayLayout = mock(DisplayLayout.class); 1905 doReturn(Surface.ROTATION_180).when(displayLayout).getUpsideDownRotation(); 1906 // By default we ignore nav bar in deciding if a seamless rotation is allowed. 1907 doReturn(true).when(displayLayout).allowSeamlessRotationDespiteNavBarMoving(); 1908 1909 DisplayController out = mock(DisplayController.class); 1910 doReturn(displayLayout).when(out).getDisplayLayout(DEFAULT_DISPLAY); 1911 return out; 1912 } 1913 createTestTransitions()1914 private Transitions createTestTransitions() { 1915 ShellInit shellInit = new ShellInit(mMainExecutor); 1916 final Transitions t = new Transitions(mContext, shellInit, mock(ShellController.class), 1917 mOrganizer, mTransactionPool, createTestDisplayController(), mDisplayInsets, 1918 mMainExecutor, mMainHandler, mAnimExecutor, mAnimHandler, 1919 mock(HomeTransitionObserver.class), 1920 mock(FocusTransitionObserver.class)); 1921 shellInit.init(); 1922 return t; 1923 } 1924 } 1925