Apply TaskFragmentOrganizer changes in one WCT (1/2)

Before, when receive TaskFragment transaction, we apply changes in
multiple WindowContainerTransactions. Now, update to apply all changes
in one WCT for the whole TaskFragment transaction.

Will update the TestAPI in the followup cl.

Bug: 240519866
Test: pass existing
Change-Id: I2e15b187bee95193874d76bc6963d3e01eba3910
This commit is contained in:
Chris Li
2022-08-02 16:08:37 +08:00
parent 99da16cdc8
commit e9a1037439
10 changed files with 271 additions and 215 deletions

View File

@@ -147,13 +147,25 @@ public class TaskFragmentOrganizer extends WindowOrganizer {
}
}
/** Called when a TaskFragment is created and organized by this organizer. */
/**
* Called when a TaskFragment is created and organized by this organizer.
*
* @param taskFragmentInfo Info of the TaskFragment that is created.
*/
public void onTaskFragmentAppeared(@NonNull TaskFragmentInfo taskFragmentInfo) {}
/** Called when the status of an organized TaskFragment is changed. */
/**
* Called when the status of an organized TaskFragment is changed.
*
* @param taskFragmentInfo Info of the TaskFragment that is changed.
*/
public void onTaskFragmentInfoChanged(@NonNull TaskFragmentInfo taskFragmentInfo) {}
/** Called when an organized TaskFragment is removed. */
/**
* Called when an organized TaskFragment is removed.
*
* @param taskFragmentInfo Info of the TaskFragment that is removed.
*/
public void onTaskFragmentVanished(@NonNull TaskFragmentInfo taskFragmentInfo) {}
/**
@@ -176,6 +188,9 @@ public class TaskFragmentOrganizer extends WindowOrganizer {
* For case like screen size change, it will trigger onTaskFragmentParentInfoChanged with new
* Task bounds, but may not trigger onTaskFragmentInfoChanged because there can be an override
* bounds.
*
* @param taskId Id of the parent Task that is changed.
* @param parentConfig Config of the parent Task.
* @hide
*/
public void onTaskFragmentParentInfoChanged(int taskId, @NonNull Configuration parentConfig) {
@@ -226,7 +241,7 @@ public class TaskFragmentOrganizer extends WindowOrganizer {
/**
* Called when an Activity is reparented to the Task with organized TaskFragment. For example,
* when an Activity enters and then exits Picture-in-picture, it will be reparented back to its
* orginial Task. In this case, we need to notify the organizer so that it can check if the
* original Task. In this case, we need to notify the organizer so that it can check if the
* Activity matches any split rule.
*
* @param taskId The Task that the activity is reparented to.

View File

@@ -62,13 +62,18 @@ class JetpackTaskFragmentOrganizer extends TaskFragmentOrganizer {
* Callback that notifies the controller about changes to task fragments.
*/
interface TaskFragmentCallback {
void onTaskFragmentAppeared(@NonNull TaskFragmentInfo taskFragmentInfo);
void onTaskFragmentInfoChanged(@NonNull TaskFragmentInfo taskFragmentInfo);
void onTaskFragmentVanished(@NonNull TaskFragmentInfo taskFragmentInfo);
void onTaskFragmentParentInfoChanged(int taskId, @NonNull Configuration parentConfig);
void onActivityReparentedToTask(int taskId, @NonNull Intent activityIntent,
@NonNull IBinder activityToken);
void onTaskFragmentError(@Nullable TaskFragmentInfo taskFragmentInfo, int opType);
void onTaskFragmentAppeared(@NonNull WindowContainerTransaction wct,
@NonNull TaskFragmentInfo taskFragmentInfo);
void onTaskFragmentInfoChanged(@NonNull WindowContainerTransaction wct,
@NonNull TaskFragmentInfo taskFragmentInfo);
void onTaskFragmentVanished(@NonNull WindowContainerTransaction wct,
@NonNull TaskFragmentInfo taskFragmentInfo);
void onTaskFragmentParentInfoChanged(@NonNull WindowContainerTransaction wct,
int taskId, @NonNull Configuration parentConfig);
void onActivityReparentedToTask(@NonNull WindowContainerTransaction wct,
int taskId, @NonNull Intent activityIntent, @NonNull IBinder activityToken);
void onTaskFragmentError(@NonNull WindowContainerTransaction wct,
@Nullable TaskFragmentInfo taskFragmentInfo, int opType);
}
/**
@@ -158,27 +163,16 @@ class JetpackTaskFragmentOrganizer extends TaskFragmentOrganizer {
updateWindowingMode(wct, fragmentToken, WINDOWING_MODE_UNDEFINED);
}
/**
* Expands an existing TaskFragment to fill parent.
* @param fragmentToken token of an existing TaskFragment.
*/
void expandTaskFragment(@NonNull IBinder fragmentToken) {
WindowContainerTransaction wct = new WindowContainerTransaction();
expandTaskFragment(wct, fragmentToken);
applyTransaction(wct);
}
/**
* Expands an Activity to fill parent by moving it to a new TaskFragment.
* @param fragmentToken token to create new TaskFragment with.
* @param activity activity to move to the fill-parent TaskFragment.
*/
void expandActivity(@NonNull IBinder fragmentToken, @NonNull Activity activity) {
final WindowContainerTransaction wct = new WindowContainerTransaction();
void expandActivity(@NonNull WindowContainerTransaction wct, @NonNull IBinder fragmentToken,
@NonNull Activity activity) {
createTaskFragmentAndReparentActivity(
wct, fragmentToken, activity.getActivityToken(), new Rect(),
WINDOWING_MODE_UNDEFINED, activity);
applyTransaction(wct);
}
/**
@@ -278,43 +272,55 @@ class JetpackTaskFragmentOrganizer extends TaskFragmentOrganizer {
@Override
public void onTaskFragmentAppeared(@NonNull TaskFragmentInfo taskFragmentInfo) {
final WindowContainerTransaction wct = new WindowContainerTransaction();
final IBinder fragmentToken = taskFragmentInfo.getFragmentToken();
mFragmentInfos.put(fragmentToken, taskFragmentInfo);
mCallback.onTaskFragmentAppeared(taskFragmentInfo);
mCallback.onTaskFragmentAppeared(wct, taskFragmentInfo);
applyTransaction(wct);
}
@Override
public void onTaskFragmentInfoChanged(@NonNull TaskFragmentInfo taskFragmentInfo) {
final WindowContainerTransaction wct = new WindowContainerTransaction();
final IBinder fragmentToken = taskFragmentInfo.getFragmentToken();
mFragmentInfos.put(fragmentToken, taskFragmentInfo);
mCallback.onTaskFragmentInfoChanged(taskFragmentInfo);
mCallback.onTaskFragmentInfoChanged(wct, taskFragmentInfo);
applyTransaction(wct);
}
@Override
public void onTaskFragmentVanished(@NonNull TaskFragmentInfo taskFragmentInfo) {
final WindowContainerTransaction wct = new WindowContainerTransaction();
mFragmentInfos.remove(taskFragmentInfo.getFragmentToken());
mCallback.onTaskFragmentVanished(taskFragmentInfo);
mCallback.onTaskFragmentVanished(wct, taskFragmentInfo);
applyTransaction(wct);
}
@Override
public void onTaskFragmentParentInfoChanged(int taskId, @NonNull Configuration parentConfig) {
mCallback.onTaskFragmentParentInfoChanged(taskId, parentConfig);
final WindowContainerTransaction wct = new WindowContainerTransaction();
mCallback.onTaskFragmentParentInfoChanged(wct, taskId, parentConfig);
applyTransaction(wct);
}
@Override
public void onActivityReparentedToTask(int taskId, @NonNull Intent activityIntent,
@NonNull IBinder activityToken) {
mCallback.onActivityReparentedToTask(taskId, activityIntent, activityToken);
final WindowContainerTransaction wct = new WindowContainerTransaction();
mCallback.onActivityReparentedToTask(wct, taskId, activityIntent, activityToken);
applyTransaction(wct);
}
@Override
public void onTaskFragmentError(@NonNull IBinder errorCallbackToken,
@Nullable TaskFragmentInfo taskFragmentInfo,
int opType, @NonNull Throwable exception) {
final WindowContainerTransaction wct = new WindowContainerTransaction();
if (taskFragmentInfo != null) {
final IBinder fragmentToken = taskFragmentInfo.getFragmentToken();
mFragmentInfos.put(fragmentToken, taskFragmentInfo);
}
mCallback.onTaskFragmentError(taskFragmentInfo, opType);
mCallback.onTaskFragmentError(wct, taskFragmentInfo, opType);
applyTransaction(wct);
}
}

View File

@@ -145,35 +145,36 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
}
@Override
public void onTaskFragmentAppeared(@NonNull TaskFragmentInfo taskFragmentInfo) {
public void onTaskFragmentAppeared(@NonNull WindowContainerTransaction wct,
@NonNull TaskFragmentInfo taskFragmentInfo) {
synchronized (mLock) {
TaskFragmentContainer container = getContainer(taskFragmentInfo.getFragmentToken());
if (container == null) {
return;
}
container.setInfo(taskFragmentInfo);
container.setInfo(wct, taskFragmentInfo);
if (container.isFinished()) {
mPresenter.cleanupContainer(container, false /* shouldFinishDependent */);
mPresenter.cleanupContainer(wct, container, false /* shouldFinishDependent */);
} else {
// Update with the latest Task configuration.
mPresenter.updateContainer(container);
updateContainer(wct, container);
}
updateCallbackIfNecessary();
}
}
@Override
public void onTaskFragmentInfoChanged(@NonNull TaskFragmentInfo taskFragmentInfo) {
public void onTaskFragmentInfoChanged(@NonNull WindowContainerTransaction wct,
@NonNull TaskFragmentInfo taskFragmentInfo) {
synchronized (mLock) {
TaskFragmentContainer container = getContainer(taskFragmentInfo.getFragmentToken());
if (container == null) {
return;
}
final WindowContainerTransaction wct = new WindowContainerTransaction();
final boolean wasInPip = isInPictureInPicture(container);
container.setInfo(taskFragmentInfo);
container.setInfo(wct, taskFragmentInfo);
final boolean isInPip = isInPictureInPicture(container);
// Check if there are no running activities - consider the container empty if there are
// no non-finishing activities left.
@@ -183,15 +184,15 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
// Instead, the original split should be cleanup, and the dependent may be
// expanded to fullscreen.
cleanupForEnterPip(wct, container);
mPresenter.cleanupContainer(container, false /* shouldFinishDependent */, wct);
mPresenter.cleanupContainer(wct, container, false /* shouldFinishDependent */);
} else if (taskFragmentInfo.isTaskClearedForReuse()) {
// Do not finish the dependents if this TaskFragment was cleared due to
// launching activity in the Task.
mPresenter.cleanupContainer(container, false /* shouldFinishDependent */, wct);
mPresenter.cleanupContainer(wct, container, false /* shouldFinishDependent */);
} else if (!container.isWaitingActivityAppear()) {
// Do not finish the container before the expected activity appear until
// timeout.
mPresenter.cleanupContainer(container, true /* shouldFinishDependent */, wct);
mPresenter.cleanupContainer(wct, container, true /* shouldFinishDependent */);
}
} else if (wasInPip && isInPip) {
// No update until exit PIP.
@@ -208,13 +209,13 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
// needed.
updateContainer(wct, container);
}
mPresenter.applyTransaction(wct);
updateCallbackIfNecessary();
}
}
@Override
public void onTaskFragmentVanished(@NonNull TaskFragmentInfo taskFragmentInfo) {
public void onTaskFragmentVanished(@NonNull WindowContainerTransaction wct,
@NonNull TaskFragmentInfo taskFragmentInfo) {
synchronized (mLock) {
final TaskFragmentContainer container = getContainer(
taskFragmentInfo.getFragmentToken());
@@ -225,9 +226,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
final TaskFragmentContainer newTopContainer = getTopActiveContainer(
container.getTaskId());
if (newTopContainer != null) {
final WindowContainerTransaction wct = new WindowContainerTransaction();
updateContainer(wct, newTopContainer);
mPresenter.applyTransaction(wct);
}
updateCallbackIfNecessary();
}
@@ -236,7 +235,8 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
}
@Override
public void onTaskFragmentParentInfoChanged(int taskId, @NonNull Configuration parentConfig) {
public void onTaskFragmentParentInfoChanged(@NonNull WindowContainerTransaction wct,
int taskId, @NonNull Configuration parentConfig) {
synchronized (mLock) {
onTaskConfigurationChanged(taskId, parentConfig);
if (isInPictureInPicture(parentConfig)) {
@@ -256,7 +256,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
final TaskFragmentContainer container = containers.get(i);
// Wait until onTaskFragmentAppeared to update new container.
if (!container.isFinished() && !container.isWaitingActivityAppear()) {
mPresenter.updateContainer(container);
updateContainer(wct, container);
}
}
updateCallbackIfNecessary();
@@ -264,7 +264,8 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
}
@Override
public void onActivityReparentedToTask(int taskId, @NonNull Intent activityIntent,
public void onActivityReparentedToTask(@NonNull WindowContainerTransaction wct,
int taskId, @NonNull Intent activityIntent,
@NonNull IBinder activityToken) {
synchronized (mLock) {
// If the activity belongs to the current app process, we treat it as a new activity
@@ -275,10 +276,10 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
// launching to top. We allow split as primary for activity reparent because the
// activity may be split as primary before it is reparented out. In that case, we
// want to show it as primary again when it is reparented back.
if (!resolveActivityToContainer(activity, true /* isOnReparent */)) {
if (!resolveActivityToContainer(wct, activity, true /* isOnReparent */)) {
// When there is no embedding rule matched, try to place it in the top container
// like a normal launch.
placeActivityInTopContainer(activity);
placeActivityInTopContainer(wct, activity);
}
updateCallbackIfNecessary();
return;
@@ -293,7 +294,6 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
// If the activity belongs to a different app process, we treat it as starting new
// intent, since both actions might result in a new activity that should appear in an
// organized TaskFragment.
final WindowContainerTransaction wct = new WindowContainerTransaction();
TaskFragmentContainer targetContainer = resolveStartActivityIntent(wct, taskId,
activityIntent, null /* launchingActivity */);
if (targetContainer == null) {
@@ -306,14 +306,14 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
}
wct.reparentActivityToTaskFragment(targetContainer.getTaskFragmentToken(),
activityToken);
mPresenter.applyTransaction(wct);
// Because the activity does not belong to the organizer process, we wait until
// onTaskFragmentAppeared to trigger updateCallbackIfNecessary().
}
}
@Override
public void onTaskFragmentError(@Nullable TaskFragmentInfo taskFragmentInfo, int opType) {
public void onTaskFragmentError(@NonNull WindowContainerTransaction wct,
@Nullable TaskFragmentInfo taskFragmentInfo, int opType) {
synchronized (mLock) {
switch (opType) {
case HIERARCHY_OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENT:
@@ -329,10 +329,11 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
}
// Update the latest taskFragmentInfo and perform necessary clean-up
container.setInfo(taskFragmentInfo);
container.setInfo(wct, taskFragmentInfo);
container.clearPendingAppearedActivities();
if (container.isEmpty()) {
mPresenter.cleanupContainer(container, false /* shouldFinishDependent */);
mPresenter.cleanupContainer(wct, container,
false /* shouldFinishDependent */);
}
break;
}
@@ -343,7 +344,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
}
}
/** Called on receiving {@link #onTaskFragmentVanished(TaskFragmentInfo)} for cleanup. */
/** Called on receiving {@link #onTaskFragmentVanished} for cleanup. */
private void cleanupTaskFragment(@NonNull IBinder taskFragmentToken) {
for (int i = mTaskContainers.size() - 1; i >= 0; i--) {
final TaskContainer taskContainer = mTaskContainers.valueAt(i);
@@ -422,10 +423,12 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
}
@VisibleForTesting
void onActivityCreated(@NonNull Activity launchedActivity) {
@GuardedBy("mLock")
void onActivityCreated(@NonNull WindowContainerTransaction wct,
@NonNull Activity launchedActivity) {
// TODO(b/229680885): we don't support launching into primary yet because we want to always
// launch the new activity on top.
resolveActivityToContainer(launchedActivity, false /* isOnReparent */);
resolveActivityToContainer(wct, launchedActivity, false /* isOnReparent */);
updateCallbackIfNecessary();
}
@@ -440,7 +443,8 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
*/
@VisibleForTesting
@GuardedBy("mLock")
boolean resolveActivityToContainer(@NonNull Activity activity, boolean isOnReparent) {
boolean resolveActivityToContainer(@NonNull WindowContainerTransaction wct,
@NonNull Activity activity, boolean isOnReparent) {
if (isInPictureInPicture(activity) || activity.isFinishing()) {
// We don't embed activity when it is in PIP, or finishing. Return true since we don't
// want any extra handling.
@@ -472,12 +476,12 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
// 1. Whether the new launched activity should always expand.
if (shouldExpand(activity, null /* intent */)) {
expandActivity(activity);
expandActivity(wct, activity);
return true;
}
// 2. Whether the new launched activity should launch a placeholder.
if (launchPlaceholderIfNecessary(activity, !isOnReparent)) {
if (launchPlaceholderIfNecessary(wct, activity, !isOnReparent)) {
return true;
}
@@ -492,11 +496,11 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
// Can't find any activity below.
return false;
}
if (putActivitiesIntoSplitIfNecessary(activityBelow, activity)) {
if (putActivitiesIntoSplitIfNecessary(wct, activityBelow, activity)) {
// Have split rule of [ activityBelow | launchedActivity ].
return true;
}
if (isOnReparent && putActivitiesIntoSplitIfNecessary(activity, activityBelow)) {
if (isOnReparent && putActivitiesIntoSplitIfNecessary(wct, activity, activityBelow)) {
// Have split rule of [ launchedActivity | activityBelow].
return true;
}
@@ -519,19 +523,20 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
// Can't find the top activity on the other split TaskFragment.
return false;
}
if (putActivitiesIntoSplitIfNecessary(otherTopActivity, activity)) {
if (putActivitiesIntoSplitIfNecessary(wct, otherTopActivity, activity)) {
// Have split rule of [ otherTopActivity | launchedActivity ].
return true;
}
// Have split rule of [ launchedActivity | otherTopActivity].
return isOnReparent && putActivitiesIntoSplitIfNecessary(activity, otherTopActivity);
return isOnReparent && putActivitiesIntoSplitIfNecessary(wct, activity, otherTopActivity);
}
/**
* Places the given activity to the top most TaskFragment in the task if there is any.
*/
@VisibleForTesting
void placeActivityInTopContainer(@NonNull Activity activity) {
void placeActivityInTopContainer(@NonNull WindowContainerTransaction wct,
@NonNull Activity activity) {
if (getContainerWithActivity(activity) != null) {
// The activity has already been put in a TaskFragment. This is likely to be done by
// the server when the activity is started.
@@ -547,20 +552,20 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
return;
}
targetContainer.addPendingAppearedActivity(activity);
final WindowContainerTransaction wct = new WindowContainerTransaction();
wct.reparentActivityToTaskFragment(targetContainer.getTaskFragmentToken(),
activity.getActivityToken());
mPresenter.applyTransaction(wct);
}
/**
* Starts an activity to side of the launchingActivity with the provided split config.
*/
private void startActivityToSide(@NonNull Activity launchingActivity, @NonNull Intent intent,
@GuardedBy("mLock")
private void startActivityToSide(@NonNull WindowContainerTransaction wct,
@NonNull Activity launchingActivity, @NonNull Intent intent,
@Nullable Bundle options, @NonNull SplitRule sideRule,
@Nullable Consumer<Exception> failureCallback, boolean isPlaceholder) {
try {
mPresenter.startActivityToSide(launchingActivity, intent, options, sideRule,
mPresenter.startActivityToSide(wct, launchingActivity, intent, options, sideRule,
isPlaceholder);
} catch (Exception e) {
if (failureCallback != null) {
@@ -573,15 +578,17 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
* Expands the given activity by either expanding the TaskFragment it is currently in or putting
* it into a new expanded TaskFragment.
*/
private void expandActivity(@NonNull Activity activity) {
@GuardedBy("mLock")
private void expandActivity(@NonNull WindowContainerTransaction wct,
@NonNull Activity activity) {
final TaskFragmentContainer container = getContainerWithActivity(activity);
if (shouldContainerBeExpanded(container)) {
// Make sure that the existing container is expanded.
mPresenter.expandTaskFragment(container.getTaskFragmentToken());
mPresenter.expandTaskFragment(wct, container.getTaskFragmentToken());
} else {
// Put activity into a new expanded container.
final TaskFragmentContainer newContainer = newContainer(activity, getTaskId(activity));
mPresenter.expandActivity(newContainer.getTaskFragmentToken(), activity);
mPresenter.expandActivity(wct, newContainer.getTaskFragmentToken(), activity);
}
}
@@ -667,8 +674,8 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
* and returns {@code true}. Otherwise, returns {@code false}.
*/
@GuardedBy("mLock")
private boolean putActivitiesIntoSplitIfNecessary(@NonNull Activity primaryActivity,
@NonNull Activity secondaryActivity) {
private boolean putActivitiesIntoSplitIfNecessary(@NonNull WindowContainerTransaction wct,
@NonNull Activity primaryActivity, @NonNull Activity secondaryActivity) {
final SplitPairRule splitRule = getSplitRule(primaryActivity, secondaryActivity);
if (splitRule == null) {
return false;
@@ -686,23 +693,23 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
return true;
}
secondaryContainer.addPendingAppearedActivity(secondaryActivity);
final WindowContainerTransaction wct = new WindowContainerTransaction();
if (mPresenter.expandSplitContainerIfNeeded(wct, splitContainer, primaryActivity,
secondaryActivity, null /* secondaryIntent */)
!= RESULT_EXPAND_FAILED_NO_TF_INFO) {
wct.reparentActivityToTaskFragment(
secondaryContainer.getTaskFragmentToken(),
secondaryActivity.getActivityToken());
mPresenter.applyTransaction(wct);
return true;
}
}
// Create new split pair.
mPresenter.createNewSplitContainer(primaryActivity, secondaryActivity, splitRule);
mPresenter.createNewSplitContainer(wct, primaryActivity, secondaryActivity, splitRule);
return true;
}
private void onActivityConfigurationChanged(@NonNull Activity activity) {
@GuardedBy("mLock")
private void onActivityConfigurationChanged(@NonNull WindowContainerTransaction wct,
@NonNull Activity activity) {
if (activity.isFinishing()) {
// Do nothing if the activity is currently finishing.
return;
@@ -721,7 +728,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
}
// Check if activity requires a placeholder
launchPlaceholderIfNecessary(activity, false /* isOnCreated */);
launchPlaceholderIfNecessary(wct, activity, false /* isOnCreated */);
}
@VisibleForTesting
@@ -741,7 +748,22 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
* creation.
*/
void onTaskFragmentAppearEmptyTimeout(@NonNull TaskFragmentContainer container) {
mPresenter.cleanupContainer(container, false /* shouldFinishDependent */);
synchronized (mLock) {
final WindowContainerTransaction wct = new WindowContainerTransaction();
onTaskFragmentAppearEmptyTimeout(wct, container);
mPresenter.applyTransaction(wct);
}
}
/**
* Called when we have been waiting too long for the TaskFragment to become non-empty after
* creation.
*/
void onTaskFragmentAppearEmptyTimeout(@NonNull WindowContainerTransaction wct,
@NonNull TaskFragmentContainer container) {
synchronized (mLock) {
mPresenter.cleanupContainer(wct, container, false /* shouldFinishDependent */);
}
}
/**
@@ -971,6 +993,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
}
/** Cleanups all the dependencies when the TaskFragment is entering PIP. */
@GuardedBy("mLock")
private void cleanupForEnterPip(@NonNull WindowContainerTransaction wct,
@NonNull TaskFragmentContainer container) {
final TaskContainer taskContainer = container.getTaskContainer();
@@ -1084,9 +1107,10 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
* Updates the presentation of the container. If the container is part of the split or should
* have a placeholder, it will also update the other part of the split.
*/
@GuardedBy("mLock")
void updateContainer(@NonNull WindowContainerTransaction wct,
@NonNull TaskFragmentContainer container) {
if (launchPlaceholderIfNecessary(container)) {
if (launchPlaceholderIfNecessary(wct, container)) {
// Placeholder was launched, the positions will be updated when the activity is added
// to the secondary container.
return;
@@ -1111,7 +1135,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
// Skip position update - one or both containers are finished.
return;
}
if (dismissPlaceholderIfNecessary(splitContainer)) {
if (dismissPlaceholderIfNecessary(wct, splitContainer)) {
// Placeholder was finished, the positions will be updated when its container is emptied
return;
}
@@ -1173,16 +1197,20 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
/**
* Checks if the container requires a placeholder and launches it if necessary.
*/
private boolean launchPlaceholderIfNecessary(@NonNull TaskFragmentContainer container) {
@GuardedBy("mLock")
private boolean launchPlaceholderIfNecessary(@NonNull WindowContainerTransaction wct,
@NonNull TaskFragmentContainer container) {
final Activity topActivity = container.getTopNonFinishingActivity();
if (topActivity == null) {
return false;
}
return launchPlaceholderIfNecessary(topActivity, false /* isOnCreated */);
return launchPlaceholderIfNecessary(wct, topActivity, false /* isOnCreated */);
}
boolean launchPlaceholderIfNecessary(@NonNull Activity activity, boolean isOnCreated) {
@GuardedBy("mLock")
boolean launchPlaceholderIfNecessary(@NonNull WindowContainerTransaction wct,
@NonNull Activity activity, boolean isOnCreated) {
if (activity.isFinishing()) {
return false;
}
@@ -1216,7 +1244,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
// TODO(b/190433398): Handle failed request
final Bundle options = getPlaceholderOptions(activity, isOnCreated);
startActivityToSide(activity, placeholderRule.getPlaceholderIntent(), options,
startActivityToSide(wct, activity, placeholderRule.getPlaceholderIntent(), options,
placeholderRule, null /* failureCallback */, true /* isPlaceholder */);
return true;
}
@@ -1243,7 +1271,9 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
}
@VisibleForTesting
boolean dismissPlaceholderIfNecessary(@NonNull SplitContainer splitContainer) {
@GuardedBy("mLock")
boolean dismissPlaceholderIfNecessary(@NonNull WindowContainerTransaction wct,
@NonNull SplitContainer splitContainer) {
if (!splitContainer.isPlaceholderContainer()) {
return false;
}
@@ -1257,7 +1287,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
return false;
}
mPresenter.cleanupContainer(splitContainer.getSecondaryContainer(),
mPresenter.cleanupContainer(wct, splitContainer.getSecondaryContainer(),
false /* shouldFinishDependent */);
return true;
}
@@ -1560,14 +1590,18 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
// that we don't launch it if an activity itself already requested something to be
// launched to side.
synchronized (mLock) {
SplitController.this.onActivityCreated(activity);
final WindowContainerTransaction wct = new WindowContainerTransaction();
SplitController.this.onActivityCreated(wct, activity);
mPresenter.applyTransaction(wct);
}
}
@Override
public void onActivityConfigurationChanged(@NonNull Activity activity) {
synchronized (mLock) {
SplitController.this.onActivityConfigurationChanged(activity);
final WindowContainerTransaction wct = new WindowContainerTransaction();
SplitController.this.onActivityConfigurationChanged(wct, activity);
mPresenter.applyTransaction(wct);
}
}

View File

@@ -108,31 +108,12 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
registerOrganizer();
}
/**
* Updates the presentation of the provided container.
*/
void updateContainer(@NonNull TaskFragmentContainer container) {
final WindowContainerTransaction wct = new WindowContainerTransaction();
mController.updateContainer(wct, container);
applyTransaction(wct);
}
/**
* Deletes the specified container and all other associated and dependent containers in the same
* transaction.
*/
void cleanupContainer(@NonNull TaskFragmentContainer container, boolean shouldFinishDependent) {
final WindowContainerTransaction wct = new WindowContainerTransaction();
cleanupContainer(container, shouldFinishDependent, wct);
applyTransaction(wct);
}
/**
* Deletes the specified container and all other associated and dependent containers in the same
* transaction.
*/
void cleanupContainer(@NonNull TaskFragmentContainer container, boolean shouldFinishDependent,
@NonNull WindowContainerTransaction wct) {
void cleanupContainer(@NonNull WindowContainerTransaction wct,
@NonNull TaskFragmentContainer container, boolean shouldFinishDependent) {
container.finish(shouldFinishDependent, this, wct, mController);
final TaskFragmentContainer newTopContainer = mController.getTopActiveContainer(
@@ -190,10 +171,9 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
* created and the activity will be re-parented to it.
* @param rule The split rule to be applied to the container.
*/
void createNewSplitContainer(@NonNull Activity primaryActivity,
@NonNull Activity secondaryActivity, @NonNull SplitPairRule rule) {
final WindowContainerTransaction wct = new WindowContainerTransaction();
void createNewSplitContainer(@NonNull WindowContainerTransaction wct,
@NonNull Activity primaryActivity, @NonNull Activity secondaryActivity,
@NonNull SplitPairRule rule) {
final Rect parentBounds = getParentContainerBounds(primaryActivity);
final Pair<Size, Size> minDimensionsPair = getActivitiesMinDimensionsPair(primaryActivity,
secondaryActivity);
@@ -219,8 +199,6 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
minDimensionsPair);
mController.registerSplit(wct, primaryContainer, primaryActivity, secondaryContainer, rule);
applyTransaction(wct);
}
/**
@@ -262,7 +240,8 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
* @param rule The split rule to be applied to the container.
* @param isPlaceholder Whether the launch is a placeholder.
*/
void startActivityToSide(@NonNull Activity launchingActivity, @NonNull Intent activityIntent,
void startActivityToSide(@NonNull WindowContainerTransaction wct,
@NonNull Activity launchingActivity, @NonNull Intent activityIntent,
@Nullable Bundle activityOptions, @NonNull SplitRule rule, boolean isPlaceholder) {
final Rect parentBounds = getParentContainerBounds(launchingActivity);
final Pair<Size, Size> minDimensionsPair = getActivityIntentMinDimensionsPair(
@@ -284,7 +263,6 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
launchingActivity, taskId);
final int windowingMode = mController.getTaskContainer(taskId)
.getWindowingModeForSplitTaskFragment(primaryRectBounds);
final WindowContainerTransaction wct = new WindowContainerTransaction();
mController.registerSplit(wct, primaryContainer, launchingActivity, secondaryContainer,
rule);
startActivityToSide(wct, primaryContainer.getTaskFragmentToken(), primaryRectBounds,
@@ -294,7 +272,6 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
// When placeholder is launched in split, we should keep the focus on the primary.
wct.requestFocusOnTaskFragment(primaryContainer.getTaskFragmentToken());
}
applyTransaction(wct);
}
/**

View File

@@ -251,19 +251,22 @@ class TaskFragmentContainer {
return mInfo;
}
void setInfo(@NonNull TaskFragmentInfo info) {
void setInfo(@NonNull WindowContainerTransaction wct, @NonNull TaskFragmentInfo info) {
if (!mIsFinished && mInfo == null && info.isEmpty()) {
// onTaskFragmentAppeared with empty info. We will remove the TaskFragment if no
// pending appeared intent/activities. Otherwise, wait and removing the TaskFragment if
// it is still empty after timeout.
mAppearEmptyTimeout = () -> {
mAppearEmptyTimeout = null;
mController.onTaskFragmentAppearEmptyTimeout(this);
};
if (mPendingAppearedIntent != null || !mPendingAppearedActivities.isEmpty()) {
mAppearEmptyTimeout = () -> {
mAppearEmptyTimeout = null;
// Call without the pass-in wct when timeout. We need to applyWct directly
// in this case.
mController.onTaskFragmentAppearEmptyTimeout(this);
};
mController.getHandler().postDelayed(mAppearEmptyTimeout, APPEAR_EMPTY_TIMEOUT_MS);
} else {
mAppearEmptyTimeout.run();
mAppearEmptyTimeout = null;
mController.onTaskFragmentAppearEmptyTimeout(wct, this);
}
} else if (mAppearEmptyTimeout != null && !info.isEmpty()) {
mController.getHandler().removeCallbacks(mAppearEmptyTimeout);

View File

@@ -56,6 +56,8 @@ import java.util.ArrayList;
* Build/Install/Run:
* atest WMJetpackUnitTests:JetpackTaskFragmentOrganizerTest
*/
// Suppress GuardedBy warning on unit tests
@SuppressWarnings("GuardedBy")
@Presubmit
@SmallTest
@RunWith(AndroidJUnit4.class)
@@ -119,7 +121,7 @@ public class JetpackTaskFragmentOrganizerTest {
new Intent(), taskContainer, mSplitController);
final TaskFragmentInfo info = createMockInfo(container);
mOrganizer.mFragmentInfos.put(container.getTaskFragmentToken(), info);
container.setInfo(info);
container.setInfo(mTransaction, info);
mOrganizer.expandTaskFragment(mTransaction, container.getTaskFragmentToken());

View File

@@ -89,6 +89,8 @@ import java.util.List;
* Build/Install/Run:
* atest WMJetpackUnitTests:SplitControllerTest
*/
// Suppress GuardedBy warning on unit tests
@SuppressWarnings("GuardedBy")
@Presubmit
@SmallTest
@RunWith(AndroidJUnit4.class)
@@ -158,14 +160,14 @@ public class SplitControllerTest {
final TaskFragmentInfo info = mock(TaskFragmentInfo.class);
doReturn(new ArrayList<>()).when(info).getActivities();
doReturn(true).when(info).isEmpty();
tf1.setInfo(info);
tf1.setInfo(mTransaction, info);
assertWithMessage("Must return tf because we are waiting for tf1 to become non-empty after"
+ " creation.")
.that(mSplitController.getTopActiveContainer(TASK_ID)).isEqualTo(tf1);
doReturn(false).when(info).isEmpty();
tf1.setInfo(info);
tf1.setInfo(mTransaction, info);
assertWithMessage("Must return null because tf1 becomes empty.")
.that(mSplitController.getTopActiveContainer(TASK_ID)).isNull();
@@ -177,7 +179,7 @@ public class SplitControllerTest {
doReturn(tf.getTaskFragmentToken()).when(mInfo).getFragmentToken();
// The TaskFragment has been removed in the server, we only need to cleanup the reference.
mSplitController.onTaskFragmentVanished(mInfo);
mSplitController.onTaskFragmentVanished(mTransaction, mInfo);
verify(mSplitPresenter, never()).deleteTaskFragment(any(), any());
verify(mSplitController).removeContainer(tf);
@@ -187,9 +189,10 @@ public class SplitControllerTest {
@Test
public void testOnTaskFragmentAppearEmptyTimeout() {
final TaskFragmentContainer tf = mSplitController.newContainer(mActivity, TASK_ID);
mSplitController.onTaskFragmentAppearEmptyTimeout(tf);
mSplitController.onTaskFragmentAppearEmptyTimeout(mTransaction, tf);
verify(mSplitPresenter).cleanupContainer(tf, false /* shouldFinishDependent */);
verify(mSplitPresenter).cleanupContainer(mTransaction, tf,
false /* shouldFinishDependent */);
}
@Test
@@ -229,8 +232,8 @@ public class SplitControllerTest {
spyOn(tf);
doReturn(mActivity).when(tf).getTopNonFinishingActivity();
doReturn(true).when(tf).isEmpty();
doReturn(true).when(mSplitController).launchPlaceholderIfNecessary(mActivity,
false /* isOnCreated */);
doReturn(true).when(mSplitController).launchPlaceholderIfNecessary(mTransaction,
mActivity, false /* isOnCreated */);
doNothing().when(mSplitPresenter).updateSplitContainer(any(), any(), any());
mSplitController.updateContainer(mTransaction, tf);
@@ -250,7 +253,7 @@ public class SplitControllerTest {
mSplitController.updateContainer(mTransaction, tf);
verify(mSplitController, never()).dismissPlaceholderIfNecessary(any());
verify(mSplitController, never()).dismissPlaceholderIfNecessary(any(), any());
// Verify if tf is not in the top splitContainer,
final SplitContainer splitContainer = mock(SplitContainer.class);
@@ -264,7 +267,7 @@ public class SplitControllerTest {
mSplitController.updateContainer(mTransaction, tf);
verify(mSplitController, never()).dismissPlaceholderIfNecessary(any());
verify(mSplitController, never()).dismissPlaceholderIfNecessary(any(), any());
// Verify if one or both containers in the top SplitContainer are finished,
// dismissPlaceholder() won't be called.
@@ -273,12 +276,12 @@ public class SplitControllerTest {
mSplitController.updateContainer(mTransaction, tf);
verify(mSplitController, never()).dismissPlaceholderIfNecessary(any());
verify(mSplitController, never()).dismissPlaceholderIfNecessary(any(), any());
// Verify if placeholder should be dismissed, updateSplitContainer() won't be called.
doReturn(false).when(tf).isFinished();
doReturn(true).when(mSplitController)
.dismissPlaceholderIfNecessary(splitContainer);
.dismissPlaceholderIfNecessary(mTransaction, splitContainer);
mSplitController.updateContainer(mTransaction, tf);
@@ -286,7 +289,7 @@ public class SplitControllerTest {
// Verify if the top active split is updated if both of its containers are not finished.
doReturn(false).when(mSplitController)
.dismissPlaceholderIfNecessary(splitContainer);
.dismissPlaceholderIfNecessary(mTransaction, splitContainer);
mSplitController.updateContainer(mTransaction, tf);
@@ -315,21 +318,23 @@ public class SplitControllerTest {
@Test
public void testOnActivityCreated() {
mSplitController.onActivityCreated(mActivity);
mSplitController.onActivityCreated(mTransaction, mActivity);
// Disallow to split as primary because we want the new launch to be always on top.
verify(mSplitController).resolveActivityToContainer(mActivity, false /* isOnReparent */);
verify(mSplitController).resolveActivityToContainer(mTransaction, mActivity,
false /* isOnReparent */);
}
@Test
public void testOnActivityReparentedToTask_sameProcess() {
mSplitController.onActivityReparentedToTask(TASK_ID, new Intent(),
mSplitController.onActivityReparentedToTask(mTransaction, TASK_ID, new Intent(),
mActivity.getActivityToken());
// Treated as on activity created, but allow to split as primary.
verify(mSplitController).resolveActivityToContainer(mActivity, true /* isOnReparent */);
verify(mSplitController).resolveActivityToContainer(mTransaction,
mActivity, true /* isOnReparent */);
// Try to place the activity to the top TaskFragment when there is no matched rule.
verify(mSplitController).placeActivityInTopContainer(mActivity);
verify(mSplitController).placeActivityInTopContainer(mTransaction, mActivity);
}
@Test
@@ -339,10 +344,10 @@ public class SplitControllerTest {
final IBinder activityToken = new Binder();
final Intent intent = new Intent();
mSplitController.onActivityReparentedToTask(TASK_ID, intent, activityToken);
mSplitController.onActivityReparentedToTask(mTransaction, TASK_ID, intent, activityToken);
// Treated as starting new intent
verify(mSplitController, never()).resolveActivityToContainer(any(), anyBoolean());
verify(mSplitController, never()).resolveActivityToContainer(any(), any(), anyBoolean());
verify(mSplitController).resolveStartActivityIntent(any(), eq(TASK_ID), eq(intent),
isNull());
}
@@ -504,26 +509,29 @@ public class SplitControllerTest {
@Test
public void testPlaceActivityInTopContainer() {
mSplitController.placeActivityInTopContainer(mActivity);
mSplitController.placeActivityInTopContainer(mTransaction, mActivity);
verify(mSplitPresenter, never()).applyTransaction(any());
verify(mTransaction, never()).reparentActivityToTaskFragment(any(), any());
mSplitController.newContainer(new Intent(), mActivity, TASK_ID);
mSplitController.placeActivityInTopContainer(mActivity);
// Place in the top container if there is no other rule matched.
final TaskFragmentContainer topContainer = mSplitController
.newContainer(new Intent(), mActivity, TASK_ID);
mSplitController.placeActivityInTopContainer(mTransaction, mActivity);
verify(mSplitPresenter).applyTransaction(any());
verify(mTransaction).reparentActivityToTaskFragment(topContainer.getTaskFragmentToken(),
mActivity.getActivityToken());
// Not reparent if activity is in a TaskFragment.
clearInvocations(mSplitPresenter);
clearInvocations(mTransaction);
mSplitController.newContainer(mActivity, TASK_ID);
mSplitController.placeActivityInTopContainer(mActivity);
mSplitController.placeActivityInTopContainer(mTransaction, mActivity);
verify(mSplitPresenter, never()).applyTransaction(any());
verify(mTransaction, never()).reparentActivityToTaskFragment(any(), any());
}
@Test
public void testResolveActivityToContainer_noRuleMatched() {
final boolean result = mSplitController.resolveActivityToContainer(mActivity,
final boolean result = mSplitController.resolveActivityToContainer(mTransaction, mActivity,
false /* isOnReparent */);
assertFalse(result);
@@ -535,7 +543,7 @@ public class SplitControllerTest {
setupExpandRule(mActivity);
// When the activity is not in any TaskFragment, create a new expanded TaskFragment for it.
final boolean result = mSplitController.resolveActivityToContainer(mActivity,
final boolean result = mSplitController.resolveActivityToContainer(mTransaction, mActivity,
false /* isOnReparent */);
final TaskFragmentContainer container = mSplitController.getContainerWithActivity(
mActivity);
@@ -543,7 +551,8 @@ public class SplitControllerTest {
assertTrue(result);
assertNotNull(container);
verify(mSplitController).newContainer(mActivity, TASK_ID);
verify(mSplitPresenter).expandActivity(container.getTaskFragmentToken(), mActivity);
verify(mSplitPresenter).expandActivity(mTransaction, container.getTaskFragmentToken(),
mActivity);
}
@Test
@@ -552,11 +561,11 @@ public class SplitControllerTest {
// When the activity is not in any TaskFragment, create a new expanded TaskFragment for it.
final TaskFragmentContainer container = mSplitController.newContainer(mActivity, TASK_ID);
final boolean result = mSplitController.resolveActivityToContainer(mActivity,
final boolean result = mSplitController.resolveActivityToContainer(mTransaction, mActivity,
false /* isOnReparent */);
assertTrue(result);
verify(mSplitPresenter).expandTaskFragment(container.getTaskFragmentToken());
verify(mSplitPresenter).expandTaskFragment(mTransaction, container.getTaskFragmentToken());
}
@Test
@@ -566,14 +575,15 @@ public class SplitControllerTest {
// When the activity is not in any TaskFragment, create a new expanded TaskFragment for it.
final Activity activity = createMockActivity();
addSplitTaskFragments(activity, mActivity);
final boolean result = mSplitController.resolveActivityToContainer(mActivity,
final boolean result = mSplitController.resolveActivityToContainer(mTransaction, mActivity,
false /* isOnReparent */);
final TaskFragmentContainer container = mSplitController.getContainerWithActivity(
mActivity);
assertTrue(result);
assertNotNull(container);
verify(mSplitPresenter).expandActivity(container.getTaskFragmentToken(), mActivity);
verify(mSplitPresenter).expandActivity(mTransaction, container.getTaskFragmentToken(),
mActivity);
}
@Test
@@ -583,11 +593,11 @@ public class SplitControllerTest {
(SplitPlaceholderRule) mSplitController.getSplitRules().get(0);
// Launch placeholder if the activity is not in any TaskFragment.
final boolean result = mSplitController.resolveActivityToContainer(mActivity,
final boolean result = mSplitController.resolveActivityToContainer(mTransaction, mActivity,
false /* isOnReparent */);
assertTrue(result);
verify(mSplitPresenter).startActivityToSide(mActivity, PLACEHOLDER_INTENT,
verify(mSplitPresenter).startActivityToSide(mTransaction, mActivity, PLACEHOLDER_INTENT,
mSplitController.getPlaceholderOptions(mActivity, true /* isOnCreated */),
placeholderRule, true /* isPlaceholder */);
}
@@ -600,11 +610,11 @@ public class SplitControllerTest {
final Activity activity = createMockActivity();
mSplitController.newContainer(mActivity, TASK_ID);
mSplitController.newContainer(activity, TASK_ID);
final boolean result = mSplitController.resolveActivityToContainer(mActivity,
final boolean result = mSplitController.resolveActivityToContainer(mTransaction, mActivity,
false /* isOnReparent */);
assertFalse(result);
verify(mSplitPresenter, never()).startActivityToSide(any(), any(), any(), any(),
verify(mSplitPresenter, never()).startActivityToSide(any(), any(), any(), any(), any(),
anyBoolean());
}
@@ -616,11 +626,11 @@ public class SplitControllerTest {
// Launch placeholder if the activity is in the topmost expanded TaskFragment.
mSplitController.newContainer(mActivity, TASK_ID);
final boolean result = mSplitController.resolveActivityToContainer(mActivity,
final boolean result = mSplitController.resolveActivityToContainer(mTransaction, mActivity,
false /* isOnReparent */);
assertTrue(result);
verify(mSplitPresenter).startActivityToSide(mActivity, PLACEHOLDER_INTENT,
verify(mSplitPresenter).startActivityToSide(mTransaction, mActivity, PLACEHOLDER_INTENT,
mSplitController.getPlaceholderOptions(mActivity, true /* isOnCreated */),
placeholderRule, true /* isPlaceholder */);
}
@@ -632,11 +642,11 @@ public class SplitControllerTest {
// Don't launch placeholder if the activity is in primary split.
final Activity secondaryActivity = createMockActivity();
addSplitTaskFragments(mActivity, secondaryActivity);
final boolean result = mSplitController.resolveActivityToContainer(mActivity,
final boolean result = mSplitController.resolveActivityToContainer(mTransaction, mActivity,
false /* isOnReparent */);
assertFalse(result);
verify(mSplitPresenter, never()).startActivityToSide(any(), any(), any(), any(),
verify(mSplitPresenter, never()).startActivityToSide(any(), any(), any(), any(), any(),
anyBoolean());
}
@@ -649,11 +659,11 @@ public class SplitControllerTest {
// Launch placeholder if the activity is in secondary split.
final Activity primaryActivity = createMockActivity();
addSplitTaskFragments(primaryActivity, mActivity);
final boolean result = mSplitController.resolveActivityToContainer(mActivity,
final boolean result = mSplitController.resolveActivityToContainer(mTransaction, mActivity,
false /* isOnReparent */);
assertTrue(result);
verify(mSplitPresenter).startActivityToSide(mActivity, PLACEHOLDER_INTENT,
verify(mSplitPresenter).startActivityToSide(mTransaction, mActivity, PLACEHOLDER_INTENT,
mSplitController.getPlaceholderOptions(mActivity, true /* isOnCreated */),
placeholderRule, true /* isPlaceholder */);
}
@@ -676,7 +686,7 @@ public class SplitControllerTest {
secondaryContainer,
splitRule);
clearInvocations(mSplitController);
final boolean result = mSplitController.resolveActivityToContainer(mActivity,
final boolean result = mSplitController.resolveActivityToContainer(mTransaction, mActivity,
false /* isOnReparent */);
assertTrue(result);
@@ -705,7 +715,7 @@ public class SplitControllerTest {
final Activity launchedActivity = createMockActivity();
primaryContainer.addPendingAppearedActivity(launchedActivity);
assertFalse(mSplitController.resolveActivityToContainer(launchedActivity,
assertFalse(mSplitController.resolveActivityToContainer(mTransaction, launchedActivity,
false /* isOnReparent */));
}
@@ -717,7 +727,7 @@ public class SplitControllerTest {
// Activity is already in secondary split, no need to create new split.
addSplitTaskFragments(primaryActivity, mActivity);
clearInvocations(mSplitController);
final boolean result = mSplitController.resolveActivityToContainer(mActivity,
final boolean result = mSplitController.resolveActivityToContainer(mTransaction, mActivity,
false /* isOnReparent */);
assertTrue(result);
@@ -735,7 +745,7 @@ public class SplitControllerTest {
addSplitTaskFragments(primaryActivity, secondaryActivity);
mSplitController.getContainerWithActivity(secondaryActivity)
.addPendingAppearedActivity(mActivity);
final boolean result = mSplitController.resolveActivityToContainer(mActivity,
final boolean result = mSplitController.resolveActivityToContainer(mTransaction, mActivity,
false /* isOnReparent */);
assertFalse(result);
@@ -760,7 +770,7 @@ public class SplitControllerTest {
mActivity,
secondaryContainer,
placeholderRule);
final boolean result = mSplitController.resolveActivityToContainer(mActivity,
final boolean result = mSplitController.resolveActivityToContainer(mTransaction, mActivity,
false /* isOnReparent */);
assertTrue(result);
@@ -774,7 +784,7 @@ public class SplitControllerTest {
final TaskFragmentContainer container = mSplitController.newContainer(activityBelow,
TASK_ID);
container.addPendingAppearedActivity(mActivity);
final boolean result = mSplitController.resolveActivityToContainer(mActivity,
final boolean result = mSplitController.resolveActivityToContainer(mTransaction, mActivity,
false /* isOnReparent */);
assertTrue(result);
@@ -790,14 +800,15 @@ public class SplitControllerTest {
final TaskFragmentContainer container = mSplitController.newContainer(activityBelow,
TASK_ID);
container.addPendingAppearedActivity(mActivity);
boolean result = mSplitController.resolveActivityToContainer(mActivity,
boolean result = mSplitController.resolveActivityToContainer(mTransaction, mActivity,
false /* isOnReparent */);
assertFalse(result);
assertEquals(container, mSplitController.getContainerWithActivity(mActivity));
// Allow to split as primary.
result = mSplitController.resolveActivityToContainer(mActivity, true /* isOnReparent */);
result = mSplitController.resolveActivityToContainer(mTransaction, mActivity,
true /* isOnReparent */);
assertTrue(result);
assertSplitPair(mActivity, activityBelow);
@@ -815,7 +826,7 @@ public class SplitControllerTest {
final TaskFragmentContainer secondaryContainer = mSplitController.getContainerWithActivity(
activityBelow);
secondaryContainer.addPendingAppearedActivity(mActivity);
final boolean result = mSplitController.resolveActivityToContainer(mActivity,
final boolean result = mSplitController.resolveActivityToContainer(mTransaction, mActivity,
false /* isOnReparent */);
final TaskFragmentContainer container = mSplitController.getContainerWithActivity(
mActivity);
@@ -836,14 +847,15 @@ public class SplitControllerTest {
final TaskFragmentContainer primaryContainer = mSplitController.getContainerWithActivity(
primaryActivity);
primaryContainer.addPendingAppearedActivity(mActivity);
boolean result = mSplitController.resolveActivityToContainer(mActivity,
boolean result = mSplitController.resolveActivityToContainer(mTransaction, mActivity,
false /* isOnReparent */);
assertFalse(result);
assertEquals(primaryContainer, mSplitController.getContainerWithActivity(mActivity));
result = mSplitController.resolveActivityToContainer(mActivity, true /* isOnReparent */);
result = mSplitController.resolveActivityToContainer(mTransaction, mActivity,
true /* isOnReparent */);
assertTrue(result);
assertSplitPair(mActivity, primaryActivity);
@@ -861,7 +873,7 @@ public class SplitControllerTest {
container.addPendingAppearedActivity(mActivity);
// Allow to split as primary.
boolean result = mSplitController.resolveActivityToContainer(mActivity,
boolean result = mSplitController.resolveActivityToContainer(mTransaction, mActivity,
true /* isOnReparent */);
assertTrue(result);
@@ -879,15 +891,13 @@ public class SplitControllerTest {
TASK_ID);
container.addPendingAppearedActivity(mActivity);
boolean result = mSplitController.resolveActivityToContainer(mActivity,
boolean result = mSplitController.resolveActivityToContainer(mTransaction, mActivity,
false /* isOnReparent */);
assertTrue(result);
assertSplitPair(activityBelow, mActivity, true /* matchParentBounds */);
}
// Suppress GuardedBy warning on unit tests
@SuppressWarnings("GuardedBy")
@Test
public void testResolveActivityToContainer_minDimensions_shouldExpandSplitContainer() {
final Activity primaryActivity = createMockActivity();
@@ -899,14 +909,14 @@ public class SplitControllerTest {
doReturn(secondaryActivity).when(mSplitController).findActivityBelow(eq(mActivity));
clearInvocations(mSplitPresenter);
boolean result = mSplitController.resolveActivityToContainer(mActivity,
boolean result = mSplitController.resolveActivityToContainer(mTransaction, mActivity,
false /* isOnReparent */);
assertTrue(result);
assertSplitPair(primaryActivity, mActivity, true /* matchParentBounds */);
assertEquals(mSplitController.getContainerWithActivity(secondaryActivity),
mSplitController.getContainerWithActivity(mActivity));
verify(mSplitPresenter, never()).createNewSplitContainer(any(), any(), any());
verify(mSplitPresenter, never()).createNewSplitContainer(any(), any(), any(), any());
}
@Test
@@ -914,7 +924,7 @@ public class SplitControllerTest {
doReturn(new Binder()).when(mSplitController).getInitialTaskFragmentToken(mActivity);
// No need to handle when the new launched activity is in an unknown TaskFragment.
assertTrue(mSplitController.resolveActivityToContainer(mActivity,
assertTrue(mSplitController.resolveActivityToContainer(mTransaction, mActivity,
false /* isOnReparent */));
}
@@ -993,7 +1003,7 @@ public class SplitControllerTest {
private void setupTaskFragmentInfo(@NonNull TaskFragmentContainer container,
@NonNull Activity activity) {
final TaskFragmentInfo info = createMockTaskFragmentInfo(container, activity);
container.setInfo(info);
container.setInfo(mTransaction, info);
mSplitPresenter.mFragmentInfos.put(container.getTaskFragmentToken(), info);
}

View File

@@ -78,6 +78,8 @@ import org.mockito.MockitoAnnotations;
* Build/Install/Run:
* atest WMJetpackUnitTests:SplitPresenterTest
*/
// Suppress GuardedBy warning on unit tests
@SuppressWarnings("GuardedBy")
@Presubmit
@SmallTest
@RunWith(AndroidJUnit4.class)
@@ -226,8 +228,9 @@ public class SplitPresenterTest {
mTransaction, splitContainer, mActivity, secondaryActivity,
null /* secondaryIntent */));
primaryTf.setInfo(createMockTaskFragmentInfo(primaryTf, mActivity));
secondaryTf.setInfo(createMockTaskFragmentInfo(secondaryTf, secondaryActivity));
primaryTf.setInfo(mTransaction, createMockTaskFragmentInfo(primaryTf, mActivity));
secondaryTf.setInfo(mTransaction,
createMockTaskFragmentInfo(secondaryTf, secondaryActivity));
assertEquals(RESULT_EXPANDED, mPresenter.expandSplitContainerIfNeeded(mTransaction,
splitContainer, mActivity, secondaryActivity, null /* secondaryIntent */));

View File

@@ -19,6 +19,8 @@ package androidx.window.extensions.embedding;
import static androidx.window.extensions.embedding.EmbeddingTestUtils.TASK_ID;
import static androidx.window.extensions.embedding.EmbeddingTestUtils.createMockTaskFragmentInfo;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static org.junit.Assert.assertEquals;
@@ -36,7 +38,6 @@ import static org.mockito.Mockito.never;
import android.app.Activity;
import android.content.Intent;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
import android.platform.test.annotations.Presubmit;
import android.window.TaskFragmentInfo;
@@ -62,25 +63,27 @@ import java.util.List;
* Build/Install/Run:
* atest WMJetpackUnitTests:TaskFragmentContainerTest
*/
// Suppress GuardedBy warning on unit tests
@SuppressWarnings("GuardedBy")
@Presubmit
@SmallTest
@RunWith(AndroidJUnit4.class)
public class TaskFragmentContainerTest {
@Mock
private SplitPresenter mPresenter;
@Mock
private SplitController mController;
@Mock
private TaskFragmentInfo mInfo;
@Mock
private Handler mHandler;
private WindowContainerTransaction mTransaction;
private Activity mActivity;
private Intent mIntent;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
doReturn(mHandler).when(mController).getHandler();
mController = new SplitController();
spyOn(mController);
mActivity = createMockActivity();
mIntent = new Intent();
}
@@ -123,7 +126,7 @@ public class TaskFragmentContainerTest {
// Remove all references after the container has appeared in server.
doReturn(new ArrayList<>()).when(mInfo).getActivities();
container.setInfo(mInfo);
container.setInfo(mTransaction, mInfo);
container.finish(true /* shouldFinishDependent */, mPresenter, wct, mController);
verify(mActivity, never()).finish();
@@ -137,7 +140,7 @@ public class TaskFragmentContainerTest {
final TaskFragmentContainer container0 = new TaskFragmentContainer(mActivity,
null /* pendingAppearedIntent */, taskContainer, mController);
final TaskFragmentInfo info = createMockTaskFragmentInfo(container0, mActivity);
container0.setInfo(info);
container0.setInfo(mTransaction, info);
// Request to reparent the activity to a new TaskFragment.
final TaskFragmentContainer container1 = new TaskFragmentContainer(mActivity,
null /* pendingAppearedIntent */, taskContainer, mController);
@@ -163,7 +166,7 @@ public class TaskFragmentContainerTest {
final TaskFragmentInfo info0 = createMockTaskFragmentInfo(pendingActivityContainer,
mActivity);
pendingActivityContainer.setInfo(info0);
pendingActivityContainer.setInfo(mTransaction, info0);
assertTrue(pendingActivityContainer.mPendingAppearedActivities.isEmpty());
@@ -175,7 +178,7 @@ public class TaskFragmentContainerTest {
final TaskFragmentInfo info1 = createMockTaskFragmentInfo(pendingIntentContainer,
mActivity);
pendingIntentContainer.setInfo(info1);
pendingIntentContainer.setInfo(mTransaction, info1);
assertNull(pendingIntentContainer.getPendingAppearedIntent());
}
@@ -191,18 +194,19 @@ public class TaskFragmentContainerTest {
final TaskFragmentInfo info = mock(TaskFragmentInfo.class);
doReturn(new ArrayList<>()).when(info).getActivities();
doReturn(true).when(info).isEmpty();
container.setInfo(info);
container.setInfo(mTransaction, info);
assertTrue(container.isWaitingActivityAppear());
doReturn(false).when(info).isEmpty();
container.setInfo(info);
container.setInfo(mTransaction, info);
assertFalse(container.isWaitingActivityAppear());
}
@Test
public void testAppearEmptyTimeout() {
doNothing().when(mController).onTaskFragmentAppearEmptyTimeout(any(), any());
final TaskContainer taskContainer = new TaskContainer(TASK_ID);
final TaskFragmentContainer container = new TaskFragmentContainer(null /* activity */,
mIntent, taskContainer, mController);
@@ -213,20 +217,20 @@ public class TaskFragmentContainerTest {
final TaskFragmentInfo info = mock(TaskFragmentInfo.class);
container.mInfo = null;
doReturn(true).when(info).isEmpty();
container.setInfo(info);
container.setInfo(mTransaction, info);
assertNotNull(container.mAppearEmptyTimeout);
// Not set if it is not appeared empty.
doReturn(new ArrayList<>()).when(info).getActivities();
doReturn(false).when(info).isEmpty();
container.setInfo(info);
container.setInfo(mTransaction, info);
assertNull(container.mAppearEmptyTimeout);
// Remove timeout after the container becomes non-empty.
doReturn(false).when(info).isEmpty();
container.setInfo(info);
container.setInfo(mTransaction, info);
assertNull(container.mAppearEmptyTimeout);
@@ -234,7 +238,7 @@ public class TaskFragmentContainerTest {
container.mInfo = null;
container.setPendingAppearedIntent(mIntent);
doReturn(true).when(info).isEmpty();
container.setInfo(info);
container.setInfo(mTransaction, info);
container.mAppearEmptyTimeout.run();
assertNull(container.mAppearEmptyTimeout);
@@ -260,7 +264,7 @@ public class TaskFragmentContainerTest {
final List<IBinder> runningActivities = Lists.newArrayList(activity0.getActivityToken(),
activity1.getActivityToken());
doReturn(runningActivities).when(mInfo).getActivities();
container.setInfo(mInfo);
container.setInfo(mTransaction, mInfo);
activities = container.collectNonFinishingActivities();
assertEquals(3, activities.size());
@@ -295,7 +299,7 @@ public class TaskFragmentContainerTest {
final Activity activity = createMockActivity();
final List<IBinder> runningActivities = Lists.newArrayList(activity.getActivityToken());
doReturn(runningActivities).when(mInfo).getActivities();
container.setInfo(mInfo);
container.setInfo(mTransaction, mInfo);
assertEquals(activity, container.getBottomMostActivity());
}

View File

@@ -204,7 +204,7 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
mTaskFragment);
mController.dispatchPendingEvents();
verify(mOrganizer).onTaskFragmentInfoChanged(mTaskFragmentInfo);
verify(mOrganizer).onTaskFragmentInfoChanged(eq(mTaskFragmentInfo));
}
@Test
@@ -231,7 +231,7 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
verify(mOrganizer, never()).onTaskFragmentAppeared(any());
verify(mOrganizer, never()).onTaskFragmentInfoChanged(any());
verify(mOrganizer, never()).onTaskFragmentParentInfoChanged(anyInt(), any());
verify(mOrganizer).onTaskFragmentVanished(mTaskFragmentInfo);
verify(mOrganizer).onTaskFragmentVanished(eq(mTaskFragmentInfo));
// Not trigger onTaskFragmentInfoChanged.
// Call onTaskFragmentAppeared before calling onTaskFragmentInfoChanged.
@@ -247,7 +247,7 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
verify(mOrganizer, never()).onTaskFragmentAppeared(any());
verify(mOrganizer, never()).onTaskFragmentInfoChanged(any());
verify(mOrganizer, never()).onTaskFragmentParentInfoChanged(anyInt(), any());
verify(mOrganizer).onTaskFragmentVanished(mTaskFragmentInfo);
verify(mOrganizer).onTaskFragmentVanished(eq(mTaskFragmentInfo));
}
@Test
@@ -298,7 +298,8 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
mErrorToken, null /* taskFragment */, -1 /* opType */, exception);
mController.dispatchPendingEvents();
verify(mOrganizer).onTaskFragmentError(eq(mErrorToken), eq(null), eq(-1), eq(exception));
verify(mOrganizer).onTaskFragmentError(eq(mErrorToken), eq(null), eq(-1),
eq(exception));
}
@Test
@@ -324,8 +325,8 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
mController.onActivityReparentedToTask(activity);
mController.dispatchPendingEvents();
verify(mOrganizer).onActivityReparentedToTask(task.mTaskId, activity.intent,
activity.token);
verify(mOrganizer).onActivityReparentedToTask(eq(task.mTaskId), eq(activity.intent),
eq(activity.token));
// Notify organizer if there is any embedded in the Task.
final TaskFragment taskFragment = new TaskFragmentBuilder(mAtm)
@@ -340,7 +341,8 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
mController.dispatchPendingEvents();
verify(mOrganizer, times(2))
.onActivityReparentedToTask(task.mTaskId, activity.intent, activity.token);
.onActivityReparentedToTask(eq(task.mTaskId), eq(activity.intent),
eq(activity.token));
}
@Test