Loading libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java +44 −12 Original line number Diff line number Diff line Loading @@ -831,7 +831,8 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen return true; } if (!isOnReparent && getContainerWithActivity(activity) == null final TaskFragmentContainer container = getContainerWithActivity(activity); if (!isOnReparent && container == null && getTaskFragmentTokenFromActivityClientRecord(activity) != null) { // We can't find the new launched activity in any recorded container, but it is // currently placed in an embedded TaskFragment. This can happen in two cases: Loading @@ -843,11 +844,21 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen return true; } final TaskFragmentContainer container = getContainerWithActivity(activity); if (!isOnReparent && container != null && container.getTaskContainer().getTopNonFinishingTaskFragmentContainer() // Skip resolving if the activity is on a pinned TaskFragmentContainer. // TODO(b/243518738): skip resolving for overlay container. if (container != null) { final TaskContainer taskContainer = container.getTaskContainer(); if (taskContainer.isTaskFragmentContainerPinned(container)) { return true; } } final TaskContainer taskContainer = container != null ? container.getTaskContainer() : null; if (!isOnReparent && taskContainer != null && taskContainer.getTopNonFinishingTaskFragmentContainer(false /* includePin */) != container) { // Do not resolve if the launched activity is not the top-most container in the Task. // Do not resolve if the launched activity is not the top-most container (excludes // the pinned container) in the Task. return true; } Loading Loading @@ -1244,6 +1255,19 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen @GuardedBy("mLock") TaskFragmentContainer resolveStartActivityIntent(@NonNull WindowContainerTransaction wct, int taskId, @NonNull Intent intent, @Nullable Activity launchingActivity) { // Skip resolving if started from pinned TaskFragmentContainer. // TODO(b/243518738): skip resolving for overlay container. if (launchingActivity != null) { final TaskFragmentContainer taskFragmentContainer = getContainerWithActivity( launchingActivity); final TaskContainer taskContainer = taskFragmentContainer != null ? taskFragmentContainer.getTaskContainer() : null; if (taskContainer != null && taskContainer.isTaskFragmentContainerPinned( taskFragmentContainer)) { return null; } } /* * We will check the following to see if there is any embedding rule matched: * 1. Whether the new activity intent should always expand. Loading Loading @@ -1584,6 +1608,13 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen return; } // If the secondary container is pinned, it should not be removed. final SplitContainer activeContainer = getActiveSplitForContainer(existingSplitContainer.getSecondaryContainer()); if (activeContainer instanceof SplitPinContainer) { return; } existingSplitContainer.getSecondaryContainer().finish( false /* shouldFinishDependent */, mPresenter, wct, this); } Loading Loading @@ -1625,12 +1656,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen // background. return; } final SplitContainer splitContainer = getActiveSplitForContainer(container); if (splitContainer instanceof SplitPinContainer && updateSplitContainerIfNeeded(splitContainer, wct, null /* splitAttributes */)) { // A SplitPinContainer exists and is updated. return; } if (launchPlaceholderIfNecessary(wct, container)) { // Placeholder was launched, the positions will be updated when the activity is added // to the secondary container. Loading @@ -1643,6 +1669,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen // If the info is not available yet the task fragment will be expanded when it's ready return; } final SplitContainer splitContainer = getActiveSplitForContainer(container); if (splitContainer == null) { return; } Loading Loading @@ -1826,6 +1853,10 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen // Don't launch placeholder for primary split container. return false; } if (splitContainer instanceof SplitPinContainer) { // Don't launch placeholder if pinned return false; } return true; } Loading Loading @@ -2072,8 +2103,9 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen * Returns {@code true} if an Activity with the provided component name should always be * expanded to occupy full task bounds. Such activity must not be put in a split. */ @VisibleForTesting @GuardedBy("mLock") private boolean shouldExpand(@Nullable Activity activity, @Nullable Intent intent) { boolean shouldExpand(@Nullable Activity activity, @Nullable Intent intent) { for (EmbeddingRule rule : mSplitRules) { if (!(rule instanceof ActivityRule)) { continue; Loading libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java +13 −0 Original line number Diff line number Diff line Loading @@ -179,8 +179,16 @@ class TaskContainer { @Nullable TaskFragmentContainer getTopNonFinishingTaskFragmentContainer() { return getTopNonFinishingTaskFragmentContainer(true /* includePin */); } @Nullable TaskFragmentContainer getTopNonFinishingTaskFragmentContainer(boolean includePin) { for (int i = mContainers.size() - 1; i >= 0; i--) { final TaskFragmentContainer container = mContainers.get(i); if (!includePin && isTaskFragmentContainerPinned(container)) { continue; } if (!container.isFinished()) { return container; } Loading Loading @@ -266,6 +274,11 @@ class TaskContainer { return mSplitPinContainer; } boolean isTaskFragmentContainerPinned(@NonNull TaskFragmentContainer taskFragmentContainer) { return mSplitPinContainer != null && mSplitPinContainer.getSecondaryContainer() == taskFragmentContainer; } void addTaskFragmentContainer(@NonNull TaskFragmentContainer taskFragmentContainer) { mContainers.add(taskFragmentContainer); onTaskFragmentContainerUpdated(); Loading libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java +35 −0 Original line number Diff line number Diff line Loading @@ -594,6 +594,18 @@ public class SplitControllerTest { assertNotEquals(container, secondaryContainer); } @Test public void testResolveStartActivityIntent_skipIfPinned() { final TaskFragmentContainer container = createMockTaskFragmentContainer(mActivity); final TaskContainer taskContainer = container.getTaskContainer(); spyOn(taskContainer); final Intent intent = new Intent(); setupSplitRule(mActivity, intent); doReturn(true).when(taskContainer).isTaskFragmentContainerPinned(container); assertNull(mSplitController.resolveStartActivityIntent(mTransaction, TASK_ID, intent, mActivity)); } @Test public void testPlaceActivityInTopContainer() { mSplitController.placeActivityInTopContainer(mTransaction, mActivity); Loading Loading @@ -1043,6 +1055,29 @@ public class SplitControllerTest { false /* isOnReparent */)); } @Test public void testResolveActivityToContainer_skipIfNonTopOrPinned() { final TaskFragmentContainer container = createMockTaskFragmentContainer(mActivity); final Activity pinnedActivity = createMockActivity(); final TaskFragmentContainer topContainer = mSplitController.newContainer(pinnedActivity, TASK_ID); final TaskContainer taskContainer = container.getTaskContainer(); spyOn(taskContainer); doReturn(container).when(taskContainer).getTopNonFinishingTaskFragmentContainer(false); doReturn(true).when(taskContainer).isTaskFragmentContainerPinned(topContainer); // No need to handle when the new launched activity is in a pinned TaskFragment. assertTrue(mSplitController.resolveActivityToContainer(mTransaction, pinnedActivity, false /* isOnReparent */)); verify(mSplitController, never()).shouldExpand(any(), any()); // Should proceed to resolve if the new launched activity is in the next top TaskFragment // (e.g. the top-most TaskFragment is pinned) mSplitController.resolveActivityToContainer(mTransaction, mActivity, false /* isOnReparent */); verify(mSplitController).shouldExpand(any(), any()); } @Test public void testGetPlaceholderOptions() { // Setup to make sure a transaction record is started. Loading Loading
libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java +44 −12 Original line number Diff line number Diff line Loading @@ -831,7 +831,8 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen return true; } if (!isOnReparent && getContainerWithActivity(activity) == null final TaskFragmentContainer container = getContainerWithActivity(activity); if (!isOnReparent && container == null && getTaskFragmentTokenFromActivityClientRecord(activity) != null) { // We can't find the new launched activity in any recorded container, but it is // currently placed in an embedded TaskFragment. This can happen in two cases: Loading @@ -843,11 +844,21 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen return true; } final TaskFragmentContainer container = getContainerWithActivity(activity); if (!isOnReparent && container != null && container.getTaskContainer().getTopNonFinishingTaskFragmentContainer() // Skip resolving if the activity is on a pinned TaskFragmentContainer. // TODO(b/243518738): skip resolving for overlay container. if (container != null) { final TaskContainer taskContainer = container.getTaskContainer(); if (taskContainer.isTaskFragmentContainerPinned(container)) { return true; } } final TaskContainer taskContainer = container != null ? container.getTaskContainer() : null; if (!isOnReparent && taskContainer != null && taskContainer.getTopNonFinishingTaskFragmentContainer(false /* includePin */) != container) { // Do not resolve if the launched activity is not the top-most container in the Task. // Do not resolve if the launched activity is not the top-most container (excludes // the pinned container) in the Task. return true; } Loading Loading @@ -1244,6 +1255,19 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen @GuardedBy("mLock") TaskFragmentContainer resolveStartActivityIntent(@NonNull WindowContainerTransaction wct, int taskId, @NonNull Intent intent, @Nullable Activity launchingActivity) { // Skip resolving if started from pinned TaskFragmentContainer. // TODO(b/243518738): skip resolving for overlay container. if (launchingActivity != null) { final TaskFragmentContainer taskFragmentContainer = getContainerWithActivity( launchingActivity); final TaskContainer taskContainer = taskFragmentContainer != null ? taskFragmentContainer.getTaskContainer() : null; if (taskContainer != null && taskContainer.isTaskFragmentContainerPinned( taskFragmentContainer)) { return null; } } /* * We will check the following to see if there is any embedding rule matched: * 1. Whether the new activity intent should always expand. Loading Loading @@ -1584,6 +1608,13 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen return; } // If the secondary container is pinned, it should not be removed. final SplitContainer activeContainer = getActiveSplitForContainer(existingSplitContainer.getSecondaryContainer()); if (activeContainer instanceof SplitPinContainer) { return; } existingSplitContainer.getSecondaryContainer().finish( false /* shouldFinishDependent */, mPresenter, wct, this); } Loading Loading @@ -1625,12 +1656,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen // background. return; } final SplitContainer splitContainer = getActiveSplitForContainer(container); if (splitContainer instanceof SplitPinContainer && updateSplitContainerIfNeeded(splitContainer, wct, null /* splitAttributes */)) { // A SplitPinContainer exists and is updated. return; } if (launchPlaceholderIfNecessary(wct, container)) { // Placeholder was launched, the positions will be updated when the activity is added // to the secondary container. Loading @@ -1643,6 +1669,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen // If the info is not available yet the task fragment will be expanded when it's ready return; } final SplitContainer splitContainer = getActiveSplitForContainer(container); if (splitContainer == null) { return; } Loading Loading @@ -1826,6 +1853,10 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen // Don't launch placeholder for primary split container. return false; } if (splitContainer instanceof SplitPinContainer) { // Don't launch placeholder if pinned return false; } return true; } Loading Loading @@ -2072,8 +2103,9 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen * Returns {@code true} if an Activity with the provided component name should always be * expanded to occupy full task bounds. Such activity must not be put in a split. */ @VisibleForTesting @GuardedBy("mLock") private boolean shouldExpand(@Nullable Activity activity, @Nullable Intent intent) { boolean shouldExpand(@Nullable Activity activity, @Nullable Intent intent) { for (EmbeddingRule rule : mSplitRules) { if (!(rule instanceof ActivityRule)) { continue; Loading
libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java +13 −0 Original line number Diff line number Diff line Loading @@ -179,8 +179,16 @@ class TaskContainer { @Nullable TaskFragmentContainer getTopNonFinishingTaskFragmentContainer() { return getTopNonFinishingTaskFragmentContainer(true /* includePin */); } @Nullable TaskFragmentContainer getTopNonFinishingTaskFragmentContainer(boolean includePin) { for (int i = mContainers.size() - 1; i >= 0; i--) { final TaskFragmentContainer container = mContainers.get(i); if (!includePin && isTaskFragmentContainerPinned(container)) { continue; } if (!container.isFinished()) { return container; } Loading Loading @@ -266,6 +274,11 @@ class TaskContainer { return mSplitPinContainer; } boolean isTaskFragmentContainerPinned(@NonNull TaskFragmentContainer taskFragmentContainer) { return mSplitPinContainer != null && mSplitPinContainer.getSecondaryContainer() == taskFragmentContainer; } void addTaskFragmentContainer(@NonNull TaskFragmentContainer taskFragmentContainer) { mContainers.add(taskFragmentContainer); onTaskFragmentContainerUpdated(); Loading
libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java +35 −0 Original line number Diff line number Diff line Loading @@ -594,6 +594,18 @@ public class SplitControllerTest { assertNotEquals(container, secondaryContainer); } @Test public void testResolveStartActivityIntent_skipIfPinned() { final TaskFragmentContainer container = createMockTaskFragmentContainer(mActivity); final TaskContainer taskContainer = container.getTaskContainer(); spyOn(taskContainer); final Intent intent = new Intent(); setupSplitRule(mActivity, intent); doReturn(true).when(taskContainer).isTaskFragmentContainerPinned(container); assertNull(mSplitController.resolveStartActivityIntent(mTransaction, TASK_ID, intent, mActivity)); } @Test public void testPlaceActivityInTopContainer() { mSplitController.placeActivityInTopContainer(mTransaction, mActivity); Loading Loading @@ -1043,6 +1055,29 @@ public class SplitControllerTest { false /* isOnReparent */)); } @Test public void testResolveActivityToContainer_skipIfNonTopOrPinned() { final TaskFragmentContainer container = createMockTaskFragmentContainer(mActivity); final Activity pinnedActivity = createMockActivity(); final TaskFragmentContainer topContainer = mSplitController.newContainer(pinnedActivity, TASK_ID); final TaskContainer taskContainer = container.getTaskContainer(); spyOn(taskContainer); doReturn(container).when(taskContainer).getTopNonFinishingTaskFragmentContainer(false); doReturn(true).when(taskContainer).isTaskFragmentContainerPinned(topContainer); // No need to handle when the new launched activity is in a pinned TaskFragment. assertTrue(mSplitController.resolveActivityToContainer(mTransaction, pinnedActivity, false /* isOnReparent */)); verify(mSplitController, never()).shouldExpand(any(), any()); // Should proceed to resolve if the new launched activity is in the next top TaskFragment // (e.g. the top-most TaskFragment is pinned) mSplitController.resolveActivityToContainer(mTransaction, mActivity, false /* isOnReparent */); verify(mSplitController).shouldExpand(any(), any()); } @Test public void testGetPlaceholderOptions() { // Setup to make sure a transaction record is started. Loading