diff options
4 files changed, 78 insertions, 28 deletions
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java index 003268b33cdc..1f78f6f6e129 100644 --- a/services/core/java/com/android/server/wm/ActivityStarter.java +++ b/services/core/java/com/android/server/wm/ActivityStarter.java @@ -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() diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index 6ab3916425f9..a46544d6c902 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -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(); diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java index 3be70114172b..31bc2818978d 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java @@ -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 */); diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java index 189a1dacb891..971826dd09b6 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java @@ -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); + } } |