Merge "Changes in PopupWindow API"
diff --git a/core/java/android/view/InsetsAnimationControlImpl.java b/core/java/android/view/InsetsAnimationControlImpl.java
index 583651d..dd88e3c 100644
--- a/core/java/android/view/InsetsAnimationControlImpl.java
+++ b/core/java/android/view/InsetsAnimationControlImpl.java
@@ -33,6 +33,7 @@
import android.view.InsetsState.InsetSide;
import android.view.SyncRtSurfaceTransactionApplier.SurfaceParams;
import android.view.WindowInsets.Type.InsetType;
+import android.view.WindowManager.LayoutParams;
import com.android.internal.annotations.VisibleForTesting;
@@ -165,7 +166,8 @@
@Nullable @InsetSide SparseIntArray typeSideMap) {
return state.calculateInsets(frame, false /* isScreenRound */,
false /* alwaysConsumerNavBar */, null /* displayCutout */,
- null /* legacyContentInsets */, null /* legacyStableInsets */, typeSideMap)
+ null /* legacyContentInsets */, null /* legacyStableInsets */,
+ LayoutParams.SOFT_INPUT_ADJUST_RESIZE /* legacySoftInputMode*/, typeSideMap)
.getInsets(mTypes);
}
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index 7ad97a6..2586000 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -112,6 +112,8 @@
private int mPendingTypesToShow;
+ private int mLastLegacySoftInputMode;
+
public InsetsController(ViewRootImpl viewRoot) {
mViewRoot = viewRoot;
mAnimCallback = () -> {
@@ -126,13 +128,17 @@
}
WindowInsets insets = state.calculateInsets(mFrame, mLastInsets.isRound(),
mLastInsets.shouldAlwaysConsumeNavBar(), mLastInsets.getDisplayCutout(),
- mLastLegacyContentInsets, mLastLegacyStableInsets,
+ mLastLegacyContentInsets, mLastLegacyStableInsets, mLastLegacySoftInputMode,
null /* typeSideMap */);
mViewRoot.mView.dispatchWindowInsetsAnimationProgress(insets);
};
}
void onFrameChanged(Rect frame) {
+ if (mFrame.equals(frame)) {
+ return;
+ }
+ mViewRoot.notifyInsetsChanged();
mFrame.set(frame);
}
@@ -160,11 +166,12 @@
@VisibleForTesting
public WindowInsets calculateInsets(boolean isScreenRound,
boolean alwaysConsumeNavBar, DisplayCutout cutout, Rect legacyContentInsets,
- Rect legacyStableInsets) {
+ Rect legacyStableInsets, int legacySoftInputMode) {
mLastLegacyContentInsets.set(legacyContentInsets);
mLastLegacyStableInsets.set(legacyStableInsets);
+ mLastLegacySoftInputMode = legacySoftInputMode;
mLastInsets = mState.calculateInsets(mFrame, isScreenRound, alwaysConsumeNavBar, cutout,
- legacyContentInsets, legacyStableInsets,
+ legacyContentInsets, legacyStableInsets, legacySoftInputMode,
null /* typeSideMap */);
return mLastInsets;
}
@@ -257,11 +264,21 @@
private void controlWindowInsetsAnimation(@InsetType int types,
WindowInsetsAnimationControlListener listener, boolean fromIme) {
+ // If the frame of our window doesn't span the entire display, the control API makes very
+ // little sense, as we don't deal with negative insets. So just cancel immediately.
+ if (!mState.getDisplayFrame().equals(mFrame)) {
+ listener.onCancelled();
+ return;
+ }
+ controlAnimationUnchecked(types, listener, mFrame, fromIme);
+ }
+
+ private void controlAnimationUnchecked(@InsetType int types,
+ WindowInsetsAnimationControlListener listener, Rect frame, boolean fromIme) {
if (types == 0) {
// nothing to animate.
return;
}
-
// TODO: Check whether we already have a controller.
final ArraySet<Integer> internalTypes = mState.toInternalType(types);
final SparseArray<InsetsSourceConsumer> consumers = new SparseArray<>();
@@ -285,7 +302,7 @@
}
final InsetsAnimationControlImpl controller = new InsetsAnimationControlImpl(consumers,
- mFrame, mState, listener, typesReady,
+ frame, mState, listener, typesReady,
() -> new SyncRtSurfaceTransactionApplier(mViewRoot.mView), this);
mAnimationControls.add(controller);
}
@@ -436,6 +453,7 @@
// nothing to animate.
return;
}
+
WindowInsetsAnimationControlListener listener = new WindowInsetsAnimationControlListener() {
@Override
public void onReady(WindowInsetsAnimationController controller, int types) {
@@ -479,7 +497,10 @@
// TODO: Instead of clearing this here, properly wire up
// InsetsAnimationControlImpl.finish() to remove this from mAnimationControls.
mAnimationControls.clear();
- controlWindowInsetsAnimation(types, listener, fromIme);
+
+ // Show/hide animations always need to be relative to the display frame, in order that shown
+ // and hidden state insets are correct.
+ controlAnimationUnchecked(types, listener, mState.getDisplayFrame(), fromIme);
}
private void hideDirectly(@InsetType int types) {
diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java
index 4f809fe6..69f86aa 100644
--- a/core/java/android/view/InsetsState.java
+++ b/core/java/android/view/InsetsState.java
@@ -19,6 +19,7 @@
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.WindowInsets.Type.IME;
import static android.view.WindowInsets.Type.SIZE;
import static android.view.WindowInsets.Type.indexOf;
@@ -34,11 +35,13 @@
import android.util.SparseIntArray;
import android.view.WindowInsets.Type;
import android.view.WindowInsets.Type.InsetType;
+import android.view.WindowManager.LayoutParams;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
+import java.util.Objects;
/**
* Holder for state of system windows that cause window insets for all other windows in the system.
@@ -104,6 +107,11 @@
private final ArrayMap<Integer, InsetsSource> mSources = new ArrayMap<>();
+ /**
+ * The frame of the display these sources are relative to.
+ */
+ private final Rect mDisplayFrame = new Rect();
+
public InsetsState() {
}
@@ -124,7 +132,7 @@
public WindowInsets calculateInsets(Rect frame, boolean isScreenRound,
boolean alwaysConsumeNavBar, DisplayCutout cutout,
@Nullable Rect legacyContentInsets, @Nullable Rect legacyStableInsets,
- @Nullable @InsetSide SparseIntArray typeSideMap) {
+ int legacySoftInputMode, @Nullable @InsetSide SparseIntArray typeSideMap) {
Insets[] typeInsetsMap = new Insets[Type.SIZE];
Insets[] typeMaxInsetsMap = new Insets[Type.SIZE];
boolean[] typeVisibilityMap = new boolean[SIZE];
@@ -140,8 +148,12 @@
if (source == null) {
continue;
}
- if (ViewRootImpl.sNewInsetsMode != NEW_INSETS_MODE_FULL
- && (type == TYPE_TOP_BAR || type == TYPE_NAVIGATION_BAR)) {
+
+ boolean skipSystemBars = ViewRootImpl.sNewInsetsMode != NEW_INSETS_MODE_FULL
+ && (type == TYPE_TOP_BAR || type == TYPE_NAVIGATION_BAR);
+ boolean skipIme = source.getType() == TYPE_IME
+ && (legacySoftInputMode & LayoutParams.SOFT_INPUT_ADJUST_RESIZE) == 0;
+ if (skipSystemBars || skipIme) {
typeVisibilityMap[indexOf(toPublicType(type))] = source.isVisible();
continue;
}
@@ -209,6 +221,14 @@
return mSources.computeIfAbsent(type, InsetsSource::new);
}
+ public void setDisplayFrame(Rect frame) {
+ mDisplayFrame.set(frame);
+ }
+
+ public Rect getDisplayFrame() {
+ return mDisplayFrame;
+ }
+
/**
* Modifies the state of this class to exclude a certain type to make it ready for dispatching
* to the client.
@@ -224,6 +244,7 @@
}
public void set(InsetsState other, boolean copySources) {
+ mDisplayFrame.set(other.mDisplayFrame);
mSources.clear();
if (copySources) {
for (int i = 0; i < other.mSources.size(); i++) {
@@ -323,6 +344,9 @@
InsetsState state = (InsetsState) o;
+ if (!mDisplayFrame.equals(state.mDisplayFrame)) {
+ return false;
+ }
if (mSources.size() != state.mSources.size()) {
return false;
}
@@ -341,7 +365,7 @@
@Override
public int hashCode() {
- return mSources.hashCode();
+ return Objects.hash(mDisplayFrame, mSources);
}
public InsetsState(Parcel in) {
@@ -355,9 +379,10 @@
@Override
public void writeToParcel(Parcel dest, int flags) {
+ dest.writeParcelable(mDisplayFrame, flags);
dest.writeInt(mSources.size());
for (int i = 0; i < mSources.size(); i++) {
- dest.writeParcelable(mSources.valueAt(i), 0 /* flags */);
+ dest.writeParcelable(mSources.valueAt(i), flags);
}
}
@@ -374,6 +399,7 @@
public void readFromParcel(Parcel in) {
mSources.clear();
+ mDisplayFrame.set(in.readParcelable(null /* loader */));
final int size = in.readInt();
for (int i = 0; i < size; i++) {
final InsetsSource source = in.readParcelable(null /* loader */);
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 156972f..1a782ee 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -1887,7 +1887,7 @@
mLastWindowInsets = mInsetsController.calculateInsets(
mContext.getResources().getConfiguration().isScreenRound(),
mAttachInfo.mAlwaysConsumeNavBar, displayCutout,
- contentInsets, stableInsets);
+ contentInsets, stableInsets, mWindowAttributes.softInputMode);
} else {
mLastWindowInsets = new WindowInsets(contentInsets, stableInsets,
mContext.getResources().getConfiguration().isScreenRound(),
diff --git a/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java b/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java
index da81d17..80b1f9c 100644
--- a/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java
+++ b/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java
@@ -19,6 +19,7 @@
import static android.view.ImeInsetsSourceConsumer.areEditorsSimilar;
import static android.view.InsetsState.TYPE_IME;
+import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
@@ -73,7 +74,7 @@
false,
new DisplayCutout(
Insets.of(10, 10, 10, 10), rect, rect, rect, rect),
- rect, rect);
+ rect, rect, SOFT_INPUT_ADJUST_RESIZE);
mImeConsumer = new ImeInsetsSourceConsumer(
new InsetsState(), Transaction::new, mController);
});
diff --git a/core/tests/coretests/src/android/view/InsetsControllerTest.java b/core/tests/coretests/src/android/view/InsetsControllerTest.java
index 6dad6a2..731d564 100644
--- a/core/tests/coretests/src/android/view/InsetsControllerTest.java
+++ b/core/tests/coretests/src/android/view/InsetsControllerTest.java
@@ -20,10 +20,16 @@
import static android.view.InsetsState.TYPE_NAVIGATION_BAR;
import static android.view.InsetsState.TYPE_TOP_BAR;
+import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertNull;
import static junit.framework.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
import android.content.Context;
import android.graphics.Insets;
@@ -73,7 +79,7 @@
false,
new DisplayCutout(
Insets.of(10, 10, 10, 10), rect, rect, rect, rect),
- rect, rect);
+ rect, rect, SOFT_INPUT_ADJUST_RESIZE);
});
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
}
@@ -95,6 +101,17 @@
}
@Test
+ public void testFrameDoesntMatchDisplay() {
+ mController.onFrameChanged(new Rect(0, 0, 100, 100));
+ mController.getState().setDisplayFrame(new Rect(0, 0, 200, 200));
+ WindowInsetsAnimationControlListener controlListener =
+ mock(WindowInsetsAnimationControlListener.class);
+ mController.controlWindowInsetsAnimation(0, controlListener);
+ verify(controlListener).onCancelled();
+ verify(controlListener, never()).onReady(any(), anyInt());
+ }
+
+ @Test
public void testAnimationEndState() {
InsetsSourceControl[] controls = prepareControls();
InsetsSourceControl navBar = controls[0];
diff --git a/core/tests/coretests/src/android/view/InsetsStateTest.java b/core/tests/coretests/src/android/view/InsetsStateTest.java
index 03af67d..bd036b0 100644
--- a/core/tests/coretests/src/android/view/InsetsStateTest.java
+++ b/core/tests/coretests/src/android/view/InsetsStateTest.java
@@ -25,6 +25,8 @@
import static android.view.InsetsState.TYPE_SIDE_BAR_3;
import static android.view.InsetsState.TYPE_TOP_BAR;
+import static android.view.WindowInsets.Type.ime;
+import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
@@ -37,6 +39,7 @@
import android.platform.test.annotations.Presubmit;
import android.util.SparseIntArray;
import android.view.WindowInsets.Type;
+import android.view.test.InsetsModeSession;
import androidx.test.filters.FlakyTest;
import androidx.test.runner.AndroidJUnit4;
@@ -53,49 +56,70 @@
private InsetsState mState2 = new InsetsState();
@Test
- public void testCalculateInsets() {
+ public void testCalculateInsets() throws Exception {
+ try (final InsetsModeSession session =
+ new InsetsModeSession(ViewRootImpl.NEW_INSETS_MODE_FULL)) {
+ mState.getSource(TYPE_TOP_BAR).setFrame(new Rect(0, 0, 100, 100));
+ mState.getSource(TYPE_TOP_BAR).setVisible(true);
+ mState.getSource(TYPE_IME).setFrame(new Rect(0, 200, 100, 300));
+ mState.getSource(TYPE_IME).setVisible(true);
+ SparseIntArray typeSideMap = new SparseIntArray();
+ WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), false, false,
+ DisplayCutout.NO_CUTOUT, null, null, SOFT_INPUT_ADJUST_RESIZE, typeSideMap);
+ assertEquals(Insets.of(0, 100, 0, 100), insets.getSystemWindowInsets());
+ assertEquals(Insets.of(0, 100, 0, 100), insets.getInsets(Type.all()));
+ assertEquals(INSET_SIDE_TOP, typeSideMap.get(TYPE_TOP_BAR));
+ assertEquals(INSET_SIDE_BOTTOM, typeSideMap.get(TYPE_IME));
+ assertEquals(Insets.of(0, 100, 0, 0), insets.getInsets(Type.topBar()));
+ assertEquals(Insets.of(0, 0, 0, 100), insets.getInsets(Type.ime()));
+ }
+ }
+
+ @Test
+ public void testCalculateInsets_imeAndNav() throws Exception{
+ try (final InsetsModeSession session =
+ new InsetsModeSession(ViewRootImpl.NEW_INSETS_MODE_FULL)) {
+ mState.getSource(TYPE_NAVIGATION_BAR).setFrame(new Rect(0, 200, 100, 300));
+ mState.getSource(TYPE_NAVIGATION_BAR).setVisible(true);
+ mState.getSource(TYPE_IME).setFrame(new Rect(0, 100, 100, 300));
+ mState.getSource(TYPE_IME).setVisible(true);
+ WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), false, false,
+ DisplayCutout.NO_CUTOUT, null, null, SOFT_INPUT_ADJUST_RESIZE, null);
+ assertEquals(100, insets.getStableInsetBottom());
+ assertEquals(Insets.of(0, 0, 0, 100), insets.getMaxInsets(Type.systemBars()));
+ assertEquals(Insets.of(0, 0, 0, 200), insets.getSystemWindowInsets());
+ assertEquals(Insets.of(0, 0, 0, 200), insets.getInsets(Type.all()));
+ assertEquals(Insets.of(0, 0, 0, 100), insets.getInsets(Type.sideBars()));
+ assertEquals(Insets.of(0, 0, 0, 200), insets.getInsets(Type.ime()));
+ }
+ }
+
+ @Test
+ public void testCalculateInsets_navRightStatusTop() throws Exception {
+ try (final InsetsModeSession session =
+ new InsetsModeSession(ViewRootImpl.NEW_INSETS_MODE_FULL)) {
+ mState.getSource(TYPE_TOP_BAR).setFrame(new Rect(0, 0, 100, 100));
+ mState.getSource(TYPE_TOP_BAR).setVisible(true);
+ mState.getSource(TYPE_NAVIGATION_BAR).setFrame(new Rect(80, 0, 100, 300));
+ mState.getSource(TYPE_NAVIGATION_BAR).setVisible(true);
+ WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), false, false,
+ DisplayCutout.NO_CUTOUT, null, null, 0, null);
+ assertEquals(Insets.of(0, 100, 20, 0), insets.getSystemWindowInsets());
+ assertEquals(Insets.of(0, 100, 0, 0), insets.getInsets(Type.topBar()));
+ assertEquals(Insets.of(0, 0, 20, 0), insets.getInsets(Type.sideBars()));
+ }
+ }
+
+ @Test
+ public void testCalculateInsets_imeIgnoredWithoutAdjustResize() {
mState.getSource(TYPE_TOP_BAR).setFrame(new Rect(0, 0, 100, 100));
mState.getSource(TYPE_TOP_BAR).setVisible(true);
mState.getSource(TYPE_IME).setFrame(new Rect(0, 200, 100, 300));
mState.getSource(TYPE_IME).setVisible(true);
- SparseIntArray typeSideMap = new SparseIntArray();
WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), false, false,
- DisplayCutout.NO_CUTOUT, null, null, typeSideMap);
- assertEquals(Insets.of(0, 100, 0, 100), insets.getSystemWindowInsets());
- assertEquals(Insets.of(0, 100, 0, 100), insets.getInsets(Type.all()));
- assertEquals(INSET_SIDE_TOP, typeSideMap.get(TYPE_TOP_BAR));
- assertEquals(INSET_SIDE_BOTTOM, typeSideMap.get(TYPE_IME));
- assertEquals(Insets.of(0, 100, 0, 0), insets.getInsets(Type.topBar()));
- assertEquals(Insets.of(0, 0, 0, 100), insets.getInsets(Type.ime()));
- }
-
- @Test
- public void testCalculateInsets_imeAndNav() {
- mState.getSource(TYPE_NAVIGATION_BAR).setFrame(new Rect(0, 200, 100, 300));
- mState.getSource(TYPE_NAVIGATION_BAR).setVisible(true);
- mState.getSource(TYPE_IME).setFrame(new Rect(0, 100, 100, 300));
- mState.getSource(TYPE_IME).setVisible(true);
- WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), false, false,
- DisplayCutout.NO_CUTOUT, null, null, null);
- assertEquals(100, insets.getStableInsetBottom());
- assertEquals(Insets.of(0, 0, 0, 100), insets.getMaxInsets(Type.all()));
- assertEquals(Insets.of(0, 0, 0, 200), insets.getSystemWindowInsets());
- assertEquals(Insets.of(0, 0, 0, 200), insets.getInsets(Type.all()));
- assertEquals(Insets.of(0, 0, 0, 100), insets.getInsets(Type.sideBars()));
- assertEquals(Insets.of(0, 0, 0, 200), insets.getInsets(Type.ime()));
- }
-
- @Test
- public void testCalculateInsets_navRightStatusTop() {
- mState.getSource(TYPE_TOP_BAR).setFrame(new Rect(0, 0, 100, 100));
- mState.getSource(TYPE_TOP_BAR).setVisible(true);
- mState.getSource(TYPE_NAVIGATION_BAR).setFrame(new Rect(80, 0, 100, 300));
- mState.getSource(TYPE_NAVIGATION_BAR).setVisible(true);
- WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), false, false,
- DisplayCutout.NO_CUTOUT, null, null, null);
- assertEquals(Insets.of(0, 100, 20, 0), insets.getSystemWindowInsets());
- assertEquals(Insets.of(0, 100, 0, 0), insets.getInsets(Type.topBar()));
- assertEquals(Insets.of(0, 0, 20, 0), insets.getInsets(Type.sideBars()));
+ DisplayCutout.NO_CUTOUT, null, null, 0, null);
+ assertEquals(0, insets.getSystemWindowInsetBottom());
+ assertTrue(insets.isVisible(ime()));
}
@Test
@@ -106,7 +130,7 @@
mState.getSource(TYPE_IME).setVisible(true);
mState.removeSource(TYPE_IME);
WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), false, false,
- DisplayCutout.NO_CUTOUT, null, null, null);
+ DisplayCutout.NO_CUTOUT, null, null, SOFT_INPUT_ADJUST_RESIZE, null);
assertEquals(0, insets.getSystemWindowInsetBottom());
}
@@ -114,14 +138,14 @@
public void testEquals_differentRect() {
mState.getSource(TYPE_TOP_BAR).setFrame(new Rect(0, 0, 100, 100));
mState2.getSource(TYPE_TOP_BAR).setFrame(new Rect(0, 0, 10, 10));
- assertNotEquals(mState, mState2);
+ assertNotEqualsAndHashCode();
}
@Test
public void testEquals_differentSource() {
mState.getSource(TYPE_TOP_BAR).setFrame(new Rect(0, 0, 100, 100));
mState2.getSource(TYPE_IME).setFrame(new Rect(0, 0, 100, 100));
- assertNotEquals(mState, mState2);
+ assertNotEqualsAndHashCode();
}
@Test
@@ -130,7 +154,7 @@
mState.getSource(TYPE_IME).setFrame(new Rect(0, 0, 100, 100));
mState2.getSource(TYPE_IME).setFrame(new Rect(0, 0, 100, 100));
mState2.getSource(TYPE_TOP_BAR).setFrame(new Rect(0, 0, 100, 100));
- assertEquals(mState, mState2);
+ assertEqualsAndHashCode();
}
@Test
@@ -138,7 +162,21 @@
mState.getSource(TYPE_IME).setFrame(new Rect(0, 0, 100, 100));
mState.getSource(TYPE_IME).setVisible(true);
mState2.getSource(TYPE_IME).setFrame(new Rect(0, 0, 100, 100));
- assertNotEquals(mState, mState2);
+ assertNotEqualsAndHashCode();
+ }
+
+ @Test
+ public void testEquals_differentFrame() {
+ mState.setDisplayFrame(new Rect(0, 1, 2, 3));
+ mState.setDisplayFrame(new Rect(4, 5, 6, 7));
+ assertNotEqualsAndHashCode();
+ }
+
+ @Test
+ public void testEquals_sameFrame() {
+ mState.setDisplayFrame(new Rect(0, 1, 2, 3));
+ mState2.setDisplayFrame(new Rect(0, 1, 2, 3));
+ assertEqualsAndHashCode();
}
@Test
@@ -148,6 +186,7 @@
mState.getSource(TYPE_TOP_BAR).setFrame(new Rect(0, 0, 100, 100));
Parcel p = Parcel.obtain();
mState.writeToParcel(p, 0 /* flags */);
+ p.setDataPosition(0);
mState2.readFromParcel(p);
p.recycle();
assertEquals(mState, mState2);
@@ -161,4 +200,14 @@
assertTrue(InsetsState.getDefaultVisibility(TYPE_SIDE_BAR_3));
assertFalse(InsetsState.getDefaultVisibility(TYPE_IME));
}
+
+ private void assertEqualsAndHashCode() {
+ assertEquals(mState, mState2);
+ assertEquals(mState.hashCode(), mState2.hashCode());
+ }
+
+ private void assertNotEqualsAndHashCode() {
+ assertNotEquals(mState, mState2);
+ assertNotEquals(mState.hashCode(), mState2.hashCode());
+ }
}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 928b57c..429bce0 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -38,6 +38,7 @@
import static android.view.Surface.ROTATION_270;
import static android.view.Surface.ROTATION_90;
import static android.view.View.GONE;
+import static android.view.ViewRootImpl.NEW_INSETS_MODE_NONE;
import static android.view.WindowManager.DOCKED_BOTTOM;
import static android.view.WindowManager.DOCKED_INVALID;
import static android.view.WindowManager.DOCKED_TOP;
@@ -172,6 +173,7 @@
import android.view.SurfaceControl.Transaction;
import android.view.SurfaceSession;
import android.view.View;
+import android.view.ViewRootImpl;
import android.view.WindowManager;
import android.view.WindowManagerPolicyConstants.PointerEventListener;
@@ -3251,6 +3253,36 @@
mInputMethodTargetWaitingAnim = targetWaitingAnim;
assignWindowLayers(false /* setLayoutNeeded */);
mInsetsStateController.onImeTargetChanged(target);
+ updateImeParent();
+ }
+
+ private void updateImeParent() {
+ if (ViewRootImpl.sNewInsetsMode == NEW_INSETS_MODE_NONE) {
+ return;
+ }
+ final SurfaceControl newParent = computeImeParent();
+ if (newParent != null) {
+ mPendingTransaction.reparent(mImeWindowsContainers.mSurfaceControl, newParent);
+ scheduleAnimation();
+ }
+ }
+
+ /**
+ * Computes the window the IME should be attached to.
+ */
+ @VisibleForTesting
+ SurfaceControl computeImeParent() {
+
+ // Attach it to app if the target is part of an app and such app is covering the entire
+ // screen. If it's not covering the entire screen the IME might extend beyond the apps
+ // bounds.
+ if (mInputMethodTarget != null && mInputMethodTarget.mAppToken != null &&
+ mInputMethodTarget.getWindowingMode() == WINDOWING_MODE_FULLSCREEN) {
+ return mInputMethodTarget.mAppToken.getSurfaceControl();
+ }
+
+ // Otherwise, we just attach it to the display.
+ return mWindowingLayer;
}
boolean getNeedsMenu(WindowState top, WindowManagerPolicy.WindowState bottom) {
@@ -4885,6 +4917,11 @@
.reparent(mWindowingLayer, sc).reparent(mOverlayLayer, sc);
}
+ @VisibleForTesting
+ SurfaceControl getWindowingLayer() {
+ return mWindowingLayer;
+ }
+
/**
* Create a portal window handle for input. This window transports any touch to the display
* indicated by {@link InputWindowHandle#portalToDisplayId} if the touch hits this window.
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index bbf115f..f98ca3d 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -27,6 +27,7 @@
import static android.content.res.Configuration.UI_MODE_TYPE_MASK;
import static android.view.InsetsState.TYPE_TOP_BAR;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+import static android.view.ViewRootImpl.NEW_INSETS_MODE_NONE;
import static android.view.WindowManager.INPUT_CONSUMER_NAVIGATION;
import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
import static android.view.WindowManager.LayoutParams.FIRST_SYSTEM_WINDOW;
@@ -134,6 +135,7 @@
import android.view.PointerIcon;
import android.view.Surface;
import android.view.View;
+import android.view.ViewRootImpl;
import android.view.WindowManager;
import android.view.WindowManager.LayoutParams;
import android.view.WindowManagerGlobal;
@@ -1900,7 +1902,10 @@
if (win.isVoiceInteraction()) {
cf.set(displayFrames.mVoiceContent);
} else {
- if (adjust != SOFT_INPUT_ADJUST_RESIZE) {
+ // IME Insets are handled on the client for ADJUST_RESIZE in the new
+ // insets world
+ if (ViewRootImpl.sNewInsetsMode != NEW_INSETS_MODE_NONE
+ || adjust != SOFT_INPUT_ADJUST_RESIZE) {
cf.set(displayFrames.mDock);
} else {
cf.set(displayFrames.mContent);
@@ -1991,7 +1996,11 @@
of.set(displayFrames.mRestricted);
df.set(displayFrames.mRestricted);
pf.set(displayFrames.mRestricted);
- if (adjust != SOFT_INPUT_ADJUST_RESIZE) {
+
+ // IME Insets are handled on the client for ADJUST_RESIZE in the new insets
+ // world
+ if (ViewRootImpl.sNewInsetsMode != NEW_INSETS_MODE_NONE
+ || adjust != SOFT_INPUT_ADJUST_RESIZE) {
cf.set(displayFrames.mDock);
} else {
cf.set(displayFrames.mContent);
diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java
index afae9c4..a1b52f4 100644
--- a/services/core/java/com/android/server/wm/InsetsStateController.java
+++ b/services/core/java/com/android/server/wm/InsetsStateController.java
@@ -112,6 +112,7 @@
* Called when a layout pass has occurred.
*/
void onPostLayout() {
+ mState.setDisplayFrame(mDisplayContent.getBounds());
for (int i = mControllers.size() - 1; i>= 0; i--) {
mControllers.valueAt(i).onPostLayout();
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index 3826fac..a62bc71 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -51,17 +51,22 @@
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.eq;
import android.annotation.SuppressLint;
+import android.app.WindowConfiguration;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.SystemClock;
import android.platform.test.annotations.Presubmit;
import android.util.DisplayMetrics;
import android.view.DisplayCutout;
+import android.view.DisplayInfo;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.Surface;
+import android.view.ViewRootImpl;
+import android.view.test.InsetsModeSession;
import androidx.test.filters.FlakyTest;
import androidx.test.filters.SmallTest;
@@ -628,6 +633,39 @@
eq(activityRecord), anyBoolean(), eq(dc.getDisplayId()));
}
+ @Test
+ public void testComputeImeParent_app() throws Exception {
+ try (final InsetsModeSession session =
+ new InsetsModeSession(ViewRootImpl.NEW_INSETS_MODE_IME)) {
+ final DisplayContent dc = createNewDisplay();
+ dc.mInputMethodTarget = createWindow(null, TYPE_BASE_APPLICATION, "app");
+ assertEquals(dc.mInputMethodTarget.mAppToken.getSurfaceControl(),
+ dc.computeImeParent());
+ }
+ }
+
+ @Test
+ public void testComputeImeParent_app_notFullscreen() throws Exception {
+ try (final InsetsModeSession session =
+ new InsetsModeSession(ViewRootImpl.NEW_INSETS_MODE_IME)) {
+ final DisplayContent dc = createNewDisplay();
+ dc.mInputMethodTarget = createWindow(null, TYPE_STATUS_BAR, "app");
+ dc.mInputMethodTarget.setWindowingMode(
+ WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
+ assertEquals(dc.getWindowingLayer(), dc.computeImeParent());
+ }
+ }
+
+ @Test
+ public void testComputeImeParent_noApp() throws Exception {
+ try (final InsetsModeSession session =
+ new InsetsModeSession(ViewRootImpl.NEW_INSETS_MODE_IME)) {
+ final DisplayContent dc = createNewDisplay();
+ dc.mInputMethodTarget = createWindow(null, TYPE_STATUS_BAR, "statusBar");
+ assertEquals(dc.getWindowingLayer(), dc.computeImeParent());
+ }
+ }
+
private boolean isOptionsPanelAtRight(int displayId) {
return (mWm.getPreferredOptionsPanelGravity(displayId) & Gravity.RIGHT) == Gravity.RIGHT;
}
diff --git a/tests/utils/testutils/java/android/view/test/InsetsModeSession.java b/tests/utils/testutils/java/android/view/test/InsetsModeSession.java
new file mode 100644
index 0000000..c83dfa4
--- /dev/null
+++ b/tests/utils/testutils/java/android/view/test/InsetsModeSession.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.test;
+
+import android.view.ViewRootImpl;
+
+/**
+ * Session to set insets mode for {@link ViewRootImpl#sNewInsetsMode}.
+ */
+public class InsetsModeSession implements AutoCloseable {
+
+ private int mOldMode;
+
+ public InsetsModeSession(int flag) {
+ mOldMode = ViewRootImpl.sNewInsetsMode;
+ ViewRootImpl.sNewInsetsMode = flag;
+ }
+
+ @Override
+ public void close() throws Exception {
+ ViewRootImpl.sNewInsetsMode = mOldMode;
+ }
+}