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

Commit 23998b60 authored by Felix Stern's avatar Felix Stern
Browse files

Changing IMM#hideSoftInputFromWindow return value for app with target SDK's < B

Some older apps are calling InputMethodManager#hideSoftInputFromWindow multiple times, until it returns false, to show then their in-app keyboard. Before Android Baklava, the return value was an indicator, whether the request has been send to IMMS. For apps targeting those releases, we return true only if the IME was requested visible before.
Apps that target Android Baklava and onwards should use the View.OnApplyWindowInsetsListener to get changes in the IME visibility.

Fix: 395521150
Fix: 395986009
Flag: android.view.inputmethod.refactor_insets_controller
Test: manual with app with targetSDK < Android Baklava: call IMS#hideSoftInputFromWindow while IME is showing (expect true), and while hidden (expect false)
Change-Id: I1e4588f017fecd49b3aa999fa7749ae7a3bb0d1e
parent 5e6a3f32
Loading
Loading
Loading
Loading
+50 −10
Original line number Diff line number Diff line
@@ -465,6 +465,26 @@ public final class InputMethodManager {
    @EnabledSince(targetSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM)
    private static final long USE_ASYNC_SHOW_HIDE_METHOD = 352594277L; // This is a bug id.

    /**
     * Always return {@code true} when {@link #hideSoftInputFromWindow(IBinder, int)} and
     * {@link #hideSoftInputFromWindow(IBinder, int, ResultReceiver, int, ImeTracker.Token)} is
     * called.
     * <p>
     * Apps can incorrectly rely on the return value of
     * {@link #hideSoftInputFromWindow(IBinder, int)} to guess whether the IME was hidden.
     * However, it was never a guarantee that the IME will actually hide.
     * Therefore, it was recommended using the {@link View.OnApplyWindowInsetsListener}.
     * <p>
     * Starting Android {@link Build.VERSION_CODES.BAKLAVA}, the return value will always be
     * true, independent from whether the request was eventually sent to system server or not.
     * Apps that need to know when the IME visibility changes should use
     * {@link View.OnApplyWindowInsetsListener}. For apps that target earlier releases, it
     * returns an estimate ({@code true} if the IME was showing before).
     */
    @ChangeId
    @EnabledSince(targetSdkVersion = Build.VERSION_CODES.BAKLAVA)
    private static final long ALWAYS_RETURN_TRUE_HIDE_SOFT_INPUT_FROM_WINDOW = 395521150L; // bug id

    /**
     * If {@code true}, avoid calling the
     * {@link com.android.server.inputmethod.InputMethodManagerService InputMethodManagerService}
@@ -2531,9 +2551,14 @@ public final class InputMethodManager {
     *
     * @param windowToken The token of the window that is making the request,
     * as returned by {@link View#getWindowToken() View.getWindowToken()}.
     * @return {@code true} if a request was sent to system_server, {@code false} otherwise. Note:
     * this does not return result of the request. For result use {@link ResultReceiver} in
     * {@link #hideSoftInputFromWindow(IBinder, int, ResultReceiver)} instead.
     * @return <p>For apps targeting Android {@link Build.VERSION_CODES.BAKLAVA}, onwards, it
     * will always return {@code true}. To see when the IME is hidden, use
     * {@link View.OnApplyWindowInsetsListener} and verify the provided {@link WindowInsets} for
     * the visibility of IME.
     *
     * <p>For apps targeting releases before Android Baklava: returns {@code true} if a request
     * was sent to system_server, {@code false} otherwise. Note: This does not return the result
     * of that request (i.e. whether the IME was actually hidden).
     */
    public boolean hideSoftInputFromWindow(IBinder windowToken, @HideFlags int flags) {
        return hideSoftInputFromWindow(windowToken, flags, null);
@@ -2563,7 +2588,8 @@ public final class InputMethodManager {
     * {@link #RESULT_UNCHANGED_HIDDEN}, {@link #RESULT_SHOWN}, or
     * {@link #RESULT_HIDDEN}.
     * @return {@code true} if a request was sent to system_server, {@code false} otherwise. Note:
     * this does not return result of the request. For result use {@param resultReceiver} instead.
     * This does not return the result of that request (i.e. whether the IME was actually hidden).
     * For result use {@param resultReceiver} instead.
     *
     * @deprecated The {@link ResultReceiver} is not a reliable way of determining whether the
     * Input Method is actually shown or hidden. If result is needed, use
@@ -2603,7 +2629,10 @@ public final class InputMethodManager {
                ImeTracker.forLogging().onFailed(statsToken, ImeTracker.PHASE_CLIENT_VIEW_SERVED);
                ImeTracker.forLatency().onHideFailed(statsToken,
                        ImeTracker.PHASE_CLIENT_VIEW_SERVED, ActivityThread::currentApplication);
                return false;
                // with the flag enabled and targeting Android Baklava onwards, the return value
                // should be always true (was false before).
                return Flags.refactorInsetsController() && CompatChanges.isChangeEnabled(
                        ALWAYS_RETURN_TRUE_HIDE_SOFT_INPUT_FROM_WINDOW);
            }

            ImeTracker.forLogging().onProgress(statsToken, ImeTracker.PHASE_CLIENT_VIEW_SERVED);
@@ -2618,15 +2647,18 @@ public final class InputMethodManager {
                        // under us. The current input has been closed before (from checkFocus).
                        ImeTracker.forLogging().onFailed(statsToken,
                                ImeTracker.PHASE_CLIENT_VIEW_HANDLER_AVAILABLE);
                        return false;
                        // with the flag enabled and targeting Android Baklava onwards, the
                        // return value should be always true (was false before).
                        return Flags.refactorInsetsController() && CompatChanges.isChangeEnabled(
                                ALWAYS_RETURN_TRUE_HIDE_SOFT_INPUT_FROM_WINDOW);
                    }
                    ImeTracker.forLogging().onProgress(statsToken,
                            ImeTracker.PHASE_CLIENT_VIEW_HANDLER_AVAILABLE);

                    if (resultReceiver != null) {
                    final boolean imeReqVisible =
                            (viewRootImpl.getInsetsController().getRequestedVisibleTypes()
                                    & WindowInsets.Type.ime()) != 0;
                    if (resultReceiver != null) {
                        resultReceiver.send(
                                !imeReqVisible ? InputMethodManager.RESULT_UNCHANGED_HIDDEN
                                        : InputMethodManager.RESULT_HIDDEN, null);
@@ -2642,8 +2674,16 @@ public final class InputMethodManager {
                        viewRootImpl.getInsetsController().hide(WindowInsets.Type.ime(),
                                false /* fromIme */, statsToken);
                    }
                    if (!CompatChanges.isChangeEnabled(
                            ALWAYS_RETURN_TRUE_HIDE_SOFT_INPUT_FROM_WINDOW)) {
                        // if the IME was not visible before, the additional hide won't change
                        // anything.
                        return imeReqVisible;
                    }
                return true;
                }
                // Targeting Android Baklava onwards, this method will always return true.
                return CompatChanges.isChangeEnabled(
                        ALWAYS_RETURN_TRUE_HIDE_SOFT_INPUT_FROM_WINDOW);
            } else {
                return IInputMethodManagerGlobalInvoker.hideSoftInput(mClient, windowToken,
                        statsToken, flags, resultReceiver, reason, mAsyncShowHideMethodEnabled);