Move visible insets calculation to client
As otherwise they may be out of sync, leading to shifting when the
IME disappears.
Bug: 111084606
Test: InsetsSourceTest, InsetsStateTest, InsetsSourceProviderTest
Change-Id: Ifd7dfa6694efccf8693fd46bec1a9dea879790ff
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index 0207abd..775490c 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -42,6 +42,7 @@
import android.view.WindowInsets.Type.InsetsType;
import android.view.WindowInsetsAnimationCallback.AnimationBounds;
import android.view.WindowInsetsAnimationCallback.InsetsAnimation;
+import android.view.WindowManager.LayoutParams.SoftInputModeFlags;
import android.view.animation.Interpolator;
import android.view.animation.PathInterpolator;
@@ -299,6 +300,14 @@
}
/**
+ * @see InsetsState#calculateVisibleInsets(Rect, Rect, int)
+ */
+ public Rect calculateVisibleInsets(Rect legacyVisibleInsets,
+ @SoftInputModeFlags int softInputMode) {
+ return mState.calculateVisibleInsets(mFrame, legacyVisibleInsets, softInputMode);
+ }
+
+ /**
* Called when the server has dispatched us a new set of inset controls.
*/
public void onControlsChanged(InsetsSourceControl[] activeControls) {
diff --git a/core/java/android/view/InsetsSource.java b/core/java/android/view/InsetsSource.java
index 324d562..67ccfd6 100644
--- a/core/java/android/view/InsetsSource.java
+++ b/core/java/android/view/InsetsSource.java
@@ -16,6 +16,7 @@
package android.view;
+import android.annotation.Nullable;
import android.graphics.Insets;
import android.graphics.Rect;
import android.os.Parcel;
@@ -23,6 +24,7 @@
import android.view.InsetsState.InternalInsetsType;
import java.io.PrintWriter;
+import java.util.Objects;
/**
* Represents the state of a single window generating insets for clients.
@@ -34,6 +36,7 @@
/** Frame of the source in screen coordinate space */
private final Rect mFrame;
+ private @Nullable Rect mVisibleFrame;
private boolean mVisible;
private final Rect mTmpFrame = new Rect();
@@ -54,6 +57,10 @@
mFrame.set(frame);
}
+ public void setVisibleFrame(@Nullable Rect visibleFrame) {
+ mVisibleFrame = visibleFrame != null ? new Rect(visibleFrame) : visibleFrame;
+ }
+
public void setVisible(boolean visible) {
mVisible = visible;
}
@@ -66,6 +73,10 @@
return mFrame;
}
+ public @Nullable Rect getVisibleFrame() {
+ return mVisibleFrame;
+ }
+
public boolean isVisible() {
return mVisible;
}
@@ -79,10 +90,22 @@
* source.
*/
public Insets calculateInsets(Rect relativeFrame, boolean ignoreVisibility) {
+ return calculateInsets(relativeFrame, mFrame, ignoreVisibility);
+ }
+
+ /**
+ * Like {@link #calculateInsets(Rect, boolean)}, but will return visible insets.
+ */
+ public Insets calculateVisibleInsets(Rect relativeFrame) {
+ return calculateInsets(relativeFrame, mVisibleFrame != null ? mVisibleFrame : mFrame,
+ false /* ignoreVisibility */);
+ }
+
+ private Insets calculateInsets(Rect relativeFrame, Rect frame, boolean ignoreVisibility) {
if (!ignoreVisibility && !mVisible) {
return Insets.NONE;
}
- if (!mTmpFrame.setIntersect(mFrame, relativeFrame)) {
+ if (!mTmpFrame.setIntersect(frame, relativeFrame)) {
return Insets.NONE;
}
@@ -110,6 +133,9 @@
pw.print(prefix);
pw.print("InsetsSource type="); pw.print(InsetsState.typeToString(mType));
pw.print(" frame="); pw.print(mFrame.toShortString());
+ if (mVisibleFrame != null) {
+ pw.print(" visibleFrmae="); pw.print(mVisibleFrame.toShortString());
+ }
pw.print(" visible="); pw.print(mVisible);
pw.println();
}
@@ -123,6 +149,7 @@
if (mType != that.mType) return false;
if (mVisible != that.mVisible) return false;
+ if (!Objects.equals(mVisibleFrame, that.mVisibleFrame)) return false;
return mFrame.equals(that.mFrame);
}
@@ -137,6 +164,7 @@
public InsetsSource(Parcel in) {
mType = in.readInt();
mFrame = in.readParcelable(null /* loader */);
+ mVisibleFrame = in.readParcelable(null /* loader */);
mVisible = in.readBoolean();
}
@@ -149,6 +177,7 @@
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(mType);
dest.writeParcelable(mFrame, 0 /* flags*/);
+ dest.writeParcelable(mVisibleFrame, 0 /* flags */);
dest.writeBoolean(mVisible);
}
diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java
index ae1e579..e33ca70 100644
--- a/core/java/android/view/InsetsState.java
+++ b/core/java/android/view/InsetsState.java
@@ -19,12 +19,14 @@
import static android.view.ViewRootImpl.NEW_INSETS_MODE_FULL;
import static android.view.ViewRootImpl.NEW_INSETS_MODE_IME;
import static android.view.ViewRootImpl.NEW_INSETS_MODE_NONE;
+import static android.view.ViewRootImpl.sNewInsetsMode;
import static android.view.WindowInsets.Type.IME;
import static android.view.WindowInsets.Type.MANDATORY_SYSTEM_GESTURES;
import static android.view.WindowInsets.Type.SIZE;
import static android.view.WindowInsets.Type.SYSTEM_GESTURES;
import static android.view.WindowInsets.Type.ime;
import static android.view.WindowInsets.Type.indexOf;
+import static android.view.WindowInsets.Type.isVisibleInsetsType;
import static android.view.WindowInsets.Type.systemBars;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST;
@@ -41,6 +43,7 @@
import android.view.WindowInsets.Type;
import android.view.WindowInsets.Type.InsetsType;
import android.view.WindowManager.LayoutParams;
+import android.view.WindowManager.LayoutParams.SoftInputModeFlags;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
@@ -186,6 +189,32 @@
: systemBars());
}
+ public Rect calculateVisibleInsets(Rect frame, Rect legacyVisibleInsets,
+ @SoftInputModeFlags int softInputMode) {
+ if (sNewInsetsMode == NEW_INSETS_MODE_NONE) {
+ return legacyVisibleInsets;
+ }
+
+ Insets insets = Insets.NONE;
+ for (int type = FIRST_TYPE; type <= LAST_TYPE; type++) {
+ InsetsSource source = mSources.get(type);
+ if (source == null) {
+ continue;
+ }
+ if (sNewInsetsMode != NEW_INSETS_MODE_FULL && type != ITYPE_IME) {
+ continue;
+ }
+
+ // Ignore everything that's not a system bar or IME.
+ int publicType = InsetsState.toPublicType(type);
+ if (!isVisibleInsetsType(publicType, softInputMode)) {
+ continue;
+ }
+ insets = Insets.max(source.calculateVisibleInsets(frame), insets);
+ }
+ return insets.toRect();
+ }
+
private void processSource(InsetsSource source, Rect relativeFrame, boolean ignoreVisibility,
Insets[] typeInsetsMap, @Nullable @InternalInsetsSide SparseIntArray typeSideMap,
@Nullable boolean[] typeVisibilityMap) {
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index ab89ef4..ca8ba4c 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -2103,6 +2103,12 @@
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
+ private void updateVisibleInsets() {
+ Rect visibleInsets = mInsetsController.calculateVisibleInsets(mPendingVisibleInsets,
+ mWindowAttributes.softInputMode);
+ mAttachInfo.mVisibleInsets.set(visibleInsets);
+ }
+
InsetsController getInsetsController() {
return mInsetsController;
}
@@ -2251,7 +2257,7 @@
insetsChanged = true;
}
if (!mPendingVisibleInsets.equals(mAttachInfo.mVisibleInsets)) {
- mAttachInfo.mVisibleInsets.set(mPendingVisibleInsets);
+ updateVisibleInsets();
if (DEBUG_LAYOUT) Log.v(mTag, "Visible insets changing to: "
+ mAttachInfo.mVisibleInsets);
}
@@ -2317,6 +2323,7 @@
if (mApplyInsetsRequested) {
mApplyInsetsRequested = false;
+ updateVisibleInsets();
dispatchApplyInsets(host);
if (mLayoutRequested) {
// Short-circuit catching a new layout request here, so
@@ -2501,7 +2508,7 @@
contentInsetsChanged = true;
}
if (visibleInsetsChanged) {
- mAttachInfo.mVisibleInsets.set(mPendingVisibleInsets);
+ updateVisibleInsets();
if (DEBUG_LAYOUT) Log.v(mTag, "Visible insets changing to: "
+ mAttachInfo.mVisibleInsets);
}
diff --git a/core/java/android/view/WindowInsets.java b/core/java/android/view/WindowInsets.java
index 9df131d..9291b56 100644
--- a/core/java/android/view/WindowInsets.java
+++ b/core/java/android/view/WindowInsets.java
@@ -29,6 +29,8 @@
import static android.view.WindowInsets.Type.all;
import static android.view.WindowInsets.Type.indexOf;
import static android.view.WindowInsets.Type.systemBars;
+import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING;
+import static android.view.WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST;
import android.annotation.IntDef;
import android.annotation.IntRange;
@@ -40,6 +42,7 @@
import android.graphics.Rect;
import android.util.SparseArray;
import android.view.WindowInsets.Type.InsetsType;
+import android.view.WindowManager.LayoutParams.SoftInputModeFlags;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethod;
@@ -1289,6 +1292,17 @@
public static @InsetsType int all() {
return 0xFFFFFFFF;
}
+
+ /**
+ * Checks whether the specified type is considered to be part of visible insets.
+ * @hide
+ */
+ public static boolean isVisibleInsetsType(int type,
+ @SoftInputModeFlags int softInputModeFlags) {
+ int softInputMode = softInputModeFlags & SOFT_INPUT_MASK_ADJUST;
+ return (type & Type.systemBars()) != 0
+ || (softInputMode != SOFT_INPUT_ADJUST_NOTHING && (type & Type.ime()) != 0);
+ }
}
/**
diff --git a/core/tests/coretests/src/android/view/InsetsSourceTest.java b/core/tests/coretests/src/android/view/InsetsSourceTest.java
index d7f50ba..e3b08bb 100644
--- a/core/tests/coretests/src/android/view/InsetsSourceTest.java
+++ b/core/tests/coretests/src/android/view/InsetsSourceTest.java
@@ -108,5 +108,30 @@
assertEquals(Insets.of(0, 100, 0, 0), insets);
}
+ @Test
+ public void testCalculateVisibleInsets_default() {
+ mSource.setFrame(new Rect(0, 0, 500, 100));
+ Insets insets = mSource.calculateVisibleInsets(new Rect(100, 0, 500, 500));
+ assertEquals(Insets.of(0, 100, 0, 0), insets);
+ }
+
+ @Test
+ public void testCalculateVisibleInsets_override() {
+ mSource.setFrame(new Rect(0, 0, 500, 100));
+ mSource.setVisibleFrame(new Rect(0, 0, 500, 200));
+ Insets insets = mSource.calculateVisibleInsets(new Rect(100, 0, 500, 500));
+ assertEquals(Insets.of(0, 200, 0, 0), insets);
+ }
+
+ @Test
+ public void testCalculateVisibleInsets_invisible() {
+ mSource.setFrame(new Rect(0, 0, 500, 100));
+ mSource.setVisibleFrame(new Rect(0, 0, 500, 200));
+ mSource.setVisible(false);
+ Insets insets = mSource.calculateVisibleInsets(new Rect(100, 0, 500, 500));
+ assertEquals(Insets.of(0, 0, 0, 0), insets);
+ }
+
+
// Parcel and equals already tested via InsetsStateTest
}
diff --git a/core/tests/coretests/src/android/view/InsetsStateTest.java b/core/tests/coretests/src/android/view/InsetsStateTest.java
index fa2ffcca..4b76fee 100644
--- a/core/tests/coretests/src/android/view/InsetsStateTest.java
+++ b/core/tests/coretests/src/android/view/InsetsStateTest.java
@@ -18,12 +18,14 @@
import static android.view.InsetsState.ISIDE_BOTTOM;
import static android.view.InsetsState.ISIDE_TOP;
+import static android.view.InsetsState.ITYPE_BOTTOM_GESTURES;
import static android.view.InsetsState.ITYPE_CAPTION_BAR;
import static android.view.InsetsState.ITYPE_IME;
import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
import static android.view.InsetsState.ITYPE_STATUS_BAR;
import static android.view.WindowInsets.Type.ime;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING;
+import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
import static org.junit.Assert.assertEquals;
@@ -209,6 +211,42 @@
assertFalse(InsetsState.getDefaultVisibility(ITYPE_IME));
}
+ @Test
+ public void testCalculateVisibleInsets() throws Exception {
+ try (final InsetsModeSession session =
+ new InsetsModeSession(ViewRootImpl.NEW_INSETS_MODE_FULL)) {
+ mState.getSource(ITYPE_STATUS_BAR).setFrame(new Rect(0, 0, 100, 100));
+ mState.getSource(ITYPE_STATUS_BAR).setVisible(true);
+ mState.getSource(ITYPE_IME).setFrame(new Rect(0, 200, 100, 300));
+ mState.getSource(ITYPE_IME).setVisible(true);
+
+ // Make sure bottom gestures are ignored
+ mState.getSource(ITYPE_BOTTOM_GESTURES).setFrame(new Rect(0, 100, 100, 300));
+ mState.getSource(ITYPE_BOTTOM_GESTURES).setVisible(true);
+ Rect visibleInsets = mState.calculateVisibleInsets(
+ new Rect(0, 0, 100, 300), new Rect(), SOFT_INPUT_ADJUST_PAN);
+ assertEquals(new Rect(0, 100, 0, 100), visibleInsets);
+ }
+ }
+
+ @Test
+ public void testCalculateVisibleInsets_adjustNothing() throws Exception {
+ try (final InsetsModeSession session =
+ new InsetsModeSession(ViewRootImpl.NEW_INSETS_MODE_FULL)) {
+ mState.getSource(ITYPE_STATUS_BAR).setFrame(new Rect(0, 0, 100, 100));
+ mState.getSource(ITYPE_STATUS_BAR).setVisible(true);
+ mState.getSource(ITYPE_IME).setFrame(new Rect(0, 200, 100, 300));
+ mState.getSource(ITYPE_IME).setVisible(true);
+
+ // Make sure bottom gestures are ignored
+ mState.getSource(ITYPE_BOTTOM_GESTURES).setFrame(new Rect(0, 100, 100, 300));
+ mState.getSource(ITYPE_BOTTOM_GESTURES).setVisible(true);
+ Rect visibleInsets = mState.calculateVisibleInsets(
+ new Rect(0, 0, 100, 300), new Rect(), SOFT_INPUT_ADJUST_NOTHING);
+ assertEquals(new Rect(0, 100, 0, 0), visibleInsets);
+ }
+ }
+
private void assertEqualsAndHashCode() {
assertEquals(mState, mState2);
assertEquals(mState.hashCode(), mState2.hashCode());
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index f9ad03f..9c62e99 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -2061,7 +2061,8 @@
cf.set(displayFrames.mRestricted);
}
applyStableConstraints(sysUiFl, fl, cf, displayFrames);
- if (adjust != SOFT_INPUT_ADJUST_NOTHING) {
+ if (ViewRootImpl.sNewInsetsMode == NEW_INSETS_MODE_NONE
+ && adjust != SOFT_INPUT_ADJUST_NOTHING) {
vf.set(displayFrames.mCurrent);
} else {
vf.set(cf);
@@ -2138,7 +2139,8 @@
applyStableConstraints(sysUiFl, fl, cf, displayFrames);
- if (adjust != SOFT_INPUT_ADJUST_NOTHING) {
+ if (ViewRootImpl.sNewInsetsMode == NEW_INSETS_MODE_NONE
+ && adjust != SOFT_INPUT_ADJUST_NOTHING) {
vf.set(displayFrames.mCurrent);
} else {
vf.set(cf);
@@ -2179,7 +2181,8 @@
cf.set(displayFrames.mContent);
df.set(displayFrames.mContent);
}
- if (adjust != SOFT_INPUT_ADJUST_NOTHING) {
+ if (ViewRootImpl.sNewInsetsMode == NEW_INSETS_MODE_NONE
+ && adjust != SOFT_INPUT_ADJUST_NOTHING) {
vf.set(displayFrames.mCurrent);
} else {
vf.set(cf);
diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
index a13383d..5a591ec 100644
--- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
@@ -129,6 +129,7 @@
if (win == null) {
setServerVisible(false);
mSource.setFrame(new Rect());
+ mSource.setVisibleFrame(null);
} else if (mControllable) {
mWin.setControllableInsetProvider(this);
if (mControlTarget != null) {
@@ -160,6 +161,15 @@
mTmpRect.inset(mWin.mGivenContentInsets);
}
mSource.setFrame(mTmpRect);
+
+ if (mWin.mGivenVisibleInsets.left != 0 || mWin.mGivenVisibleInsets.top != 0
+ || mWin.mGivenVisibleInsets.right != 0 || mWin.mGivenVisibleInsets.bottom != 0) {
+ mTmpRect.set(mWin.getFrameLw());
+ mTmpRect.inset(mWin.mGivenVisibleInsets);
+ mSource.setVisibleFrame(mTmpRect);
+ } else {
+ mSource.setVisibleFrame(null);
+ }
}
/**
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java
index 09ac9ce..d819b1a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java
@@ -63,6 +63,26 @@
assertEquals(Insets.of(0, 100, 0, 0),
mProvider.getSource().calculateInsets(new Rect(0, 0, 500, 500),
false /* ignoreVisibility */));
+ assertEquals(Insets.of(0, 100, 0, 0),
+ mProvider.getSource().calculateVisibleInsets(new Rect(0, 0, 500, 500)));
+ }
+
+ @Test
+ public void testPostLayout_givenInsets() {
+ final WindowState ime = createWindow(null, TYPE_APPLICATION, "ime");
+ ime.getFrameLw().set(0, 0, 500, 100);
+ ime.getGivenContentInsetsLw().set(0, 0, 0, 60);
+ ime.getGivenVisibleInsetsLw().set(0, 0, 0, 75);
+ ime.mHasSurface = true;
+ mProvider.setWindow(ime, null);
+ mProvider.onPostLayout();
+ assertEquals(new Rect(0, 0, 500, 40), mProvider.getSource().getFrame());
+ assertEquals(new Rect(0, 0, 500, 25), mProvider.getSource().getVisibleFrame());
+ assertEquals(Insets.of(0, 40, 0, 0),
+ mProvider.getSource().calculateInsets(new Rect(0, 0, 500, 500),
+ false /* ignoreVisibility */));
+ assertEquals(Insets.of(0, 25, 0, 0),
+ mProvider.getSource().calculateVisibleInsets(new Rect(0, 0, 500, 500)));
}
@Test