Add pendingAppearedIntent to TaskFragmentContainer

1. When an activity is launched without specifying launch TaskFragment,
   it may be placed to the primary TaskFragment of a split if all
   activities in secondary split are finishing. In this case, we still
   want to check if activity should be splitted with the activity below.

   In order to do that, we need to know if it should be split with the
   secondary TaskFragment, which may still be empty, so store the
   pending appeared Intent until the TaskFragment becomes non-empty.

   i.  The secondary TaskFragment can be empty when the primary trys to
       start the secondary during #onCreated.
   ii. If the Intent never actually started, it will trigger
       #onTaskFragmentAppearEmptyTimeout to update the existing top
       container.

2. When we are finishing a TaskFragment, we used to finish all
   activities in it as well. We should not do that for activity that is
   requested to be reparented (as pending appeared).

Fix: 233684054
Test: atest WMJetpackUnitTests:TaskFragmentContainerTest
Test: atest WMJetpackUnitTests:SplitControllerTest
Change-Id: I47329b23709da7e069a6a0c84124a3d7d08aae12
This commit is contained in:
Chris Li
2022-05-24 23:43:45 +08:00
parent aa6bb0adb5
commit f7f106b437
7 changed files with 227 additions and 68 deletions

View File

@@ -530,11 +530,18 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
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.
// onCreate.
final TaskFragmentContainer secondaryContainer = splitContainer.getSecondaryContainer();
final Intent secondaryIntent = secondaryContainer.getPendingAppearedIntent();
if (secondaryIntent != null) {
// Check with the pending Intent before it is started on the server side.
// This can happen if the launched Activity start a new Intent to secondary during
// #onCreated().
return getSplitRule(launchedActivity, secondaryIntent) != null;
}
final Activity secondaryActivity = secondaryContainer.getTopNonFinishingActivity();
return secondaryActivity == null
|| getSplitRule(launchedActivity, secondaryActivity) != null;
return secondaryActivity != null
&& getSplitRule(launchedActivity, secondaryActivity) != null;
}
// Check if the new launched activity is a placeholder.
@@ -573,7 +580,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
Activity activityBelow = null;
final TaskFragmentContainer container = getContainerWithActivity(activity);
if (container != null) {
final List<Activity> containerActivities = container.collectActivities();
final List<Activity> containerActivities = container.collectNonFinishingActivities();
final int index = containerActivities.indexOf(activity);
if (index > 0) {
activityBelow = containerActivities.get(index - 1);
@@ -691,7 +698,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
// 1. Whether the new activity intent should always expand.
if (shouldExpand(null /* activity */, intent)) {
return createEmptyExpandedContainer(wct, taskId, launchingActivity);
return createEmptyExpandedContainer(wct, intent, taskId, launchingActivity);
}
// 2. Whether the launching activity (if set) should be split with the new activity intent.
@@ -742,7 +749,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
*/
@Nullable
private TaskFragmentContainer createEmptyExpandedContainer(
@NonNull WindowContainerTransaction wct, int taskId,
@NonNull WindowContainerTransaction wct, @NonNull Intent intent, int taskId,
@Nullable Activity launchingActivity) {
// We need an activity in the organizer process in the same Task to use as the owner
// activity, as well as to get the Task window info.
@@ -759,8 +766,8 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
// Can't find any activity in the Task that we can use as the owner activity.
return null;
}
final TaskFragmentContainer expandedContainer = newContainer(null /* activity */,
activityInTask, taskId);
final TaskFragmentContainer expandedContainer = newContainer(intent, activityInTask,
taskId);
mPresenter.createTaskFragment(wct, expandedContainer.getTaskFragmentToken(),
activityInTask.getActivityToken(), new Rect(), WINDOWING_MODE_UNDEFINED);
return expandedContainer;
@@ -789,7 +796,8 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
return splitContainer.getSecondaryContainer();
}
// Create a new TaskFragment to split with the primary activity for the new activity.
return mPresenter.createNewSplitWithEmptySideContainer(wct, primaryActivity, splitRule);
return mPresenter.createNewSplitWithEmptySideContainer(wct, primaryActivity, intent,
splitRule);
}
/**
@@ -813,21 +821,34 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
return null;
}
TaskFragmentContainer newContainer(@NonNull Activity activity, int taskId) {
return newContainer(activity, activity, taskId);
TaskFragmentContainer newContainer(@NonNull Activity pendingAppearedActivity, int taskId) {
return newContainer(pendingAppearedActivity, pendingAppearedActivity, taskId);
}
TaskFragmentContainer newContainer(@NonNull Activity pendingAppearedActivity,
@NonNull Activity activityInTask, int taskId) {
return newContainer(pendingAppearedActivity, null /* pendingAppearedIntent */,
activityInTask, taskId);
}
TaskFragmentContainer newContainer(@NonNull Intent pendingAppearedIntent,
@NonNull Activity activityInTask, int taskId) {
return newContainer(null /* pendingAppearedActivity */, pendingAppearedIntent,
activityInTask, taskId);
}
/**
* Creates and registers a new organized container with an optional activity that will be
* re-parented to it in a WCT.
*
* @param activity the activity that will be reparented to the TaskFragment.
* @param activityInTask activity in the same Task so that we can get the Task bounds if
* needed.
* @param taskId parent Task of the new TaskFragment.
* @param pendingAppearedActivity the activity that will be reparented to the TaskFragment.
* @param pendingAppearedIntent the Intent that will be started in the TaskFragment.
* @param activityInTask activity in the same Task so that we can get the Task bounds
* if needed.
* @param taskId parent Task of the new TaskFragment.
*/
TaskFragmentContainer newContainer(@Nullable Activity activity,
@NonNull Activity activityInTask, int taskId) {
TaskFragmentContainer newContainer(@Nullable Activity pendingAppearedActivity,
@Nullable Intent pendingAppearedIntent, @NonNull Activity activityInTask, int taskId) {
if (activityInTask == null) {
throw new IllegalArgumentException("activityInTask must not be null,");
}
@@ -835,8 +856,8 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
mTaskContainers.put(taskId, new TaskContainer(taskId));
}
final TaskContainer taskContainer = mTaskContainers.get(taskId);
final TaskFragmentContainer container = new TaskFragmentContainer(activity, taskContainer,
this);
final TaskFragmentContainer container = new TaskFragmentContainer(pendingAppearedActivity,
pendingAppearedIntent, taskContainer, this);
if (!taskContainer.isTaskBoundsInitialized()) {
// Get the initial bounds before the TaskFragment has appeared.
final Rect taskBounds = SplitPresenter.getTaskBoundsFromActivity(activityInTask);

View File

@@ -101,7 +101,7 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
@NonNull
TaskFragmentContainer createNewSplitWithEmptySideContainer(
@NonNull WindowContainerTransaction wct, @NonNull Activity primaryActivity,
@NonNull SplitPairRule rule) {
@NonNull Intent secondaryIntent, @NonNull SplitPairRule rule) {
final Rect parentBounds = getParentContainerBounds(primaryActivity);
final Rect primaryRectBounds = getBoundsForPosition(POSITION_START, parentBounds, rule,
isLtr(primaryActivity, rule));
@@ -111,7 +111,7 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
// Create new empty task fragment
final int taskId = primaryContainer.getTaskId();
final TaskFragmentContainer secondaryContainer = mController.newContainer(
null /* activity */, primaryActivity, taskId);
secondaryIntent, primaryActivity, taskId);
final Rect secondaryRectBounds = getBoundsForPosition(POSITION_END, parentBounds,
rule, isLtr(primaryActivity, rule));
final int windowingMode = mController.getTaskContainer(taskId)
@@ -224,7 +224,7 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
}
final int taskId = primaryContainer.getTaskId();
TaskFragmentContainer secondaryContainer = mController.newContainer(null /* activity */,
final TaskFragmentContainer secondaryContainer = mController.newContainer(activityIntent,
launchingActivity, taskId);
final int windowingMode = mController.getTaskContainer(taskId)
.getWindowingModeForSplitTaskFragment(primaryRectBounds);

View File

@@ -22,6 +22,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.Activity;
import android.app.WindowConfiguration.WindowingMode;
import android.content.Intent;
import android.graphics.Rect;
import android.os.Binder;
import android.os.IBinder;
@@ -64,7 +65,16 @@ class TaskFragmentContainer {
* Activities that are being reparented or being started to this container, but haven't been
* added to {@link #mInfo} yet.
*/
private final ArrayList<Activity> mPendingAppearedActivities = new ArrayList<>();
@VisibleForTesting
final ArrayList<Activity> mPendingAppearedActivities = new ArrayList<>();
/**
* When this container is created for an {@link Intent} to start within, we store that Intent
* until the container becomes non-empty on the server side, so that we can use it to check
* rules associated with this container.
*/
@Nullable
private Intent mPendingAppearedIntent;
/** Containers that are dependent on this one and should be completely destroyed on exit. */
private final List<TaskFragmentContainer> mContainersToFinishOnExit =
@@ -99,15 +109,22 @@ class TaskFragmentContainer {
* Creates a container with an existing activity that will be re-parented to it in a window
* container transaction.
*/
TaskFragmentContainer(@Nullable Activity activity, @NonNull TaskContainer taskContainer,
TaskFragmentContainer(@Nullable Activity pendingAppearedActivity,
@Nullable Intent pendingAppearedIntent, @NonNull TaskContainer taskContainer,
@NonNull SplitController controller) {
if ((pendingAppearedActivity == null && pendingAppearedIntent == null)
|| (pendingAppearedActivity != null && pendingAppearedIntent != null)) {
throw new IllegalArgumentException(
"One and only one of pending activity and intent must be non-null");
}
mController = controller;
mToken = new Binder("TaskFragmentContainer");
mTaskContainer = taskContainer;
taskContainer.mContainers.add(this);
if (activity != null) {
addPendingAppearedActivity(activity);
if (pendingAppearedActivity != null) {
addPendingAppearedActivity(pendingAppearedActivity);
}
mPendingAppearedIntent = pendingAppearedIntent;
}
/**
@@ -118,9 +135,9 @@ class TaskFragmentContainer {
return mToken;
}
/** List of activities that belong to this container and live in this process. */
/** List of non-finishing activities that belong to this container and live in this process. */
@NonNull
List<Activity> collectActivities() {
List<Activity> collectNonFinishingActivities() {
final List<Activity> allActivities = new ArrayList<>();
if (mInfo != null) {
// Add activities reported from the server.
@@ -154,13 +171,14 @@ class TaskFragmentContainer {
return false;
}
return mPendingAppearedActivities.isEmpty()
&& mInfo.getActivities().size() == collectActivities().size();
&& mInfo.getActivities().size() == collectNonFinishingActivities().size();
}
ActivityStack toActivityStack() {
return new ActivityStack(collectActivities(), isEmpty());
return new ActivityStack(collectNonFinishingActivities(), isEmpty());
}
/** Adds the activity that will be reparented to this container. */
void addPendingAppearedActivity(@NonNull Activity pendingAppearedActivity) {
if (hasActivity(pendingAppearedActivity.getActivityToken())) {
return;
@@ -174,6 +192,11 @@ class TaskFragmentContainer {
mPendingAppearedActivities.remove(pendingAppearedActivity);
}
@Nullable
Intent getPendingAppearedIntent() {
return mPendingAppearedIntent;
}
boolean hasActivity(@NonNull IBinder token) {
if (mInfo != null && mInfo.getActivities().contains(token)) {
return true;
@@ -219,7 +242,12 @@ class TaskFragmentContainer {
}
mInfo = info;
if (mInfo == null || mPendingAppearedActivities.isEmpty()) {
if (mInfo == null || mInfo.isEmpty()) {
return;
}
// Only track the pending Intent when the container is empty.
mPendingAppearedIntent = null;
if (mPendingAppearedActivities.isEmpty()) {
return;
}
// Cleanup activities that were being re-parented
@@ -234,20 +262,13 @@ class TaskFragmentContainer {
@Nullable
Activity getTopNonFinishingActivity() {
List<Activity> activities = collectActivities();
if (activities.isEmpty()) {
return null;
}
int i = activities.size() - 1;
while (i >= 0 && activities.get(i).isFinishing()) {
i--;
}
return i >= 0 ? activities.get(i) : null;
final List<Activity> activities = collectNonFinishingActivities();
return activities.isEmpty() ? null : activities.get(activities.size() - 1);
}
@Nullable
Activity getBottomMostActivity() {
final List<Activity> activities = collectActivities();
final List<Activity> activities = collectNonFinishingActivities();
return activities.isEmpty() ? null : activities.get(0);
}
@@ -320,8 +341,11 @@ class TaskFragmentContainer {
private void finishActivities(boolean shouldFinishDependent, @NonNull SplitPresenter presenter,
@NonNull WindowContainerTransaction wct, @NonNull SplitController controller) {
// Finish own activities
for (Activity activity : collectActivities()) {
if (!activity.isFinishing()) {
for (Activity activity : collectNonFinishingActivities()) {
if (!activity.isFinishing()
// In case we have requested to reparent the activity to another container (as
// pendingAppeared), we don't want to finish it with this container.
&& mController.getContainerWithActivity(activity) == this) {
activity.finish();
}
}

View File

@@ -28,6 +28,7 @@ import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import android.content.Intent;
import android.content.res.Configuration;
import android.graphics.Point;
import android.os.Handler;
@@ -115,7 +116,7 @@ public class JetpackTaskFragmentOrganizerTest {
public void testExpandTaskFragment() {
final TaskContainer taskContainer = new TaskContainer(TASK_ID);
final TaskFragmentContainer container = new TaskFragmentContainer(null /* activity */,
taskContainer, mSplitController);
new Intent(), taskContainer, mSplitController);
final TaskFragmentInfo info = createMockInfo(container);
mOrganizer.mFragmentInfos.put(container.getTaskFragmentToken(), info);
container.setInfo(info);

View File

@@ -123,7 +123,7 @@ public class SplitControllerTest {
final TaskContainer taskContainer = new TaskContainer(TASK_ID);
// tf1 has no running activity so is not active.
final TaskFragmentContainer tf1 = new TaskFragmentContainer(null /* activity */,
taskContainer, mSplitController);
new Intent(), taskContainer, mSplitController);
// tf2 has running activity so is active.
final TaskFragmentContainer tf2 = mock(TaskFragmentContainer.class);
doReturn(1).when(tf2).getRunningActivityCount();
@@ -205,7 +205,8 @@ public class SplitControllerTest {
assertThrows(IllegalArgumentException.class, () ->
mSplitController.newContainer(mActivity, null /* launchingActivity */, TASK_ID));
final TaskFragmentContainer tf = mSplitController.newContainer(null, mActivity, TASK_ID);
final TaskFragmentContainer tf = mSplitController.newContainer(mActivity, mActivity,
TASK_ID);
final TaskContainer taskContainer = mSplitController.getTaskContainer(TASK_ID);
assertNotNull(tf);
@@ -307,7 +308,7 @@ public class SplitControllerTest {
@Test
public void testOnActivityReparentToTask_diffProcess() {
// Create an empty TaskFragment to initialize for the Task.
mSplitController.newContainer(null, mActivity, TASK_ID);
mSplitController.newContainer(new Intent(), mActivity, TASK_ID);
final IBinder activityToken = new Binder();
final Intent intent = new Intent();
@@ -417,7 +418,7 @@ public class SplitControllerTest {
verify(mSplitPresenter, never()).applyTransaction(any());
mSplitController.newContainer(null /* activity */, mActivity, TASK_ID);
mSplitController.newContainer(new Intent(), mActivity, TASK_ID);
mSplitController.placeActivityInTopContainer(mActivity);
verify(mSplitPresenter).applyTransaction(any());
@@ -436,7 +437,7 @@ public class SplitControllerTest {
false /* isOnReparent */);
assertFalse(result);
verify(mSplitController, never()).newContainer(any(), any(), anyInt());
verify(mSplitController, never()).newContainer(any(), any(), any(), anyInt());
}
@Test
@@ -577,7 +578,7 @@ public class SplitControllerTest {
final TaskFragmentContainer primaryContainer = mSplitController.newContainer(mActivity,
TASK_ID);
final TaskFragmentContainer secondaryContainer = mSplitController.newContainer(
null /* activity */, mActivity, TASK_ID);
secondaryIntent, mActivity, TASK_ID);
mSplitController.registerSplit(
mTransaction,
primaryContainer,
@@ -589,10 +590,35 @@ public class SplitControllerTest {
false /* isOnReparent */);
assertTrue(result);
verify(mSplitController, never()).newContainer(any(), any(), anyInt());
verify(mSplitController, never()).newContainer(any(), any(), any(), anyInt());
verify(mSplitController, never()).registerSplit(any(), any(), any(), any(), any());
}
@Test
public void testResolveActivityToContainer_splitRule_inPrimarySplitWithNoRuleMatched() {
final Intent secondaryIntent = new Intent();
setupSplitRule(mActivity, secondaryIntent);
final SplitPairRule splitRule = (SplitPairRule) mSplitController.getSplitRules().get(0);
// The new launched activity is in primary split, but there is no rule for it to split with
// the secondary, so return false.
final TaskFragmentContainer primaryContainer = mSplitController.newContainer(mActivity,
TASK_ID);
final TaskFragmentContainer secondaryContainer = mSplitController.newContainer(
secondaryIntent, mActivity, TASK_ID);
mSplitController.registerSplit(
mTransaction,
primaryContainer,
mActivity,
secondaryContainer,
splitRule);
final Activity launchedActivity = createMockActivity();
primaryContainer.addPendingAppearedActivity(launchedActivity);
assertFalse(mSplitController.resolveActivityToContainer(launchedActivity,
false /* isOnReparent */));
}
@Test
public void testResolveActivityToContainer_splitRule_inSecondarySplitWithRuleMatched() {
final Activity primaryActivity = createMockActivity();
@@ -605,7 +631,7 @@ public class SplitControllerTest {
false /* isOnReparent */);
assertTrue(result);
verify(mSplitController, never()).newContainer(any(), any(), anyInt());
verify(mSplitController, never()).newContainer(any(), any(), any(), anyInt());
verify(mSplitController, never()).registerSplit(any(), any(), any(), any(), any());
}

View File

@@ -30,6 +30,7 @@ import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import android.app.Activity;
import android.content.Intent;
import android.graphics.Rect;
import android.platform.test.annotations.Presubmit;
@@ -142,7 +143,7 @@ public class TaskContainerTest {
assertTrue(taskContainer.isEmpty());
final TaskFragmentContainer tf = new TaskFragmentContainer(null /* activity */,
taskContainer, mController);
new Intent(), taskContainer, mController);
assertFalse(taskContainer.isEmpty());
@@ -158,11 +159,11 @@ public class TaskContainerTest {
assertNull(taskContainer.getTopTaskFragmentContainer());
final TaskFragmentContainer tf0 = new TaskFragmentContainer(null /* activity */,
taskContainer, mController);
new Intent(), taskContainer, mController);
assertEquals(tf0, taskContainer.getTopTaskFragmentContainer());
final TaskFragmentContainer tf1 = new TaskFragmentContainer(null /* activity */,
taskContainer, mController);
new Intent(), taskContainer, mController);
assertEquals(tf1, taskContainer.getTopTaskFragmentContainer());
}

View File

@@ -22,6 +22,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
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.Mockito.clearInvocations;
@@ -29,12 +30,17 @@ import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import android.annotation.NonNull;
import android.app.Activity;
import android.content.Intent;
import android.content.res.Configuration;
import android.graphics.Point;
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.WindowContainerToken;
import android.window.WindowContainerTransaction;
import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -49,6 +55,7 @@ import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
@@ -72,19 +79,35 @@ public class TaskFragmentContainerTest {
@Mock
private Handler mHandler;
private Activity mActivity;
private Intent mIntent;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
doReturn(mHandler).when(mController).getHandler();
mActivity = createMockActivity();
mIntent = new Intent();
}
@Test
public void testNewContainer() {
final TaskContainer taskContainer = new TaskContainer(TASK_ID);
// One of the activity and the intent must be non-null
assertThrows(IllegalArgumentException.class,
() -> new TaskFragmentContainer(null, null, taskContainer, mController));
// One of the activity and the intent must be null.
assertThrows(IllegalArgumentException.class,
() -> new TaskFragmentContainer(mActivity, mIntent, taskContainer, mController));
}
@Test
public void testFinish() {
final TaskContainer taskContainer = new TaskContainer(TASK_ID);
final TaskFragmentContainer container = new TaskFragmentContainer(mActivity, taskContainer,
mController);
final TaskFragmentContainer container = new TaskFragmentContainer(mActivity,
null /* pendingAppearedIntent */, taskContainer, mController);
doReturn(container).when(mController).getContainerWithActivity(mActivity);
final WindowContainerTransaction wct = new WindowContainerTransaction();
// Only remove the activity, but not clear the reference until appeared.
@@ -112,11 +135,60 @@ public class TaskFragmentContainerTest {
verify(mController).removeContainer(container);
}
@Test
public void testFinish_notFinishActivityThatIsReparenting() {
final TaskContainer taskContainer = new TaskContainer(TASK_ID);
final TaskFragmentContainer container0 = new TaskFragmentContainer(mActivity,
null /* pendingAppearedIntent */, taskContainer, mController);
final TaskFragmentInfo info = createMockTaskFragmentInfo(container0, mActivity);
container0.setInfo(info);
// Request to reparent the activity to a new TaskFragment.
final TaskFragmentContainer container1 = new TaskFragmentContainer(mActivity,
null /* pendingAppearedIntent */, taskContainer, mController);
doReturn(container1).when(mController).getContainerWithActivity(mActivity);
final WindowContainerTransaction wct = new WindowContainerTransaction();
// The activity is requested to be reparented, so don't finish it.
container0.finish(true /* shouldFinishDependent */, mPresenter, wct, mController);
verify(mActivity, never()).finish();
verify(mPresenter).deleteTaskFragment(wct, container0.getTaskFragmentToken());
verify(mController).removeContainer(container0);
}
@Test
public void testSetInfo() {
final TaskContainer taskContainer = new TaskContainer(TASK_ID);
// Pending activity should be cleared when it has appeared on server side.
final TaskFragmentContainer pendingActivityContainer = new TaskFragmentContainer(mActivity,
null /* pendingAppearedIntent */, taskContainer, mController);
assertTrue(pendingActivityContainer.mPendingAppearedActivities.contains(mActivity));
final TaskFragmentInfo info0 = createMockTaskFragmentInfo(pendingActivityContainer,
mActivity);
pendingActivityContainer.setInfo(info0);
assertTrue(pendingActivityContainer.mPendingAppearedActivities.isEmpty());
// Pending intent should be cleared when the container becomes non-empty.
final TaskFragmentContainer pendingIntentContainer = new TaskFragmentContainer(
null /* pendingAppearedActivity */, mIntent, taskContainer, mController);
assertEquals(mIntent, pendingIntentContainer.getPendingAppearedIntent());
final TaskFragmentInfo info1 = createMockTaskFragmentInfo(pendingIntentContainer,
mActivity);
pendingIntentContainer.setInfo(info1);
assertNull(pendingIntentContainer.getPendingAppearedIntent());
}
@Test
public void testIsWaitingActivityAppear() {
final TaskContainer taskContainer = new TaskContainer(TASK_ID);
final TaskFragmentContainer container = new TaskFragmentContainer(null /* activity */,
taskContainer, mController);
mIntent, taskContainer, mController);
assertTrue(container.isWaitingActivityAppear());
@@ -137,7 +209,7 @@ public class TaskFragmentContainerTest {
public void testAppearEmptyTimeout() {
final TaskContainer taskContainer = new TaskContainer(TASK_ID);
final TaskFragmentContainer container = new TaskFragmentContainer(null /* activity */,
taskContainer, mController);
mIntent, taskContainer, mController);
assertNull(container.mAppearEmptyTimeout);
@@ -173,16 +245,16 @@ public class TaskFragmentContainerTest {
}
@Test
public void testCollectActivities() {
public void testCollectNonFinishingActivities() {
final TaskContainer taskContainer = new TaskContainer(TASK_ID);
final TaskFragmentContainer container = new TaskFragmentContainer(null /* activity */,
taskContainer, mController);
List<Activity> activities = container.collectActivities();
mIntent, taskContainer, mController);
List<Activity> activities = container.collectNonFinishingActivities();
assertTrue(activities.isEmpty());
container.addPendingAppearedActivity(mActivity);
activities = container.collectActivities();
activities = container.collectNonFinishingActivities();
assertEquals(1, activities.size());
@@ -192,7 +264,7 @@ public class TaskFragmentContainerTest {
activity1.getActivityToken());
doReturn(runningActivities).when(mInfo).getActivities();
container.setInfo(mInfo);
activities = container.collectActivities();
activities = container.collectNonFinishingActivities();
assertEquals(3, activities.size());
assertEquals(activity0, activities.get(0));
@@ -204,21 +276,21 @@ public class TaskFragmentContainerTest {
public void testAddPendingActivity() {
final TaskContainer taskContainer = new TaskContainer(TASK_ID);
final TaskFragmentContainer container = new TaskFragmentContainer(null /* activity */,
taskContainer, mController);
mIntent, taskContainer, mController);
container.addPendingAppearedActivity(mActivity);
assertEquals(1, container.collectActivities().size());
assertEquals(1, container.collectNonFinishingActivities().size());
container.addPendingAppearedActivity(mActivity);
assertEquals(1, container.collectActivities().size());
assertEquals(1, container.collectNonFinishingActivities().size());
}
@Test
public void testGetBottomMostActivity() {
final TaskContainer taskContainer = new TaskContainer(TASK_ID);
final TaskFragmentContainer container = new TaskFragmentContainer(null /* activity */,
taskContainer, mController);
mIntent, taskContainer, mController);
container.addPendingAppearedActivity(mActivity);
assertEquals(mActivity, container.getBottomMostActivity());
@@ -239,4 +311,18 @@ public class TaskFragmentContainerTest {
doReturn(activity).when(mController).getActivity(activityToken);
return activity;
}
/** Creates a mock TaskFragmentInfo for the given TaskFragment. */
private TaskFragmentInfo createMockTaskFragmentInfo(@NonNull TaskFragmentContainer container,
@NonNull Activity activity) {
return new TaskFragmentInfo(container.getTaskFragmentToken(),
mock(WindowContainerToken.class),
new Configuration(),
1,
true /* isVisible */,
Collections.singletonList(activity.getActivityToken()),
new Point(),
false /* isTaskClearedForReuse */,
false /* isTaskFragmentClearedForPip */);
}
}