Merge "Notify organizer when the last activity in TaskFragment enter PiP" into tm-dev am: 88671a050f
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/17713423 Change-Id: If7c315659887740f520ac5f53125a21971bda37f Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
@@ -52,9 +52,6 @@ public final class TaskFragmentInfo implements Parcelable {
|
|||||||
@NonNull
|
@NonNull
|
||||||
private final Configuration mConfiguration = new Configuration();
|
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. */
|
/** The number of the running activities in the TaskFragment. */
|
||||||
private final int mRunningActivityCount;
|
private final int mRunningActivityCount;
|
||||||
|
|
||||||
@@ -77,21 +74,27 @@ public final class TaskFragmentInfo implements Parcelable {
|
|||||||
*/
|
*/
|
||||||
private final boolean mIsTaskClearedForReuse;
|
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 */
|
/** @hide */
|
||||||
public TaskFragmentInfo(
|
public TaskFragmentInfo(
|
||||||
@NonNull IBinder fragmentToken, @NonNull WindowContainerToken token,
|
@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 isVisible, @NonNull List<IBinder> activities, @NonNull Point positionInParent,
|
||||||
boolean isTaskClearedForReuse) {
|
boolean isTaskClearedForReuse, boolean isTaskFragmentClearedForPip) {
|
||||||
mFragmentToken = requireNonNull(fragmentToken);
|
mFragmentToken = requireNonNull(fragmentToken);
|
||||||
mToken = requireNonNull(token);
|
mToken = requireNonNull(token);
|
||||||
mConfiguration.setTo(configuration);
|
mConfiguration.setTo(configuration);
|
||||||
mIsEmpty = isEmpty;
|
|
||||||
mRunningActivityCount = runningActivityCount;
|
mRunningActivityCount = runningActivityCount;
|
||||||
mIsVisible = isVisible;
|
mIsVisible = isVisible;
|
||||||
mActivities.addAll(activities);
|
mActivities.addAll(activities);
|
||||||
mPositionInParent = requireNonNull(positionInParent);
|
mPositionInParent = requireNonNull(positionInParent);
|
||||||
mIsTaskClearedForReuse = isTaskClearedForReuse;
|
mIsTaskClearedForReuse = isTaskClearedForReuse;
|
||||||
|
mIsTaskFragmentClearedForPip = isTaskFragmentClearedForPip;
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
@@ -110,7 +113,7 @@ public final class TaskFragmentInfo implements Parcelable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean isEmpty() {
|
public boolean isEmpty() {
|
||||||
return mIsEmpty;
|
return mRunningActivityCount == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasRunningActivity() {
|
public boolean hasRunningActivity() {
|
||||||
@@ -140,6 +143,11 @@ public final class TaskFragmentInfo implements Parcelable {
|
|||||||
return mIsTaskClearedForReuse;
|
return mIsTaskClearedForReuse;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @hide */
|
||||||
|
public boolean isTaskFragmentClearedForPip() {
|
||||||
|
return mIsTaskFragmentClearedForPip;
|
||||||
|
}
|
||||||
|
|
||||||
@WindowingMode
|
@WindowingMode
|
||||||
public int getWindowingMode() {
|
public int getWindowingMode() {
|
||||||
return mConfiguration.windowConfiguration.getWindowingMode();
|
return mConfiguration.windowConfiguration.getWindowingMode();
|
||||||
@@ -156,25 +164,25 @@ public final class TaskFragmentInfo implements Parcelable {
|
|||||||
|
|
||||||
return mFragmentToken.equals(that.mFragmentToken)
|
return mFragmentToken.equals(that.mFragmentToken)
|
||||||
&& mToken.equals(that.mToken)
|
&& mToken.equals(that.mToken)
|
||||||
&& mIsEmpty == that.mIsEmpty
|
|
||||||
&& mRunningActivityCount == that.mRunningActivityCount
|
&& mRunningActivityCount == that.mRunningActivityCount
|
||||||
&& mIsVisible == that.mIsVisible
|
&& mIsVisible == that.mIsVisible
|
||||||
&& getWindowingMode() == that.getWindowingMode()
|
&& getWindowingMode() == that.getWindowingMode()
|
||||||
&& mActivities.equals(that.mActivities)
|
&& mActivities.equals(that.mActivities)
|
||||||
&& mPositionInParent.equals(that.mPositionInParent)
|
&& mPositionInParent.equals(that.mPositionInParent)
|
||||||
&& mIsTaskClearedForReuse == that.mIsTaskClearedForReuse;
|
&& mIsTaskClearedForReuse == that.mIsTaskClearedForReuse
|
||||||
|
&& mIsTaskFragmentClearedForPip == that.mIsTaskFragmentClearedForPip;
|
||||||
}
|
}
|
||||||
|
|
||||||
private TaskFragmentInfo(Parcel in) {
|
private TaskFragmentInfo(Parcel in) {
|
||||||
mFragmentToken = in.readStrongBinder();
|
mFragmentToken = in.readStrongBinder();
|
||||||
mToken = in.readTypedObject(WindowContainerToken.CREATOR);
|
mToken = in.readTypedObject(WindowContainerToken.CREATOR);
|
||||||
mConfiguration.readFromParcel(in);
|
mConfiguration.readFromParcel(in);
|
||||||
mIsEmpty = in.readBoolean();
|
|
||||||
mRunningActivityCount = in.readInt();
|
mRunningActivityCount = in.readInt();
|
||||||
mIsVisible = in.readBoolean();
|
mIsVisible = in.readBoolean();
|
||||||
in.readBinderList(mActivities);
|
in.readBinderList(mActivities);
|
||||||
mPositionInParent = requireNonNull(in.readTypedObject(Point.CREATOR));
|
mPositionInParent = requireNonNull(in.readTypedObject(Point.CREATOR));
|
||||||
mIsTaskClearedForReuse = in.readBoolean();
|
mIsTaskClearedForReuse = in.readBoolean();
|
||||||
|
mIsTaskFragmentClearedForPip = in.readBoolean();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @hide */
|
/** @hide */
|
||||||
@@ -183,12 +191,12 @@ public final class TaskFragmentInfo implements Parcelable {
|
|||||||
dest.writeStrongBinder(mFragmentToken);
|
dest.writeStrongBinder(mFragmentToken);
|
||||||
dest.writeTypedObject(mToken, flags);
|
dest.writeTypedObject(mToken, flags);
|
||||||
mConfiguration.writeToParcel(dest, flags);
|
mConfiguration.writeToParcel(dest, flags);
|
||||||
dest.writeBoolean(mIsEmpty);
|
|
||||||
dest.writeInt(mRunningActivityCount);
|
dest.writeInt(mRunningActivityCount);
|
||||||
dest.writeBoolean(mIsVisible);
|
dest.writeBoolean(mIsVisible);
|
||||||
dest.writeBinderList(mActivities);
|
dest.writeBinderList(mActivities);
|
||||||
dest.writeTypedObject(mPositionInParent, flags);
|
dest.writeTypedObject(mPositionInParent, flags);
|
||||||
dest.writeBoolean(mIsTaskClearedForReuse);
|
dest.writeBoolean(mIsTaskClearedForReuse);
|
||||||
|
dest.writeBoolean(mIsTaskFragmentClearedForPip);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
@@ -210,12 +218,12 @@ public final class TaskFragmentInfo implements Parcelable {
|
|||||||
return "TaskFragmentInfo{"
|
return "TaskFragmentInfo{"
|
||||||
+ " fragmentToken=" + mFragmentToken
|
+ " fragmentToken=" + mFragmentToken
|
||||||
+ " token=" + mToken
|
+ " token=" + mToken
|
||||||
+ " isEmpty=" + mIsEmpty
|
|
||||||
+ " runningActivityCount=" + mRunningActivityCount
|
+ " runningActivityCount=" + mRunningActivityCount
|
||||||
+ " isVisible=" + mIsVisible
|
+ " isVisible=" + mIsVisible
|
||||||
+ " activities=" + mActivities
|
+ " activities=" + mActivities
|
||||||
+ " positionInParent=" + mPositionInParent
|
+ " positionInParent=" + mPositionInParent
|
||||||
+ " isTaskClearedForReuse=" + mIsTaskClearedForReuse
|
+ " 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
|
// Check if there are no running activities - consider the container empty if there are no
|
||||||
// non-finishing activities left.
|
// non-finishing activities left.
|
||||||
if (!taskFragmentInfo.hasRunningActivity()) {
|
if (!taskFragmentInfo.hasRunningActivity()) {
|
||||||
// TODO(b/225371112): Don't finish dependent if the last activity is moved to the PIP
|
if (taskFragmentInfo.isTaskFragmentClearedForPip()) {
|
||||||
// Task.
|
// Do not finish the dependents if the last activity is reparented to PiP.
|
||||||
// Do not finish the dependents if this TaskFragment was cleared due to launching
|
// Instead, the original split should be cleanup, and the dependent may be expanded
|
||||||
// activity in the Task.
|
// to fullscreen.
|
||||||
final boolean shouldFinishDependent =
|
cleanupForEnterPip(container);
|
||||||
!taskFragmentInfo.isTaskClearedForReuse();
|
mPresenter.cleanupContainer(container, false /* shouldFinishDependent */);
|
||||||
mPresenter.cleanupContainer(container, 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) {
|
} else if (wasInPip && isInPip) {
|
||||||
// No update until exit PIP.
|
// No update until exit PIP.
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -2006,6 +2006,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
|
|||||||
// of the activity entering PIP
|
// of the activity entering PIP
|
||||||
r.getDisplayContent().prepareAppTransition(TRANSIT_NONE);
|
r.getDisplayContent().prepareAppTransition(TRANSIT_NONE);
|
||||||
|
|
||||||
|
final TaskFragment organizedTf = r.getOrganizedTaskFragment();
|
||||||
// TODO: Does it make sense to only count non-finishing activities?
|
// TODO: Does it make sense to only count non-finishing activities?
|
||||||
final boolean singleActivity = task.getActivityCount() == 1;
|
final boolean singleActivity = task.getActivityCount() == 1;
|
||||||
final Task rootTask;
|
final Task rootTask;
|
||||||
@@ -2043,6 +2044,14 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
|
|||||||
task.clearLastRecentsAnimationTransaction(false /* forceRemoveOverlay */);
|
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
|
// There are multiple activities in the task and moving the top activity should
|
||||||
// reveal/leave the other activities in their original task.
|
// reveal/leave the other activities in their original task.
|
||||||
// On the other hand, ActivityRecord#onParentChanged takes care of setting the
|
// 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
|
// Reset the state that indicates it can enter PiP while pausing after we've moved it
|
||||||
// to the root pinned task
|
// to the root pinned task
|
||||||
r.supportsEnterPipOnTaskSwitch = false;
|
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 {
|
} finally {
|
||||||
mService.continueWindowLayout();
|
mService.continueWindowLayout();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -192,6 +192,12 @@ class TaskFragment extends WindowContainer<WindowContainer> {
|
|||||||
*/
|
*/
|
||||||
boolean mClearedTaskForReuse;
|
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
|
* 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.
|
* 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());
|
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() {
|
boolean isTopActivityFocusable() {
|
||||||
final ActivityRecord r = topRunningActivity();
|
final ActivityRecord r = topRunningActivity();
|
||||||
return r != null ? r.isFocusable()
|
return r != null ? r.isFocusable()
|
||||||
@@ -1709,6 +1725,7 @@ class TaskFragment extends WindowContainer<WindowContainer> {
|
|||||||
void addChild(WindowContainer child, int index) {
|
void addChild(WindowContainer child, int index) {
|
||||||
ActivityRecord r = topRunningActivity();
|
ActivityRecord r = topRunningActivity();
|
||||||
mClearedTaskForReuse = false;
|
mClearedTaskForReuse = false;
|
||||||
|
mClearedTaskFragmentForPip = false;
|
||||||
|
|
||||||
boolean isAddingActivity = child.asActivityRecord() != null;
|
boolean isAddingActivity = child.asActivityRecord() != null;
|
||||||
final Task task = isAddingActivity ? getTask() : null;
|
final Task task = isAddingActivity ? getTask() : null;
|
||||||
@@ -2253,22 +2270,16 @@ class TaskFragment extends WindowContainer<WindowContainer> {
|
|||||||
}
|
}
|
||||||
final Point positionInParent = new Point();
|
final Point positionInParent = new Point();
|
||||||
getRelativePosition(positionInParent);
|
getRelativePosition(positionInParent);
|
||||||
final int[] runningActivityCount = new int[1];
|
|
||||||
forAllActivities(a -> {
|
|
||||||
if (!a.finishing) {
|
|
||||||
runningActivityCount[0]++;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return new TaskFragmentInfo(
|
return new TaskFragmentInfo(
|
||||||
mFragmentToken,
|
mFragmentToken,
|
||||||
mRemoteToken.toWindowContainerToken(),
|
mRemoteToken.toWindowContainerToken(),
|
||||||
getConfiguration(),
|
getConfiguration(),
|
||||||
runningActivityCount[0] == 0,
|
getNonFinishingActivityCount(),
|
||||||
runningActivityCount[0],
|
|
||||||
isVisible(),
|
isVisible(),
|
||||||
childActivities,
|
childActivities,
|
||||||
positionInParent,
|
positionInParent,
|
||||||
mClearedTaskForReuse);
|
mClearedTaskForReuse,
|
||||||
|
mClearedTaskFragmentForPip);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
|
|||||||
@@ -209,7 +209,7 @@ public class TaskFragmentTest extends WindowTestsBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testEmbeddedTaskFragmentEnterPip_resetOrganizerOverrideConfig() {
|
public void testEmbeddedTaskFragmentEnterPip_singleActivity_resetOrganizerOverrideConfig() {
|
||||||
final TaskFragment taskFragment = new TaskFragmentBuilder(mAtm)
|
final TaskFragment taskFragment = new TaskFragmentBuilder(mAtm)
|
||||||
.setOrganizer(mOrganizer)
|
.setOrganizer(mOrganizer)
|
||||||
.setFragmentToken(new Binder())
|
.setFragmentToken(new Binder())
|
||||||
@@ -241,6 +241,38 @@ public class TaskFragmentTest extends WindowTestsBase {
|
|||||||
assertEquals(Configuration.EMPTY, taskFragment.getRequestedOverrideConfiguration());
|
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
|
@Test
|
||||||
public void testActivityHasOverlayOverUntrustedModeEmbedded() {
|
public void testActivityHasOverlayOverUntrustedModeEmbedded() {
|
||||||
final Task rootTask = createTask(mDisplayContent, WINDOWING_MODE_MULTI_WINDOW,
|
final Task rootTask = createTask(mDisplayContent, WINDOWING_MODE_MULTI_WINDOW,
|
||||||
|
|||||||
Reference in New Issue
Block a user