Allow launching placeholder when the TaskFragment is visible

Before, we only launch placeholder from the top most
TaskFragmentContainer. Now we also launch placeholder from the one
that is not the top most, but is visible. This can happen when the
top most is transparent.

Bug: 261550242
Test: atest WMJetpackUnitTests:SplitControllerTest
Merged-In: I6e3c4122d5d618b8a9ac054c19b0a3d19e7d3b20
Change-Id: I6e3c4122d5d618b8a9ac054c19b0a3d19e7d3b20
This commit is contained in:
Chris Li
2022-12-07 17:21:49 +08:00
parent e87c348fcd
commit ffecd01711
7 changed files with 139 additions and 25 deletions

View File

@@ -425,12 +425,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());
}
@@ -470,6 +466,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
@@ -1320,9 +1323,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).
@@ -1515,14 +1515,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;
}
@@ -1550,6 +1544,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.

View File

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

View File

@@ -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) {

View File

@@ -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 */,

View File

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

View File

@@ -500,6 +500,29 @@ public class TaskFragmentContainerTest {
assertEquals(2, taskContainer.indexOf(tf1));
}
@Test
public void testIsVisible() {
final TaskContainer taskContainer = createTestTaskContainer();
final TaskFragmentContainer container = new TaskFragmentContainer(
null /* pendingAppearedActivity */, new Intent(), taskContainer, mController,
null /* pairedPrimaryTaskFragment */);
// Not visible when there is not appeared.
assertFalse(container.isVisible());
// Respect info.isVisible.
TaskFragmentInfo info = createMockTaskFragmentInfo(container, mActivity,
true /* isVisible */);
container.setInfo(mTransaction, info);
assertTrue(container.isVisible());
info = createMockTaskFragmentInfo(container, mActivity, false /* isVisible */);
container.setInfo(mTransaction, info);
assertFalse(container.isVisible());
}
/** Creates a mock activity in the organizer process. */
private Activity createMockActivity() {
final Activity activity = mock(Activity.class);

View File

@@ -2552,7 +2552,7 @@ class TaskFragment extends WindowContainer<WindowContainer> {
mRemoteToken.toWindowContainerToken(),
getConfiguration(),
getNonFinishingActivityCount(),
isVisibleRequested(),
shouldBeVisible(null /* starting */),
childActivities,
positionInParent,
mClearedTaskForReuse,
@@ -2848,6 +2848,8 @@ class TaskFragment extends WindowContainer<WindowContainer> {
if (parentTf != null) {
parentTf.onActivityVisibleRequestedChanged();
}
// Send the info changed to update the TaskFragment visibility.
sendTaskFragmentInfoChanged();
}
@Nullable