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;
}
/**
* 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
* {@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,
@Nullable WindowContainerToken newParent, @Nullable int[] windowingModes,
@Nullable int[] activityTypes, boolean onTop) {
mHierarchyOps.add(HierarchyOp.createForChildrenTasksReparent(
currentParent != null ? currentParent.asBinder() : null,
newParent != null ? newParent.asBinder() : null,
windowingModes,
activityTypes,
onTop));
return this;
return reparentTasks(currentParent, newParent, windowingModes, activityTypes, onTop,
false /* reparentTopOnly */);
}
/**
@@ -948,6 +973,8 @@ public final class WindowContainerTransaction implements Parcelable {
// Moves/reparents to top of parent when {@code true}, otherwise moves/reparents to bottom.
private boolean mToTop;
private boolean mReparentTopOnly;
@Nullable
private int[] mWindowingModes;
@@ -985,13 +1012,15 @@ public final class WindowContainerTransaction implements Parcelable {
}
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)
.setContainer(currentParent)
.setReparentContainer(newParent)
.setWindowingModes(windowingModes)
.setActivityTypes(activityTypes)
.setToTop(onTop)
.setReparentTopOnly(reparentTopOnly)
.build();
}
@@ -1040,6 +1069,7 @@ public final class WindowContainerTransaction implements Parcelable {
mContainer = copy.mContainer;
mReparent = copy.mReparent;
mToTop = copy.mToTop;
mReparentTopOnly = copy.mReparentTopOnly;
mWindowingModes = copy.mWindowingModes;
mActivityTypes = copy.mActivityTypes;
mLaunchOptions = copy.mLaunchOptions;
@@ -1053,6 +1083,7 @@ public final class WindowContainerTransaction implements Parcelable {
mContainer = in.readStrongBinder();
mReparent = in.readStrongBinder();
mToTop = in.readBoolean();
mReparentTopOnly = in.readBoolean();
mWindowingModes = in.createIntArray();
mActivityTypes = in.createIntArray();
mLaunchOptions = in.readBundle();
@@ -1093,6 +1124,10 @@ public final class WindowContainerTransaction implements Parcelable {
return mToTop;
}
public boolean getReparentTopOnly() {
return mReparentTopOnly;
}
public int[] getWindowingModes() {
return mWindowingModes;
}
@@ -1126,12 +1161,13 @@ public final class WindowContainerTransaction implements Parcelable {
switch (mType) {
case HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT:
return "{ChildrenTasksReparent: from=" + mContainer + " to=" + mReparent
+ " mToTop=" + mToTop + " mWindowingMode=" + mWindowingModes
+ " mActivityType=" + mActivityTypes + "}";
+ " mToTop=" + mToTop + " mReparentTopOnly=" + mReparentTopOnly
+ " mWindowingMode=" + Arrays.toString(mWindowingModes)
+ " mActivityType=" + Arrays.toString(mActivityTypes) + "}";
case HIERARCHY_OP_TYPE_SET_LAUNCH_ROOT:
return "{SetLaunchRoot: container=" + mContainer
+ " mWindowingMode=" + mWindowingModes
+ " mActivityType=" + mActivityTypes + "}";
+ " mWindowingMode=" + Arrays.toString(mWindowingModes)
+ " mActivityType=" + Arrays.toString(mActivityTypes) + "}";
case HIERARCHY_OP_TYPE_REPARENT:
return "{reparent: " + mContainer + " to " + (mToTop ? "top of " : "bottom of ")
+ mReparent + "}";
@@ -1163,8 +1199,9 @@ public final class WindowContainerTransaction implements Parcelable {
+ " adjacentContainer=" + mReparent + "}";
default:
return "{mType=" + mType + " container=" + mContainer + " reparent=" + mReparent
+ " mToTop=" + mToTop + " mWindowingMode=" + mWindowingModes
+ " mActivityType=" + mActivityTypes + "}";
+ " mToTop=" + mToTop
+ " mWindowingMode=" + Arrays.toString(mWindowingModes)
+ " mActivityType=" + Arrays.toString(mActivityTypes) + "}";
}
}
@@ -1174,6 +1211,7 @@ public final class WindowContainerTransaction implements Parcelable {
dest.writeStrongBinder(mContainer);
dest.writeStrongBinder(mReparent);
dest.writeBoolean(mToTop);
dest.writeBoolean(mReparentTopOnly);
dest.writeIntArray(mWindowingModes);
dest.writeIntArray(mActivityTypes);
dest.writeBundle(mLaunchOptions);
@@ -1211,6 +1249,8 @@ public final class WindowContainerTransaction implements Parcelable {
private boolean mToTop;
private boolean mReparentTopOnly;
@Nullable
private int[] mWindowingModes;
@@ -1248,6 +1288,11 @@ public final class WindowContainerTransaction implements Parcelable {
return this;
}
Builder setReparentTopOnly(boolean reparentTopOnly) {
mReparentTopOnly = reparentTopOnly;
return this;
}
Builder setWindowingModes(@Nullable int[] windowingModes) {
mWindowingModes = windowingModes;
return this;
@@ -1290,6 +1335,7 @@ public final class WindowContainerTransaction implements Parcelable {
? Arrays.copyOf(mActivityTypes, mActivityTypes.length)
: null;
hierarchyOp.mToTop = mToTop;
hierarchyOp.mReparentTopOnly = mReparentTopOnly;
hierarchyOp.mLaunchOptions = mLaunchOptions;
hierarchyOp.mActivityIntent = mActivityIntent;
hierarchyOp.mPendingIntent = mPendingIntent;

View File

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

View File

@@ -263,7 +263,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
@SplitPosition int sideStagePosition) {
final WindowContainerTransaction wct = new WindowContainerTransaction();
setSideStagePosition(sideStagePosition, wct);
mMainStage.activate(getMainStageBounds(), wct);
mMainStage.activate(getMainStageBounds(), wct, true /* reparent */);
mSideStage.addTask(task, getSideStageBounds(), wct);
mSyncQueue.queue(wct);
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
// while task 1 is on the side stage.
mMainStage.activate(getMainStageBounds(), wct);
mMainStage.activate(getMainStageBounds(), wct, false /* reparent */);
mSideStage.setBounds(getSideStageBounds(), wct);
// 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
// while task 1 is on the side stage.
mMainStage.activate(getMainStageBounds(), wct);
mMainStage.activate(getMainStageBounds(), wct, false /* reparent */);
mSideStage.setBounds(getSideStageBounds(), wct);
// 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) {
final WindowContainerTransaction wct = new WindowContainerTransaction();
// Make sure the main stage is active.
mMainStage.activate(getMainStageBounds(), wct);
mMainStage.activate(getMainStageBounds(), wct, true /* reparent */);
mSideStage.setBounds(getSideStageBounds(), wct);
mTaskOrganizer.applyTransaction(wct);
}

View File

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

View File

@@ -316,7 +316,8 @@ public class SplitTransitionTests extends ShellTestCase {
mock(SurfaceControl.Transaction.class),
mock(SurfaceControl.Transaction.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) {

View File

@@ -18,9 +18,11 @@ package com.android.wm.shell.splitscreen;
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.view.Display.DEFAULT_DISPLAY;
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_TOP_OR_LEFT;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.clearInvocations;
@@ -111,7 +113,8 @@ public class StageCoordinatorTests extends ShellTestCase {
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),
any(WindowContainerTransaction.class));
}

View File

@@ -83,7 +83,7 @@ import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import java.util.function.Function;
/**
* 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.
final ArrayList<Task> tasksToReparent = new ArrayList<>();
currentParent.forAllTasks((Consumer<Task>) (task) -> {
currentParent.forAllTasks((Function<Task, Boolean>) task -> {
Slog.i(TAG, " Processing task=" + task);
if (task.mCreatedByOrganizer
|| task.getParent() != finalCurrentParent) {
final boolean reparent;
if (task.mCreatedByOrganizer || task.getParent() != finalCurrentParent) {
// We only care about non-organized task that are direct children of the thing we
// are reparenting from.
return;
return false;
}
if (newParentInMultiWindow && !task.supportsMultiWindowInDisplayArea(newParentTda)) {
Slog.e(TAG, "reparentChildrenTasksHierarchyOp non-resizeable task to multi window,"
+ " 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;
tasksToReparent.add(task);
}, !hop.getToTop());
if (hop.getToTop()) {
tasksToReparent.add(0, task);
} else {
tasksToReparent.add(task);
}
return hop.getReparentTopOnly() && tasksToReparent.size() == 1;
});
final int count = tasksToReparent.size();
for (int i = 0; i < count; ++i) {