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

Commit 7d9eb2b6 authored by Ming-Shin Lu's avatar Ming-Shin Lu Committed by Adrian Roos
Browse files

Fix flickering when switched to the task without IME shown

When switching tasks from the task has IME shown to another task
without showing IME (e.g. youtube apps.), the flickering might happen
because the next activity accidentally received the IME insets attached
in the previous activity.

If the IME is attached to one app window on that time, even the next
app window is behind the IME window, conceptually the window should
not receive the IME insets if the next window is not eligible IME
requester and ready to show IME on top of it.

Fix the logic in InsetsPolicy#adjustVisibilityForIme for the above
case.

Fix: 192337037
Bug: 194746204
Test: atest WindowStateTests#\
        testAdjustImeInsetsVisibilityWhenTaskSwitchIsAnimating
Merged-In: Ib3a1eacedd3763bcf1e6eed82e7efdf01c50b204
Change-Id: Ib3a1eacedd3763bcf1e6eed82e7efdf01c50b204
(cherry picked from commit f4eb0544)
parent 72553d51
Loading
Loading
Loading
Loading
+36 −10
Original line number Diff line number Diff line
@@ -32,6 +32,10 @@ import static android.view.WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_B
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_SHOW_STATUS_BAR;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION;

import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_RECENTS;
import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.StatusBarManager;
@@ -211,7 +215,7 @@ class InsetsPolicy {
    InsetsState getInsetsForWindow(WindowState target) {
        final InsetsState originalState = mStateController.getInsetsForWindow(target);
        final InsetsState state = adjustVisibilityForTransientTypes(originalState);
        return target.mIsImWindow ? adjustVisibilityForIme(state, state == originalState) : state;
        return adjustVisibilityForIme(target, state, state == originalState);
    }

    /**
@@ -241,17 +245,39 @@ class InsetsPolicy {
        return state;
    }

    // Navigation bar insets is always visible to IME.
    private static InsetsState adjustVisibilityForIme(InsetsState originalState,
    private InsetsState adjustVisibilityForIme(WindowState w, InsetsState originalState,
            boolean copyState) {
        if (w.mIsImWindow) {
            // Navigation bar insets is always visible to IME.
            final InsetsSource originalNavSource = originalState.peekSource(ITYPE_NAVIGATION_BAR);
            if (originalNavSource != null && !originalNavSource.isVisible()) {
            final InsetsState state = copyState ? new InsetsState(originalState) : originalState;
                final InsetsState state = copyState ? new InsetsState(originalState)
                        : originalState;
                final InsetsSource navSource = new InsetsSource(originalNavSource);
                navSource.setVisible(true);
                state.addSource(navSource);
                return state;
            }
        } else if (w.mActivityRecord != null && !w.mActivityRecord.mLastImeShown) {
            // During switching tasks with gestural navigation, if the IME is attached to
            // one app window on that time, even the next app window is behind the IME window,
            // conceptually the window should not receive the IME insets if the next window is
            // not eligible IME requester and ready to show IME on top of it.
            final boolean shouldImeAttachedToApp = mDisplayContent.shouldImeAttachedToApp();
            final InsetsSource originalImeSource = originalState.peekSource(ITYPE_IME);

            if (originalImeSource != null && shouldImeAttachedToApp
                    && (w.isAnimating(PARENTS | TRANSITION, ANIMATION_TYPE_RECENTS)
                            || !w.getRequestedVisibility(ITYPE_IME))) {
                final InsetsState state = copyState ? new InsetsState(originalState)
                        : originalState;

                final InsetsSource imeSource = new InsetsSource(originalImeSource);
                imeSource.setVisible(false);
                state.addSource(imeSource);
                return state;
            }
        }
        return originalState;
    }

+33 −0
Original line number Diff line number Diff line
@@ -53,6 +53,9 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.server.wm.DisplayContent.IME_TARGET_CONTROL;
import static com.android.server.wm.DisplayContent.IME_TARGET_LAYERING;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_RECENTS;
import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
import static com.android.server.wm.WindowContainer.SYNC_STATE_WAITING_FOR_DRAW;

import static com.google.common.truth.Truth.assertThat;
@@ -891,6 +894,36 @@ public class WindowStateTests extends WindowTestsBase {
        assertTrue(mAppWindow.getInsetsState().getSourceOrDefaultVisibility(ITYPE_NAVIGATION_BAR));
    }

    @UseTestDisplay(addWindows = W_INPUT_METHOD)
    @Test
    public void testAdjustImeInsetsVisibilityWhenTaskSwitchIsAnimating() {
        final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
        final WindowState app2 = createWindow(null, TYPE_APPLICATION, "app2");
        final InsetsStateController controller = mDisplayContent.getInsetsStateController();
        controller.getImeSourceProvider().setWindow(mImeWindow, null, null);

        // Simulate app requests IME with updating all windows Insets State when IME is above app.
        mDisplayContent.setImeLayeringTarget(app);
        mDisplayContent.setImeInputTarget(app);
        assertTrue(mDisplayContent.shouldImeAttachedToApp());
        controller.getImeSourceProvider().scheduleShowImePostLayout(app);
        controller.getImeSourceProvider().getSource().setVisible(true);
        controller.updateAboveInsetsState(mImeWindow, false);

        // Simulate task switching animation happens when switching app to app2.
        spyOn(app);
        spyOn(app2);
        doReturn(true).when(app).isAnimating(PARENTS | TRANSITION, ANIMATION_TYPE_RECENTS);
        doReturn(true).when(app2).isAnimating(PARENTS | TRANSITION, ANIMATION_TYPE_RECENTS);
        app.mActivityRecord.mLastImeShown = true;

        // Verify the IME insets is visible on app, but not for app2 during task animating.
        InsetsState stateApp = app.getInsetsState();
        InsetsState stateApp2 = app2.getInsetsState();
        assertTrue(stateApp.getSource(ITYPE_IME).isVisible());
        assertFalse(stateApp2.getSource(ITYPE_IME).isVisible());
    }

    @UseTestDisplay(addWindows = { W_ACTIVITY })
    @Test
    public void testUpdateImeControlTargetWhenLeavingMultiWindow() {