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:
@@ -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.
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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 */,
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user