blob: d02ad992c7e8c3c0e44581a1a90b891ba20f9c6b [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 android.annotation.IntDef;
import android.annotation.Nullable;
import android.content.Context;
import android.graphics.Color;
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/** Reads letterbox configs from resources and controls their overrides at runtime. */
final class LetterboxConfiguration {
/**
* Override of aspect ratio for fixed orientation letterboxing that is set via ADB with
* set-fixed-orientation-letterbox-aspect-ratio or via {@link
* com.android.internal.R.dimen.config_fixedOrientationLetterboxAspectRatio} will be ignored
* if it is <= this value.
*/
static final float MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO = 1.0f;
/** Enum for Letterbox background type. */
@Retention(RetentionPolicy.SOURCE)
@IntDef({LETTERBOX_BACKGROUND_SOLID_COLOR, LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND,
LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING, LETTERBOX_BACKGROUND_WALLPAPER})
@interface LetterboxBackgroundType {};
/** Solid background using color specified in R.color.config_letterboxBackgroundColor. */
static final int LETTERBOX_BACKGROUND_SOLID_COLOR = 0;
/** Color specified in R.attr.colorBackground for the letterboxed application. */
static final int LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND = 1;
/** Color specified in R.attr.colorBackgroundFloating for the letterboxed application. */
static final int LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING = 2;
/** Using wallpaper as a background which can be blurred or dimmed with dark scrim. */
static final int LETTERBOX_BACKGROUND_WALLPAPER = 3;
/**
* Enum for Letterbox reachability position types.
*
* <p>Order from left to right is important since it's used in {@link
* #movePositionForReachabilityToNextRightStop} and {@link
* #movePositionForReachabilityToNextLeftStop}.
*/
@Retention(RetentionPolicy.SOURCE)
@IntDef({LETTERBOX_REACHABILITY_POSITION_LEFT, LETTERBOX_REACHABILITY_POSITION_CENTER,
LETTERBOX_REACHABILITY_POSITION_RIGHT})
@interface LetterboxReachabilityPosition {};
/** Letterboxed app window is aligned to the left side. */
static final int LETTERBOX_REACHABILITY_POSITION_LEFT = 0;
/** Letterboxed app window is positioned in the horizontal center. */
static final int LETTERBOX_REACHABILITY_POSITION_CENTER = 1;
/** Letterboxed app window is aligned to the right side. */
static final int LETTERBOX_REACHABILITY_POSITION_RIGHT = 2;
final Context mContext;
// Aspect ratio of letterbox for fixed orientation, values <=
// MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO will be ignored.
private float mFixedOrientationLetterboxAspectRatio;
// Corners radius for activities presented in the letterbox mode, values < 0 will be ignored.
private int mLetterboxActivityCornersRadius;
// Color for {@link #LETTERBOX_BACKGROUND_SOLID_COLOR} letterbox background type.
@Nullable private Color mLetterboxBackgroundColorOverride;
// Color resource id for {@link #LETTERBOX_BACKGROUND_SOLID_COLOR} letterbox background type.
@Nullable private Integer mLetterboxBackgroundColorResourceIdOverride;
@LetterboxBackgroundType
private int mLetterboxBackgroundType;
// Blur radius for LETTERBOX_BACKGROUND_WALLPAPER option in mLetterboxBackgroundType.
// Values <= 0 are ignored and 0 is used instead.
private int mLetterboxBackgroundWallpaperBlurRadius;
// Alpha of a black scrim shown over wallpaper letterbox background when
// LETTERBOX_BACKGROUND_WALLPAPER option is selected for mLetterboxBackgroundType.
// Values < 0 or >= 1 are ignored and 0.0 (transparent) is used instead.
private float mLetterboxBackgroundWallpaperDarkScrimAlpha;
// Horizontal position of a center of the letterboxed app window. 0 corresponds to the left
// side of the screen and 1.0 to the right side.
private float mLetterboxHorizontalPositionMultiplier;
// Default horizontal position the letterboxed app window when reachability is enabled and
// an app is fullscreen in landscape device orientatio.
// It is used as a starting point for mLetterboxPositionForReachability.
@LetterboxReachabilityPosition
private int mDefaultPositionForReachability;
// Whether reachability repositioning is allowed for letterboxed fullscreen apps in landscape
// device orientation.
private boolean mIsReachabilityEnabled;
// Horizontal position of a center of the letterboxed app window which is global to prevent
// "jumps" when switching between letterboxed apps. It's updated to reposition the app window
// in response to a double tap gesture (see LetterboxUiController#handleDoubleTap). Used in
// LetterboxUiController#getHorizontalPositionMultiplier which is called from
// ActivityRecord#updateResolvedBoundsHorizontalPosition.
// TODO(b/199426138): Global reachability setting causes a jump when resuming an app from
// Overview after changing position in another app.
@LetterboxReachabilityPosition
private volatile int mLetterboxPositionForReachability;
// Whether education is allowed for letterboxed fullscreen apps.
private boolean mIsEducationEnabled;
LetterboxConfiguration(Context systemUiContext) {
mContext = systemUiContext;
mFixedOrientationLetterboxAspectRatio = mContext.getResources().getFloat(
R.dimen.config_fixedOrientationLetterboxAspectRatio);
mLetterboxActivityCornersRadius = mContext.getResources().getInteger(
R.integer.config_letterboxActivityCornersRadius);
mLetterboxBackgroundType = readLetterboxBackgroundTypeFromConfig(mContext);
mLetterboxBackgroundWallpaperBlurRadius = mContext.getResources().getDimensionPixelSize(
R.dimen.config_letterboxBackgroundWallpaperBlurRadius);
mLetterboxBackgroundWallpaperDarkScrimAlpha = mContext.getResources().getFloat(
R.dimen.config_letterboxBackgroundWallaperDarkScrimAlpha);
mLetterboxHorizontalPositionMultiplier = mContext.getResources().getFloat(
R.dimen.config_letterboxHorizontalPositionMultiplier);
mIsReachabilityEnabled = mContext.getResources().getBoolean(
R.bool.config_letterboxIsReachabilityEnabled);
mDefaultPositionForReachability = readLetterboxReachabilityPositionFromConfig(mContext);
mLetterboxPositionForReachability = mDefaultPositionForReachability;
mIsEducationEnabled = mContext.getResources().getBoolean(
R.bool.config_letterboxIsEducationEnabled);
}
/**
* Overrides the aspect ratio of letterbox for fixed orientation. If given value is <= {@link
* #MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO}, both it and a value of {@link
* com.android.internal.R.dimen.config_fixedOrientationLetterboxAspectRatio} will be ignored and
* the framework implementation will be used to determine the aspect ratio.
*/
@VisibleForTesting
void setFixedOrientationLetterboxAspectRatio(float aspectRatio) {
mFixedOrientationLetterboxAspectRatio = aspectRatio;
}
/**
* Resets the aspect ratio of letterbox for fixed orientation to {@link
* com.android.internal.R.dimen.config_fixedOrientationLetterboxAspectRatio}.
*/
@VisibleForTesting
void resetFixedOrientationLetterboxAspectRatio() {
mFixedOrientationLetterboxAspectRatio = mContext.getResources().getFloat(
com.android.internal.R.dimen.config_fixedOrientationLetterboxAspectRatio);
}
/**
* Gets the aspect ratio of letterbox for fixed orientation.
*/
float getFixedOrientationLetterboxAspectRatio() {
return mFixedOrientationLetterboxAspectRatio;
}
/**
* Whether corners of letterboxed activities are rounded.
*/
boolean isLetterboxActivityCornersRounded() {
return getLetterboxActivityCornersRadius() != 0;
}
/**
* Gets corners raidus for activities presented in the letterbox mode.
*/
int getLetterboxActivityCornersRadius() {
return mLetterboxActivityCornersRadius;
}
/**
* Gets color of letterbox background which is used when {@link
* #getLetterboxBackgroundType()} is {@link #LETTERBOX_BACKGROUND_SOLID_COLOR} or as
* fallback for other backfround types.
*/
Color getLetterboxBackgroundColor() {
if (mLetterboxBackgroundColorOverride != null) {
return mLetterboxBackgroundColorOverride;
}
int colorId = mLetterboxBackgroundColorResourceIdOverride != null
? mLetterboxBackgroundColorResourceIdOverride
: R.color.config_letterboxBackgroundColor;
// Query color dynamically because material colors extracted from wallpaper are updated
// when wallpaper is changed.
return Color.valueOf(mContext.getResources().getColor(colorId));
}
/**
* Gets {@link LetterboxBackgroundType} specified in {@link
* com.android.internal.R.integer.config_letterboxBackgroundType} or over via ADB command.
*/
@LetterboxBackgroundType
int getLetterboxBackgroundType() {
return mLetterboxBackgroundType;
}
/** Returns a string representing the given {@link LetterboxBackgroundType}. */
static String letterboxBackgroundTypeToString(
@LetterboxBackgroundType int backgroundType) {
switch (backgroundType) {
case LETTERBOX_BACKGROUND_SOLID_COLOR:
return "LETTERBOX_BACKGROUND_SOLID_COLOR";
case LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND:
return "LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND";
case LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING:
return "LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING";
case LETTERBOX_BACKGROUND_WALLPAPER:
return "LETTERBOX_BACKGROUND_WALLPAPER";
default:
return "unknown=" + backgroundType;
}
}
@LetterboxBackgroundType
private static int readLetterboxBackgroundTypeFromConfig(Context context) {
int backgroundType = context.getResources().getInteger(
com.android.internal.R.integer.config_letterboxBackgroundType);
return backgroundType == LETTERBOX_BACKGROUND_SOLID_COLOR
|| backgroundType == LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND
|| backgroundType == LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING
|| backgroundType == LETTERBOX_BACKGROUND_WALLPAPER
? backgroundType : LETTERBOX_BACKGROUND_SOLID_COLOR;
}
/**
* Gets alpha of a black scrim shown over wallpaper letterbox background.
*/
float getLetterboxBackgroundWallpaperDarkScrimAlpha() {
return mLetterboxBackgroundWallpaperDarkScrimAlpha;
}
/**
* Gets blur raidus for {@link #LETTERBOX_BACKGROUND_WALLPAPER} option in {@link
* mLetterboxBackgroundType}.
*/
int getLetterboxBackgroundWallpaperBlurRadius() {
return mLetterboxBackgroundWallpaperBlurRadius;
}
/*
* Gets horizontal position of a center of the letterboxed app window specified
* in {@link com.android.internal.R.dimen.config_letterboxHorizontalPositionMultiplier}
* or via an ADB command. 0 corresponds to the left side of the screen and 1 to the
* right side.
*/
float getLetterboxHorizontalPositionMultiplier() {
return (mLetterboxHorizontalPositionMultiplier < 0.0f
|| mLetterboxHorizontalPositionMultiplier > 1.0f)
// Default to central position if invalid value is provided.
? 0.5f : mLetterboxHorizontalPositionMultiplier;
}
/**
* Overrides horizontal position of a center of the letterboxed app window. If given value < 0
* or > 1, then it and a value of {@link
* com.android.internal.R.dimen.config_letterboxHorizontalPositionMultiplier} are ignored and
* central position (0.5) is used.
*/
@VisibleForTesting
void setLetterboxHorizontalPositionMultiplier(float multiplier) {
mLetterboxHorizontalPositionMultiplier = multiplier;
}
/**
* Resets horizontal position of a center of the letterboxed app window to {@link
* com.android.internal.R.dimen.config_letterboxHorizontalPositionMultiplier}.
*/
@VisibleForTesting
void resetLetterboxHorizontalPositionMultiplier() {
mLetterboxHorizontalPositionMultiplier = mContext.getResources().getFloat(
com.android.internal.R.dimen.config_letterboxHorizontalPositionMultiplier);
}
/*
* Whether reachability repositioning is allowed for letterboxed fullscreen apps in landscape
* device orientation.
*/
boolean getIsReachabilityEnabled() {
return mIsReachabilityEnabled;
}
/**
* Overrides whether reachability repositioning is allowed for letterboxed fullscreen apps in
* landscape device orientation.
*/
@VisibleForTesting
void setIsReachabilityEnabled(boolean enabled) {
mIsReachabilityEnabled = enabled;
}
/**
* Resets whether reachability repositioning is allowed for letterboxed fullscreen apps in
* landscape device orientation to {@link R.bool.config_letterboxIsReachabilityEnabled}.
*/
@VisibleForTesting
void resetIsReachabilityEnabled() {
mIsReachabilityEnabled = mContext.getResources().getBoolean(
R.bool.config_letterboxIsReachabilityEnabled);
}
/*
* Gets default horizontal position of the letterboxed app window when reachability is enabled.
* Specified in {@link R.integer.config_letterboxDefaultPositionForReachability} or via an ADB
* command.
*/
@LetterboxReachabilityPosition
int getDefaultPositionForReachability() {
return mDefaultPositionForReachability;
}
@LetterboxReachabilityPosition
private static int readLetterboxReachabilityPositionFromConfig(Context context) {
int position = context.getResources().getInteger(
R.integer.config_letterboxDefaultPositionForReachability);
return position == LETTERBOX_REACHABILITY_POSITION_LEFT
|| position == LETTERBOX_REACHABILITY_POSITION_CENTER
|| position == LETTERBOX_REACHABILITY_POSITION_RIGHT
? position : LETTERBOX_REACHABILITY_POSITION_CENTER;
}
/*
* Gets horizontal position of a center of the letterboxed app window when reachability
* is enabled specified. 0 corresponds to the left side of the screen and 1 to the right side.
*
* <p>The position multiplier is changed after each double tap in the letterbox area.
*/
float getHorizontalMultiplierForReachability() {
switch (mLetterboxPositionForReachability) {
case LETTERBOX_REACHABILITY_POSITION_LEFT:
return 0.0f;
case LETTERBOX_REACHABILITY_POSITION_CENTER:
return 0.5f;
case LETTERBOX_REACHABILITY_POSITION_RIGHT:
return 1.0f;
default:
throw new AssertionError(
"Unexpected letterbox position type: " + mLetterboxPositionForReachability);
}
}
/** Returns a string representing the given {@link LetterboxReachabilityPosition}. */
static String letterboxReachabilityPositionToString(
@LetterboxReachabilityPosition int position) {
switch (position) {
case LETTERBOX_REACHABILITY_POSITION_LEFT:
return "LETTERBOX_REACHABILITY_POSITION_LEFT";
case LETTERBOX_REACHABILITY_POSITION_CENTER:
return "LETTERBOX_REACHABILITY_POSITION_CENTER";
case LETTERBOX_REACHABILITY_POSITION_RIGHT:
return "LETTERBOX_REACHABILITY_POSITION_RIGHT";
default:
throw new AssertionError(
"Unexpected letterbox position type: " + position);
}
}
/**
* Changes letterbox position for reachability to the next available one on the right side.
*/
void movePositionForReachabilityToNextRightStop() {
mLetterboxPositionForReachability = Math.min(
mLetterboxPositionForReachability + 1, LETTERBOX_REACHABILITY_POSITION_RIGHT);
}
/**
* Changes letterbox position for reachability to the next available one on the left side.
*/
void movePositionForReachabilityToNextLeftStop() {
mLetterboxPositionForReachability = Math.max(mLetterboxPositionForReachability - 1, 0);
}
/**
* Whether education is allowed for letterboxed fullscreen apps.
*/
boolean getIsEducationEnabled() {
return mIsEducationEnabled;
}
/**
* Overrides whether education is allowed for letterboxed fullscreen apps.
*/
@VisibleForTesting
void setIsEducationEnabled(boolean enabled) {
mIsEducationEnabled = enabled;
}
}