Cleanup handleActivityCreated
1. For new created activity, check if it should be split with the other activity that is currently split with the activity below. 2. When the activity is reparented back to the Task from PiP window, check if it can be split with the existing. 3. Breakdown the long #handleActivityCreated to smaller pieces. Bug: 225371112 Test: atest WMJetpackUnitTests:SplitControllerTest Test: atest WMJetpackUnitTests:TaskFragmentContainerTest Change-Id: Iba9f1487328768f5f2c27241d04c57ee7e5ed1d4
This commit is contained in:
@@ -32,6 +32,7 @@ import android.app.ActivityClient;
|
||||
import android.app.ActivityOptions;
|
||||
import android.app.ActivityThread;
|
||||
import android.app.Instrumentation;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.res.Configuration;
|
||||
@@ -236,7 +237,16 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
|
||||
// If the activity belongs to the current app process, we treat it as a new activity launch.
|
||||
final Activity activity = getActivity(activityToken);
|
||||
if (activity != null) {
|
||||
onActivityCreated(activity);
|
||||
// We don't allow split as primary for new launch because we currently only support
|
||||
// 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 /* canSplitAsPrimary */)) {
|
||||
// When there is no embedding rule matched, try to place it in the top container
|
||||
// like a normal launch.
|
||||
placeActivityInTopContainer(activity);
|
||||
}
|
||||
updateCallbackIfNecessary();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -253,7 +263,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
|
||||
TaskFragmentContainer targetContainer = resolveStartActivityIntent(wct, taskId,
|
||||
activityIntent, null /* launchingActivity */);
|
||||
if (targetContainer == null) {
|
||||
// When there is no split rule matched, try to place it in the top container like a
|
||||
// When there is no embedding rule matched, try to place it in the top container like a
|
||||
// normal launch.
|
||||
targetContainer = taskContainer.getTopTaskFragmentContainer();
|
||||
}
|
||||
@@ -339,89 +349,244 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
|
||||
return false;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void onActivityCreated(@NonNull Activity launchedActivity) {
|
||||
handleActivityCreated(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 /* canSplitAsPrimary */);
|
||||
updateCallbackIfNecessary();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the activity start should be routed to a particular container. It can create a new
|
||||
* container for the activity and a new split container if necessary.
|
||||
* Checks if the new added activity should be routed to a particular container. It can create a
|
||||
* new container for the activity and a new split container if necessary.
|
||||
* @param launchedActivity the new launched activity.
|
||||
* @param canSplitAsPrimary whether we can put the new launched activity into primary split.
|
||||
* @return {@code true} if the activity was placed in TaskFragment container.
|
||||
*/
|
||||
// TODO(b/190433398): Break down into smaller functions.
|
||||
void handleActivityCreated(@NonNull Activity launchedActivity) {
|
||||
@VisibleForTesting
|
||||
boolean resolveActivityToContainer(@NonNull Activity launchedActivity,
|
||||
boolean canSplitAsPrimary) {
|
||||
if (isInPictureInPicture(launchedActivity) || launchedActivity.isFinishing()) {
|
||||
// We don't embed activity when it is in PIP, or finishing.
|
||||
return;
|
||||
// We don't embed activity when it is in PIP, or finishing. Return true since we don't
|
||||
// want any extra handling.
|
||||
return true;
|
||||
}
|
||||
final TaskFragmentContainer currentContainer = getContainerWithActivity(launchedActivity);
|
||||
|
||||
// Check if the activity is configured to always be expanded.
|
||||
/*
|
||||
* We will check the following to see if there is any embedding rule matched:
|
||||
* 1. Whether the new launched activity should always expand.
|
||||
* 2. Whether the new launched activity should launch a placeholder.
|
||||
* 3. Whether the new launched activity has already been in a split with a rule matched
|
||||
* (likely done in #onStartActivity).
|
||||
* 4. Whether the activity below (if any) should be split with the new launched activity.
|
||||
* 5. Whether the activity split with the activity below (if any) should be split with the
|
||||
* new launched activity.
|
||||
*/
|
||||
|
||||
// 1. Whether the new launched activity should always expand.
|
||||
if (shouldExpand(launchedActivity, null /* intent */)) {
|
||||
if (shouldContainerBeExpanded(currentContainer)) {
|
||||
// Make sure that the existing container is expanded
|
||||
mPresenter.expandTaskFragment(currentContainer.getTaskFragmentToken());
|
||||
} else {
|
||||
// Put activity into a new expanded container
|
||||
final TaskFragmentContainer newContainer = newContainer(launchedActivity,
|
||||
launchedActivity.getTaskId());
|
||||
mPresenter.expandActivity(newContainer.getTaskFragmentToken(),
|
||||
launchedActivity);
|
||||
}
|
||||
return;
|
||||
expandActivity(launchedActivity);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check if activity requires a placeholder
|
||||
// 2. Whether the new launched activity should launch a placeholder.
|
||||
if (launchPlaceholderIfNecessary(launchedActivity)) {
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
// TODO(b/190433398): Check if it is a placeholder and there is already another split
|
||||
// created by the primary activity. This is necessary for the case when the primary activity
|
||||
// launched another secondary in the split, but the placeholder was still launched by the
|
||||
// logic above. We didn't prevent the placeholder launcher because we didn't know that
|
||||
// another secondary activity is coming up.
|
||||
// 3. Whether the new launched activity has already been in a split with a rule matched.
|
||||
if (isNewActivityInSplitWithRuleMatched(launchedActivity)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check if the activity should form a split with the activity below in the same task
|
||||
// fragment.
|
||||
// 4. Whether the activity below (if any) should be split with the new launched activity.
|
||||
final Activity activityBelow = findActivityBelow(launchedActivity);
|
||||
if (activityBelow == null) {
|
||||
// Can't find any activity below.
|
||||
return false;
|
||||
}
|
||||
if (putActivitiesIntoSplitIfNecessary(activityBelow, launchedActivity)) {
|
||||
// Have split rule of [ activityBelow | launchedActivity ].
|
||||
return true;
|
||||
}
|
||||
if (canSplitAsPrimary
|
||||
&& putActivitiesIntoSplitIfNecessary(launchedActivity, activityBelow)) {
|
||||
// Have split rule of [ launchedActivity | activityBelow].
|
||||
return true;
|
||||
}
|
||||
|
||||
// 5. Whether the activity split with the activity below (if any) should be split with the
|
||||
// new launched activity.
|
||||
final TaskFragmentContainer activityBelowContainer = getContainerWithActivity(
|
||||
activityBelow);
|
||||
final SplitContainer topSplit = getActiveSplitForContainer(activityBelowContainer);
|
||||
if (topSplit == null || !isTopMostSplit(topSplit)) {
|
||||
// Skip if it is not the topmost split.
|
||||
return false;
|
||||
}
|
||||
final TaskFragmentContainer otherTopContainer =
|
||||
topSplit.getPrimaryContainer() == activityBelowContainer
|
||||
? topSplit.getSecondaryContainer()
|
||||
: topSplit.getPrimaryContainer();
|
||||
final Activity otherTopActivity = otherTopContainer.getTopNonFinishingActivity();
|
||||
if (otherTopActivity == null || otherTopActivity == launchedActivity) {
|
||||
// Can't find the top activity on the other split TaskFragment.
|
||||
return false;
|
||||
}
|
||||
if (putActivitiesIntoSplitIfNecessary(otherTopActivity, launchedActivity)) {
|
||||
// Have split rule of [ otherTopActivity | launchedActivity ].
|
||||
return true;
|
||||
}
|
||||
// Have split rule of [ launchedActivity | otherTopActivity].
|
||||
return canSplitAsPrimary
|
||||
&& putActivitiesIntoSplitIfNecessary(launchedActivity, otherTopActivity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Places the given activity to the top most TaskFragment in the task if there is any.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
void placeActivityInTopContainer(@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.
|
||||
return;
|
||||
}
|
||||
final int taskId = getTaskId(activity);
|
||||
final TaskContainer taskContainer = getTaskContainer(taskId);
|
||||
if (taskContainer == null) {
|
||||
return;
|
||||
}
|
||||
final TaskFragmentContainer targetContainer = taskContainer.getTopTaskFragmentContainer();
|
||||
if (targetContainer == null) {
|
||||
return;
|
||||
}
|
||||
targetContainer.addPendingAppearedActivity(activity);
|
||||
final WindowContainerTransaction wct = new WindowContainerTransaction();
|
||||
wct.reparentActivityToTaskFragment(targetContainer.getTaskFragmentToken(),
|
||||
activity.getActivityToken());
|
||||
mPresenter.applyTransaction(wct);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
final TaskFragmentContainer container = getContainerWithActivity(activity);
|
||||
if (shouldContainerBeExpanded(container)) {
|
||||
// Make sure that the existing container is expanded.
|
||||
mPresenter.expandTaskFragment(container.getTaskFragmentToken());
|
||||
} else {
|
||||
// Put activity into a new expanded container.
|
||||
final TaskFragmentContainer newContainer = newContainer(activity, getTaskId(activity));
|
||||
mPresenter.expandActivity(newContainer.getTaskFragmentToken(), activity);
|
||||
}
|
||||
}
|
||||
|
||||
/** Whether the given new launched activity is in a split with a rule matched. */
|
||||
private boolean isNewActivityInSplitWithRuleMatched(@NonNull Activity launchedActivity) {
|
||||
final TaskFragmentContainer container = getContainerWithActivity(launchedActivity);
|
||||
final SplitContainer splitContainer = getActiveSplitForContainer(container);
|
||||
if (splitContainer == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (container == splitContainer.getPrimaryContainer()) {
|
||||
// The new launched can be in the primary container when it is starting a new activity
|
||||
// onCreate, thus the secondary may still be empty.
|
||||
final TaskFragmentContainer secondaryContainer = splitContainer.getSecondaryContainer();
|
||||
final Activity secondaryActivity = secondaryContainer.getTopNonFinishingActivity();
|
||||
return secondaryActivity == null
|
||||
|| getSplitRule(launchedActivity, secondaryActivity) != null;
|
||||
}
|
||||
|
||||
// Check if the new launched activity is a placeholder.
|
||||
if (splitContainer.getSplitRule() instanceof SplitPlaceholderRule) {
|
||||
final SplitPlaceholderRule placeholderRule =
|
||||
(SplitPlaceholderRule) splitContainer.getSplitRule();
|
||||
final ComponentName placeholderName = placeholderRule.getPlaceholderIntent()
|
||||
.getComponent();
|
||||
// TODO(b/232330767): Do we have a better way to check this?
|
||||
return placeholderName == null
|
||||
|| placeholderName.equals(launchedActivity.getComponentName())
|
||||
|| placeholderRule.getPlaceholderIntent().equals(launchedActivity.getIntent());
|
||||
}
|
||||
|
||||
// Check if the new launched activity should be split with the primary top activity.
|
||||
final Activity primaryActivity = splitContainer.getPrimaryContainer()
|
||||
.getTopNonFinishingActivity();
|
||||
if (primaryActivity == null) {
|
||||
return false;
|
||||
}
|
||||
/* TODO(b/231845476) we should always respect clearTop.
|
||||
final SplitPairRule curSplitRule = (SplitPairRule) splitContainer.getSplitRule();
|
||||
final SplitPairRule splitRule = getSplitRule(primaryActivity, launchedActivity);
|
||||
return splitRule != null && haveSamePresentation(splitRule, curSplitRule)
|
||||
// If the new launched split rule should clear top and it is not the bottom most,
|
||||
// it means we should create a new split pair and clear the existing secondary.
|
||||
&& (!splitRule.shouldClearTop()
|
||||
|| container.getBottomMostActivity() == launchedActivity);
|
||||
*/
|
||||
return getSplitRule(primaryActivity, launchedActivity) != null;
|
||||
}
|
||||
|
||||
/** Finds the activity below the given activity. */
|
||||
@Nullable
|
||||
private Activity findActivityBelow(@NonNull Activity activity) {
|
||||
Activity activityBelow = null;
|
||||
if (currentContainer != null) {
|
||||
final List<Activity> containerActivities = currentContainer.collectActivities();
|
||||
final int index = containerActivities.indexOf(launchedActivity);
|
||||
final TaskFragmentContainer container = getContainerWithActivity(activity);
|
||||
if (container != null) {
|
||||
final List<Activity> containerActivities = container.collectActivities();
|
||||
final int index = containerActivities.indexOf(activity);
|
||||
if (index > 0) {
|
||||
activityBelow = containerActivities.get(index - 1);
|
||||
}
|
||||
}
|
||||
if (activityBelow == null) {
|
||||
IBinder belowToken = ActivityClient.getInstance().getActivityTokenBelow(
|
||||
launchedActivity.getActivityToken());
|
||||
final IBinder belowToken = ActivityClient.getInstance().getActivityTokenBelow(
|
||||
activity.getActivityToken());
|
||||
if (belowToken != null) {
|
||||
activityBelow = getActivity(belowToken);
|
||||
}
|
||||
}
|
||||
if (activityBelow == null) {
|
||||
return;
|
||||
}
|
||||
return activityBelow;
|
||||
}
|
||||
|
||||
// Check if the split is already set.
|
||||
final TaskFragmentContainer activityBelowContainer = getContainerWithActivity(
|
||||
activityBelow);
|
||||
if (currentContainer != null && activityBelowContainer != null) {
|
||||
final SplitContainer existingSplit = getActiveSplitForContainers(currentContainer,
|
||||
activityBelowContainer);
|
||||
if (existingSplit != null) {
|
||||
// There is already an active split with the activity below.
|
||||
return;
|
||||
/**
|
||||
* Checks if there is a rule to split the two activities. If there is one, puts them into split
|
||||
* and returns {@code true}. Otherwise, returns {@code false}.
|
||||
*/
|
||||
private boolean putActivitiesIntoSplitIfNecessary(@NonNull Activity primaryActivity,
|
||||
@NonNull Activity secondaryActivity) {
|
||||
final SplitPairRule splitRule = getSplitRule(primaryActivity, secondaryActivity);
|
||||
if (splitRule == null) {
|
||||
return false;
|
||||
}
|
||||
final TaskFragmentContainer primaryContainer = getContainerWithActivity(
|
||||
primaryActivity);
|
||||
final SplitContainer splitContainer = getActiveSplitForContainer(primaryContainer);
|
||||
if (splitContainer != null && primaryContainer == splitContainer.getPrimaryContainer()
|
||||
&& canReuseContainer(splitRule, splitContainer.getSplitRule())) {
|
||||
// Can launch in the existing secondary container if the rules share the same
|
||||
// presentation.
|
||||
final TaskFragmentContainer secondaryContainer = splitContainer.getSecondaryContainer();
|
||||
if (secondaryContainer == getContainerWithActivity(secondaryActivity)) {
|
||||
// The activity is already in the target TaskFragment.
|
||||
return true;
|
||||
}
|
||||
secondaryContainer.addPendingAppearedActivity(secondaryActivity);
|
||||
final WindowContainerTransaction wct = new WindowContainerTransaction();
|
||||
wct.reparentActivityToTaskFragment(
|
||||
secondaryContainer.getTaskFragmentToken(),
|
||||
secondaryActivity.getActivityToken());
|
||||
mPresenter.applyTransaction(wct);
|
||||
return true;
|
||||
}
|
||||
|
||||
final SplitPairRule splitPairRule = getSplitRule(activityBelow, launchedActivity);
|
||||
if (splitPairRule == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
mPresenter.createNewSplitContainer(activityBelow, launchedActivity,
|
||||
splitPairRule);
|
||||
// Create new split pair.
|
||||
mPresenter.createNewSplitContainer(primaryActivity, secondaryActivity, splitRule);
|
||||
return true;
|
||||
}
|
||||
|
||||
private void onActivityConfigurationChanged(@NonNull Activity activity) {
|
||||
@@ -601,7 +766,10 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
|
||||
final IBinder activityToken = activity.getActivityToken();
|
||||
for (int i = mTaskContainers.size() - 1; i >= 0; i--) {
|
||||
final List<TaskFragmentContainer> containers = mTaskContainers.valueAt(i).mContainers;
|
||||
for (TaskFragmentContainer container : containers) {
|
||||
// Traverse from top to bottom in case an activity is added to top pending, and hasn't
|
||||
// received update from server yet.
|
||||
for (int j = containers.size() - 1; j >= 0; j--) {
|
||||
final TaskFragmentContainer container = containers.get(j);
|
||||
if (container.hasActivity(activityToken)) {
|
||||
return container;
|
||||
}
|
||||
@@ -798,8 +966,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
|
||||
if (splitContainer == null) {
|
||||
return;
|
||||
}
|
||||
final List<SplitContainer> splitContainers = container.getTaskContainer().mSplitContainers;
|
||||
if (splitContainer != splitContainers.get(splitContainers.size() - 1)) {
|
||||
if (!isTopMostSplit(splitContainer)) {
|
||||
// Skip position update - it isn't the topmost split.
|
||||
return;
|
||||
}
|
||||
@@ -815,6 +982,13 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
|
||||
mPresenter.updateSplitContainer(splitContainer, container, wct);
|
||||
}
|
||||
|
||||
/** Whether the given split is the topmost split in the Task. */
|
||||
private boolean isTopMostSplit(@NonNull SplitContainer splitContainer) {
|
||||
final List<SplitContainer> splitContainers = splitContainer.getPrimaryContainer()
|
||||
.getTaskContainer().mSplitContainers;
|
||||
return splitContainer == splitContainers.get(splitContainers.size() - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the top active split container that has the provided container, if available.
|
||||
*/
|
||||
@@ -1014,14 +1188,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
|
||||
if (container == null) {
|
||||
return false;
|
||||
}
|
||||
final List<SplitContainer> splitContainers = container.getTaskContainer().mSplitContainers;
|
||||
for (SplitContainer splitContainer : splitContainers) {
|
||||
if (container.equals(splitContainer.getPrimaryContainer())
|
||||
|| container.equals(splitContainer.getSecondaryContainer())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
return getActiveSplitForContainer(container) == null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1279,15 +1446,18 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
|
||||
if (!isContainerReusableRule(rule1) || !isContainerReusableRule(rule2)) {
|
||||
return false;
|
||||
}
|
||||
final SplitPairRule pairRule1 = (SplitPairRule) rule1;
|
||||
final SplitPairRule pairRule2 = (SplitPairRule) rule2;
|
||||
return haveSamePresentation((SplitPairRule) rule1, (SplitPairRule) rule2);
|
||||
}
|
||||
|
||||
/** Whether the two rules have the same presentation. */
|
||||
private static boolean haveSamePresentation(SplitPairRule rule1, SplitPairRule rule2) {
|
||||
// TODO(b/231655482): add util method to do the comparison in SplitPairRule.
|
||||
return pairRule1.getSplitRatio() == pairRule2.getSplitRatio()
|
||||
&& pairRule1.getLayoutDirection() == pairRule2.getLayoutDirection()
|
||||
&& pairRule1.getFinishPrimaryWithSecondary()
|
||||
== pairRule2.getFinishPrimaryWithSecondary()
|
||||
&& pairRule1.getFinishSecondaryWithPrimary()
|
||||
== pairRule2.getFinishSecondaryWithPrimary();
|
||||
return rule1.getSplitRatio() == rule2.getSplitRatio()
|
||||
&& rule1.getLayoutDirection() == rule2.getLayoutDirection()
|
||||
&& rule1.getFinishPrimaryWithSecondary()
|
||||
== rule2.getFinishPrimaryWithSecondary()
|
||||
&& rule1.getFinishSecondaryWithPrimary()
|
||||
== rule2.getFinishSecondaryWithPrimary();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -121,20 +121,24 @@ class TaskFragmentContainer {
|
||||
/** List of activities that belong to this container and live in this process. */
|
||||
@NonNull
|
||||
List<Activity> collectActivities() {
|
||||
final List<Activity> allActivities = new ArrayList<>();
|
||||
if (mInfo != null) {
|
||||
// Add activities reported from the server.
|
||||
for (IBinder token : mInfo.getActivities()) {
|
||||
final Activity activity = mController.getActivity(token);
|
||||
if (activity != null && !activity.isFinishing()) {
|
||||
allActivities.add(activity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add the re-parenting activity, in case the server has not yet reported the task
|
||||
// fragment info update with it placed in this container. We still want to apply rules
|
||||
// in this intermediate state.
|
||||
List<Activity> allActivities = new ArrayList<>();
|
||||
if (!mPendingAppearedActivities.isEmpty()) {
|
||||
allActivities.addAll(mPendingAppearedActivities);
|
||||
}
|
||||
// Add activities reported from the server.
|
||||
if (mInfo == null) {
|
||||
return allActivities;
|
||||
}
|
||||
for (IBinder token : mInfo.getActivities()) {
|
||||
Activity activity = mController.getActivity(token);
|
||||
if (activity != null && !activity.isFinishing() && !allActivities.contains(activity)) {
|
||||
// Place those on top of the list since they will be on the top after reported from the
|
||||
// server.
|
||||
for (Activity activity : mPendingAppearedActivities) {
|
||||
if (!activity.isFinishing()) {
|
||||
allActivities.add(activity);
|
||||
}
|
||||
}
|
||||
@@ -241,6 +245,12 @@ class TaskFragmentContainer {
|
||||
return i >= 0 ? activities.get(i) : null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
Activity getBottomMostActivity() {
|
||||
final List<Activity> activities = collectActivities();
|
||||
return activities.isEmpty() ? null : activities.get(0);
|
||||
}
|
||||
|
||||
boolean isEmpty() {
|
||||
return mPendingAppearedActivities.isEmpty() && (mInfo == null || mInfo.isEmpty());
|
||||
}
|
||||
|
||||
@@ -31,8 +31,11 @@ import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertThrows;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyBoolean;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.ArgumentMatchers.isNull;
|
||||
import static org.mockito.Mockito.clearInvocations;
|
||||
import static org.mockito.Mockito.doNothing;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.mock;
|
||||
@@ -40,6 +43,7 @@ import static org.mockito.Mockito.never;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.app.Activity;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Intent;
|
||||
import android.content.res.Configuration;
|
||||
import android.content.res.Resources;
|
||||
@@ -80,6 +84,8 @@ public class SplitControllerTest {
|
||||
private static final int TASK_ID = 10;
|
||||
private static final Rect TASK_BOUNDS = new Rect(0, 0, 600, 1200);
|
||||
private static final float SPLIT_RATIO = 0.5f;
|
||||
private static final Intent PLACEHOLDER_INTENT = new Intent().setComponent(
|
||||
new ComponentName("test", "placeholder"));
|
||||
|
||||
private Activity mActivity;
|
||||
@Mock
|
||||
@@ -101,6 +107,7 @@ public class SplitControllerTest {
|
||||
mSplitPresenter = mSplitController.mPresenter;
|
||||
spyOn(mSplitController);
|
||||
spyOn(mSplitPresenter);
|
||||
doNothing().when(mSplitPresenter).applyTransaction(any());
|
||||
final Configuration activityConfig = new Configuration();
|
||||
activityConfig.windowConfiguration.setBounds(TASK_BOUNDS);
|
||||
activityConfig.windowConfiguration.setMaxBounds(TASK_BOUNDS);
|
||||
@@ -275,13 +282,25 @@ public class SplitControllerTest {
|
||||
verify(mSplitPresenter).updateSplitContainer(splitContainer, tf, mTransaction);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnActivityCreated() {
|
||||
mSplitController.onActivityCreated(mActivity);
|
||||
|
||||
// Disallow to split as primary because we want the new launch to be always on top.
|
||||
verify(mSplitController).resolveActivityToContainer(mActivity,
|
||||
false /* canSplitAsPrimary */);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnActivityReparentToTask_sameProcess() {
|
||||
mSplitController.onActivityReparentToTask(TASK_ID, new Intent(),
|
||||
mActivity.getActivityToken());
|
||||
|
||||
// Treated as on activity created.
|
||||
verify(mSplitController).onActivityCreated(mActivity);
|
||||
// Treated as on activity created, but allow to split as primary.
|
||||
verify(mSplitController).resolveActivityToContainer(mActivity,
|
||||
true /* canSplitAsPrimary */);
|
||||
// Try to place the activity to the top TaskFragment when there is no matched rule.
|
||||
verify(mSplitController).placeActivityInTopContainer(mActivity);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -294,7 +313,7 @@ public class SplitControllerTest {
|
||||
mSplitController.onActivityReparentToTask(TASK_ID, intent, activityToken);
|
||||
|
||||
// Treated as starting new intent
|
||||
verify(mSplitController, never()).onActivityCreated(mActivity);
|
||||
verify(mSplitController, never()).resolveActivityToContainer(any(), anyBoolean());
|
||||
verify(mSplitController).resolveStartActivityIntent(any(), eq(TASK_ID), eq(intent),
|
||||
isNull());
|
||||
}
|
||||
@@ -391,6 +410,327 @@ public class SplitControllerTest {
|
||||
assertSplitPair(primaryContainer, container);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPlaceActivityInTopContainer() {
|
||||
mSplitController.placeActivityInTopContainer(mActivity);
|
||||
|
||||
verify(mSplitPresenter, never()).applyTransaction(any());
|
||||
|
||||
mSplitController.newContainer(null /* activity */, mActivity, TASK_ID);
|
||||
mSplitController.placeActivityInTopContainer(mActivity);
|
||||
|
||||
verify(mSplitPresenter).applyTransaction(any());
|
||||
|
||||
// Not reparent if activity is in a TaskFragment.
|
||||
clearInvocations(mSplitPresenter);
|
||||
mSplitController.newContainer(mActivity, TASK_ID);
|
||||
mSplitController.placeActivityInTopContainer(mActivity);
|
||||
|
||||
verify(mSplitPresenter, never()).applyTransaction(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResolveActivityToContainer_noRuleMatched() {
|
||||
final boolean result = mSplitController.resolveActivityToContainer(mActivity,
|
||||
false /* canSplitAsPrimary */);
|
||||
|
||||
assertFalse(result);
|
||||
verify(mSplitController, never()).newContainer(any(), any(), anyInt());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResolveActivityToContainer_expandRule_notInTaskFragment() {
|
||||
setupExpandRule(mActivity);
|
||||
|
||||
// When the activity is not in any TaskFragment, create a new expanded TaskFragment for it.
|
||||
final boolean result = mSplitController.resolveActivityToContainer(mActivity,
|
||||
false /* canSplitAsPrimary */);
|
||||
final TaskFragmentContainer container = mSplitController.getContainerWithActivity(
|
||||
mActivity);
|
||||
|
||||
assertTrue(result);
|
||||
assertNotNull(container);
|
||||
verify(mSplitController).newContainer(mActivity, TASK_ID);
|
||||
verify(mSplitPresenter).expandActivity(container.getTaskFragmentToken(), mActivity);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResolveActivityToContainer_expandRule_inSingleTaskFragment() {
|
||||
setupExpandRule(mActivity);
|
||||
|
||||
// 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,
|
||||
false /* canSplitAsPrimary */);
|
||||
|
||||
assertTrue(result);
|
||||
verify(mSplitPresenter).expandTaskFragment(container.getTaskFragmentToken());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResolveActivityToContainer_expandRule_inSplitTaskFragment() {
|
||||
setupExpandRule(mActivity);
|
||||
|
||||
// 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,
|
||||
false /* canSplitAsPrimary */);
|
||||
final TaskFragmentContainer container = mSplitController.getContainerWithActivity(
|
||||
mActivity);
|
||||
|
||||
assertTrue(result);
|
||||
assertNotNull(container);
|
||||
verify(mSplitPresenter).expandActivity(container.getTaskFragmentToken(), mActivity);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResolveActivityToContainer_placeholderRule_notInTaskFragment() {
|
||||
setupPlaceholderRule(mActivity);
|
||||
final SplitPlaceholderRule placeholderRule =
|
||||
(SplitPlaceholderRule) mSplitController.getSplitRules().get(0);
|
||||
|
||||
// Launch placeholder if the activity is not in any TaskFragment.
|
||||
final boolean result = mSplitController.resolveActivityToContainer(mActivity,
|
||||
false /* canSplitAsPrimary */);
|
||||
|
||||
assertTrue(result);
|
||||
verify(mSplitPresenter).startActivityToSide(mActivity, PLACEHOLDER_INTENT,
|
||||
null /* activityOptions */, placeholderRule, true /* isPlaceholder */);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResolveActivityToContainer_placeholderRule_inOccludedTaskFragment() {
|
||||
setupPlaceholderRule(mActivity);
|
||||
|
||||
// Don't launch placeholder if the activity is not in the topmost active TaskFragment.
|
||||
final Activity activity = createMockActivity();
|
||||
mSplitController.newContainer(mActivity, TASK_ID);
|
||||
mSplitController.newContainer(activity, TASK_ID);
|
||||
final boolean result = mSplitController.resolveActivityToContainer(mActivity,
|
||||
false /* canSplitAsPrimary */);
|
||||
|
||||
assertFalse(result);
|
||||
verify(mSplitPresenter, never()).startActivityToSide(any(), any(), any(), any(),
|
||||
anyBoolean());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResolveActivityToContainer_placeholderRule_inTopMostTaskFragment() {
|
||||
setupPlaceholderRule(mActivity);
|
||||
final SplitPlaceholderRule placeholderRule =
|
||||
(SplitPlaceholderRule) mSplitController.getSplitRules().get(0);
|
||||
|
||||
// Launch placeholder if the activity is in the topmost expanded TaskFragment.
|
||||
mSplitController.newContainer(mActivity, TASK_ID);
|
||||
final boolean result = mSplitController.resolveActivityToContainer(mActivity,
|
||||
false /* canSplitAsPrimary */);
|
||||
|
||||
assertTrue(result);
|
||||
verify(mSplitPresenter).startActivityToSide(mActivity, PLACEHOLDER_INTENT,
|
||||
null /* activityOptions */, placeholderRule, true /* isPlaceholder */);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResolveActivityToContainer_placeholderRule_inPrimarySplit() {
|
||||
setupPlaceholderRule(mActivity);
|
||||
|
||||
// Don't launch placeholder if the activity is in primary split.
|
||||
final Activity secondaryActivity = createMockActivity();
|
||||
addSplitTaskFragments(mActivity, secondaryActivity);
|
||||
final boolean result = mSplitController.resolveActivityToContainer(mActivity,
|
||||
false /* canSplitAsPrimary */);
|
||||
|
||||
assertFalse(result);
|
||||
verify(mSplitPresenter, never()).startActivityToSide(any(), any(), any(), any(),
|
||||
anyBoolean());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResolveActivityToContainer_placeholderRule_inSecondarySplit() {
|
||||
setupPlaceholderRule(mActivity);
|
||||
final SplitPlaceholderRule placeholderRule =
|
||||
(SplitPlaceholderRule) mSplitController.getSplitRules().get(0);
|
||||
|
||||
// Launch placeholder if the activity is in secondary split.
|
||||
final Activity primaryActivity = createMockActivity();
|
||||
addSplitTaskFragments(primaryActivity, mActivity);
|
||||
final boolean result = mSplitController.resolveActivityToContainer(mActivity,
|
||||
false /* canSplitAsPrimary */);
|
||||
|
||||
assertTrue(result);
|
||||
verify(mSplitPresenter).startActivityToSide(mActivity, PLACEHOLDER_INTENT,
|
||||
null /* activityOptions */, placeholderRule, true /* isPlaceholder */);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResolveActivityToContainer_splitRule_inPrimarySplitWithRuleMatched() {
|
||||
final Intent secondaryIntent = new Intent();
|
||||
setupSplitRule(mActivity, secondaryIntent);
|
||||
final SplitPairRule splitRule = (SplitPairRule) mSplitController.getSplitRules().get(0);
|
||||
|
||||
// Activity is already in primary split, no need to create new split.
|
||||
final TaskFragmentContainer primaryContainer = mSplitController.newContainer(mActivity,
|
||||
TASK_ID);
|
||||
final TaskFragmentContainer secondaryContainer = mSplitController.newContainer(
|
||||
null /* activity */, mActivity, TASK_ID);
|
||||
mSplitController.registerSplit(
|
||||
mTransaction,
|
||||
primaryContainer,
|
||||
mActivity,
|
||||
secondaryContainer,
|
||||
splitRule);
|
||||
clearInvocations(mSplitController);
|
||||
final boolean result = mSplitController.resolveActivityToContainer(mActivity,
|
||||
false /* canSplitAsPrimary */);
|
||||
|
||||
assertTrue(result);
|
||||
verify(mSplitController, never()).newContainer(any(), any(), anyInt());
|
||||
verify(mSplitController, never()).registerSplit(any(), any(), any(), any(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResolveActivityToContainer_splitRule_inSecondarySplitWithRuleMatched() {
|
||||
final Activity primaryActivity = createMockActivity();
|
||||
setupSplitRule(primaryActivity, mActivity);
|
||||
|
||||
// Activity is already in secondary split, no need to create new split.
|
||||
addSplitTaskFragments(primaryActivity, mActivity);
|
||||
clearInvocations(mSplitController);
|
||||
final boolean result = mSplitController.resolveActivityToContainer(mActivity,
|
||||
false /* canSplitAsPrimary */);
|
||||
|
||||
assertTrue(result);
|
||||
verify(mSplitController, never()).newContainer(any(), any(), anyInt());
|
||||
verify(mSplitController, never()).registerSplit(any(), any(), any(), any(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResolveActivityToContainer_splitRule_inSecondarySplitWithNoRuleMatched() {
|
||||
final Activity primaryActivity = createMockActivity();
|
||||
final Activity secondaryActivity = createMockActivity();
|
||||
setupSplitRule(primaryActivity, secondaryActivity);
|
||||
|
||||
// Activity is in secondary split, but there is no rule to split it with primary.
|
||||
addSplitTaskFragments(primaryActivity, secondaryActivity);
|
||||
mSplitController.getContainerWithActivity(secondaryActivity)
|
||||
.addPendingAppearedActivity(mActivity);
|
||||
final boolean result = mSplitController.resolveActivityToContainer(mActivity,
|
||||
false /* canSplitAsPrimary */);
|
||||
|
||||
assertFalse(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResolveActivityToContainer_placeholderRule_isPlaceholderWithRuleMatched() {
|
||||
final Activity primaryActivity = createMockActivity();
|
||||
setupPlaceholderRule(primaryActivity);
|
||||
final SplitPlaceholderRule placeholderRule =
|
||||
(SplitPlaceholderRule) mSplitController.getSplitRules().get(0);
|
||||
doReturn(PLACEHOLDER_INTENT).when(mActivity).getIntent();
|
||||
|
||||
// Activity is a placeholder.
|
||||
final TaskFragmentContainer primaryContainer = mSplitController.newContainer(
|
||||
primaryActivity, TASK_ID);
|
||||
final TaskFragmentContainer secondaryContainer = mSplitController.newContainer(mActivity,
|
||||
TASK_ID);
|
||||
mSplitController.registerSplit(
|
||||
mTransaction,
|
||||
primaryContainer,
|
||||
mActivity,
|
||||
secondaryContainer,
|
||||
placeholderRule);
|
||||
final boolean result = mSplitController.resolveActivityToContainer(mActivity,
|
||||
false /* canSplitAsPrimary */);
|
||||
|
||||
assertTrue(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResolveActivityToContainer_splitRule_splitWithActivityBelowAsSecondary() {
|
||||
final Activity activityBelow = createMockActivity();
|
||||
setupSplitRule(activityBelow, mActivity);
|
||||
|
||||
final TaskFragmentContainer container = mSplitController.newContainer(activityBelow,
|
||||
TASK_ID);
|
||||
container.addPendingAppearedActivity(mActivity);
|
||||
final boolean result = mSplitController.resolveActivityToContainer(mActivity,
|
||||
false /* canSplitAsPrimary */);
|
||||
|
||||
assertTrue(result);
|
||||
assertSplitPair(activityBelow, mActivity);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResolveActivityToContainer_splitRule_splitWithActivityBelowAsPrimary() {
|
||||
final Activity activityBelow = createMockActivity();
|
||||
setupSplitRule(mActivity, activityBelow);
|
||||
|
||||
// Disallow to split as primary.
|
||||
final TaskFragmentContainer container = mSplitController.newContainer(activityBelow,
|
||||
TASK_ID);
|
||||
container.addPendingAppearedActivity(mActivity);
|
||||
boolean result = mSplitController.resolveActivityToContainer(mActivity,
|
||||
false /* canSplitAsPrimary */);
|
||||
|
||||
assertFalse(result);
|
||||
assertEquals(container, mSplitController.getContainerWithActivity(mActivity));
|
||||
|
||||
// Allow to split as primary.
|
||||
result = mSplitController.resolveActivityToContainer(mActivity,
|
||||
true /* canSplitAsPrimary */);
|
||||
|
||||
assertTrue(result);
|
||||
assertSplitPair(mActivity, activityBelow);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResolveActivityToContainer_splitRule_splitWithCurrentPrimaryAsSecondary() {
|
||||
final Activity primaryActivity = createMockActivity();
|
||||
setupSplitRule(primaryActivity, mActivity);
|
||||
|
||||
final Activity activityBelow = createMockActivity();
|
||||
addSplitTaskFragments(primaryActivity, activityBelow);
|
||||
final TaskFragmentContainer primaryContainer = mSplitController.getContainerWithActivity(
|
||||
primaryActivity);
|
||||
final TaskFragmentContainer secondaryContainer = mSplitController.getContainerWithActivity(
|
||||
activityBelow);
|
||||
secondaryContainer.addPendingAppearedActivity(mActivity);
|
||||
final boolean result = mSplitController.resolveActivityToContainer(mActivity,
|
||||
false /* canSplitAsPrimary */);
|
||||
final TaskFragmentContainer container = mSplitController.getContainerWithActivity(
|
||||
mActivity);
|
||||
|
||||
assertTrue(result);
|
||||
// TODO(b/231845476) we should always respect clearTop.
|
||||
// assertNotEquals(secondaryContainer, container);
|
||||
assertSplitPair(primaryContainer, container);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResolveActivityToContainer_splitRule_splitWithCurrentPrimaryAsPrimary() {
|
||||
final Activity primaryActivity = createMockActivity();
|
||||
setupSplitRule(mActivity, primaryActivity);
|
||||
|
||||
final Activity activityBelow = createMockActivity();
|
||||
addSplitTaskFragments(primaryActivity, activityBelow);
|
||||
final TaskFragmentContainer primaryContainer = mSplitController.getContainerWithActivity(
|
||||
primaryActivity);
|
||||
primaryContainer.addPendingAppearedActivity(mActivity);
|
||||
boolean result = mSplitController.resolveActivityToContainer(mActivity,
|
||||
false /* canSplitAsPrimary */);
|
||||
|
||||
assertFalse(result);
|
||||
assertEquals(primaryContainer, mSplitController.getContainerWithActivity(mActivity));
|
||||
|
||||
|
||||
result = mSplitController.resolveActivityToContainer(mActivity,
|
||||
true /* canSplitAsPrimary */);
|
||||
|
||||
assertTrue(result);
|
||||
assertSplitPair(mActivity, primaryActivity);
|
||||
}
|
||||
|
||||
/** Creates a mock activity in the organizer process. */
|
||||
private Activity createMockActivity() {
|
||||
final Activity activity = mock(Activity.class);
|
||||
@@ -398,6 +738,7 @@ public class SplitControllerTest {
|
||||
final IBinder activityToken = new Binder();
|
||||
doReturn(activityToken).when(activity).getActivityToken();
|
||||
doReturn(activity).when(mSplitController).getActivity(activityToken);
|
||||
doReturn(TASK_ID).when(activity).getTaskId();
|
||||
return activity;
|
||||
}
|
||||
|
||||
@@ -418,12 +759,18 @@ public class SplitControllerTest {
|
||||
/** Creates a mock TaskFragment that has been registered and appeared in the organizer. */
|
||||
private TaskFragmentContainer createMockTaskFragmentContainer(@NonNull Activity activity) {
|
||||
final TaskFragmentContainer container = mSplitController.newContainer(activity, TASK_ID);
|
||||
final TaskFragmentInfo info = createMockTaskFragmentInfo(container, activity);
|
||||
container.setInfo(createMockTaskFragmentInfo(container, activity));
|
||||
mSplitPresenter.mFragmentInfos.put(container.getTaskFragmentToken(), info);
|
||||
setupTaskFragmentInfo(container, activity);
|
||||
return container;
|
||||
}
|
||||
|
||||
/** Setups the given TaskFragment as it has appeared in the server. */
|
||||
private void setupTaskFragmentInfo(@NonNull TaskFragmentContainer container,
|
||||
@NonNull Activity activity) {
|
||||
final TaskFragmentInfo info = createMockTaskFragmentInfo(container, activity);
|
||||
container.setInfo(info);
|
||||
mSplitPresenter.mFragmentInfos.put(container.getTaskFragmentToken(), info);
|
||||
}
|
||||
|
||||
/** Setups a rule to always expand the given intent. */
|
||||
private void setupExpandRule(@NonNull Intent expandIntent) {
|
||||
final ActivityRule expandRule = new ActivityRule.Builder(r -> false, expandIntent::equals)
|
||||
@@ -432,6 +779,23 @@ public class SplitControllerTest {
|
||||
mSplitController.setEmbeddingRules(Collections.singleton(expandRule));
|
||||
}
|
||||
|
||||
/** Setups a rule to always expand the given activity. */
|
||||
private void setupExpandRule(@NonNull Activity expandActivity) {
|
||||
final ActivityRule expandRule = new ActivityRule.Builder(expandActivity::equals, i -> false)
|
||||
.setShouldAlwaysExpand(true)
|
||||
.build();
|
||||
mSplitController.setEmbeddingRules(Collections.singleton(expandRule));
|
||||
}
|
||||
|
||||
/** Setups a rule to launch placeholder for the given activity. */
|
||||
private void setupPlaceholderRule(@NonNull Activity primaryActivity) {
|
||||
final SplitRule placeholderRule = new SplitPlaceholderRule.Builder(PLACEHOLDER_INTENT,
|
||||
primaryActivity::equals, i -> false, w -> true)
|
||||
.setSplitRatio(SPLIT_RATIO)
|
||||
.build();
|
||||
mSplitController.setEmbeddingRules(Collections.singleton(placeholderRule));
|
||||
}
|
||||
|
||||
/** Setups a rule to always split the given activities. */
|
||||
private void setupSplitRule(@NonNull Activity primaryActivity,
|
||||
@NonNull Intent secondaryIntent) {
|
||||
@@ -439,6 +803,13 @@ public class SplitControllerTest {
|
||||
mSplitController.setEmbeddingRules(Collections.singleton(splitRule));
|
||||
}
|
||||
|
||||
/** Setups a rule to always split the given activities. */
|
||||
private void setupSplitRule(@NonNull Activity primaryActivity,
|
||||
@NonNull Activity secondaryActivity) {
|
||||
final SplitRule splitRule = createSplitRule(primaryActivity, secondaryActivity);
|
||||
mSplitController.setEmbeddingRules(Collections.singleton(splitRule));
|
||||
}
|
||||
|
||||
/** Creates a rule to always split the given activity and the given intent. */
|
||||
private SplitRule createSplitRule(@NonNull Activity primaryActivity,
|
||||
@NonNull Intent secondaryIntent) {
|
||||
@@ -499,19 +870,31 @@ public class SplitControllerTest {
|
||||
TASK_BOUNDS.bottom);
|
||||
}
|
||||
|
||||
/** Asserts that the two given activities are in split. */
|
||||
private void assertSplitPair(@NonNull Activity primaryActivity,
|
||||
@NonNull Activity secondaryActivity) {
|
||||
assertSplitPair(mSplitController.getContainerWithActivity(primaryActivity),
|
||||
mSplitController.getContainerWithActivity(secondaryActivity));
|
||||
}
|
||||
|
||||
/** Asserts that the two given TaskFragments are in split. */
|
||||
private void assertSplitPair(@NonNull TaskFragmentContainer primaryContainer,
|
||||
@NonNull TaskFragmentContainer secondaryContainer) {
|
||||
assertNotNull(primaryContainer);
|
||||
assertNotNull(secondaryContainer);
|
||||
assertTrue(primaryContainer.areLastRequestedBoundsEqual(
|
||||
getSplitBounds(true /* isPrimary */)));
|
||||
assertTrue(secondaryContainer.areLastRequestedBoundsEqual(
|
||||
getSplitBounds(false /* isPrimary */)));
|
||||
assertTrue(primaryContainer.isLastRequestedWindowingModeEqual(WINDOWING_MODE_MULTI_WINDOW));
|
||||
assertTrue(secondaryContainer.isLastRequestedWindowingModeEqual(
|
||||
WINDOWING_MODE_MULTI_WINDOW));
|
||||
assertNotNull(mSplitController.getActiveSplitForContainers(primaryContainer,
|
||||
secondaryContainer));
|
||||
if (primaryContainer.mInfo != null) {
|
||||
assertTrue(primaryContainer.areLastRequestedBoundsEqual(
|
||||
getSplitBounds(true /* isPrimary */)));
|
||||
assertTrue(primaryContainer.isLastRequestedWindowingModeEqual(
|
||||
WINDOWING_MODE_MULTI_WINDOW));
|
||||
}
|
||||
if (secondaryContainer.mInfo != null) {
|
||||
assertTrue(secondaryContainer.areLastRequestedBoundsEqual(
|
||||
getSplitBounds(false /* isPrimary */)));
|
||||
assertTrue(secondaryContainer.isLastRequestedWindowingModeEqual(
|
||||
WINDOWING_MODE_MULTI_WINDOW));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ package androidx.window.extensions.embedding;
|
||||
|
||||
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
@@ -29,7 +30,9 @@ import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.os.Binder;
|
||||
import android.os.Handler;
|
||||
import android.os.IBinder;
|
||||
import android.platform.test.annotations.Presubmit;
|
||||
import android.window.TaskFragmentInfo;
|
||||
import android.window.WindowContainerTransaction;
|
||||
@@ -37,6 +40,8 @@ import android.window.WindowContainerTransaction;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import androidx.test.filters.SmallTest;
|
||||
|
||||
import com.google.android.collect.Lists;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
@@ -44,6 +49,7 @@ import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Test class for {@link TaskFragmentContainer}.
|
||||
@@ -62,16 +68,16 @@ public class TaskFragmentContainerTest {
|
||||
@Mock
|
||||
private SplitController mController;
|
||||
@Mock
|
||||
private Activity mActivity;
|
||||
@Mock
|
||||
private TaskFragmentInfo mInfo;
|
||||
@Mock
|
||||
private Handler mHandler;
|
||||
private Activity mActivity;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
doReturn(mHandler).when(mController).getHandler();
|
||||
mActivity = createMockActivity();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -165,4 +171,72 @@ public class TaskFragmentContainerTest {
|
||||
assertNull(container.mAppearEmptyTimeout);
|
||||
verify(mController).onTaskFragmentAppearEmptyTimeout(container);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCollectActivities() {
|
||||
final TaskContainer taskContainer = new TaskContainer(TASK_ID);
|
||||
final TaskFragmentContainer container = new TaskFragmentContainer(null /* activity */,
|
||||
taskContainer, mController);
|
||||
List<Activity> activities = container.collectActivities();
|
||||
|
||||
assertTrue(activities.isEmpty());
|
||||
|
||||
container.addPendingAppearedActivity(mActivity);
|
||||
activities = container.collectActivities();
|
||||
|
||||
assertEquals(1, activities.size());
|
||||
|
||||
final Activity activity0 = createMockActivity();
|
||||
final Activity activity1 = createMockActivity();
|
||||
final List<IBinder> runningActivities = Lists.newArrayList(activity0.getActivityToken(),
|
||||
activity1.getActivityToken());
|
||||
doReturn(runningActivities).when(mInfo).getActivities();
|
||||
container.setInfo(mInfo);
|
||||
activities = container.collectActivities();
|
||||
|
||||
assertEquals(3, activities.size());
|
||||
assertEquals(activity0, activities.get(0));
|
||||
assertEquals(activity1, activities.get(1));
|
||||
assertEquals(mActivity, activities.get(2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddPendingActivity() {
|
||||
final TaskContainer taskContainer = new TaskContainer(TASK_ID);
|
||||
final TaskFragmentContainer container = new TaskFragmentContainer(null /* activity */,
|
||||
taskContainer, mController);
|
||||
container.addPendingAppearedActivity(mActivity);
|
||||
|
||||
assertEquals(1, container.collectActivities().size());
|
||||
|
||||
container.addPendingAppearedActivity(mActivity);
|
||||
|
||||
assertEquals(1, container.collectActivities().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetBottomMostActivity() {
|
||||
final TaskContainer taskContainer = new TaskContainer(TASK_ID);
|
||||
final TaskFragmentContainer container = new TaskFragmentContainer(null /* activity */,
|
||||
taskContainer, mController);
|
||||
container.addPendingAppearedActivity(mActivity);
|
||||
|
||||
assertEquals(mActivity, container.getBottomMostActivity());
|
||||
|
||||
final Activity activity = createMockActivity();
|
||||
final List<IBinder> runningActivities = Lists.newArrayList(activity.getActivityToken());
|
||||
doReturn(runningActivities).when(mInfo).getActivities();
|
||||
container.setInfo(mInfo);
|
||||
|
||||
assertEquals(activity, container.getBottomMostActivity());
|
||||
}
|
||||
|
||||
/** Creates a mock activity in the organizer process. */
|
||||
private Activity createMockActivity() {
|
||||
final Activity activity = mock(Activity.class);
|
||||
final IBinder activityToken = new Binder();
|
||||
doReturn(activityToken).when(activity).getActivityToken();
|
||||
doReturn(activity).when(mController).getActivity(activityToken);
|
||||
return activity;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -893,7 +893,11 @@ public class AppTransitionController {
|
||||
// We cannot promote the animation on Task's parent when the task is in
|
||||
// clearing task in case the animating get stuck when performing the opening
|
||||
// task that behind it.
|
||||
|| (current.asTask() != null && current.asTask().mInRemoveTask)) {
|
||||
|| (current.asTask() != null && current.asTask().mInRemoveTask)
|
||||
// We cannot promote the animation to changing window. This may happen when an
|
||||
// activity is open in a TaskFragment that is resizing, while the existing
|
||||
// activity in the TaskFragment is reparented to another TaskFragment.
|
||||
|| parent.isChangingAppTransition()) {
|
||||
canPromote = false;
|
||||
} else {
|
||||
// In case a descendant of the parent belongs to the other group, we cannot promote
|
||||
|
||||
Reference in New Issue
Block a user