blob: cd071af6c60e93f600b3b9e97e9c9d1fae1c8b5c [file] [log] [blame]
/*
* 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 com.android.server.wm;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
import static android.util.DisplayMetrics.DENSITY_DEFAULT;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
import android.annotation.NonNull;
import android.content.pm.ActivityInfo;
import android.graphics.Rect;
import android.util.Size;
import android.view.View;
/**
* The static class that defines some utility constants and functions that are shared among launch
* params modifiers.
*/
class LaunchParamsUtil {
private static final String TAG = TAG_WITH_CLASS_NAME ? "LaunchParamsUtil" : TAG_ATM;
private static final boolean DEBUG = false;
// Screen size of Nexus 5x
static final int DEFAULT_PORTRAIT_FREEFORM_WIDTH_DP = 412;
static final int DEFAULT_PORTRAIT_FREEFORM_HEIGHT_DP = 732;
// One of the most common tablet sizes that are small enough to fit in most large screens.
private static final int DEFAULT_LANDSCAPE_FREEFORM_WIDTH_DP = 1064;
private static final int DEFAULT_LANDSCAPE_FREEFORM_HEIGHT_DP = 600;
private static final int DISPLAY_EDGE_OFFSET_DP = 27;
private static final Rect TMP_STABLE_BOUNDS = new Rect();
private LaunchParamsUtil() {}
/**
* Gets centered bounds of width x height. If inOutBounds is not empty, the result bounds
* centers at its center or displayArea's app bounds center if inOutBounds is empty.
*/
static void centerBounds(@NonNull TaskDisplayArea displayArea, int width, int height,
@NonNull Rect inOutBounds) {
if (inOutBounds.isEmpty()) {
displayArea.getStableRect(inOutBounds);
}
final int left = inOutBounds.centerX() - width / 2;
final int top = inOutBounds.centerY() - height / 2;
inOutBounds.set(left, top, left + width, top + height);
}
/**
* Calculate the default size for a freeform environment. |defaultSize| is used as the default
* DP size, but if this is null, the portrait phone size is used.
*/
static Size getDefaultFreeformSize(@NonNull ActivityRecord activityRecord,
@NonNull TaskDisplayArea displayArea,
@NonNull ActivityInfo.WindowLayout layout, int orientation,
@NonNull Rect stableBounds) {
// Get window size based on Nexus 5x screen, we assume that this is enough to show content
// of activities.
final float density = (float) displayArea.getConfiguration().densityDpi / DENSITY_DEFAULT;
final int freeformWidthInDp = (orientation == SCREEN_ORIENTATION_LANDSCAPE)
? DEFAULT_LANDSCAPE_FREEFORM_WIDTH_DP : DEFAULT_PORTRAIT_FREEFORM_WIDTH_DP;
final int freeformHeightInDp = (orientation == SCREEN_ORIENTATION_LANDSCAPE)
? DEFAULT_LANDSCAPE_FREEFORM_HEIGHT_DP : DEFAULT_PORTRAIT_FREEFORM_HEIGHT_DP;
final int freeformWidth = (int) (freeformWidthInDp * density + 0.5f);
final int freeformHeight = (int) (freeformHeightInDp * density + 0.5f);
// Minimum layout requirements.
final int layoutMinWidth = (layout == null) ? -1 : layout.minWidth;
final int layoutMinHeight = (layout == null) ? -1 : layout.minHeight;
// Max size, which is letterboxing/pillarboxing in displayArea. That's to say the large
// dimension of default size is the small dimension of displayArea size, and the small
// dimension of default size is calculated to keep the same aspect ratio as the
// displayArea's. Here we use stable bounds of displayArea because that indicates the area
// that isn't occupied by system widgets (e.g. sysbar and navbar).
final int portraitHeight = Math.min(stableBounds.width(), stableBounds.height());
final int otherDimension = Math.max(stableBounds.width(), stableBounds.height());
final int portraitWidth = (portraitHeight * portraitHeight) / otherDimension;
final int maxWidth = (orientation == SCREEN_ORIENTATION_LANDSCAPE) ? portraitHeight
: portraitWidth;
final int maxHeight = (orientation == SCREEN_ORIENTATION_LANDSCAPE) ? portraitWidth
: portraitHeight;
final int width = Math.min(maxWidth, Math.max(freeformWidth, layoutMinWidth));
final int height = Math.min(maxHeight, Math.max(freeformHeight, layoutMinHeight));
final float aspectRatio = (float) Math.max(width, height) / (float) Math.min(width, height);
// Aspect ratio requirements.
final float minAspectRatio = activityRecord.getMinAspectRatio();
final float maxAspectRatio = activityRecord.info.getMaxAspectRatio();
// Adjust the width and height to the aspect ratio requirements.
int adjWidth = width;
int adjHeight = height;
if (minAspectRatio >= 1 && aspectRatio < minAspectRatio) {
// The aspect ratio is below the minimum, adjust it to the minimum.
if (orientation == SCREEN_ORIENTATION_LANDSCAPE) {
// Fix the width, scale the height.
adjHeight = (int) (adjWidth / minAspectRatio + 0.5f);
} else {
// Fix the height, scale the width.
adjWidth = (int) (adjHeight / minAspectRatio + 0.5f);
}
} else if (maxAspectRatio >= 1 && aspectRatio > maxAspectRatio) {
// The aspect ratio exceeds the maximum, adjust it to the maximum.
if (orientation == SCREEN_ORIENTATION_LANDSCAPE) {
// Fix the width, scale the height.
adjHeight = (int) (adjWidth / maxAspectRatio + 0.5f);
} else {
// Fix the height, scale the width.
adjWidth = (int) (adjHeight / maxAspectRatio + 0.5f);
}
}
return new Size(adjWidth, adjHeight);
}
static void adjustBoundsToFitInDisplayArea(@NonNull TaskDisplayArea displayArea,
int layoutDirection,
@NonNull ActivityInfo.WindowLayout layout,
@NonNull Rect inOutBounds) {
// Give a small margin between the window bounds and the display bounds.
final Rect stableBounds = TMP_STABLE_BOUNDS;
displayArea.getStableRect(stableBounds);
final float density = (float) displayArea.getConfiguration().densityDpi / DENSITY_DEFAULT;
final int displayEdgeOffset = (int) (DISPLAY_EDGE_OFFSET_DP * density + 0.5f);
stableBounds.inset(displayEdgeOffset, displayEdgeOffset);
if (stableBounds.width() < inOutBounds.width()
|| stableBounds.height() < inOutBounds.height()) {
final float heightShrinkRatio = stableBounds.width() / (float) inOutBounds.width();
final float widthShrinkRatio =
stableBounds.height() / (float) inOutBounds.height();
final float shrinkRatio = Math.min(heightShrinkRatio, widthShrinkRatio);
// Minimum layout requirements.
final int layoutMinWidth = (layout == null) ? -1 : layout.minWidth;
final int layoutMinHeight = (layout == null) ? -1 : layout.minHeight;
int adjustedWidth = Math.max(layoutMinWidth, (int) (inOutBounds.width() * shrinkRatio));
int adjustedHeight = Math.max(layoutMinHeight,
(int) (inOutBounds.height() * shrinkRatio));
if (stableBounds.width() < adjustedWidth
|| stableBounds.height() < adjustedHeight) {
// There is no way for us to fit the bounds in the displayArea without breaking min
// size constraints. Set the min size to make visible as much content as possible.
final int left = layoutDirection == View.LAYOUT_DIRECTION_RTL
? stableBounds.right - adjustedWidth
: stableBounds.left;
inOutBounds.set(left, stableBounds.top, left + adjustedWidth,
stableBounds.top + adjustedHeight);
return;
}
inOutBounds.set(inOutBounds.left, inOutBounds.top,
inOutBounds.left + adjustedWidth, inOutBounds.top + adjustedHeight);
}
final int dx;
if (inOutBounds.right > stableBounds.right) {
// Right edge is out of displayArea.
dx = stableBounds.right - inOutBounds.right;
} else if (inOutBounds.left < stableBounds.left) {
// Left edge is out of displayArea.
dx = stableBounds.left - inOutBounds.left;
} else {
// Vertical edges are all in displayArea.
dx = 0;
}
final int dy;
if (inOutBounds.top < stableBounds.top) {
// Top edge is out of displayArea.
dy = stableBounds.top - inOutBounds.top;
} else if (inOutBounds.bottom > stableBounds.bottom) {
// Bottom edge is out of displayArea.
dy = stableBounds.bottom - inOutBounds.bottom;
} else {
// Horizontal edges are all in displayArea.
dy = 0;
}
inOutBounds.offset(dx, dy);
}
}