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

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

Better IME transition while switching app with recents (5/N)

With CL[1], the IME surface will have snapshot when transtioning to the
next task.
 
We can now remove the previous hacky logics like dedicates to keep the
previous IME and make the true IME target while task transitioning.
And, move the call of updateImeParent() from
DC#setInputMethodTarget to DC#updateImeControlTarget,
 
to ensure that the reparenting of IME insets source control can be done
when the IME insets visiblity settled down after the IME insets control
changed, to prevent unnecessary flickering during the time period between
reparenting IME parent and start IME insets animation.

Also, modify UpdateInputForAllWindowsConsumer#accept to let
mRecentsAnimationInputConsumer can be above IME target activity which to
prevent mis-touch or keystoke may left while quickly taping
soft-keyboard during swping up to recents.

[1]: I6bef36c779a28777408576f57e5d1c67d5d48e3f
 
Bug: 166736352
Bug: 153145997
Bug: 172815805
Bug: 174222049
Bug: 167604724

Test: manual as below steps:
   0) Device with 2-buttons or 3-buttons navbar mode.
   1) Launching an app with focusing an editor to show soft-keyboard.
   2) Pressing home key and observe if the IME screenshot exists during
      closing activity transition.
Test: manual as below steps:
   1) Launching an app with focusing an editor to show soft-keyboard.
   2) Tap IME settings icon to launch IME settings from Gboard.
   3) Observe if IME screenshot exists or soft-keyboard hided on the closing
      activity during transiting to IME settings.
   4) Press back key or swipe back to app task, observe if the keyboard
      showing animation occurs on the app task without janking.
Test manual as below steps:
   1) Launching an app with focusing an editor to show soft-keyboard.
   2) Swiping this app task to recents
   3) Quickly touch keyboard when leaving the finger from the screen.
   4) Expect there is no keystroke left or see the mistouch in keyboard
      while the task view into recents.

Change-Id: Ia6722e1cbccd7adc8aed1828265f6fa4df78df63
parent 4964839e
Loading
Loading
Loading
Loading
+15 −71
Original line number Diff line number Diff line
@@ -3536,7 +3536,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
            if (updateImeTarget) {
                if (DEBUG_INPUT_METHOD) Slog.w(TAG_WM, "Moving IM target from "
                        + mImeLayeringTarget + " to null since mInputMethodWindow is null");
                setImeLayeringTarget(null, mImeLayeringTargetWaitingAnim);
                setImeLayeringTargetInner(null);
            }
            return null;
        }
@@ -3553,41 +3553,9 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
        mUpdateImeTarget = updateImeTarget;
        WindowState target = getWindow(mComputeImeTargetPredicate);

        // Keeps the IME target with the last window while swiping up to recents to prevent
        // flickering due to IME hide animation on top of recents.
        // TODO(b/166736352): This logic should go away once we switch over target immediately
        //  and do the screenshot to preserve IME on disappearing target
        if (target != null && curTarget != null && target.isActivityTypeHome()
                && curTarget.getInsetsState().getSource(ITYPE_IME).isVisible()) {
            return curTarget;
        }

        // Yet more tricksyness!  If this window is a "starting" window, we do actually want
        // to be on top of it, but it is not -really- where input will go. So look down below
        // for a real window to target...
        if (target != null && target.mAttrs.type == TYPE_APPLICATION_STARTING) {
            final ActivityRecord activity = target.mActivityRecord;
            if (activity != null) {
                final WindowState betterTarget = activity.getImeTargetBelowWindow(target);
                if (betterTarget != null) {
                    target = betterTarget;
                }
            }
        }

        if (DEBUG_INPUT_METHOD && updateImeTarget) Slog.v(TAG_WM,
                "Proposed new IME target: " + target + " for display: " + getDisplayId());

        // Now, a special case -- if the last target's window is in the process of exiting, but
        // not removed, keep on the last target to avoid IME flicker. The exception is if the
        // current target is home since we want opening apps to become the IME target right away.
        if (curTarget != null && !curTarget.mRemoved && curTarget.isDisplayed()
                && curTarget.isClosing() && !curTarget.isActivityTypeHome()) {
            if (DEBUG_INPUT_METHOD) Slog.v(TAG_WM, "Not changing target till current window is"
                    + " closing and not removed");
            return curTarget;
        }

        if (DEBUG_INPUT_METHOD) Slog.v(TAG_WM, "Desired input method target=" + target
                + " updateImeTarget=" + updateImeTarget);

@@ -3596,42 +3564,16 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
                if (DEBUG_INPUT_METHOD) Slog.w(TAG_WM, "Moving IM target from " + curTarget
                        + " to null." + (SHOW_STACK_CRAWLS ? " Callers="
                        + Debug.getCallers(4) : ""));
                setImeLayeringTarget(null, mImeLayeringTargetWaitingAnim);
                setImeLayeringTargetInner(null);
            }

            return null;
        }

        if (updateImeTarget) {
            ActivityRecord activity = curTarget == null ? null : curTarget.mActivityRecord;
            if (activity != null) {

                // Now some fun for dealing with window animations that modify the Z order. We need
                // to look at all windows below the current target that are in this app, finding the
                // highest visible one in layering.
                WindowState highestTarget = null;
                if (activity.isAnimating(PARENTS | TRANSITION)) {
                    highestTarget = activity.getHighestAnimLayerWindow(curTarget);
                }

                if (highestTarget != null) {
                    if (DEBUG_INPUT_METHOD) {
                        Slog.v(TAG_WM, mAppTransition + " " + highestTarget + " animating="
                                + highestTarget.isAnimating(TRANSITION | PARENTS));
                    }

                    if (mAppTransition.isTransitionSet()) {
                        // If we are currently setting up for an animation, hold everything until we
                        // can find out what will happen.
                        setImeLayeringTarget(highestTarget, true);
                        return highestTarget;
                    }
                }
            }

            if (DEBUG_INPUT_METHOD) Slog.w(TAG_WM, "Moving IM target from " + curTarget + " to "
                    + target + (SHOW_STACK_CRAWLS ? " Callers=" + Debug.getCallers(4) : ""));
            setImeLayeringTarget(target, false);
            setImeLayeringTargetInner(target);
        }

        return target;
@@ -3733,11 +3675,9 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
     * Sets the window the IME is on top of.
     * @param target window to place the IME surface on top of. If {@code null}, the IME will be
     *               placed at its parent's surface.
     * @param targetWaitingAnim if {@code true}, hold off on modifying the animation layer of
     *                          the target.
     */
    private void setImeLayeringTarget(@Nullable WindowState target, boolean targetWaitingAnim) {
        if (target == mImeLayeringTarget && mImeLayeringTargetWaitingAnim == targetWaitingAnim) {
    private void setImeLayeringTargetInner(@Nullable WindowState target) {
        if (target == mImeLayeringTarget) {
            return;
        }
        // Prepare the IME screenshot for the last IME target when its task is applying app
@@ -3750,7 +3690,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp

        ProtoLog.i(WM_DEBUG_IME, "setInputMethodTarget %s", target);
        mImeLayeringTarget = target;
        mImeLayeringTargetWaitingAnim = targetWaitingAnim;

        // 1. Reparent the IME container window to the target root DA to get the correct bounds and
        // config. (Only happens when the target window is in a different root DA)
@@ -3762,13 +3701,12 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
                targetRoot.placeImeContainer(mImeWindowsContainer);
            }
        }
        // 2. Reparent the IME container surface to either the input target app, or the IME window
        // parent.
        updateImeParent();
        // 3. Assign window layers based on the IME surface parent to make sure it is on top of the
        // 2. Assign window layers based on the IME surface parent to make sure it is on top of the
        // app.
        assignWindowLayers(true /* setLayoutNeeded */);
        // 4. Update the IME control target to apply any inset change and animation.
        // 3. Update the IME control target to apply any inset change and animation.
        // 4. Reparent the IME container surface to either the input target app, or the IME window
        // parent.
        updateImeControlTarget();
    }

@@ -3898,8 +3836,14 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
    }

    void updateImeControlTarget() {
        InsetsControlTarget prevImeControlTarget = mImeControlTarget;
        mImeControlTarget = computeImeControlTarget();
        mInsetsStateController.onImeControlTargetChanged(mImeControlTarget);
        // Update Ime parent when IME insets leash created, which is the best time that default
        // IME visibility has been settled down after IME control target changed.
        if (prevImeControlTarget != mImeControlTarget) {
            updateImeParent();
        }

        final WindowState win = InsetsControlTarget.asWindowOrNull(mImeControlTarget);
        final IBinder token = win != null ? win.mClient.asBinder() : null;
+1 −1
Original line number Diff line number Diff line
@@ -132,7 +132,7 @@ class InputConsumerImpl implements IBinder.DeathRecipient {
        t.hide(mInputSurface);
    }

    void show(SurfaceControl.Transaction t, WindowState w) {
    void show(SurfaceControl.Transaction t, WindowContainer w) {
        t.show(mInputSurface);
        t.setInputWindowInfo(mInputSurface, mWindowHandle);
        t.setRelativeLayer(mInputSurface, w.getSurfaceControl(), 1);
+1 −1
Original line number Diff line number Diff line
@@ -516,7 +516,7 @@ final class InputMonitor {
            if (mAddRecentsAnimationInputConsumerHandle && shouldApplyRecentsInputConsumer) {
                if (recentsAnimationController.updateInputConsumerForApp(
                        mRecentsAnimationInputConsumer.mWindowHandle)) {
                    mRecentsAnimationInputConsumer.show(mInputTransaction, w);
                    mRecentsAnimationInputConsumer.show(mInputTransaction, w.mActivityRecord);
                    mAddRecentsAnimationInputConsumerHandle = false;
                }
            }
+21 −28
Original line number Diff line number Diff line
@@ -282,6 +282,25 @@ public class RecentsAnimationController implements DeathRecipient {
                            task.setCanAffectSystemUiFlags(behindSystemBars);
                        }
                    }
                    if (!behindSystemBars) {
                        // Make sure to update the correct IME parent in case that the IME parent
                        // may be computed as display layer when re-layout window happens during
                        // rotation but there is intermediate state that the bounds of task and
                        // the IME target's activity is not the same during rotating.
                        mDisplayContent.updateImeParent();

                        // Hiding IME if IME window is not attached to app.
                        // Since some windowing mode is not proper to snapshot Task with IME window
                        // while the app transitioning to the next task (e.g. split-screen mode)
                        if (!mDisplayContent.isImeAttachedToApp()) {
                            final InputMethodManagerInternal inputMethodManagerInternal =
                                    LocalServices.getService(InputMethodManagerInternal.class);
                            if (inputMethodManagerInternal != null) {
                                inputMethodManagerInternal.hideCurrentInputMethod(
                                        SoftInputShowHideReason.HIDE_RECENTS_ANIMATION);
                            }
                        }
                    }
                    mService.mWindowPlacerLocked.requestTraversal();
                }
            } finally {
@@ -299,7 +318,6 @@ public class RecentsAnimationController implements DeathRecipient {
                    if (mCanceled) {
                        return;
                    }

                    mInputConsumerEnabled = enabled;
                    final InputMonitor inputMonitor = mDisplayContent.getInputMonitor();
                    inputMonitor.updateInputWindowsLw(true /*force*/);
@@ -310,34 +328,9 @@ public class RecentsAnimationController implements DeathRecipient {
            }
        }

        // TODO(b/166736352): Remove this method without the need to expose to launcher.
        @Override
        public void hideCurrentInputMethod() {
            final long token = Binder.clearCallingIdentity();
            try {
                synchronized (mService.getWindowManagerLock()) {
                    // Make sure to update the correct IME parent in case that the IME parent may
                    // be computed as display layer when re-layout window happens during rotation
                    // but there is intermediate state that the bounds of task and the IME
                    // target's activity is not the same during rotating.
                    mDisplayContent.updateImeParent();

                    // Ignore hiding IME if IME window is attached to app.
                    // Since we would like to snapshot Task with IME window while transitioning
                    // to recents.
                    if (mDisplayContent.isImeAttachedToApp()) {
                        return;
                    }
                }
                final InputMethodManagerInternal inputMethodManagerInternal =
                        LocalServices.getService(InputMethodManagerInternal.class);
                if (inputMethodManagerInternal != null) {
                    inputMethodManagerInternal.hideCurrentInputMethod(
                            SoftInputShowHideReason.HIDE_RECENTS_ANIMATION);
                }
            } finally {
                Binder.restoreCallingIdentity(token);
            }
        }
        public void hideCurrentInputMethod() { }

        @Override
        public void setDeferCancelUntilNextTransition(boolean defer, boolean screenshot) {
+6 −4
Original line number Diff line number Diff line
@@ -304,22 +304,24 @@ public class DisplayContentTests extends WindowTestsBase {
        assertEquals(startingWin, imeTarget);
        startingWin.mHidden = false;

        // Verify that an app window launching behind the starting window becomes the target
        // Verify that the starting window still be an ime target even an app window launching
        // behind it.
        final WindowState appWin = createWindow(null, TYPE_BASE_APPLICATION, activity, "appWin");
        appWin.setHasSurface(true);
        assertTrue(appWin.canBeImeTarget());

        imeTarget = mDisplayContent.computeImeTarget(false /* updateImeTarget */);
        assertEquals(appWin, imeTarget);
        assertEquals(startingWin, imeTarget);
        appWin.mHidden = false;

        // Verify that an child window can be an ime target even behind a launching app window
        // Verify that the starting window still be an ime target even the child window behind a
        // launching app window
        final WindowState childWin = createWindow(appWin,
                TYPE_APPLICATION_ATTACHED_DIALOG, "childWin");
        childWin.setHasSurface(true);
        assertTrue(childWin.canBeImeTarget());
        imeTarget = mDisplayContent.computeImeTarget(false /* updateImeTarget */);
        assertEquals(childWin, imeTarget);
        assertEquals(startingWin, imeTarget);
    }

    @UseTestDisplay(addAllCommonWindows = true)