Merge "Stabilize overview/quick-switch transitions" into udc-dev

This commit is contained in:
Evan Rosky
2023-02-23 22:16:26 +00:00
committed by Android (Google) Code Review
2 changed files with 144 additions and 61 deletions

View File

@@ -36,7 +36,6 @@ import android.os.IBinder;
import android.os.RemoteException;
import android.util.ArrayMap;
import android.util.Log;
import android.util.SparseArray;
import android.view.IRecentsAnimationController;
import android.view.RemoteAnimationTarget;
import android.view.SurfaceControl;
@@ -106,12 +105,24 @@ public class RemoteTransitionCompat {
private RecentsAnimationListener mListener = null;
private RecentsAnimationControllerCompat mWrapped = null;
private IRemoteTransitionFinishedCallback mFinishCB = null;
/**
* List of tasks that we are switching away from via this transition. Upon finish, these
* pausing tasks will become invisible.
* These need to be ordered since the order must be restored if there is no task-switch.
*/
private ArrayList<TaskState> mPausingTasks = null;
/**
* List of tasks that we are switching to. Upon finish, these will remain visible and
* on top.
*/
private ArrayList<TaskState> mOpeningTasks = null;
private WindowContainerToken mPipTask = null;
private WindowContainerToken mRecentsTask = null;
private int mRecentsTaskId = 0;
private TransitionInfo mInfo = null;
private ArrayList<SurfaceControl> mOpeningLeashes = null;
private boolean mOpeningSeparateHome = false;
private ArrayMap<SurfaceControl, SurfaceControl> mLeashMap = null;
private PictureInPictureSurfaceTransaction mPipTransaction = null;
@@ -120,6 +131,15 @@ public class RemoteTransitionCompat {
private RemoteAnimationTarget[] mAppearedTargets;
private boolean mWillFinishToHome = false;
/** The animation is idle, waiting for the user to choose a task to switch to. */
private static final int STATE_NORMAL = 0;
/** The user chose a new task to switch to and the animation is animating to it. */
private static final int STATE_NEW_TASK = 1;
/** The latest state that the recents animation is operating in. */
private int mState = STATE_NORMAL;
void start(RecentsAnimationControllerCompat wrapped, RecentsAnimationListener listener,
IBinder transition, TransitionInfo info, SurfaceControl.Transaction t,
IRemoteTransitionFinishedCallback finishedCallback) {
@@ -132,12 +152,14 @@ public class RemoteTransitionCompat {
mInfo = info;
mFinishCB = finishedCallback;
mPausingTasks = new ArrayList<>();
mOpeningTasks = new ArrayList<>();
mPipTask = null;
mRecentsTask = null;
mRecentsTaskId = -1;
mLeashMap = new ArrayMap<>();
mTransition = transition;
mKeyguardLocked = (info.getFlags() & TRANSIT_FLAG_KEYGUARD_LOCKED) != 0;
mState = STATE_NORMAL;
final ArrayList<RemoteAnimationTarget> apps = new ArrayList<>();
final ArrayList<RemoteAnimationTarget> wallpapers = new ArrayList<>();
@@ -178,6 +200,9 @@ public class RemoteTransitionCompat {
} else if (taskInfo != null && taskInfo.topActivityType == ACTIVITY_TYPE_HOME) {
mRecentsTask = taskInfo.token;
mRecentsTaskId = taskInfo.taskId;
} else if (change.getMode() == TRANSIT_OPEN
|| change.getMode() == TRANSIT_TO_FRONT) {
mOpeningTasks.add(new TaskState(change, target.leash));
}
}
}
@@ -189,34 +214,41 @@ public class RemoteTransitionCompat {
@SuppressLint("NewApi")
boolean merge(TransitionInfo info, SurfaceControl.Transaction t) {
SparseArray<TransitionInfo.Change> openingTasks = null;
ArrayList<TransitionInfo.Change> openingTasks = null;
ArrayList<TransitionInfo.Change> closingTasks = null;
mAppearedTargets = null;
boolean foundHomeOpening = false;
mOpeningSeparateHome = false;
TransitionInfo.Change recentsOpening = null;
boolean foundRecentsClosing = false;
boolean hasChangingApp = false;
for (int i = info.getChanges().size() - 1; i >= 0; --i) {
final RemoteAnimationTargetCompat.LeafTaskFilter leafTaskFilter =
new RemoteAnimationTargetCompat.LeafTaskFilter();
for (int i = 0; i < info.getChanges().size(); ++i) {
final TransitionInfo.Change change = info.getChanges().get(i);
final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
final boolean isLeafTask = leafTaskFilter.test(change);
if (change.getMode() == TRANSIT_OPEN || change.getMode() == TRANSIT_TO_FRONT) {
final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
if (taskInfo != null) {
if (mRecentsTask.equals(change.getContainer())) {
recentsOpening = change;
} else if (isLeafTask) {
if (taskInfo.topActivityType == ACTIVITY_TYPE_HOME) {
// This is usually a 3p launcher
foundHomeOpening = true;
mOpeningSeparateHome = true;
}
if (openingTasks == null) {
openingTasks = new SparseArray<>();
openingTasks = new ArrayList<>();
}
if (taskInfo.hasParentTask()) {
// Collects opening leaf tasks only since Launcher monitors leaf task
// ids to perform recents animation.
openingTasks.remove(taskInfo.parentTaskId);
}
openingTasks.put(taskInfo.taskId, change);
openingTasks.add(change);
}
} else if (change.getMode() == TRANSIT_CLOSE
|| change.getMode() == TRANSIT_TO_BACK) {
if (mRecentsTask.equals(change.getContainer())) {
foundRecentsClosing = true;
} else if (isLeafTask) {
if (closingTasks == null) {
closingTasks = new ArrayList<>();
}
closingTasks.add(change);
}
} else if (change.getMode() == TRANSIT_CHANGE) {
hasChangingApp = true;
@@ -234,45 +266,72 @@ public class RemoteTransitionCompat {
}
return false;
}
if (openingTasks == null) return false;
int pauseMatches = 0;
if (!foundHomeOpening) {
if (recentsOpening != null) {
// the recents task re-appeared. This happens if the user gestures before the
// task-switch (NEW_TASK) animation finishes.
if (mState == STATE_NORMAL) {
Log.e(TAG, "Returning to recents while recents is already idle.");
}
if (closingTasks == null || closingTasks.size() == 0) {
Log.e(TAG, "Returning to recents without closing any opening tasks.");
}
// Setup may hide it initially since it doesn't know that overview was still active.
t.show(recentsOpening.getLeash());
t.setAlpha(recentsOpening.getLeash(), 1.f);
mState = STATE_NORMAL;
}
boolean didMergeThings = false;
if (closingTasks != null) {
// Cancelling a task-switch. Move the tasks back to mPausing from mOpening
for (int i = 0; i < closingTasks.size(); ++i) {
final TransitionInfo.Change change = closingTasks.get(i);
int openingIdx = TaskState.indexOf(mOpeningTasks, change);
if (openingIdx < 0) {
Log.e(TAG, "Back to existing recents animation from an unrecognized "
+ "task: " + change.getTaskInfo().taskId);
continue;
}
mPausingTasks.add(mOpeningTasks.remove(openingIdx));
didMergeThings = true;
}
}
if (openingTasks != null && openingTasks.size() > 0) {
// Switching to some new tasks, add to mOpening and remove from mPausing. Also,
// enter NEW_TASK state since this will start the switch-to animation.
final int layer = mInfo.getChanges().size() * 3;
final RemoteAnimationTarget[] targets =
new RemoteAnimationTarget[openingTasks.size()];
for (int i = 0; i < openingTasks.size(); ++i) {
if (TaskState.indexOf(mPausingTasks, openingTasks.valueAt(i)) >= 0) {
++pauseMatches;
final TransitionInfo.Change change = openingTasks.get(i);
int pausingIdx = TaskState.indexOf(mPausingTasks, change);
if (pausingIdx >= 0) {
// Something is showing/opening a previously-pausing app.
targets[i] = newTarget(change, layer, mPausingTasks.get(pausingIdx).mLeash);
mOpeningTasks.add(mPausingTasks.remove(pausingIdx));
// Setup hides opening tasks initially, so make it visible again (since we
// are already showing it).
t.show(change.getLeash());
t.setAlpha(change.getLeash(), 1.f);
} else {
// We are receiving new opening tasks, so convert to onTasksAppeared.
targets[i] = newTarget(change, layer, info, t, mLeashMap);
t.reparent(targets[i].leash, mInfo.getRootLeash());
t.setLayer(targets[i].leash, layer);
mOpeningTasks.add(new TaskState(change, targets[i].leash));
}
}
didMergeThings = true;
mState = STATE_NEW_TASK;
mAppearedTargets = targets;
}
if (pauseMatches > 0) {
if (pauseMatches != mPausingTasks.size()) {
// We are not really "returning" properly... something went wrong.
throw new IllegalStateException("\"Concelling\" a recents transitions by "
+ "unpausing " + pauseMatches + " apps after pausing "
+ mPausingTasks.size() + " apps.");
}
// In this case, we are "returning" to an already running app, so just consume
// the merge and do nothing.
info.releaseAllSurfaces();
t.close();
return true;
}
final int layer = mInfo.getChanges().size() * 3;
mOpeningLeashes = new ArrayList<>();
mOpeningSeparateHome = foundHomeOpening;
final RemoteAnimationTarget[] targets =
new RemoteAnimationTarget[openingTasks.size()];
for (int i = 0; i < openingTasks.size(); ++i) {
final TransitionInfo.Change change = openingTasks.valueAt(i);
mOpeningLeashes.add(change.getLeash());
// We are receiving new opening tasks, so convert to onTasksAppeared.
targets[i] = newTarget(change, layer, info, t, mLeashMap);
t.reparent(targets[i].leash, mInfo.getRootLeash());
t.setLayer(targets[i].leash, layer);
if (!didMergeThings) {
// Didn't recognize anything in incoming transition so don't merge it.
Log.w(TAG, "Don't know how to merge this transition.");
return false;
}
t.apply();
// not using the incoming anim-only surfaces
info.releaseAnimSurfaces();
mAppearedTargets = targets;
return true;
}
@@ -300,6 +359,8 @@ public class RemoteTransitionCompat {
if (enabled) {
// transient launches don't receive focus automatically. Since we are taking over
// the gesture now, take focus explicitly.
// This also moves recents back to top if the user gestured before a switch
// animation finished.
try {
ActivityTaskManager.getService().setFocusedTask(mRecentsTaskId);
} catch (RemoteException e) {
@@ -336,8 +397,8 @@ public class RemoteTransitionCompat {
if (toHome) wct.reorder(mRecentsTask, true /* toTop */);
else wct.restoreTransientOrder(mRecentsTask);
}
if (!toHome && !mWillFinishToHome && mPausingTasks != null && mOpeningLeashes == null) {
// The gesture went back to opening the app rather than continuing with
if (!toHome && !mWillFinishToHome && mPausingTasks != null && mState == STATE_NORMAL) {
// The gesture is returning to the pausing-task(s) rather than continuing with
// recents, so end the transition by moving the app back to the top (and also
// re-showing it's task).
for (int i = mPausingTasks.size() - 1; i >= 0; --i) {
@@ -349,25 +410,28 @@ public class RemoteTransitionCompat {
wct.restoreTransientOrder(mRecentsTask);
}
} else if (toHome && mOpeningSeparateHome && mPausingTasks != null) {
// Special situaition where 3p launcher was changed during recents (this happens
// Special situation where 3p launcher was changed during recents (this happens
// during tapltests...). Here we get both "return to home" AND "home opening".
// This is basically going home, but we have to restore recents order and also
// treat the home "pausing" task properly.
for (int i = mPausingTasks.size() - 1; i >= 0; --i) {
final TaskState state = mPausingTasks.get(i);
// This is basically going home, but we have to restore the recents and home order.
for (int i = 0; i < mOpeningTasks.size(); ++i) {
final TaskState state = mOpeningTasks.get(i);
if (state.mTaskInfo.topActivityType == ACTIVITY_TYPE_HOME) {
// Treat as opening (see above)
// Make sure it is on top.
wct.reorder(state.mToken, true /* onTop */);
t.show(state.mTaskSurface);
} else {
// Treat as hiding (see below)
t.hide(state.mTaskSurface);
}
t.show(state.mTaskSurface);
}
for (int i = mPausingTasks.size() - 1; i >= 0; --i) {
t.hide(mPausingTasks.get(i).mTaskSurface);
}
if (!mKeyguardLocked && mRecentsTask != null) {
wct.restoreTransientOrder(mRecentsTask);
}
} else {
// The general case: committing to recents, going home, or switching tasks.
for (int i = 0; i < mOpeningTasks.size(); ++i) {
t.show(mOpeningTasks.get(i).mTaskSurface);
}
for (int i = 0; i < mPausingTasks.size(); ++i) {
if (!sendUserLeaveHint) {
// This means recents is not *actually* finishing, so of course we gotta
@@ -391,7 +455,7 @@ public class RemoteTransitionCompat {
try {
mFinishCB.onTransitionFinished(wct.isEmpty() ? null : wct, t);
} catch (RemoteException e) {
Log.e("RemoteTransitionCompat", "Failed to call animation finish callback", e);
Log.e(TAG, "Failed to call animation finish callback", e);
t.apply();
}
// Only release the non-local created surface references. The animator is responsible
@@ -402,12 +466,13 @@ public class RemoteTransitionCompat {
mListener = null;
mFinishCB = null;
mPausingTasks = null;
mOpeningTasks = null;
mAppearedTargets = null;
mInfo = null;
mOpeningLeashes = null;
mOpeningSeparateHome = false;
mLeashMap = null;
mTransition = null;
mState = STATE_NORMAL;
}
@Override public void setDeferCancelUntilNextTransition(boolean defer, boolean screenshot) {

View File

@@ -65,6 +65,7 @@ import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_PIP;
import static android.view.WindowManager.TRANSIT_TO_FRONT;
import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_TO_LAUNCHER_CLEAR_SNAPSHOT;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_CONFIGURATION;
@@ -2034,7 +2035,20 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
return;
}
if (r.moveFocusableActivityToTop("setFocusedTask")) {
final Transition transition = (getTransitionController().isCollecting()
|| !getTransitionController().isShellTransitionsEnabled()) ? null
: getTransitionController().createTransition(TRANSIT_TO_FRONT);
if (transition != null) {
// Set ready before doing anything. If order does change, then that will set it unready
// so that we wait for the new lifecycles to complete.
transition.setReady(task, true /* ready */);
}
final boolean movedToTop = r.moveFocusableActivityToTop("setFocusedTask");
if (movedToTop) {
if (transition != null) {
getTransitionController().requestStartTransition(
transition, null /* startTask */, null /* remote */, null /* display */);
}
mRootWindowContainer.resumeFocusedTasksTopActivities();
} else if (touchedActivity != null && touchedActivity.isFocusable()) {
final TaskFragment parent = touchedActivity.getTaskFragment();
@@ -2046,6 +2060,10 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
true /* updateInputWindows */);
}
}
if (transition != null && !movedToTop) {
// No order changes and focus-changes, alone, aren't captured in transitions.
transition.abort();
}
}
@Override