From aeb52f75d085767141e6ffe72e180adec6f6a43e Mon Sep 17 00:00:00 2001 From: Evan Rosky Date: Wed, 28 Jul 2021 15:54:46 -0700 Subject: [PATCH] Add an inputconsumer override method for modern recents transitions Shell transitions doesn't use RecentsAnimationController anymore. Instead, its more structured to just inform inputmonitor when it is and isn't animating recents. InputMonitor then can use this extra information to reroute focus to the recents input consumer at the right times. Unlike legacy transitions, recents will actually be on top and get preferred focus when active. However, the live tile will still be drawn above it. This means we need to also provide InputMonitor with the live-tile activity (the "reference" activity) so it can place the recents inputconsumer at the right z-order. Bug: 193565597 Test: atest ReOpenImeWindowTest Change-Id: Iad37888789e14bc049b1a52dd5142eb6c3ea2e99 --- .../com/android/server/wm/InputMonitor.java | 41 +++++++++++- .../com/android/server/wm/Transition.java | 65 +++++++++++++++++-- 2 files changed, 99 insertions(+), 7 deletions(-) diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java index d819557006a63..609f3cd4337cc 100644 --- a/services/core/java/com/android/server/wm/InputMonitor.java +++ b/services/core/java/com/android/server/wm/InputMonitor.java @@ -47,6 +47,7 @@ import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_INPUT; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; import static com.android.server.wm.WindowManagerService.LOGTAG_INPUT_FOCUS; +import android.annotation.Nullable; import android.graphics.Rect; import android.graphics.Region; import android.os.Handler; @@ -66,6 +67,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.protolog.common.ProtoLog; import java.io.PrintWriter; +import java.lang.ref.WeakReference; import java.util.Set; import java.util.function.Consumer; @@ -99,6 +101,15 @@ final class InputMonitor { */ private final ArrayMap mInputConsumers = new ArrayMap(); + /** + * Set when recents (overview) is active as part of a shell transition. While set, any focus + * going to the recents activity will be redirected to the Recents input consumer. Since we + * draw the live-tile above the recents activity, we also need to provide that activity as a + * z-layering reference so that we can place the recents input consumer above it. + */ + private WeakReference mActiveRecentsActivity = null; + private WeakReference mActiveRecentsLayerRef = null; + /** * Representation of a input consumer that the policy has added to the window manager to consume * input events going to windows below it. @@ -394,6 +405,21 @@ final class InputMonitor { } } + /** + * Inform InputMonitor when recents is active so it can enable the recents input consumer. + * @param activity The active recents activity. {@code null} means recents is not active. + * @param layer An activity whose Z-layer is used as a reference for how to sort the consumer. + */ + void setActiveRecents(@Nullable ActivityRecord activity, @Nullable ActivityRecord layer) { + final boolean clear = activity == null; + mActiveRecentsActivity = clear ? null : new WeakReference<>(activity); + mActiveRecentsLayerRef = clear ? null : new WeakReference<>(layer); + } + + private static T getWeak(WeakReference ref) { + return ref != null ? ref.get() : null; + } + /** * Called when the current input focus changes. */ @@ -404,8 +430,10 @@ final class InputMonitor { if (recentsAnimationInputConsumer != null && focus != null) { final RecentsAnimationController recentsAnimationController = mService.getRecentsAnimationController(); - final boolean shouldApplyRecentsInputConsumer = recentsAnimationController != null - && recentsAnimationController.shouldApplyInputConsumer(focus.mActivityRecord); + final boolean shouldApplyRecentsInputConsumer = (recentsAnimationController != null + && recentsAnimationController.shouldApplyInputConsumer(focus.mActivityRecord)) + // Shell transitions doesn't use RecentsAnimationController + || getWeak(mActiveRecentsActivity) != null; if (shouldApplyRecentsInputConsumer) { requestFocus(recentsAnimationInputConsumer.mWindowHandle.token, recentsAnimationInputConsumer.mName); @@ -505,6 +533,14 @@ final class InputMonitor { mInDrag = inDrag; resetInputConsumers(mInputTransaction); + // Update recents input consumer layer if active + if (mAddRecentsAnimationInputConsumerHandle + && getWeak(mActiveRecentsActivity) != null) { + final WindowContainer layer = getWeak(mActiveRecentsLayerRef); + mRecentsAnimationInputConsumer.show(mInputTransaction, + layer != null ? layer : getWeak(mActiveRecentsActivity)); + mAddRecentsAnimationInputConsumerHandle = false; + } mDisplayContent.forAllWindows(this, true /* traverseTopToBottom */); updateInputFocusRequest(mRecentsAnimationInputConsumer); @@ -539,6 +575,7 @@ final class InputMonitor { final int privateFlags = w.mAttrs.privateFlags; + // This only works for legacy transitions. if (mAddRecentsAnimationInputConsumerHandle && shouldApplyRecentsInputConsumer) { if (recentsAnimationController.updateInputConsumerForApp( mRecentsAnimationInputConsumer.mWindowHandle)) { diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java index 5366bd42fa1a5..2cca20f1fde50 100644 --- a/services/core/java/com/android/server/wm/Transition.java +++ b/services/core/java/com/android/server/wm/Transition.java @@ -16,10 +16,13 @@ package com.android.server.wm; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS; import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.app.WindowConfiguration.ROTATION_UNDEFINED; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.Display.INVALID_DISPLAY; +import static android.view.WindowManager.INPUT_CONSUMER_RECENTS_ANIMATION; import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS; import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_UNSPECIFIED; import static android.view.WindowManager.TRANSIT_CHANGE; @@ -149,7 +152,7 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe // TODO(b/188595497): remove when not needed. /** @see RecentsAnimationController#mNavigationBarAttachedToApp */ private boolean mNavBarAttachedToApp = false; - private int mNavBarDisplayId = INVALID_DISPLAY; + private int mRecentsDisplayId = INVALID_DISPLAY; Transition(@TransitionType int type, @TransitionFlags int flags, TransitionController controller, BLASTSyncEngine syncEngine) { @@ -169,6 +172,11 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe return mSyncId; } + @TransitionFlags + int getFlags() { + return mFlags; + } + /** * Formally starts the transition. Participants can be collected before this is started, * but this won't consider itself ready until started -- even if all the participants have @@ -390,6 +398,13 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe sendRemoteCallback(mClientAnimationFinishCallback); legacyRestoreNavigationBarFromApp(); + + if (mRecentsDisplayId != INVALID_DISPLAY) { + // Clean up input monitors (for recents) + final DisplayContent dc = + mController.mAtm.mRootWindowContainer.getDisplayContent(mRecentsDisplayId); + dc.getInputMonitor().setActiveRecents(null /* activity */, null /* layer */); + } } void abort() { @@ -534,7 +549,43 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe } final DisplayContent dc = mController.mAtm.mRootWindowContainer.getDisplayContent(displayId); - if (dc == null || !dc.getDisplayPolicy().shouldAttachNavBarToAppDuringTransition() + if (dc == null) return; + mRecentsDisplayId = displayId; + + // Recents has an input-consumer to grab input from the "live tile" app. Set that up here + final InputConsumerImpl recentsAnimationInputConsumer = + dc.getInputMonitor().getInputConsumer(INPUT_CONSUMER_RECENTS_ANIMATION); + if (recentsAnimationInputConsumer != null) { + // find the top-most going-away activity and the recents activity. The top-most + // is used as layer reference while the recents is used for registering the consumer + // override. + ActivityRecord recentsActivity = null; + ActivityRecord topActivity = null; + for (int i = 0; i < info.getChanges().size(); ++i) { + final TransitionInfo.Change change = info.getChanges().get(i); + if (change.getTaskInfo() == null) continue; + final Task task = Task.fromWindowContainerToken( + info.getChanges().get(i).getTaskInfo().token); + if (task == null) continue; + final int activityType = change.getTaskInfo().topActivityType; + final boolean isRecents = activityType == ACTIVITY_TYPE_HOME + || activityType == ACTIVITY_TYPE_RECENTS; + if (isRecents && recentsActivity == null) { + recentsActivity = task.getTopVisibleActivity(); + } else if (!isRecents && topActivity == null) { + topActivity = task.getTopNonFinishingActivity(); + } + } + if (recentsActivity != null && topActivity != null) { + recentsAnimationInputConsumer.mWindowHandle.touchableRegion.set( + topActivity.getBounds()); + dc.getInputMonitor().setActiveRecents(recentsActivity, topActivity); + } + } + + // The rest of this function handles nav-bar reparenting + + if (!dc.getDisplayPolicy().shouldAttachNavBarToAppDuringTransition() // Skip the case where the nav bar is controlled by fade rotation. || dc.getFadeRotationAnimationController() != null) { return; @@ -561,7 +612,6 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe return; } mNavBarAttachedToApp = true; - mNavBarDisplayId = displayId; navWindow.mToken.cancelAnimation(); final SurfaceControl.Transaction t = navWindow.mToken.getPendingTransaction(); final SurfaceControl navSurfaceControl = navWindow.mToken.getSurfaceControl(); @@ -585,12 +635,17 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe if (!mNavBarAttachedToApp) return; mNavBarAttachedToApp = false; + if (mRecentsDisplayId == INVALID_DISPLAY) { + Slog.e(TAG, "Reparented navigation bar without a valid display"); + mRecentsDisplayId = DEFAULT_DISPLAY; + } + if (mController.mStatusBar != null) { - mController.mStatusBar.setNavigationBarLumaSamplingEnabled(mNavBarDisplayId, true); + mController.mStatusBar.setNavigationBarLumaSamplingEnabled(mRecentsDisplayId, true); } final DisplayContent dc = - mController.mAtm.mRootWindowContainer.getDisplayContent(mNavBarDisplayId); + mController.mAtm.mRootWindowContainer.getDisplayContent(mRecentsDisplayId); final WindowState navWindow = dc.getDisplayPolicy().getNavigationBar(); if (navWindow == null) return; navWindow.setSurfaceTranslationY(0);