Merge "Apply TaskFragmentOrganizer changes in one WCT (1/2)" into tm-qpr-dev

This commit is contained in:
Chris Li
2022-08-19 08:56:42 +00:00
committed by Android (Google) Code Review
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