diff options
| author | 2022-07-21 10:18:06 -0700 | |
|---|---|---|
| committer | 2022-08-04 10:05:24 -0700 | |
| commit | ef72f1134b72ea4c616e67bec7badabfc090eb36 (patch) | |
| tree | 6b66d3670ddcc4602a9833fc7fb37aac112862cb | |
| parent | fa81e0082b503a6bc1d86bb825a88ffef4bc0ca9 (diff) | |
Handle alwaysOnTop in DisplayAreas.
- Also add setAlwaysOnTop API in WindowContainerTransaction.
Bug: 188102153
Test: atest DisplayAreaTest
Test: Tested manually by setting it on DisplayAreas
Change-Id: I683e3c6bdf1bb6a590829a197f5dbef2c171d7e9
Merged-In: I683e3c6bdf1bb6a590829a197f5dbef2c171d7e9
4 files changed, 159 insertions, 5 deletions
diff --git a/core/java/android/window/WindowContainerTransaction.java b/core/java/android/window/WindowContainerTransaction.java index 3bffa890122a..9e88f9026bcc 100644 --- a/core/java/android/window/WindowContainerTransaction.java +++ b/core/java/android/window/WindowContainerTransaction.java @@ -679,7 +679,39 @@ public final class WindowContainerTransaction implements Parcelable { .build(); mHierarchyOps.add(hierarchyOp); return this; + } + /** + * Sets/removes the always on top flag for this {@code windowContainer}. See + * {@link com.android.server.wm.ConfigurationContainer#setAlwaysOnTop(boolean)}. + * Please note that this method is only intended to be used for a + * {@link com.android.server.wm.DisplayArea}. + * + * <p> + * Setting always on top to {@code True} will also make the {@code windowContainer} to move + * to the top. + * </p> + * <p> + * Setting always on top to {@code False} will make this {@code windowContainer} to move + * below the other always on top sibling containers. + * </p> + * + * @param windowContainer the container which the flag need to be updated for. + * @param alwaysOnTop denotes whether or not always on top flag should be set. + * @hide + */ + @NonNull + public WindowContainerTransaction setAlwaysOnTop( + @NonNull WindowContainerToken windowContainer, + boolean alwaysOnTop) { + final HierarchyOp hierarchyOp = + new HierarchyOp.Builder( + HierarchyOp.HIERARCHY_OP_TYPE_SET_ALWAYS_ON_TOP) + .setContainer(windowContainer.asBinder()) + .setAlwaysOnTop(alwaysOnTop) + .build(); + mHierarchyOps.add(hierarchyOp); + return this; } /** @@ -1091,6 +1123,7 @@ public final class WindowContainerTransaction implements Parcelable { public static final int HIERARCHY_OP_TYPE_ADD_RECT_INSETS_PROVIDER = 16; public static final int HIERARCHY_OP_TYPE_REMOVE_INSETS_PROVIDER = 17; public static final int HIERARCHY_OP_TYPE_REQUEST_FOCUS_ON_TASK_FRAGMENT = 18; + public static final int HIERARCHY_OP_TYPE_SET_ALWAYS_ON_TOP = 19; // The following key(s) are for use with mLaunchOptions: // When launching a task (eg. from recents), this is the taskId to be launched. @@ -1141,6 +1174,8 @@ public final class WindowContainerTransaction implements Parcelable { @Nullable private ShortcutInfo mShortcutInfo; + private boolean mAlwaysOnTop; + public static HierarchyOp createForReparent( @NonNull IBinder container, @Nullable IBinder reparent, boolean toTop) { return new HierarchyOp.Builder(HIERARCHY_OP_TYPE_REPARENT) @@ -1238,6 +1273,7 @@ public final class WindowContainerTransaction implements Parcelable { mTaskFragmentCreationOptions = copy.mTaskFragmentCreationOptions; mPendingIntent = copy.mPendingIntent; mShortcutInfo = copy.mShortcutInfo; + mAlwaysOnTop = copy.mAlwaysOnTop; } protected HierarchyOp(Parcel in) { @@ -1259,6 +1295,7 @@ public final class WindowContainerTransaction implements Parcelable { mTaskFragmentCreationOptions = in.readTypedObject(TaskFragmentCreationParams.CREATOR); mPendingIntent = in.readTypedObject(PendingIntent.CREATOR); mShortcutInfo = in.readTypedObject(ShortcutInfo.CREATOR); + mAlwaysOnTop = in.readBoolean(); } public int getType() { @@ -1324,6 +1361,10 @@ public final class WindowContainerTransaction implements Parcelable { return mActivityIntent; } + public boolean isAlwaysOnTop() { + return mAlwaysOnTop; + } + @Nullable public TaskFragmentCreationParams getTaskFragmentCreationOptions() { return mTaskFragmentCreationOptions; @@ -1392,6 +1433,9 @@ public final class WindowContainerTransaction implements Parcelable { + " insetsType=" + Arrays.toString(mInsetsTypes) + "}"; case HIERARCHY_OP_TYPE_REQUEST_FOCUS_ON_TASK_FRAGMENT: return "{requestFocusOnTaskFragment: container=" + mContainer + "}"; + case HIERARCHY_OP_TYPE_SET_ALWAYS_ON_TOP: + return "{setAlwaysOnTop: container=" + mContainer + + " alwaysOnTop=" + mAlwaysOnTop + "}"; default: return "{mType=" + mType + " container=" + mContainer + " reparent=" + mReparent + " mToTop=" + mToTop @@ -1421,6 +1465,7 @@ public final class WindowContainerTransaction implements Parcelable { dest.writeTypedObject(mTaskFragmentCreationOptions, flags); dest.writeTypedObject(mPendingIntent, flags); dest.writeTypedObject(mShortcutInfo, flags); + dest.writeBoolean(mAlwaysOnTop); } @Override @@ -1479,6 +1524,8 @@ public final class WindowContainerTransaction implements Parcelable { @Nullable private ShortcutInfo mShortcutInfo; + private boolean mAlwaysOnTop; + Builder(int type) { mType = type; } @@ -1538,6 +1585,11 @@ public final class WindowContainerTransaction implements Parcelable { return this; } + Builder setAlwaysOnTop(boolean alwaysOnTop) { + mAlwaysOnTop = alwaysOnTop; + return this; + } + Builder setTaskFragmentCreationOptions( @Nullable TaskFragmentCreationParams taskFragmentCreationOptions) { mTaskFragmentCreationOptions = taskFragmentCreationOptions; @@ -1566,6 +1618,7 @@ public final class WindowContainerTransaction implements Parcelable { hierarchyOp.mLaunchOptions = mLaunchOptions; hierarchyOp.mActivityIntent = mActivityIntent; hierarchyOp.mPendingIntent = mPendingIntent; + hierarchyOp.mAlwaysOnTop = mAlwaysOnTop; hierarchyOp.mTaskFragmentCreationOptions = mTaskFragmentCreationOptions; hierarchyOp.mShortcutInfo = mShortcutInfo; diff --git a/services/core/java/com/android/server/wm/DisplayArea.java b/services/core/java/com/android/server/wm/DisplayArea.java index 863782a4316e..04229063732f 100644 --- a/services/core/java/com/android/server/wm/DisplayArea.java +++ b/services/core/java/com/android/server/wm/DisplayArea.java @@ -207,6 +207,23 @@ public class DisplayArea<T extends WindowContainer> extends WindowContainer<T> { return false; } + @Override + public void setAlwaysOnTop(boolean alwaysOnTop) { + if (isAlwaysOnTop() == alwaysOnTop) { + return; + } + super.setAlwaysOnTop(alwaysOnTop); + // positionChildAtTop() must be called even when always on top gets turned off because + // we need to make sure that the display area is moved from among always on top containers + // to below other always on top containers. Since the position the display area should be + // inserted into is calculated properly in {@link DisplayContent#getTopInsertPosition()} + // in both cases, we can just request that the root task is put at top here. + if (getParent().asDisplayArea() != null) { + getParent().asDisplayArea().positionChildAt(POSITION_TOP, this, + false /* includingParents */); + } + } + boolean getIgnoreOrientationRequest() { // Adding an exception for when ignoreOrientationRequest is overridden at runtime for all // DisplayArea-s. For example, this is needed for the Kids Mode since many Kids apps aren't @@ -234,6 +251,18 @@ public class DisplayArea<T extends WindowContainer> extends WindowContainer<T> { // The min possible position we can insert the child at. int minPosition = findMinPositionForChildDisplayArea(child); + // Place all non-always-on-top containers below always-on-top ones. + int alwaysOnTopCount = 0; + for (int i = minPosition; i <= maxPosition; i++) { + if (mChildren.get(i).isAlwaysOnTop()) { + alwaysOnTopCount++; + } + } + if (child.isAlwaysOnTop()) { + minPosition = maxPosition - alwaysOnTopCount + 1; + } else { + maxPosition -= alwaysOnTopCount; + } return Math.max(Math.min(requestPosition, maxPosition), minPosition); } diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java index 48740a3c2f50..93f2139be677 100644 --- a/services/core/java/com/android/server/wm/WindowOrganizerController.java +++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java @@ -37,6 +37,7 @@ import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_RESTORE_TRANSIENT_ORDER; import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_ADJACENT_ROOTS; import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_ADJACENT_TASK_FRAGMENTS; +import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_ALWAYS_ON_TOP; import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_LAUNCH_ADJACENT_FLAG_ROOT; import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_LAUNCH_ROOT; import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENT; @@ -1067,6 +1068,18 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub WindowContainer.fromBinder(hop.getContainer()) .removeLocalInsetsSourceProvider(hop.getInsetsTypes()); break; + case HIERARCHY_OP_TYPE_SET_ALWAYS_ON_TOP: + final WindowContainer container = WindowContainer.fromBinder(hop.getContainer()); + if (container == null || container.asDisplayArea() == null + || !container.isAttached()) { + Slog.e(TAG, "Attempt to operate on unknown or detached display area: " + + container); + break; + } + container.setAlwaysOnTop(hop.isAlwaysOnTop()); + effects |= TRANSACT_EFFECTS_LIFECYCLE; + break; + } return effects; } diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java index d94e6c995223..b87c5a364c82 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java @@ -16,6 +16,7 @@ package com.android.server.wm; +import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; @@ -399,9 +400,9 @@ public class DisplayAreaTest extends WindowTestsBase { parentBounds.right / 2, parentBounds.bottom); final Rect childBounds2 = new Rect(parentBounds.right / 2, parentBounds.top, parentBounds.right, parentBounds.bottom); - TestDisplayArea parentDa = new TestDisplayArea(mWm, parentBounds); - TestDisplayArea childDa1 = new TestDisplayArea(mWm, childBounds1); - TestDisplayArea childDa2 = new TestDisplayArea(mWm, childBounds2); + TestDisplayArea parentDa = new TestDisplayArea(mWm, parentBounds, "Parent"); + TestDisplayArea childDa1 = new TestDisplayArea(mWm, childBounds1, "Child1"); + TestDisplayArea childDa2 = new TestDisplayArea(mWm, childBounds2, "Child2"); parentDa.addChild(childDa1, 0); parentDa.addChild(childDa2, 1); @@ -619,9 +620,67 @@ public class DisplayAreaTest extends WindowTestsBase { controller.registerOrganizer(mockDisplayAreaOrganizer, FEATURE_VENDOR_FIRST); } + @Test + public void testSetAlwaysOnTop_movesDisplayAreaToTop() { + final Rect bounds = new Rect(0, 0, 100, 100); + DisplayArea<WindowContainer> parent = new TestDisplayArea(mWm, bounds, "Parent"); + parent.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); + DisplayArea<WindowContainer> child1 = new TestDisplayArea(mWm, bounds, "Child1"); + child1.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); + DisplayArea<WindowContainer> child2 = new TestDisplayArea(mWm, bounds, "Child2"); + child2.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); + parent.addChild(child2, 0); + parent.addChild(child1, 1); + + child2.setAlwaysOnTop(true); + + assertEquals(parent.getChildAt(1), child2); + assertThat(child2.isAlwaysOnTop()).isTrue(); + } + + @Test + public void testDisplayAreaRequestsTopPosition_alwaysOnTopSiblingExists_doesNotMoveToTop() { + final Rect bounds = new Rect(0, 0, 100, 100); + DisplayArea<WindowContainer> parent = new TestDisplayArea(mWm, bounds, "Parent"); + parent.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); + DisplayArea<WindowContainer> alwaysOnTopChild = new TestDisplayArea(mWm, bounds, + "AlwaysOnTopChild"); + alwaysOnTopChild.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); + DisplayArea<WindowContainer> child = new TestDisplayArea(mWm, bounds, "Child"); + child.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); + parent.addChild(alwaysOnTopChild, 0); + parent.addChild(child, 1); + alwaysOnTopChild.setAlwaysOnTop(true); + + parent.positionChildAt(POSITION_TOP, child, false /* includingParents */); + + assertEquals(parent.getChildAt(1), alwaysOnTopChild); + assertEquals(parent.getChildAt(0), child); + } + + @Test + public void testAlwaysOnTopDisplayArea_requestsNonTopLocation_doesNotMove() { + final Rect bounds = new Rect(0, 0, 100, 100); + DisplayArea<WindowContainer> parent = new TestDisplayArea(mWm, bounds, "Parent"); + parent.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); + DisplayArea<WindowContainer> alwaysOnTopChild = new TestDisplayArea(mWm, bounds, + "AlwaysOnTopChild"); + alwaysOnTopChild.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); + DisplayArea<WindowContainer> child = new TestDisplayArea(mWm, bounds, "Child"); + child.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); + parent.addChild(alwaysOnTopChild, 0); + parent.addChild(child, 1); + alwaysOnTopChild.setAlwaysOnTop(true); + + parent.positionChildAt(POSITION_BOTTOM, alwaysOnTopChild, false /* includingParents */); + + assertEquals(parent.getChildAt(1), alwaysOnTopChild); + assertEquals(parent.getChildAt(0), child); + } + private static class TestDisplayArea<T extends WindowContainer> extends DisplayArea<T> { - private TestDisplayArea(WindowManagerService wms, Rect bounds) { - super(wms, ANY, "half display area"); + private TestDisplayArea(WindowManagerService wms, Rect bounds, String name) { + super(wms, ANY, name); setBounds(bounds); } |