Loading libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java +9 −2 Original line number Diff line number Diff line Loading @@ -318,8 +318,15 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, startIntentLegacy(intent, fillInIntent, position, options); return; } mStageCoordinator.startIntent(intent, fillInIntent, STAGE_TYPE_UNDEFINED, position, options, null /* remote */); try { options = mStageCoordinator.resolveStartStage(STAGE_TYPE_UNDEFINED, position, options, null /* wct */); intent.send(mContext, 0, fillInIntent, null /* onFinished */, null /* handler */, null /* requiredPermission */, options); } catch (PendingIntent.CanceledException e) { Slog.e(TAG, "Failed to launch task", e); } } private void startIntentLegacy(PendingIntent intent, Intent fillInIntent, Loading libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java +47 −66 Original line number Diff line number Diff line Loading @@ -44,7 +44,6 @@ import static com.android.wm.shell.splitscreen.SplitScreenController.exitReasonT import static com.android.wm.shell.splitscreen.SplitScreenTransitions.FLAG_IS_DIVIDER_BAR; import static com.android.wm.shell.transition.Transitions.ENABLE_SHELL_TRANSITIONS; import static com.android.wm.shell.transition.Transitions.TRANSIT_SPLIT_DISMISS_SNAP; import static com.android.wm.shell.transition.Transitions.TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE; import static com.android.wm.shell.transition.Transitions.TRANSIT_SPLIT_SCREEN_PAIR_OPEN; import static com.android.wm.shell.transition.Transitions.isClosingType; import static com.android.wm.shell.transition.Transitions.isOpeningType; Loading @@ -54,10 +53,8 @@ import android.annotation.Nullable; import android.app.ActivityManager; import android.app.ActivityOptions; import android.app.ActivityTaskManager; import android.app.PendingIntent; import android.app.WindowConfiguration; import android.content.Context; import android.content.Intent; import android.graphics.Rect; import android.hardware.devicestate.DeviceStateManager; import android.os.Bundle; Loading Loading @@ -436,17 +433,6 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, mTaskOrganizer.applyTransaction(wct); } public void startIntent(PendingIntent intent, Intent fillInIntent, @StageType int stage, @SplitPosition int position, @androidx.annotation.Nullable Bundle options, @Nullable RemoteTransition remoteTransition) { final WindowContainerTransaction wct = new WindowContainerTransaction(); options = resolveStartStage(stage, position, options, wct); wct.sendPendingIntent(intent, fillInIntent, options); mSplitTransitions.startEnterTransition( TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE, wct, remoteTransition, this); } /** * Collects all the current child tasks of a specific split and prepares transaction to evict * them to display. Loading Loading @@ -1158,12 +1144,14 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, mSplitTransitions.mPendingDismiss = transition; } } else { // Not in split mode, so look for an open into a split stage just so we can whine and // complain about how this isn't a supported operation. // Not in split mode, so look for an open into a split stage to active split screen. if ((type == TRANSIT_OPEN || type == TRANSIT_TO_FRONT)) { if (getStageOfTask(triggerTask) != null) { throw new IllegalStateException("Entering split implicitly with only one task" + " isn't supported."); // One task is appearing in split, prepare to enter split screen. out = new WindowContainerTransaction(); mSplitTransitions.mPendingEnter = transition; mMainStage.activate(getMainStageBounds(), out, true /* includingTopTask */); mSideStage.moveToTop(getSideStageBounds(), out); } } } Loading Loading @@ -1232,8 +1220,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, private boolean startPendingEnterAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction t) { if (info.getType() == TRANSIT_SPLIT_SCREEN_PAIR_OPEN) { // First, verify that we actually have opened 2 apps in split. // First, verify that we actually have opened apps in both splits. TransitionInfo.Change mainChild = null; TransitionInfo.Change sideChild = null; for (int iC = 0; iC < info.getChanges().size(); ++iC) { Loading @@ -1256,8 +1243,6 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, // Update local states (before animating). setDividerVisibility(true); setSideStagePosition(SPLIT_POSITION_BOTTOM_OR_RIGHT, false /* updateBounds */, null /* wct */); setSplitsVisible(true); addDividerBarToTransition(info, t, true /* show */); Loading @@ -1279,10 +1264,6 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, + " before startAnimation()."); } return true; } else { // TODO: other entry method animations throw new RuntimeException("Unsupported split-entry"); } } private boolean startPendingDismissAnimation(@NonNull IBinder transition, Loading libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java +51 −1 Original line number Diff line number Diff line Loading @@ -24,6 +24,7 @@ import static android.view.WindowManager.TRANSIT_OPEN; import static android.view.WindowManager.TRANSIT_TO_BACK; import static android.view.WindowManager.TRANSIT_TO_FRONT; import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT; import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REORDER; import static com.android.wm.shell.splitscreen.SplitTestUtils.createMockSurface; import static com.android.wm.shell.transition.Transitions.TRANSIT_SPLIT_SCREEN_PAIR_OPEN; Loading Loading @@ -66,7 +67,6 @@ import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.common.TransactionPool; import com.android.wm.shell.common.split.SplitLayout; import com.android.wm.shell.recents.RecentTasksController; import com.android.wm.shell.transition.Transitions; import org.junit.Before; Loading Loading @@ -132,6 +132,40 @@ public class SplitTransitionTests extends ShellTestCase { .setParentTaskId(mSideStage.mRootTaskInfo.taskId).build(); } @Test public void testLaunchToSide() { ActivityManager.RunningTaskInfo newTask = new TestRunningTaskInfoBuilder() .setParentTaskId(mSideStage.mRootTaskInfo.taskId).build(); ActivityManager.RunningTaskInfo reparentTask = new TestRunningTaskInfoBuilder() .setParentTaskId(mMainStage.mRootTaskInfo.taskId).build(); // Create a request to start a new task in side stage TransitionRequestInfo request = new TransitionRequestInfo(TRANSIT_TO_FRONT, newTask, null); IBinder transition = mock(IBinder.class); WindowContainerTransaction result = mStageCoordinator.handleRequest(transition, request); // it should handle the transition to enter split screen. assertNotNull(result); assertTrue(containsSplitEnter(result)); // simulate the transition TransitionInfo.Change openChange = createChange(TRANSIT_OPEN, newTask); TransitionInfo.Change reparentChange = createChange(TRANSIT_CHANGE, reparentTask); TransitionInfo info = new TransitionInfo(TRANSIT_TO_FRONT, 0); info.addChange(openChange); info.addChange(reparentChange); mSideStage.onTaskAppeared(newTask, createMockSurface()); mMainStage.onTaskAppeared(reparentTask, createMockSurface()); boolean accepted = mStageCoordinator.startAnimation(transition, info, mock(SurfaceControl.Transaction.class), mock(SurfaceControl.Transaction.class), mock(Transitions.TransitionFinishCallback.class)); assertTrue(accepted); assertTrue(mStageCoordinator.isSplitScreenVisible()); } @Test public void testLaunchPair() { TransitionInfo info = createEnterPairInfo(); Loading Loading @@ -324,6 +358,22 @@ public class SplitTransitionTests extends ShellTestCase { true /* includingTopTask */); } private boolean containsSplitEnter(@NonNull WindowContainerTransaction wct) { boolean movedMainToFront = false; boolean movedSideToFront = false; for (int i = 0; i < wct.getHierarchyOps().size(); ++i) { WindowContainerTransaction.HierarchyOp op = wct.getHierarchyOps().get(i); if (op.getType() == HIERARCHY_OP_TYPE_REORDER) { if (op.getContainer() == mMainStage.mRootTaskInfo.token.asBinder()) { movedMainToFront = true; } else if (op.getContainer() == mSideStage.mRootTaskInfo.token.asBinder()) { movedSideToFront = true; } } } return movedMainToFront && movedSideToFront; } private boolean containsSplitExit(@NonNull WindowContainerTransaction wct) { // reparenting of child tasks to null constitutes exiting split. boolean reparentedMain = false; Loading services/core/java/com/android/server/wm/ActivityStarter.java +2 −1 Original line number Diff line number Diff line Loading @@ -1672,7 +1672,8 @@ class ActivityStarter { } if (newTransition != null) { transitionController.requestStartTransition(newTransition, mTargetTask, remoteTransition, null /* displayChange */); mTargetTask == null ? r.getTask() : mTargetTask, remoteTransition, null /* displayChange */); } else if (started) { // Make the collecting transition wait until this request is ready. transitionController.setReady(r, false); Loading Loading
libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java +9 −2 Original line number Diff line number Diff line Loading @@ -318,8 +318,15 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, startIntentLegacy(intent, fillInIntent, position, options); return; } mStageCoordinator.startIntent(intent, fillInIntent, STAGE_TYPE_UNDEFINED, position, options, null /* remote */); try { options = mStageCoordinator.resolveStartStage(STAGE_TYPE_UNDEFINED, position, options, null /* wct */); intent.send(mContext, 0, fillInIntent, null /* onFinished */, null /* handler */, null /* requiredPermission */, options); } catch (PendingIntent.CanceledException e) { Slog.e(TAG, "Failed to launch task", e); } } private void startIntentLegacy(PendingIntent intent, Intent fillInIntent, Loading
libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java +47 −66 Original line number Diff line number Diff line Loading @@ -44,7 +44,6 @@ import static com.android.wm.shell.splitscreen.SplitScreenController.exitReasonT import static com.android.wm.shell.splitscreen.SplitScreenTransitions.FLAG_IS_DIVIDER_BAR; import static com.android.wm.shell.transition.Transitions.ENABLE_SHELL_TRANSITIONS; import static com.android.wm.shell.transition.Transitions.TRANSIT_SPLIT_DISMISS_SNAP; import static com.android.wm.shell.transition.Transitions.TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE; import static com.android.wm.shell.transition.Transitions.TRANSIT_SPLIT_SCREEN_PAIR_OPEN; import static com.android.wm.shell.transition.Transitions.isClosingType; import static com.android.wm.shell.transition.Transitions.isOpeningType; Loading @@ -54,10 +53,8 @@ import android.annotation.Nullable; import android.app.ActivityManager; import android.app.ActivityOptions; import android.app.ActivityTaskManager; import android.app.PendingIntent; import android.app.WindowConfiguration; import android.content.Context; import android.content.Intent; import android.graphics.Rect; import android.hardware.devicestate.DeviceStateManager; import android.os.Bundle; Loading Loading @@ -436,17 +433,6 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, mTaskOrganizer.applyTransaction(wct); } public void startIntent(PendingIntent intent, Intent fillInIntent, @StageType int stage, @SplitPosition int position, @androidx.annotation.Nullable Bundle options, @Nullable RemoteTransition remoteTransition) { final WindowContainerTransaction wct = new WindowContainerTransaction(); options = resolveStartStage(stage, position, options, wct); wct.sendPendingIntent(intent, fillInIntent, options); mSplitTransitions.startEnterTransition( TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE, wct, remoteTransition, this); } /** * Collects all the current child tasks of a specific split and prepares transaction to evict * them to display. Loading Loading @@ -1158,12 +1144,14 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, mSplitTransitions.mPendingDismiss = transition; } } else { // Not in split mode, so look for an open into a split stage just so we can whine and // complain about how this isn't a supported operation. // Not in split mode, so look for an open into a split stage to active split screen. if ((type == TRANSIT_OPEN || type == TRANSIT_TO_FRONT)) { if (getStageOfTask(triggerTask) != null) { throw new IllegalStateException("Entering split implicitly with only one task" + " isn't supported."); // One task is appearing in split, prepare to enter split screen. out = new WindowContainerTransaction(); mSplitTransitions.mPendingEnter = transition; mMainStage.activate(getMainStageBounds(), out, true /* includingTopTask */); mSideStage.moveToTop(getSideStageBounds(), out); } } } Loading Loading @@ -1232,8 +1220,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, private boolean startPendingEnterAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction t) { if (info.getType() == TRANSIT_SPLIT_SCREEN_PAIR_OPEN) { // First, verify that we actually have opened 2 apps in split. // First, verify that we actually have opened apps in both splits. TransitionInfo.Change mainChild = null; TransitionInfo.Change sideChild = null; for (int iC = 0; iC < info.getChanges().size(); ++iC) { Loading @@ -1256,8 +1243,6 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, // Update local states (before animating). setDividerVisibility(true); setSideStagePosition(SPLIT_POSITION_BOTTOM_OR_RIGHT, false /* updateBounds */, null /* wct */); setSplitsVisible(true); addDividerBarToTransition(info, t, true /* show */); Loading @@ -1279,10 +1264,6 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, + " before startAnimation()."); } return true; } else { // TODO: other entry method animations throw new RuntimeException("Unsupported split-entry"); } } private boolean startPendingDismissAnimation(@NonNull IBinder transition, Loading
libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java +51 −1 Original line number Diff line number Diff line Loading @@ -24,6 +24,7 @@ import static android.view.WindowManager.TRANSIT_OPEN; import static android.view.WindowManager.TRANSIT_TO_BACK; import static android.view.WindowManager.TRANSIT_TO_FRONT; import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT; import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REORDER; import static com.android.wm.shell.splitscreen.SplitTestUtils.createMockSurface; import static com.android.wm.shell.transition.Transitions.TRANSIT_SPLIT_SCREEN_PAIR_OPEN; Loading Loading @@ -66,7 +67,6 @@ import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.common.TransactionPool; import com.android.wm.shell.common.split.SplitLayout; import com.android.wm.shell.recents.RecentTasksController; import com.android.wm.shell.transition.Transitions; import org.junit.Before; Loading Loading @@ -132,6 +132,40 @@ public class SplitTransitionTests extends ShellTestCase { .setParentTaskId(mSideStage.mRootTaskInfo.taskId).build(); } @Test public void testLaunchToSide() { ActivityManager.RunningTaskInfo newTask = new TestRunningTaskInfoBuilder() .setParentTaskId(mSideStage.mRootTaskInfo.taskId).build(); ActivityManager.RunningTaskInfo reparentTask = new TestRunningTaskInfoBuilder() .setParentTaskId(mMainStage.mRootTaskInfo.taskId).build(); // Create a request to start a new task in side stage TransitionRequestInfo request = new TransitionRequestInfo(TRANSIT_TO_FRONT, newTask, null); IBinder transition = mock(IBinder.class); WindowContainerTransaction result = mStageCoordinator.handleRequest(transition, request); // it should handle the transition to enter split screen. assertNotNull(result); assertTrue(containsSplitEnter(result)); // simulate the transition TransitionInfo.Change openChange = createChange(TRANSIT_OPEN, newTask); TransitionInfo.Change reparentChange = createChange(TRANSIT_CHANGE, reparentTask); TransitionInfo info = new TransitionInfo(TRANSIT_TO_FRONT, 0); info.addChange(openChange); info.addChange(reparentChange); mSideStage.onTaskAppeared(newTask, createMockSurface()); mMainStage.onTaskAppeared(reparentTask, createMockSurface()); boolean accepted = mStageCoordinator.startAnimation(transition, info, mock(SurfaceControl.Transaction.class), mock(SurfaceControl.Transaction.class), mock(Transitions.TransitionFinishCallback.class)); assertTrue(accepted); assertTrue(mStageCoordinator.isSplitScreenVisible()); } @Test public void testLaunchPair() { TransitionInfo info = createEnterPairInfo(); Loading Loading @@ -324,6 +358,22 @@ public class SplitTransitionTests extends ShellTestCase { true /* includingTopTask */); } private boolean containsSplitEnter(@NonNull WindowContainerTransaction wct) { boolean movedMainToFront = false; boolean movedSideToFront = false; for (int i = 0; i < wct.getHierarchyOps().size(); ++i) { WindowContainerTransaction.HierarchyOp op = wct.getHierarchyOps().get(i); if (op.getType() == HIERARCHY_OP_TYPE_REORDER) { if (op.getContainer() == mMainStage.mRootTaskInfo.token.asBinder()) { movedMainToFront = true; } else if (op.getContainer() == mSideStage.mRootTaskInfo.token.asBinder()) { movedSideToFront = true; } } } return movedMainToFront && movedSideToFront; } private boolean containsSplitExit(@NonNull WindowContainerTransaction wct) { // reparenting of child tasks to null constitutes exiting split. boolean reparentedMain = false; Loading
services/core/java/com/android/server/wm/ActivityStarter.java +2 −1 Original line number Diff line number Diff line Loading @@ -1672,7 +1672,8 @@ class ActivityStarter { } if (newTransition != null) { transitionController.requestStartTransition(newTransition, mTargetTask, remoteTransition, null /* displayChange */); mTargetTask == null ? r.getTask() : mTargetTask, remoteTransition, null /* displayChange */); } else if (started) { // Make the collecting transition wait until this request is ready. transitionController.setReady(r, false); Loading