From b3727558832ed7f41b3823a9eccbb9c064652cb3 Mon Sep 17 00:00:00 2001 From: Ming-Shin Lu Date: Sun, 8 May 2022 14:31:02 +0000 Subject: [PATCH] Fix IME insets animation stucked by wrong InsetsHint There is a chance that if InsetsSourceProvider#updateControlForTarget invoked when its serverVisible not yet visible, then the insetsHint of the new InsetsSourceControl will be empty because CL[1] will set insets source frame as empty when the serverVisible is false. This would make the insetsHint of the control may not able to get updated if the source frame size is same as the last visible source frame in the next onPostLayout, that affects the insets animation will not fullly animated because of wrong InsetsHint. Add a mInsetsHint field in InsetsSourceProvider to strore the last insetsHint from onPostLayout for initializing the new source control. [1]: Ie7845de2830cdbdfd0049b8eef5a5f0704f796e8 Fix: 230581950 Bug: 229938982 Test: manual as issue steps. Test: atest InsetsStateControllTest#testGetInsetsHintForNewControl Change-Id: I0fd5845d1f816b558deeed490c93ec207e2f0607 --- .../server/wm/InsetsSourceProvider.java | 6 ++-- .../server/wm/InsetsStateControllerTest.java | 30 +++++++++++++++++++ 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java index 9853d1304b142..358e93d89f64d 100644 --- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java +++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java @@ -82,6 +82,7 @@ abstract class InsetsSourceProvider { private boolean mIsLeashReadyForDispatching; private final Rect mSourceFrame = new Rect(); private final Rect mLastSourceFrame = new Rect(); + private @NonNull Insets mInsetsHint = Insets.NONE; private final Consumer mSetLeashPositionConsumer = t -> { if (mControl != null) { @@ -298,6 +299,7 @@ abstract class InsetsSourceProvider { if (!insetsHint.equals(mControl.getInsetsHint())) { changed = true; mControl.setInsetsHint(insetsHint); + mInsetsHint = insetsHint; } mLastSourceFrame.set(mSource.getFrame()); } @@ -433,8 +435,8 @@ abstract class InsetsSourceProvider { final SurfaceControl leash = mAdapter.mCapturedLeash; mControlTarget = target; updateVisibility(); - mControl = new InsetsSourceControl(mSource.getType(), leash, surfacePosition, - mSource.calculateInsets(mWindowContainer.getBounds(), true /* ignoreVisibility */)); + mControl = new InsetsSourceControl(mSource.getType(), leash, surfacePosition, mInsetsHint); + ProtoLog.d(WM_DEBUG_WINDOW_INSETS, "InsetsSource Control %s for target %s", mControl, mControlTarget); } diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java index 6d022262b7200..ffa21fadff6b9 100644 --- a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java @@ -449,6 +449,36 @@ public class InsetsStateControllerTest extends WindowTestsBase { assertNotNull(app.getInsetsState().peekSource(ITYPE_NAVIGATION_BAR)); } + @UseTestDisplay(addWindows = W_INPUT_METHOD) + @Test + public void testGetInsetsHintForNewControl() { + final WindowState app1 = createTestWindow("app1"); + final WindowState app2 = createTestWindow("app2"); + + makeWindowVisible(mImeWindow); + final InsetsSourceProvider imeInsetsProvider = getController().getSourceProvider(ITYPE_IME); + imeInsetsProvider.setWindowContainer(mImeWindow, null, null); + imeInsetsProvider.updateSourceFrame(mImeWindow.getFrame()); + + imeInsetsProvider.updateControlForTarget(app1, false); + imeInsetsProvider.onPostLayout(); + final InsetsSourceControl control1 = imeInsetsProvider.getControl(app1); + assertNotNull(control1); + assertEquals(imeInsetsProvider.getSource().getFrame().height(), + control1.getInsetsHint().bottom); + + // Simulate the IME control target updated from app1 to app2 when IME insets was invisible. + imeInsetsProvider.setServerVisible(false); + imeInsetsProvider.updateControlForTarget(app2, false); + + // Verify insetsHint of the new control is same as last IME source frame after the layout. + imeInsetsProvider.onPostLayout(); + final InsetsSourceControl control2 = imeInsetsProvider.getControl(app2); + assertNotNull(control2); + assertEquals(imeInsetsProvider.getSource().getFrame().height(), + control2.getInsetsHint().bottom); + } + private WindowState createTestWindow(String name) { final WindowState win = createWindow(null, TYPE_APPLICATION, name); win.setHasSurface(true);