blob: d6527679370062d20f52b815dd734b830156795b [file] [log] [blame]
/*
* Copyright (C) 2021 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.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__LETTERBOX_POSITION__BOTTOM;
import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__LETTERBOX_POSITION__CENTER;
import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__LETTERBOX_POSITION__LEFT;
import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__LETTERBOX_POSITION__RIGHT;
import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__LETTERBOX_POSITION__TOP;
import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__LETTERBOX_POSITION__UNKNOWN_POSITION;
import static com.android.internal.util.FrameworkStatsLog.LETTERBOX_POSITION_CHANGED__POSITION_CHANGE__BOTTOM_TO_CENTER;
import static com.android.internal.util.FrameworkStatsLog.LETTERBOX_POSITION_CHANGED__POSITION_CHANGE__CENTER_TO_BOTTOM;
import static com.android.internal.util.FrameworkStatsLog.LETTERBOX_POSITION_CHANGED__POSITION_CHANGE__CENTER_TO_LEFT;
import static com.android.internal.util.FrameworkStatsLog.LETTERBOX_POSITION_CHANGED__POSITION_CHANGE__CENTER_TO_RIGHT;
import static com.android.internal.util.FrameworkStatsLog.LETTERBOX_POSITION_CHANGED__POSITION_CHANGE__CENTER_TO_TOP;
import static com.android.internal.util.FrameworkStatsLog.LETTERBOX_POSITION_CHANGED__POSITION_CHANGE__LEFT_TO_CENTER;
import static com.android.internal.util.FrameworkStatsLog.LETTERBOX_POSITION_CHANGED__POSITION_CHANGE__RIGHT_TO_CENTER;
import static com.android.internal.util.FrameworkStatsLog.LETTERBOX_POSITION_CHANGED__POSITION_CHANGE__TOP_TO_CENTER;
import static com.android.server.wm.ActivityRecord.computeAspectRatio;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND;
import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING;
import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_BACKGROUND_SOLID_COLOR;
import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_BACKGROUND_WALLPAPER;
import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_CENTER;
import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_LEFT;
import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_RIGHT;
import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_VERTICAL_REACHABILITY_POSITION_BOTTOM;
import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_VERTICAL_REACHABILITY_POSITION_CENTER;
import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_VERTICAL_REACHABILITY_POSITION_TOP;
import static com.android.server.wm.LetterboxConfiguration.MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO;
import static com.android.server.wm.LetterboxConfiguration.letterboxBackgroundTypeToString;
import android.annotation.Nullable;
import android.app.ActivityManager.TaskDescription;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Color;
import android.graphics.Point;
import android.graphics.Rect;
import android.util.Slog;
import android.view.InsetsSource;
import android.view.InsetsState;
import android.view.RoundedCorner;
import android.view.SurfaceControl;
import android.view.SurfaceControl.Transaction;
import android.view.WindowManager;
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.wm.LetterboxConfiguration.LetterboxBackgroundType;
import java.io.PrintWriter;
/** Controls behaviour of the letterbox UI for {@link mActivityRecord}. */
// TODO(b/185262487): Improve test coverage of this class. Parts of it are tested in
// SizeCompatTests and LetterboxTests but not all.
// TODO(b/185264020): Consider making LetterboxUiController applicable to any level of the
// hierarchy in addition to ActivityRecord (Task, DisplayArea, ...).
final class LetterboxUiController {
private static final String TAG = TAG_WITH_CLASS_NAME ? "LetterboxUiController" : TAG_ATM;
private final Point mTmpPoint = new Point();
private final LetterboxConfiguration mLetterboxConfiguration;
private final ActivityRecord mActivityRecord;
// Taskbar expanded height. Used to determine whether to crop an app window to display rounded
// corners above the taskbar.
private float mExpandedTaskBarHeight;
private boolean mShowWallpaperForLetterboxBackground;
@Nullable
private Letterbox mLetterbox;
LetterboxUiController(WindowManagerService wmService, ActivityRecord activityRecord) {
mLetterboxConfiguration = wmService.mLetterboxConfiguration;
// Given activityRecord may not be fully constructed since LetterboxUiController
// is created in its constructor. It shouldn't be used in this constructor but it's safe
// to use it after since controller is only used in ActivityRecord.
mActivityRecord = activityRecord;
mExpandedTaskBarHeight =
getResources().getDimensionPixelSize(R.dimen.taskbar_frame_height);
}
/** Cleans up {@link Letterbox} if it exists.*/
void destroy() {
if (mLetterbox != null) {
mLetterbox.destroy();
mLetterbox = null;
}
}
void onMovedToDisplay(int displayId) {
if (mLetterbox != null) {
mLetterbox.onMovedToDisplay(displayId);
}
}
boolean hasWallpaperBackgroudForLetterbox() {
return mShowWallpaperForLetterboxBackground;
}
/** Gets the letterbox insets. The insets will be empty if there is no letterbox. */
Rect getLetterboxInsets() {
if (mLetterbox != null) {
return mLetterbox.getInsets();
} else {
return new Rect();
}
}
/** Gets the inner bounds of letterbox. The bounds will be empty if there is no letterbox. */
void getLetterboxInnerBounds(Rect outBounds) {
if (mLetterbox != null) {
outBounds.set(mLetterbox.getInnerFrame());
} else {
outBounds.setEmpty();
}
}
/**
* @return {@code true} if bar shown within a given rectangle is allowed to be fully transparent
* when the current activity is displayed.
*/
boolean isFullyTransparentBarAllowed(Rect rect) {
return mLetterbox == null || mLetterbox.notIntersectsOrFullyContains(rect);
}
void updateLetterboxSurface(WindowState winHint) {
final WindowState w = mActivityRecord.findMainWindow();
if (w != winHint && winHint != null && w != null) {
return;
}
layoutLetterbox(winHint);
if (mLetterbox != null && mLetterbox.needsApplySurfaceChanges()) {
mLetterbox.applySurfaceChanges(mActivityRecord.getSyncTransaction());
}
}
void layoutLetterbox(WindowState winHint) {
final WindowState w = mActivityRecord.findMainWindow();
if (w == null || winHint != null && w != winHint) {
return;
}
updateRoundedCorners(w);
// If there is another main window that is not an application-starting window, we should
// update rounded corners for it as well, to avoid flickering rounded corners.
final WindowState nonStartingAppW = mActivityRecord.findMainWindow(
/* includeStartingApp= */ false);
if (nonStartingAppW != null && nonStartingAppW != w) {
updateRoundedCorners(nonStartingAppW);
}
updateWallpaperForLetterbox(w);
if (shouldShowLetterboxUi(w)) {
if (mLetterbox == null) {
mLetterbox = new Letterbox(() -> mActivityRecord.makeChildSurface(null),
mActivityRecord.mWmService.mTransactionFactory,
this::shouldLetterboxHaveRoundedCorners,
this::getLetterboxBackgroundColor,
this::hasWallpaperBackgroudForLetterbox,
this::getLetterboxWallpaperBlurRadius,
this::getLetterboxWallpaperDarkScrimAlpha,
this::handleHorizontalDoubleTap,
this::handleVerticalDoubleTap);
mLetterbox.attachInput(w);
}
mActivityRecord.getPosition(mTmpPoint);
// Get the bounds of the "space-to-fill". The transformed bounds have the highest
// priority because the activity is launched in a rotated environment. In multi-window
// mode, the task-level represents this. In fullscreen-mode, the task container does
// (since the orientation letterbox is also applied to the task).
final Rect transformedBounds = mActivityRecord.getFixedRotationTransformDisplayBounds();
final Rect spaceToFill = transformedBounds != null
? transformedBounds
: mActivityRecord.inMultiWindowMode()
? mActivityRecord.getTask().getBounds()
: mActivityRecord.getRootTask().getParent().getBounds();
mLetterbox.layout(spaceToFill, w.getFrame(), mTmpPoint);
} else if (mLetterbox != null) {
mLetterbox.hide();
}
}
private boolean shouldLetterboxHaveRoundedCorners() {
// TODO(b/214030873): remove once background is drawn for transparent activities
// Letterbox shouldn't have rounded corners if the activity is transparent
return mLetterboxConfiguration.isLetterboxActivityCornersRounded()
&& mActivityRecord.fillsParent();
}
float getHorizontalPositionMultiplier(Configuration parentConfiguration) {
// Don't check resolved configuration because it may not be updated yet during
// configuration change.
return isHorizontalReachabilityEnabled(parentConfiguration)
// Using the last global dynamic position to avoid "jumps" when moving
// between apps or activities.
? mLetterboxConfiguration.getHorizontalMultiplierForReachability()
: mLetterboxConfiguration.getLetterboxHorizontalPositionMultiplier();
}
float getVerticalPositionMultiplier(Configuration parentConfiguration) {
// Don't check resolved configuration because it may not be updated yet during
// configuration change.
return isVerticalReachabilityEnabled(parentConfiguration)
// Using the last global dynamic position to avoid "jumps" when moving
// between apps or activities.
? mLetterboxConfiguration.getVerticalMultiplierForReachability()
: mLetterboxConfiguration.getLetterboxVerticalPositionMultiplier();
}
float getFixedOrientationLetterboxAspectRatio() {
return mActivityRecord.shouldCreateCompatDisplayInsets()
? getDefaultMinAspectRatioForUnresizableApps()
: mLetterboxConfiguration.getFixedOrientationLetterboxAspectRatio();
}
private float getDefaultMinAspectRatioForUnresizableApps() {
if (!mLetterboxConfiguration.getIsSplitScreenAspectRatioForUnresizableAppsEnabled()
|| mActivityRecord.getDisplayContent() == null) {
return mLetterboxConfiguration.getDefaultMinAspectRatioForUnresizableApps()
> MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO
? mLetterboxConfiguration.getDefaultMinAspectRatioForUnresizableApps()
: mLetterboxConfiguration.getFixedOrientationLetterboxAspectRatio();
}
int dividerWindowWidth =
getResources().getDimensionPixelSize(R.dimen.docked_stack_divider_thickness);
int dividerInsets =
getResources().getDimensionPixelSize(R.dimen.docked_stack_divider_insets);
int dividerSize = dividerWindowWidth - dividerInsets * 2;
// Getting the same aspect ratio that apps get in split screen.
Rect bounds = new Rect(mActivityRecord.getDisplayContent().getBounds());
if (bounds.width() >= bounds.height()) {
bounds.inset(/* dx */ dividerSize / 2, /* dy */ 0);
bounds.right = bounds.centerX();
} else {
bounds.inset(/* dx */ 0, /* dy */ dividerSize / 2);
bounds.bottom = bounds.centerY();
}
return computeAspectRatio(bounds);
}
Resources getResources() {
return mActivityRecord.mWmService.mContext.getResources();
}
private void handleHorizontalDoubleTap(int x) {
if (!isHorizontalReachabilityEnabled() || mActivityRecord.isInTransition()) {
return;
}
if (mLetterbox.getInnerFrame().left <= x && mLetterbox.getInnerFrame().right >= x) {
// Only react to clicks at the sides of the letterboxed app window.
return;
}
int letterboxPositionForHorizontalReachability = mLetterboxConfiguration
.getLetterboxPositionForHorizontalReachability();
if (mLetterbox.getInnerFrame().left > x) {
// Moving to the next stop on the left side of the app window: right > center > left.
mLetterboxConfiguration.movePositionForHorizontalReachabilityToNextLeftStop();
int changeToLog =
letterboxPositionForHorizontalReachability
== LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_CENTER
? LETTERBOX_POSITION_CHANGED__POSITION_CHANGE__CENTER_TO_LEFT
: LETTERBOX_POSITION_CHANGED__POSITION_CHANGE__RIGHT_TO_CENTER;
logLetterboxPositionChange(changeToLog);
} else if (mLetterbox.getInnerFrame().right < x) {
// Moving to the next stop on the right side of the app window: left > center > right.
mLetterboxConfiguration.movePositionForHorizontalReachabilityToNextRightStop();
int changeToLog =
letterboxPositionForHorizontalReachability
== LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_CENTER
? LETTERBOX_POSITION_CHANGED__POSITION_CHANGE__CENTER_TO_RIGHT
: LETTERBOX_POSITION_CHANGED__POSITION_CHANGE__LEFT_TO_CENTER;
logLetterboxPositionChange(changeToLog);
}
// TODO(197549949): Add animation for transition.
mActivityRecord.recomputeConfiguration();
}
private void handleVerticalDoubleTap(int y) {
if (!isVerticalReachabilityEnabled() || mActivityRecord.isInTransition()) {
return;
}
if (mLetterbox.getInnerFrame().top <= y && mLetterbox.getInnerFrame().bottom >= y) {
// Only react to clicks at the top and bottom of the letterboxed app window.
return;
}
int letterboxPositionForVerticalReachability = mLetterboxConfiguration
.getLetterboxPositionForVerticalReachability();
if (mLetterbox.getInnerFrame().top > y) {
// Moving to the next stop on the top side of the app window: bottom > center > top.
mLetterboxConfiguration.movePositionForVerticalReachabilityToNextTopStop();
int changeToLog =
letterboxPositionForVerticalReachability
== LETTERBOX_VERTICAL_REACHABILITY_POSITION_CENTER
? LETTERBOX_POSITION_CHANGED__POSITION_CHANGE__CENTER_TO_TOP
: LETTERBOX_POSITION_CHANGED__POSITION_CHANGE__BOTTOM_TO_CENTER;
logLetterboxPositionChange(changeToLog);
} else if (mLetterbox.getInnerFrame().bottom < y) {
// Moving to the next stop on the bottom side of the app window: top > center > bottom.
mLetterboxConfiguration.movePositionForVerticalReachabilityToNextBottomStop();
int changeToLog =
letterboxPositionForVerticalReachability
== LETTERBOX_VERTICAL_REACHABILITY_POSITION_CENTER
? LETTERBOX_POSITION_CHANGED__POSITION_CHANGE__CENTER_TO_BOTTOM
: LETTERBOX_POSITION_CHANGED__POSITION_CHANGE__TOP_TO_CENTER;
logLetterboxPositionChange(changeToLog);
}
// TODO(197549949): Add animation for transition.
mActivityRecord.recomputeConfiguration();
}
/**
* Whether horizontal reachability is enabled for an activity in the current configuration.
*
* <p>Conditions that needs to be met:
* <ul>
* <li>Activity is portrait-only.
* <li>Fullscreen window in landscape device orientation.
* <li>Horizontal Reachability is enabled.
* </ul>
*/
private boolean isHorizontalReachabilityEnabled(Configuration parentConfiguration) {
return mLetterboxConfiguration.getIsHorizontalReachabilityEnabled()
&& parentConfiguration.windowConfiguration.getWindowingMode()
== WINDOWING_MODE_FULLSCREEN
&& (parentConfiguration.orientation == ORIENTATION_LANDSCAPE
&& mActivityRecord.getRequestedConfigurationOrientation() == ORIENTATION_PORTRAIT);
}
private boolean isHorizontalReachabilityEnabled() {
return isHorizontalReachabilityEnabled(mActivityRecord.getParent().getConfiguration());
}
/**
* Whether vertical reachability is enabled for an activity in the current configuration.
*
* <p>Conditions that needs to be met:
* <ul>
* <li>Activity is landscape-only.
* <li>Fullscreen window in portrait device orientation.
* <li>Vertical Reachability is enabled.
* </ul>
*/
private boolean isVerticalReachabilityEnabled(Configuration parentConfiguration) {
return mLetterboxConfiguration.getIsVerticalReachabilityEnabled()
&& parentConfiguration.windowConfiguration.getWindowingMode()
== WINDOWING_MODE_FULLSCREEN
&& (parentConfiguration.orientation == ORIENTATION_PORTRAIT
&& mActivityRecord.getRequestedConfigurationOrientation() == ORIENTATION_LANDSCAPE);
}
private boolean isVerticalReachabilityEnabled() {
return isVerticalReachabilityEnabled(mActivityRecord.getParent().getConfiguration());
}
@VisibleForTesting
boolean shouldShowLetterboxUi(WindowState mainWindow) {
return isSurfaceReadyAndVisible(mainWindow) && mainWindow.areAppWindowBoundsLetterboxed()
// Check for FLAG_SHOW_WALLPAPER explicitly instead of using
// WindowContainer#showWallpaper because the later will return true when this
// activity is using blurred wallpaper for letterbox backgroud.
&& (mainWindow.mAttrs.flags & FLAG_SHOW_WALLPAPER) == 0;
}
@VisibleForTesting
boolean isSurfaceReadyAndVisible(WindowState mainWindow) {
boolean surfaceReady = mainWindow.isDrawn() // Regular case
// Waiting for relayoutWindow to call preserveSurface
|| mainWindow.isDragResizeChanged();
return surfaceReady && (mActivityRecord.isVisible()
|| mActivityRecord.isVisibleRequested());
}
private Color getLetterboxBackgroundColor() {
final WindowState w = mActivityRecord.findMainWindow();
if (w == null || w.isLetterboxedForDisplayCutout()) {
return Color.valueOf(Color.BLACK);
}
@LetterboxBackgroundType int letterboxBackgroundType =
mLetterboxConfiguration.getLetterboxBackgroundType();
TaskDescription taskDescription = mActivityRecord.taskDescription;
switch (letterboxBackgroundType) {
case LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING:
if (taskDescription != null && taskDescription.getBackgroundColorFloating() != 0) {
return Color.valueOf(taskDescription.getBackgroundColorFloating());
}
break;
case LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND:
if (taskDescription != null && taskDescription.getBackgroundColor() != 0) {
return Color.valueOf(taskDescription.getBackgroundColor());
}
break;
case LETTERBOX_BACKGROUND_WALLPAPER:
if (hasWallpaperBackgroudForLetterbox()) {
// Color is used for translucent scrim that dims wallpaper.
return Color.valueOf(Color.BLACK);
}
Slog.w(TAG, "Wallpaper option is selected for letterbox background but "
+ "blur is not supported by a device or not supported in the current "
+ "window configuration or both alpha scrim and blur radius aren't "
+ "provided so using solid color background");
break;
case LETTERBOX_BACKGROUND_SOLID_COLOR:
return mLetterboxConfiguration.getLetterboxBackgroundColor();
default:
throw new AssertionError(
"Unexpected letterbox background type: " + letterboxBackgroundType);
}
// If picked option configured incorrectly or not supported then default to a solid color
// background.
return mLetterboxConfiguration.getLetterboxBackgroundColor();
}
private void updateRoundedCorners(WindowState mainWindow) {
final SurfaceControl windowSurface = mainWindow.getClientViewRootSurface();
if (windowSurface != null && windowSurface.isValid()) {
Transaction transaction = mActivityRecord.getSyncTransaction();
final InsetsState insetsState = mainWindow.getInsetsState();
final InsetsSource taskbarInsetsSource =
insetsState.peekSource(InsetsState.ITYPE_EXTRA_NAVIGATION_BAR);
if (!isLetterboxedNotForDisplayCutout(mainWindow)
|| !mLetterboxConfiguration.isLetterboxActivityCornersRounded()
|| taskbarInsetsSource == null) {
transaction
.setWindowCrop(windowSurface, null)
.setCornerRadius(windowSurface, 0);
return;
}
Rect cropBounds = null;
// Rounded corners should be displayed above the taskbar. When taskbar is hidden,
// an insets frame is equal to a navigation bar which shouldn't affect position of
// rounded corners since apps are expected to handle navigation bar inset.
// This condition checks whether the taskbar is visible.
// Do not crop the taskbar inset if the window is in immersive mode - the user can
// swipe to show/hide the taskbar as an overlay.
if (taskbarInsetsSource.getFrame().height() >= mExpandedTaskBarHeight
&& taskbarInsetsSource.isVisible()) {
cropBounds = new Rect(mActivityRecord.getBounds());
// Activity bounds are in screen coordinates while (0,0) for activity's surface
// control is at the top left corner of an app window so offsetting bounds
// accordingly.
cropBounds.offsetTo(0, 0);
// Rounded cornerners should be displayed above the taskbar.
cropBounds.bottom =
Math.min(cropBounds.bottom, taskbarInsetsSource.getFrame().top);
if (mActivityRecord.inSizeCompatMode()
&& mActivityRecord.getSizeCompatScale() < 1.0f) {
cropBounds.scale(1.0f / mActivityRecord.getSizeCompatScale());
}
}
transaction
.setWindowCrop(windowSurface, cropBounds)
.setCornerRadius(windowSurface, getRoundedCorners(insetsState));
}
}
// Returns rounded corners radius based on override in
// R.integer.config_letterboxActivityCornersRadius or min device bottom corner radii.
// Device corners can be different on the right and left sides but we use the same radius
// for all corners for consistency and pick a minimal bottom one for consistency with a
// taskbar rounded corners.
private int getRoundedCorners(InsetsState insetsState) {
if (mLetterboxConfiguration.getLetterboxActivityCornersRadius() >= 0) {
return mLetterboxConfiguration.getLetterboxActivityCornersRadius();
}
return Math.min(
getInsetsStateCornerRadius(insetsState, RoundedCorner.POSITION_BOTTOM_LEFT),
getInsetsStateCornerRadius(insetsState, RoundedCorner.POSITION_BOTTOM_RIGHT));
}
private int getInsetsStateCornerRadius(
InsetsState insetsState, @RoundedCorner.Position int position) {
RoundedCorner corner = insetsState.getRoundedCorners().getRoundedCorner(position);
return corner == null ? 0 : corner.getRadius();
}
private boolean isLetterboxedNotForDisplayCutout(WindowState mainWindow) {
return shouldShowLetterboxUi(mainWindow)
&& !mainWindow.isLetterboxedForDisplayCutout();
}
private void updateWallpaperForLetterbox(WindowState mainWindow) {
@LetterboxBackgroundType int letterboxBackgroundType =
mLetterboxConfiguration.getLetterboxBackgroundType();
boolean wallpaperShouldBeShown =
letterboxBackgroundType == LETTERBOX_BACKGROUND_WALLPAPER
// Don't use wallpaper as a background if letterboxed for display cutout.
&& isLetterboxedNotForDisplayCutout(mainWindow)
// Check that dark scrim alpha or blur radius are provided
&& (getLetterboxWallpaperBlurRadius() > 0
|| getLetterboxWallpaperDarkScrimAlpha() > 0)
// Check that blur is supported by a device if blur radius is provided.
&& (getLetterboxWallpaperBlurRadius() <= 0
|| isLetterboxWallpaperBlurSupported());
if (mShowWallpaperForLetterboxBackground != wallpaperShouldBeShown) {
mShowWallpaperForLetterboxBackground = wallpaperShouldBeShown;
mActivityRecord.requestUpdateWallpaperIfNeeded();
}
}
private int getLetterboxWallpaperBlurRadius() {
int blurRadius = mLetterboxConfiguration.getLetterboxBackgroundWallpaperBlurRadius();
return blurRadius < 0 ? 0 : blurRadius;
}
private float getLetterboxWallpaperDarkScrimAlpha() {
float alpha = mLetterboxConfiguration.getLetterboxBackgroundWallpaperDarkScrimAlpha();
// No scrim by default.
return (alpha < 0 || alpha >= 1) ? 0.0f : alpha;
}
private boolean isLetterboxWallpaperBlurSupported() {
return mLetterboxConfiguration.mContext.getSystemService(WindowManager.class)
.isCrossWindowBlurEnabled();
}
void dump(PrintWriter pw, String prefix) {
final WindowState mainWin = mActivityRecord.findMainWindow();
if (mainWin == null) {
return;
}
boolean areBoundsLetterboxed = mainWin.areAppWindowBoundsLetterboxed();
pw.println(prefix + "areBoundsLetterboxed=" + areBoundsLetterboxed);
if (!areBoundsLetterboxed) {
return;
}
pw.println(prefix + " letterboxReason=" + getLetterboxReasonString(mainWin));
pw.println(prefix + " activityAspectRatio="
+ mActivityRecord.computeAspectRatio(mActivityRecord.getBounds()));
boolean shouldShowLetterboxUi = shouldShowLetterboxUi(mainWin);
pw.println(prefix + "shouldShowLetterboxUi=" + shouldShowLetterboxUi);
if (!shouldShowLetterboxUi) {
return;
}
pw.println(prefix + " letterboxBackgroundColor=" + Integer.toHexString(
getLetterboxBackgroundColor().toArgb()));
pw.println(prefix + " letterboxBackgroundType="
+ letterboxBackgroundTypeToString(
mLetterboxConfiguration.getLetterboxBackgroundType()));
pw.println(prefix + " letterboxCornerRadius="
+ getRoundedCorners(mainWin.getInsetsState()));
if (mLetterboxConfiguration.getLetterboxBackgroundType()
== LETTERBOX_BACKGROUND_WALLPAPER) {
pw.println(prefix + " isLetterboxWallpaperBlurSupported="
+ isLetterboxWallpaperBlurSupported());
pw.println(prefix + " letterboxBackgroundWallpaperDarkScrimAlpha="
+ getLetterboxWallpaperDarkScrimAlpha());
pw.println(prefix + " letterboxBackgroundWallpaperBlurRadius="
+ getLetterboxWallpaperBlurRadius());
}
pw.println(prefix + " isHorizontalReachabilityEnabled="
+ isHorizontalReachabilityEnabled());
pw.println(prefix + " isVerticalReachabilityEnabled=" + isVerticalReachabilityEnabled());
pw.println(prefix + " letterboxHorizontalPositionMultiplier="
+ getHorizontalPositionMultiplier(mActivityRecord.getParent().getConfiguration()));
pw.println(prefix + " letterboxVerticalPositionMultiplier="
+ getVerticalPositionMultiplier(mActivityRecord.getParent().getConfiguration()));
pw.println(prefix + " fixedOrientationLetterboxAspectRatio="
+ mLetterboxConfiguration.getFixedOrientationLetterboxAspectRatio());
pw.println(prefix + " defaultMinAspectRatioForUnresizableApps="
+ mLetterboxConfiguration.getDefaultMinAspectRatioForUnresizableApps());
pw.println(prefix + " isSplitScreenAspectRatioForUnresizableAppsEnabled="
+ mLetterboxConfiguration.getIsSplitScreenAspectRatioForUnresizableAppsEnabled());
}
/**
* Returns a string representing the reason for letterboxing. This method assumes the activity
* is letterboxed.
*/
private String getLetterboxReasonString(WindowState mainWin) {
if (mActivityRecord.inSizeCompatMode()) {
return "SIZE_COMPAT_MODE";
}
if (mActivityRecord.isLetterboxedForFixedOrientationAndAspectRatio()) {
return "FIXED_ORIENTATION";
}
if (mainWin.isLetterboxedForDisplayCutout()) {
return "DISPLAY_CUTOUT";
}
if (mActivityRecord.isAspectRatioApplied()) {
return "ASPECT_RATIO";
}
return "UNKNOWN_REASON";
}
private int letterboxHorizontalReachabilityPositionToLetterboxPosition(
@LetterboxConfiguration.LetterboxHorizontalReachabilityPosition int position) {
switch (position) {
case LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_LEFT:
return APP_COMPAT_STATE_CHANGED__LETTERBOX_POSITION__LEFT;
case LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_CENTER:
return APP_COMPAT_STATE_CHANGED__LETTERBOX_POSITION__CENTER;
case LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_RIGHT:
return APP_COMPAT_STATE_CHANGED__LETTERBOX_POSITION__RIGHT;
default:
throw new AssertionError(
"Unexpected letterbox horizontal reachability position type: "
+ position);
}
}
private int letterboxVerticalReachabilityPositionToLetterboxPosition(
@LetterboxConfiguration.LetterboxVerticalReachabilityPosition int position) {
switch (position) {
case LETTERBOX_VERTICAL_REACHABILITY_POSITION_TOP:
return APP_COMPAT_STATE_CHANGED__LETTERBOX_POSITION__TOP;
case LETTERBOX_VERTICAL_REACHABILITY_POSITION_CENTER:
return APP_COMPAT_STATE_CHANGED__LETTERBOX_POSITION__CENTER;
case LETTERBOX_VERTICAL_REACHABILITY_POSITION_BOTTOM:
return APP_COMPAT_STATE_CHANGED__LETTERBOX_POSITION__BOTTOM;
default:
throw new AssertionError(
"Unexpected letterbox vertical reachability position type: "
+ position);
}
}
int getLetterboxPositionForLogging() {
int positionToLog = APP_COMPAT_STATE_CHANGED__LETTERBOX_POSITION__UNKNOWN_POSITION;
if (isHorizontalReachabilityEnabled()) {
int letterboxPositionForHorizontalReachability = getLetterboxConfiguration()
.getLetterboxPositionForHorizontalReachability();
positionToLog = letterboxHorizontalReachabilityPositionToLetterboxPosition(
letterboxPositionForHorizontalReachability);
} else if (isVerticalReachabilityEnabled()) {
int letterboxPositionForVerticalReachability = getLetterboxConfiguration()
.getLetterboxPositionForVerticalReachability();
positionToLog = letterboxVerticalReachabilityPositionToLetterboxPosition(
letterboxPositionForVerticalReachability);
}
return positionToLog;
}
private LetterboxConfiguration getLetterboxConfiguration() {
return mLetterboxConfiguration;
}
/**
* Logs letterbox position changes via {@link ActivityMetricsLogger#logLetterboxPositionChange}.
*/
private void logLetterboxPositionChange(int letterboxPositionChange) {
mActivityRecord.mTaskSupervisor.getActivityMetricsLogger()
.logLetterboxPositionChange(mActivityRecord, letterboxPositionChange);
}
}