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
This commit is contained in:
Evan Rosky
2023-04-25 17:54:54 -07:00
parent b4963b6da5
commit 7f51866c11
2 changed files with 44 additions and 7 deletions

View File

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

View File

@@ -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()) {