Merge "Fix ConcurrentModificationException on finishing TaskFragment" into tm-dev am: 0d7ece81e8
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/18615113 Change-Id: Id15081e917190d0a93ae8870c4a9d623c95e5023 Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
@@ -280,6 +280,9 @@ class TaskFragmentContainer {
|
|||||||
* Adds a container that should be finished when this container is finished.
|
* Adds a container that should be finished when this container is finished.
|
||||||
*/
|
*/
|
||||||
void addContainerToFinishOnExit(@NonNull TaskFragmentContainer containerToFinish) {
|
void addContainerToFinishOnExit(@NonNull TaskFragmentContainer containerToFinish) {
|
||||||
|
if (mIsFinished) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
mContainersToFinishOnExit.add(containerToFinish);
|
mContainersToFinishOnExit.add(containerToFinish);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -287,6 +290,9 @@ class TaskFragmentContainer {
|
|||||||
* Removes a container that should be finished when this container is finished.
|
* Removes a container that should be finished when this container is finished.
|
||||||
*/
|
*/
|
||||||
void removeContainerToFinishOnExit(@NonNull TaskFragmentContainer containerToRemove) {
|
void removeContainerToFinishOnExit(@NonNull TaskFragmentContainer containerToRemove) {
|
||||||
|
if (mIsFinished) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
mContainersToFinishOnExit.remove(containerToRemove);
|
mContainersToFinishOnExit.remove(containerToRemove);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -294,6 +300,9 @@ class TaskFragmentContainer {
|
|||||||
* Adds an activity that should be finished when this container is finished.
|
* Adds an activity that should be finished when this container is finished.
|
||||||
*/
|
*/
|
||||||
void addActivityToFinishOnExit(@NonNull Activity activityToFinish) {
|
void addActivityToFinishOnExit(@NonNull Activity activityToFinish) {
|
||||||
|
if (mIsFinished) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
mActivitiesToFinishOnExit.add(activityToFinish);
|
mActivitiesToFinishOnExit.add(activityToFinish);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -301,11 +310,17 @@ class TaskFragmentContainer {
|
|||||||
* Removes an activity that should be finished when this container is finished.
|
* Removes an activity that should be finished when this container is finished.
|
||||||
*/
|
*/
|
||||||
void removeActivityToFinishOnExit(@NonNull Activity activityToRemove) {
|
void removeActivityToFinishOnExit(@NonNull Activity activityToRemove) {
|
||||||
|
if (mIsFinished) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
mActivitiesToFinishOnExit.remove(activityToRemove);
|
mActivitiesToFinishOnExit.remove(activityToRemove);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Removes all dependencies that should be finished when this container is finished. */
|
/** Removes all dependencies that should be finished when this container is finished. */
|
||||||
void resetDependencies() {
|
void resetDependencies() {
|
||||||
|
if (mIsFinished) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
mContainersToFinishOnExit.clear();
|
mContainersToFinishOnExit.clear();
|
||||||
mActivitiesToFinishOnExit.clear();
|
mActivitiesToFinishOnExit.clear();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,9 @@ package androidx.window.extensions.embedding;
|
|||||||
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_UNDEFINED;
|
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
|
||||||
|
|
||||||
|
import static androidx.window.extensions.embedding.SplitRule.FINISH_ALWAYS;
|
||||||
|
import static androidx.window.extensions.embedding.SplitRule.FINISH_NEVER;
|
||||||
|
|
||||||
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
|
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
|
||||||
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
|
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
|
||||||
|
|
||||||
@@ -89,6 +92,10 @@ public class SplitControllerTest {
|
|||||||
private static final Intent PLACEHOLDER_INTENT = new Intent().setComponent(
|
private static final Intent PLACEHOLDER_INTENT = new Intent().setComponent(
|
||||||
new ComponentName("test", "placeholder"));
|
new ComponentName("test", "placeholder"));
|
||||||
|
|
||||||
|
/** Default finish behavior in Jetpack. */
|
||||||
|
private static final int DEFAULT_FINISH_PRIMARY_WITH_SECONDARY = FINISH_NEVER;
|
||||||
|
private static final int DEFAULT_FINISH_SECONDARY_WITH_PRIMARY = FINISH_ALWAYS;
|
||||||
|
|
||||||
private Activity mActivity;
|
private Activity mActivity;
|
||||||
@Mock
|
@Mock
|
||||||
private Resources mActivityResources;
|
private Resources mActivityResources;
|
||||||
@@ -788,6 +795,38 @@ public class SplitControllerTest {
|
|||||||
assertTrue(activityOptions.getAvoidMoveToFront());
|
assertTrue(activityOptions.getAvoidMoveToFront());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFinishTwoSplitThatShouldFinishTogether() {
|
||||||
|
// Setup two split pairs that should finish each other when finishing one.
|
||||||
|
final Activity secondaryActivity0 = createMockActivity();
|
||||||
|
final Activity secondaryActivity1 = createMockActivity();
|
||||||
|
final TaskFragmentContainer primaryContainer = createMockTaskFragmentContainer(mActivity);
|
||||||
|
final TaskFragmentContainer secondaryContainer0 = createMockTaskFragmentContainer(
|
||||||
|
secondaryActivity0);
|
||||||
|
final TaskFragmentContainer secondaryContainer1 = createMockTaskFragmentContainer(
|
||||||
|
secondaryActivity1);
|
||||||
|
final TaskContainer taskContainer = mSplitController.getTaskContainer(TASK_ID);
|
||||||
|
final SplitRule rule0 = createSplitRule(mActivity, secondaryActivity0, FINISH_ALWAYS,
|
||||||
|
FINISH_ALWAYS, false /* clearTop */);
|
||||||
|
final SplitRule rule1 = createSplitRule(mActivity, secondaryActivity1, FINISH_ALWAYS,
|
||||||
|
FINISH_ALWAYS, false /* clearTop */);
|
||||||
|
registerSplitPair(primaryContainer, secondaryContainer0, rule0);
|
||||||
|
registerSplitPair(primaryContainer, secondaryContainer1, rule1);
|
||||||
|
|
||||||
|
primaryContainer.finish(true /* shouldFinishDependent */, mSplitPresenter,
|
||||||
|
mTransaction, mSplitController);
|
||||||
|
|
||||||
|
// All containers and activities should be finished based on the FINISH_ALWAYS behavior.
|
||||||
|
assertTrue(primaryContainer.isFinished());
|
||||||
|
assertTrue(secondaryContainer0.isFinished());
|
||||||
|
assertTrue(secondaryContainer1.isFinished());
|
||||||
|
verify(mActivity).finish();
|
||||||
|
verify(secondaryActivity0).finish();
|
||||||
|
verify(secondaryActivity1).finish();
|
||||||
|
assertTrue(taskContainer.mContainers.isEmpty());
|
||||||
|
assertTrue(taskContainer.mSplitContainers.isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
/** Creates a mock activity in the organizer process. */
|
/** Creates a mock activity in the organizer process. */
|
||||||
private Activity createMockActivity() {
|
private Activity createMockActivity() {
|
||||||
final Activity activity = mock(Activity.class);
|
final Activity activity = mock(Activity.class);
|
||||||
@@ -863,7 +902,9 @@ public class SplitControllerTest {
|
|||||||
/** Setups a rule to always split the given activities. */
|
/** Setups a rule to always split the given activities. */
|
||||||
private void setupSplitRule(@NonNull Activity primaryActivity,
|
private void setupSplitRule(@NonNull Activity primaryActivity,
|
||||||
@NonNull Activity secondaryActivity) {
|
@NonNull Activity secondaryActivity) {
|
||||||
final SplitRule splitRule = createSplitRule(primaryActivity, secondaryActivity);
|
final SplitRule splitRule = createSplitRule(primaryActivity, secondaryActivity,
|
||||||
|
DEFAULT_FINISH_PRIMARY_WITH_SECONDARY, DEFAULT_FINISH_SECONDARY_WITH_PRIMARY,
|
||||||
|
true /* clearTop */);
|
||||||
mSplitController.setEmbeddingRules(Collections.singleton(splitRule));
|
mSplitController.setEmbeddingRules(Collections.singleton(splitRule));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -883,29 +924,44 @@ public class SplitControllerTest {
|
|||||||
/** Creates a rule to always split the given activities. */
|
/** Creates a rule to always split the given activities. */
|
||||||
private SplitRule createSplitRule(@NonNull Activity primaryActivity,
|
private SplitRule createSplitRule(@NonNull Activity primaryActivity,
|
||||||
@NonNull Activity secondaryActivity) {
|
@NonNull Activity secondaryActivity) {
|
||||||
|
return createSplitRule(primaryActivity, secondaryActivity,
|
||||||
|
DEFAULT_FINISH_PRIMARY_WITH_SECONDARY, DEFAULT_FINISH_SECONDARY_WITH_PRIMARY,
|
||||||
|
true /* clearTop */);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Creates a rule to always split the given activities with the given finish behaviors. */
|
||||||
|
private SplitRule createSplitRule(@NonNull Activity primaryActivity,
|
||||||
|
@NonNull Activity secondaryActivity, int finishPrimaryWithSecondary,
|
||||||
|
int finishSecondaryWithPrimary, boolean clearTop) {
|
||||||
final Pair<Activity, Activity> targetPair = new Pair<>(primaryActivity, secondaryActivity);
|
final Pair<Activity, Activity> targetPair = new Pair<>(primaryActivity, secondaryActivity);
|
||||||
return new SplitPairRule.Builder(
|
return new SplitPairRule.Builder(
|
||||||
targetPair::equals,
|
targetPair::equals,
|
||||||
activityIntentPair -> false,
|
activityIntentPair -> false,
|
||||||
w -> true)
|
w -> true)
|
||||||
.setSplitRatio(SPLIT_RATIO)
|
.setSplitRatio(SPLIT_RATIO)
|
||||||
.setShouldClearTop(true)
|
.setFinishPrimaryWithSecondary(finishPrimaryWithSecondary)
|
||||||
|
.setFinishSecondaryWithPrimary(finishSecondaryWithPrimary)
|
||||||
|
.setShouldClearTop(clearTop)
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Adds a pair of TaskFragments as split for the given activities. */
|
/** Adds a pair of TaskFragments as split for the given activities. */
|
||||||
private void addSplitTaskFragments(@NonNull Activity primaryActivity,
|
private void addSplitTaskFragments(@NonNull Activity primaryActivity,
|
||||||
@NonNull Activity secondaryActivity) {
|
@NonNull Activity secondaryActivity) {
|
||||||
final TaskFragmentContainer primaryContainer = createMockTaskFragmentContainer(
|
registerSplitPair(createMockTaskFragmentContainer(primaryActivity),
|
||||||
primaryActivity);
|
createMockTaskFragmentContainer(secondaryActivity),
|
||||||
final TaskFragmentContainer secondaryContainer = createMockTaskFragmentContainer(
|
createSplitRule(primaryActivity, secondaryActivity));
|
||||||
secondaryActivity);
|
}
|
||||||
|
|
||||||
|
/** Registers the two given TaskFragments as split pair. */
|
||||||
|
private void registerSplitPair(@NonNull TaskFragmentContainer primaryContainer,
|
||||||
|
@NonNull TaskFragmentContainer secondaryContainer, @NonNull SplitRule rule) {
|
||||||
mSplitController.registerSplit(
|
mSplitController.registerSplit(
|
||||||
mock(WindowContainerTransaction.class),
|
mock(WindowContainerTransaction.class),
|
||||||
primaryContainer,
|
primaryContainer,
|
||||||
primaryActivity,
|
primaryContainer.getTopNonFinishingActivity(),
|
||||||
secondaryContainer,
|
secondaryContainer,
|
||||||
createSplitRule(primaryActivity, secondaryActivity));
|
rule);
|
||||||
|
|
||||||
// We need to set those in case we are not respecting clear top.
|
// We need to set those in case we are not respecting clear top.
|
||||||
// TODO(b/231845476) we should always respect clearTop.
|
// TODO(b/231845476) we should always respect clearTop.
|
||||||
|
|||||||
Reference in New Issue
Block a user