Merge "Support restoring the original task ordering of transient-launches" into tm-dev

This commit is contained in:
Evan Rosky
2022-02-25 03:47:37 +00:00
committed by Android (Google) Code Review
7 changed files with 94 additions and 21 deletions

View File

@@ -570,6 +570,24 @@ public final class WindowContainerTransaction implements Parcelable {
return this;
}
/**
* If `container` was brought to front as a transient-launch (eg. recents), this will reorder
* the container back to where it was prior to the transient-launch. This way if a transient
* launch is "aborted", the z-ordering of containers in WM should be restored to before the
* launch.
* @hide
*/
@NonNull
public WindowContainerTransaction restoreTransientOrder(
@NonNull WindowContainerToken container) {
final HierarchyOp hierarchyOp =
new HierarchyOp.Builder(HierarchyOp.HIERARCHY_OP_TYPE_RESTORE_TRANSIENT_ORDER)
.setContainer(container.asBinder())
.build();
mHierarchyOps.add(hierarchyOp);
return this;
}
/**
* When this {@link WindowContainerTransaction} failed to finish on the server side, it will
* trigger callback with this {@param errorCallbackToken}.
@@ -974,6 +992,7 @@ public final class WindowContainerTransaction implements Parcelable {
public static final int HIERARCHY_OP_TYPE_PENDING_INTENT = 12;
public static final int HIERARCHY_OP_TYPE_SET_ADJACENT_TASK_FRAGMENTS = 13;
public static final int HIERARCHY_OP_TYPE_START_SHORTCUT = 14;
public static final int HIERARCHY_OP_TYPE_RESTORE_TRANSIENT_ORDER = 15;
// The following key(s) are for use with mLaunchOptions:
// When launching a task (eg. from recents), this is the taskId to be launched.

View File

@@ -132,12 +132,13 @@ public class RemoteTransitionCompat implements Parcelable {
// the current going-away task on top of recents, though, so move it to front
final ArrayList<WindowContainerToken> pausingTasks = new ArrayList<>();
WindowContainerToken pipTask = null;
WindowContainerToken recentsTask = null;
for (int i = info.getChanges().size() - 1; i >= 0; --i) {
final TransitionInfo.Change change = info.getChanges().get(i);
final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
if (change.getMode() == TRANSIT_CLOSE || change.getMode() == TRANSIT_TO_BACK) {
t.setLayer(leashMap.get(change.getLeash()),
info.getChanges().size() * 3 - i);
final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
if (taskInfo == null) {
continue;
}
@@ -147,11 +148,14 @@ public class RemoteTransitionCompat implements Parcelable {
&& taskInfo.pictureInPictureParams.isAutoEnterEnabled()) {
pipTask = taskInfo.token;
}
} else if (change.getTaskInfo() != null
&& change.getTaskInfo().topActivityType == ACTIVITY_TYPE_RECENTS) {
} else if (taskInfo != null
&& taskInfo.topActivityType == ACTIVITY_TYPE_RECENTS) {
// This task is for recents, keep it on top.
t.setLayer(leashMap.get(change.getLeash()),
info.getChanges().size() * 3 - i);
recentsTask = taskInfo.token;
} else if (taskInfo != null && taskInfo.topActivityType == ACTIVITY_TYPE_HOME) {
recentsTask = taskInfo.token;
}
}
// Also make all the wallpapers opaque since we want the visible from the start
@@ -160,7 +164,7 @@ public class RemoteTransitionCompat implements Parcelable {
}
t.apply();
mRecentsSession.setup(controller, info, finishedCallback, pausingTasks, pipTask,
leashMap, mToken);
recentsTask, leashMap, mToken);
recents.onAnimationStart(mRecentsSession, apps, wallpapers, new Rect(0, 0, 0, 0),
new Rect());
}
@@ -209,6 +213,7 @@ public class RemoteTransitionCompat implements Parcelable {
private IRemoteTransitionFinishedCallback mFinishCB = null;
private ArrayList<WindowContainerToken> mPausingTasks = null;
private WindowContainerToken mPipTask = null;
private WindowContainerToken mRecentsTask = null;
private TransitionInfo mInfo = null;
private ArrayList<SurfaceControl> mOpeningLeashes = null;
private ArrayMap<SurfaceControl, SurfaceControl> mLeashMap = null;
@@ -218,7 +223,8 @@ public class RemoteTransitionCompat implements Parcelable {
void setup(RecentsAnimationControllerCompat wrapped, TransitionInfo info,
IRemoteTransitionFinishedCallback finishCB,
ArrayList<WindowContainerToken> pausingTasks, WindowContainerToken pipTask,
ArrayMap<SurfaceControl, SurfaceControl> leashMap, IBinder transition) {
WindowContainerToken recentsTask, ArrayMap<SurfaceControl, SurfaceControl> leashMap,
IBinder transition) {
if (mInfo != null) {
throw new IllegalStateException("Trying to run a new recents animation while"
+ " recents is already active.");
@@ -228,6 +234,7 @@ public class RemoteTransitionCompat implements Parcelable {
mFinishCB = finishCB;
mPausingTasks = pausingTasks;
mPipTask = pipTask;
mRecentsTask = recentsTask;
mLeashMap = leashMap;
mTransition = transition;
}
@@ -329,6 +336,9 @@ public class RemoteTransitionCompat implements Parcelable {
wct.reorder(mPausingTasks.get(i), true /* onTop */);
t.show(mInfo.getChange(mPausingTasks.get(i)).getLeash());
}
if (mRecentsTask != null) {
wct.restoreTransientOrder(mRecentsTask);
}
} else {
wct = null;
if (mPipTask != null && mPipTransaction != null) {

View File

@@ -208,6 +208,9 @@ class ActivityStarter {
private boolean mAvoidMoveToFront;
private boolean mFrozeTaskList;
private boolean mTransientLaunch;
// The task which was above the targetTask before starting this activity. null if the targetTask
// was already on top or if the activity is in a new task.
private Task mPriorAboveTask;
// We must track when we deliver the new intent since multiple code paths invoke
// {@link #deliverNewIntent}. This is due to early returns in the code path. This flag is used
@@ -1666,7 +1669,8 @@ class ActivityStarter {
if (isTransient) {
// `r` isn't guaranteed to be the actual relevant activity, so we must wait
// until after we launched to identify the relevant activity.
transitionController.setTransientLaunch(mLastStartActivityRecord);
transitionController.setTransientLaunch(mLastStartActivityRecord,
mPriorAboveTask);
}
if (newTransition != null) {
transitionController.requestStartTransition(newTransition,
@@ -1785,6 +1789,10 @@ class ActivityStarter {
return startResult;
}
if (targetTask != null) {
mPriorAboveTask = TaskDisplayArea.getRootTaskAbove(targetTask.getRootTask());
}
final ActivityRecord targetTaskTop = newTask
? null : targetTask.getTopNonFinishingActivity();
if (targetTaskTop != null) {

View File

@@ -167,8 +167,11 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
*/
private final ArraySet<WindowToken> mVisibleAtTransitionEndTokens = new ArraySet<>();
/** Set of transient activities (lifecycle initially tied to this transition). */
private ArraySet<ActivityRecord> mTransientLaunches = null;
/**
* Set of transient activities (lifecycle initially tied to this transition) and their
* restore-below tasks.
*/
private ArrayMap<ActivityRecord, Task> mTransientLaunches = null;
/** Custom activity-level animation options and callbacks. */
private TransitionInfo.AnimationOptions mOverrideOptions;
@@ -196,17 +199,26 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
}
/** Records an activity as transient-launch. This activity must be already collected. */
void setTransientLaunch(@NonNull ActivityRecord activity) {
void setTransientLaunch(@NonNull ActivityRecord activity, @Nullable Task restoreBelow) {
if (mTransientLaunches == null) {
mTransientLaunches = new ArraySet<>();
mTransientLaunches = new ArrayMap<>();
}
mTransientLaunches.add(activity);
mTransientLaunches.put(activity, restoreBelow);
ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, "Transition %d: Set %s as "
+ "transient-launch", mSyncId, activity);
}
boolean isTransientLaunch(@NonNull ActivityRecord activity) {
return mTransientLaunches != null && mTransientLaunches.contains(activity);
return mTransientLaunches != null && mTransientLaunches.containsKey(activity);
}
Task getTransientLaunchRestoreTarget(@NonNull WindowContainer container) {
for (int i = 0; i < mTransientLaunches.size(); ++i) {
if (mTransientLaunches.keyAt(i).isDescendantOf(container)) {
return mTransientLaunches.valueAt(i);
}
}
return null;
}
boolean isOnDisplay(@NonNull DisplayContent dc) {
@@ -464,7 +476,7 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
&& ar.pictureInPictureArgs.isAutoEnterEnabled()) {
if (mTransientLaunches != null) {
for (int j = 0; j < mTransientLaunches.size(); ++j) {
if (mTransientLaunches.valueAt(j).isVisibleRequested()) {
if (mTransientLaunches.keyAt(j).isVisibleRequested()) {
// force enable pip-on-task-switch now that we've committed
// to actually launching to the transient activity.
ar.supportsEnterPipOnTaskSwitch = true;
@@ -543,7 +555,7 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
// Transient-launch activities cannot be IME target (WindowState#canBeImeTarget),
// so re-compute in case the IME target is changed after transition.
for (int t = 0; t < mTransientLaunches.size(); ++t) {
if (mTransientLaunches.valueAt(t).getDisplayContent() == dc) {
if (mTransientLaunches.keyAt(t).getDisplayContent() == dc) {
dc.computeImeTarget(true /* updateImeTarget */);
break;
}

View File

@@ -520,10 +520,12 @@ class TransitionController {
/**
* Record that the launch of {@param activity} is transient (meaning its lifecycle is currently
* tied to the transition).
* @param restoreBelowTask If non-null, the activity's task will be ordered right below this
* task if requested.
*/
void setTransientLaunch(@NonNull ActivityRecord activity) {
void setTransientLaunch(@NonNull ActivityRecord activity, @Nullable Task restoreBelowTask) {
if (mCollectingTransition == null) return;
mCollectingTransition.setTransientLaunch(activity);
mCollectingTransition.setTransientLaunch(activity, restoreBelowTask);
// TODO(b/188669821): Remove once legacy recents behavior is moved to shell.
// Also interpret HOME transient launch as recents

View File

@@ -30,6 +30,7 @@ import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REPARENT;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REPARENT_ACTIVITY_TO_TASK_FRAGMENT;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REPARENT_CHILDREN;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_RESTORE_TRANSIENT_ORDER;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_ADJACENT_ROOTS;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_ADJACENT_TASK_FRAGMENTS;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_LAUNCH_ADJACENT_FLAG_ROOT;
@@ -358,10 +359,11 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
if (t != null && callback != null) {
syncId = startSyncWithOrganizer(callback);
}
final Transition transition = Transition.fromBinder(transitionToken);
// apply the incoming transaction before finish in case it alters the visibility
// of the participants.
if (t != null) {
applyTransaction(t, syncId, null /*transition*/, caller);
applyTransaction(t, syncId, null /*transition*/, caller, transition);
}
getTransitionController().finishTransition(transitionToken);
if (syncId >= 0) {
@@ -374,13 +376,20 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
}
}
private void applyTransaction(@NonNull WindowContainerTransaction t, int syncId,
@Nullable Transition transition, @NonNull CallerInfo caller) {
applyTransaction(t, syncId, transition, caller, null /* finishTransition */);
}
/**
* @param syncId If non-null, this will be a sync-transaction.
* @param transition A transition to collect changes into.
* @param caller Info about the calling process.
* @param finishTransition The transition that is currently being finished.
*/
private void applyTransaction(@NonNull WindowContainerTransaction t, int syncId,
@Nullable Transition transition, @NonNull CallerInfo caller) {
@Nullable Transition transition, @NonNull CallerInfo caller,
@Nullable Transition finishTransition) {
int effects = 0;
ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Apply window transaction, syncId=%d", syncId);
mService.deferWindowLayout();
@@ -433,7 +442,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
for (int i = 0; i < hopSize; ++i) {
effects |= applyHierarchyOp(hops.get(i), effects, syncId, transition,
isInLockTaskMode, caller, t.getErrorCallbackToken(),
t.getTaskFragmentOrganizer());
t.getTaskFragmentOrganizer(), finishTransition);
}
}
// Queue-up bounds-change transactions for tasks which are now organized. Do
@@ -604,7 +613,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
private int applyHierarchyOp(WindowContainerTransaction.HierarchyOp hop, int effects,
int syncId, @Nullable Transition transition, boolean isInLockTaskMode,
@NonNull CallerInfo caller, @Nullable IBinder errorCallbackToken,
@Nullable ITaskFragmentOrganizer organizer) {
@Nullable ITaskFragmentOrganizer organizer, @Nullable Transition finishTransition) {
final int type = hop.getType();
switch (type) {
case HIERARCHY_OP_TYPE_SET_LAUNCH_ROOT: {
@@ -873,6 +882,19 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
effects |= TRANSACT_EFFECTS_LIFECYCLE;
break;
}
case HIERARCHY_OP_TYPE_RESTORE_TRANSIENT_ORDER: {
if (finishTransition == null) break;
final WindowContainer container = WindowContainer.fromBinder(hop.getContainer());
if (container == null) break;
final Task thisTask = container.asActivityRecord() != null
? container.asActivityRecord().getTask() : container.asTask();
if (thisTask == null) break;
final Task restoreAt = finishTransition.getTransientLaunchRestoreTarget(container);
if (restoreAt == null) break;
final TaskDisplayArea taskDisplayArea = thisTask.getTaskDisplayArea();
taskDisplayArea.moveRootTaskBehindRootTask(thisTask.getRootTask(), restoreAt);
break;
}
}
return effects;
}

View File

@@ -713,7 +713,7 @@ public class TransitionTests extends WindowTestsBase {
closeTransition.collectExistenceChange(activity1);
closeTransition.collectExistenceChange(task2);
closeTransition.collectExistenceChange(activity2);
closeTransition.setTransientLaunch(activity2);
closeTransition.setTransientLaunch(activity2, null /* restoreBelow */);
activity1.mVisibleRequested = false;
activity2.mVisibleRequested = true;