Loading libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java +37 −17 Original line number Diff line number Diff line Loading @@ -431,12 +431,8 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen if (container != null) { // Cleanup if the TaskFragment vanished is not requested by the organizer. removeContainer(container); // Make sure the top container is updated. final TaskFragmentContainer newTopContainer = getTopActiveContainer( container.getTaskId()); if (newTopContainer != null) { updateContainer(wct, newTopContainer); } // Make sure the containers in the Task are up-to-date. updateContainersInTaskIfVisible(wct, container.getTaskId()); } cleanupTaskFragment(taskFragmentInfo.getFragmentToken()); } Loading Loading @@ -476,6 +472,13 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen updateContainersInTask(wct, taskContainer); } void updateContainersInTaskIfVisible(@NonNull WindowContainerTransaction wct, int taskId) { final TaskContainer taskContainer = getTaskContainer(taskId); if (taskContainer != null && taskContainer.isVisible()) { updateContainersInTask(wct, taskContainer); } } private void updateContainersInTask(@NonNull WindowContainerTransaction wct, @NonNull TaskContainer taskContainer) { // Update all TaskFragments in the Task. Make a copy of the list since some may be Loading Loading @@ -1328,9 +1331,6 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen void removeContainer(@NonNull TaskFragmentContainer container) { // Remove all split containers that included this one final TaskContainer taskContainer = container.getTaskContainer(); if (taskContainer == null) { return; } taskContainer.mContainers.remove(container); // Marked as a pending removal which will be removed after it is actually removed on the // server side (#onTaskFragmentVanished). Loading Loading @@ -1523,14 +1523,8 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen } final TaskFragmentContainer container = getContainerWithActivity(activity); // Don't launch placeholder if the container is occluded. if (container != null && container != getTopActiveContainer(container.getTaskId())) { return false; } final SplitContainer splitContainer = getActiveSplitForContainer(container); if (splitContainer != null && container.equals(splitContainer.getPrimaryContainer())) { // Don't launch placeholder in primary split container if (container != null && !allowLaunchPlaceholder(container)) { // We don't allow activity in this TaskFragment to launch placeholder. return false; } Loading Loading @@ -1558,6 +1552,32 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen return true; } /** 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()); if (container != topContainer) { // The container is not the top most. if (!container.isVisible()) { // In case the container is visible (the one on top may be transparent), we may // still want to launch placeholder even if it is not the top most. return false; } if (topContainer.isWaitingActivityAppear()) { // When the top container appeared info is not sent by the server yet, the visible // check above may not be reliable. return false; } } final SplitContainer splitContainer = getActiveSplitForContainer(container); if (splitContainer != null && container.equals(splitContainer.getPrimaryContainer())) { // Don't launch placeholder for primary split container. return false; } return true; } /** * Gets the activity options for starting the placeholder activity. In case the placeholder is * launched when the Task is in the background, we don't want to bring the Task to the front. Loading libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java +2 −6 Original line number Diff line number Diff line Loading @@ -154,12 +154,8 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer { void cleanupContainer(@NonNull WindowContainerTransaction wct, @NonNull TaskFragmentContainer container, boolean shouldFinishDependent) { container.finish(shouldFinishDependent, this, wct, mController); final TaskFragmentContainer newTopContainer = mController.getTopActiveContainer( container.getTaskId()); if (newTopContainer != null) { mController.updateContainer(wct, newTopContainer); } // Make sure the containers in the Task is up-to-date. mController.updateContainersInTaskIfVisible(wct, container.getTaskId()); } /** Loading libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java +5 −0 Original line number Diff line number Diff line Loading @@ -184,6 +184,11 @@ class TaskFragmentContainer { return allActivities; } /** Whether this TaskFragment is visible. */ boolean isVisible() { return mInfo != null && mInfo.isVisible(); } /** Whether the TaskFragment is in an intermediate state waiting for the server update.*/ boolean isInIntermediateState() { if (mInfo == null) { Loading libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/EmbeddingTestUtils.java +7 −1 Original line number Diff line number Diff line Loading @@ -163,11 +163,17 @@ public class EmbeddingTestUtils { /** Creates a mock TaskFragmentInfo for the given TaskFragment. */ static TaskFragmentInfo createMockTaskFragmentInfo(@NonNull TaskFragmentContainer container, @NonNull Activity activity) { return createMockTaskFragmentInfo(container, activity, true /* isVisible */); } /** Creates a mock TaskFragmentInfo for the given TaskFragment. */ static TaskFragmentInfo createMockTaskFragmentInfo(@NonNull TaskFragmentContainer container, @NonNull Activity activity, boolean isVisible) { return new TaskFragmentInfo(container.getTaskFragmentToken(), mock(WindowContainerToken.class), new Configuration(), 1, true /* isVisible */, isVisible, Collections.singletonList(activity.getActivityToken()), new Point(), false /* isTaskClearedForReuse */, Loading libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java +62 −0 Original line number Diff line number Diff line Loading @@ -1254,6 +1254,68 @@ public class SplitControllerTest { verify(mEmbeddingCallback).accept(any()); } @Test public void testLaunchPlaceholderIfNecessary_nonEmbeddedActivity() { // Launch placeholder for non embedded activity. setupPlaceholderRule(mActivity); mTransactionManager.startNewTransaction(); mSplitController.launchPlaceholderIfNecessary(mTransaction, mActivity, true /* isOnCreated */); verify(mTransaction).startActivityInTaskFragment(any(), any(), eq(PLACEHOLDER_INTENT), any()); } @Test public void testLaunchPlaceholderIfNecessary_embeddedInTopTaskFragment() { // Launch placeholder for activity in top TaskFragment. setupPlaceholderRule(mActivity); mTransactionManager.startNewTransaction(); final TaskFragmentContainer container = mSplitController.newContainer(mActivity, TASK_ID); mSplitController.launchPlaceholderIfNecessary(mTransaction, mActivity, true /* isOnCreated */); assertTrue(container.hasActivity(mActivity.getActivityToken())); verify(mTransaction).startActivityInTaskFragment(any(), any(), eq(PLACEHOLDER_INTENT), any()); } @Test public void testLaunchPlaceholderIfNecessary_embeddedBelowTaskFragment() { // Do not launch placeholder for invisible activity below the top TaskFragment. setupPlaceholderRule(mActivity); mTransactionManager.startNewTransaction(); final TaskFragmentContainer bottomTf = mSplitController.newContainer(mActivity, TASK_ID); final TaskFragmentContainer topTf = mSplitController.newContainer(new Intent(), mActivity, TASK_ID); bottomTf.setInfo(mTransaction, createMockTaskFragmentInfo(bottomTf, mActivity, false /* isVisible */)); topTf.setInfo(mTransaction, createMockTaskFragmentInfo(topTf, createMockActivity())); assertFalse(bottomTf.isVisible()); mSplitController.launchPlaceholderIfNecessary(mTransaction, mActivity, true /* isOnCreated */); verify(mTransaction, never()).startActivityInTaskFragment(any(), any(), any(), any()); } @Test public void testLaunchPlaceholderIfNecessary_embeddedBelowTransparentTaskFragment() { // Launch placeholder for visible activity below the top TaskFragment. setupPlaceholderRule(mActivity); mTransactionManager.startNewTransaction(); final TaskFragmentContainer bottomTf = mSplitController.newContainer(mActivity, TASK_ID); final TaskFragmentContainer topTf = mSplitController.newContainer(new Intent(), mActivity, TASK_ID); bottomTf.setInfo(mTransaction, createMockTaskFragmentInfo(bottomTf, mActivity, true /* isVisible */)); topTf.setInfo(mTransaction, createMockTaskFragmentInfo(topTf, createMockActivity())); assertTrue(bottomTf.isVisible()); mSplitController.launchPlaceholderIfNecessary(mTransaction, mActivity, true /* isOnCreated */); verify(mTransaction).startActivityInTaskFragment(any(), any(), any(), any()); } /** Creates a mock activity in the organizer process. */ private Activity createMockActivity() { return createMockActivity(TASK_ID); Loading Loading
libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java +37 −17 Original line number Diff line number Diff line Loading @@ -431,12 +431,8 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen if (container != null) { // Cleanup if the TaskFragment vanished is not requested by the organizer. removeContainer(container); // Make sure the top container is updated. final TaskFragmentContainer newTopContainer = getTopActiveContainer( container.getTaskId()); if (newTopContainer != null) { updateContainer(wct, newTopContainer); } // Make sure the containers in the Task are up-to-date. updateContainersInTaskIfVisible(wct, container.getTaskId()); } cleanupTaskFragment(taskFragmentInfo.getFragmentToken()); } Loading Loading @@ -476,6 +472,13 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen updateContainersInTask(wct, taskContainer); } void updateContainersInTaskIfVisible(@NonNull WindowContainerTransaction wct, int taskId) { final TaskContainer taskContainer = getTaskContainer(taskId); if (taskContainer != null && taskContainer.isVisible()) { updateContainersInTask(wct, taskContainer); } } private void updateContainersInTask(@NonNull WindowContainerTransaction wct, @NonNull TaskContainer taskContainer) { // Update all TaskFragments in the Task. Make a copy of the list since some may be Loading Loading @@ -1328,9 +1331,6 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen void removeContainer(@NonNull TaskFragmentContainer container) { // Remove all split containers that included this one final TaskContainer taskContainer = container.getTaskContainer(); if (taskContainer == null) { return; } taskContainer.mContainers.remove(container); // Marked as a pending removal which will be removed after it is actually removed on the // server side (#onTaskFragmentVanished). Loading Loading @@ -1523,14 +1523,8 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen } final TaskFragmentContainer container = getContainerWithActivity(activity); // Don't launch placeholder if the container is occluded. if (container != null && container != getTopActiveContainer(container.getTaskId())) { return false; } final SplitContainer splitContainer = getActiveSplitForContainer(container); if (splitContainer != null && container.equals(splitContainer.getPrimaryContainer())) { // Don't launch placeholder in primary split container if (container != null && !allowLaunchPlaceholder(container)) { // We don't allow activity in this TaskFragment to launch placeholder. return false; } Loading Loading @@ -1558,6 +1552,32 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen return true; } /** 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()); if (container != topContainer) { // The container is not the top most. if (!container.isVisible()) { // In case the container is visible (the one on top may be transparent), we may // still want to launch placeholder even if it is not the top most. return false; } if (topContainer.isWaitingActivityAppear()) { // When the top container appeared info is not sent by the server yet, the visible // check above may not be reliable. return false; } } final SplitContainer splitContainer = getActiveSplitForContainer(container); if (splitContainer != null && container.equals(splitContainer.getPrimaryContainer())) { // Don't launch placeholder for primary split container. return false; } return true; } /** * Gets the activity options for starting the placeholder activity. In case the placeholder is * launched when the Task is in the background, we don't want to bring the Task to the front. Loading
libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java +2 −6 Original line number Diff line number Diff line Loading @@ -154,12 +154,8 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer { void cleanupContainer(@NonNull WindowContainerTransaction wct, @NonNull TaskFragmentContainer container, boolean shouldFinishDependent) { container.finish(shouldFinishDependent, this, wct, mController); final TaskFragmentContainer newTopContainer = mController.getTopActiveContainer( container.getTaskId()); if (newTopContainer != null) { mController.updateContainer(wct, newTopContainer); } // Make sure the containers in the Task is up-to-date. mController.updateContainersInTaskIfVisible(wct, container.getTaskId()); } /** Loading
libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java +5 −0 Original line number Diff line number Diff line Loading @@ -184,6 +184,11 @@ class TaskFragmentContainer { return allActivities; } /** Whether this TaskFragment is visible. */ boolean isVisible() { return mInfo != null && mInfo.isVisible(); } /** Whether the TaskFragment is in an intermediate state waiting for the server update.*/ boolean isInIntermediateState() { if (mInfo == null) { Loading
libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/EmbeddingTestUtils.java +7 −1 Original line number Diff line number Diff line Loading @@ -163,11 +163,17 @@ public class EmbeddingTestUtils { /** Creates a mock TaskFragmentInfo for the given TaskFragment. */ static TaskFragmentInfo createMockTaskFragmentInfo(@NonNull TaskFragmentContainer container, @NonNull Activity activity) { return createMockTaskFragmentInfo(container, activity, true /* isVisible */); } /** Creates a mock TaskFragmentInfo for the given TaskFragment. */ static TaskFragmentInfo createMockTaskFragmentInfo(@NonNull TaskFragmentContainer container, @NonNull Activity activity, boolean isVisible) { return new TaskFragmentInfo(container.getTaskFragmentToken(), mock(WindowContainerToken.class), new Configuration(), 1, true /* isVisible */, isVisible, Collections.singletonList(activity.getActivityToken()), new Point(), false /* isTaskClearedForReuse */, Loading
libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java +62 −0 Original line number Diff line number Diff line Loading @@ -1254,6 +1254,68 @@ public class SplitControllerTest { verify(mEmbeddingCallback).accept(any()); } @Test public void testLaunchPlaceholderIfNecessary_nonEmbeddedActivity() { // Launch placeholder for non embedded activity. setupPlaceholderRule(mActivity); mTransactionManager.startNewTransaction(); mSplitController.launchPlaceholderIfNecessary(mTransaction, mActivity, true /* isOnCreated */); verify(mTransaction).startActivityInTaskFragment(any(), any(), eq(PLACEHOLDER_INTENT), any()); } @Test public void testLaunchPlaceholderIfNecessary_embeddedInTopTaskFragment() { // Launch placeholder for activity in top TaskFragment. setupPlaceholderRule(mActivity); mTransactionManager.startNewTransaction(); final TaskFragmentContainer container = mSplitController.newContainer(mActivity, TASK_ID); mSplitController.launchPlaceholderIfNecessary(mTransaction, mActivity, true /* isOnCreated */); assertTrue(container.hasActivity(mActivity.getActivityToken())); verify(mTransaction).startActivityInTaskFragment(any(), any(), eq(PLACEHOLDER_INTENT), any()); } @Test public void testLaunchPlaceholderIfNecessary_embeddedBelowTaskFragment() { // Do not launch placeholder for invisible activity below the top TaskFragment. setupPlaceholderRule(mActivity); mTransactionManager.startNewTransaction(); final TaskFragmentContainer bottomTf = mSplitController.newContainer(mActivity, TASK_ID); final TaskFragmentContainer topTf = mSplitController.newContainer(new Intent(), mActivity, TASK_ID); bottomTf.setInfo(mTransaction, createMockTaskFragmentInfo(bottomTf, mActivity, false /* isVisible */)); topTf.setInfo(mTransaction, createMockTaskFragmentInfo(topTf, createMockActivity())); assertFalse(bottomTf.isVisible()); mSplitController.launchPlaceholderIfNecessary(mTransaction, mActivity, true /* isOnCreated */); verify(mTransaction, never()).startActivityInTaskFragment(any(), any(), any(), any()); } @Test public void testLaunchPlaceholderIfNecessary_embeddedBelowTransparentTaskFragment() { // Launch placeholder for visible activity below the top TaskFragment. setupPlaceholderRule(mActivity); mTransactionManager.startNewTransaction(); final TaskFragmentContainer bottomTf = mSplitController.newContainer(mActivity, TASK_ID); final TaskFragmentContainer topTf = mSplitController.newContainer(new Intent(), mActivity, TASK_ID); bottomTf.setInfo(mTransaction, createMockTaskFragmentInfo(bottomTf, mActivity, true /* isVisible */)); topTf.setInfo(mTransaction, createMockTaskFragmentInfo(topTf, createMockActivity())); assertTrue(bottomTf.isVisible()); mSplitController.launchPlaceholderIfNecessary(mTransaction, mActivity, true /* isOnCreated */); verify(mTransaction).startActivityInTaskFragment(any(), any(), any(), any()); } /** Creates a mock activity in the organizer process. */ private Activity createMockActivity() { return createMockActivity(TASK_ID); Loading