diff options
8 files changed, 130 insertions, 2 deletions
diff --git a/core/api/test-current.txt b/core/api/test-current.txt index 9f57502d5cf0..1f941436ae8b 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -728,6 +728,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 { @@ -2561,6 +2566,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); diff --git a/core/java/android/window/WindowContainerTransaction.java b/core/java/android/window/WindowContainerTransaction.java index 6bc31107bddf..f6cd7e240bd2 100644 --- a/core/java/android/window/WindowContainerTransaction.java +++ b/core/java/android/window/WindowContainerTransaction.java @@ -323,6 +323,21 @@ public final class WindowContainerTransaction implements Parcelable { } /** + * 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 * other in an unusable state. When false, other is left alone, but @@ -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 diff --git a/graphics/java/android/graphics/Rect.java b/graphics/java/android/graphics/Rect.java index 87ca9607dbce..7e68bc06d506 100644 --- a/graphics/java/android/graphics/Rect.java +++ b/graphics/java/android/graphics/Rect.java @@ -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; @@ -702,6 +703,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 */ @Override diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java index bdac37ab737e..5c1d18e71800 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java @@ -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 diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java index 30f2701151d7..efd42ce552b1 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java @@ -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); diff --git a/services/core/java/com/android/server/wm/ConfigurationContainer.java b/services/core/java/com/android/server/wm/ConfigurationContainer.java index 0fc4cdb6a928..d90e88576909 100644 --- a/services/core/java/com/android/server/wm/ConfigurationContainer.java +++ b/services/core/java/com/android/server/wm/ConfigurationContainer.java @@ -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 + " "); diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index f24c3ad0836f..2295ee35a4fb 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -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) { diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java index 1509ff67a529..dcab478ad7a8 100644 --- a/services/core/java/com/android/server/wm/WindowOrganizerController.java +++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java @@ -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"); |