From d4d90ac8a88e601a4418c44fe45563070581c19b Mon Sep 17 00:00:00 2001 From: Ming-Shin Lu Date: Wed, 11 Nov 2020 13:17:18 +0800 Subject: [PATCH] Better IME transition while switching app with recents (5/N) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- .../com/android/server/wm/DisplayContent.java | 86 ++++--------------- .../android/server/wm/InputConsumerImpl.java | 2 +- .../com/android/server/wm/InputMonitor.java | 2 +- .../server/wm/RecentsAnimationController.java | 49 +++++------ .../server/wm/DisplayContentTests.java | 10 ++- 5 files changed, 44 insertions(+), 105 deletions(-) diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 7e09e63781815..bf05f5067328a 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -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; diff --git a/services/core/java/com/android/server/wm/InputConsumerImpl.java b/services/core/java/com/android/server/wm/InputConsumerImpl.java index e35621a84b37d..4d0c75c61aabd 100644 --- a/services/core/java/com/android/server/wm/InputConsumerImpl.java +++ b/services/core/java/com/android/server/wm/InputConsumerImpl.java @@ -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); diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java index cc77cfa1e8e5b..c2331a00193a6 100644 --- a/services/core/java/com/android/server/wm/InputMonitor.java +++ b/services/core/java/com/android/server/wm/InputMonitor.java @@ -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; } } diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java index 00b6cebbe32db..9b92a811892ea 100644 --- a/services/core/java/com/android/server/wm/RecentsAnimationController.java +++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java @@ -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) { diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java index b27d016beb8d9..83282a5b8e5ab 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java @@ -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)