From 7f51866c1154b84a25afc9a617dad4730d2c449a Mon Sep 17 00:00:00 2001 From: Evan Rosky Date: Tue, 25 Apr 2023 17:54:54 -0700 Subject: [PATCH] Put activity-start into its own transition when recents collecting If a touch through gesture-bar starts an activity, the touch itself immediately starts recents, then the app (receiving the touch-through) might start an activity. Because activity-launches must be handled in sync (due to the API requiring a return value), it can't wait for an ongoing transition like normal. Instead, if there is an ongoing transition, it usually just gets added into it. Normally this is fine (expected, even, for trampolines); however, in the recents case, we don't want this because it causes a flicker. In this CL, we basically allow startActivity to create its own transition in parallel as long as the transition it is "interrupting" is recents. This way the animations for recents and activity launch are isolated. Bug: 276755325 Test: in gmail, flip back-and-forth between it's activity-tabs by tapping through the gesture area Change-Id: Id633baf17445938145132711b556effb852a5aea --- .../android/server/wm/ActivityStarter.java | 5 +- .../server/wm/TransitionController.java | 46 +++++++++++++++++-- 2 files changed, 44 insertions(+), 7 deletions(-) diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java index a27f3e49457dc..19a12f2b9cc04 100644 --- a/services/core/java/com/android/server/wm/ActivityStarter.java +++ b/services/core/java/com/android/server/wm/ActivityStarter.java @@ -1468,9 +1468,8 @@ class ActivityStarter { // transition based on a sub-action. // Only do the create here (and defer requestStart) since startActivityInner might abort. final TransitionController transitionController = r.mTransitionController; - Transition newTransition = (!transitionController.isCollecting() - && transitionController.getTransitionPlayer() != null) - ? transitionController.createTransition(TRANSIT_OPEN) : null; + Transition newTransition = transitionController.isShellTransitionsEnabled() + ? transitionController.createAndStartCollecting(TRANSIT_OPEN) : null; RemoteTransition remoteTransition = r.takeRemoteTransition(); try { mService.deferWindowLayout(); diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java index 19ef40b616343..784c4b408039d 100644 --- a/services/core/java/com/android/server/wm/TransitionController.java +++ b/services/core/java/com/android/server/wm/TransitionController.java @@ -858,7 +858,7 @@ class TransitionController { tryStartCollectFromQueue(); } - private boolean canStartCollectingNow(Transition queued) { + private boolean canStartCollectingNow(@Nullable Transition queued) { if (mCollectingTransition == null) return true; // Population (collect until ready) is still serialized, so always wait for that. if (!mCollectingTransition.isPopulated()) return false; @@ -931,14 +931,14 @@ class TransitionController { * `collecting` transition. It may still ultimately block in sync-engine or become dependent * in {@link #getIsIndependent} later. */ - boolean getCanBeIndependent(Transition collecting, Transition queued) { + boolean getCanBeIndependent(Transition collecting, @Nullable Transition queued) { // For tests - if (queued.mParallelCollectType == Transition.PARALLEL_TYPE_MUTUAL + if (queued != null && queued.mParallelCollectType == Transition.PARALLEL_TYPE_MUTUAL && collecting.mParallelCollectType == Transition.PARALLEL_TYPE_MUTUAL) { return true; } // For recents - if (queued.mParallelCollectType == Transition.PARALLEL_TYPE_RECENTS) { + if (queued != null && queued.mParallelCollectType == Transition.PARALLEL_TYPE_RECENTS) { if (collecting.mParallelCollectType == Transition.PARALLEL_TYPE_RECENTS) { // Must serialize with itself. return false; @@ -1235,6 +1235,44 @@ class TransitionController { return true; } + /** + * This will create and start collecting for a transition if possible. If there's no way to + * start collecting for `parallelType` now, then this returns null. + * + * WARNING: ONLY use this if the transition absolutely cannot be deferred! + */ + @NonNull + Transition createAndStartCollecting(int type) { + if (mTransitionPlayer == null) { + return null; + } + if (!mQueuedTransitions.isEmpty()) { + // There is a queue, so it's not possible to start immediately + return null; + } + if (mSyncEngine.hasActiveSync()) { + if (isCollecting()) { + // Check if we can run in parallel here. + if (canStartCollectingNow(null /* transit */)) { + // create and collect in parallel. + ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS_MIN, "Moving #%d from" + + " collecting to waiting.", mCollectingTransition.getSyncId()); + mWaitingTransitions.add(mCollectingTransition); + mCollectingTransition = null; + Transition transit = new Transition(type, 0 /* flags */, this, mSyncEngine); + moveToCollecting(transit); + return transit; + } + } else { + Slog.w(TAG, "Ongoing Sync outside of transition."); + } + return null; + } + Transition transit = new Transition(type, 0 /* flags */, this, mSyncEngine); + moveToCollecting(transit); + return transit; + } + /** Returns {@code true} if it started collecting, {@code false} if it was queued. */ boolean startLegacySyncOrQueue(BLASTSyncEngine.SyncGroup syncGroup, Runnable applySync) { if (!mQueuedTransitions.isEmpty() || mSyncEngine.hasActiveSync()) {