summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Chris Li <lihongyu@google.com> 2021-01-19 10:00:35 -0800
committer Chris Li <lihongyu@google.com> 2021-01-27 20:16:53 -0800
commit600acce1a85f5eb407eeba558ead36220ddc1c79 (patch)
treec7f3feaee5f30bbdb2b4773fc63c115c581bdd78
parentd23e082711b67a6b4f9fb983cacff7563059ce86 (diff)
Allow non-resizable apps in split-screen (4/n)
1. Apply Task level letterboxing for multi window 2. Draw letterboxes if needed in multi window Bug: 176061101 Test: atest WmTests:SizeCompatTests Change-Id: I701cd027ad15d20e4c5ab17ef2dab892cf8bb606
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java5
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java2
-rw-r--r--services/core/java/com/android/server/wm/Task.java90
-rw-r--r--services/core/java/com/android/server/wm/WindowState.java20
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java55
6 files changed, 128 insertions, 46 deletions
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 509cbdef41ff..a5cd3ffe5359 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -1371,7 +1371,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
final Rect spaceToFill = transformedBounds != null
? transformedBounds
: inMultiWindowMode()
- ? task.getBounds()
+ ? getRootTask().getBounds()
: getRootTask().getParent().getBounds();
mLetterbox.layout(spaceToFill, w.getFrame(), mTmpPoint);
} else if (mLetterbox != null) {
@@ -6589,8 +6589,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// which point, the activity type is still undefined if it will be standard.
// For other non-standard types, the type is set in the constructor, so this should
// not be a problem.
- && isActivityTypeStandardOrUndefined()
- && !mAtmService.mForceResizableActivities;
+ && isActivityTypeStandardOrUndefined();
}
@Override
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 8841a9b87498..b41c877809e3 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -3607,7 +3607,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
&& mImeLayeringTarget.mActivityRecord.matchParentBounds()
// IME is attached to non-Letterboxed app windows, other than windows with
// LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER flag. (Refer to WS.isLetterboxedAppWindow())
- && mImeLayeringTarget.matchesRootDisplayAreaBounds();
+ && mImeLayeringTarget.matchesDisplayAreaBounds();
}
/**
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 79d112385d67..ae69ceab3d37 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -35,6 +35,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMAR
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.app.WindowConfiguration.activityTypeToString;
+import static android.app.WindowConfiguration.isSplitScreenWindowingMode;
import static android.app.WindowConfiguration.windowingModeToString;
import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
@@ -2865,41 +2866,61 @@ class Task extends WindowContainer<WindowContainer> {
adjustForMinimalTaskDimensions(outOverrideBounds, previousBounds, newParentConfig);
if (windowingMode == WINDOWING_MODE_FREEFORM) {
- // by policy, make sure the window remains within parent somewhere
- final float density =
- ((float) newParentConfig.densityDpi) / DisplayMetrics.DENSITY_DEFAULT;
- final Rect parentBounds =
- new Rect(newParentConfig.windowConfiguration.getBounds());
- final DisplayContent display = getDisplayContent();
- if (display != null) {
- // If a freeform window moves below system bar, there is no way to move it again
- // by touch. Because its caption is covered by system bar. So we exclude them
- // from root task bounds. and then caption will be shown inside stable area.
- final Rect stableBounds = new Rect();
- display.getStableRect(stableBounds);
- parentBounds.intersect(stableBounds);
- }
-
- fitWithinBounds(outOverrideBounds, parentBounds,
- (int) (density * WindowState.MINIMUM_VISIBLE_WIDTH_IN_DP),
- (int) (density * WindowState.MINIMUM_VISIBLE_HEIGHT_IN_DP));
+ computeFreeformBounds(outOverrideBounds, newParentConfig);
+ return;
+ }
- // Prevent to overlap caption with stable insets.
- final int offsetTop = parentBounds.top - outOverrideBounds.top;
- if (offsetTop > 0) {
- outOverrideBounds.offset(0, offsetTop);
- }
+ if (isSplitScreenWindowingMode(windowingMode)
+ || windowingMode == WINDOWING_MODE_MULTI_WINDOW) {
+ // This is to compute whether the task should be letterboxed to handle non-resizable app
+ // in multi window. There is no split screen only logic.
+ computeLetterboxBounds(outOverrideBounds, newParentConfig);
}
}
- /**
- * Compute bounds (letterbox or pillarbox) for
- * {@link WindowConfiguration#WINDOWING_MODE_FULLSCREEN} when the parent doesn't handle the
- * orientation change and the requested orientation is different from the parent.
- */
+ /** Computes bounds for {@link WindowConfiguration#WINDOWING_MODE_FULLSCREEN}. */
+ @VisibleForTesting
void computeFullscreenBounds(@NonNull Rect outBounds, @NonNull Configuration newParentConfig) {
// In FULLSCREEN mode, always start with empty bounds to indicate "fill parent".
outBounds.setEmpty();
+ computeLetterboxBounds(outBounds, newParentConfig);
+ }
+
+ /** Computes bounds for {@link WindowConfiguration#WINDOWING_MODE_FREEFORM}. */
+ private void computeFreeformBounds(@NonNull Rect outBounds,
+ @NonNull Configuration newParentConfig) {
+ // by policy, make sure the window remains within parent somewhere
+ final float density =
+ ((float) newParentConfig.densityDpi) / DisplayMetrics.DENSITY_DEFAULT;
+ final Rect parentBounds =
+ new Rect(newParentConfig.windowConfiguration.getBounds());
+ final DisplayContent display = getDisplayContent();
+ if (display != null) {
+ // If a freeform window moves below system bar, there is no way to move it again
+ // by touch. Because its caption is covered by system bar. So we exclude them
+ // from root task bounds. and then caption will be shown inside stable area.
+ final Rect stableBounds = new Rect();
+ display.getStableRect(stableBounds);
+ parentBounds.intersect(stableBounds);
+ }
+
+ fitWithinBounds(outBounds, parentBounds,
+ (int) (density * WindowState.MINIMUM_VISIBLE_WIDTH_IN_DP),
+ (int) (density * WindowState.MINIMUM_VISIBLE_HEIGHT_IN_DP));
+
+ // Prevent to overlap caption with stable insets.
+ final int offsetTop = parentBounds.top - outBounds.top;
+ if (offsetTop > 0) {
+ outBounds.offset(0, offsetTop);
+ }
+ }
+
+ /**
+ * Computes bounds (letterbox or pillarbox) when the parent doesn't handle the orientation
+ * change and the requested orientation is different from the parent.
+ */
+ private void computeLetterboxBounds(@NonNull Rect outBounds,
+ @NonNull Configuration newParentConfig) {
if (handlesOrientationChangeFromDescendant()) {
// No need to letterbox at task level. Display will handle fixed-orientation requests.
return;
@@ -2957,6 +2978,8 @@ class Task extends WindowContainer<WindowContainer> {
aspect = letterboxAspectRatioOverride > MIN_TASK_LETTERBOX_ASPECT_RATIO
? letterboxAspectRatioOverride : aspect;
+ // Store the current bounds to be able to revert to size compat mode values below if needed.
+ mTmpFullBounds.set(outBounds);
if (forcedOrientation == ORIENTATION_LANDSCAPE) {
final int height = (int) Math.rint(parentWidth / aspect);
final int top = parentBounds.centerY() - height / 2;
@@ -2975,7 +2998,7 @@ class Task extends WindowContainer<WindowContainer> {
// The app shouldn't be resized, we only do task letterboxing if the compat bounds
// is also from the same task letterbox. Otherwise, clear the task bounds to show
// app in size compat mode.
- outBounds.setEmpty();
+ outBounds.set(mTmpFullBounds);
}
}
}
@@ -3361,8 +3384,9 @@ class Task extends WindowContainer<WindowContainer> {
@Override
boolean handlesOrientationChangeFromDescendant() {
return super.handlesOrientationChangeFromDescendant()
- // Display won't rotate for the orientation request if the TaskDisplayArea can't
- // specify orientation.
+ // Display won't rotate for the orientation request if the Task/TaskDisplayArea
+ // can't specify orientation.
+ && canSpecifyOrientation()
&& getDisplayArea().canSpecifyOrientation();
}
@@ -3875,7 +3899,9 @@ class Task extends WindowContainer<WindowContainer> {
}
boolean isTaskLetterboxed() {
- return getWindowingMode() == WINDOWING_MODE_FULLSCREEN && !matchParentBounds();
+ // No letterbox for multi window root task
+ return !matchParentBounds()
+ && (getWindowingMode() == WINDOWING_MODE_FULLSCREEN || !isRootTask());
}
@Override
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 3be4e78a122b..289aacde63f5 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -2108,12 +2108,12 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
return getDisplayContent().getBounds().equals(getBounds());
}
- boolean matchesRootDisplayAreaBounds() {
- RootDisplayArea root = getRootDisplayArea();
- if (root == null || root == getDisplayContent()) {
+ boolean matchesDisplayAreaBounds() {
+ final DisplayArea displayArea = getDisplayArea();
+ if (displayArea == null) {
return matchesDisplayBounds();
}
- return root.getBounds().equals(getBounds());
+ return displayArea.getBounds().equals(getBounds());
}
/**
@@ -3760,16 +3760,20 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
return getDisplayContent().mCurrentFocus == this;
}
-
/** Is this window in a container that takes up the entire screen space? */
private boolean inAppWindowThatMatchesParentBounds() {
return mActivityRecord == null || (mActivityRecord.matchParentBounds() && !inMultiWindowMode());
}
- /** @return true when the window is in fullscreen mode, but has non-fullscreen bounds set, or
- * is transitioning into/out-of fullscreen. */
+ /** @return true when the window should be letterboxed. */
boolean isLetterboxedAppWindow() {
- return !inMultiWindowMode() && !matchesRootDisplayAreaBounds()
+ // Fullscreen mode but doesn't fill display area.
+ return (!inMultiWindowMode() && !matchesDisplayAreaBounds())
+ // Activity in size compat.
+ || (mActivityRecord != null && mActivityRecord.inSizeCompatMode())
+ // Task letterboxed.
+ || (getTask() != null && getTask().isTaskLetterboxed())
+ // Letterboxed for display cutout.
|| isLetterboxedForDisplayCutout();
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index 83282a5b8e5a..78dd4b8119e3 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -558,7 +558,7 @@ public class DisplayContentTests extends WindowTestsBase {
// hence isLetterboxedAppWindow() returns true.
ws.mActivityRecord.getConfiguration().windowConfiguration.setBounds(new Rect(1, 1, 1, 1));
assertFalse("matchesRootDisplayAreaBounds() should return false",
- ws.matchesRootDisplayAreaBounds());
+ ws.matchesDisplayAreaBounds());
assertTrue("isLetterboxedAppWindow() should return true", ws.isLetterboxedAppWindow());
assertTrue("IME shouldn't be attached to app",
dc.computeImeParent() != dc.getImeTarget(IME_TARGET_LAYERING).getWindow()
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index db773241f063..bc2a2fdb4eda 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -16,15 +16,16 @@
package com.android.server.wm;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;
import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
+import static android.view.Surface.ROTATION_180;
import static android.view.Surface.ROTATION_270;
import static android.view.Surface.ROTATION_90;
-import static android.view.SurfaceProto.ROTATION_180;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
@@ -35,6 +36,7 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.server.wm.DisplayContent.IME_TARGET_LAYERING;
import static com.android.server.wm.Task.ActivityState.STOPPED;
+import static com.android.server.wm.WindowContainer.POSITION_TOP;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -905,6 +907,57 @@ public class SizeCompatTests extends WindowTestsBase {
assertEquals(1000, activityBounds.height());
}
+ @Test
+ public void testSupportsNonResizableInSplitScreen() {
+ // Support non resizable in multi window
+ mAtm.mSupportsNonResizableMultiWindow = true;
+ setUpDisplaySizeWithApp(1000, 2800);
+ final TestSplitOrganizer organizer =
+ new TestSplitOrganizer(mAtm, mActivity.getDisplayContent());
+
+ // Non-resizable landscape activity
+ prepareUnresizable(mActivity, SCREEN_ORIENTATION_LANDSCAPE);
+ final Rect originalBounds = new Rect(mActivity.getBounds());
+
+ // Move activity to split screen
+ mTask.reparent(organizer.mPrimary, POSITION_TOP,
+ false /*moveParents*/, "test");
+ assertEquals(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, mTask.getWindowingMode());
+ assertEquals(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, mActivity.getWindowingMode());
+
+ // Non-resizable activity in size compat mode
+ assertScaled();
+ assertEquals(originalBounds,
+ mActivity.getConfiguration().windowConfiguration.getBounds());
+
+ // Recompute the natural configuration of the non-resizable activity and the split screen.
+ mActivity.clearSizeCompatMode();
+
+ // Draw letterbox.
+ mActivity.setVisible(false);
+ mActivity.mDisplayContent.prepareAppTransition(WindowManager.TRANSIT_OPEN);
+ mActivity.mDisplayContent.mOpeningApps.add(mActivity);
+ addWindowToActivity(mActivity);
+ mActivity.mRootWindowContainer.performSurfacePlacement();
+
+ // Split screen is also in portrait [1000,1400], so Task should be in letterbox, and
+ // activity fills task.
+ assertEquals(ORIENTATION_LANDSCAPE, mTask.getConfiguration().orientation);
+ assertEquals(ORIENTATION_LANDSCAPE, mActivity.getConfiguration().orientation);
+ assertFitted();
+ assertTrue(mTask.isTaskLetterboxed());
+
+ // Letterbox should fill the gap between the split screen and the letterboxed task.
+ final Rect primarySplitBounds = new Rect(organizer.mPrimary.getBounds());
+ final Rect letterboxedTaskBounds = new Rect(mTask.getBounds());
+ assertTrue(primarySplitBounds.contains(letterboxedTaskBounds));
+ assertEquals(new Rect(letterboxedTaskBounds.left - primarySplitBounds.left,
+ letterboxedTaskBounds.top - primarySplitBounds.top,
+ primarySplitBounds.right - letterboxedTaskBounds.right,
+ primarySplitBounds.bottom - letterboxedTaskBounds.bottom),
+ mActivity.getLetterboxInsets());
+ }
+
private static WindowState addWindowToActivity(ActivityRecord activity) {
final WindowManager.LayoutParams params = new WindowManager.LayoutParams();
params.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;