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

Commit 5edf6c37 authored by Felix Stern's avatar Felix Stern
Browse files

Removing IMS surface in InputMethodService#hideSoftInput after hiding the window

After hiding the IME, we formerly removed its surface via an explicit call in the ImeInsetsSourceConsumer, later in InsetsController, but was requested too early, as the animation was not finished at the time. Removing the surface should only happen, after the IME window was already hidden and can be done in InputMethodService#hideSoftInput.

Bug: 298172246
Flag: android.view.inputmethod.refactor_insets_controller
Test: atest com.android.inputmethodservice.InputMethodServiceTest#testSurfaceRemovedAfterHideSoftInput
Change-Id: I546327a5dfb570dcf3999f94e4556d08ac2a70b6
parent c26379f2
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -990,6 +990,8 @@ public class InputMethodService extends AbstractInputMethodService {
            }
            Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
            if (android.view.inputmethod.Flags.refactorInsetsController()) {
                // After the IME window was hidden, we can remove its surface
                scheduleImeSurfaceRemoval();
                // The hide request first finishes the animation and then proceeds to the server
                // side, finally reaching here, marking this the end state.
                ImeTracker.forLogging().onHidden(statsToken);
+5 −1
Original line number Diff line number Diff line
@@ -1299,8 +1299,12 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
            // Handle the pending show request for other insets types since the IME insets
            // has being requested hidden.
            handlePendingControlRequest(statsToken);
            if (!Flags.refactorInsetsController()) {
                // the surface can't be removed until the end of the animation. This is handled by
                // IMMS after the window was requested to be hidden.
                getImeSourceConsumer().removeSurface();
            }
        }
        applyAnimation(typesReady, false /* show */, fromIme, false /* skipsCallbacks */,
                statsToken);
    }
+3 −1
Original line number Diff line number Diff line
@@ -230,7 +230,9 @@ public class InsetsSourceConsumerTest {
                    new InsetsSourceControl(ID_STATUS_BAR, statusBars(), mLeash,
                            false /* initialVisible */, new Point(), Insets.NONE),
                    new int[1], hideTypes, new int[1], new int[1]);
            if (!android.view.inputmethod.Flags.refactorInsetsController()) {
                assertTrue(mRemoveSurfaceCalled);
            }
            assertEquals(0, hideTypes[0]);
        });

+7 −3
Original line number Diff line number Diff line
@@ -324,10 +324,12 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
                        }
                        applyVisibilityToLeash(imeSourceControl);
                    }
                    if (!android.view.inputmethod.Flags.refactorInsetsController()) {
                        if (!mImeShowing) {
                            removeImeSurface(mDisplayId);
                        }
                    }
                }
            } else {
                if (!android.view.inputmethod.Flags.refactorInsetsController()
                        && mAnimation != null) {
@@ -663,7 +665,9 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
                        ImeTracker.forLogging().onProgress(mStatsToken,
                                ImeTracker.PHASE_WM_ANIMATION_RUNNING);
                        t.hide(animatingLeash);
                        if (!android.view.inputmethod.Flags.refactorInsetsController()) {
                            removeImeSurface(mDisplayId);
                        }
                        if (android.view.inputmethod.Flags.refactorInsetsController()) {
                            setVisibleDirectly(false /* visible */, statsToken);
                        }
+60 −0
Original line number Diff line number Diff line
@@ -40,10 +40,14 @@ import android.content.res.Configuration;
import android.graphics.Insets;
import android.os.Build;
import android.os.RemoteException;
import android.platform.test.annotations.RequiresFlagsEnabled;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.provider.Settings;
import android.server.wm.WindowManagerStateHelper;
import android.util.Log;
import android.view.View;
import android.view.WindowInsets;
import android.view.WindowInsetsAnimation;
import android.view.WindowManagerGlobal;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.Flags;
@@ -72,6 +76,7 @@ import org.junit.Test;
import org.junit.rules.TestName;
import org.junit.runner.RunWith;

import java.util.List;
import java.util.Objects;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@@ -244,6 +249,61 @@ public class InputMethodServiceTest {
                EVENT_HIDE, true /* eventExpected */, false /* shown */, "IME is not shown");
    }

    /**
     * This checks that the surface is removed after the window was hidden in
     * InputMethodService#hideSoftInput
     */
    @Test
    @RequiresFlagsEnabled(Flags.FLAG_REFACTOR_INSETS_CONTROLLER)
    public void testSurfaceRemovedAfterHideSoftInput() {
        setShowImeWithHardKeyboard(true /* enabled */);

        // Triggers to show IME via public API.
        verifyInputViewStatusOnMainSync(() -> mActivity.showImeWithWindowInsetsController(),
                EVENT_SHOW, true /* eventExpected */, true /* shown */, "IME is shown");
        assertWithMessage("IME is shown").that(mInputMethodService.isInputViewShown()).isTrue();

        final var window = mInputMethodService.getWindow().getWindow();
        assertWithMessage("IME window exists").that(window).isNotNull();
        assertWithMessage("IME window showing").that(
                window.getDecorView().getVisibility()).isEqualTo(View.VISIBLE);

        mActivity.getWindow().getDecorView().setWindowInsetsAnimationCallback(
                new WindowInsetsAnimation.Callback(
                        WindowInsetsAnimation.Callback.DISPATCH_MODE_CONTINUE_ON_SUBTREE) {
                    @NonNull
                    @Override
                    public WindowInsetsAnimation.Bounds onStart(
                            @NonNull WindowInsetsAnimation animation,
                            @NonNull WindowInsetsAnimation.Bounds bounds) {
                        return super.onStart(animation, bounds);
                    }

                    @NonNull
                    @Override
                    public WindowInsets onProgress(@NonNull WindowInsets insets,
                            @NonNull List<WindowInsetsAnimation> runningAnimations) {
                        assertWithMessage("IME surface not removed during the animation").that(
                                window.getDecorView().getVisibility()).isEqualTo(View.VISIBLE);
                        return insets;
                    }

                    @Override
                    public void onEnd(@NonNull WindowInsetsAnimation animation) {
                        assertWithMessage(
                                "IME surface not removed before the end of the animation").that(
                                window.getDecorView().getVisibility()).isEqualTo(View.VISIBLE);
                        super.onEnd(animation);
                    }
                });

        // Triggers to hide IME via public API.
        verifyInputViewStatusOnMainSync(() -> mActivity.hideImeWithWindowInsetsController(),
                EVENT_HIDE, true /* eventExpected */, false /* shown */, "IME is not shown");
        eventually(() -> assertWithMessage("IME window not showing").that(
                window.getDecorView().getVisibility()).isEqualTo(View.GONE));
    }

    /**
     * This checks the result of calling IMS#requestShowSelf and IMS#requestHideSelf.
     */