Merge "Add pendingAppearedIntent to TaskFragmentContainer" into tm-dev

This commit is contained in:
TreeHugger Robot
2022-05-26 19:06:54 +00:00
committed by Android (Google) Code Review
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()) { if (container == splitContainer.getPrimaryContainer()) {
// The new launched can be in the primary container when it is starting a new activity // 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 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(); final Activity secondaryActivity = secondaryContainer.getTopNonFinishingActivity();
return secondaryActivity == null return secondaryActivity != null
|| getSplitRule(launchedActivity, secondaryActivity) != null; && getSplitRule(launchedActivity, secondaryActivity) != null;
} }
// Check if the new launched activity is a placeholder. // Check if the new launched activity is a placeholder.
@@ -573,7 +580,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
Activity activityBelow = null; Activity activityBelow = null;
final TaskFragmentContainer container = getContainerWithActivity(activity); final TaskFragmentContainer container = getContainerWithActivity(activity);
if (container != null) { if (container != null) {
final List<Activity> containerActivities = container.collectActivities(); final List<Activity> containerActivities = container.collectNonFinishingActivities();
final int index = containerActivities.indexOf(activity); final int index = containerActivities.indexOf(activity);
if (index > 0) { if (index > 0) {
activityBelow = containerActivities.get(index - 1); activityBelow = containerActivities.get(index - 1);
@@ -691,7 +698,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
// 1. Whether the new activity intent should always expand. // 1. Whether the new activity intent should always expand.
if (shouldExpand(null /* activity */, intent)) { 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. // 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 @Nullable
private TaskFragmentContainer createEmptyExpandedContainer( private TaskFragmentContainer createEmptyExpandedContainer(
@NonNull WindowContainerTransaction wct, int taskId, @NonNull WindowContainerTransaction wct, @NonNull Intent intent, int taskId,
@Nullable Activity launchingActivity) { @Nullable Activity launchingActivity) {
// We need an activity in the organizer process in the same Task to use as the owner // 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. // 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. // Can't find any activity in the Task that we can use as the owner activity.
return null; return null;
} }
final TaskFragmentContainer expandedContainer = newContainer(null /* activity */, final TaskFragmentContainer expandedContainer = newContainer(intent, activityInTask,
activityInTask, taskId); taskId);
mPresenter.createTaskFragment(wct, expandedContainer.getTaskFragmentToken(), mPresenter.createTaskFragment(wct, expandedContainer.getTaskFragmentToken(),
activityInTask.getActivityToken(), new Rect(), WINDOWING_MODE_UNDEFINED); activityInTask.getActivityToken(), new Rect(), WINDOWING_MODE_UNDEFINED);
return expandedContainer; return expandedContainer;
@@ -789,7 +796,8 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
return splitContainer.getSecondaryContainer(); return splitContainer.getSecondaryContainer();
} }
// Create a new TaskFragment to split with the primary activity for the new activity. // 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; return null;
} }
TaskFragmentContainer newContainer(@NonNull Activity activity, int taskId) { TaskFragmentContainer newContainer(@NonNull Activity pendingAppearedActivity, int taskId) {
return newContainer(activity, activity, 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 * Creates and registers a new organized container with an optional activity that will be
* re-parented to it in a WCT. * re-parented to it in a WCT.
* *
* @param activity the activity that will be reparented to the TaskFragment. * @param pendingAppearedActivity 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 * @param pendingAppearedIntent the Intent that will be started in the TaskFragment.
* needed. * @param activityInTask activity in the same Task so that we can get the Task bounds
* @param taskId parent Task of the new TaskFragment. * if needed.
* @param taskId parent Task of the new TaskFragment.
*/ */
TaskFragmentContainer newContainer(@Nullable Activity activity, TaskFragmentContainer newContainer(@Nullable Activity pendingAppearedActivity,
@NonNull Activity activityInTask, int taskId) { @Nullable Intent pendingAppearedIntent, @NonNull Activity activityInTask, int taskId) {
if (activityInTask == null) { if (activityInTask == null) {
throw new IllegalArgumentException("activityInTask must not be 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)); mTaskContainers.put(taskId, new TaskContainer(taskId));
} }
final TaskContainer taskContainer = mTaskContainers.get(taskId); final TaskContainer taskContainer = mTaskContainers.get(taskId);
final TaskFragmentContainer container = new TaskFragmentContainer(activity, taskContainer, final TaskFragmentContainer container = new TaskFragmentContainer(pendingAppearedActivity,
this); pendingAppearedIntent, taskContainer, this);
if (!taskContainer.isTaskBoundsInitialized()) { if (!taskContainer.isTaskBoundsInitialized()) {
// Get the initial bounds before the TaskFragment has appeared. // Get the initial bounds before the TaskFragment has appeared.
final Rect taskBounds = SplitPresenter.getTaskBoundsFromActivity(activityInTask); final Rect taskBounds = SplitPresenter.getTaskBoundsFromActivity(activityInTask);

View File

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

View File

@@ -22,6 +22,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable; import android.annotation.Nullable;
import android.app.Activity; import android.app.Activity;
import android.app.WindowConfiguration.WindowingMode; import android.app.WindowConfiguration.WindowingMode;
import android.content.Intent;
import android.graphics.Rect; import android.graphics.Rect;
import android.os.Binder; import android.os.Binder;
import android.os.IBinder; 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 * Activities that are being reparented or being started to this container, but haven't been
* added to {@link #mInfo} yet. * 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. */ /** Containers that are dependent on this one and should be completely destroyed on exit. */
private final List<TaskFragmentContainer> mContainersToFinishOnExit = 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 * Creates a container with an existing activity that will be re-parented to it in a window
* container transaction. * container transaction.
*/ */
TaskFragmentContainer(@Nullable Activity activity, @NonNull TaskContainer taskContainer, TaskFragmentContainer(@Nullable Activity pendingAppearedActivity,
@Nullable Intent pendingAppearedIntent, @NonNull TaskContainer taskContainer,
@NonNull SplitController controller) { @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; mController = controller;
mToken = new Binder("TaskFragmentContainer"); mToken = new Binder("TaskFragmentContainer");
mTaskContainer = taskContainer; mTaskContainer = taskContainer;
taskContainer.mContainers.add(this); taskContainer.mContainers.add(this);
if (activity != null) { if (pendingAppearedActivity != null) {
addPendingAppearedActivity(activity); addPendingAppearedActivity(pendingAppearedActivity);
} }
mPendingAppearedIntent = pendingAppearedIntent;
} }
/** /**
@@ -118,9 +135,9 @@ class TaskFragmentContainer {
return mToken; 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 @NonNull
List<Activity> collectActivities() { List<Activity> collectNonFinishingActivities() {
final List<Activity> allActivities = new ArrayList<>(); final List<Activity> allActivities = new ArrayList<>();
if (mInfo != null) { if (mInfo != null) {
// Add activities reported from the server. // Add activities reported from the server.
@@ -154,13 +171,14 @@ class TaskFragmentContainer {
return false; return false;
} }
return mPendingAppearedActivities.isEmpty() return mPendingAppearedActivities.isEmpty()
&& mInfo.getActivities().size() == collectActivities().size(); && mInfo.getActivities().size() == collectNonFinishingActivities().size();
} }
ActivityStack toActivityStack() { 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) { void addPendingAppearedActivity(@NonNull Activity pendingAppearedActivity) {
if (hasActivity(pendingAppearedActivity.getActivityToken())) { if (hasActivity(pendingAppearedActivity.getActivityToken())) {
return; return;
@@ -174,6 +192,11 @@ class TaskFragmentContainer {
mPendingAppearedActivities.remove(pendingAppearedActivity); mPendingAppearedActivities.remove(pendingAppearedActivity);
} }
@Nullable
Intent getPendingAppearedIntent() {
return mPendingAppearedIntent;
}
boolean hasActivity(@NonNull IBinder token) { boolean hasActivity(@NonNull IBinder token) {
if (mInfo != null && mInfo.getActivities().contains(token)) { if (mInfo != null && mInfo.getActivities().contains(token)) {
return true; return true;
@@ -219,7 +242,12 @@ class TaskFragmentContainer {
} }
mInfo = info; 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; return;
} }
// Cleanup activities that were being re-parented // Cleanup activities that were being re-parented
@@ -234,20 +262,13 @@ class TaskFragmentContainer {
@Nullable @Nullable
Activity getTopNonFinishingActivity() { Activity getTopNonFinishingActivity() {
List<Activity> activities = collectActivities(); final List<Activity> activities = collectNonFinishingActivities();
if (activities.isEmpty()) { return activities.isEmpty() ? null : activities.get(activities.size() - 1);
return null;
}
int i = activities.size() - 1;
while (i >= 0 && activities.get(i).isFinishing()) {
i--;
}
return i >= 0 ? activities.get(i) : null;
} }
@Nullable @Nullable
Activity getBottomMostActivity() { Activity getBottomMostActivity() {
final List<Activity> activities = collectActivities(); final List<Activity> activities = collectNonFinishingActivities();
return activities.isEmpty() ? null : activities.get(0); return activities.isEmpty() ? null : activities.get(0);
} }
@@ -320,8 +341,11 @@ class TaskFragmentContainer {
private void finishActivities(boolean shouldFinishDependent, @NonNull SplitPresenter presenter, private void finishActivities(boolean shouldFinishDependent, @NonNull SplitPresenter presenter,
@NonNull WindowContainerTransaction wct, @NonNull SplitController controller) { @NonNull WindowContainerTransaction wct, @NonNull SplitController controller) {
// Finish own activities // Finish own activities
for (Activity activity : collectActivities()) { for (Activity activity : collectNonFinishingActivities()) {
if (!activity.isFinishing()) { 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(); 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.mock;
import static org.mockito.Mockito.never; import static org.mockito.Mockito.never;
import android.content.Intent;
import android.content.res.Configuration; import android.content.res.Configuration;
import android.graphics.Point; import android.graphics.Point;
import android.os.Handler; import android.os.Handler;
@@ -115,7 +116,7 @@ public class JetpackTaskFragmentOrganizerTest {
public void testExpandTaskFragment() { public void testExpandTaskFragment() {
final TaskContainer taskContainer = new TaskContainer(TASK_ID); final TaskContainer taskContainer = new TaskContainer(TASK_ID);
final TaskFragmentContainer container = new TaskFragmentContainer(null /* activity */, final TaskFragmentContainer container = new TaskFragmentContainer(null /* activity */,
taskContainer, mSplitController); new Intent(), taskContainer, mSplitController);
final TaskFragmentInfo info = createMockInfo(container); final TaskFragmentInfo info = createMockInfo(container);
mOrganizer.mFragmentInfos.put(container.getTaskFragmentToken(), info); mOrganizer.mFragmentInfos.put(container.getTaskFragmentToken(), info);
container.setInfo(info); container.setInfo(info);

View File

@@ -123,7 +123,7 @@ public class SplitControllerTest {
final TaskContainer taskContainer = new TaskContainer(TASK_ID); final TaskContainer taskContainer = new TaskContainer(TASK_ID);
// tf1 has no running activity so is not active. // tf1 has no running activity so is not active.
final TaskFragmentContainer tf1 = new TaskFragmentContainer(null /* activity */, final TaskFragmentContainer tf1 = new TaskFragmentContainer(null /* activity */,
taskContainer, mSplitController); new Intent(), taskContainer, mSplitController);
// tf2 has running activity so is active. // tf2 has running activity so is active.
final TaskFragmentContainer tf2 = mock(TaskFragmentContainer.class); final TaskFragmentContainer tf2 = mock(TaskFragmentContainer.class);
doReturn(1).when(tf2).getRunningActivityCount(); doReturn(1).when(tf2).getRunningActivityCount();
@@ -205,7 +205,8 @@ public class SplitControllerTest {
assertThrows(IllegalArgumentException.class, () -> assertThrows(IllegalArgumentException.class, () ->
mSplitController.newContainer(mActivity, null /* launchingActivity */, TASK_ID)); 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); final TaskContainer taskContainer = mSplitController.getTaskContainer(TASK_ID);
assertNotNull(tf); assertNotNull(tf);
@@ -307,7 +308,7 @@ public class SplitControllerTest {
@Test @Test
public void testOnActivityReparentToTask_diffProcess() { public void testOnActivityReparentToTask_diffProcess() {
// Create an empty TaskFragment to initialize for the Task. // 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 IBinder activityToken = new Binder();
final Intent intent = new Intent(); final Intent intent = new Intent();
@@ -417,7 +418,7 @@ public class SplitControllerTest {
verify(mSplitPresenter, never()).applyTransaction(any()); verify(mSplitPresenter, never()).applyTransaction(any());
mSplitController.newContainer(null /* activity */, mActivity, TASK_ID); mSplitController.newContainer(new Intent(), mActivity, TASK_ID);
mSplitController.placeActivityInTopContainer(mActivity); mSplitController.placeActivityInTopContainer(mActivity);
verify(mSplitPresenter).applyTransaction(any()); verify(mSplitPresenter).applyTransaction(any());
@@ -436,7 +437,7 @@ public class SplitControllerTest {
false /* isOnReparent */); false /* isOnReparent */);
assertFalse(result); assertFalse(result);
verify(mSplitController, never()).newContainer(any(), any(), anyInt()); verify(mSplitController, never()).newContainer(any(), any(), any(), anyInt());
} }
@Test @Test
@@ -577,7 +578,7 @@ public class SplitControllerTest {
final TaskFragmentContainer primaryContainer = mSplitController.newContainer(mActivity, final TaskFragmentContainer primaryContainer = mSplitController.newContainer(mActivity,
TASK_ID); TASK_ID);
final TaskFragmentContainer secondaryContainer = mSplitController.newContainer( final TaskFragmentContainer secondaryContainer = mSplitController.newContainer(
null /* activity */, mActivity, TASK_ID); secondaryIntent, mActivity, TASK_ID);
mSplitController.registerSplit( mSplitController.registerSplit(
mTransaction, mTransaction,
primaryContainer, primaryContainer,
@@ -589,10 +590,35 @@ public class SplitControllerTest {
false /* isOnReparent */); false /* isOnReparent */);
assertTrue(result); 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()); 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 @Test
public void testResolveActivityToContainer_splitRule_inSecondarySplitWithRuleMatched() { public void testResolveActivityToContainer_splitRule_inSecondarySplitWithRuleMatched() {
final Activity primaryActivity = createMockActivity(); final Activity primaryActivity = createMockActivity();
@@ -605,7 +631,7 @@ public class SplitControllerTest {
false /* isOnReparent */); false /* isOnReparent */);
assertTrue(result); 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()); 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 static org.mockito.Mockito.mock;
import android.app.Activity; import android.app.Activity;
import android.content.Intent;
import android.graphics.Rect; import android.graphics.Rect;
import android.platform.test.annotations.Presubmit; import android.platform.test.annotations.Presubmit;
@@ -142,7 +143,7 @@ public class TaskContainerTest {
assertTrue(taskContainer.isEmpty()); assertTrue(taskContainer.isEmpty());
final TaskFragmentContainer tf = new TaskFragmentContainer(null /* activity */, final TaskFragmentContainer tf = new TaskFragmentContainer(null /* activity */,
taskContainer, mController); new Intent(), taskContainer, mController);
assertFalse(taskContainer.isEmpty()); assertFalse(taskContainer.isEmpty());
@@ -158,11 +159,11 @@ public class TaskContainerTest {
assertNull(taskContainer.getTopTaskFragmentContainer()); assertNull(taskContainer.getTopTaskFragmentContainer());
final TaskFragmentContainer tf0 = new TaskFragmentContainer(null /* activity */, final TaskFragmentContainer tf0 = new TaskFragmentContainer(null /* activity */,
taskContainer, mController); new Intent(), taskContainer, mController);
assertEquals(tf0, taskContainer.getTopTaskFragmentContainer()); assertEquals(tf0, taskContainer.getTopTaskFragmentContainer());
final TaskFragmentContainer tf1 = new TaskFragmentContainer(null /* activity */, final TaskFragmentContainer tf1 = new TaskFragmentContainer(null /* activity */,
taskContainer, mController); new Intent(), taskContainer, mController);
assertEquals(tf1, taskContainer.getTopTaskFragmentContainer()); 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.assertFalse;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull; import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.clearInvocations; 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.mock;
import static org.mockito.Mockito.never; import static org.mockito.Mockito.never;
import android.annotation.NonNull;
import android.app.Activity; import android.app.Activity;
import android.content.Intent;
import android.content.res.Configuration;
import android.graphics.Point;
import android.os.Binder; import android.os.Binder;
import android.os.Handler; import android.os.Handler;
import android.os.IBinder; import android.os.IBinder;
import android.platform.test.annotations.Presubmit; import android.platform.test.annotations.Presubmit;
import android.window.TaskFragmentInfo; import android.window.TaskFragmentInfo;
import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction; import android.window.WindowContainerTransaction;
import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -49,6 +55,7 @@ import org.mockito.Mock;
import org.mockito.MockitoAnnotations; import org.mockito.MockitoAnnotations;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.List; import java.util.List;
/** /**
@@ -72,19 +79,35 @@ public class TaskFragmentContainerTest {
@Mock @Mock
private Handler mHandler; private Handler mHandler;
private Activity mActivity; private Activity mActivity;
private Intent mIntent;
@Before @Before
public void setup() { public void setup() {
MockitoAnnotations.initMocks(this); MockitoAnnotations.initMocks(this);
doReturn(mHandler).when(mController).getHandler(); doReturn(mHandler).when(mController).getHandler();
mActivity = createMockActivity(); 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 @Test
public void testFinish() { public void testFinish() {
final TaskContainer taskContainer = new TaskContainer(TASK_ID); final TaskContainer taskContainer = new TaskContainer(TASK_ID);
final TaskFragmentContainer container = new TaskFragmentContainer(mActivity, taskContainer, final TaskFragmentContainer container = new TaskFragmentContainer(mActivity,
mController); null /* pendingAppearedIntent */, taskContainer, mController);
doReturn(container).when(mController).getContainerWithActivity(mActivity);
final WindowContainerTransaction wct = new WindowContainerTransaction(); final WindowContainerTransaction wct = new WindowContainerTransaction();
// Only remove the activity, but not clear the reference until appeared. // Only remove the activity, but not clear the reference until appeared.
@@ -112,11 +135,60 @@ public class TaskFragmentContainerTest {
verify(mController).removeContainer(container); 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 @Test
public void testIsWaitingActivityAppear() { public void testIsWaitingActivityAppear() {
final TaskContainer taskContainer = new TaskContainer(TASK_ID); final TaskContainer taskContainer = new TaskContainer(TASK_ID);
final TaskFragmentContainer container = new TaskFragmentContainer(null /* activity */, final TaskFragmentContainer container = new TaskFragmentContainer(null /* activity */,
taskContainer, mController); mIntent, taskContainer, mController);
assertTrue(container.isWaitingActivityAppear()); assertTrue(container.isWaitingActivityAppear());
@@ -137,7 +209,7 @@ public class TaskFragmentContainerTest {
public void testAppearEmptyTimeout() { public void testAppearEmptyTimeout() {
final TaskContainer taskContainer = new TaskContainer(TASK_ID); final TaskContainer taskContainer = new TaskContainer(TASK_ID);
final TaskFragmentContainer container = new TaskFragmentContainer(null /* activity */, final TaskFragmentContainer container = new TaskFragmentContainer(null /* activity */,
taskContainer, mController); mIntent, taskContainer, mController);
assertNull(container.mAppearEmptyTimeout); assertNull(container.mAppearEmptyTimeout);
@@ -173,16 +245,16 @@ public class TaskFragmentContainerTest {
} }
@Test @Test
public void testCollectActivities() { public void testCollectNonFinishingActivities() {
final TaskContainer taskContainer = new TaskContainer(TASK_ID); final TaskContainer taskContainer = new TaskContainer(TASK_ID);
final TaskFragmentContainer container = new TaskFragmentContainer(null /* activity */, final TaskFragmentContainer container = new TaskFragmentContainer(null /* activity */,
taskContainer, mController); mIntent, taskContainer, mController);
List<Activity> activities = container.collectActivities(); List<Activity> activities = container.collectNonFinishingActivities();
assertTrue(activities.isEmpty()); assertTrue(activities.isEmpty());
container.addPendingAppearedActivity(mActivity); container.addPendingAppearedActivity(mActivity);
activities = container.collectActivities(); activities = container.collectNonFinishingActivities();
assertEquals(1, activities.size()); assertEquals(1, activities.size());
@@ -192,7 +264,7 @@ public class TaskFragmentContainerTest {
activity1.getActivityToken()); activity1.getActivityToken());
doReturn(runningActivities).when(mInfo).getActivities(); doReturn(runningActivities).when(mInfo).getActivities();
container.setInfo(mInfo); container.setInfo(mInfo);
activities = container.collectActivities(); activities = container.collectNonFinishingActivities();
assertEquals(3, activities.size()); assertEquals(3, activities.size());
assertEquals(activity0, activities.get(0)); assertEquals(activity0, activities.get(0));
@@ -204,21 +276,21 @@ public class TaskFragmentContainerTest {
public void testAddPendingActivity() { public void testAddPendingActivity() {
final TaskContainer taskContainer = new TaskContainer(TASK_ID); final TaskContainer taskContainer = new TaskContainer(TASK_ID);
final TaskFragmentContainer container = new TaskFragmentContainer(null /* activity */, final TaskFragmentContainer container = new TaskFragmentContainer(null /* activity */,
taskContainer, mController); mIntent, taskContainer, mController);
container.addPendingAppearedActivity(mActivity); container.addPendingAppearedActivity(mActivity);
assertEquals(1, container.collectActivities().size()); assertEquals(1, container.collectNonFinishingActivities().size());
container.addPendingAppearedActivity(mActivity); container.addPendingAppearedActivity(mActivity);
assertEquals(1, container.collectActivities().size()); assertEquals(1, container.collectNonFinishingActivities().size());
} }
@Test @Test
public void testGetBottomMostActivity() { public void testGetBottomMostActivity() {
final TaskContainer taskContainer = new TaskContainer(TASK_ID); final TaskContainer taskContainer = new TaskContainer(TASK_ID);
final TaskFragmentContainer container = new TaskFragmentContainer(null /* activity */, final TaskFragmentContainer container = new TaskFragmentContainer(null /* activity */,
taskContainer, mController); mIntent, taskContainer, mController);
container.addPendingAppearedActivity(mActivity); container.addPendingAppearedActivity(mActivity);
assertEquals(mActivity, container.getBottomMostActivity()); assertEquals(mActivity, container.getBottomMostActivity());
@@ -239,4 +311,18 @@ public class TaskFragmentContainerTest {
doReturn(activity).when(mController).getActivity(activityToken); doReturn(activity).when(mController).getActivity(activityToken);
return activity; 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 */);
}
} }