diff options
| author | 2023-10-23 23:10:11 +0000 | |
|---|---|---|
| committer | 2023-10-23 23:10:11 +0000 | |
| commit | bfd2ee97cad0efdf328f0af9a209145e9c4dd6d3 (patch) | |
| tree | 0df229a3e8d1d28f3ac2e2ce29b0e0b152bc1491 | |
| parent | de2fbadef5af91bc29e48c1da6fc8b8d798f135c (diff) | |
| parent | 7ebf80c2b7625267eefbfc144aea41f299fe3278 (diff) | |
Merge "Exclude overlay when reporting top non-finishing container by default" into main
4 files changed, 78 insertions, 80 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 2f1eec15e415..602ab18aa882 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java @@ -892,7 +892,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen && taskContainer.getTopNonFinishingTaskFragmentContainer(false /* includePin */) != container) { // Do not resolve if the launched activity is not the top-most container (excludes - // the pinned container) in the Task. + // the pinned and overlay container) in the Task. return true; } @@ -1756,31 +1756,6 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen } /** - * Returns the topmost not finished container in Task of given task id. - */ - @GuardedBy("mLock") - @Nullable - TaskFragmentContainer getTopActiveContainer(int taskId) { - final TaskContainer taskContainer = mTaskContainers.get(taskId); - if (taskContainer == null) { - return null; - } - final List<TaskFragmentContainer> containers = taskContainer.getTaskFragmentContainers(); - for (int i = containers.size() - 1; i >= 0; i--) { - final TaskFragmentContainer container = containers.get(i); - if (!container.isFinished() && (container.getRunningActivityCount() > 0 - // We may be waiting for the top TaskFragment to become non-empty after - // creation. In that case, we don't want to treat the TaskFragment below it as - // top active, otherwise it may incorrectly launch placeholder on top of the - // pending TaskFragment. - || container.isWaitingActivityAppear())) { - return container; - } - } - return null; - } - - /** * Updates the presentation of the container. If the container is part of the split or should * have a placeholder, it will also update the other part of the split. */ @@ -1968,7 +1943,8 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen /** Whether or not to allow activity in this container to launch placeholder. */ @GuardedBy("mLock") private boolean allowLaunchPlaceholder(@NonNull TaskFragmentContainer container) { - final TaskFragmentContainer topContainer = getTopActiveContainer(container.getTaskId()); + final TaskFragmentContainer topContainer = container.getTaskContainer() + .getTopNonFinishingTaskFragmentContainer(); if (container != topContainer) { // The container is not the top most. if (!container.isVisible()) { diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java index f4427aaba182..9e533808ccc0 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java @@ -191,11 +191,20 @@ class TaskContainer { @Nullable TaskFragmentContainer getTopNonFinishingTaskFragmentContainer(boolean includePin) { + return getTopNonFinishingTaskFragmentContainer(includePin, false /* includeOverlay */); + } + + @Nullable + TaskFragmentContainer getTopNonFinishingTaskFragmentContainer(boolean includePin, + boolean includeOverlay) { for (int i = mContainers.size() - 1; i >= 0; i--) { final TaskFragmentContainer container = mContainers.get(i); if (!includePin && isTaskFragmentContainerPinned(container)) { continue; } + if (!includeOverlay && container.isOverlay()) { + continue; + } if (!container.isFinished()) { return container; } diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java index af8017ae9544..405f0b2f51dc 100644 --- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java +++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java @@ -21,6 +21,7 @@ import static android.view.Display.DEFAULT_DISPLAY; import static androidx.window.extensions.embedding.EmbeddingTestUtils.TASK_BOUNDS; import static androidx.window.extensions.embedding.EmbeddingTestUtils.TASK_ID; import static androidx.window.extensions.embedding.EmbeddingTestUtils.createMockTaskFragmentInfo; +import static androidx.window.extensions.embedding.EmbeddingTestUtils.createSplitPairRuleBuilder; import static androidx.window.extensions.embedding.OverlayCreateParams.KEY_OVERLAY_CREATE_PARAMS; import static androidx.window.extensions.embedding.OverlayCreateParams.KEY_OVERLAY_CREATE_PARAMS_BOUNDS; import static androidx.window.extensions.embedding.OverlayCreateParams.KEY_OVERLAY_CREATE_PARAMS_TAG; @@ -364,6 +365,54 @@ public class OverlayPresentationTest { assertThat(overlayContainer.getOverlayTag()).isEqualTo(TEST_OVERLAY_CREATE_PARAMS.getTag()); } + @Test + public void testGetTopNonFishingTaskFragmentContainerWithOverlay() { + final TaskFragmentContainer overlayContainer = + createTestOverlayContainer(TASK_ID, "test1"); + + // Add a SplitPinContainer, the overlay should be on top + final Activity primaryActivity = createMockActivity(); + final Activity secondaryActivity = createMockActivity(); + + final TaskFragmentContainer primaryContainer = + createMockTaskFragmentContainer(primaryActivity); + final TaskFragmentContainer secondaryContainer = + createMockTaskFragmentContainer(secondaryActivity); + final SplitPairRule splitPairRule = createSplitPairRuleBuilder( + activityActivityPair -> true /* activityPairPredicate */, + activityIntentPair -> true /* activityIntentPairPredicate */, + parentWindowMetrics -> true /* parentWindowMetricsPredicate */).build(); + mSplitController.registerSplit(mTransaction, primaryContainer, primaryActivity, + secondaryContainer, splitPairRule, splitPairRule.getDefaultSplitAttributes()); + SplitPinRule splitPinRule = new SplitPinRule.Builder(new SplitAttributes.Builder().build(), + parentWindowMetrics -> true /* parentWindowMetricsPredicate */).build(); + mSplitController.pinTopActivityStack(TASK_ID, splitPinRule); + final TaskFragmentContainer topPinnedContainer = mSplitController.getTaskContainer(TASK_ID) + .getSplitPinContainer().getSecondaryContainer(); + + // Add a normal container after the overlay, the overlay should still on top, + // and the SplitPinContainer should also on top of the normal one. + final TaskFragmentContainer container = createMockTaskFragmentContainer(mActivity); + + final TaskContainer taskContainer = mSplitController.getTaskContainer(TASK_ID); + + assertThat(taskContainer.getTaskFragmentContainers()) + .containsExactly(primaryContainer, container, secondaryContainer, overlayContainer) + .inOrder(); + + assertWithMessage("The pinned container must be returned excluding the overlay") + .that(taskContainer.getTopNonFinishingTaskFragmentContainer()) + .isEqualTo(topPinnedContainer); + + assertThat(taskContainer.getTopNonFinishingTaskFragmentContainer(false)) + .isEqualTo(container); + + assertWithMessage("The overlay container must be returned since it's always on top") + .that(taskContainer.getTopNonFinishingTaskFragmentContainer( + false /* includePin */, true /* includeOverlay */)) + .isEqualTo(overlayContainer); + } + /** * A simplified version of {@link SplitController.ActivityStartMonitor * #createOrUpdateOverlayTaskFragmentIfNeeded} @@ -375,6 +424,15 @@ public class OverlayPresentationTest { taskId, mIntent, mActivity); } + /** Creates a mock TaskFragment that has been registered and appeared in the organizer. */ + @NonNull + private TaskFragmentContainer createMockTaskFragmentContainer(@NonNull Activity activity) { + final TaskFragmentContainer container = mSplitController.newContainer(activity, + activity.getTaskId()); + setupTaskFragmentInfo(container, activity); + return container; + } + @NonNull private TaskFragmentContainer createTestOverlayContainer(int taskId, @NonNull String tag) { TaskFragmentContainer overlayContainer = mSplitController.newContainer( 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 6c0b3cf7971d..b0a74614a08e 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 @@ -48,13 +48,12 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.doCallRealM import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; -import static com.google.common.truth.Truth.assertWithMessage; - import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; @@ -176,52 +175,6 @@ public class SplitControllerTest { } @Test - public void testGetTopActiveContainer() { - final TaskContainer taskContainer = createTestTaskContainer(); - // tf1 has no running activity so is not active. - final TaskFragmentContainer tf1 = new TaskFragmentContainer(null /* activity */, - new Intent(), taskContainer, mSplitController, null /* pairedPrimaryContainer */); - // tf2 has running activity so is active. - final TaskFragmentContainer tf2 = mock(TaskFragmentContainer.class); - doReturn(1).when(tf2).getRunningActivityCount(); - taskContainer.addTaskFragmentContainer(tf2); - // tf3 is finished so is not active. - final TaskFragmentContainer tf3 = mock(TaskFragmentContainer.class); - doReturn(true).when(tf3).isFinished(); - doReturn(false).when(tf3).isWaitingActivityAppear(); - taskContainer.addTaskFragmentContainer(tf3); - mSplitController.mTaskContainers.put(TASK_ID, taskContainer); - - assertWithMessage("Must return tf2 because tf3 is not active.") - .that(mSplitController.getTopActiveContainer(TASK_ID)).isEqualTo(tf2); - - taskContainer.removeTaskFragmentContainer(tf3); - - assertWithMessage("Must return tf2 because tf2 has running activity.") - .that(mSplitController.getTopActiveContainer(TASK_ID)).isEqualTo(tf2); - - taskContainer.removeTaskFragmentContainer(tf2); - - assertWithMessage("Must return tf because we are waiting for tf1 to appear.") - .that(mSplitController.getTopActiveContainer(TASK_ID)).isEqualTo(tf1); - - final TaskFragmentInfo info = mock(TaskFragmentInfo.class); - doReturn(new ArrayList<>()).when(info).getActivities(); - doReturn(true).when(info).isEmpty(); - tf1.setInfo(mTransaction, info); - - assertWithMessage("Must return tf because we are waiting for tf1 to become non-empty after" - + " creation.") - .that(mSplitController.getTopActiveContainer(TASK_ID)).isEqualTo(tf1); - - doReturn(false).when(info).isEmpty(); - tf1.setInfo(mTransaction, info); - - assertWithMessage("Must return null because tf1 becomes empty.") - .that(mSplitController.getTopActiveContainer(TASK_ID)).isNull(); - } - - @Test public void testOnTaskFragmentVanished() { final TaskFragmentContainer tf = mSplitController.newContainer(mActivity, TASK_ID); doReturn(tf.getTaskFragmentToken()).when(mInfo).getFragmentToken(); @@ -306,7 +259,9 @@ public class SplitControllerTest { mSplitController.updateContainer(mTransaction, tf); - verify(mSplitController, never()).getTopActiveContainer(TASK_ID); + TaskContainer taskContainer = tf.getTaskContainer(); + spyOn(taskContainer); + verify(taskContainer, never()).getTopNonFinishingTaskFragmentContainer(); // Verify if tf is not in split, dismissPlaceholderIfNecessary won't be called. doReturn(false).when(mSplitController).shouldContainerBeExpanded(tf); @@ -321,7 +276,7 @@ public class SplitControllerTest { doReturn(tf).when(splitContainer).getSecondaryContainer(); doReturn(createTestTaskContainer()).when(splitContainer).getTaskContainer(); doReturn(createSplitRule(mActivity, mActivity)).when(splitContainer).getSplitRule(); - final TaskContainer taskContainer = mSplitController.getTaskContainer(TASK_ID); + taskContainer = mSplitController.getTaskContainer(TASK_ID); taskContainer.addSplitContainer(splitContainer); // Add a mock SplitContainer on top of splitContainer final SplitContainer splitContainer2 = mock(SplitContainer.class); @@ -1569,9 +1524,9 @@ public class SplitControllerTest { addSplitTaskFragments(primaryActivity, thirdActivity); // Ensure another SplitContainer is added and the pinned TaskFragment still on top - assertTrue(taskContainer.getSplitContainers().size() == splitContainerCount + +1); - assertTrue(mSplitController.getTopActiveContainer(TASK_ID).getTopNonFinishingActivity() - == secondaryActivity); + assertEquals(taskContainer.getSplitContainers().size(), splitContainerCount + +1); + assertSame(taskContainer.getTopNonFinishingTaskFragmentContainer() + .getTopNonFinishingActivity(), secondaryActivity); } /** Creates a mock activity in the organizer process. */ |