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
This commit is contained in:
Gaurav Bhola
2022-07-21 10:18:06 -07:00
parent fa81e0082b
commit ef72f1134b
4 changed files with 159 additions and 5 deletions

View File

@@ -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;

View File

@@ -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);
}

View File

@@ -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;
}

View File

@@ -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);
}