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:
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 + " ");
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user