diff options
| author | 2022-05-26 19:06:54 +0000 | |
|---|---|---|
| committer | 2022-05-26 19:06:54 +0000 | |
| commit | 3f75b0485accae00cb8b86b019c70502a623363a (patch) | |
| tree | 0b4f84eb90b73ed1384ef233dabe2024d08680c6 | |
| parent | e00b1837906d1796e2e7310945bed2072e053dc6 (diff) | |
| parent | f7f106b4372b9b1a2254ae878aea91aaf55c7073 (diff) | |
Merge "Add pendingAppearedIntent to TaskFragmentContainer" into tm-dev
7 files changed, 227 insertions, 68 deletions
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java index 3380a23f3c09..575c3f002791 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java @@ -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); diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java index 43d0402c1525..ac3b05a0e825 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java @@ -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); diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java index 26ddae4a0818..497c2478806e 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java @@ -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(); } } diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizerTest.java index 792a53168a0d..a191e685f651 100644 --- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizerTest.java +++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizerTest.java @@ -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); diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java index 2fd491312f63..937d670287ad 100644 --- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java +++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java @@ -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,11 +590,36 @@ 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(); setupSplitRule(primaryActivity, mActivity); @@ -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()); } diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java index f1042ab6ce7d..ebe202db4e54 100644 --- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java +++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java @@ -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()); } diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentContainerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentContainerTest.java index 587878f3bf01..fcbd8a3ac020 100644 --- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentContainerTest.java +++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentContainerTest.java @@ -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. @@ -113,10 +136,59 @@ public class TaskFragmentContainerTest { } @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 */); + } } |