Reparent IME window and handle non-fullscreen windows correctly
IME window is attached to the IME target if possible. This ensures a smooth enter/exit animation when the activity is coming in/going away. Furthermore, if the controlling window doesn't span the entire display, we can't offer controlling it in a frame-by-frame manner, and we need to do the inset calculations relative to the display frame. Test: adb shell setprop persist.wm.new_insets 1 adb shell setprop persist.pre_render_ime_views 0 Test: Open IME, go home, reopen app Test: Show dialog with EditText Bug: 111084606 Change-Id: Id40470f6f8284b48acfa4719049afd14fde332d6
This commit is contained in:
committed by
Jorim Jaggi
parent
437ed07bb1
commit
a6f3561667
@@ -133,6 +133,10 @@ public class InsetsController implements WindowInsetsController {
|
||||
}
|
||||
|
||||
void onFrameChanged(Rect frame) {
|
||||
if (mFrame.equals(frame)) {
|
||||
return;
|
||||
}
|
||||
mViewRoot.notifyInsetsChanged();
|
||||
mFrame.set(frame);
|
||||
}
|
||||
|
||||
@@ -257,11 +261,21 @@ public class InsetsController implements WindowInsetsController {
|
||||
|
||||
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 +299,7 @@ public class InsetsController implements WindowInsetsController {
|
||||
}
|
||||
|
||||
final InsetsAnimationControlImpl controller = new InsetsAnimationControlImpl(consumers,
|
||||
mFrame, mState, listener, typesReady,
|
||||
frame, mState, listener, typesReady,
|
||||
() -> new SyncRtSurfaceTransactionApplier(mViewRoot.mView), this);
|
||||
mAnimationControls.add(controller);
|
||||
}
|
||||
@@ -436,6 +450,7 @@ public class InsetsController implements WindowInsetsController {
|
||||
// nothing to animate.
|
||||
return;
|
||||
}
|
||||
|
||||
WindowInsetsAnimationControlListener listener = new WindowInsetsAnimationControlListener() {
|
||||
@Override
|
||||
public void onReady(WindowInsetsAnimationController controller, int types) {
|
||||
@@ -479,7 +494,10 @@ public class InsetsController implements WindowInsetsController {
|
||||
// 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) {
|
||||
|
||||
@@ -39,6 +39,7 @@ 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 +105,11 @@ public class InsetsState implements Parcelable {
|
||||
|
||||
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() {
|
||||
}
|
||||
|
||||
@@ -209,6 +215,14 @@ public class InsetsState implements Parcelable {
|
||||
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 +238,7 @@ public class InsetsState implements Parcelable {
|
||||
}
|
||||
|
||||
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 +338,9 @@ public class InsetsState implements Parcelable {
|
||||
|
||||
InsetsState state = (InsetsState) o;
|
||||
|
||||
if (!mDisplayFrame.equals(state.mDisplayFrame)) {
|
||||
return false;
|
||||
}
|
||||
if (mSources.size() != state.mSources.size()) {
|
||||
return false;
|
||||
}
|
||||
@@ -341,7 +359,7 @@ public class InsetsState implements Parcelable {
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return mSources.hashCode();
|
||||
return Objects.hash(mDisplayFrame, mSources);
|
||||
}
|
||||
|
||||
public InsetsState(Parcel in) {
|
||||
@@ -355,9 +373,10 @@ public class InsetsState implements Parcelable {
|
||||
|
||||
@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 +393,7 @@ public class InsetsState implements Parcelable {
|
||||
|
||||
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 */);
|
||||
|
||||
@@ -24,6 +24,11 @@ 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;
|
||||
@@ -94,6 +99,17 @@ public class InsetsControllerTest {
|
||||
assertNull(mController.getSourceConsumer(TYPE_TOP_BAR).getControl());
|
||||
}
|
||||
|
||||
@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();
|
||||
|
||||
@@ -37,6 +37,7 @@ import android.os.Parcel;
|
||||
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 +54,58 @@ public class InsetsStateTest {
|
||||
private InsetsState mState2 = new InsetsState();
|
||||
|
||||
@Test
|
||||
public void testCalculateInsets() {
|
||||
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()));
|
||||
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, 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()));
|
||||
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, 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() {
|
||||
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()));
|
||||
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, 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
|
||||
@@ -114,14 +124,14 @@ public class InsetsStateTest {
|
||||
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 +140,7 @@ public class InsetsStateTest {
|
||||
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 +148,21 @@ public class InsetsStateTest {
|
||||
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 +172,7 @@ public class InsetsStateTest {
|
||||
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 +186,14 @@ public class InsetsStateTest {
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,6 +38,7 @@ import static android.view.Surface.ROTATION_180;
|
||||
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;
|
||||
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 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
|
||||
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 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo
|
||||
.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.
|
||||
|
||||
@@ -112,6 +112,7 @@ class InsetsStateController {
|
||||
* 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();
|
||||
}
|
||||
|
||||
@@ -51,17 +51,22 @@ import static org.junit.Assert.assertNotNull;
|
||||
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 @@ public class DisplayContentTests extends WindowTestsBase {
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user