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);