Merge "Stabilize overview/quick-switch transitions" into udc-dev
This commit is contained in:
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user