Merge "Integrate launch-to-side split transition to shell transition"
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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.
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1232,57 +1220,50 @@ 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.
|
||||
TransitionInfo.Change mainChild = null;
|
||||
TransitionInfo.Change sideChild = null;
|
||||
for (int iC = 0; iC < info.getChanges().size(); ++iC) {
|
||||
final TransitionInfo.Change change = info.getChanges().get(iC);
|
||||
final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
|
||||
if (taskInfo == null || !taskInfo.hasParentTask()) continue;
|
||||
final @StageType int stageType = getStageType(getStageOfTask(taskInfo));
|
||||
if (stageType == STAGE_TYPE_MAIN) {
|
||||
mainChild = change;
|
||||
} else if (stageType == STAGE_TYPE_SIDE) {
|
||||
sideChild = change;
|
||||
}
|
||||
// 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) {
|
||||
final TransitionInfo.Change change = info.getChanges().get(iC);
|
||||
final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
|
||||
if (taskInfo == null || !taskInfo.hasParentTask()) continue;
|
||||
final @StageType int stageType = getStageType(getStageOfTask(taskInfo));
|
||||
if (stageType == STAGE_TYPE_MAIN) {
|
||||
mainChild = change;
|
||||
} else if (stageType == STAGE_TYPE_SIDE) {
|
||||
sideChild = change;
|
||||
}
|
||||
if (mainChild == null || sideChild == null) {
|
||||
throw new IllegalStateException("Launched 2 tasks in split, but didn't receive"
|
||||
+ " 2 tasks in transition. Possibly one of them failed to launch");
|
||||
// TODO: fallback logic. Probably start a new transition to exit split before
|
||||
// applying anything here. Ideally consolidate with transition-merging.
|
||||
}
|
||||
|
||||
// Update local states (before animating).
|
||||
setDividerVisibility(true);
|
||||
setSideStagePosition(SPLIT_POSITION_BOTTOM_OR_RIGHT, false /* updateBounds */,
|
||||
null /* wct */);
|
||||
setSplitsVisible(true);
|
||||
|
||||
addDividerBarToTransition(info, t, true /* show */);
|
||||
|
||||
// Make some noise if things aren't totally expected. These states shouldn't effect
|
||||
// transitions locally, but remotes (like Launcher) may get confused if they were
|
||||
// depending on listener callbacks. This can happen because task-organizer callbacks
|
||||
// aren't serialized with transition callbacks.
|
||||
// TODO(b/184679596): Find a way to either include task-org information in
|
||||
// the transition, or synchronize task-org callbacks.
|
||||
if (!mMainStage.containsTask(mainChild.getTaskInfo().taskId)) {
|
||||
Log.w(TAG, "Expected onTaskAppeared on " + mMainStage
|
||||
+ " to have been called with " + mainChild.getTaskInfo().taskId
|
||||
+ " before startAnimation().");
|
||||
}
|
||||
if (!mSideStage.containsTask(sideChild.getTaskInfo().taskId)) {
|
||||
Log.w(TAG, "Expected onTaskAppeared on " + mSideStage
|
||||
+ " to have been called with " + sideChild.getTaskInfo().taskId
|
||||
+ " before startAnimation().");
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
// TODO: other entry method animations
|
||||
throw new RuntimeException("Unsupported split-entry");
|
||||
}
|
||||
if (mainChild == null || sideChild == null) {
|
||||
throw new IllegalStateException("Launched 2 tasks in split, but didn't receive"
|
||||
+ " 2 tasks in transition. Possibly one of them failed to launch");
|
||||
// TODO: fallback logic. Probably start a new transition to exit split before
|
||||
// applying anything here. Ideally consolidate with transition-merging.
|
||||
}
|
||||
|
||||
// Update local states (before animating).
|
||||
setDividerVisibility(true);
|
||||
setSplitsVisible(true);
|
||||
|
||||
addDividerBarToTransition(info, t, true /* show */);
|
||||
|
||||
// Make some noise if things aren't totally expected. These states shouldn't effect
|
||||
// transitions locally, but remotes (like Launcher) may get confused if they were
|
||||
// depending on listener callbacks. This can happen because task-organizer callbacks
|
||||
// aren't serialized with transition callbacks.
|
||||
// TODO(b/184679596): Find a way to either include task-org information in
|
||||
// the transition, or synchronize task-org callbacks.
|
||||
if (!mMainStage.containsTask(mainChild.getTaskInfo().taskId)) {
|
||||
Log.w(TAG, "Expected onTaskAppeared on " + mMainStage
|
||||
+ " to have been called with " + mainChild.getTaskInfo().taskId
|
||||
+ " before startAnimation().");
|
||||
}
|
||||
if (!mSideStage.containsTask(sideChild.getTaskInfo().taskId)) {
|
||||
Log.w(TAG, "Expected onTaskAppeared on " + mSideStage
|
||||
+ " to have been called with " + sideChild.getTaskInfo().taskId
|
||||
+ " before startAnimation().");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean startPendingDismissAnimation(@NonNull IBinder transition,
|
||||
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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();
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user