Support enter PIP on Task switch for TaskFragment

Before, when the Task contains more than one visible TaskFragments,
only the activity on the top would support enter pip on task swtich.
Now, activities on any visible TaskFragments can have the chance to
enter pip on task switch.

Bug: 225371112
Test: atest WmTests:TaskFragmentTest
Change-Id: I1721b3e0476927dffe6d8367c8ede433b10af837
This commit is contained in:
Chris Li
2022-03-25 23:31:09 +08:00
parent 94bba02785
commit 96f7464dbd
4 changed files with 78 additions and 28 deletions

View File

@@ -1874,9 +1874,8 @@ class ActivityStarter {
false /* forceSend */, mStartActivity);
final boolean isTaskSwitch = startedTask != prevTopTask && !startedTask.isEmbedded();
mTargetRootTask.startActivityLocked(mStartActivity,
topRootTask != null ? topRootTask.getTopNonFinishingActivity() : null, newTask,
isTaskSwitch, mOptions, sourceRecord);
mTargetRootTask.startActivityLocked(mStartActivity, topRootTask, newTask, isTaskSwitch,
mOptions, sourceRecord);
if (mDoResume) {
final ActivityRecord topTaskActivity = startedTask.topRunningActivityLocked();
if (!mTargetRootTask.isTopActivityFocusable()

View File

@@ -5031,9 +5031,9 @@ class Task extends TaskFragment {
return mRootWindowContainer.resumeHomeActivity(prev, reason, getDisplayArea());
}
void startActivityLocked(ActivityRecord r, @Nullable ActivityRecord focusedTopActivity,
boolean newTask, boolean isTaskSwitch, ActivityOptions options,
@Nullable ActivityRecord sourceRecord) {
void startActivityLocked(ActivityRecord r, @Nullable Task topTask, boolean newTask,
boolean isTaskSwitch, ActivityOptions options, @Nullable ActivityRecord sourceRecord) {
final ActivityRecord pipCandidate = findEnterPipOnTaskSwitchCandidate(topTask);
Task rTask = r.getTask();
final boolean allowMoveToFront = options == null || !options.getAvoidMoveToFront();
final boolean isOrhasTask = rTask == this || hasChild(rTask);
@@ -5099,10 +5099,8 @@ class Task extends TaskFragment {
// supporting picture-in-picture while pausing only if the starting activity
// would not be considered an overlay on top of the current activity
// (eg. not fullscreen, or the assistant)
if (canEnterPipOnTaskSwitch(focusedTopActivity,
null /* toFrontTask */, r, options)) {
focusedTopActivity.supportsEnterPipOnTaskSwitch = true;
}
enableEnterPipOnTaskSwitch(pipCandidate,
null /* toFrontTask */, r, options);
transit = TRANSIT_OLD_TASK_OPEN;
}
}
@@ -5159,20 +5157,44 @@ class Task extends TaskFragment {
}
}
/** On Task switch, finds the top activity that supports PiP. */
@Nullable
static ActivityRecord findEnterPipOnTaskSwitchCandidate(@Nullable Task topTask) {
if (topTask == null) {
return null;
}
final ActivityRecord[] candidate = new ActivityRecord[1];
topTask.forAllLeafTaskFragments(tf -> {
// Find the top activity that may enter Pip while pausing.
final ActivityRecord topActivity = tf.getTopNonFinishingActivity();
if (topActivity != null && topActivity.isState(RESUMED, PAUSING)
&& topActivity.supportsPictureInPicture()) {
candidate[0] = topActivity;
return true;
}
return false;
});
return candidate[0];
}
/**
* @return Whether the switch to another task can trigger the currently running activity to
* When switching to another Task, marks the currently PiP candidate activity as supporting to
* enter PiP while it is pausing (if supported). Only one of {@param toFrontTask} or
* {@param toFrontActivity} should be set.
*/
private boolean canEnterPipOnTaskSwitch(ActivityRecord pipCandidate,
Task toFrontTask, ActivityRecord toFrontActivity, ActivityOptions opts) {
private static void enableEnterPipOnTaskSwitch(@Nullable ActivityRecord pipCandidate,
@Nullable Task toFrontTask, @Nullable ActivityRecord toFrontActivity,
@Nullable ActivityOptions opts) {
if (pipCandidate == null) {
return;
}
if (opts != null && opts.disallowEnterPictureInPictureWhileLaunching()) {
// Ensure the caller has requested not to trigger auto-enter PiP
return false;
return;
}
if (pipCandidate == null || pipCandidate.inPinnedWindowingMode()) {
// Ensure that we do not trigger entering PiP an activity on the root pinned task
return false;
if (pipCandidate.inPinnedWindowingMode()) {
// Ensure that we do not trigger entering PiP an activity on the root pinned task.
return;
}
final boolean isTransient = opts != null && opts.getTransientLaunch();
final Task targetRootTask = toFrontTask != null
@@ -5181,9 +5203,10 @@ class Task extends TaskFragment {
// Ensure the task/activity being brought forward is not the assistant and is not
// transient. In the case of transient-launch, we want to wait until the end of the
// transition and only allow switch if the transient launch was committed.
return false;
return;
}
return true;
pipCandidate.supportsEnterPipOnTaskSwitch = true;
}
/**
@@ -5492,9 +5515,8 @@ class Task extends TaskFragment {
AppTimeTracker timeTracker, boolean deferResume, String reason) {
if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "moveTaskToFront: " + tr);
final Task topRootTask = getDisplayArea().getTopRootTask();
final ActivityRecord topActivity = topRootTask != null
? topRootTask.getTopNonFinishingActivity() : null;
final ActivityRecord pipCandidate = findEnterPipOnTaskSwitchCandidate(
getDisplayArea().getTopRootTask());
if (tr != this && !tr.isDescendantOf(this)) {
// nothing to do!
@@ -5549,10 +5571,7 @@ class Task extends TaskFragment {
// picture-in-picture while paused only if the task would not be considered an oerlay
// on top
// of the current activity (eg. not fullscreen, or the assistant)
if (canEnterPipOnTaskSwitch(topActivity, tr, null /* toFrontActivity */,
options)) {
topActivity.supportsEnterPipOnTaskSwitch = true;
}
enableEnterPipOnTaskSwitch(pipCandidate, tr, null /* toFrontActivity */, options);
if (!deferResume) {
mRootWindowContainer.resumeFocusedTasksTopActivities();

View File

@@ -2693,7 +2693,7 @@ public class ActivityRecordTests extends WindowTestsBase {
.resumeFocusedTasksTopActivities();
// Make mVisibleSetFromTransferredStartingWindow true.
final ActivityRecord middle = new ActivityBuilder(mAtm).setTask(task).build();
task.startActivityLocked(middle, null /* focusedTopActivity */,
task.startActivityLocked(middle, null /* topTask */,
false /* newTask */, false /* isTaskSwitch */, null /* options */,
null /* sourceRecord */);
middle.makeFinishingLocked();
@@ -2706,7 +2706,7 @@ public class ActivityRecordTests extends WindowTestsBase {
// a visible activity.
top.setVisible(false);
// The finishing middle should be able to transfer starting window to top.
task.startActivityLocked(top, null /* focusedTopActivity */,
task.startActivityLocked(top, null /* topTask */,
false /* newTask */, false /* isTaskSwitch */, null /* options */,
null /* sourceRecord */);

View File

@@ -21,9 +21,11 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.server.wm.ActivityRecord.State.RESUMED;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.clearInvocations;
import android.graphics.Rect;
@@ -170,4 +172,34 @@ public class TaskFragmentTest extends WindowTestsBase {
false /* preserveWindows */);
assertEquals(true, activityBelow.isVisibleRequested());
}
@Test
public void testMoveTaskToFront_supportsEnterPipOnTaskSwitchForAdjacentTaskFragment() {
final Task bottomTask = createTask(mDisplayContent);
final ActivityRecord bottomActivity = createActivityRecord(bottomTask);
final Task topTask = createTask(mDisplayContent);
// First create primary TF, and then secondary TF, so that the secondary will be on the top.
final TaskFragment primaryTf = createTaskFragmentWithParentTask(
topTask, false /* createEmbeddedTask */);
final TaskFragment secondaryTf = createTaskFragmentWithParentTask(
topTask, false /* createEmbeddedTask */);
final ActivityRecord primaryActivity = primaryTf.getTopMostActivity();
final ActivityRecord secondaryActivity = secondaryTf.getTopMostActivity();
doReturn(true).when(primaryActivity).supportsPictureInPicture();
doReturn(false).when(secondaryActivity).supportsPictureInPicture();
primaryTf.setAdjacentTaskFragment(secondaryTf, false /* moveAdjacentTogether */);
primaryActivity.setState(RESUMED, "test");
secondaryActivity.setState(RESUMED, "test");
assertEquals(topTask, bottomTask.getDisplayArea().getTopRootTask());
// When moving Task to front, the resumed activity that supports PIP should support enter
// PIP on Task switch even if it is not the topmost in the Task.
bottomTask.moveTaskToFront(bottomTask, false /* noAnimation */, null /* options */,
null /* timeTracker */, "test");
assertTrue(primaryActivity.supportsEnterPipOnTaskSwitch);
assertFalse(secondaryActivity.supportsEnterPipOnTaskSwitch);
}
}