diff options
author | Ming-Shin Lu <lumark@google.com> | 2022-01-19 00:54:21 +0800 |
---|---|---|
committer | Xin Li <delphij@google.com> | 2022-03-02 18:17:48 -0800 |
commit | 83c396e9e7fd9a94ee410cae1f8a5fe21dbaa96b (patch) | |
tree | 64bcf59a1a90a02446b4d1b8f46a9e0ae454fbc5 | |
parent | ade248a9bc7797209838d3d780f2ee03cff84d64 (diff) | |
download | base-83c396e9e7fd9a94ee410cae1f8a5fe21dbaa96b.tar.gz |
Fix IME flicker by dispatching unrelated insets after unset IME frozen
CL[1] to add InsetsStateController#updateAboveInsetsState in
DC#updateImeInputAndControlTarget that after setting
mImeInsetsFrozenUntilStartInput as false to unfreeze IME insets,
so that increases the possibility to deliver the last IME insets
change to non-IME input target window that didn't request show IME.
(Since updateImeInputAndControlTarget will call
WindowState#insetsChanged to client eventually)
As typically the new IME insets change will deliver to the client when
server receives InsetsStateController#onInsetsModified(InsetsControlTarget)
from the client requests IME visible after
DC#updateImeInputAndControlTarget.
So in DC#updateImeInputAndControlTarget, we should ensure to unfreeze
IME insets after the input target updated, in case updateAboveInsetsState
may deliver unrelated IME insets change to the non-IME requester.
[1]: I57357ba85501397fa5926ab4dee116c42df24506
Bug: 213522825
Test: atest ActivityRecordTests#\
testImeInsetsFrozenFlag_noDispatchVisibleInsetsWhenAppNotRequest
Test: atest WindowStateTests#\
testAdjustImeInsetsVisibilityWhenSwitchingApps
Test: Verify Bug 195385541 and Bug 195846009 that keyboard
won't cover the edit text.
(cherry picked from commit 938eb8fd2ded04bbcd5f32576a7bb4eaf186d56a)
(cherry picked from commit be42921e05ba3d1946efc090054cd4a498f22b80)
Merged-In: I95c5b45bd4cf9c30ff7771b2db485c8ae2b0c6db
Change-Id: I95c5b45bd4cf9c30ff7771b2db485c8ae2b0c6db
-rw-r--r-- | services/core/java/com/android/server/wm/DisplayContent.java | 8 | ||||
-rw-r--r-- | services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java | 47 |
2 files changed, 52 insertions, 3 deletions
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 1e31fdab30d0..42c6dd43ebce 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -4122,9 +4122,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp * which controls the visibility and animation of the input method window. */ void updateImeInputAndControlTarget(WindowState target) { - if (target != null && target.mActivityRecord != null) { - target.mActivityRecord.mImeInsetsFrozenUntilStartInput = false; - } if (mImeInputTarget != target) { ProtoLog.i(WM_DEBUG_IME, "setInputMethodInputTarget %s", target); setImeInputTarget(target); @@ -4132,6 +4129,11 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp .getRawInsetsState().getSourceOrDefaultVisibility(ITYPE_IME)); updateImeControlTarget(); } + // Unfreeze IME insets after the new target updated, in case updateAboveInsetsState may + // deliver unrelated IME insets change to the non-IME requester. + if (target != null && target.mActivityRecord != null) { + target.mActivityRecord.mImeInsetsFrozenUntilStartInput = false; + } } void updateImeControlTarget() { diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java index 9a68b5f1b609..dd4cc5022cd2 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java @@ -99,6 +99,7 @@ import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.isA; +import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.never; @@ -131,6 +132,7 @@ import android.view.IWindowManager; import android.view.IWindowSession; import android.view.InsetsSource; import android.view.InsetsState; +import android.view.InsetsVisibilities; import android.view.RemoteAnimationAdapter; import android.view.RemoteAnimationTarget; import android.view.Surface; @@ -147,6 +149,7 @@ import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; import org.mockito.invocation.InvocationOnMock; import java.util.ArrayList; @@ -3076,6 +3079,50 @@ public class ActivityRecordTests extends WindowTestsBase { assertEquals(state.getSource(ITYPE_IME).getFrame(), imeSource.getFrame()); } + @UseTestDisplay(addWindows = {W_ACTIVITY, W_INPUT_METHOD}) + @Test + public void testImeInsetsFrozenFlag_noDispatchVisibleInsetsWhenAppNotRequest() + throws RemoteException { + final WindowState app1 = createWindow(null, TYPE_APPLICATION, "app1"); + final WindowState app2 = createWindow(null, TYPE_APPLICATION, "app2"); + + mDisplayContent.getInsetsStateController().getSourceProvider(ITYPE_IME).setWindow( + mImeWindow, null, null); + mImeWindow.getControllableInsetProvider().setServerVisible(true); + + // Simulate app2 is closing and let app1 is visible to be IME targets. + makeWindowVisibleAndDrawn(app1, mImeWindow); + mDisplayContent.setImeLayeringTarget(app1); + mDisplayContent.updateImeInputAndControlTarget(app1); + app2.mActivityRecord.commitVisibility(false, false); + + // app1 requests IME visible. + final InsetsVisibilities requestedVisibilities = new InsetsVisibilities(); + requestedVisibilities.setVisibility(ITYPE_IME, true); + app1.setRequestedVisibilities(requestedVisibilities); + mDisplayContent.getInsetsStateController().onInsetsModified(app1); + + // Verify app1's IME insets is visible and app2's IME insets frozen flag set. + assertTrue(app1.getInsetsState().peekSource(ITYPE_IME).isVisible()); + assertTrue(app2.mActivityRecord.mImeInsetsFrozenUntilStartInput); + + // Simulate switching to app2 to make it visible to be IME targets. + makeWindowVisibleAndDrawn(app2); + spyOn(app2); + spyOn(app2.mClient); + ArgumentCaptor<InsetsState> insetsStateCaptor = ArgumentCaptor.forClass(InsetsState.class); + doReturn(true).when(app2).isReadyToDispatchInsetsState(); + mDisplayContent.setImeLayeringTarget(app2); + mDisplayContent.updateImeInputAndControlTarget(app2); + + // Verify after unfreezing app2's IME insets state, we won't dispatch visible IME insets + // to client if the app didn't request IME visible. + assertFalse(app2.mActivityRecord.mImeInsetsFrozenUntilStartInput); + verify(app2.mClient, atLeastOnce()).insetsChanged(insetsStateCaptor.capture(), anyBoolean(), + anyBoolean()); + assertFalse(insetsStateCaptor.getAllValues().get(0).peekSource(ITYPE_IME).isVisible()); + } + @Test public void testInClosingAnimation_doNotHideSurface() { final WindowState app = createWindow(null, TYPE_APPLICATION, "app"); |