Merge "Remove pendingAppearedActivity when activity is destroyed" into tm-dev

This commit is contained in:
Chris Li
2022-05-10 02:12:08 +00:00
committed by Android (Google) Code Review
8 changed files with 97 additions and 55 deletions

View File

@@ -422,6 +422,18 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
launchPlaceholderIfNecessary(activity); launchPlaceholderIfNecessary(activity);
} }
@VisibleForTesting
void onActivityDestroyed(@NonNull Activity activity) {
// Remove any pending appeared activity, as the server won't send finished activity to the
// organizer.
for (int i = mTaskContainers.size() - 1; i >= 0; i--) {
mTaskContainers.valueAt(i).cleanupPendingAppearedActivity(activity);
}
// We didn't trigger the callback if there were any pending appeared activities, so check
// again after the pending is removed.
updateCallbackIfNecessary();
}
/** /**
* Called when we have been waiting too long for the TaskFragment to become non-empty after * Called when we have been waiting too long for the TaskFragment to become non-empty after
* creation. * creation.
@@ -465,12 +477,12 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
if (activityInTask == null) { if (activityInTask == null) {
throw new IllegalArgumentException("activityInTask must not be null,"); throw new IllegalArgumentException("activityInTask must not be null,");
} }
final TaskFragmentContainer container = new TaskFragmentContainer(activity, taskId, this);
if (!mTaskContainers.contains(taskId)) { if (!mTaskContainers.contains(taskId)) {
mTaskContainers.put(taskId, new TaskContainer(taskId)); mTaskContainers.put(taskId, new TaskContainer(taskId));
} }
final TaskContainer taskContainer = mTaskContainers.get(taskId); final TaskContainer taskContainer = mTaskContainers.get(taskId);
taskContainer.mContainers.add(container); final TaskFragmentContainer container = new TaskFragmentContainer(activity, taskContainer,
this);
if (!taskContainer.isTaskBoundsInitialized()) { if (!taskContainer.isTaskBoundsInitialized()) {
// Get the initial bounds before the TaskFragment has appeared. // Get the initial bounds before the TaskFragment has appeared.
final Rect taskBounds = SplitPresenter.getTaskBoundsFromActivity(activityInTask); final Rect taskBounds = SplitPresenter.getTaskBoundsFromActivity(activityInTask);
@@ -500,14 +512,13 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
if (splitRule instanceof SplitPairRule && ((SplitPairRule) splitRule).shouldClearTop()) { if (splitRule instanceof SplitPairRule && ((SplitPairRule) splitRule).shouldClearTop()) {
removeExistingSecondaryContainers(wct, primaryContainer); removeExistingSecondaryContainers(wct, primaryContainer);
} }
mTaskContainers.get(primaryContainer.getTaskId()).mSplitContainers.add(splitContainer); primaryContainer.getTaskContainer().mSplitContainers.add(splitContainer);
} }
/** Cleanups all the dependencies when the TaskFragment is entering PIP. */ /** Cleanups all the dependencies when the TaskFragment is entering PIP. */
private void cleanupForEnterPip(@NonNull WindowContainerTransaction wct, private void cleanupForEnterPip(@NonNull WindowContainerTransaction wct,
@NonNull TaskFragmentContainer container) { @NonNull TaskFragmentContainer container) {
final int taskId = container.getTaskId(); final TaskContainer taskContainer = container.getTaskContainer();
final TaskContainer taskContainer = mTaskContainers.get(taskId);
if (taskContainer == null) { if (taskContainer == null) {
return; return;
} }
@@ -545,8 +556,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
*/ */
void removeContainer(@NonNull TaskFragmentContainer container) { void removeContainer(@NonNull TaskFragmentContainer container) {
// Remove all split containers that included this one // Remove all split containers that included this one
final int taskId = container.getTaskId(); final TaskContainer taskContainer = container.getTaskContainer();
final TaskContainer taskContainer = mTaskContainers.get(taskId);
if (taskContainer == null) { if (taskContainer == null) {
return; return;
} }
@@ -637,8 +647,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
if (splitContainer == null) { if (splitContainer == null) {
return; return;
} }
final List<SplitContainer> splitContainers = mTaskContainers.get(container.getTaskId()) final List<SplitContainer> splitContainers = container.getTaskContainer().mSplitContainers;
.mSplitContainers;
if (splitContainer != splitContainers.get(splitContainers.size() - 1)) { if (splitContainer != splitContainers.get(splitContainers.size() - 1)) {
// Skip position update - it isn't the topmost split. // Skip position update - it isn't the topmost split.
return; return;
@@ -660,8 +669,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
*/ */
@Nullable @Nullable
private SplitContainer getActiveSplitForContainer(@NonNull TaskFragmentContainer container) { private SplitContainer getActiveSplitForContainer(@NonNull TaskFragmentContainer container) {
final List<SplitContainer> splitContainers = mTaskContainers.get(container.getTaskId()) final List<SplitContainer> splitContainers = container.getTaskContainer().mSplitContainers;
.mSplitContainers;
if (splitContainers.isEmpty()) { if (splitContainers.isEmpty()) {
return null; return null;
} }
@@ -683,11 +691,8 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
private SplitContainer getActiveSplitForContainers( private SplitContainer getActiveSplitForContainers(
@NonNull TaskFragmentContainer firstContainer, @NonNull TaskFragmentContainer firstContainer,
@NonNull TaskFragmentContainer secondContainer) { @NonNull TaskFragmentContainer secondContainer) {
final List<SplitContainer> splitContainers = mTaskContainers.get(firstContainer.getTaskId()) final List<SplitContainer> splitContainers = firstContainer.getTaskContainer()
.mSplitContainers; .mSplitContainers;
if (splitContainers == null) {
return null;
}
for (int i = splitContainers.size() - 1; i >= 0; i--) { for (int i = splitContainers.size() - 1; i >= 0; i--) {
final SplitContainer splitContainer = splitContainers.get(i); final SplitContainer splitContainer = splitContainers.get(i);
final TaskFragmentContainer primary = splitContainer.getPrimaryContainer(); final TaskFragmentContainer primary = splitContainer.getPrimaryContainer();
@@ -856,11 +861,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
if (container == null) { if (container == null) {
return false; return false;
} }
final List<SplitContainer> splitContainers = mTaskContainers.get(container.getTaskId()) final List<SplitContainer> splitContainers = container.getTaskContainer().mSplitContainers;
.mSplitContainers;
if (splitContainers == null) {
return true;
}
for (SplitContainer splitContainer : splitContainers) { for (SplitContainer splitContainer : splitContainers) {
if (container.equals(splitContainer.getPrimaryContainer()) if (container.equals(splitContainer.getPrimaryContainer())
|| container.equals(splitContainer.getSecondaryContainer())) { || container.equals(splitContainer.getSecondaryContainer())) {
@@ -1046,6 +1047,11 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
public void onActivityConfigurationChanged(Activity activity) { public void onActivityConfigurationChanged(Activity activity) {
SplitController.this.onActivityConfigurationChanged(activity); SplitController.this.onActivityConfigurationChanged(activity);
} }
@Override
public void onActivityPostDestroyed(Activity activity) {
SplitController.this.onActivityDestroyed(activity);
}
} }
/** Executor that posts on the main application thread. */ /** Executor that posts on the main application thread. */

View File

@@ -291,8 +291,7 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
// When placeholder is shown in split, we should keep the focus on the primary. // When placeholder is shown in split, we should keep the focus on the primary.
wct.requestFocusOnTaskFragment(primaryContainer.getTaskFragmentToken()); wct.requestFocusOnTaskFragment(primaryContainer.getTaskFragmentToken());
} }
final TaskContainer taskContainer = mController.getTaskContainer( final TaskContainer taskContainer = updatedContainer.getTaskContainer();
updatedContainer.getTaskId());
final int windowingMode = taskContainer.getWindowingModeForSplitTaskFragment( final int windowingMode = taskContainer.getWindowingModeForSplitTaskFragment(
primaryRectBounds); primaryRectBounds);
updateTaskFragmentWindowingModeIfRegistered(wct, primaryContainer, windowingMode); updateTaskFragmentWindowingModeIfRegistered(wct, primaryContainer, windowingMode);
@@ -456,12 +455,7 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
@NonNull @NonNull
Rect getParentContainerBounds(@NonNull TaskFragmentContainer container) { Rect getParentContainerBounds(@NonNull TaskFragmentContainer container) {
final int taskId = container.getTaskId(); return container.getTaskContainer().getTaskBounds();
final TaskContainer taskContainer = mController.getTaskContainer(taskId);
if (taskContainer == null) {
throw new IllegalStateException("Can't find TaskContainer taskId=" + taskId);
}
return taskContainer.getTaskBounds();
} }
@NonNull @NonNull

View File

@@ -16,12 +16,14 @@
package androidx.window.extensions.embedding; package androidx.window.extensions.embedding;
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import android.annotation.NonNull; import android.annotation.NonNull;
import android.annotation.Nullable; import android.annotation.Nullable;
import android.app.Activity;
import android.app.WindowConfiguration; import android.app.WindowConfiguration;
import android.app.WindowConfiguration.WindowingMode; import android.app.WindowConfiguration.WindowingMode;
import android.graphics.Rect; import android.graphics.Rect;
@@ -62,6 +64,9 @@ class TaskContainer {
final Set<IBinder> mFinishedContainer = new ArraySet<>(); final Set<IBinder> mFinishedContainer = new ArraySet<>();
TaskContainer(int taskId) { TaskContainer(int taskId) {
if (taskId == INVALID_TASK_ID) {
throw new IllegalArgumentException("Invalid Task id");
}
mTaskId = taskId; mTaskId = taskId;
} }
@@ -130,4 +135,11 @@ class TaskContainer {
boolean isEmpty() { boolean isEmpty() {
return mContainers.isEmpty() && mFinishedContainer.isEmpty(); return mContainers.isEmpty() && mFinishedContainer.isEmpty();
} }
/** Removes the pending appeared activity from all TaskFragments in this Task. */
void cleanupPendingAppearedActivity(@NonNull Activity pendingAppearedActivity) {
for (TaskFragmentContainer container : mContainers) {
container.removePendingAppearedActivity(pendingAppearedActivity);
}
}
} }

View File

@@ -16,7 +16,6 @@
package androidx.window.extensions.embedding; package androidx.window.extensions.embedding;
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import android.annotation.NonNull; import android.annotation.NonNull;
@@ -52,8 +51,9 @@ class TaskFragmentContainer {
@NonNull @NonNull
private final IBinder mToken; private final IBinder mToken;
/** Parent leaf Task id. */ /** Parent leaf Task. */
private final int mTaskId; @NonNull
private final TaskContainer mTaskContainer;
/** /**
* Server-provided task fragment information. * Server-provided task fragment information.
@@ -100,14 +100,12 @@ class TaskFragmentContainer {
* Creates a container with an existing activity that will be re-parented to it in a window * Creates a container with an existing activity that will be re-parented to it in a window
* container transaction. * container transaction.
*/ */
TaskFragmentContainer(@Nullable Activity activity, int taskId, TaskFragmentContainer(@Nullable Activity activity, @NonNull TaskContainer taskContainer,
@NonNull SplitController controller) { @NonNull SplitController controller) {
mController = controller; mController = controller;
mToken = new Binder("TaskFragmentContainer"); mToken = new Binder("TaskFragmentContainer");
if (taskId == INVALID_TASK_ID) { mTaskContainer = taskContainer;
throw new IllegalArgumentException("Invalid Task id"); taskContainer.mContainers.add(this);
}
mTaskId = taskId;
if (activity != null) { if (activity != null) {
addPendingAppearedActivity(activity); addPendingAppearedActivity(activity);
} }
@@ -162,9 +160,18 @@ class TaskFragmentContainer {
} }
void addPendingAppearedActivity(@NonNull Activity pendingAppearedActivity) { void addPendingAppearedActivity(@NonNull Activity pendingAppearedActivity) {
if (hasActivity(pendingAppearedActivity.getActivityToken())) {
return;
}
// Remove the pending activity from other TaskFragments.
mTaskContainer.cleanupPendingAppearedActivity(pendingAppearedActivity);
mPendingAppearedActivities.add(pendingAppearedActivity); mPendingAppearedActivities.add(pendingAppearedActivity);
} }
void removePendingAppearedActivity(@NonNull Activity pendingAppearedActivity) {
mPendingAppearedActivities.remove(pendingAppearedActivity);
}
boolean hasActivity(@NonNull IBinder token) { boolean hasActivity(@NonNull IBinder token) {
if (mInfo != null && mInfo.getActivities().contains(token)) { if (mInfo != null && mInfo.getActivities().contains(token)) {
return true; return true;
@@ -376,7 +383,13 @@ class TaskFragmentContainer {
/** Gets the parent leaf Task id. */ /** Gets the parent leaf Task id. */
int getTaskId() { int getTaskId() {
return mTaskId; return mTaskContainer.getTaskId();
}
/** Gets the parent Task. */
@NonNull
TaskContainer getTaskContainer() {
return mTaskContainer;
} }
@Override @Override
@@ -392,6 +405,7 @@ class TaskFragmentContainer {
*/ */
private String toString(boolean includeContainersToFinishOnExit) { private String toString(boolean includeContainersToFinishOnExit) {
return "TaskFragmentContainer{" return "TaskFragmentContainer{"
+ " parentTaskId=" + getTaskId()
+ " token=" + mToken + " token=" + mToken
+ " topNonFinishingActivity=" + getTopNonFinishingActivity() + " topNonFinishingActivity=" + getTopNonFinishingActivity()
+ " runningActivityCount=" + getRunningActivityCount() + " runningActivityCount=" + getRunningActivityCount()

View File

@@ -113,8 +113,9 @@ public class JetpackTaskFragmentOrganizerTest {
@Test @Test
public void testExpandTaskFragment() { public void testExpandTaskFragment() {
final TaskContainer taskContainer = new TaskContainer(TASK_ID);
final TaskFragmentContainer container = new TaskFragmentContainer(null /* activity */, final TaskFragmentContainer container = new TaskFragmentContainer(null /* activity */,
TASK_ID, mSplitController); taskContainer, mSplitController);
final TaskFragmentInfo info = createMockInfo(container); final TaskFragmentInfo info = createMockInfo(container);
mOrganizer.mFragmentInfos.put(container.getTaskFragmentToken(), info); mOrganizer.mFragmentInfos.put(container.getTaskFragmentToken(), info);
container.setInfo(info); container.setInfo(info);

View File

@@ -22,8 +22,10 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.google.common.truth.Truth.assertWithMessage; import static com.google.common.truth.Truth.assertWithMessage;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doNothing;
@@ -35,6 +37,7 @@ import android.app.Activity;
import android.content.res.Configuration; import android.content.res.Configuration;
import android.content.res.Resources; import android.content.res.Resources;
import android.graphics.Rect; import android.graphics.Rect;
import android.os.Binder;
import android.os.Handler; import android.os.Handler;
import android.platform.test.annotations.Presubmit; import android.platform.test.annotations.Presubmit;
import android.window.TaskFragmentInfo; import android.window.TaskFragmentInfo;
@@ -96,20 +99,18 @@ public class SplitControllerTest {
@Test @Test
public void testGetTopActiveContainer() { public void testGetTopActiveContainer() {
TaskContainer taskContainer = new TaskContainer(TASK_ID); final TaskContainer taskContainer = new TaskContainer(TASK_ID);
// tf1 has no running activity so is not active.
final TaskFragmentContainer tf1 = new TaskFragmentContainer(null /* activity */,
taskContainer, mSplitController);
// tf2 has running activity so is active.
final TaskFragmentContainer tf2 = mock(TaskFragmentContainer.class);
doReturn(1).when(tf2).getRunningActivityCount();
taskContainer.mContainers.add(tf2);
// tf3 is finished so is not active. // tf3 is finished so is not active.
TaskFragmentContainer tf3 = mock(TaskFragmentContainer.class); final TaskFragmentContainer tf3 = mock(TaskFragmentContainer.class);
doReturn(true).when(tf3).isFinished(); doReturn(true).when(tf3).isFinished();
doReturn(false).when(tf3).isWaitingActivityAppear(); doReturn(false).when(tf3).isWaitingActivityAppear();
// tf2 has running activity so is active.
TaskFragmentContainer tf2 = mock(TaskFragmentContainer.class);
doReturn(1).when(tf2).getRunningActivityCount();
// tf1 has no running activity so is not active.
TaskFragmentContainer tf1 = new TaskFragmentContainer(null /* activity */, TASK_ID,
mSplitController);
taskContainer.mContainers.add(tf1);
taskContainer.mContainers.add(tf2);
taskContainer.mContainers.add(tf3); taskContainer.mContainers.add(tf3);
mSplitController.mTaskContainers.put(TASK_ID, taskContainer); mSplitController.mTaskContainers.put(TASK_ID, taskContainer);
@@ -163,6 +164,18 @@ public class SplitControllerTest {
verify(mSplitPresenter).cleanupContainer(tf, false /* shouldFinishDependent */); verify(mSplitPresenter).cleanupContainer(tf, false /* shouldFinishDependent */);
} }
@Test
public void testOnActivityDestroyed() {
doReturn(new Binder()).when(mActivity).getActivityToken();
final TaskFragmentContainer tf = mSplitController.newContainer(mActivity, TASK_ID);
assertTrue(tf.hasActivity(mActivity.getActivityToken()));
mSplitController.onActivityDestroyed(mActivity);
assertFalse(tf.hasActivity(mActivity.getActivityToken()));
}
@Test @Test
public void testNewContainer() { public void testNewContainer() {
// Must pass in a valid activity. // Must pass in a valid activity.

View File

@@ -137,9 +137,8 @@ public class TaskContainerTest {
assertTrue(taskContainer.isEmpty()); assertTrue(taskContainer.isEmpty());
final TaskFragmentContainer tf = new TaskFragmentContainer(null /* activity */, TASK_ID, final TaskFragmentContainer tf = new TaskFragmentContainer(null /* activity */,
mController); taskContainer, mController);
taskContainer.mContainers.add(tf);
assertFalse(taskContainer.isEmpty()); assertFalse(taskContainer.isEmpty());

View File

@@ -76,7 +76,8 @@ public class TaskFragmentContainerTest {
@Test @Test
public void testFinish() { public void testFinish() {
final TaskFragmentContainer container = new TaskFragmentContainer(mActivity, TASK_ID, final TaskContainer taskContainer = new TaskContainer(TASK_ID);
final TaskFragmentContainer container = new TaskFragmentContainer(mActivity, taskContainer,
mController); mController);
final WindowContainerTransaction wct = new WindowContainerTransaction(); final WindowContainerTransaction wct = new WindowContainerTransaction();
@@ -107,8 +108,9 @@ public class TaskFragmentContainerTest {
@Test @Test
public void testIsWaitingActivityAppear() { public void testIsWaitingActivityAppear() {
final TaskContainer taskContainer = new TaskContainer(TASK_ID);
final TaskFragmentContainer container = new TaskFragmentContainer(null /* activity */, final TaskFragmentContainer container = new TaskFragmentContainer(null /* activity */,
TASK_ID, mController); taskContainer, mController);
assertTrue(container.isWaitingActivityAppear()); assertTrue(container.isWaitingActivityAppear());
@@ -127,8 +129,9 @@ public class TaskFragmentContainerTest {
@Test @Test
public void testAppearEmptyTimeout() { public void testAppearEmptyTimeout() {
final TaskContainer taskContainer = new TaskContainer(TASK_ID);
final TaskFragmentContainer container = new TaskFragmentContainer(null /* activity */, final TaskFragmentContainer container = new TaskFragmentContainer(null /* activity */,
TASK_ID, mController); taskContainer, mController);
assertNull(container.mAppearEmptyTimeout); assertNull(container.mAppearEmptyTimeout);