diff options
| author | 2022-08-14 02:05:42 +0000 | |
|---|---|---|
| committer | 2022-08-14 02:05:42 +0000 | |
| commit | 805dcc2d74d3cbe7a44d3036a10f2a8de45bbe4c (patch) | |
| tree | 99afe0fb74654ffd4d6266f224c8f0537e635106 | |
| parent | 07fad3278c5e3471fd5a4018670e7f203ea1e09e (diff) | |
| parent | 8945b5bcfceb3060fd6ff954d9ce1d703cf4a093 (diff) | |
Merge "Fix IME jumpcut when playing user IME animation after IME restarted"
4 files changed, 66 insertions, 10 deletions
diff --git a/core/java/android/view/InsetsSourceConsumer.java b/core/java/android/view/InsetsSourceConsumer.java index d63c25a09382..5236fe772a7b 100644 --- a/core/java/android/view/InsetsSourceConsumer.java +++ b/core/java/android/view/InsetsSourceConsumer.java @@ -176,7 +176,9 @@ public class InsetsSourceConsumer { // If we have a new leash, make sure visibility is up-to-date, even though we // didn't want to run an animation above. - applyRequestedVisibilityToControl(); + if (mController.getAnimationType(control.getType()) == ANIMATION_TYPE_NONE) { + applyRequestedVisibilityToControl(); + } // Remove the surface that owned by last control when it lost. if (!requestedVisible && lastControl == null) { diff --git a/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java b/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java index 2054b4fe9a35..8cf118c4b79a 100644 --- a/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java +++ b/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java @@ -18,8 +18,10 @@ package android.view; import static android.view.InsetsController.ANIMATION_TYPE_NONE; import static android.view.InsetsController.ANIMATION_TYPE_USER; +import static android.view.InsetsSourceConsumer.ShowResult.SHOW_IMMEDIATELY; import static android.view.InsetsState.ITYPE_IME; import static android.view.InsetsState.ITYPE_STATUS_BAR; +import static android.view.WindowInsets.Type.ime; import static android.view.WindowInsets.Type.statusBars; import static junit.framework.Assert.assertEquals; @@ -28,6 +30,7 @@ import static junit.framework.TestCase.assertTrue; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyZeroInteractions; @@ -75,6 +78,7 @@ public class InsetsSourceConsumerTest { private boolean mRemoveSurfaceCalled = false; private InsetsController mController; private InsetsState mState; + private ViewRootImpl mViewRoot; @Before public void setup() { @@ -86,10 +90,9 @@ public class InsetsSourceConsumerTest { instrumentation.runOnMainSync(() -> { final Context context = instrumentation.getTargetContext(); // cannot mock ViewRootImpl since it's final. - final ViewRootImpl viewRootImpl = new ViewRootImpl(context, - context.getDisplayNoVerify()); + mViewRoot = new ViewRootImpl(context, context.getDisplayNoVerify()); try { - viewRootImpl.setView(new TextView(context), new LayoutParams(), null); + mViewRoot.setView(new TextView(context), new LayoutParams(), null); } catch (BadTokenException e) { // activity isn't running, lets ignore BadTokenException. } @@ -97,7 +100,7 @@ public class InsetsSourceConsumerTest { mSpyInsetsSource = Mockito.spy(new InsetsSource(ITYPE_STATUS_BAR)); mState.addSource(mSpyInsetsSource); - mController = new InsetsController(new ViewRootInsetsControllerHost(viewRootImpl)); + mController = new InsetsController(new ViewRootInsetsControllerHost(mViewRoot)); mConsumer = new InsetsSourceConsumer(ITYPE_STATUS_BAR, mState, () -> mMockTransaction, mController) { @Override @@ -207,4 +210,40 @@ public class InsetsSourceConsumerTest { }); } + + @Test + public void testWontUpdateImeLeashVisibility_whenAnimation() { + InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> { + InsetsState state = new InsetsState(); + ViewRootInsetsControllerHost host = new ViewRootInsetsControllerHost(mViewRoot); + InsetsController insetsController = new InsetsController(host, (controller, type) -> { + if (type == ITYPE_IME) { + return new InsetsSourceConsumer(ITYPE_IME, state, + () -> mMockTransaction, controller) { + @Override + public int requestShow(boolean fromController) { + return SHOW_IMMEDIATELY; + } + }; + } + return new InsetsSourceConsumer(type, controller.getState(), Transaction::new, + controller); + }, host.getHandler()); + InsetsSourceConsumer imeConsumer = insetsController.getSourceConsumer(ITYPE_IME); + + // Initial IME insets source control with its leash. + imeConsumer.setControl(new InsetsSourceControl(ITYPE_IME, mLeash, + false /* initialVisible */, new Point(), Insets.NONE), new int[1], new int[1]); + reset(mMockTransaction); + + // Verify when the app requests controlling show IME animation, the IME leash + // visibility won't be updated when the consumer received the same leash in setControl. + insetsController.controlWindowInsetsAnimation(ime(), 0L, + null /* interpolator */, null /* cancellationSignal */, null /* listener */); + assertTrue(insetsController.getAnimationType(ITYPE_IME) == ANIMATION_TYPE_USER); + imeConsumer.setControl(new InsetsSourceControl(ITYPE_IME, mLeash, + true /* initialVisible */, new Point(), Insets.NONE), new int[1], new int[1]); + verify(mMockTransaction, never()).show(mLeash); + }); + } } diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index c8692ca81b2e..fccf54d83198 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -4416,13 +4416,20 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp */ @VisibleForTesting InsetsControlTarget computeImeControlTarget() { + if (mImeInputTarget == null) { + // A special case that if there is no IME input target while the IME is being killed, + // in case seeing unexpected IME surface visibility change when delivering the IME leash + // to the remote insets target during the IME restarting, but the focus window is not in + // multi-windowing mode, return null target until the next input target updated. + return null; + } + + final WindowState imeInputTarget = mImeInputTarget.getWindowState(); if (!isImeControlledByApp() && mRemoteInsetsControlTarget != null - || (mImeInputTarget != null - && getImeHostOrFallback(mImeInputTarget.getWindowState()) - == mRemoteInsetsControlTarget)) { + || getImeHostOrFallback(imeInputTarget) == mRemoteInsetsControlTarget) { return mRemoteInsetsControlTarget; } else { - return mImeInputTarget != null ? mImeInputTarget.getWindowState() : null; + return imeInputTarget; } } 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 2cf9c01a4476..28d2aa157e0a 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java @@ -1252,7 +1252,15 @@ public class DisplayContentTests extends WindowTestsBase { public void testComputeImeControlTarget() throws Exception { final DisplayContent dc = createNewDisplay(); dc.setRemoteInsetsController(createDisplayWindowInsetsController()); - dc.setImeInputTarget(createWindow(null, TYPE_BASE_APPLICATION, "app")); + dc.mCurrentFocus = createWindow(null, TYPE_BASE_APPLICATION, "app"); + + // Expect returning null IME control target when the focus window has not yet been the + // IME input target (e.g. IME is restarting) in fullscreen windowing mode. + dc.setImeInputTarget(null); + assertFalse(dc.mCurrentFocus.inMultiWindowMode()); + assertNull(dc.computeImeControlTarget()); + + dc.setImeInputTarget(dc.mCurrentFocus); dc.setImeLayeringTarget(dc.getImeInputTarget().getWindowState()); assertEquals(dc.getImeInputTarget().getWindowState(), dc.computeImeControlTarget()); } |