diff options
10 files changed, 117 insertions, 11 deletions
diff --git a/core/java/android/window/TaskFragmentParentInfo.java b/core/java/android/window/TaskFragmentParentInfo.java index 841354a92192..e6eeca4b4801 100644 --- a/core/java/android/window/TaskFragmentParentInfo.java +++ b/core/java/android/window/TaskFragmentParentInfo.java @@ -35,17 +35,21 @@ public class TaskFragmentParentInfo implements Parcelable { private final boolean mVisible; + private final boolean mHasDirectActivity; + public TaskFragmentParentInfo(@NonNull Configuration configuration, int displayId, - boolean visible) { + boolean visible, boolean hasDirectActivity) { mConfiguration.setTo(configuration); mDisplayId = displayId; mVisible = visible; + mHasDirectActivity = hasDirectActivity; } public TaskFragmentParentInfo(@NonNull TaskFragmentParentInfo info) { mConfiguration.setTo(info.getConfiguration()); mDisplayId = info.mDisplayId; mVisible = info.mVisible; + mHasDirectActivity = info.mHasDirectActivity; } /** The {@link Configuration} of the parent Task */ @@ -68,6 +72,14 @@ public class TaskFragmentParentInfo implements Parcelable { } /** + * Whether the parent Task has any direct child activity, which is not embedded in any + * TaskFragment, or not + */ + public boolean hasDirectActivity() { + return mHasDirectActivity; + } + + /** * Returns {@code true} if the parameters which are important for task fragment * organizers are equal between this {@link TaskFragmentParentInfo} and {@code that}. * Note that this method is usually called with @@ -80,7 +92,7 @@ public class TaskFragmentParentInfo implements Parcelable { return false; } return getWindowingMode() == that.getWindowingMode() && mDisplayId == that.mDisplayId - && mVisible == that.mVisible; + && mVisible == that.mVisible && mHasDirectActivity == that.mHasDirectActivity; } @WindowConfiguration.WindowingMode @@ -94,6 +106,7 @@ public class TaskFragmentParentInfo implements Parcelable { + "config=" + mConfiguration + ", displayId=" + mDisplayId + ", visible=" + mVisible + + ", hasDirectActivity=" + mHasDirectActivity + "}"; } @@ -114,7 +127,8 @@ public class TaskFragmentParentInfo implements Parcelable { final TaskFragmentParentInfo that = (TaskFragmentParentInfo) obj; return mConfiguration.equals(that.mConfiguration) && mDisplayId == that.mDisplayId - && mVisible == that.mVisible; + && mVisible == that.mVisible + && mHasDirectActivity == that.mHasDirectActivity; } @Override @@ -122,6 +136,7 @@ public class TaskFragmentParentInfo implements Parcelable { int result = mConfiguration.hashCode(); result = 31 * result + mDisplayId; result = 31 * result + (mVisible ? 1 : 0); + result = 31 * result + (mHasDirectActivity ? 1 : 0); return result; } @@ -130,12 +145,14 @@ public class TaskFragmentParentInfo implements Parcelable { mConfiguration.writeToParcel(dest, flags); dest.writeInt(mDisplayId); dest.writeBoolean(mVisible); + dest.writeBoolean(mHasDirectActivity); } private TaskFragmentParentInfo(Parcel in) { mConfiguration.readFromParcel(in); mDisplayId = in.readInt(); mVisible = in.readBoolean(); + mHasDirectActivity = in.readBoolean(); } public static final Creator<TaskFragmentParentInfo> CREATOR = diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java index 49606f0bf485..7743ad55debb 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java @@ -1763,6 +1763,15 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen return; } + if (container.isFinished()) { + return; + } + + if (container.isOverlay()) { + updateOverlayContainer(wct, container); + return; + } + if (launchPlaceholderIfNecessary(wct, container)) { // Placeholder was launched, the positions will be updated when the activity is added // to the secondary container. @@ -1783,6 +1792,25 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen updateSplitContainerIfNeeded(splitContainer, wct, null /* splitAttributes */); } + + @VisibleForTesting + // Suppress GuardedBy warning because lint ask to mark this method as + // @GuardedBy(mPresenter.mController.mLock), which is mLock itself + @SuppressWarnings("GuardedBy") + @GuardedBy("mLock") + void updateOverlayContainer(@NonNull WindowContainerTransaction wct, + @NonNull TaskFragmentContainer container) { + final TaskContainer taskContainer = container.getTaskContainer(); + // Dismiss the overlay container if it's the only container in the task and there's no + // direct activity in the parent task. + if (taskContainer.getTaskFragmentContainers().size() == 1 + && !taskContainer.hasDirectActivity()) { + container.finish(false /* shouldFinishDependent */, mPresenter, wct, this); + } + + // TODO(b/295805054): Add the logic to update overlay container + } + /** * Updates {@link SplitContainer} with the given {@link SplitAttributes} if the * {@link SplitContainer} is the top most and not finished. If passed {@link SplitAttributes} diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java index 9e533808ccc0..eeb3ccf0d4cb 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java @@ -75,6 +75,8 @@ class TaskContainer { private boolean mIsVisible; + private boolean mHasDirectActivity; + /** * TaskFragments that the organizer has requested to be closed. They should be removed when * the organizer receives @@ -102,8 +104,9 @@ class TaskContainer { mConfiguration = taskProperties.getConfiguration(); mDisplayId = taskProperties.getDisplayId(); // Note that it is always called when there's a new Activity is started, which implies - // the host task is visible. + // the host task is visible and has an activity in the task. mIsVisible = true; + mHasDirectActivity = true; } int getTaskId() { @@ -118,6 +121,10 @@ class TaskContainer { return mIsVisible; } + boolean hasDirectActivity() { + return mHasDirectActivity; + } + @NonNull TaskProperties getTaskProperties() { return new TaskProperties(mDisplayId, mConfiguration); @@ -127,6 +134,7 @@ class TaskContainer { mConfiguration.setTo(info.getConfiguration()); mDisplayId = info.getDisplayId(); mIsVisible = info.isVisible(); + mHasDirectActivity = info.hasDirectActivity(); } /** diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java index 405f0b2f51dc..e74d5fb4d0be 100644 --- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java +++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java @@ -58,6 +58,7 @@ import android.os.IBinder; import android.platform.test.annotations.Presubmit; import android.platform.test.flag.junit.SetFlagsRule; import android.window.TaskFragmentInfo; +import android.window.TaskFragmentParentInfo; import android.window.WindowContainerTransaction; import androidx.annotation.NonNull; @@ -413,6 +414,33 @@ public class OverlayPresentationTest { .isEqualTo(overlayContainer); } + @Test + public void testUpdateContainer_dontInvokeUpdateOverlayForNonOverlayContainer() { + TaskFragmentContainer taskFragmentContainer = createMockTaskFragmentContainer(mActivity); + + mSplitController.updateContainer(mTransaction, taskFragmentContainer); + verify(mSplitController, never()).updateOverlayContainer(any(), any()); + } + + @Test + public void testUpdateOverlayContainer_dismissOverlayIfNeeded() { + TaskFragmentContainer overlayContainer = createTestOverlayContainer(TASK_ID, "test"); + + mSplitController.updateOverlayContainer(mTransaction, overlayContainer); + + final TaskContainer taskContainer = overlayContainer.getTaskContainer(); + assertThat(taskContainer.getTaskFragmentContainers()).containsExactly(overlayContainer); + + taskContainer.updateTaskFragmentParentInfo(new TaskFragmentParentInfo(Configuration.EMPTY, + DEFAULT_DISPLAY, true /* visible */, false /* hasDirectActivity */)); + + mSplitController.updateOverlayContainer(mTransaction, overlayContainer); + + assertWithMessage("The overlay must be dismissed since there's no activity" + + " in the task and other taskFragment.") + .that(taskContainer.getTaskFragmentContainers()).isEmpty(); + } + /** * A simplified version of {@link SplitController.ActivityStartMonitor * #createOrUpdateOverlayTaskFragmentIfNeeded} diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java index 96839c57d745..02031a67e7e3 100644 --- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java +++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java @@ -1139,7 +1139,7 @@ public class SplitControllerTest { public void testOnTransactionReady_taskFragmentParentInfoChanged() { final TaskFragmentTransaction transaction = new TaskFragmentTransaction(); final TaskFragmentParentInfo parentInfo = new TaskFragmentParentInfo(Configuration.EMPTY, - DEFAULT_DISPLAY, true); + DEFAULT_DISPLAY, true /* visible */, false /* hasDirectActivity */); transaction.addChange(new TaskFragmentTransaction.Change( TYPE_TASK_FRAGMENT_PARENT_INFO_CHANGED) .setTaskId(TASK_ID) diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java index 21889960a8b2..e3f51697c284 100644 --- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java +++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java @@ -79,14 +79,14 @@ public class TaskContainerTest { configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN); taskContainer.updateTaskFragmentParentInfo(new TaskFragmentParentInfo(configuration, - DEFAULT_DISPLAY, true /* visible */)); + DEFAULT_DISPLAY, true /* visible */, false /* hasDirectActivity */)); assertEquals(WINDOWING_MODE_MULTI_WINDOW, taskContainer.getWindowingModeForSplitTaskFragment(splitBounds)); configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM); taskContainer.updateTaskFragmentParentInfo(new TaskFragmentParentInfo(configuration, - DEFAULT_DISPLAY, true /* visible */)); + DEFAULT_DISPLAY, true /* visible */, false /* hasDirectActivity */)); assertEquals(WINDOWING_MODE_FREEFORM, taskContainer.getWindowingModeForSplitTaskFragment(splitBounds)); @@ -106,13 +106,13 @@ public class TaskContainerTest { configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN); taskContainer.updateTaskFragmentParentInfo(new TaskFragmentParentInfo(configuration, - DEFAULT_DISPLAY, true /* visible */)); + DEFAULT_DISPLAY, true /* visible */, false /* hasDirectActivity */)); assertFalse(taskContainer.isInPictureInPicture()); configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_PINNED); taskContainer.updateTaskFragmentParentInfo(new TaskFragmentParentInfo(configuration, - DEFAULT_DISPLAY, true /* visible */)); + DEFAULT_DISPLAY, true /* visible */, false /* hasDirectActivity */)); assertTrue(taskContainer.isInPictureInPicture()); } diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index 6c31e3e69350..12ef22841a4d 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -3643,7 +3643,7 @@ class Task extends TaskFragment { */ TaskFragmentParentInfo getTaskFragmentParentInfo() { return new TaskFragmentParentInfo(getConfiguration(), getDisplayId(), - shouldBeVisible(null /* starting */)); + shouldBeVisible(null /* starting */), hasNonFinishingDirectActivity()); } @Override @@ -6056,6 +6056,11 @@ class Task extends TaskFragment { // Non-root task position changed. mRootWindowContainer.invalidateTaskLayers(); } + + if (child.asActivityRecord() != null) { + // Send for TaskFragmentParentInfo#hasDirectActivity change. + sendTaskFragmentParentInfoChangedIfNeeded(); + } } void reparent(TaskDisplayArea newParent, boolean onTop) { diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java index 82d34246f857..2fc531a4e9f6 100644 --- a/services/core/java/com/android/server/wm/TaskFragment.java +++ b/services/core/java/com/android/server/wm/TaskFragment.java @@ -1049,6 +1049,10 @@ class TaskFragment extends WindowContainer<WindowContainer> { return getActivity(ActivityRecord::canBeTopRunning); } + /** + * Reports non-finishing activity count including this TaskFragment's child embedded + * TaskFragments' children activities. + */ int getNonFinishingActivityCount() { final int[] runningActivityCount = new int[1]; forAllActivities(a -> { @@ -1059,6 +1063,20 @@ class TaskFragment extends WindowContainer<WindowContainer> { return runningActivityCount[0]; } + /** + * Returns {@code true} if there's any non-finishing direct children activity, which is not + * embedded in TaskFragments + */ + boolean hasNonFinishingDirectActivity() { + for (int i = getChildCount() - 1; i >= 0; --i) { + final ActivityRecord activity = getChildAt(i).asActivityRecord(); + if (activity != null && !activity.finishing) { + return true; + } + } + return false; + } + boolean isTopActivityFocusable() { final ActivityRecord r = topRunningActivity(); return r != null ? r.isFocusable() diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java index 6235b3b67145..c57b05130e77 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java @@ -1742,6 +1742,7 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase { private void testApplyTransaction_reorder_failsIfNotSystemOrganizer_common( @TaskFragmentOperation.OperationType int opType) { final Task task = createTask(mDisplayContent); + doNothing().when(task).sendTaskFragmentParentInfoChangedIfNeeded(); // Create a non-embedded Activity at the bottom. final ActivityRecord bottomActivity = new ActivityBuilder(mAtm) .setTask(task) @@ -1934,7 +1935,7 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase { /** Setups the mock Task as the parent of the given TaskFragment. */ private static void setupMockParent(TaskFragment taskFragment, Task mockParent) { doReturn(mockParent).when(taskFragment).getTask(); - doReturn(new TaskFragmentParentInfo(new Configuration(), DEFAULT_DISPLAY, true)) + doReturn(new TaskFragmentParentInfo(new Configuration(), DEFAULT_DISPLAY, true, true)) .when(mockParent).getTaskFragmentParentInfo(); // Task needs to be visible diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java index 0c580697bc4a..435a8357dabb 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java @@ -1568,6 +1568,7 @@ public class TaskTests extends WindowTestsBase { final TaskFragment fragment = createTaskFragmentWithEmbeddedActivity(task, organizer); final ActivityRecord embeddedActivity = fragment.getTopMostActivity(); + doNothing().when(task).sendTaskFragmentParentInfoChangedIfNeeded(); task.moveActivityToFront(activity); assertEquals("Activity must be moved to front", activity, task.getTopMostActivity()); |