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

Commit 5a810c84 authored by Ming-Shin Lu's avatar Ming-Shin Lu
Browse files

Don't skip IME animation when backed to Task has no editor focus

When the dialer app previously shows IME, if the user swipe out the
dialer task to launcher or switching task and then navigating back
to dialer task, will see IME surface will fade-out because the
editor view has been cleared the focus.

The issue happens when user taping editText to show keyboard and
the IME show animaion will be skipped.

It could be related the CL[1] that we skip the animation once
when the task snapshot has the IME surface, but we should not
skip IME show animation if the launching task has been cleared
the editor focus.

[1]: I83ffc03119e01da71ad12f8ad8043cf0730dfd50

Fix: 183347520
Bug: 166736352
Test: manual as steps in commit message
Test: atest ImeInsetsSourceConsumerTest#\
      testImeGetAndClearSkipAnimationOnce_expectNoSkip
Change-Id: Ib140801f1ce03b5566e756914f96dacba3ad8892
parent 39217229
Loading
Loading
Loading
Loading
+2 −2
Original line number Original line Diff line number Diff line
@@ -51,8 +51,8 @@ public final class ImeInsetsSourceConsumer extends InsetsSourceConsumer {
    }
    }


    @Override
    @Override
    public void onWindowFocusGained() {
    public void onWindowFocusGained(boolean hasViewFocus) {
        super.onWindowFocusGained();
        super.onWindowFocusGained(hasViewFocus);
        getImm().registerImeConsumer(this);
        getImm().registerImeConsumer(this);
    }
    }


+5 −4
Original line number Original line Diff line number Diff line
@@ -1302,8 +1302,8 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
    /**
    /**
     * Called when current window gains focus.
     * Called when current window gains focus.
     */
     */
    public void onWindowFocusGained() {
    public void onWindowFocusGained(boolean hasViewFocused) {
        getSourceConsumer(ITYPE_IME).onWindowFocusGained();
        getSourceConsumer(ITYPE_IME).onWindowFocusGained(hasViewFocused);
    }
    }


    /**
    /**
@@ -1366,8 +1366,9 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
            final InsetsSourceControl imeControl = consumer != null ? consumer.getControl() : null;
            final InsetsSourceControl imeControl = consumer != null ? consumer.getControl() : null;
            // Skip showing animation once that made by system for some reason.
            // Skip showing animation once that made by system for some reason.
            // (e.g. starting window with IME snapshot)
            // (e.g. starting window with IME snapshot)
            if (imeControl != null && show) {
            if (imeControl != null) {
                skipAnim = imeControl.getAndClearSkipAnimationOnce();
                skipAnim = imeControl.getAndClearSkipAnimationOnce() && show
                        && consumer.hasViewFocusWhenWindowFocusGain();
            }
            }
        }
        }
        applyAnimation(types, show, fromIme, skipAnim);
        applyAnimation(types, show, fromIme, skipAnim);
+11 −1
Original line number Original line Diff line number Diff line
@@ -81,6 +81,11 @@ public class InsetsSourceConsumer {
    private final Supplier<Transaction> mTransactionSupplier;
    private final Supplier<Transaction> mTransactionSupplier;
    private @Nullable InsetsSourceControl mSourceControl;
    private @Nullable InsetsSourceControl mSourceControl;
    private boolean mHasWindowFocus;
    private boolean mHasWindowFocus;

    /**
     * Whether the view has focus returned by {@link #onWindowFocusGained(boolean)}.
     */
    private boolean mHasViewFocusWhenWindowFocusGain;
    private Rect mPendingFrame;
    private Rect mPendingFrame;
    private Rect mPendingVisibleFrame;
    private Rect mPendingVisibleFrame;


@@ -223,8 +228,9 @@ public class InsetsSourceConsumer {
    /**
    /**
     * Called when current window gains focus
     * Called when current window gains focus
     */
     */
    public void onWindowFocusGained() {
    public void onWindowFocusGained(boolean hasViewFocus) {
        mHasWindowFocus = true;
        mHasWindowFocus = true;
        mHasViewFocusWhenWindowFocusGain = hasViewFocus;
    }
    }


    /**
    /**
@@ -238,6 +244,10 @@ public class InsetsSourceConsumer {
        return mHasWindowFocus;
        return mHasWindowFocus;
    }
    }


    boolean hasViewFocusWhenWindowFocusGain() {
        return mHasViewFocusWhenWindowFocusGain;
    }

    boolean applyLocalVisibilityOverride() {
    boolean applyLocalVisibilityOverride() {
        final InsetsSource source = mState.peekSource(mType);
        final InsetsSource source = mState.peekSource(mType);
        final boolean isVisible = source != null ? source.isVisible() : getDefaultVisibility(mType);
        final boolean isVisible = source != null ? source.isVisible() : getDefaultVisibility(mType);
+3 −3
Original line number Original line Diff line number Diff line
@@ -3354,8 +3354,9 @@ public final class ViewRootImpl implements ViewParent,
        }
        }
        // TODO (b/131181940): Make sure this doesn't leak Activity with mActivityConfigCallback
        // TODO (b/131181940): Make sure this doesn't leak Activity with mActivityConfigCallback
        // config changes.
        // config changes.
        final View focusedView = mView != null ? mView.findFocus() : null;
        if (hasWindowFocus) {
        if (hasWindowFocus) {
            mInsetsController.onWindowFocusGained();
            mInsetsController.onWindowFocusGained(focusedView != null /* hasViewFocused */);
        } else {
        } else {
            mInsetsController.onWindowFocusLost();
            mInsetsController.onWindowFocusLost();
        }
        }
@@ -3404,8 +3405,7 @@ public final class ViewRootImpl implements ViewParent,


            // Note: must be done after the focus change callbacks,
            // Note: must be done after the focus change callbacks,
            // so all of the view state is set up correctly.
            // so all of the view state is set up correctly.
            mImeFocusController.onPostWindowFocus(mView != null ? mView.findFocus() : null,
            mImeFocusController.onPostWindowFocus(focusedView, hasWindowFocus, mWindowAttributes);
                    hasWindowFocus, mWindowAttributes);


            if (hasWindowFocus) {
            if (hasWindowFocus) {
                // Clear the forward bit.  We can just do this directly, since
                // Clear the forward bit.  We can just do this directly, since
+46 −12
Original line number Original line Diff line number Diff line
@@ -97,7 +97,7 @@ public class ImeInsetsSourceConsumerTest {


        InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
        InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
            // test if setVisibility can show IME
            // test if setVisibility can show IME
            mImeConsumer.onWindowFocusGained();
            mImeConsumer.onWindowFocusGained(true);
            mController.show(WindowInsets.Type.ime(), true /* fromIme */);
            mController.show(WindowInsets.Type.ime(), true /* fromIme */);
            mController.cancelExistingAnimations();
            mController.cancelExistingAnimations();
            assertTrue(mController.getSourceConsumer(ime.getType()).isRequestedVisible());
            assertTrue(mController.getSourceConsumer(ime.getType()).isRequestedVisible());
@@ -116,7 +116,7 @@ public class ImeInsetsSourceConsumerTest {


        InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
        InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
            // Request IME visible before control is available.
            // Request IME visible before control is available.
            mImeConsumer.onWindowFocusGained();
            mImeConsumer.onWindowFocusGained(true);
            mController.show(WindowInsets.Type.ime(), true /* fromIme */);
            mController.show(WindowInsets.Type.ime(), true /* fromIme */);


            // set control and verify visibility is applied.
            // set control and verify visibility is applied.
@@ -132,24 +132,58 @@ public class ImeInsetsSourceConsumerTest {
    }
    }


    @Test
    @Test
    public void testImeGetAndClearSkipAnimationOnce() {
    public void testImeGetAndClearSkipAnimationOnce_expectSkip() {
        // Expect IME animation will skipped when the IME is visible at first place.
        verifyImeGetAndClearSkipAnimationOnce(true /* hasWindowFocus */, true /* hasViewFocus */,
                true /* expectSkipAnim */);
    }

    @Test
    public void testImeGetAndClearSkipAnimationOnce_expectNoSkip() {
        // Expect IME animation will not skipped if previously no view focused when gained the
        // window focus and requesting the IME visible next time.
        verifyImeGetAndClearSkipAnimationOnce(true /* hasWindowFocus */, false /* hasViewFocus */,
                false /* expectSkipAnim */);
    }

    private void verifyImeGetAndClearSkipAnimationOnce(boolean hasWindowFocus, boolean hasViewFocus,
            boolean expectSkipAnim) {
        InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
        InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
            // Request IME visible before control is available.
            // Request IME visible before control is available.
            mImeConsumer.onWindowFocusGained();
            mImeConsumer.onWindowFocusGained(hasWindowFocus);
            final boolean imeVisible = hasWindowFocus && hasViewFocus;
            if (imeVisible) {
                mController.show(WindowInsets.Type.ime(), true /* fromIme */);
                mController.show(WindowInsets.Type.ime(), true /* fromIme */);
            }


            // set control and verify visibility is applied.
            // set control and verify visibility is applied.
            InsetsSourceControl control = Mockito.spy(
            InsetsSourceControl control = Mockito.spy(
                    new InsetsSourceControl(ITYPE_IME, mLeash, new Point(), Insets.NONE));
                    new InsetsSourceControl(ITYPE_IME, mLeash, new Point(), Insets.NONE));
            // Simulate IME source control set this flag when the target has starting window.
            // Simulate IME source control set this flag when the target has starting window.
            control.setSkipAnimationOnce(true);
            control.setSkipAnimationOnce(true);

            if (imeVisible) {
                // Verify IME applyAnimation should be triggered when control becomes available,
                // and expect skip animation state after getAndClearSkipAnimationOnce invoked.
                mController.onControlsChanged(new InsetsSourceControl[]{ control });
                verify(control).getAndClearSkipAnimationOnce();
                verify(mController).applyAnimation(eq(WindowInsets.Type.ime()),
                        eq(true) /* show */, eq(false) /* fromIme */,
                        eq(expectSkipAnim) /* skipAnim */);
            }

            // If previously hasViewFocus is false, verify when requesting the IME visible next
            // time will not skip animation.
            if (!hasViewFocus) {
                mController.show(WindowInsets.Type.ime(), true);
                mController.onControlsChanged(new InsetsSourceControl[]{ control });
                mController.onControlsChanged(new InsetsSourceControl[]{ control });
                // Verify IME show animation should be triggered when control becomes available and
                // Verify IME show animation should be triggered when control becomes available and
                // the animation will be skipped by getAndClearSkipAnimationOnce invoked.
                // the animation will be skipped by getAndClearSkipAnimationOnce invoked.
                verify(control).getAndClearSkipAnimationOnce();
                verify(control).getAndClearSkipAnimationOnce();
            verify(mController).applyAnimation(
                verify(mController).applyAnimation(eq(WindowInsets.Type.ime()),
                    eq(WindowInsets.Type.ime()), eq(true) /* show */, eq(false) /* fromIme */,
                        eq(true) /* show */, eq(true) /* fromIme */,
                    eq(true) /* skipAnim */);
                        eq(false) /* skipAnim */);
            }
        });
        });
    }
    }
}
}
Loading