Only update mAboveInsetsState when necessary
This CL only updates mAboveInsetsState while the z-order of a window is
changed or the display info has been updated.
This can be a step to make client compute its window frame locally.
Bug: 161810301
Bug: 175861127
Test: atest DisplayPolicyTests DisplayPolicyLayoutTests
Change-Id: Ic202ca82ae3b0aeaf4d95ba6d0847970aa9896a6
diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java
index 219190f..21dd1fb 100644
--- a/core/java/android/view/InsetsState.java
+++ b/core/java/android/view/InsetsState.java
@@ -20,8 +20,6 @@
import static android.view.InsetsStateProto.DISPLAY_FRAME;
import static android.view.InsetsStateProto.SOURCES;
import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
-import static android.view.WindowInsets.Type.MANDATORY_SYSTEM_GESTURES;
-import static android.view.WindowInsets.Type.SYSTEM_GESTURES;
import static android.view.WindowInsets.Type.displayCutout;
import static android.view.WindowInsets.Type.ime;
import static android.view.WindowInsets.Type.indexOf;
@@ -106,14 +104,11 @@
public static final int ITYPE_NAVIGATION_BAR = 1;
public static final int ITYPE_CAPTION_BAR = 2;
- // The always visible types are visible to all windows regardless of the z-order.
- public static final int FIRST_ALWAYS_VISIBLE_TYPE = 3;
- public static final int ITYPE_TOP_GESTURES = FIRST_ALWAYS_VISIBLE_TYPE;
+ public static final int ITYPE_TOP_GESTURES = 3;
public static final int ITYPE_BOTTOM_GESTURES = 4;
public static final int ITYPE_LEFT_GESTURES = 5;
public static final int ITYPE_RIGHT_GESTURES = 6;
- /** Additional gesture inset types that map into {@link Type.MANDATORY_SYSTEM_GESTURES}. */
public static final int ITYPE_TOP_MANDATORY_GESTURES = 7;
public static final int ITYPE_BOTTOM_MANDATORY_GESTURES = 8;
public static final int ITYPE_LEFT_MANDATORY_GESTURES = 9;
@@ -123,7 +118,6 @@
public static final int ITYPE_TOP_DISPLAY_CUTOUT = 12;
public static final int ITYPE_RIGHT_DISPLAY_CUTOUT = 13;
public static final int ITYPE_BOTTOM_DISPLAY_CUTOUT = 14;
- public static final int LAST_ALWAYS_VISIBLE_TYPE = ITYPE_BOTTOM_DISPLAY_CUTOUT;
public static final int ITYPE_LEFT_TAPPABLE_ELEMENT = 15;
public static final int ITYPE_TOP_TAPPABLE_ELEMENT = 16;
@@ -188,18 +182,6 @@
}
/**
- * Mirror the always visible sources from the other state. They will share the same object for
- * the always visible types.
- *
- * @param other the state to mirror the mirrored sources from.
- */
- public void mirrorAlwaysVisibleInsetsSources(InsetsState other) {
- for (int type = FIRST_ALWAYS_VISIBLE_TYPE; type <= LAST_ALWAYS_VISIBLE_TYPE; type++) {
- mSources[type] = other.mSources[type];
- }
- }
-
- /**
* Calculates {@link WindowInsets} based on the current source configuration.
*
* @param frame The frame to calculate the insets relative to.
@@ -380,14 +362,14 @@
processSourceAsPublicType(source, typeInsetsMap, typeSideMap, typeVisibilityMap,
insets, type);
- if (type == MANDATORY_SYSTEM_GESTURES) {
+ if (type == Type.MANDATORY_SYSTEM_GESTURES) {
// Mandatory system gestures are also system gestures.
// TODO: find a way to express this more generally. One option would be to define
// Type.systemGestureInsets() as NORMAL | MANDATORY, but then we lose the
// ability to set systemGestureInsets() independently from
// mandatorySystemGestureInsets() in the Builder.
processSourceAsPublicType(source, typeInsetsMap, typeSideMap, typeVisibilityMap,
- insets, SYSTEM_GESTURES);
+ insets, Type.SYSTEM_GESTURES);
}
}
@@ -493,9 +475,14 @@
* to the client.
*
* @param type The {@link InternalInsetsType} of the source to remove
+ * @return {@code true} if this InsetsState was modified; {@code false} otherwise.
*/
- public void removeSource(@InternalInsetsType int type) {
+ public boolean removeSource(@InternalInsetsType int type) {
+ if (mSources[type] == null) {
+ return false;
+ }
mSources[type] = null;
+ return true;
}
/**
@@ -552,6 +539,24 @@
}
}
+ /**
+ * Sets the values from the other InsetsState. But for sources, only specific types of source
+ * would be set.
+ *
+ * @param other the other InsetsState.
+ * @param types the only types of sources would be set.
+ */
+ public void set(InsetsState other, @InsetsType int types) {
+ mDisplayFrame.set(other.mDisplayFrame);
+ mDisplayCutout.set(other.mDisplayCutout);
+ mRoundedCorners = other.getRoundedCorners();
+ final ArraySet<Integer> t = toInternalType(types);
+ for (int i = t.size() - 1; i >= 0; i--) {
+ final int type = t.valueAt(i);
+ mSources[type] = other.mSources[type];
+ }
+ }
+
public void addSource(InsetsSource source) {
mSources[source.getType()] = source;
}
@@ -575,6 +580,18 @@
if ((types & Type.CAPTION_BAR) != 0) {
result.add(ITYPE_CAPTION_BAR);
}
+ if ((types & Type.SYSTEM_GESTURES) != 0) {
+ result.add(ITYPE_LEFT_GESTURES);
+ result.add(ITYPE_TOP_GESTURES);
+ result.add(ITYPE_RIGHT_GESTURES);
+ result.add(ITYPE_BOTTOM_GESTURES);
+ }
+ if ((types & Type.MANDATORY_SYSTEM_GESTURES) != 0) {
+ result.add(ITYPE_LEFT_MANDATORY_GESTURES);
+ result.add(ITYPE_TOP_MANDATORY_GESTURES);
+ result.add(ITYPE_RIGHT_MANDATORY_GESTURES);
+ result.add(ITYPE_BOTTOM_MANDATORY_GESTURES);
+ }
if ((types & Type.DISPLAY_CUTOUT) != 0) {
result.add(ITYPE_LEFT_DISPLAY_CUTOUT);
result.add(ITYPE_TOP_DISPLAY_CUTOUT);
diff --git a/core/java/android/view/RoundedCorners.java b/core/java/android/view/RoundedCorners.java
index 015e804..569c287 100644
--- a/core/java/android/view/RoundedCorners.java
+++ b/core/java/android/view/RoundedCorners.java
@@ -335,7 +335,7 @@
}
if (o instanceof RoundedCorners) {
RoundedCorners r = (RoundedCorners) o;
- return Arrays.deepEquals(mRoundedCorners, ((RoundedCorners) o).mRoundedCorners);
+ return Arrays.deepEquals(mRoundedCorners, r.mRoundedCorners);
}
return false;
}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 86968ed..f66c760 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -673,10 +673,6 @@
// Used in updating override configurations
private final Configuration mTempConfig = new Configuration();
- // Used in performing layout, to record the insets provided by other windows above the current
- // window.
- private InsetsState mTmpAboveInsetsState = new InsetsState();
-
/**
* Used to prevent recursions when calling
* {@link #ensureActivitiesVisible(ActivityRecord, int, boolean, boolean)}
@@ -778,13 +774,6 @@
+ " parentHidden=" + w.isParentWindowHidden());
}
- // Sets mAboveInsets for each window. Windows behind the window providing the insets can
- // receive the insets.
- if (!w.mAboveInsetsState.equals(mTmpAboveInsetsState)) {
- w.mAboveInsetsState.set(mTmpAboveInsetsState);
- mWinInsetsChanged.add(w);
- }
-
// If this view is GONE, then skip it -- keep the current frame, and let the caller know
// so they can ignore it if they want. (We do the normal layout for INVISIBLE windows,
// since that means "perform layout as normal, just don't display").
@@ -818,16 +807,8 @@
+ " mContainingFrame=" + w.getContainingFrame()
+ " mDisplayFrame=" + w.getDisplayFrame());
}
- provideInsetsByWindow(w);
};
- private void provideInsetsByWindow(WindowState w) {
- for (int i = 0; i < w.mProvidedInsetsSources.size(); i++) {
- final InsetsSource providedSource = w.mProvidedInsetsSources.valueAt(i);
- mTmpAboveInsetsState.addSource(providedSource);
- }
- }
-
private final Consumer<WindowState> mPerformLayoutAttached = w -> {
if (w.mLayoutAttached) {
if (DEBUG_LAYOUT) Slog.v(TAG, "2ND PASS " + w + " mHaveFrame=" + w.mHaveFrame
@@ -2511,8 +2492,13 @@
void onDisplayInfoChanged() {
final DisplayInfo info = mDisplayInfo;
- mDisplayFrames.onDisplayInfoUpdated(info, calculateDisplayCutoutForRotation(info.rotation),
- calculateRoundedCornersForRotation(info.rotation));
+ if (mDisplayFrames.onDisplayInfoUpdated(info,
+ calculateDisplayCutoutForRotation(info.rotation),
+ calculateRoundedCornersForRotation(info.rotation))) {
+ // TODO(b/161810301): Set notifyInsetsChange to true while the server no longer performs
+ // layout.
+ mInsetsStateController.onDisplayInfoUpdated(false /* notifyInsetsChange */);
+ }
mInputMonitor.layoutInputConsumers(info.logicalWidth, info.logicalHeight);
mDisplayPolicy.onDisplayInfoChanged(info);
}
@@ -3767,8 +3753,11 @@
// 2. Assign window layers based on the IME surface parent to make sure it is on top of the
// app.
assignWindowLayers(true /* setLayoutNeeded */);
- // 3. Update the IME control target to apply any inset change and animation.
- // 4. Reparent the IME container surface to either the input target app, or the IME window
+ // 3. The z-order of IME might have been changed. Update the above insets state.
+ mInsetsStateController.updateAboveInsetsState(
+ mInputMethodWindow, true /* notifyInsetsChange */);
+ // 4. Update the IME control target to apply any inset change and animation.
+ // 5. Reparent the IME container surface to either the input target app, or the IME window
// parent.
updateImeControlTarget();
}
@@ -4341,14 +4330,6 @@
+ " dh=" + mDisplayInfo.logicalHeight);
}
- // Used to indicate that we have processed the insets windows. This needs to be after
- // beginLayoutLw to ensure the raw insets state display related info is initialized.
- final InsetsState rawInsetsState = getInsetsStateController().getRawInsetsState();
- mTmpAboveInsetsState = new InsetsState();
- mTmpAboveInsetsState.setDisplayFrame(rawInsetsState.getDisplayFrame());
- mTmpAboveInsetsState.setDisplayCutout(rawInsetsState.getDisplayCutout());
- mTmpAboveInsetsState.mirrorAlwaysVisibleInsetsSources(rawInsetsState);
-
int seq = mLayoutSeq + 1;
if (seq < 0) seq = 0;
mLayoutSeq = seq;
diff --git a/services/core/java/com/android/server/wm/DisplayFrames.java b/services/core/java/com/android/server/wm/DisplayFrames.java
index e4230a2..a37f3f2 100644
--- a/services/core/java/com/android/server/wm/DisplayFrames.java
+++ b/services/core/java/com/android/server/wm/DisplayFrames.java
@@ -21,6 +21,7 @@
import static android.view.InsetsState.ITYPE_RIGHT_DISPLAY_CUTOUT;
import static android.view.InsetsState.ITYPE_TOP_DISPLAY_CUTOUT;
+import android.annotation.NonNull;
import android.graphics.Rect;
import android.util.proto.ProtoOutputStream;
import android.view.DisplayCutout;
@@ -70,25 +71,28 @@
* @param info the updated {@link DisplayInfo}.
* @param displayCutout the updated {@link DisplayCutout}.
* @param roundedCorners the updated {@link RoundedCorners}.
+ * @return {@code true} if the insets state has been changed; {@code false} otherwise.
*/
- public void onDisplayInfoUpdated(DisplayInfo info, WmDisplayCutout displayCutout,
- RoundedCorners roundedCorners) {
- mDisplayWidth = info.logicalWidth;
- mDisplayHeight = info.logicalHeight;
+ public boolean onDisplayInfoUpdated(DisplayInfo info, @NonNull WmDisplayCutout displayCutout,
+ @NonNull RoundedCorners roundedCorners) {
mRotation = info.rotation;
- final WmDisplayCutout wmDisplayCutout =
- displayCutout != null ? displayCutout : WmDisplayCutout.NO_CUTOUT;
final InsetsState state = mInsetsState;
- final Rect unrestricted = mUnrestricted;
final Rect safe = mDisplayCutoutSafe;
- final DisplayCutout cutout = wmDisplayCutout.getDisplayCutout();
+ final DisplayCutout cutout = displayCutout.getDisplayCutout();
+ if (mDisplayWidth == info.logicalWidth && mDisplayHeight == info.logicalHeight
+ && state.getDisplayCutout().equals(cutout)
+ && state.getRoundedCorners().equals(roundedCorners)) {
+ return false;
+ }
+ mDisplayWidth = info.logicalWidth;
+ mDisplayHeight = info.logicalHeight;
+ final Rect unrestricted = mUnrestricted;
unrestricted.set(0, 0, mDisplayWidth, mDisplayHeight);
safe.set(Integer.MIN_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE);
state.setDisplayFrame(unrestricted);
state.setDisplayCutout(cutout);
- state.setRoundedCorners(roundedCorners != null ? roundedCorners
- : RoundedCorners.NO_ROUNDED_CORNERS);
+ state.setRoundedCorners(roundedCorners);
if (!cutout.isEmpty()) {
if (cutout.getSafeInsetLeft() > 0) {
safe.left = unrestricted.left + cutout.getSafeInsetLeft();
@@ -116,6 +120,7 @@
state.removeSource(ITYPE_RIGHT_DISPLAY_CUTOUT);
state.removeSource(ITYPE_BOTTOM_DISPLAY_CUTOUT);
}
+ return true;
}
public void dumpDebug(ProtoOutputStream proto, long fieldId) {
diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java
index 580d328..75176df 100644
--- a/services/core/java/com/android/server/wm/InsetsStateController.java
+++ b/services/core/java/com/android/server/wm/InsetsStateController.java
@@ -27,6 +27,9 @@
import static android.view.InsetsState.ITYPE_INVALID;
import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
import static android.view.InsetsState.ITYPE_STATUS_BAR;
+import static android.view.WindowInsets.Type.displayCutout;
+import static android.view.WindowInsets.Type.mandatorySystemGestures;
+import static android.view.WindowInsets.Type.systemGestures;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
@@ -293,6 +296,76 @@
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
+ /**
+ * Updates {@link WindowState#mAboveInsetsState} for all windows in the display while the
+ * z-order of a window is changed.
+ *
+ * @param win The window whose z-order has changed.
+ * @param notifyInsetsChange {@code true} if the clients should be notified about the change.
+ */
+ void updateAboveInsetsState(WindowState win, boolean notifyInsetsChange) {
+ if (win == null || win.getDisplayContent() != mDisplayContent) {
+ return;
+ }
+ final boolean[] aboveWin = { true };
+ final InsetsState aboveInsetsState = new InsetsState();
+ aboveInsetsState.set(mState,
+ displayCutout() | systemGestures() | mandatorySystemGestures());
+ final SparseArray<InsetsSource> winProvidedSources = win.mProvidedInsetsSources;
+ final ArrayList<WindowState> insetsChangedWindows = new ArrayList<>();
+ mDisplayContent.forAllWindows(w -> {
+ if (aboveWin[0]) {
+ if (w == win) {
+ aboveWin[0] = false;
+ if (!win.mAboveInsetsState.equals(aboveInsetsState)) {
+ win.mAboveInsetsState.set(aboveInsetsState);
+ insetsChangedWindows.add(win);
+ }
+ return winProvidedSources.size() == 0;
+ } else {
+ final SparseArray<InsetsSource> providedSources = w.mProvidedInsetsSources;
+ for (int i = providedSources.size() - 1; i >= 0; i--) {
+ aboveInsetsState.addSource(providedSources.valueAt(i));
+ }
+ if (winProvidedSources.size() == 0) {
+ return false;
+ }
+ boolean changed = false;
+ for (int i = winProvidedSources.size() - 1; i >= 0; i--) {
+ changed |= w.mAboveInsetsState.removeSource(winProvidedSources.keyAt(i));
+ }
+ if (changed) {
+ insetsChangedWindows.add(w);
+ }
+ }
+ } else {
+ for (int i = winProvidedSources.size() - 1; i >= 0; i--) {
+ w.mAboveInsetsState.addSource(winProvidedSources.valueAt(i));
+ }
+ insetsChangedWindows.add(w);
+ }
+ return false;
+ }, true /* traverseTopToBottom */);
+ if (notifyInsetsChange) {
+ for (int i = insetsChangedWindows.size() - 1; i >= 0; i--) {
+ mDispatchInsetsChanged.accept(insetsChangedWindows.get(i));
+ }
+ }
+ }
+
+ void onDisplayInfoUpdated(boolean notifyInsetsChange) {
+ final ArrayList<WindowState> insetsChangedWindows = new ArrayList<>();
+ mDisplayContent.forAllWindows(w -> {
+ w.mAboveInsetsState.set(mState, displayCutout());
+ insetsChangedWindows.add(w);
+ }, true /* traverseTopToBottom */);
+ if (notifyInsetsChange) {
+ for (int i = insetsChangedWindows.size() - 1; i >= 0; i--) {
+ mDispatchInsetsChanged.accept(insetsChangedWindows.get(i));
+ }
+ }
+ }
+
void onInsetsModified(InsetsControlTarget caller) {
boolean changed = false;
for (int i = mProviders.size() - 1; i >= 0; i--) {
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 5dc5ab7..99246dd2 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -1874,6 +1874,10 @@
displayContent.sendNewConfiguration();
}
+ // This window doesn't have a frame yet. Don't let this window cause the insets change.
+ displayContent.getInsetsStateController().updateAboveInsetsState(
+ win, false /* notifyInsetsChanged */);
+
getInsetsSourceControls(win, outActiveControls);
}
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index a94b0aa..a8ab01f 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -211,11 +211,11 @@
import android.os.WorkSource;
import android.provider.Settings;
import android.text.TextUtils;
-import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.DisplayMetrics;
import android.util.MergedConfiguration;
import android.util.Slog;
+import android.util.SparseArray;
import android.util.TimeUtils;
import android.util.proto.ProtoOutputStream;
import android.view.Display;
@@ -648,12 +648,12 @@
/**
* The insets state of sources provided by windows above the current window.
*/
- InsetsState mAboveInsetsState = new InsetsState();
+ final InsetsState mAboveInsetsState = new InsetsState();
/**
* The insets sources provided by this window.
*/
- ArrayMap<Integer, InsetsSource> mProvidedInsetsSources = new ArrayMap<>();
+ final SparseArray<InsetsSource> mProvidedInsetsSources = new SparseArray<>();
/**
* Surface insets from the previous call to relayout(), used to track
@@ -2204,7 +2204,7 @@
}
}
- @Override
+ @Override
void removeImmediately() {
super.removeImmediately();
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
index b1ea4a5..5a0466a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
@@ -130,7 +130,7 @@
// insets state with the global one.
final InsetsState insetsState =
win.getDisplayContent().getInsetsStateController().getRawInsetsState();
- win.mAboveInsetsState = insetsState;
+ win.mAboveInsetsState.set(insetsState);
}
public void setRotation(int rotation, boolean includingWindows) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
index 2163661..47cf53b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
@@ -18,6 +18,7 @@
import static android.view.InsetsState.ITYPE_IME;
import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
+import static android.view.RoundedCorners.NO_ROUNDED_CORNERS;
import static android.view.Surface.ROTATION_0;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
@@ -37,6 +38,7 @@
import static com.android.server.policy.WindowManagerPolicy.NAV_BAR_BOTTOM;
import static com.android.server.policy.WindowManagerPolicy.NAV_BAR_RIGHT;
+import static com.android.server.wm.utils.WmDisplayCutout.NO_CUTOUT;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -300,9 +302,9 @@
displayPolicy.addWindowLw(mNavBarWindow, mNavBarWindow.mAttrs);
mNavBarWindow.getControllableInsetProvider().setServerVisible(true);
final InsetsState state = mDisplayContent.getInsetsStateController().getRawInsetsState();
- mImeWindow.mAboveInsetsState = state;
+ mImeWindow.mAboveInsetsState.set(state);
mDisplayContent.mDisplayFrames = new DisplayFrames(mDisplayContent.getDisplayId(),
- state, displayInfo, null /* displayCutout */, null /* roundedCorners*/);
+ state, displayInfo, NO_CUTOUT, NO_ROUNDED_CORNERS);
mDisplayContent.setInputMethodWindowLocked(mImeWindow);
mImeWindow.mAttrs.setFitInsetsSides(Side.all() & ~Side.BOTTOM);
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java
index 20775e8..683ed88 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java
@@ -117,7 +117,7 @@
static Pair<DisplayInfo, WmDisplayCutout> displayInfoAndCutoutForRotation(int rotation,
boolean withDisplayCutout, boolean isLongEdgeCutout) {
final DisplayInfo info = new DisplayInfo();
- WmDisplayCutout cutout = null;
+ WmDisplayCutout cutout = WmDisplayCutout.NO_CUTOUT;
final boolean flippedDimensions = rotation == ROTATION_90 || rotation == ROTATION_270;
info.logicalWidth = flippedDimensions ? DISPLAY_HEIGHT : DISPLAY_WIDTH;
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
index ee293fc..be03603 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
@@ -32,13 +32,14 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.server.wm.DisplayContent.IME_TARGET_INPUT;
+import static com.android.server.wm.WindowContainer.POSITION_TOP;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.atLeast;
+import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
@@ -208,7 +209,7 @@
app.mAboveInsetsState.getSource(ITYPE_IME).setFrame(mImeWindow.getFrame());
// Make sure app got notified.
- verify(app, atLeast(1)).notifyInsetsChanged();
+ verify(app, atLeastOnce()).notifyInsetsChanged();
// app will get visible IME insets while below IME.
assertTrue(getController().getInsetsForWindow(app).getSource(ITYPE_IME).isVisible());
@@ -336,6 +337,92 @@
assertFalse(app.getInsetsState().getSource(ITYPE_STATUS_BAR).isVisible());
}
+ @Test
+ public void testUpdateAboveInsetsState_provideInsets() {
+ final WindowState app = createTestWindow("app");
+ final WindowState statusBar = createTestWindow("statusBar");
+ final WindowState navBar = createTestWindow("navBar");
+
+ getController().getSourceProvider(ITYPE_STATUS_BAR).setWindow(statusBar, null, null);
+
+ assertNull(app.mAboveInsetsState.peekSource(ITYPE_STATUS_BAR));
+ assertNull(statusBar.mAboveInsetsState.peekSource(ITYPE_STATUS_BAR));
+ assertNull(navBar.mAboveInsetsState.peekSource(ITYPE_STATUS_BAR));
+
+ getController().updateAboveInsetsState(statusBar, true /* notifyInsetsChange */);
+
+ assertNotNull(app.mAboveInsetsState.peekSource(ITYPE_STATUS_BAR));
+ assertNull(statusBar.mAboveInsetsState.peekSource(ITYPE_STATUS_BAR));
+ assertNull(navBar.mAboveInsetsState.peekSource(ITYPE_STATUS_BAR));
+
+ verify(app, atLeastOnce()).notifyInsetsChanged();
+ }
+
+ @Test
+ public void testUpdateAboveInsetsState_receiveInsets() {
+ final WindowState app = createTestWindow("app");
+ final WindowState statusBar = createTestWindow("statusBar");
+ final WindowState navBar = createTestWindow("navBar");
+
+ getController().getSourceProvider(ITYPE_STATUS_BAR).setWindow(statusBar, null, null);
+ getController().getSourceProvider(ITYPE_NAVIGATION_BAR).setWindow(navBar, null, null);
+
+ assertNull(app.mAboveInsetsState.peekSource(ITYPE_STATUS_BAR));
+ assertNull(app.mAboveInsetsState.peekSource(ITYPE_NAVIGATION_BAR));
+
+ getController().updateAboveInsetsState(app, true /* notifyInsetsChange */);
+
+ assertNotNull(app.mAboveInsetsState.peekSource(ITYPE_STATUS_BAR));
+ assertNotNull(app.mAboveInsetsState.peekSource(ITYPE_NAVIGATION_BAR));
+
+ verify(app, atLeastOnce()).notifyInsetsChanged();
+ }
+
+ @Test
+ public void testUpdateAboveInsetsState_zOrderChanged() {
+ final WindowState ime = createTestWindow("ime");
+ final WindowState app = createTestWindow("app");
+ final WindowState statusBar = createTestWindow("statusBar");
+ final WindowState navBar = createTestWindow("navBar");
+
+ getController().getSourceProvider(ITYPE_IME).setWindow(ime, null, null);
+ getController().getSourceProvider(ITYPE_IME).setClientVisible(true);
+ getController().getSourceProvider(ITYPE_STATUS_BAR).setWindow(statusBar, null, null);
+ getController().getSourceProvider(ITYPE_NAVIGATION_BAR).setWindow(navBar, null, null);
+ getController().updateAboveInsetsState(ime, false /* notifyInsetsChange */);
+ getController().updateAboveInsetsState(statusBar, false /* notifyInsetsChange */);
+ getController().updateAboveInsetsState(navBar, false /* notifyInsetsChange */);
+
+ // ime is below others.
+ assertNull(app.mAboveInsetsState.peekSource(ITYPE_IME));
+ assertNull(statusBar.mAboveInsetsState.peekSource(ITYPE_IME));
+ assertNull(navBar.mAboveInsetsState.peekSource(ITYPE_IME));
+ assertNotNull(ime.mAboveInsetsState.peekSource(ITYPE_STATUS_BAR));
+ assertNotNull(ime.mAboveInsetsState.peekSource(ITYPE_NAVIGATION_BAR));
+
+ ime.getParent().positionChildAt(POSITION_TOP, ime, true /* includingParents */);
+ getController().updateAboveInsetsState(ime, true /* notifyInsetsChange */);
+
+ // ime is above others.
+ assertNotNull(app.mAboveInsetsState.peekSource(ITYPE_IME));
+ assertNotNull(statusBar.mAboveInsetsState.peekSource(ITYPE_IME));
+ assertNotNull(navBar.mAboveInsetsState.peekSource(ITYPE_IME));
+ assertNull(ime.mAboveInsetsState.peekSource(ITYPE_STATUS_BAR));
+ assertNull(ime.mAboveInsetsState.peekSource(ITYPE_NAVIGATION_BAR));
+
+ verify(ime, atLeastOnce()).notifyInsetsChanged();
+ verify(app, atLeastOnce()).notifyInsetsChanged();
+ verify(statusBar, atLeastOnce()).notifyInsetsChanged();
+ verify(navBar, atLeastOnce()).notifyInsetsChanged();
+ }
+
+ private WindowState createTestWindow(String name) {
+ final WindowState win = createWindow(null, TYPE_APPLICATION, name);
+ win.setHasSurface(true);
+ spyOn(win);
+ return win;
+ }
+
private InsetsStateController getController() {
return mDisplayContent.getInsetsStateController();
}