Merge "Only update mAboveInsetsState when necessary" into sc-dev am: 657ec55928

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/13482537

MUST ONLY BE SUBMITTED BY AUTOMERGER

Change-Id: I14bce177d5ec4995578423188225bbad00ed3cec
This commit is contained in:
Tiger Huang
2021-02-22 17:34:52 +00:00
committed by Automerger Merge Worker
11 changed files with 243 additions and 74 deletions

View File

@@ -20,8 +20,6 @@ import static android.view.InsetsStateProto.DISPLAY_CUTOUT;
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 class InsetsState implements Parcelable {
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 class InsetsState implements Parcelable {
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;
@@ -187,18 +181,6 @@ public class InsetsState implements Parcelable {
set(copy, copySources);
}
/**
* 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.
*
@@ -380,14 +362,14 @@ public class InsetsState implements Parcelable {
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 @@ public class InsetsState implements Parcelable {
* 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 @@ public class InsetsState implements Parcelable {
}
}
/**
* 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 @@ public class InsetsState implements Parcelable {
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);

View File

@@ -335,7 +335,7 @@ public class RoundedCorners implements Parcelable {
}
if (o instanceof RoundedCorners) {
RoundedCorners r = (RoundedCorners) o;
return Arrays.deepEquals(mRoundedCorners, ((RoundedCorners) o).mRoundedCorners);
return Arrays.deepEquals(mRoundedCorners, r.mRoundedCorners);
}
return false;
}

View File

@@ -673,10 +673,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
// 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 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
+ " 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 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
+ " 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 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
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 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
// 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 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
+ " 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;

View File

@@ -21,6 +21,7 @@ import static android.view.InsetsState.ITYPE_LEFT_DISPLAY_CUTOUT;
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 @@ public class DisplayFrames {
* @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 @@ public class DisplayFrames {
state.removeSource(ITYPE_RIGHT_DISPLAY_CUTOUT);
state.removeSource(ITYPE_BOTTOM_DISPLAY_CUTOUT);
}
return true;
}
public void dumpDebug(ProtoOutputStream proto, long fieldId) {

View File

@@ -27,6 +27,9 @@ import static android.view.InsetsState.ITYPE_IME;
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 @@ class InsetsStateController {
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--) {

View File

@@ -1881,6 +1881,10 @@ public class WindowManagerService extends IWindowManager.Stub
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);
}

View File

@@ -211,11 +211,11 @@ import android.os.Trace;
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;
@@ -651,12 +651,12 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
/**
* 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
@@ -2202,7 +2202,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
}
}
@Override
@Override
void removeImmediately() {
super.removeImmediately();

View File

@@ -130,7 +130,7 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase {
// 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) {

View File

@@ -18,6 +18,7 @@ package com.android.server.wm;
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 android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
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 @@ public class DisplayPolicyTests extends WindowTestsBase {
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);

View File

@@ -117,7 +117,7 @@ public class DisplayPolicyTestsBase extends WindowTestsBase {
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;

View File

@@ -32,13 +32,14 @@ import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
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 @@ public class InsetsStateControllerTest extends WindowTestsBase {
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 @@ public class InsetsStateControllerTest extends WindowTestsBase {
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();
}