WindowContainerTransaction for setting adjacent roots

Ability to specify that 2 root tasks are adjcent there by allowing them
to occlude their parent. Similar to how the legacy primary/secondary
split windowing modes are used to occlude their parent.

Also, occlude parent if a child in multi-window mode as a matching
bounds.

Bug: 177166639
Test: AppConfigurationTests#testSplitscreenPortraitAppOrientationRequests
Change-Id: I7d54bfa188700a76b0bca911d2011123c21553ad
This commit is contained in:
Wale Ogunwale
2021-01-15 22:39:24 -08:00
parent 9e5c68e73f
commit c04c4c1098
8 changed files with 130 additions and 2 deletions

View File

@@ -727,6 +727,11 @@ package android.graphics {
method @AnyThread @NonNull public static android.graphics.ImageDecoder.Source createSource(android.content.res.Resources, java.io.InputStream, int);
}
public final class Rect implements android.os.Parcelable {
method public void splitHorizontally(@NonNull android.graphics.Rect...);
method public void splitVertically(@NonNull android.graphics.Rect...);
}
}
package android.graphics.drawable {
@@ -2560,6 +2565,7 @@ package android.window {
method @NonNull public android.window.WindowContainerTransaction reparentTasks(@Nullable android.window.WindowContainerToken, @Nullable android.window.WindowContainerToken, @Nullable int[], @Nullable int[], boolean);
method @NonNull public android.window.WindowContainerTransaction scheduleFinishEnterPip(@NonNull android.window.WindowContainerToken, @NonNull android.graphics.Rect);
method @NonNull public android.window.WindowContainerTransaction setActivityWindowingMode(@NonNull android.window.WindowContainerToken, int);
method @NonNull public android.window.WindowContainerTransaction setAdjacentRoots(@NonNull android.window.WindowContainerToken, @NonNull android.window.WindowContainerToken);
method @NonNull public android.window.WindowContainerTransaction setAppBounds(@NonNull android.window.WindowContainerToken, @NonNull android.graphics.Rect);
method @NonNull public android.window.WindowContainerTransaction setBounds(@NonNull android.window.WindowContainerToken, @NonNull android.graphics.Rect);
method @NonNull public android.window.WindowContainerTransaction setBoundsChangeTransaction(@NonNull android.window.WindowContainerToken, @NonNull android.view.SurfaceControl.Transaction);

View File

@@ -322,6 +322,21 @@ public final class WindowContainerTransaction implements Parcelable {
return this;
}
/**
* Sets to containers adjacent to each other. Containers below two visible adjacent roots will
* be made invisible. This currently only applies to Task containers created by organizer.
* @param root1 the first root.
* @param root2 the second root.
*/
@NonNull
public WindowContainerTransaction setAdjacentRoots(
@NonNull WindowContainerToken root1, @NonNull WindowContainerToken root2) {
mHierarchyOps.add(HierarchyOp.createForAdjacentRoots(
root1.asBinder(),
root2.asBinder()));
return this;
}
/**
* Merges another WCT into this one.
* @param transfer When true, this will transfer everything from other potentially leaving
@@ -642,6 +657,7 @@ public final class WindowContainerTransaction implements Parcelable {
public static final int HIERARCHY_OP_TYPE_REORDER = 1;
public static final int HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT = 2;
public static final int HIERARCHY_OP_TYPE_SET_LAUNCH_ROOT = 3;
public static final int HIERARCHY_OP_TYPE_SET_ADJACENT_ROOTS = 4;
private final int mType;
@@ -680,6 +696,11 @@ public final class WindowContainerTransaction implements Parcelable {
container, null, windowingModes, activityTypes, false);
}
public static HierarchyOp createForAdjacentRoots(IBinder root1, IBinder root2) {
return new HierarchyOp(HIERARCHY_OP_TYPE_SET_ADJACENT_ROOTS,
root1, root2, null, null, false);
}
private HierarchyOp(int type, @NonNull IBinder container, @Nullable IBinder reparent,
int[] windowingModes, int[] activityTypes, boolean toTop) {
mType = type;
@@ -728,6 +749,11 @@ public final class WindowContainerTransaction implements Parcelable {
return mContainer;
}
@NonNull
public IBinder getAdjacentRoot() {
return mReparent;
}
public boolean getToTop() {
return mToTop;
}
@@ -756,6 +782,9 @@ public final class WindowContainerTransaction implements Parcelable {
+ mReparent + "}";
case HIERARCHY_OP_TYPE_REORDER:
return "{reorder: " + mContainer + " to " + (mToTop ? "top" : "bottom") + "}";
case HIERARCHY_OP_TYPE_SET_ADJACENT_ROOTS:
return "{SetAdjacentRoot: container=" + mContainer
+ " adjacentRoot=" + mReparent + "}";
default:
return "{mType=" + mType + " container=" + mContainer + " reparent=" + mReparent
+ " mToTop=" + mToTop + " mWindowingMode=" + mWindowingModes

View File

@@ -19,6 +19,7 @@ package android.graphics;
import android.annotation.CheckResult;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.TestApi;
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.os.Parcelable;
@@ -701,6 +702,40 @@ public final class Rect implements Parcelable {
}
}
/**
* Splits this Rect into small rects of the same width.
* @hide
*/
@TestApi
public void splitVertically(@NonNull Rect ...splits) {
final int count = splits.length;
final int splitWidth = width() / count;
for (int i = 0; i < count; i++) {
final Rect split = splits[i];
split.left = left + (splitWidth * i);
split.top = top;
split.right = split.left + splitWidth;
split.bottom = bottom;
}
}
/**
* Splits this Rect into small rects of the same height.
* @hide
*/
@TestApi
public void splitHorizontally(@NonNull Rect ...outSplits) {
final int count = outSplits.length;
final int splitHeight = height() / count;
for (int i = 0; i < count; i++) {
final Rect split = outSplits[i];
split.left = left;
split.top = top + (splitHeight * i);
split.right = right;
split.bottom = split.top + splitHeight;
}
}
/**
* Parcelable interface methods
*/

View File

@@ -142,6 +142,15 @@ class StageCoordinator implements SplitLayout.LayoutChangeListener,
mTaskOrganizer.applyTransaction(wct);
}
private void onStageRootTaskAppeared(StageListenerImpl stageListener) {
if (mMainStageListener.mHasRootTask && mSideStageListener.mHasRootTask) {
final WindowContainerTransaction wct = new WindowContainerTransaction();
// Make the stages adjacent to each other so they occlude what's behind them.
wct.setAdjacentRoots(mMainStage.mRootTaskInfo.token, mSideStage.mRootTaskInfo.token);
mTaskOrganizer.applyTransaction(wct);
}
}
private void onStageRootTaskVanished(StageListenerImpl stageListener) {
if (stageListener == mMainStageListener || stageListener == mSideStageListener) {
final WindowContainerTransaction wct = new WindowContainerTransaction();
@@ -357,6 +366,7 @@ class StageCoordinator implements SplitLayout.LayoutChangeListener,
@Override
public void onRootTaskAppeared() {
mHasRootTask = true;
StageCoordinator.this.onStageRootTaskAppeared(this);
}
@Override

View File

@@ -70,9 +70,9 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener {
@CallSuper
public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) {
if (!taskInfo.hasParentTask()) {
mCallbacks.onRootTaskAppeared();
mRootLeash = leash;
mRootTaskInfo = taskInfo;
mCallbacks.onRootTaskAppeared();
} else if (taskInfo.parentTaskId == mRootTaskInfo.taskId) {
mChildrenLeashes.put(taskInfo.taskId, leash);
mChildrenTaskInfo.put(taskInfo.taskId, taskInfo);

View File

@@ -662,7 +662,9 @@ public abstract class ConfigurationContainer<E extends ConfigurationContainer> {
pw.println(getName()
+ " type=" + activityTypeToString(getActivityType())
+ " mode=" + windowingModeToString(getWindowingMode())
+ " override-mode=" + windowingModeToString(getRequestedOverrideWindowingMode()));
+ " override-mode=" + windowingModeToString(getRequestedOverrideWindowingMode())
+ " requested-bounds=" + getRequestedOverrideBounds().toShortString()
+ " bounds=" + getBounds().toShortString());
for (int i = getChildCount() - 1; i >= 0; --i) {
final E cc = getChildAt(i);
pw.print(childPrefix + "#" + i + " ");

View File

@@ -29,6 +29,7 @@ import static android.app.WindowConfiguration.PINNED_WINDOWING_MODE_ELEVATION_IN
import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
@@ -433,6 +434,7 @@ class Task extends WindowContainer<WindowContainer> {
CharSequence lastDescription; // Last description captured for this item.
Task mAdjacentTask; // Task adjacent to this one.
int mAffiliatedTaskId; // taskId of parent affiliation or self if no parent.
Task mPrevAffiliate; // previous task in affiliated chain.
int mPrevAffiliateTaskId = INVALID_TASK_ID; // previous id for persistence.
@@ -1564,6 +1566,11 @@ class Task extends WindowContainer<WindowContainer> {
mTaskId, mUserId);
}
void setAdjacentTask(Task adjacent) {
mAdjacentTask = adjacent;
adjacent.mAdjacentTask = this;
}
void setTaskToAffiliateWith(Task taskToAffiliateWith) {
closeRecentsChain();
mAffiliatedTaskId = taskToAffiliateWith.mAffiliatedTaskId;
@@ -4244,6 +4251,7 @@ class Task extends WindowContainer<WindowContainer> {
}
}
final List<Task> adjacentTasks = new ArrayList<>();
final int windowingMode = getWindowingMode();
final boolean isAssistantType = isActivityTypeAssistant();
for (int i = parent.getChildCount() - 1; i >= 0; --i) {
@@ -4273,6 +4281,15 @@ class Task extends WindowContainer<WindowContainer> {
continue;
}
return TASK_VISIBILITY_INVISIBLE;
} else if (otherWindowingMode == WINDOWING_MODE_MULTI_WINDOW
&& other.matchParentBounds()) {
if (other.isTranslucent(starting)) {
// Can be visible behind a translucent task.
gotTranslucentFullscreen = true;
continue;
}
// Multi-window task that matches parent bounds would occlude other children.
return TASK_VISIBILITY_INVISIBLE;
} else if (otherWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
&& !gotOpaqueSplitScreenPrimary) {
gotSplitScreenStack = true;
@@ -4305,6 +4322,20 @@ class Task extends WindowContainer<WindowContainer> {
// assistant window surfaces in window manager whenever it is visible.
return TASK_VISIBILITY_INVISIBLE;
}
if (other.mAdjacentTask != null) {
if (adjacentTasks.contains(other.mAdjacentTask)) {
if (other.isTranslucent(starting)
|| other.mAdjacentTask.isTranslucent(starting)) {
// Can be visible behind a translucent adjacent tasks.
gotTranslucentFullscreen = true;
continue;
}
// Can not be visible behind adjacent tasks.
return TASK_VISIBILITY_INVISIBLE;
} else {
adjacentTasks.add(other);
}
}
}
if (!shouldBeVisible) {

View File

@@ -20,6 +20,7 @@ import static android.Manifest.permission.READ_FRAME_BUFFER;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REORDER;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REPARENT;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_ADJACENT_ROOTS;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_LAUNCH_ROOT;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_ORGANIZER;
@@ -279,6 +280,9 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
case HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT:
effects |= reparentChildrenTasksHierarchyOp(hop, transition, syncId);
break;
case HIERARCHY_OP_TYPE_SET_ADJACENT_ROOTS:
effects |= setAdjacentRootsHierarchyOp(hop);
break;
case HIERARCHY_OP_TYPE_REORDER:
case HIERARCHY_OP_TYPE_REPARENT:
final WindowContainer wc = WindowContainer.fromBinder(hop.getContainer());
@@ -597,6 +601,17 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
return TRANSACT_EFFECTS_LIFECYCLE;
}
private int setAdjacentRootsHierarchyOp(WindowContainerTransaction.HierarchyOp hop) {
final Task root1 = WindowContainer.fromBinder(hop.getContainer()).asTask();
final Task root2 = WindowContainer.fromBinder(hop.getAdjacentRoot()).asTask();
if (!root1.mCreatedByOrganizer || !root2.mCreatedByOrganizer) {
throw new IllegalArgumentException("setAdjacentRootsHierarchyOp: Not created by"
+ " organizer root1=" + root1 + " root2=" + root2);
}
root1.setAdjacentTask(root2);
return TRANSACT_EFFECTS_LIFECYCLE;
}
private void sanitizeWindowContainer(WindowContainer wc) {
if (!(wc instanceof Task) && !(wc instanceof DisplayArea)) {
throw new RuntimeException("Invalid token in task or displayArea transaction");