Fix back stack for PiP from multi-activity Task
Details in design doc go/pip-and-back Video: http://recall/-/aaaaaabFQoRHlzixHdtY/g8lbXnC8ln0t54ARxfPELo Bug: 184758170 Test: ensure back stack works for Netflix, see video Test: atest PinnedStackTests Test: atest RootWindowContainerTests Change-Id: I9e2f8d0d50dd107ff30fd7afe6273d8347aac803
This commit is contained in:
@@ -161,6 +161,7 @@ package android.app {
|
||||
method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) public void moveTaskToRootTask(int, int, boolean);
|
||||
method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) public void removeRootTasksInWindowingModes(@NonNull int[]);
|
||||
method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) public void removeRootTasksWithActivityTypes(@NonNull int[]);
|
||||
method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) public boolean removeTask(int);
|
||||
method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) public void resizeTask(int, android.graphics.Rect);
|
||||
method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) public void startSystemLockTaskMode(int);
|
||||
method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) public void stopSystemLockTaskMode();
|
||||
|
||||
@@ -469,7 +469,8 @@ public class ActivityTaskManager {
|
||||
}
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
/** Removes task by a given taskId */
|
||||
@RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS)
|
||||
public boolean removeTask(int taskId) {
|
||||
try {
|
||||
return getService().removeTask(taskId);
|
||||
|
||||
@@ -308,6 +308,7 @@ message TaskProto {
|
||||
optional float minimize_amount = 27;
|
||||
optional bool created_by_organizer = 28;
|
||||
optional string affinity = 29;
|
||||
optional bool has_child_pip_activity = 30;
|
||||
}
|
||||
|
||||
/* represents ActivityRecordProto */
|
||||
|
||||
@@ -587,7 +587,14 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
|
||||
|
||||
AnimatingActivityRegistry mAnimatingActivityRegistry;
|
||||
|
||||
private Task mLastParent;
|
||||
// Set whenever the ActivityRecord gets reparented to a Task so we can know the last known
|
||||
// parent was when the ActivityRecord is detached from the hierarchy
|
||||
private Task mLastKnownParent;
|
||||
|
||||
// Set to the previous Task parent of the ActivityRecord when it is reparented to a new Task
|
||||
// due to picture-in-picture. This gets cleared whenever this activity or the Task
|
||||
// it references to gets removed. This should also be cleared when we move out of pip.
|
||||
private Task mLastParentBeforePip;
|
||||
|
||||
boolean firstWindowDrawn;
|
||||
/** Whether the visible window(s) of this activity is drawn. */
|
||||
@@ -1096,6 +1103,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
|
||||
pw.println(prefix + "configChanges=0x" + Integer.toHexString(info.configChanges));
|
||||
}
|
||||
}
|
||||
if (mLastParentBeforePip != null) {
|
||||
pw.println(prefix + "lastParentTaskIdBeforePip=" + mLastParentBeforePip.mTaskId);
|
||||
}
|
||||
|
||||
dumpLetterboxInfo(pw, prefix);
|
||||
}
|
||||
@@ -1349,7 +1359,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
|
||||
if (getDisplayContent() != null) {
|
||||
getDisplayContent().mClosingApps.remove(this);
|
||||
}
|
||||
} else if (mLastParent != null && mLastParent.getRootTask() != null) {
|
||||
} else if (mLastKnownParent != null && mLastKnownParent.getRootTask() != null) {
|
||||
task.getRootTask().mExitingActivities.remove(this);
|
||||
}
|
||||
final Task rootTask = getRootTask();
|
||||
@@ -1362,7 +1372,11 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
|
||||
? rootTask.getAnimatingActivityRegistry()
|
||||
: null;
|
||||
|
||||
mLastParent = task;
|
||||
mLastKnownParent = task;
|
||||
if (mLastKnownParent == mLastParentBeforePip) {
|
||||
// Activity's reparented back from pip, clear the links once established
|
||||
clearLastParentBeforePip();
|
||||
}
|
||||
|
||||
updateColorTransform();
|
||||
|
||||
@@ -1381,6 +1395,26 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets {@link #mLastParentBeforePip} to the current parent Task, it's caller's job to ensure
|
||||
* {@link #getTask()} is set before this is called.
|
||||
*/
|
||||
void setLastParentBeforePip() {
|
||||
mLastParentBeforePip = getTask();
|
||||
mLastParentBeforePip.mChildPipActivity = this;
|
||||
}
|
||||
|
||||
private void clearLastParentBeforePip() {
|
||||
if (mLastParentBeforePip != null) {
|
||||
mLastParentBeforePip.mChildPipActivity = null;
|
||||
mLastParentBeforePip = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable Task getLastParentBeforePip() {
|
||||
return mLastParentBeforePip;
|
||||
}
|
||||
|
||||
private void updateColorTransform() {
|
||||
if (mSurfaceControl != null && mLastAppSaturationInfo != null) {
|
||||
getPendingTransaction().setColorTransform(mSurfaceControl,
|
||||
@@ -3434,6 +3468,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
|
||||
*/
|
||||
void cleanUp(boolean cleanServices, boolean setState) {
|
||||
task.cleanUpActivityReferences(this);
|
||||
clearLastParentBeforePip();
|
||||
|
||||
deferRelaunchUntilPaused = false;
|
||||
frozenBeforeDestroy = false;
|
||||
|
||||
@@ -1415,6 +1415,9 @@ class RecentTasks {
|
||||
return true;
|
||||
}
|
||||
|
||||
// The given task if always treated as in visible range if it is the origin of pinned task.
|
||||
if (task.mChildPipActivity != null) return true;
|
||||
|
||||
if (mMaxNumVisibleTasks >= 0) {
|
||||
// Always keep up to the max number of recent tasks, but return false afterwards
|
||||
return numVisibleTasks <= mMaxNumVisibleTasks;
|
||||
|
||||
@@ -2124,6 +2124,8 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
|
||||
.setDeferTaskAppear(true)
|
||||
.setHasBeenVisible(true)
|
||||
.build();
|
||||
// Establish bi-directional link between the original and pinned task.
|
||||
r.setLastParentBeforePip();
|
||||
// It's possible the task entering PIP is in freeform, so save the last
|
||||
// non-fullscreen bounds. Then when this new PIP task exits PIP, it can restore
|
||||
// to its previous freeform bounds.
|
||||
|
||||
@@ -131,6 +131,7 @@ import static com.android.server.wm.TaskProto.BOUNDS;
|
||||
import static com.android.server.wm.TaskProto.CREATED_BY_ORGANIZER;
|
||||
import static com.android.server.wm.TaskProto.DISPLAY_ID;
|
||||
import static com.android.server.wm.TaskProto.FILLS_PARENT;
|
||||
import static com.android.server.wm.TaskProto.HAS_CHILD_PIP_ACTIVITY;
|
||||
import static com.android.server.wm.TaskProto.LAST_NON_FULLSCREEN_BOUNDS;
|
||||
import static com.android.server.wm.TaskProto.MIN_HEIGHT;
|
||||
import static com.android.server.wm.TaskProto.MIN_WIDTH;
|
||||
@@ -836,6 +837,14 @@ class Task extends WindowContainer<WindowContainer> {
|
||||
// The task will be removed when TaskOrganizer, which is managing the task, is destroyed.
|
||||
boolean mRemoveWithTaskOrganizer;
|
||||
|
||||
/**
|
||||
* Reference to the pinned activity that is logically parented to this task, ie.
|
||||
* the previous top activity within this task is put into pinned mode.
|
||||
* This always gets cleared in pair with the ActivityRecord-to-Task link as seen in
|
||||
* {@link ActivityRecord#clearLastParentBeforePip()}.
|
||||
*/
|
||||
ActivityRecord mChildPipActivity;
|
||||
|
||||
private Task(ActivityTaskManagerService atmService, int _taskId, Intent _intent,
|
||||
Intent _affinityIntent, String _affinity, String _rootAffinity,
|
||||
ComponentName _realActivity, ComponentName _origActivity, boolean _rootWasReset,
|
||||
@@ -1841,6 +1850,10 @@ class Task extends WindowContainer<WindowContainer> {
|
||||
|
||||
/** Completely remove all activities associated with an existing task. */
|
||||
void performClearTask(String reason) {
|
||||
// The original task is to be removed, try remove also the pinned task.
|
||||
if (mChildPipActivity != null && mChildPipActivity.getTask() != null) {
|
||||
mTaskSupervisor.removeRootTask(mChildPipActivity.getTask());
|
||||
}
|
||||
// Broken down into to cases to avoid object create due to capturing mStack.
|
||||
if (getRootTask() == null) {
|
||||
forAllActivities((r) -> {
|
||||
@@ -4449,6 +4462,7 @@ class Task extends WindowContainer<WindowContainer> {
|
||||
}
|
||||
pw.print(prefix); pw.print("taskId=" + mTaskId);
|
||||
pw.println(" rootTaskId=" + getRootTaskId());
|
||||
pw.print(prefix); pw.println("hasChildPipActivity=" + (mChildPipActivity != null));
|
||||
pw.print(prefix); pw.print("mHasBeenVisible="); pw.println(getHasBeenVisible());
|
||||
pw.print(prefix); pw.print("mResizeMode=");
|
||||
pw.print(ActivityInfo.resizeModeToString(mResizeMode));
|
||||
@@ -5328,7 +5342,6 @@ class Task extends WindowContainer<WindowContainer> {
|
||||
return;
|
||||
}
|
||||
final int currentMode = getWindowingMode();
|
||||
final int currentOverrideMode = getRequestedOverrideWindowingMode();
|
||||
final Task topTask = getTopMostTask();
|
||||
int windowingMode = preferredWindowingMode;
|
||||
|
||||
@@ -5397,9 +5410,26 @@ class Task extends WindowContainer<WindowContainer> {
|
||||
mTaskSupervisor.mNoAnimActivities.add(topActivity);
|
||||
}
|
||||
super.setWindowingMode(windowingMode);
|
||||
// setWindowingMode triggers an onConfigurationChanged cascade which can result in a
|
||||
// different resolved windowing mode (usually when preferredWindowingMode is UNDEFINED).
|
||||
windowingMode = getWindowingMode();
|
||||
|
||||
// Try reparent pinned activity back to its original task after onConfigurationChanged
|
||||
// cascade finishes. This is done on Task level instead of
|
||||
// {@link ActivityRecord#onConfigurationChanged(Configuration)} since when we exit PiP,
|
||||
// we set final windowing mode on the ActivityRecord first and then on its Task when
|
||||
// the exit PiP transition finishes. Meanwhile, the exit transition is always
|
||||
// performed on its original task, reparent immediately in ActivityRecord breaks it.
|
||||
if (currentMode == WINDOWING_MODE_PINNED) {
|
||||
if (topActivity != null && topActivity.getLastParentBeforePip() != null) {
|
||||
// Do not reparent if the pinned task is in removal, indicated by the
|
||||
// force hidden flag.
|
||||
if (!isForceHidden()) {
|
||||
final Task lastParentBeforePip = topActivity.getLastParentBeforePip();
|
||||
topActivity.reparent(lastParentBeforePip,
|
||||
lastParentBeforePip.getChildCount() /* top */,
|
||||
"movePinnedActivityToOriginalTask");
|
||||
lastParentBeforePip.moveToFront("movePinnedActivityToOriginalTask");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (creating) {
|
||||
// Nothing else to do if we don't have a window container yet. E.g. call from ctor.
|
||||
@@ -7530,7 +7560,11 @@ class Task extends WindowContainer<WindowContainer> {
|
||||
final Task task = getBottomMostTask();
|
||||
setWindowingMode(WINDOWING_MODE_UNDEFINED);
|
||||
|
||||
getDisplayArea().positionChildAt(POSITION_TOP, this, false /* includingParents */);
|
||||
// Task could have been removed from the hierarchy due to windowing mode change
|
||||
// where its only child is reparented back to their original parent task.
|
||||
if (isAttached()) {
|
||||
getDisplayArea().positionChildAt(POSITION_TOP, this, false /* includingParents */);
|
||||
}
|
||||
|
||||
mTaskSupervisor.scheduleUpdatePictureInPictureModeIfNeeded(task, this);
|
||||
});
|
||||
@@ -7831,6 +7865,7 @@ class Task extends WindowContainer<WindowContainer> {
|
||||
|
||||
proto.write(CREATED_BY_ORGANIZER, mCreatedByOrganizer);
|
||||
proto.write(AFFINITY, affinity);
|
||||
proto.write(HAS_CHILD_PIP_ACTIVITY, mChildPipActivity != null);
|
||||
|
||||
proto.end(token);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user