Merge "Make split screen only reparent top task" into sc-v2-dev

This commit is contained in:
Tony Huang
2021-10-15 05:46:13 +00:00
committed by Android (Google) Code Review
7 changed files with 99 additions and 42 deletions

View File

@@ -294,6 +294,36 @@ public final class WindowContainerTransaction implements Parcelable {
return this; return this;
} }
/**
* Reparent's all children tasks or the top task of {@param currentParent} in the specified
* {@param windowingMode} and {@param activityType} to {@param newParent} in their current
* z-order.
*
* @param currentParent of the tasks to perform the operation no.
* {@code null} will perform the operation on the display.
* @param newParent for the tasks. {@code null} will perform the operation on the display.
* @param windowingModes of the tasks to reparent.
* @param activityTypes of the tasks to reparent.
* @param onTop When {@code true}, the child goes to the top of parent; otherwise it goes to
* the bottom.
* @param reparentTopOnly When {@code true}, only reparent the top task which fit windowingModes
* and activityTypes.
* @hide
*/
@NonNull
public WindowContainerTransaction reparentTasks(@Nullable WindowContainerToken currentParent,
@Nullable WindowContainerToken newParent, @Nullable int[] windowingModes,
@Nullable int[] activityTypes, boolean onTop, boolean reparentTopOnly) {
mHierarchyOps.add(HierarchyOp.createForChildrenTasksReparent(
currentParent != null ? currentParent.asBinder() : null,
newParent != null ? newParent.asBinder() : null,
windowingModes,
activityTypes,
onTop,
reparentTopOnly));
return this;
}
/** /**
* Reparent's all children tasks of {@param currentParent} in the specified * Reparent's all children tasks of {@param currentParent} in the specified
* {@param windowingMode} and {@param activityType} to {@param newParent} in their current * {@param windowingMode} and {@param activityType} to {@param newParent} in their current
@@ -311,13 +341,8 @@ public final class WindowContainerTransaction implements Parcelable {
public WindowContainerTransaction reparentTasks(@Nullable WindowContainerToken currentParent, public WindowContainerTransaction reparentTasks(@Nullable WindowContainerToken currentParent,
@Nullable WindowContainerToken newParent, @Nullable int[] windowingModes, @Nullable WindowContainerToken newParent, @Nullable int[] windowingModes,
@Nullable int[] activityTypes, boolean onTop) { @Nullable int[] activityTypes, boolean onTop) {
mHierarchyOps.add(HierarchyOp.createForChildrenTasksReparent( return reparentTasks(currentParent, newParent, windowingModes, activityTypes, onTop,
currentParent != null ? currentParent.asBinder() : null, false /* reparentTopOnly */);
newParent != null ? newParent.asBinder() : null,
windowingModes,
activityTypes,
onTop));
return this;
} }
/** /**
@@ -948,6 +973,8 @@ public final class WindowContainerTransaction implements Parcelable {
// Moves/reparents to top of parent when {@code true}, otherwise moves/reparents to bottom. // Moves/reparents to top of parent when {@code true}, otherwise moves/reparents to bottom.
private boolean mToTop; private boolean mToTop;
private boolean mReparentTopOnly;
@Nullable @Nullable
private int[] mWindowingModes; private int[] mWindowingModes;
@@ -985,13 +1012,15 @@ public final class WindowContainerTransaction implements Parcelable {
} }
public static HierarchyOp createForChildrenTasksReparent(IBinder currentParent, public static HierarchyOp createForChildrenTasksReparent(IBinder currentParent,
IBinder newParent, int[] windowingModes, int[] activityTypes, boolean onTop) { IBinder newParent, int[] windowingModes, int[] activityTypes, boolean onTop,
boolean reparentTopOnly) {
return new HierarchyOp.Builder(HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT) return new HierarchyOp.Builder(HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT)
.setContainer(currentParent) .setContainer(currentParent)
.setReparentContainer(newParent) .setReparentContainer(newParent)
.setWindowingModes(windowingModes) .setWindowingModes(windowingModes)
.setActivityTypes(activityTypes) .setActivityTypes(activityTypes)
.setToTop(onTop) .setToTop(onTop)
.setReparentTopOnly(reparentTopOnly)
.build(); .build();
} }
@@ -1040,6 +1069,7 @@ public final class WindowContainerTransaction implements Parcelable {
mContainer = copy.mContainer; mContainer = copy.mContainer;
mReparent = copy.mReparent; mReparent = copy.mReparent;
mToTop = copy.mToTop; mToTop = copy.mToTop;
mReparentTopOnly = copy.mReparentTopOnly;
mWindowingModes = copy.mWindowingModes; mWindowingModes = copy.mWindowingModes;
mActivityTypes = copy.mActivityTypes; mActivityTypes = copy.mActivityTypes;
mLaunchOptions = copy.mLaunchOptions; mLaunchOptions = copy.mLaunchOptions;
@@ -1053,6 +1083,7 @@ public final class WindowContainerTransaction implements Parcelable {
mContainer = in.readStrongBinder(); mContainer = in.readStrongBinder();
mReparent = in.readStrongBinder(); mReparent = in.readStrongBinder();
mToTop = in.readBoolean(); mToTop = in.readBoolean();
mReparentTopOnly = in.readBoolean();
mWindowingModes = in.createIntArray(); mWindowingModes = in.createIntArray();
mActivityTypes = in.createIntArray(); mActivityTypes = in.createIntArray();
mLaunchOptions = in.readBundle(); mLaunchOptions = in.readBundle();
@@ -1093,6 +1124,10 @@ public final class WindowContainerTransaction implements Parcelable {
return mToTop; return mToTop;
} }
public boolean getReparentTopOnly() {
return mReparentTopOnly;
}
public int[] getWindowingModes() { public int[] getWindowingModes() {
return mWindowingModes; return mWindowingModes;
} }
@@ -1126,12 +1161,13 @@ public final class WindowContainerTransaction implements Parcelable {
switch (mType) { switch (mType) {
case HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT: case HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT:
return "{ChildrenTasksReparent: from=" + mContainer + " to=" + mReparent return "{ChildrenTasksReparent: from=" + mContainer + " to=" + mReparent
+ " mToTop=" + mToTop + " mWindowingMode=" + mWindowingModes + " mToTop=" + mToTop + " mReparentTopOnly=" + mReparentTopOnly
+ " mActivityType=" + mActivityTypes + "}"; + " mWindowingMode=" + Arrays.toString(mWindowingModes)
+ " mActivityType=" + Arrays.toString(mActivityTypes) + "}";
case HIERARCHY_OP_TYPE_SET_LAUNCH_ROOT: case HIERARCHY_OP_TYPE_SET_LAUNCH_ROOT:
return "{SetLaunchRoot: container=" + mContainer return "{SetLaunchRoot: container=" + mContainer
+ " mWindowingMode=" + mWindowingModes + " mWindowingMode=" + Arrays.toString(mWindowingModes)
+ " mActivityType=" + mActivityTypes + "}"; + " mActivityType=" + Arrays.toString(mActivityTypes) + "}";
case HIERARCHY_OP_TYPE_REPARENT: case HIERARCHY_OP_TYPE_REPARENT:
return "{reparent: " + mContainer + " to " + (mToTop ? "top of " : "bottom of ") return "{reparent: " + mContainer + " to " + (mToTop ? "top of " : "bottom of ")
+ mReparent + "}"; + mReparent + "}";
@@ -1163,8 +1199,9 @@ public final class WindowContainerTransaction implements Parcelable {
+ " adjacentContainer=" + mReparent + "}"; + " adjacentContainer=" + mReparent + "}";
default: default:
return "{mType=" + mType + " container=" + mContainer + " reparent=" + mReparent return "{mType=" + mType + " container=" + mContainer + " reparent=" + mReparent
+ " mToTop=" + mToTop + " mWindowingMode=" + mWindowingModes + " mToTop=" + mToTop
+ " mActivityType=" + mActivityTypes + "}"; + " mWindowingMode=" + Arrays.toString(mWindowingModes)
+ " mActivityType=" + Arrays.toString(mActivityTypes) + "}";
} }
} }
@@ -1174,6 +1211,7 @@ public final class WindowContainerTransaction implements Parcelable {
dest.writeStrongBinder(mContainer); dest.writeStrongBinder(mContainer);
dest.writeStrongBinder(mReparent); dest.writeStrongBinder(mReparent);
dest.writeBoolean(mToTop); dest.writeBoolean(mToTop);
dest.writeBoolean(mReparentTopOnly);
dest.writeIntArray(mWindowingModes); dest.writeIntArray(mWindowingModes);
dest.writeIntArray(mActivityTypes); dest.writeIntArray(mActivityTypes);
dest.writeBundle(mLaunchOptions); dest.writeBundle(mLaunchOptions);
@@ -1211,6 +1249,8 @@ public final class WindowContainerTransaction implements Parcelable {
private boolean mToTop; private boolean mToTop;
private boolean mReparentTopOnly;
@Nullable @Nullable
private int[] mWindowingModes; private int[] mWindowingModes;
@@ -1248,6 +1288,11 @@ public final class WindowContainerTransaction implements Parcelable {
return this; return this;
} }
Builder setReparentTopOnly(boolean reparentTopOnly) {
mReparentTopOnly = reparentTopOnly;
return this;
}
Builder setWindowingModes(@Nullable int[] windowingModes) { Builder setWindowingModes(@Nullable int[] windowingModes) {
mWindowingModes = windowingModes; mWindowingModes = windowingModes;
return this; return this;
@@ -1290,6 +1335,7 @@ public final class WindowContainerTransaction implements Parcelable {
? Arrays.copyOf(mActivityTypes, mActivityTypes.length) ? Arrays.copyOf(mActivityTypes, mActivityTypes.length)
: null; : null;
hierarchyOp.mToTop = mToTop; hierarchyOp.mToTop = mToTop;
hierarchyOp.mReparentTopOnly = mReparentTopOnly;
hierarchyOp.mLaunchOptions = mLaunchOptions; hierarchyOp.mLaunchOptions = mLaunchOptions;
hierarchyOp.mActivityIntent = mActivityIntent; hierarchyOp.mActivityIntent = mActivityIntent;
hierarchyOp.mPendingIntent = mPendingIntent; hierarchyOp.mPendingIntent = mPendingIntent;

View File

@@ -49,25 +49,24 @@ class MainStage extends StageTaskListener {
return mIsActive; return mIsActive;
} }
void activate(Rect rootBounds, WindowContainerTransaction wct) { void activate(Rect rootBounds, WindowContainerTransaction wct, boolean includingTopTask) {
if (mIsActive) return; if (mIsActive) return;
final WindowContainerToken rootToken = mRootTaskInfo.token; final WindowContainerToken rootToken = mRootTaskInfo.token;
wct.setBounds(rootToken, rootBounds) wct.setBounds(rootToken, rootBounds)
.setWindowingMode(rootToken, WINDOWING_MODE_MULTI_WINDOW) .setWindowingMode(rootToken, WINDOWING_MODE_MULTI_WINDOW)
.setLaunchRoot( // Moving the root task to top after the child tasks were re-parented , or the root
rootToken, // task cannot be visible and focused.
CONTROLLED_WINDOWING_MODES, .reorder(rootToken, true /* onTop */);
CONTROLLED_ACTIVITY_TYPES) if (includingTopTask) {
.reparentTasks( wct.reparentTasks(
null /* currentParent */, null /* currentParent */,
rootToken, rootToken,
CONTROLLED_WINDOWING_MODES, CONTROLLED_WINDOWING_MODES,
CONTROLLED_ACTIVITY_TYPES, CONTROLLED_ACTIVITY_TYPES,
true /* onTop */) true /* onTop */,
// Moving the root task to top after the child tasks were re-parented , or the root true /* reparentTopOnly */);
// task cannot be visible and focused. }
.reorder(rootToken, true /* onTop */);
mIsActive = true; mIsActive = true;
} }

View File

@@ -263,7 +263,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
@SplitPosition int sideStagePosition) { @SplitPosition int sideStagePosition) {
final WindowContainerTransaction wct = new WindowContainerTransaction(); final WindowContainerTransaction wct = new WindowContainerTransaction();
setSideStagePosition(sideStagePosition, wct); setSideStagePosition(sideStagePosition, wct);
mMainStage.activate(getMainStageBounds(), wct); mMainStage.activate(getMainStageBounds(), wct, true /* reparent */);
mSideStage.addTask(task, getSideStageBounds(), wct); mSideStage.addTask(task, getSideStageBounds(), wct);
mSyncQueue.queue(wct); mSyncQueue.queue(wct);
mSyncQueue.runInSync(t -> updateSurfaceBounds(null /* layout */, t)); mSyncQueue.runInSync(t -> updateSurfaceBounds(null /* layout */, t));
@@ -299,7 +299,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
// Build a request WCT that will launch both apps such that task 0 is on the main stage // Build a request WCT that will launch both apps such that task 0 is on the main stage
// while task 1 is on the side stage. // while task 1 is on the side stage.
mMainStage.activate(getMainStageBounds(), wct); mMainStage.activate(getMainStageBounds(), wct, false /* reparent */);
mSideStage.setBounds(getSideStageBounds(), wct); mSideStage.setBounds(getSideStageBounds(), wct);
// Make sure the launch options will put tasks in the corresponding split roots // Make sure the launch options will put tasks in the corresponding split roots
@@ -368,7 +368,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
// Build a request WCT that will launch both apps such that task 0 is on the main stage // Build a request WCT that will launch both apps such that task 0 is on the main stage
// while task 1 is on the side stage. // while task 1 is on the side stage.
mMainStage.activate(getMainStageBounds(), wct); mMainStage.activate(getMainStageBounds(), wct, false /* reparent */);
mSideStage.setBounds(getSideStageBounds(), wct); mSideStage.setBounds(getSideStageBounds(), wct);
// Make sure the launch options will put tasks in the corresponding split roots // Make sure the launch options will put tasks in the corresponding split roots
@@ -756,7 +756,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
} else if (isSideStage) { } else if (isSideStage) {
final WindowContainerTransaction wct = new WindowContainerTransaction(); final WindowContainerTransaction wct = new WindowContainerTransaction();
// Make sure the main stage is active. // Make sure the main stage is active.
mMainStage.activate(getMainStageBounds(), wct); mMainStage.activate(getMainStageBounds(), wct, true /* reparent */);
mSideStage.setBounds(getSideStageBounds(), wct); mSideStage.setBounds(getSideStageBounds(), wct);
mTaskOrganizer.applyTransaction(wct); mTaskOrganizer.applyTransaction(wct);
} }

View File

@@ -62,7 +62,8 @@ public class MainStageTests {
@Test @Test
public void testActiveDeactivate() { public void testActiveDeactivate() {
mMainStage.activate(mRootTaskInfo.configuration.windowConfiguration.getBounds(), mWct); mMainStage.activate(mRootTaskInfo.configuration.windowConfiguration.getBounds(), mWct,
true /* reparent */);
assertThat(mMainStage.isActive()).isTrue(); assertThat(mMainStage.isActive()).isTrue();
mMainStage.deactivate(mWct); mMainStage.deactivate(mWct);

View File

@@ -316,7 +316,8 @@ public class SplitTransitionTests extends ShellTestCase {
mock(SurfaceControl.Transaction.class), mock(SurfaceControl.Transaction.class),
mock(SurfaceControl.Transaction.class), mock(SurfaceControl.Transaction.class),
mock(Transitions.TransitionFinishCallback.class)); mock(Transitions.TransitionFinishCallback.class));
mMainStage.activate(new Rect(0, 0, 100, 100), new WindowContainerTransaction()); mMainStage.activate(new Rect(0, 0, 100, 100), new WindowContainerTransaction(),
true /* includingTopTask */);
} }
private boolean containsSplitExit(@NonNull WindowContainerTransaction wct) { private boolean containsSplitExit(@NonNull WindowContainerTransaction wct) {

View File

@@ -18,9 +18,11 @@ package com.android.wm.shell.splitscreen;
import static android.app.ActivityTaskManager.INVALID_TASK_ID; import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.view.Display.DEFAULT_DISPLAY; import static android.view.Display.DEFAULT_DISPLAY;
import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__RETURN_HOME; import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__RETURN_HOME;
import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_BOTTOM_OR_RIGHT; import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_BOTTOM_OR_RIGHT;
import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_TOP_OR_LEFT; import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_TOP_OR_LEFT;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.clearInvocations;
@@ -111,7 +113,8 @@ public class StageCoordinatorTests extends ShellTestCase {
mStageCoordinator.moveToSideStage(task, SPLIT_POSITION_BOTTOM_OR_RIGHT); mStageCoordinator.moveToSideStage(task, SPLIT_POSITION_BOTTOM_OR_RIGHT);
verify(mMainStage).activate(any(Rect.class), any(WindowContainerTransaction.class)); verify(mMainStage).activate(any(Rect.class), any(WindowContainerTransaction.class),
eq(true /* includingTopTask */));
verify(mSideStage).addTask(eq(task), any(Rect.class), verify(mSideStage).addTask(eq(task), any(Rect.class),
any(WindowContainerTransaction.class)); any(WindowContainerTransaction.class));
} }

View File

@@ -83,7 +83,7 @@ import java.util.HashMap;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.function.Consumer; import java.util.function.Function;
/** /**
* Server side implementation for the interface for organizing windows * Server side implementation for the interface for organizing windows
@@ -893,24 +893,31 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
// We want to collect the tasks first before re-parenting to avoid array shifting on us. // We want to collect the tasks first before re-parenting to avoid array shifting on us.
final ArrayList<Task> tasksToReparent = new ArrayList<>(); final ArrayList<Task> tasksToReparent = new ArrayList<>();
currentParent.forAllTasks((Consumer<Task>) (task) -> { currentParent.forAllTasks((Function<Task, Boolean>) task -> {
Slog.i(TAG, " Processing task=" + task); Slog.i(TAG, " Processing task=" + task);
if (task.mCreatedByOrganizer final boolean reparent;
|| task.getParent() != finalCurrentParent) { if (task.mCreatedByOrganizer || task.getParent() != finalCurrentParent) {
// We only care about non-organized task that are direct children of the thing we // We only care about non-organized task that are direct children of the thing we
// are reparenting from. // are reparenting from.
return; return false;
} }
if (newParentInMultiWindow && !task.supportsMultiWindowInDisplayArea(newParentTda)) { if (newParentInMultiWindow && !task.supportsMultiWindowInDisplayArea(newParentTda)) {
Slog.e(TAG, "reparentChildrenTasksHierarchyOp non-resizeable task to multi window," Slog.e(TAG, "reparentChildrenTasksHierarchyOp non-resizeable task to multi window,"
+ " task=" + task); + " task=" + task);
return; return false;
}
if (!ArrayUtils.contains(hop.getActivityTypes(), task.getActivityType())
|| !ArrayUtils.contains(hop.getWindowingModes(), task.getWindowingMode())) {
return false;
} }
if (!ArrayUtils.contains(hop.getActivityTypes(), task.getActivityType())) return;
if (!ArrayUtils.contains(hop.getWindowingModes(), task.getWindowingMode())) return;
if (hop.getToTop()) {
tasksToReparent.add(0, task);
} else {
tasksToReparent.add(task); tasksToReparent.add(task);
}, !hop.getToTop()); }
return hop.getReparentTopOnly() && tasksToReparent.size() == 1;
});
final int count = tasksToReparent.size(); final int count = tasksToReparent.size();
for (int i = 0; i < count; ++i) { for (int i = 0; i < count; ++i) {