Merge "Allow non-resizable apps in split-screen (4/n)" into sc-dev
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -3604,7 +3604,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();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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;
|
||||
@@ -2859,41 +2860,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);
|
||||
}
|
||||
computeFreeformBounds(outOverrideBounds, newParentConfig);
|
||||
return;
|
||||
}
|
||||
|
||||
fitWithinBounds(outOverrideBounds, parentBounds,
|
||||
(int) (density * WindowState.MINIMUM_VISIBLE_WIDTH_IN_DP),
|
||||
(int) (density * WindowState.MINIMUM_VISIBLE_HEIGHT_IN_DP));
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
// Prevent to overlap caption with stable insets.
|
||||
final int offsetTop = parentBounds.top - outOverrideBounds.top;
|
||||
if (offsetTop > 0) {
|
||||
outOverrideBounds.offset(0, offsetTop);
|
||||
}
|
||||
/** 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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 (letterbox or pillarbox) when the parent doesn't handle the orientation
|
||||
* change and the requested orientation is different from the parent.
|
||||
*/
|
||||
void computeFullscreenBounds(@NonNull Rect outBounds, @NonNull Configuration newParentConfig) {
|
||||
// In FULLSCREEN mode, always start with empty bounds to indicate "fill parent".
|
||||
outBounds.setEmpty();
|
||||
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;
|
||||
@@ -2951,6 +2972,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;
|
||||
@@ -2969,7 +2992,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3355,8 +3378,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();
|
||||
}
|
||||
|
||||
@@ -3869,7 +3893,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
|
||||
|
||||
@@ -2110,12 +2110,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());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3762,16 +3762,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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user