Loading libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java +64 −80 Original line number Diff line number Diff line Loading @@ -119,7 +119,8 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen // TODO(b/243518738): Move to WM Extensions if we have requirement of overlay without // association. It's not set in WM Extensions nor Wm Jetpack library currently. private static final String KEY_OVERLAY_ASSOCIATE_WITH_LAUNCHING_ACTIVITY = @VisibleForTesting static final String KEY_OVERLAY_ASSOCIATE_WITH_LAUNCHING_ACTIVITY = "androidx.window.extensions.embedding.shouldAssociateWithLaunchingActivity"; @VisibleForTesting Loading Loading @@ -2742,89 +2743,72 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen } final int taskId = getTaskId(launchActivity); if (!overlayContainers.isEmpty()) { // Overlay container policy: // 1. Overlay tag must be unique per process. // a. For associated overlay, if a new launched overlay container has the same tag as // an existing one, the existing overlay will be dismissed regardless of its task // and window hierarchy. // b. For always-on-top overlay, if there's an overlay container has the same tag in the // launched task, the overlay container will be re-used, which means the // ActivityStackAttributes will be applied and the launched activity will be positioned // on top of the overlay container. // 2. There must be at most one overlay that partially occludes a visible activity per task. // a. For associated overlay, only the top visible overlay container in the launched task // will be dismissed. // b. Always-on-top overlay is always visible. If there's an overlay with different tags // in the same task, the overlay will be dismissed in case an activity above // the overlay is dismissed and the overlay is shown unexpectedly. for (final TaskFragmentContainer overlayContainer : overlayContainers) { final boolean isTopNonFinishingOverlay = overlayContainer.equals( overlayContainer.getTaskContainer().getTopNonFinishingTaskFragmentContainer( true /* includePin */, true /* includeOverlay */)); if (taskId != overlayContainer.getTaskId()) { // If there's an overlay container with same tag in a different task, // dismiss the overlay container since the tag must be unique per process. if (overlayTag.equals(overlayContainer.getOverlayTag())) { final boolean areInSameTask = taskId == overlayContainer.getTaskId(); final boolean haveSameTag = overlayTag.equals(overlayContainer.getOverlayTag()); if (!associateLaunchingActivity && overlayContainer.isAlwaysOnTopOverlay() && haveSameTag && areInSameTask) { // Just launch the activity and update the existing always-on-top overlay // if the requested overlay is an always-on-top overlay with the same tag // as the existing one. mPresenter.applyActivityStackAttributes(wct, overlayContainer, attrs, getMinDimensions(intent)); return overlayContainer; } if (haveSameTag) { // For other tag match, we should clean up the existing overlay since the overlay // tag must be unique per process. Log.w(TAG, "The overlay container with tag:" + overlayContainer.getOverlayTag() + " is dismissed because" + " there's an existing overlay container with the same tag but" + " different task ID:" + overlayContainer.getTaskId() + ". " + "The new associated activity is " + launchActivity); + overlayContainer.getOverlayTag() + " is dismissed with " + " the launching activity=" + launchActivity + " because there's an existing overlay container with the same tag."); mPresenter.cleanupContainer(wct, overlayContainer, false /* shouldFinishDependant */); } if (!areInSameTask) { // Early return here because we won't clean-up or update overlay from different // tasks except tag collision. continue; } if (!overlayTag.equals(overlayContainer.getOverlayTag())) { // If there's an overlay container with different tag on top in the same // task, dismiss the existing overlay container. if (associateLaunchingActivity) { // For associated overlay, we only dismiss the overlay if it's the top non-finishing // child of its parent container. if (isTopNonFinishingOverlay) { Log.w(TAG, "The on-top overlay container with tag:" + overlayContainer.getOverlayTag() + " is dismissed with " + " the launching activity=" + launchActivity + "because we only allow one overlay on top."); mPresenter.cleanupContainer(wct, overlayContainer, false /* shouldFinishDependant */); } continue; } // The overlay container has the same tag and task ID with the new launching // overlay container. if (!isTopNonFinishingOverlay) { // Dismiss the invisible overlay container regardless of activity // association if it collides the tag of new launched overlay container . Log.w(TAG, "The invisible overlay container with tag:" + overlayContainer.getOverlayTag() + " is dismissed because" + " there's a launching overlay container with the same tag." + " The new associated activity is " + launchActivity); mPresenter.cleanupContainer(wct, overlayContainer, false /* shouldFinishDependant */); continue; } // Requesting an always-on-top overlay. if (!associateLaunchingActivity) { if (overlayContainer.isOverlayWithActivityAssociation()) { // Dismiss the overlay container since it has associated with an activity. Log.w(TAG, "The overlay container with tag:" + overlayContainer.getOverlayTag() + " is dismissed because" + " there's an existing overlay container with the same tag but" + " different associated launching activity. The overlay container" + " doesn't associate with any activity."); mPresenter.cleanupContainer(wct, overlayContainer, false /* shouldFinishDependant */); continue; } else { // The existing overlay container doesn't associate an activity as well. // Just update the overlay and return. // Note that going to this condition means the tag, task ID matches a // visible always-on-top overlay, and won't dismiss any overlay any more. mPresenter.applyActivityStackAttributes(wct, overlayContainer, attrs, getMinDimensions(intent)); return overlayContainer; } } if (launchActivity.getActivityToken() != overlayContainer.getAssociatedActivityToken()) { // Otherwise, we should clean up the overlay in the task because we only allow one // overlay when an always-on-top overlay is launched. Log.w(TAG, "The overlay container with tag:" + overlayContainer.getOverlayTag() + " is dismissed because" + " there's an existing overlay container with the same tag but" + " different associated launching activity. The new associated" + " activity is " + launchActivity); // The associated activity must be the same, or it will be dismissed. + overlayContainer.getOverlayTag() + " is dismissed with " + " the launching activity=" + launchActivity + "because an always-on-top overlay is launched."); mPresenter.cleanupContainer(wct, overlayContainer, false /* shouldFinishDependant */); continue; } // Reaching here means the launching activity launch an overlay container with the // same task ID, tag, while there's a previously launching visible overlay // container. We'll regard it as updating the existing overlay container. mPresenter.applyActivityStackAttributes(wct, overlayContainer, attrs, getMinDimensions(intent)); return overlayContainer; } } // Launch the overlay container to the task with taskId. return createEmptyContainer(wct, intent, taskId, attrs, launchActivity, overlayTag, Loading libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java +50 −23 Original line number Diff line number Diff line Loading @@ -30,6 +30,7 @@ import static androidx.window.extensions.embedding.EmbeddingTestUtils.createSpli import static androidx.window.extensions.embedding.EmbeddingTestUtils.createSplitPlaceholderRuleBuilder; import static androidx.window.extensions.embedding.EmbeddingTestUtils.createSplitRule; import static androidx.window.extensions.embedding.EmbeddingTestUtils.createTfContainer; import static androidx.window.extensions.embedding.SplitController.KEY_OVERLAY_ASSOCIATE_WITH_LAUNCHING_ACTIVITY; import static androidx.window.extensions.embedding.SplitPresenter.CONTAINER_POSITION_BOTTOM; import static androidx.window.extensions.embedding.SplitPresenter.CONTAINER_POSITION_LEFT; import static androidx.window.extensions.embedding.SplitPresenter.CONTAINER_POSITION_RIGHT; Loading Loading @@ -94,6 +95,7 @@ import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; Loading Loading @@ -267,7 +269,7 @@ public class OverlayPresentationTest { } @Test public void testCreateOrUpdateOverlay_visibleOverlaySameTagInTask_dismissOverlay() { public void testCreateOrUpdateOverlay_visibleOverlayInTask_dismissOverlay() { createExistingOverlayContainers(); final TaskFragmentContainer overlayContainer = Loading @@ -294,26 +296,6 @@ public class OverlayPresentationTest { .containsExactly(mOverlayContainer2, overlayContainer); } @Test public void testCreateOrUpdateOverlay_sameTagTaskAndActivity_updateOverlay() { createExistingOverlayContainers(); final Rect bounds = new Rect(0, 0, 100, 100); mSplitController.setActivityStackAttributesCalculator(params -> new ActivityStackAttributes.Builder().setRelativeBounds(bounds).build()); final TaskFragmentContainer overlayContainer = createOrUpdateOverlayTaskFragmentIfNeeded( mOverlayContainer1.getOverlayTag()); assertWithMessage("overlayContainer1 must be updated since the new overlay container" + " is launched with the same tag and task") .that(mSplitController.getAllNonFinishingOverlayContainers()) .containsExactly(mOverlayContainer1, mOverlayContainer2); assertThat(overlayContainer).isEqualTo(mOverlayContainer1); verify(mSplitPresenter).resizeTaskFragment(eq(mTransaction), eq(mOverlayContainer1.getTaskFragmentToken()), eq(bounds)); } @Test public void testCreateOrUpdateOverlay_sameTagAndTaskButNotActivity_dismissOverlay() { createExistingOverlayContainers(); Loading Loading @@ -361,6 +343,43 @@ public class OverlayPresentationTest { .containsExactly(overlayContainer); } @Test public void testCreateOrUpdateAlwaysOnTopOverlay_dismissMultipleOverlaysInTask() { createExistingOverlayContainers(); // Create another overlay in task. final TaskFragmentContainer overlayContainer3 = createTestOverlayContainer(TASK_ID, "test3"); assertThat(mSplitController.getAllNonFinishingOverlayContainers()) .containsExactly(mOverlayContainer1, mOverlayContainer2, overlayContainer3); final TaskFragmentContainer overlayContainer = createOrUpdateAlwaysOnTopOverlay("test4"); assertWithMessage("overlayContainer1 and overlayContainer3 must be dismissed") .that(mSplitController.getAllNonFinishingOverlayContainers()) .containsExactly(mOverlayContainer2, overlayContainer); } @Test public void testCreateOrUpdateAlwaysOnTopOverlay_updateOverlay() { createExistingOverlayContainers(); // Create another overlay in task. final TaskFragmentContainer alwaysOnTopOverlay = createTestOverlayContainer(TASK_ID, "test3", true /* isVisible */, false /* associateLaunchingActivity */); final ActivityStackAttributes attrs = new ActivityStackAttributes.Builder() .setRelativeBounds(new Rect(0, 0, 100, 100)).build(); mSplitController.setActivityStackAttributesCalculator(params -> attrs); Mockito.clearInvocations(mSplitPresenter); final TaskFragmentContainer overlayContainer = createOrUpdateAlwaysOnTopOverlay(alwaysOnTopOverlay.getOverlayTag()); assertWithMessage("overlayContainer1 and overlayContainer3 must be dismissed") .that(mSplitController.getAllNonFinishingOverlayContainers()) .containsExactly(mOverlayContainer2, alwaysOnTopOverlay); assertThat(overlayContainer).isEqualTo(alwaysOnTopOverlay); } @Test public void testCreateOrUpdateOverlay_launchFromSplit_returnNull() { final Activity primaryActivity = createMockActivity(); Loading Loading @@ -966,6 +985,16 @@ public class OverlayPresentationTest { launchOptions, mIntent, activity); } @Nullable private TaskFragmentContainer createOrUpdateAlwaysOnTopOverlay( @NonNull String tag) { final Bundle launchOptions = new Bundle(); launchOptions.putBoolean(KEY_OVERLAY_ASSOCIATE_WITH_LAUNCHING_ACTIVITY, false); launchOptions.putString(KEY_OVERLAY_TAG, tag); return mSplitController.createOrUpdateOverlayTaskFragmentIfNeeded(mTransaction, launchOptions, mIntent, createMockActivity()); } /** Creates a mock TaskFragment that has been registered and appeared in the organizer. */ @NonNull private TaskFragmentContainer createMockTaskFragmentContainer(@NonNull Activity activity) { Loading Loading @@ -1002,8 +1031,6 @@ public class OverlayPresentationTest { null /* launchingActivity */); } // TODO(b/243518738): add more test coverage on overlay container without activity association // once we have use cases. @NonNull private TaskFragmentContainer createTestOverlayContainer(int taskId, @NonNull String tag, boolean isVisible, boolean associateLaunchingActivity, Loading Loading
libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java +64 −80 Original line number Diff line number Diff line Loading @@ -119,7 +119,8 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen // TODO(b/243518738): Move to WM Extensions if we have requirement of overlay without // association. It's not set in WM Extensions nor Wm Jetpack library currently. private static final String KEY_OVERLAY_ASSOCIATE_WITH_LAUNCHING_ACTIVITY = @VisibleForTesting static final String KEY_OVERLAY_ASSOCIATE_WITH_LAUNCHING_ACTIVITY = "androidx.window.extensions.embedding.shouldAssociateWithLaunchingActivity"; @VisibleForTesting Loading Loading @@ -2742,89 +2743,72 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen } final int taskId = getTaskId(launchActivity); if (!overlayContainers.isEmpty()) { // Overlay container policy: // 1. Overlay tag must be unique per process. // a. For associated overlay, if a new launched overlay container has the same tag as // an existing one, the existing overlay will be dismissed regardless of its task // and window hierarchy. // b. For always-on-top overlay, if there's an overlay container has the same tag in the // launched task, the overlay container will be re-used, which means the // ActivityStackAttributes will be applied and the launched activity will be positioned // on top of the overlay container. // 2. There must be at most one overlay that partially occludes a visible activity per task. // a. For associated overlay, only the top visible overlay container in the launched task // will be dismissed. // b. Always-on-top overlay is always visible. If there's an overlay with different tags // in the same task, the overlay will be dismissed in case an activity above // the overlay is dismissed and the overlay is shown unexpectedly. for (final TaskFragmentContainer overlayContainer : overlayContainers) { final boolean isTopNonFinishingOverlay = overlayContainer.equals( overlayContainer.getTaskContainer().getTopNonFinishingTaskFragmentContainer( true /* includePin */, true /* includeOverlay */)); if (taskId != overlayContainer.getTaskId()) { // If there's an overlay container with same tag in a different task, // dismiss the overlay container since the tag must be unique per process. if (overlayTag.equals(overlayContainer.getOverlayTag())) { final boolean areInSameTask = taskId == overlayContainer.getTaskId(); final boolean haveSameTag = overlayTag.equals(overlayContainer.getOverlayTag()); if (!associateLaunchingActivity && overlayContainer.isAlwaysOnTopOverlay() && haveSameTag && areInSameTask) { // Just launch the activity and update the existing always-on-top overlay // if the requested overlay is an always-on-top overlay with the same tag // as the existing one. mPresenter.applyActivityStackAttributes(wct, overlayContainer, attrs, getMinDimensions(intent)); return overlayContainer; } if (haveSameTag) { // For other tag match, we should clean up the existing overlay since the overlay // tag must be unique per process. Log.w(TAG, "The overlay container with tag:" + overlayContainer.getOverlayTag() + " is dismissed because" + " there's an existing overlay container with the same tag but" + " different task ID:" + overlayContainer.getTaskId() + ". " + "The new associated activity is " + launchActivity); + overlayContainer.getOverlayTag() + " is dismissed with " + " the launching activity=" + launchActivity + " because there's an existing overlay container with the same tag."); mPresenter.cleanupContainer(wct, overlayContainer, false /* shouldFinishDependant */); } if (!areInSameTask) { // Early return here because we won't clean-up or update overlay from different // tasks except tag collision. continue; } if (!overlayTag.equals(overlayContainer.getOverlayTag())) { // If there's an overlay container with different tag on top in the same // task, dismiss the existing overlay container. if (associateLaunchingActivity) { // For associated overlay, we only dismiss the overlay if it's the top non-finishing // child of its parent container. if (isTopNonFinishingOverlay) { Log.w(TAG, "The on-top overlay container with tag:" + overlayContainer.getOverlayTag() + " is dismissed with " + " the launching activity=" + launchActivity + "because we only allow one overlay on top."); mPresenter.cleanupContainer(wct, overlayContainer, false /* shouldFinishDependant */); } continue; } // The overlay container has the same tag and task ID with the new launching // overlay container. if (!isTopNonFinishingOverlay) { // Dismiss the invisible overlay container regardless of activity // association if it collides the tag of new launched overlay container . Log.w(TAG, "The invisible overlay container with tag:" + overlayContainer.getOverlayTag() + " is dismissed because" + " there's a launching overlay container with the same tag." + " The new associated activity is " + launchActivity); mPresenter.cleanupContainer(wct, overlayContainer, false /* shouldFinishDependant */); continue; } // Requesting an always-on-top overlay. if (!associateLaunchingActivity) { if (overlayContainer.isOverlayWithActivityAssociation()) { // Dismiss the overlay container since it has associated with an activity. Log.w(TAG, "The overlay container with tag:" + overlayContainer.getOverlayTag() + " is dismissed because" + " there's an existing overlay container with the same tag but" + " different associated launching activity. The overlay container" + " doesn't associate with any activity."); mPresenter.cleanupContainer(wct, overlayContainer, false /* shouldFinishDependant */); continue; } else { // The existing overlay container doesn't associate an activity as well. // Just update the overlay and return. // Note that going to this condition means the tag, task ID matches a // visible always-on-top overlay, and won't dismiss any overlay any more. mPresenter.applyActivityStackAttributes(wct, overlayContainer, attrs, getMinDimensions(intent)); return overlayContainer; } } if (launchActivity.getActivityToken() != overlayContainer.getAssociatedActivityToken()) { // Otherwise, we should clean up the overlay in the task because we only allow one // overlay when an always-on-top overlay is launched. Log.w(TAG, "The overlay container with tag:" + overlayContainer.getOverlayTag() + " is dismissed because" + " there's an existing overlay container with the same tag but" + " different associated launching activity. The new associated" + " activity is " + launchActivity); // The associated activity must be the same, or it will be dismissed. + overlayContainer.getOverlayTag() + " is dismissed with " + " the launching activity=" + launchActivity + "because an always-on-top overlay is launched."); mPresenter.cleanupContainer(wct, overlayContainer, false /* shouldFinishDependant */); continue; } // Reaching here means the launching activity launch an overlay container with the // same task ID, tag, while there's a previously launching visible overlay // container. We'll regard it as updating the existing overlay container. mPresenter.applyActivityStackAttributes(wct, overlayContainer, attrs, getMinDimensions(intent)); return overlayContainer; } } // Launch the overlay container to the task with taskId. return createEmptyContainer(wct, intent, taskId, attrs, launchActivity, overlayTag, Loading
libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java +50 −23 Original line number Diff line number Diff line Loading @@ -30,6 +30,7 @@ import static androidx.window.extensions.embedding.EmbeddingTestUtils.createSpli import static androidx.window.extensions.embedding.EmbeddingTestUtils.createSplitPlaceholderRuleBuilder; import static androidx.window.extensions.embedding.EmbeddingTestUtils.createSplitRule; import static androidx.window.extensions.embedding.EmbeddingTestUtils.createTfContainer; import static androidx.window.extensions.embedding.SplitController.KEY_OVERLAY_ASSOCIATE_WITH_LAUNCHING_ACTIVITY; import static androidx.window.extensions.embedding.SplitPresenter.CONTAINER_POSITION_BOTTOM; import static androidx.window.extensions.embedding.SplitPresenter.CONTAINER_POSITION_LEFT; import static androidx.window.extensions.embedding.SplitPresenter.CONTAINER_POSITION_RIGHT; Loading Loading @@ -94,6 +95,7 @@ import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; Loading Loading @@ -267,7 +269,7 @@ public class OverlayPresentationTest { } @Test public void testCreateOrUpdateOverlay_visibleOverlaySameTagInTask_dismissOverlay() { public void testCreateOrUpdateOverlay_visibleOverlayInTask_dismissOverlay() { createExistingOverlayContainers(); final TaskFragmentContainer overlayContainer = Loading @@ -294,26 +296,6 @@ public class OverlayPresentationTest { .containsExactly(mOverlayContainer2, overlayContainer); } @Test public void testCreateOrUpdateOverlay_sameTagTaskAndActivity_updateOverlay() { createExistingOverlayContainers(); final Rect bounds = new Rect(0, 0, 100, 100); mSplitController.setActivityStackAttributesCalculator(params -> new ActivityStackAttributes.Builder().setRelativeBounds(bounds).build()); final TaskFragmentContainer overlayContainer = createOrUpdateOverlayTaskFragmentIfNeeded( mOverlayContainer1.getOverlayTag()); assertWithMessage("overlayContainer1 must be updated since the new overlay container" + " is launched with the same tag and task") .that(mSplitController.getAllNonFinishingOverlayContainers()) .containsExactly(mOverlayContainer1, mOverlayContainer2); assertThat(overlayContainer).isEqualTo(mOverlayContainer1); verify(mSplitPresenter).resizeTaskFragment(eq(mTransaction), eq(mOverlayContainer1.getTaskFragmentToken()), eq(bounds)); } @Test public void testCreateOrUpdateOverlay_sameTagAndTaskButNotActivity_dismissOverlay() { createExistingOverlayContainers(); Loading Loading @@ -361,6 +343,43 @@ public class OverlayPresentationTest { .containsExactly(overlayContainer); } @Test public void testCreateOrUpdateAlwaysOnTopOverlay_dismissMultipleOverlaysInTask() { createExistingOverlayContainers(); // Create another overlay in task. final TaskFragmentContainer overlayContainer3 = createTestOverlayContainer(TASK_ID, "test3"); assertThat(mSplitController.getAllNonFinishingOverlayContainers()) .containsExactly(mOverlayContainer1, mOverlayContainer2, overlayContainer3); final TaskFragmentContainer overlayContainer = createOrUpdateAlwaysOnTopOverlay("test4"); assertWithMessage("overlayContainer1 and overlayContainer3 must be dismissed") .that(mSplitController.getAllNonFinishingOverlayContainers()) .containsExactly(mOverlayContainer2, overlayContainer); } @Test public void testCreateOrUpdateAlwaysOnTopOverlay_updateOverlay() { createExistingOverlayContainers(); // Create another overlay in task. final TaskFragmentContainer alwaysOnTopOverlay = createTestOverlayContainer(TASK_ID, "test3", true /* isVisible */, false /* associateLaunchingActivity */); final ActivityStackAttributes attrs = new ActivityStackAttributes.Builder() .setRelativeBounds(new Rect(0, 0, 100, 100)).build(); mSplitController.setActivityStackAttributesCalculator(params -> attrs); Mockito.clearInvocations(mSplitPresenter); final TaskFragmentContainer overlayContainer = createOrUpdateAlwaysOnTopOverlay(alwaysOnTopOverlay.getOverlayTag()); assertWithMessage("overlayContainer1 and overlayContainer3 must be dismissed") .that(mSplitController.getAllNonFinishingOverlayContainers()) .containsExactly(mOverlayContainer2, alwaysOnTopOverlay); assertThat(overlayContainer).isEqualTo(alwaysOnTopOverlay); } @Test public void testCreateOrUpdateOverlay_launchFromSplit_returnNull() { final Activity primaryActivity = createMockActivity(); Loading Loading @@ -966,6 +985,16 @@ public class OverlayPresentationTest { launchOptions, mIntent, activity); } @Nullable private TaskFragmentContainer createOrUpdateAlwaysOnTopOverlay( @NonNull String tag) { final Bundle launchOptions = new Bundle(); launchOptions.putBoolean(KEY_OVERLAY_ASSOCIATE_WITH_LAUNCHING_ACTIVITY, false); launchOptions.putString(KEY_OVERLAY_TAG, tag); return mSplitController.createOrUpdateOverlayTaskFragmentIfNeeded(mTransaction, launchOptions, mIntent, createMockActivity()); } /** Creates a mock TaskFragment that has been registered and appeared in the organizer. */ @NonNull private TaskFragmentContainer createMockTaskFragmentContainer(@NonNull Activity activity) { Loading Loading @@ -1002,8 +1031,6 @@ public class OverlayPresentationTest { null /* launchingActivity */); } // TODO(b/243518738): add more test coverage on overlay container without activity association // once we have use cases. @NonNull private TaskFragmentContainer createTestOverlayContainer(int taskId, @NonNull String tag, boolean isVisible, boolean associateLaunchingActivity, Loading