Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 938eb8fd authored by Ming-Shin Lu's avatar Ming-Shin Lu
Browse files

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.

Change-Id: I95c5b45bd4cf9c30ff7771b2db485c8ae2b0c6db
parent 52cf535b
Loading
Loading
Loading
Loading
+5 −3
Original line number Diff line number Diff line
@@ -4124,9 +4124,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);
@@ -4134,6 +4131,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() {
+47 −0
Original line number Diff line number Diff line
@@ -103,6 +103,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;

@@ -136,6 +137,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;
@@ -152,6 +154,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;
@@ -3081,6 +3084,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");