[6/n] Pin ActivityStack

Avoid matching split-rules once TF is pinned, which including:

- Do not resolve split-rule while starting activities from the pinned
  TaskFragmentContainer.

- Do not remove the existing secondary container (clear-top) if the
  container is pinned because the pinned container won't be occluded
  by the new split.

- Do not launch placeholder if the container is pinned

Bug: 208573140
Test: atest PinActivityStackTests
Test: atest SplitControllerTest
Change-Id: I7a2d5d77ab87acefd3c2fd9ba11852e150adc59d
This commit is contained in:
Louis Chang
2023-04-19 14:21:26 +00:00
parent c7e45a88cc
commit 79337e5866
3 changed files with 92 additions and 12 deletions

View File

@@ -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:
@@ -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;
}
@@ -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.
@@ -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);
}
@@ -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.
@@ -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;
}
@@ -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;
}
@@ -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;

View File

@@ -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;
}
@@ -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();

View File

@@ -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);
@@ -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.