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

Commit 5d6053e4 authored by Johannes Gallmann's avatar Johannes Gallmann
Browse files

Disable IME predictive back animation in multi-window mode

In multi-window mode, the IME is controlled by DisplayImeController instead of the regular InsetsController. Therefore we must disable the InsetsController animation in that case. We should implement the animation in DisplayImeController instead for that case.

Bug: 346726115
Flag: android.view.inputmethod.predictive_back_ime
Test: Manual, i.e. verifying that no interactions happen with InsetsController, predictive animation is not played but IME dismissing works as usual in multi-window mode.
Test: atest ImeBackAnimationControllerTest
Change-Id: Iff275d7dfd1c920b246034f8dc678cd27ecacc0f
parent 57148ac6
Loading
Loading
Loading
Loading
+22 −11
Original line number Diff line number Diff line
@@ -27,6 +27,7 @@ import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.WindowConfiguration;
import android.graphics.Insets;
import android.util.Log;
import android.view.animation.BackGestureInterpolator;
@@ -137,9 +138,10 @@ public class ImeBackAnimationController implements OnBackAnimationCallback {
    @Override
    public void onBackInvoked() {
        if (!isBackAnimationAllowed() || !mIsPreCommitAnimationInProgress) {
            // play regular hide animation if back-animation is not allowed or if insets control has
            // been cancelled by the system (this can happen in split screen for example)
            mInsetsController.hide(ime());
            // play regular hide animation if predictive back-animation is not allowed or if insets
            // control has been cancelled by the system. This can happen in multi-window mode for
            // example (i.e. split-screen or activity-embedding)
            notifyHideIme();
            return;
        }
        startPostCommitAnim(/*hideIme*/ true);
@@ -209,6 +211,11 @@ public class ImeBackAnimationController implements OnBackAnimationCallback {
        if (triggerBack) {
            mInsetsController.setPredictiveBackImeHideAnimInProgress(true);
            notifyHideIme();
            // requesting IME as invisible during post-commit
            mInsetsController.setRequestedVisibleTypes(0, ime());
            // Changes the animation state. This also notifies RootView of changed insets, which
            // causes it to reset its scrollY to 0f (animated) if it was panned
            mInsetsController.onAnimationStateChanged(ime(), /*running*/ true);
        }
        if (mStartRootScrollY != 0 && !triggerBack) {
            // This causes RootView to update its scroll back to the panned position
@@ -228,12 +235,6 @@ public class ImeBackAnimationController implements OnBackAnimationCallback {
        // the IME away
        mInsetsController.getHost().getInputMethodManager()
                .notifyImeHidden(mInsetsController.getHost().getWindowToken(), statsToken);

        // requesting IME as invisible during post-commit
        mInsetsController.setRequestedVisibleTypes(0, ime());
        // Changes the animation state. This also notifies RootView of changed insets, which causes
        // it to reset its scrollY to 0f (animated) if it was panned
        mInsetsController.onAnimationStateChanged(ime(), /*running*/ true);
    }

    private void reset() {
@@ -254,8 +255,18 @@ public class ImeBackAnimationController implements OnBackAnimationCallback {
    }

    private boolean isBackAnimationAllowed() {
        // back animation is allowed in all cases except when softInputMode is adjust_resize AND
        // there is no app-registered WindowInsetsAnimationCallback AND edge-to-edge is not enabled.

        if (mViewRoot.mContext.getResources().getConfiguration().windowConfiguration
                .getWindowingMode() == WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW) {
            // TODO(b/346726115) enable predictive back animation in multi-window mode in
            //  DisplayImeController
            return false;
        }

        // otherwise, the predictive back animation is allowed in all cases except when
        // 1. softInputMode is adjust_resize AND
        // 2. there is no app-registered WindowInsetsAnimationCallback AND
        // 3. edge-to-edge is not enabled.
        return (mViewRoot.mWindowAttributes.softInputMode & SOFT_INPUT_MASK_ADJUST)
                != SOFT_INPUT_ADJUST_RESIZE
                || (mViewRoot.mView != null && mViewRoot.mView.hasWindowInsetsAnimationCallback())
+28 −5
Original line number Diff line number Diff line
@@ -34,6 +34,7 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.testng.Assert.assertEquals;

import android.app.WindowConfiguration;
import android.content.Context;
import android.graphics.Insets;
import android.platform.test.annotations.Presubmit;
@@ -102,6 +103,8 @@ public class ImeBackAnimationControllerTest {
            mViewRoot.setOnContentApplyWindowInsetsListener(
                    mock(Window.OnContentApplyWindowInsetsListener.class));
            mBackAnimationController = new ImeBackAnimationController(mViewRoot, mInsetsController);
            mViewRoot.mContext.getResources().getConfiguration().windowConfiguration
                    .setWindowingMode(WindowConfiguration.WINDOWING_MODE_FULLSCREEN);

            when(mWindowInsetsAnimationController.getHiddenStateInsets()).thenReturn(Insets.NONE);
            when(mWindowInsetsAnimationController.getShownStateInsets()).thenReturn(IME_INSETS);
@@ -156,8 +159,28 @@ public class ImeBackAnimationControllerTest {
        mBackAnimationController.onBackProgressed(new BackEvent(100f, 0f, 0.5f, EDGE_LEFT));
        // commit back gesture
        mBackAnimationController.onBackInvoked();
        // verify that InsetsController#hide is called
        verify(mInsetsController, times(1)).hide(ime());
        // verify that InputMethodManager#notifyImeHidden is called (which is the case whenever
        // getInputMethodManager is called from ImeBackAnimationController)
        verify(mViewRootInsetsControllerHost, times(1)).getInputMethodManager();
        // verify that ImeBackAnimationController does not take control over IME insets
        verify(mInsetsController, never()).controlWindowInsetsAnimation(anyInt(), any(), any(),
                anyBoolean(), anyLong(), any(), anyInt(), anyBoolean());
    }

    @Test
    public void testMultiWindowModeNotPlayingAnim() {
        // setup ViewRoot with WINDOWING_MODE_MULTI_WINDOW
        mViewRoot.mContext.getResources().getConfiguration().windowConfiguration.setWindowingMode(
                WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW);
        // start back gesture
        mBackAnimationController.onBackStarted(new BackEvent(0f, 0f, 0f, EDGE_LEFT));
        // progress back gesture
        mBackAnimationController.onBackProgressed(new BackEvent(100f, 0f, 0.5f, EDGE_LEFT));
        // commit back gesture
        mBackAnimationController.onBackInvoked();
        // verify that InputMethodManager#notifyImeHidden is called (which is the case whenever
        // getInputMethodManager is called from ImeBackAnimationController)
        verify(mViewRootInsetsControllerHost, times(1)).getInputMethodManager();
        // verify that ImeBackAnimationController does not take control over IME insets
        verify(mInsetsController, never()).controlWindowInsetsAnimation(anyInt(), any(), any(),
                anyBoolean(), anyLong(), any(), anyInt(), anyBoolean());
@@ -277,9 +300,9 @@ public class ImeBackAnimationControllerTest {

            // commit back gesture
            mBackAnimationController.onBackInvoked();

            // verify that InsetsController#hide is called
            verify(mInsetsController, times(1)).hide(ime());
            // verify that InputMethodManager#notifyImeHidden is called (which is the case whenever
            // getInputMethodManager is called from ImeBackAnimationController)
            verify(mViewRootInsetsControllerHost, times(1)).getInputMethodManager();
        });
    }