Merge "Notify organizer when the last activity in TaskFragment enter PiP" into tm-dev
This commit is contained in:
@@ -52,9 +52,6 @@ public final class TaskFragmentInfo implements Parcelable {
|
||||
@NonNull
|
||||
private final Configuration mConfiguration = new Configuration();
|
||||
|
||||
/** Whether the TaskFragment contains any child Window Container. */
|
||||
private final boolean mIsEmpty;
|
||||
|
||||
/** The number of the running activities in the TaskFragment. */
|
||||
private final int mRunningActivityCount;
|
||||
|
||||
@@ -77,21 +74,27 @@ public final class TaskFragmentInfo implements Parcelable {
|
||||
*/
|
||||
private final boolean mIsTaskClearedForReuse;
|
||||
|
||||
/**
|
||||
* Whether the last running activity in the TaskFragment was reparented to a different Task
|
||||
* because it is entering PiP.
|
||||
*/
|
||||
private final boolean mIsTaskFragmentClearedForPip;
|
||||
|
||||
/** @hide */
|
||||
public TaskFragmentInfo(
|
||||
@NonNull IBinder fragmentToken, @NonNull WindowContainerToken token,
|
||||
@NonNull Configuration configuration, boolean isEmpty, int runningActivityCount,
|
||||
@NonNull Configuration configuration, int runningActivityCount,
|
||||
boolean isVisible, @NonNull List<IBinder> activities, @NonNull Point positionInParent,
|
||||
boolean isTaskClearedForReuse) {
|
||||
boolean isTaskClearedForReuse, boolean isTaskFragmentClearedForPip) {
|
||||
mFragmentToken = requireNonNull(fragmentToken);
|
||||
mToken = requireNonNull(token);
|
||||
mConfiguration.setTo(configuration);
|
||||
mIsEmpty = isEmpty;
|
||||
mRunningActivityCount = runningActivityCount;
|
||||
mIsVisible = isVisible;
|
||||
mActivities.addAll(activities);
|
||||
mPositionInParent = requireNonNull(positionInParent);
|
||||
mIsTaskClearedForReuse = isTaskClearedForReuse;
|
||||
mIsTaskFragmentClearedForPip = isTaskFragmentClearedForPip;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@@ -110,7 +113,7 @@ public final class TaskFragmentInfo implements Parcelable {
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return mIsEmpty;
|
||||
return mRunningActivityCount == 0;
|
||||
}
|
||||
|
||||
public boolean hasRunningActivity() {
|
||||
@@ -140,6 +143,11 @@ public final class TaskFragmentInfo implements Parcelable {
|
||||
return mIsTaskClearedForReuse;
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public boolean isTaskFragmentClearedForPip() {
|
||||
return mIsTaskFragmentClearedForPip;
|
||||
}
|
||||
|
||||
@WindowingMode
|
||||
public int getWindowingMode() {
|
||||
return mConfiguration.windowConfiguration.getWindowingMode();
|
||||
@@ -156,25 +164,25 @@ public final class TaskFragmentInfo implements Parcelable {
|
||||
|
||||
return mFragmentToken.equals(that.mFragmentToken)
|
||||
&& mToken.equals(that.mToken)
|
||||
&& mIsEmpty == that.mIsEmpty
|
||||
&& mRunningActivityCount == that.mRunningActivityCount
|
||||
&& mIsVisible == that.mIsVisible
|
||||
&& getWindowingMode() == that.getWindowingMode()
|
||||
&& mActivities.equals(that.mActivities)
|
||||
&& mPositionInParent.equals(that.mPositionInParent)
|
||||
&& mIsTaskClearedForReuse == that.mIsTaskClearedForReuse;
|
||||
&& mIsTaskClearedForReuse == that.mIsTaskClearedForReuse
|
||||
&& mIsTaskFragmentClearedForPip == that.mIsTaskFragmentClearedForPip;
|
||||
}
|
||||
|
||||
private TaskFragmentInfo(Parcel in) {
|
||||
mFragmentToken = in.readStrongBinder();
|
||||
mToken = in.readTypedObject(WindowContainerToken.CREATOR);
|
||||
mConfiguration.readFromParcel(in);
|
||||
mIsEmpty = in.readBoolean();
|
||||
mRunningActivityCount = in.readInt();
|
||||
mIsVisible = in.readBoolean();
|
||||
in.readBinderList(mActivities);
|
||||
mPositionInParent = requireNonNull(in.readTypedObject(Point.CREATOR));
|
||||
mIsTaskClearedForReuse = in.readBoolean();
|
||||
mIsTaskFragmentClearedForPip = in.readBoolean();
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
@@ -183,12 +191,12 @@ public final class TaskFragmentInfo implements Parcelable {
|
||||
dest.writeStrongBinder(mFragmentToken);
|
||||
dest.writeTypedObject(mToken, flags);
|
||||
mConfiguration.writeToParcel(dest, flags);
|
||||
dest.writeBoolean(mIsEmpty);
|
||||
dest.writeInt(mRunningActivityCount);
|
||||
dest.writeBoolean(mIsVisible);
|
||||
dest.writeBinderList(mActivities);
|
||||
dest.writeTypedObject(mPositionInParent, flags);
|
||||
dest.writeBoolean(mIsTaskClearedForReuse);
|
||||
dest.writeBoolean(mIsTaskFragmentClearedForPip);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@@ -210,12 +218,12 @@ public final class TaskFragmentInfo implements Parcelable {
|
||||
return "TaskFragmentInfo{"
|
||||
+ " fragmentToken=" + mFragmentToken
|
||||
+ " token=" + mToken
|
||||
+ " isEmpty=" + mIsEmpty
|
||||
+ " runningActivityCount=" + mRunningActivityCount
|
||||
+ " isVisible=" + mIsVisible
|
||||
+ " activities=" + mActivities
|
||||
+ " positionInParent=" + mPositionInParent
|
||||
+ " isTaskClearedForReuse=" + mIsTaskClearedForReuse
|
||||
+ " isTaskFragmentClearedForPip" + mIsTaskFragmentClearedForPip
|
||||
+ "}";
|
||||
}
|
||||
|
||||
|
||||
@@ -155,13 +155,18 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
|
||||
// Check if there are no running activities - consider the container empty if there are no
|
||||
// non-finishing activities left.
|
||||
if (!taskFragmentInfo.hasRunningActivity()) {
|
||||
// TODO(b/225371112): Don't finish dependent if the last activity is moved to the PIP
|
||||
// Task.
|
||||
// Do not finish the dependents if this TaskFragment was cleared due to launching
|
||||
// activity in the Task.
|
||||
final boolean shouldFinishDependent =
|
||||
!taskFragmentInfo.isTaskClearedForReuse();
|
||||
mPresenter.cleanupContainer(container, shouldFinishDependent);
|
||||
if (taskFragmentInfo.isTaskFragmentClearedForPip()) {
|
||||
// Do not finish the dependents if the last activity is reparented to PiP.
|
||||
// Instead, the original split should be cleanup, and the dependent may be expanded
|
||||
// to fullscreen.
|
||||
cleanupForEnterPip(container);
|
||||
mPresenter.cleanupContainer(container, false /* shouldFinishDependent */);
|
||||
} else {
|
||||
// Do not finish the dependents if this TaskFragment was cleared due to launching
|
||||
// activity in the Task.
|
||||
final boolean shouldFinishDependent = !taskFragmentInfo.isTaskClearedForReuse();
|
||||
mPresenter.cleanupContainer(container, shouldFinishDependent);
|
||||
}
|
||||
} else if (wasInPip && isInPip) {
|
||||
// No update until exit PIP.
|
||||
return;
|
||||
|
||||
@@ -2006,6 +2006,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
|
||||
// of the activity entering PIP
|
||||
r.getDisplayContent().prepareAppTransition(TRANSIT_NONE);
|
||||
|
||||
final TaskFragment organizedTf = r.getOrganizedTaskFragment();
|
||||
// TODO: Does it make sense to only count non-finishing activities?
|
||||
final boolean singleActivity = task.getActivityCount() == 1;
|
||||
final Task rootTask;
|
||||
@@ -2043,6 +2044,14 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
|
||||
task.clearLastRecentsAnimationTransaction(false /* forceRemoveOverlay */);
|
||||
}
|
||||
|
||||
// The organized TaskFragment is becoming empty because this activity is reparented
|
||||
// to a new PIP Task. In this case, we should notify the organizer about why the
|
||||
// TaskFragment becomes empty.
|
||||
if (organizedTf != null && organizedTf.getNonFinishingActivityCount() == 1
|
||||
&& organizedTf.getTopNonFinishingActivity() == r) {
|
||||
organizedTf.mClearedTaskFragmentForPip = true;
|
||||
}
|
||||
|
||||
// There are multiple activities in the task and moving the top activity should
|
||||
// reveal/leave the other activities in their original task.
|
||||
// On the other hand, ActivityRecord#onParentChanged takes care of setting the
|
||||
@@ -2107,6 +2116,13 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
|
||||
// Reset the state that indicates it can enter PiP while pausing after we've moved it
|
||||
// to the root pinned task
|
||||
r.supportsEnterPipOnTaskSwitch = false;
|
||||
|
||||
if (organizedTf != null && organizedTf.mClearedTaskFragmentForPip) {
|
||||
// Dispatch the pending info to TaskFragmentOrganizer before PIP animation.
|
||||
// Otherwise, it will keep waiting for the empty TaskFragment to be non-empty.
|
||||
mService.mTaskFragmentOrganizerController.dispatchPendingInfoChangedEvent(
|
||||
organizedTf);
|
||||
}
|
||||
} finally {
|
||||
mService.continueWindowLayout();
|
||||
}
|
||||
|
||||
@@ -192,6 +192,12 @@ class TaskFragment extends WindowContainer<WindowContainer> {
|
||||
*/
|
||||
boolean mClearedTaskForReuse;
|
||||
|
||||
/**
|
||||
* The last running activity of the TaskFragment was reparented to a different Task because it
|
||||
* is entering PiP.
|
||||
*/
|
||||
boolean mClearedTaskFragmentForPip;
|
||||
|
||||
/**
|
||||
* When we are in the process of pausing an activity, before starting the
|
||||
* next one, this variable holds the activity that is currently being paused.
|
||||
@@ -847,6 +853,16 @@ class TaskFragment extends WindowContainer<WindowContainer> {
|
||||
return getActivity((r) -> r.canBeTopRunning() && r.getTask() == this.getTask());
|
||||
}
|
||||
|
||||
int getNonFinishingActivityCount() {
|
||||
final int[] runningActivityCount = new int[1];
|
||||
forAllActivities(a -> {
|
||||
if (!a.finishing) {
|
||||
runningActivityCount[0]++;
|
||||
}
|
||||
});
|
||||
return runningActivityCount[0];
|
||||
}
|
||||
|
||||
boolean isTopActivityFocusable() {
|
||||
final ActivityRecord r = topRunningActivity();
|
||||
return r != null ? r.isFocusable()
|
||||
@@ -1709,6 +1725,7 @@ class TaskFragment extends WindowContainer<WindowContainer> {
|
||||
void addChild(WindowContainer child, int index) {
|
||||
ActivityRecord r = topRunningActivity();
|
||||
mClearedTaskForReuse = false;
|
||||
mClearedTaskFragmentForPip = false;
|
||||
|
||||
boolean isAddingActivity = child.asActivityRecord() != null;
|
||||
final Task task = isAddingActivity ? getTask() : null;
|
||||
@@ -2253,22 +2270,16 @@ class TaskFragment extends WindowContainer<WindowContainer> {
|
||||
}
|
||||
final Point positionInParent = new Point();
|
||||
getRelativePosition(positionInParent);
|
||||
final int[] runningActivityCount = new int[1];
|
||||
forAllActivities(a -> {
|
||||
if (!a.finishing) {
|
||||
runningActivityCount[0]++;
|
||||
}
|
||||
});
|
||||
return new TaskFragmentInfo(
|
||||
mFragmentToken,
|
||||
mRemoteToken.toWindowContainerToken(),
|
||||
getConfiguration(),
|
||||
runningActivityCount[0] == 0,
|
||||
runningActivityCount[0],
|
||||
getNonFinishingActivityCount(),
|
||||
isVisible(),
|
||||
childActivities,
|
||||
positionInParent,
|
||||
mClearedTaskForReuse);
|
||||
mClearedTaskForReuse,
|
||||
mClearedTaskFragmentForPip);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
|
||||
@@ -209,7 +209,7 @@ public class TaskFragmentTest extends WindowTestsBase {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEmbeddedTaskFragmentEnterPip_resetOrganizerOverrideConfig() {
|
||||
public void testEmbeddedTaskFragmentEnterPip_singleActivity_resetOrganizerOverrideConfig() {
|
||||
final TaskFragment taskFragment = new TaskFragmentBuilder(mAtm)
|
||||
.setOrganizer(mOrganizer)
|
||||
.setFragmentToken(new Binder())
|
||||
@@ -241,6 +241,38 @@ public class TaskFragmentTest extends WindowTestsBase {
|
||||
assertEquals(Configuration.EMPTY, taskFragment.getRequestedOverrideConfiguration());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEmbeddedTaskFragmentEnterPip_multiActivities_notifyOrganizer() {
|
||||
final Task task = createTask(mDisplayContent);
|
||||
final TaskFragment taskFragment0 = new TaskFragmentBuilder(mAtm)
|
||||
.setParentTask(task)
|
||||
.setOrganizer(mOrganizer)
|
||||
.setFragmentToken(new Binder())
|
||||
.createActivityCount(1)
|
||||
.build();
|
||||
final TaskFragment taskFragment1 = new TaskFragmentBuilder(mAtm)
|
||||
.setParentTask(task)
|
||||
.setOrganizer(mOrganizer)
|
||||
.setFragmentToken(new Binder())
|
||||
.createActivityCount(1)
|
||||
.build();
|
||||
final ActivityRecord activity0 = taskFragment0.getTopMostActivity();
|
||||
spyOn(mAtm.mTaskFragmentOrganizerController);
|
||||
|
||||
// Move activity to pinned.
|
||||
mRootWindowContainer.moveActivityToPinnedRootTask(activity0,
|
||||
null /* launchIntoPipHostActivity */, "test");
|
||||
|
||||
// Ensure taskFragment requested config is reset.
|
||||
assertTrue(taskFragment0.mClearedTaskFragmentForPip);
|
||||
assertFalse(taskFragment1.mClearedTaskFragmentForPip);
|
||||
final TaskFragmentInfo info = taskFragment0.getTaskFragmentInfo();
|
||||
assertTrue(info.isTaskFragmentClearedForPip());
|
||||
assertTrue(info.isEmpty());
|
||||
verify(mAtm.mTaskFragmentOrganizerController)
|
||||
.dispatchPendingInfoChangedEvent(taskFragment0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testActivityHasOverlayOverUntrustedModeEmbedded() {
|
||||
final Task rootTask = createTask(mDisplayContent, WINDOWING_MODE_MULTI_WINDOW,
|
||||
|
||||
Reference in New Issue
Block a user