summaryrefslogtreecommitdiff
path: root/libs
diff options
context:
space:
mode:
Diffstat (limited to 'libs')
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java93
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java45
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java97
-rw-r--r--libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java27
-rw-r--r--libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java82
5 files changed, 257 insertions, 87 deletions
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 e20cef2bec4e..ca420c64e961 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
@@ -40,6 +40,7 @@ import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.util.ArraySet;
+import android.util.Log;
import android.util.SparseArray;
import android.window.TaskFragmentInfo;
import android.window.WindowContainerTransaction;
@@ -59,6 +60,7 @@ import java.util.function.Consumer;
*/
public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmentCallback,
ActivityEmbeddingComponent {
+ private static final String TAG = "SplitController";
@VisibleForTesting
final SplitPresenter mPresenter;
@@ -229,8 +231,8 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
}
if (taskContainer.isEmpty()) {
// Cleanup the TaskContainer if it becomes empty.
- mPresenter.stopOverrideSplitAnimation(taskContainer.mTaskId);
- mTaskContainers.remove(taskContainer.mTaskId);
+ mPresenter.stopOverrideSplitAnimation(taskContainer.getTaskId());
+ mTaskContainers.remove(taskContainer.getTaskId());
}
return;
}
@@ -241,13 +243,13 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
if (taskContainer == null) {
return;
}
- final boolean wasInPip = isInPictureInPicture(taskContainer.mConfiguration);
+ final boolean wasInPip = isInPictureInPicture(taskContainer.getConfiguration());
final boolean isInPIp = isInPictureInPicture(config);
- taskContainer.mConfiguration = config;
+ taskContainer.setConfiguration(config);
// We need to check the animation override when enter/exit PIP or has bounds changed.
boolean shouldUpdateAnimationOverride = wasInPip != isInPIp;
- if (onTaskBoundsMayChange(taskContainer, config.windowConfiguration.getBounds())
+ if (taskContainer.setTaskBounds(config.windowConfiguration.getBounds())
&& !isInPIp) {
// We don't care the bounds change when it has already entered PIP.
shouldUpdateAnimationOverride = true;
@@ -257,16 +259,6 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
}
}
- /** Returns {@code true} if the bounds is changed. */
- private boolean onTaskBoundsMayChange(@NonNull TaskContainer taskContainer,
- @NonNull Rect taskBounds) {
- if (!taskBounds.isEmpty() && !taskContainer.mTaskBounds.equals(taskBounds)) {
- taskContainer.mTaskBounds.set(taskBounds);
- return true;
- }
- return false;
- }
-
/**
* Updates if we should override transition animation. We only want to override if the Task
* bounds is large enough for at least one split rule.
@@ -279,15 +271,15 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
// We only want to override if it supports split.
if (supportSplit(taskContainer)) {
- mPresenter.startOverrideSplitAnimation(taskContainer.mTaskId);
+ mPresenter.startOverrideSplitAnimation(taskContainer.getTaskId());
} else {
- mPresenter.stopOverrideSplitAnimation(taskContainer.mTaskId);
+ mPresenter.stopOverrideSplitAnimation(taskContainer.getTaskId());
}
}
private boolean supportSplit(@NonNull TaskContainer taskContainer) {
// No split inside PIP.
- if (isInPictureInPicture(taskContainer.mConfiguration)) {
+ if (isInPictureInPicture(taskContainer.getConfiguration())) {
return false;
}
// Check if the parent container bounds can support any split rule.
@@ -295,7 +287,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
if (!(rule instanceof SplitRule)) {
continue;
}
- if (mPresenter.shouldShowSideBySide(taskContainer.mTaskBounds, (SplitRule) rule)) {
+ if (mPresenter.shouldShowSideBySide(taskContainer.getTaskBounds(), (SplitRule) rule)) {
return true;
}
}
@@ -425,21 +417,36 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
return null;
}
+ TaskFragmentContainer newContainer(@NonNull Activity activity, int taskId) {
+ return newContainer(activity, activity, taskId);
+ }
+
/**
* Creates and registers a new organized container with an optional activity that will be
* re-parented to it in a WCT.
+ *
+ * @param activity the activity that will be reparented to the TaskFragment.
+ * @param activityInTask activity in the same Task so that we can get the Task bounds if
+ * needed.
+ * @param taskId parent Task of the new TaskFragment.
*/
- TaskFragmentContainer newContainer(@Nullable Activity activity, int taskId) {
+ TaskFragmentContainer newContainer(@Nullable Activity activity,
+ @NonNull Activity activityInTask, int taskId) {
+ if (activityInTask == null) {
+ throw new IllegalArgumentException("activityInTask must not be null,");
+ }
final TaskFragmentContainer container = new TaskFragmentContainer(activity, taskId);
if (!mTaskContainers.contains(taskId)) {
mTaskContainers.put(taskId, new TaskContainer(taskId));
}
final TaskContainer taskContainer = mTaskContainers.get(taskId);
taskContainer.mContainers.add(container);
- if (activity != null && !taskContainer.isTaskBoundsInitialized()
- && onTaskBoundsMayChange(taskContainer,
- SplitPresenter.getTaskBoundsFromActivity(activity))) {
- // Initial check before any TaskFragment has appeared.
+ if (!taskContainer.isTaskBoundsInitialized()) {
+ // Get the initial bounds before the TaskFragment has appeared.
+ final Rect taskBounds = SplitPresenter.getTaskBoundsFromActivity(activityInTask);
+ if (!taskContainer.setTaskBounds(taskBounds)) {
+ Log.w(TAG, "Can't find bounds from activity=" + activityInTask);
+ }
updateAnimationOverride(taskContainer);
}
return container;
@@ -887,6 +894,11 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
return null;
}
+ @Nullable
+ TaskContainer getTaskContainer(int taskId) {
+ return mTaskContainers.get(taskId);
+ }
+
/**
* Returns {@code true} if an Activity with the provided component name should always be
* expanded to occupy full task bounds. Such activity must not be put in a split.
@@ -1211,37 +1223,4 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
return configuration != null
&& configuration.windowConfiguration.getWindowingMode() == WINDOWING_MODE_PINNED;
}
-
- /** Represents TaskFragments and split pairs below a Task. */
- @VisibleForTesting
- static class TaskContainer {
- /** The unique task id. */
- final int mTaskId;
- /** Active TaskFragments in this Task. */
- final List<TaskFragmentContainer> mContainers = new ArrayList<>();
- /** Active split pairs in this Task. */
- final List<SplitContainer> mSplitContainers = new ArrayList<>();
- /**
- * TaskFragments that the organizer has requested to be closed. They should be removed when
- * the organizer receives {@link #onTaskFragmentVanished(TaskFragmentInfo)} event for them.
- */
- final Set<IBinder> mFinishedContainer = new ArraySet<>();
- /** Available window bounds of this Task. */
- final Rect mTaskBounds = new Rect();
- /** Configuration of the Task. */
- @Nullable
- Configuration mConfiguration;
-
- TaskContainer(int taskId) {
- mTaskId = taskId;
- }
-
- boolean isEmpty() {
- return mContainers.isEmpty() && mFinishedContainer.isEmpty();
- }
-
- boolean isTaskBoundsInitialized() {
- return !mTaskBounds.isEmpty();
- }
- }
}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
index 1b49585ed7dc..716a087203d3 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
@@ -19,9 +19,9 @@ package androidx.window.extensions.embedding;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import android.app.Activity;
+import android.app.WindowConfiguration;
import android.content.Context;
import android.content.Intent;
-import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.Bundle;
import android.os.IBinder;
@@ -111,8 +111,8 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
primaryActivity, primaryRectBounds, null);
// Create new empty task fragment
- final TaskFragmentContainer secondaryContainer = mController.newContainer(null,
- primaryContainer.getTaskId());
+ final TaskFragmentContainer secondaryContainer = mController.newContainer(
+ null /* activity */, primaryActivity, primaryContainer.getTaskId());
final Rect secondaryRectBounds = getBoundsForPosition(POSITION_END, parentBounds,
rule, isLtr(primaryActivity, rule));
createTaskFragment(wct, secondaryContainer.getTaskFragmentToken(),
@@ -168,8 +168,8 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
* Creates a new expanded container.
*/
TaskFragmentContainer createNewExpandedContainer(@NonNull Activity launchingActivity) {
- final TaskFragmentContainer newContainer = mController.newContainer(null,
- launchingActivity.getTaskId());
+ final TaskFragmentContainer newContainer = mController.newContainer(null /* activity */,
+ launchingActivity, launchingActivity.getTaskId());
final WindowContainerTransaction wct = new WindowContainerTransaction();
createTaskFragment(wct, newContainer.getTaskFragmentToken(),
@@ -236,8 +236,8 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
launchingActivity.getTaskId());
}
- TaskFragmentContainer secondaryContainer = mController.newContainer(null,
- primaryContainer.getTaskId());
+ TaskFragmentContainer secondaryContainer = mController.newContainer(null /* activity */,
+ launchingActivity, primaryContainer.getTaskId());
final WindowContainerTransaction wct = new WindowContainerTransaction();
mController.registerSplit(wct, primaryContainer, launchingActivity, secondaryContainer,
rule);
@@ -398,20 +398,12 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
@NonNull
Rect getParentContainerBounds(@NonNull TaskFragmentContainer container) {
- final Configuration parentConfig = mFragmentParentConfigs.get(
- container.getTaskFragmentToken());
- if (parentConfig != null) {
- return parentConfig.windowConfiguration.getBounds();
+ final int taskId = container.getTaskId();
+ final TaskContainer taskContainer = mController.getTaskContainer(taskId);
+ if (taskContainer == null) {
+ throw new IllegalStateException("Can't find TaskContainer taskId=" + taskId);
}
-
- // If there is no parent yet - then assuming that activities are running in full task bounds
- final Activity topActivity = container.getTopNonFinishingActivity();
- final Rect bounds = topActivity != null ? getParentContainerBounds(topActivity) : null;
-
- if (bounds == null) {
- throw new IllegalStateException("Unknown parent bounds");
- }
- return bounds;
+ return taskContainer.getTaskBounds();
}
@NonNull
@@ -419,22 +411,19 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
final TaskFragmentContainer container = mController.getContainerWithActivity(
activity.getActivityToken());
if (container != null) {
- final Configuration parentConfig = mFragmentParentConfigs.get(
- container.getTaskFragmentToken());
- if (parentConfig != null) {
- return parentConfig.windowConfiguration.getBounds();
- }
+ return getParentContainerBounds(container);
}
-
return getTaskBoundsFromActivity(activity);
}
@NonNull
static Rect getTaskBoundsFromActivity(@NonNull Activity activity) {
+ final WindowConfiguration windowConfiguration =
+ activity.getResources().getConfiguration().windowConfiguration;
if (!activity.isInMultiWindowMode()) {
// In fullscreen mode the max bounds should correspond to the task bounds.
- return activity.getResources().getConfiguration().windowConfiguration.getMaxBounds();
+ return windowConfiguration.getMaxBounds();
}
- return activity.getResources().getConfiguration().windowConfiguration.getBounds();
+ return windowConfiguration.getBounds();
}
}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java
new file mode 100644
index 000000000000..be793018d969
--- /dev/null
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.extensions.embedding;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.res.Configuration;
+import android.graphics.Rect;
+import android.os.IBinder;
+import android.util.ArraySet;
+import android.window.TaskFragmentInfo;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+/** Represents TaskFragments and split pairs below a Task. */
+class TaskContainer {
+
+ /** The unique task id. */
+ private final int mTaskId;
+
+ /** Available window bounds of this Task. */
+ private final Rect mTaskBounds = new Rect();
+
+ /** Configuration of the Task. */
+ @Nullable
+ private Configuration mConfiguration;
+
+ /** Active TaskFragments in this Task. */
+ final List<TaskFragmentContainer> mContainers = new ArrayList<>();
+
+ /** Active split pairs in this Task. */
+ final List<SplitContainer> mSplitContainers = new ArrayList<>();
+
+ /**
+ * TaskFragments that the organizer has requested to be closed. They should be removed when
+ * the organizer receives {@link SplitController#onTaskFragmentVanished(TaskFragmentInfo)} event
+ * for them.
+ */
+ final Set<IBinder> mFinishedContainer = new ArraySet<>();
+
+ TaskContainer(int taskId) {
+ mTaskId = taskId;
+ }
+
+ int getTaskId() {
+ return mTaskId;
+ }
+
+ @NonNull
+ Rect getTaskBounds() {
+ return mTaskBounds;
+ }
+
+ /** Returns {@code true} if the bounds is changed. */
+ boolean setTaskBounds(@NonNull Rect taskBounds) {
+ if (!taskBounds.isEmpty() && !mTaskBounds.equals(taskBounds)) {
+ mTaskBounds.set(taskBounds);
+ return true;
+ }
+ return false;
+ }
+
+ /** Whether the Task bounds has been initialized. */
+ boolean isTaskBoundsInitialized() {
+ return !mTaskBounds.isEmpty();
+ }
+
+ @Nullable
+ Configuration getConfiguration() {
+ return mConfiguration;
+ }
+
+ void setConfiguration(@Nullable Configuration configuration) {
+ mConfiguration = configuration;
+ }
+
+ /** Whether there is any {@link TaskFragmentContainer} below this Task. */
+ boolean isEmpty() {
+ return mContainers.isEmpty() && mFinishedContainer.isEmpty();
+ }
+}
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 72519dc6da5f..e0fda58fd664 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
@@ -21,6 +21,9 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.google.common.truth.Truth.assertWithMessage;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
@@ -29,12 +32,12 @@ import static org.mockito.Mockito.never;
import android.app.Activity;
import android.content.res.Configuration;
import android.content.res.Resources;
+import android.graphics.Rect;
import android.platform.test.annotations.Presubmit;
import android.window.TaskFragmentInfo;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
-import androidx.window.extensions.embedding.SplitController.TaskContainer;
import org.junit.Before;
import org.junit.Test;
@@ -53,6 +56,7 @@ import org.mockito.MockitoAnnotations;
@RunWith(AndroidJUnit4.class)
public class SplitControllerTest {
private static final int TASK_ID = 10;
+ private static final Rect TASK_BOUNDS = new Rect(0, 0, 600, 1200);
@Mock
private Activity mActivity;
@@ -70,8 +74,11 @@ public class SplitControllerTest {
mSplitPresenter = mSplitController.mPresenter;
spyOn(mSplitController);
spyOn(mSplitPresenter);
+ final Configuration activityConfig = new Configuration();
+ activityConfig.windowConfiguration.setBounds(TASK_BOUNDS);
+ activityConfig.windowConfiguration.setMaxBounds(TASK_BOUNDS);
doReturn(mActivityResources).when(mActivity).getResources();
- doReturn(new Configuration()).when(mActivityResources).getConfiguration();
+ doReturn(activityConfig).when(mActivityResources).getConfiguration();
}
@Test
@@ -117,4 +124,20 @@ public class SplitControllerTest {
verify(mSplitController).removeContainer(tf);
verify(mActivity, never()).finish();
}
+
+ @Test
+ public void testNewContainer() {
+ // Must pass in a valid activity.
+ assertThrows(IllegalArgumentException.class, () ->
+ mSplitController.newContainer(null /* activity */, TASK_ID));
+ assertThrows(IllegalArgumentException.class, () ->
+ mSplitController.newContainer(mActivity, null /* launchingActivity */, TASK_ID));
+
+ final TaskFragmentContainer tf = mSplitController.newContainer(null, mActivity, TASK_ID);
+ final TaskContainer taskContainer = mSplitController.getTaskContainer(TASK_ID);
+
+ assertNotNull(tf);
+ assertNotNull(taskContainer);
+ assertEquals(TASK_BOUNDS, taskContainer.getTaskBounds());
+ }
}
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
new file mode 100644
index 000000000000..9fb08dffbab8
--- /dev/null
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.extensions.embedding;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.graphics.Rect;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Test class for {@link TaskContainer}.
+ *
+ * Build/Install/Run:
+ * atest WMJetpackUnitTests:TaskContainerTest
+ */
+@Presubmit
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class TaskContainerTest {
+ private static final int TASK_ID = 10;
+ private static final Rect TASK_BOUNDS = new Rect(0, 0, 600, 1200);
+
+ @Test
+ public void testIsTaskBoundsInitialized() {
+ final TaskContainer taskContainer = new TaskContainer(TASK_ID);
+
+ assertFalse(taskContainer.isTaskBoundsInitialized());
+
+ taskContainer.setTaskBounds(TASK_BOUNDS);
+
+ assertTrue(taskContainer.isTaskBoundsInitialized());
+ }
+
+ @Test
+ public void testSetTaskBounds() {
+ final TaskContainer taskContainer = new TaskContainer(TASK_ID);
+
+ assertFalse(taskContainer.setTaskBounds(new Rect()));
+
+ assertTrue(taskContainer.setTaskBounds(TASK_BOUNDS));
+
+ assertFalse(taskContainer.setTaskBounds(TASK_BOUNDS));
+ }
+
+ @Test
+ public void testIsEmpty() {
+ final TaskContainer taskContainer = new TaskContainer(TASK_ID);
+
+ assertTrue(taskContainer.isEmpty());
+
+ final TaskFragmentContainer tf = new TaskFragmentContainer(null, TASK_ID);
+ taskContainer.mContainers.add(tf);
+
+ assertFalse(taskContainer.isEmpty());
+
+ taskContainer.mFinishedContainer.add(tf.getTaskFragmentToken());
+ taskContainer.mContainers.clear();
+
+ assertFalse(taskContainer.isEmpty());
+ }
+}