From e42507dda69de1e27e5dda384367e3c5584e94ab Mon Sep 17 00:00:00 2001 From: Ming-Shin Lu Date: Thu, 10 Feb 2022 21:25:20 +0800 Subject: [PATCH] [ShellTransition]: add WMS#setRecentsAppBehindSystemBars for recents For legacy recents animation, launcher side use RecentsAnimationController#setAnimationTargetsBehindSystemBars to callback if the animating recents task target is valid to affect system bar apparence according the gesture threadshold. if the animating recents task is behind system bar, means the gesture threshod not yet passed and the task still can affect system bar apparence, and vice-versa. with shell-transition, since launcher triggers recents animation by normal startActivity without initiating RecentsAnimationController with startRecentsActivity, the launcher will be top-resumed (but the surface hierarchy the recents task is still be top-most) when starting gesture on navbar, in this case, launcher can affect the system appearence even though launcher is behind systembar. beside, previously we also rely on setAnimationTargetsBehindSystemBars to hide soft-keyboard in multi-windowing mode when quick-swich tasks, without this, soft-keyboard will keep visible when swiping up to recents. As the result, we needs to backport the most logic of setAnimationTargetsBehindSystemBars for fixing the above cases, the only difference is we renaming the method with setRecentsAppBehindSystemBars(behindSystemBars) to only set recents app can affect systembar apparence, and placing hide IME/IME icon logic into handleLegacyRecentsStartBehavior when starting recents animation. Fix: 215504556 Test: manual as below CUJs with enabling shell-transition: CUJ1: 1) Launching a app with showing IME 2) Quick-switching to the next task or swiping task to home 3) Expect no navbar icon flickering CUJ2: 1) With entering split-screen and showing IME on the split task 2) Swipe up to recents 3) Expect IME shound be hidden CUJ3: 1) Launching a app 2) Hammer tapping on the navigation bar 3) Expect the staus bar icon won't blinking Change-Id: I552eea738aed5566eb897bb9c68507e83ac43e1d --- core/java/android/view/IWindowManager.aidl | 6 ++++ .../android/view/WindowManagerGlobal.java | 8 +++++ .../com/android/server/wm/Transition.java | 35 ++++++++++++++++++- .../server/wm/TransitionController.java | 8 +++-- .../server/wm/WindowManagerService.java | 21 +++++++++++ 5 files changed, 75 insertions(+), 3 deletions(-) diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl index 53b842a0f3a2c..f8a1b45a1c75c 100644 --- a/core/java/android/view/IWindowManager.aidl +++ b/core/java/android/view/IWindowManager.aidl @@ -955,4 +955,10 @@ interface IWindowManager * @hide */ Bitmap snapshotTaskForRecents(int taskId); + + /** + * Informs the system whether the recents app is currently behind the system bars. If so, + * means the recents app can control the SystemUI flags, and vice-versa. + */ + void setRecentsAppBehindSystemBars(boolean behindSystemBars); } diff --git a/core/java/android/view/WindowManagerGlobal.java b/core/java/android/view/WindowManagerGlobal.java index 93cb0dd7a2348..2dc5fbd5439f3 100644 --- a/core/java/android/view/WindowManagerGlobal.java +++ b/core/java/android/view/WindowManagerGlobal.java @@ -752,6 +752,14 @@ public final class WindowManagerGlobal { public void removeWindowlessRoot(ViewRootImpl impl) { synchronized (mLock) { mWindowlessRoots.remove(impl); + } + } + + public void setRecentsAppBehindSystemBars(boolean behindSystemBars) { + try { + getWindowManagerService().setRecentsAppBehindSystemBars(behindSystemBars); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); } } } diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java index 0f5828c7efb02..5bbf044379e9d 100644 --- a/services/core/java/com/android/server/wm/Transition.java +++ b/services/core/java/com/android/server/wm/Transition.java @@ -80,9 +80,12 @@ import android.window.TransitionInfo; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.graphics.ColorUtils; +import com.android.internal.inputmethod.SoftInputShowHideReason; import com.android.internal.protolog.ProtoLogGroup; import com.android.internal.protolog.common.ProtoLog; import com.android.internal.util.function.pooled.PooledLambda; +import com.android.server.LocalServices; +import com.android.server.inputmethod.InputMethodManagerInternal; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -552,14 +555,24 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe asyncRotationController.onTransitionFinished(); } if (mTransientLaunches != null) { + InsetsControlTarget prevImeTarget = dc.getImeTarget( + DisplayContent.IME_TARGET_CONTROL); + InsetsControlTarget newImeTarget = null; // Transient-launch activities cannot be IME target (WindowState#canBeImeTarget), // so re-compute in case the IME target is changed after transition. for (int t = 0; t < mTransientLaunches.size(); ++t) { if (mTransientLaunches.keyAt(t).getDisplayContent() == dc) { - dc.computeImeTarget(true /* updateImeTarget */); + newImeTarget = dc.computeImeTarget(true /* updateImeTarget */); break; } } + if (mRecentsDisplayId != INVALID_DISPLAY && prevImeTarget == newImeTarget) { + // Restore IME icon only when moving the original app task to front from + // recents, in case IME icon may missing if the moving task has already been + // the current focused task. + InputMethodManagerInternal.get().updateImeWindowStatus( + false /* disableImeIcon */); + } } dc.handleCompleteDeferredRemoval(); } @@ -781,6 +794,26 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe } } + // Hiding IME/IME icon when starting quick-step with resents animation. + if (!mTargetDisplays.get(mRecentsDisplayId).isImeAttachedToApp()) { + // 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) + final InputMethodManagerInternal inputMethodManagerInternal = + LocalServices.getService(InputMethodManagerInternal.class); + if (inputMethodManagerInternal != null) { + inputMethodManagerInternal.hideCurrentInputMethod( + SoftInputShowHideReason.HIDE_RECENTS_ANIMATION); + } + } else { + // Disable IME icon explicitly when IME attached to the app in case + // IME icon might flickering while swiping to the next app task still + // in animating before the next app window focused, or IME icon + // persists on the bottom when swiping the task to recents. + InputMethodManagerInternal.get().updateImeWindowStatus( + true /* disableImeIcon */); + } + // The rest of this function handles nav-bar reparenting if (!dc.getDisplayPolicy().shouldAttachNavBarToAppDuringTransition() diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java index b0532c2a15520..1912aec5109ca 100644 --- a/services/core/java/com/android/server/wm/TransitionController.java +++ b/services/core/java/com/android/server/wm/TransitionController.java @@ -16,7 +16,6 @@ package com.android.server.wm; -import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; import static android.view.WindowManager.TRANSIT_CHANGE; import static android.view.WindowManager.TRANSIT_CLOSE; import static android.view.WindowManager.TRANSIT_FLAG_IS_RECENTS; @@ -514,8 +513,13 @@ class TransitionController { // TODO(b/188669821): Remove once legacy recents behavior is moved to shell. // Also interpret HOME transient launch as recents - if (activity.getActivityType() == ACTIVITY_TYPE_HOME) { + if (activity.isActivityTypeHomeOrRecents()) { mCollectingTransition.addFlag(TRANSIT_FLAG_IS_RECENTS); + // When starting recents animation, we assume the recents activity is behind the app + // task and should not affect system bar appearance, + // until WMS#setRecentsAppBehindSystemBars be called from launcher when passing + // the gesture threshold. + activity.getTask().setCanAffectSystemUiFlags(false); } } diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 5db72ee722d78..bf946ae1312e9 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -25,6 +25,7 @@ import static android.Manifest.permission.MODIFY_TOUCH_MODE_STATE; import static android.Manifest.permission.READ_FRAME_BUFFER; import static android.Manifest.permission.REGISTER_WINDOW_MANAGER_LISTENERS; import static android.Manifest.permission.RESTRICTED_VR_ACCESS; +import static android.Manifest.permission.START_TASKS_FROM_RECENTS; import static android.Manifest.permission.WRITE_SECURE_SETTINGS; import static android.app.ActivityManagerInternal.ALLOW_FULL_ONLY; import static android.app.ActivityManagerInternal.ALLOW_NON_FULL; @@ -8974,4 +8975,24 @@ public class WindowManagerService extends IWindowManager.Stub return Bitmap.wrapHardwareBuffer(taskSnapshot.getHardwareBuffer(), taskSnapshot.getColorSpace()); } + + @Override + public void setRecentsAppBehindSystemBars(boolean behindSystemBars) { + if (!checkCallingPermission(START_TASKS_FROM_RECENTS, "setRecentsAppBehindSystemBars()")) { + throw new SecurityException("Requires START_TASKS_FROM_RECENTS permission"); + } + final long token = Binder.clearCallingIdentity(); + try { + synchronized (mGlobalLock) { + final Task recentsApp = mRoot.getTask(task -> task.isActivityTypeHomeOrRecents() + && task.getTopVisibleActivity() != null); + if (recentsApp != null) { + recentsApp.getTask().setCanAffectSystemUiFlags(behindSystemBars); + mWindowPlacerLocked.requestTraversal(); + } + } + } finally { + Binder.restoreCallingIdentity(token); + } + } }