1 /* 2 * Copyright (C) 2021 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.wm; 18 19 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM; 20 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME; 21 22 import android.annotation.IntDef; 23 import android.annotation.NonNull; 24 import android.annotation.Nullable; 25 import android.content.Context; 26 import android.graphics.Color; 27 import android.provider.DeviceConfig; 28 import android.util.Slog; 29 30 import com.android.internal.R; 31 import com.android.internal.annotations.VisibleForTesting; 32 33 import java.lang.annotation.Retention; 34 import java.lang.annotation.RetentionPolicy; 35 import java.util.function.Function; 36 37 /** Reads letterbox configs from resources and controls their overrides at runtime. */ 38 final class LetterboxConfiguration { 39 40 private static final String TAG = TAG_WITH_CLASS_NAME ? "LetterboxConfiguration" : TAG_ATM; 41 42 // Whether camera compatibility treatment is enabled. 43 // See DisplayRotationCompatPolicy for context. 44 private static final String KEY_ENABLE_CAMERA_COMPAT_TREATMENT = 45 "enable_compat_camera_treatment"; 46 47 private static final boolean DEFAULT_VALUE_ENABLE_CAMERA_COMPAT_TREATMENT = true; 48 49 // Whether enabling rotation compat policy for immersive apps that prevents auto 50 // rotation into non-optimal screen orientation while in fullscreen. This is needed 51 // because immersive apps, such as games, are often not optimized for all 52 // orientations and can have a poor UX when rotated. Additionally, some games rely 53 // on sensors for the gameplay so users can trigger such rotations accidentally 54 // when auto rotation is on. 55 private static final String KEY_ENABLE_DISPLAY_ROTATION_IMMERSIVE_APP_COMPAT_POLICY = 56 "enable_display_rotation_immersive_app_compat_policy"; 57 58 private static final boolean DEFAULT_VALUE_ENABLE_DISPLAY_ROTATION_IMMERSIVE_APP_COMPAT_POLICY = 59 true; 60 61 // Whether ignore orientation request is allowed 62 private static final String KEY_ALLOW_IGNORE_ORIENTATION_REQUEST = 63 "allow_ignore_orientation_request"; 64 65 private static final boolean DEFAULT_VALUE_ALLOW_IGNORE_ORIENTATION_REQUEST = true; 66 67 // Whether sending compat fake focus is enabled for unfocused apps in splitscreen. 68 // Some game engines wait to get focus before drawing the content of the app so 69 // this needs to be used otherwise the apps get blacked out when they are resumed 70 // and do not have focus yet. 71 private static final String KEY_ENABLE_COMPAT_FAKE_FOCUS = "enable_compat_fake_focus"; 72 73 private static final boolean DEFAULT_VALUE_ENABLE_COMPAT_FAKE_FOCUS = true; 74 75 // Whether translucent activities policy is enabled 76 private static final String KEY_ENABLE_LETTERBOX_TRANSLUCENT_ACTIVITY = 77 "enable_letterbox_translucent_activity"; 78 79 private static final boolean DEFAULT_VALUE_ENABLE_LETTERBOX_TRANSLUCENT_ACTIVITY = true; 80 81 // Whether per-app user aspect ratio override settings is enabled 82 private static final String KEY_ENABLE_USER_ASPECT_RATIO_SETTINGS = 83 "enable_app_compat_aspect_ratio_user_settings"; 84 85 // TODO(b/288142656): Enable user aspect ratio settings by default. 86 private static final boolean DEFAULT_VALUE_ENABLE_USER_ASPECT_RATIO_SETTINGS = true; 87 88 // Whether per-app fullscreen user aspect ratio override option is enabled 89 private static final String KEY_ENABLE_USER_ASPECT_RATIO_FULLSCREEN = 90 "enable_app_compat_user_aspect_ratio_fullscreen"; 91 private static final boolean DEFAULT_VALUE_ENABLE_USER_ASPECT_RATIO_FULLSCREEN = true; 92 93 // Whether the letterbox wallpaper style is enabled by default 94 private static final String KEY_ENABLE_LETTERBOX_BACKGROUND_WALLPAPER = 95 "enable_letterbox_background_wallpaper"; 96 97 // TODO(b/290048978): Enable wallpaper as default letterbox background. 98 private static final boolean DEFAULT_VALUE_ENABLE_LETTERBOX_BACKGROUND_WALLPAPER = false; 99 100 /** 101 * Override of aspect ratio for fixed orientation letterboxing that is set via ADB with 102 * set-fixed-orientation-letterbox-aspect-ratio or via {@link 103 * com.android.internal.R.dimen.config_fixedOrientationLetterboxAspectRatio} will be ignored 104 * if it is <= this value. 105 */ 106 static final float MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO = 1.0f; 107 108 /** The default aspect ratio for a letterboxed app in multi-window mode. */ 109 static final float DEFAULT_LETTERBOX_ASPECT_RATIO_FOR_MULTI_WINDOW = 1.01f; 110 111 /** Letterboxed app window position multiplier indicating center position. */ 112 static final float LETTERBOX_POSITION_MULTIPLIER_CENTER = 0.5f; 113 114 /** Enum for Letterbox background type. */ 115 @Retention(RetentionPolicy.SOURCE) 116 @IntDef({LETTERBOX_BACKGROUND_OVERRIDE_UNSET, 117 LETTERBOX_BACKGROUND_SOLID_COLOR, 118 LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND, 119 LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING, 120 LETTERBOX_BACKGROUND_WALLPAPER}) 121 @interface LetterboxBackgroundType {}; 122 123 /** No letterbox background style set. Using the one defined by DeviceConfig. */ 124 static final int LETTERBOX_BACKGROUND_OVERRIDE_UNSET = -1; 125 126 /** Solid background using color specified in R.color.config_letterboxBackgroundColor. */ 127 static final int LETTERBOX_BACKGROUND_SOLID_COLOR = 0; 128 129 /** Color specified in R.attr.colorBackground for the letterboxed application. */ 130 static final int LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND = 1; 131 132 /** Color specified in R.attr.colorBackgroundFloating for the letterboxed application. */ 133 static final int LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING = 2; 134 135 /** Using wallpaper as a background which can be blurred or dimmed with dark scrim. */ 136 static final int LETTERBOX_BACKGROUND_WALLPAPER = 3; 137 138 /** 139 * Enum for Letterbox horizontal reachability position types. 140 * 141 * <p>Order from left to right is important since it's used in {@link 142 * #movePositionForReachabilityToNextRightStop} and {@link 143 * #movePositionForReachabilityToNextLeftStop}. 144 */ 145 @Retention(RetentionPolicy.SOURCE) 146 @IntDef({LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_LEFT, 147 LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_CENTER, 148 LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_RIGHT}) 149 @interface LetterboxHorizontalReachabilityPosition {}; 150 151 /** Letterboxed app window is aligned to the left side. */ 152 static final int LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_LEFT = 0; 153 154 /** Letterboxed app window is positioned in the horizontal center. */ 155 static final int LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_CENTER = 1; 156 157 /** Letterboxed app window is aligned to the right side. */ 158 static final int LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_RIGHT = 2; 159 160 /** 161 * Enum for Letterbox vertical reachability position types. 162 * 163 * <p>Order from top to bottom is important since it's used in {@link 164 * #movePositionForReachabilityToNextBottomStop} and {@link 165 * #movePositionForReachabilityToNextTopStop}. 166 */ 167 @Retention(RetentionPolicy.SOURCE) 168 @IntDef({LETTERBOX_VERTICAL_REACHABILITY_POSITION_TOP, 169 LETTERBOX_VERTICAL_REACHABILITY_POSITION_CENTER, 170 LETTERBOX_VERTICAL_REACHABILITY_POSITION_BOTTOM}) 171 @interface LetterboxVerticalReachabilityPosition {}; 172 173 /** Letterboxed app window is aligned to the left side. */ 174 static final int LETTERBOX_VERTICAL_REACHABILITY_POSITION_TOP = 0; 175 176 /** Letterboxed app window is positioned in the vertical center. */ 177 static final int LETTERBOX_VERTICAL_REACHABILITY_POSITION_CENTER = 1; 178 179 /** Letterboxed app window is aligned to the right side. */ 180 static final int LETTERBOX_VERTICAL_REACHABILITY_POSITION_BOTTOM = 2; 181 182 final Context mContext; 183 184 // Responsible for the persistence of letterbox[Horizontal|Vertical]PositionMultiplier 185 @NonNull 186 private final LetterboxConfigurationPersister mLetterboxConfigurationPersister; 187 188 // Aspect ratio of letterbox for fixed orientation, values <= 189 // MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO will be ignored. 190 private float mFixedOrientationLetterboxAspectRatio; 191 192 // Default min aspect ratio for unresizable apps that are eligible for the size compat mode. 193 private float mDefaultMinAspectRatioForUnresizableApps; 194 195 // Corners radius for activities presented in the letterbox mode, values < 0 will be ignored. 196 private int mLetterboxActivityCornersRadius; 197 198 // Color for {@link #LETTERBOX_BACKGROUND_SOLID_COLOR} letterbox background type. 199 @Nullable private Color mLetterboxBackgroundColorOverride; 200 201 // Color resource id for {@link #LETTERBOX_BACKGROUND_SOLID_COLOR} letterbox background type. 202 @Nullable private Integer mLetterboxBackgroundColorResourceIdOverride; 203 204 @LetterboxBackgroundType 205 private final int mLetterboxBackgroundType; 206 207 // Blur radius for LETTERBOX_BACKGROUND_WALLPAPER option from getLetterboxBackgroundType(). 208 // Values <= 0 are ignored and 0 is used instead. 209 private int mLetterboxBackgroundWallpaperBlurRadiusPx; 210 211 // Alpha of a black scrim shown over wallpaper letterbox background when 212 // LETTERBOX_BACKGROUND_WALLPAPER option is returned from getLetterboxBackgroundType(). 213 // Values < 0 or >= 1 are ignored and 0.0 (transparent) is used instead. 214 private float mLetterboxBackgroundWallpaperDarkScrimAlpha; 215 216 // Horizontal position of a center of the letterboxed app window. 0 corresponds to the left 217 // side of the screen and 1.0 to the right side. 218 private float mLetterboxHorizontalPositionMultiplier; 219 220 // Vertical position of a center of the letterboxed app window. 0 corresponds to the top 221 // side of the screen and 1.0 to the bottom side. 222 private float mLetterboxVerticalPositionMultiplier; 223 224 // Horizontal position of a center of the letterboxed app window when the device is half-folded. 225 // 0 corresponds to the left side of the screen and 1.0 to the right side. 226 private float mLetterboxBookModePositionMultiplier; 227 228 // Vertical position of a center of the letterboxed app window when the device is half-folded. 229 // 0 corresponds to the top side of the screen and 1.0 to the bottom side. 230 private float mLetterboxTabletopModePositionMultiplier; 231 232 // Default horizontal position the letterboxed app window when horizontal reachability is 233 // enabled and an app is fullscreen in landscape device orientation. 234 // It is used as a starting point for mLetterboxPositionForHorizontalReachability. 235 @LetterboxHorizontalReachabilityPosition 236 private int mDefaultPositionForHorizontalReachability; 237 238 // Default vertical position the letterboxed app window when vertical reachability is enabled 239 // and an app is fullscreen in portrait device orientation. 240 // It is used as a starting point for mLetterboxPositionForVerticalReachability. 241 @LetterboxVerticalReachabilityPosition 242 private int mDefaultPositionForVerticalReachability; 243 244 // Whether horizontal reachability repositioning is allowed for letterboxed fullscreen apps in 245 // landscape device orientation. 246 private boolean mIsHorizontalReachabilityEnabled; 247 248 // Whether vertical reachability repositioning is allowed for letterboxed fullscreen apps in 249 // portrait device orientation. 250 private boolean mIsVerticalReachabilityEnabled; 251 252 // Whether book mode automatic horizontal reachability positioning is allowed for letterboxed 253 // fullscreen apps in landscape device orientation. 254 private boolean mIsAutomaticReachabilityInBookModeEnabled; 255 256 // Whether education is allowed for letterboxed fullscreen apps. 257 private boolean mIsEducationEnabled; 258 259 // Whether using split screen aspect ratio as a default aspect ratio for unresizable apps. 260 private boolean mIsSplitScreenAspectRatioForUnresizableAppsEnabled; 261 262 // Whether using display aspect ratio as a default aspect ratio for all letterboxed apps. 263 // mIsSplitScreenAspectRatioForUnresizableAppsEnabled and 264 // config_letterboxDefaultMinAspectRatioForUnresizableApps take priority over this for 265 // unresizable apps 266 private boolean mIsDisplayAspectRatioEnabledForFixedOrientationLetterbox; 267 268 // Allows to enable letterboxing strategy for translucent activities ignoring flags. 269 private boolean mTranslucentLetterboxingOverrideEnabled; 270 271 // Allows to enable user aspect ratio settings ignoring flags. 272 private boolean mUserAppAspectRatioSettingsOverrideEnabled; 273 274 // Allows to enable fullscreen option in user aspect ratio settings ignoring flags. 275 private boolean mUserAppAspectRatioFullscreenOverrideEnabled; 276 277 // The override for letterbox background type in case it's different from 278 // LETTERBOX_BACKGROUND_OVERRIDE_UNSET 279 @LetterboxBackgroundType 280 private int mLetterboxBackgroundTypeOverride = LETTERBOX_BACKGROUND_OVERRIDE_UNSET; 281 282 // Whether we should use split screen aspect ratio for the activity when camera compat treatment 283 // is enabled and activity is connected to the camera in fullscreen. 284 private final boolean mIsCameraCompatSplitScreenAspectRatioEnabled; 285 286 // Whether activity "refresh" in camera compatibility treatment is enabled. 287 // See RefreshCallbackItem for context. 288 private boolean mIsCameraCompatTreatmentRefreshEnabled = true; 289 290 // Whether activity "refresh" in camera compatibility treatment should happen using the 291 // "stopped -> resumed" cycle rather than "paused -> resumed" cycle. Using "stop -> resumed" 292 // cycle by default due to higher success rate confirmed with app compatibility testing. 293 // See RefreshCallbackItem for context. 294 private boolean mIsCameraCompatRefreshCycleThroughStopEnabled = true; 295 296 // Whether should ignore app requested orientation in response to an app 297 // calling Activity#setRequestedOrientation. See 298 // LetterboxUiController#shouldIgnoreRequestedOrientation for details. 299 private final boolean mIsPolicyForIgnoringRequestedOrientationEnabled; 300 301 // Flags dynamically updated with {@link android.provider.DeviceConfig}. 302 @NonNull private final SynchedDeviceConfig mDeviceConfig; 303 LetterboxConfiguration(@onNull final Context systemUiContext)304 LetterboxConfiguration(@NonNull final Context systemUiContext) { 305 this(systemUiContext, new LetterboxConfigurationPersister( 306 () -> readLetterboxHorizontalReachabilityPositionFromConfig( 307 systemUiContext, /* forBookMode */ false), 308 () -> readLetterboxVerticalReachabilityPositionFromConfig( 309 systemUiContext, /* forTabletopMode */ false), 310 () -> readLetterboxHorizontalReachabilityPositionFromConfig( 311 systemUiContext, /* forBookMode */ true), 312 () -> readLetterboxVerticalReachabilityPositionFromConfig( 313 systemUiContext, /* forTabletopMode */ true))); 314 } 315 316 @VisibleForTesting LetterboxConfiguration(@onNull final Context systemUiContext, @NonNull final LetterboxConfigurationPersister letterboxConfigurationPersister)317 LetterboxConfiguration(@NonNull final Context systemUiContext, 318 @NonNull final LetterboxConfigurationPersister letterboxConfigurationPersister) { 319 mContext = systemUiContext; 320 321 mFixedOrientationLetterboxAspectRatio = mContext.getResources().getFloat( 322 R.dimen.config_fixedOrientationLetterboxAspectRatio); 323 mLetterboxBackgroundType = readLetterboxBackgroundTypeFromConfig(mContext); 324 mLetterboxActivityCornersRadius = mContext.getResources().getInteger( 325 R.integer.config_letterboxActivityCornersRadius); 326 mLetterboxBackgroundWallpaperBlurRadiusPx = mContext.getResources().getDimensionPixelSize( 327 R.dimen.config_letterboxBackgroundWallpaperBlurRadius); 328 mLetterboxBackgroundWallpaperDarkScrimAlpha = mContext.getResources().getFloat( 329 R.dimen.config_letterboxBackgroundWallaperDarkScrimAlpha); 330 mLetterboxHorizontalPositionMultiplier = mContext.getResources().getFloat( 331 R.dimen.config_letterboxHorizontalPositionMultiplier); 332 mLetterboxVerticalPositionMultiplier = mContext.getResources().getFloat( 333 R.dimen.config_letterboxVerticalPositionMultiplier); 334 mLetterboxBookModePositionMultiplier = mContext.getResources().getFloat( 335 R.dimen.config_letterboxBookModePositionMultiplier); 336 mLetterboxTabletopModePositionMultiplier = mContext.getResources().getFloat( 337 R.dimen.config_letterboxTabletopModePositionMultiplier); 338 mIsHorizontalReachabilityEnabled = mContext.getResources().getBoolean( 339 R.bool.config_letterboxIsHorizontalReachabilityEnabled); 340 mIsVerticalReachabilityEnabled = mContext.getResources().getBoolean( 341 R.bool.config_letterboxIsVerticalReachabilityEnabled); 342 mIsAutomaticReachabilityInBookModeEnabled = mContext.getResources().getBoolean( 343 R.bool.config_letterboxIsAutomaticReachabilityInBookModeEnabled); 344 mDefaultPositionForHorizontalReachability = 345 readLetterboxHorizontalReachabilityPositionFromConfig(mContext, false); 346 mDefaultPositionForVerticalReachability = 347 readLetterboxVerticalReachabilityPositionFromConfig(mContext, false); 348 mIsEducationEnabled = mContext.getResources().getBoolean( 349 R.bool.config_letterboxIsEducationEnabled); 350 setDefaultMinAspectRatioForUnresizableApps(mContext.getResources().getFloat( 351 R.dimen.config_letterboxDefaultMinAspectRatioForUnresizableApps)); 352 mIsSplitScreenAspectRatioForUnresizableAppsEnabled = mContext.getResources().getBoolean( 353 R.bool.config_letterboxIsSplitScreenAspectRatioForUnresizableAppsEnabled); 354 mIsDisplayAspectRatioEnabledForFixedOrientationLetterbox = mContext.getResources() 355 .getBoolean(R.bool 356 .config_letterboxIsDisplayAspectRatioForFixedOrientationLetterboxEnabled); 357 mIsCameraCompatSplitScreenAspectRatioEnabled = mContext.getResources().getBoolean( 358 R.bool.config_isWindowManagerCameraCompatSplitScreenAspectRatioEnabled); 359 mIsPolicyForIgnoringRequestedOrientationEnabled = mContext.getResources().getBoolean( 360 R.bool.config_letterboxIsPolicyForIgnoringRequestedOrientationEnabled); 361 362 mLetterboxConfigurationPersister = letterboxConfigurationPersister; 363 mLetterboxConfigurationPersister.start(); 364 365 mDeviceConfig = SynchedDeviceConfig.builder(DeviceConfig.NAMESPACE_WINDOW_MANAGER, 366 systemUiContext.getMainExecutor()) 367 .addDeviceConfigEntry(KEY_ENABLE_CAMERA_COMPAT_TREATMENT, 368 DEFAULT_VALUE_ENABLE_CAMERA_COMPAT_TREATMENT, 369 mContext.getResources().getBoolean( 370 R.bool.config_isWindowManagerCameraCompatTreatmentEnabled)) 371 .addDeviceConfigEntry(KEY_ENABLE_DISPLAY_ROTATION_IMMERSIVE_APP_COMPAT_POLICY, 372 DEFAULT_VALUE_ENABLE_DISPLAY_ROTATION_IMMERSIVE_APP_COMPAT_POLICY, 373 mContext.getResources().getBoolean(R.bool 374 .config_letterboxIsDisplayRotationImmersiveAppCompatPolicyEnabled)) 375 .addDeviceConfigEntry(KEY_ALLOW_IGNORE_ORIENTATION_REQUEST, 376 DEFAULT_VALUE_ALLOW_IGNORE_ORIENTATION_REQUEST, /* enabled */ true) 377 .addDeviceConfigEntry(KEY_ENABLE_COMPAT_FAKE_FOCUS, 378 DEFAULT_VALUE_ENABLE_COMPAT_FAKE_FOCUS, 379 mContext.getResources().getBoolean(R.bool.config_isCompatFakeFocusEnabled)) 380 .addDeviceConfigEntry(KEY_ENABLE_LETTERBOX_TRANSLUCENT_ACTIVITY, 381 DEFAULT_VALUE_ENABLE_LETTERBOX_TRANSLUCENT_ACTIVITY, 382 mContext.getResources().getBoolean( 383 R.bool.config_letterboxIsEnabledForTranslucentActivities)) 384 .addDeviceConfigEntry(KEY_ENABLE_USER_ASPECT_RATIO_SETTINGS, 385 DEFAULT_VALUE_ENABLE_USER_ASPECT_RATIO_SETTINGS, 386 mContext.getResources().getBoolean( 387 R.bool.config_appCompatUserAppAspectRatioSettingsIsEnabled)) 388 .addDeviceConfigEntry(KEY_ENABLE_LETTERBOX_BACKGROUND_WALLPAPER, 389 DEFAULT_VALUE_ENABLE_LETTERBOX_BACKGROUND_WALLPAPER, /* enabled */ true) 390 .addDeviceConfigEntry(KEY_ENABLE_USER_ASPECT_RATIO_FULLSCREEN, 391 DEFAULT_VALUE_ENABLE_USER_ASPECT_RATIO_FULLSCREEN, 392 mContext.getResources().getBoolean( 393 R.bool.config_appCompatUserAppAspectRatioFullscreenIsEnabled)) 394 .build(); 395 } 396 397 /** 398 * Whether enabling ignoreOrientationRequest is allowed on the device. This value is controlled 399 * via {@link android.provider.DeviceConfig}. 400 */ isIgnoreOrientationRequestAllowed()401 boolean isIgnoreOrientationRequestAllowed() { 402 return mDeviceConfig.getFlagValue(KEY_ALLOW_IGNORE_ORIENTATION_REQUEST); 403 } 404 405 /** 406 * Overrides the aspect ratio of letterbox for fixed orientation. If given value is <= {@link 407 * #MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO}, both it and a value of {@link 408 * com.android.internal.R.dimen.config_fixedOrientationLetterboxAspectRatio} will be ignored and 409 * the framework implementation will be used to determine the aspect ratio. 410 */ setFixedOrientationLetterboxAspectRatio(float aspectRatio)411 void setFixedOrientationLetterboxAspectRatio(float aspectRatio) { 412 mFixedOrientationLetterboxAspectRatio = aspectRatio; 413 } 414 415 /** 416 * Resets the aspect ratio of letterbox for fixed orientation to {@link 417 * com.android.internal.R.dimen.config_fixedOrientationLetterboxAspectRatio}. 418 */ resetFixedOrientationLetterboxAspectRatio()419 void resetFixedOrientationLetterboxAspectRatio() { 420 mFixedOrientationLetterboxAspectRatio = mContext.getResources().getFloat( 421 com.android.internal.R.dimen.config_fixedOrientationLetterboxAspectRatio); 422 } 423 424 /** 425 * Gets the aspect ratio of letterbox for fixed orientation. 426 */ getFixedOrientationLetterboxAspectRatio()427 float getFixedOrientationLetterboxAspectRatio() { 428 return mFixedOrientationLetterboxAspectRatio; 429 } 430 431 /** 432 * Resets the min aspect ratio for unresizable apps that are eligible for size compat mode. 433 */ resetDefaultMinAspectRatioForUnresizableApps()434 void resetDefaultMinAspectRatioForUnresizableApps() { 435 setDefaultMinAspectRatioForUnresizableApps(mContext.getResources().getFloat( 436 R.dimen.config_letterboxDefaultMinAspectRatioForUnresizableApps)); 437 } 438 439 /** 440 * Gets the min aspect ratio for unresizable apps that are eligible for size compat mode. 441 */ getDefaultMinAspectRatioForUnresizableApps()442 float getDefaultMinAspectRatioForUnresizableApps() { 443 return mDefaultMinAspectRatioForUnresizableApps; 444 } 445 446 /** 447 * Overrides the min aspect ratio for unresizable apps that are eligible for size compat mode. 448 */ setDefaultMinAspectRatioForUnresizableApps(float aspectRatio)449 void setDefaultMinAspectRatioForUnresizableApps(float aspectRatio) { 450 mDefaultMinAspectRatioForUnresizableApps = aspectRatio; 451 } 452 453 /** 454 * Overrides corners radius for activities presented in the letterbox mode. If given value < 0, 455 * both it and a value of {@link 456 * com.android.internal.R.integer.config_letterboxActivityCornersRadius} will be ignored and 457 * corners of the activity won't be rounded. 458 */ setLetterboxActivityCornersRadius(int cornersRadius)459 void setLetterboxActivityCornersRadius(int cornersRadius) { 460 mLetterboxActivityCornersRadius = cornersRadius; 461 } 462 463 /** 464 * Resets corners radius for activities presented in the letterbox mode to {@link 465 * com.android.internal.R.integer.config_letterboxActivityCornersRadius}. 466 */ resetLetterboxActivityCornersRadius()467 void resetLetterboxActivityCornersRadius() { 468 mLetterboxActivityCornersRadius = mContext.getResources().getInteger( 469 com.android.internal.R.integer.config_letterboxActivityCornersRadius); 470 } 471 472 /** 473 * Whether corners of letterboxed activities are rounded. 474 */ isLetterboxActivityCornersRounded()475 boolean isLetterboxActivityCornersRounded() { 476 return getLetterboxActivityCornersRadius() != 0; 477 } 478 479 /** 480 * Gets corners radius for activities presented in the letterbox mode. 481 */ getLetterboxActivityCornersRadius()482 int getLetterboxActivityCornersRadius() { 483 return mLetterboxActivityCornersRadius; 484 } 485 486 /** 487 * Gets color of letterbox background which is used when {@link 488 * #getLetterboxBackgroundType()} is {@link #LETTERBOX_BACKGROUND_SOLID_COLOR} or as 489 * fallback for other background types. 490 */ getLetterboxBackgroundColor()491 Color getLetterboxBackgroundColor() { 492 if (mLetterboxBackgroundColorOverride != null) { 493 return mLetterboxBackgroundColorOverride; 494 } 495 int colorId = mLetterboxBackgroundColorResourceIdOverride != null 496 ? mLetterboxBackgroundColorResourceIdOverride 497 : R.color.config_letterboxBackgroundColor; 498 // Query color dynamically because material colors extracted from wallpaper are updated 499 // when wallpaper is changed. 500 return Color.valueOf(mContext.getResources().getColor(colorId)); 501 } 502 503 504 /** 505 * Sets color of letterbox background which is used when {@link 506 * #getLetterboxBackgroundType()} is {@link #LETTERBOX_BACKGROUND_SOLID_COLOR} or as 507 * fallback for other background types. 508 */ setLetterboxBackgroundColor(Color color)509 void setLetterboxBackgroundColor(Color color) { 510 mLetterboxBackgroundColorOverride = color; 511 } 512 513 /** 514 * Sets color ID of letterbox background which is used when {@link 515 * #getLetterboxBackgroundType()} is {@link #LETTERBOX_BACKGROUND_SOLID_COLOR} or as 516 * fallback for other background types. 517 */ setLetterboxBackgroundColorResourceId(int colorId)518 void setLetterboxBackgroundColorResourceId(int colorId) { 519 mLetterboxBackgroundColorResourceIdOverride = colorId; 520 } 521 522 /** 523 * Resets color of letterbox background to {@link 524 * com.android.internal.R.color.config_letterboxBackgroundColor}. 525 */ resetLetterboxBackgroundColor()526 void resetLetterboxBackgroundColor() { 527 mLetterboxBackgroundColorOverride = null; 528 mLetterboxBackgroundColorResourceIdOverride = null; 529 } 530 531 /** 532 * Gets {@link LetterboxBackgroundType} specified via ADB command or the default one. 533 */ 534 @LetterboxBackgroundType getLetterboxBackgroundType()535 int getLetterboxBackgroundType() { 536 return mLetterboxBackgroundTypeOverride != LETTERBOX_BACKGROUND_OVERRIDE_UNSET 537 ? mLetterboxBackgroundTypeOverride 538 : getDefaultLetterboxBackgroundType(); 539 } 540 541 /** Overrides the letterbox background type. */ setLetterboxBackgroundTypeOverride(@etterboxBackgroundType int backgroundType)542 void setLetterboxBackgroundTypeOverride(@LetterboxBackgroundType int backgroundType) { 543 mLetterboxBackgroundTypeOverride = backgroundType; 544 } 545 546 /** 547 * Resets letterbox background type value depending on the 548 * {@link #KEY_ENABLE_LETTERBOX_BACKGROUND_WALLPAPER} built time and runtime flags. 549 * 550 * <p>If enabled, the letterbox background type value is set toZ 551 * {@link #LETTERBOX_BACKGROUND_WALLPAPER}. When disabled the letterbox background type value 552 * comes from {@link R.integer.config_letterboxBackgroundType}. 553 */ resetLetterboxBackgroundType()554 void resetLetterboxBackgroundType() { 555 mLetterboxBackgroundTypeOverride = LETTERBOX_BACKGROUND_OVERRIDE_UNSET; 556 } 557 558 // Returns KEY_ENABLE_LETTERBOX_BACKGROUND_WALLPAPER if the DeviceConfig flag is enabled 559 // or the value in com.android.internal.R.integer.config_letterboxBackgroundType if the flag 560 // is disabled. 561 @LetterboxBackgroundType getDefaultLetterboxBackgroundType()562 private int getDefaultLetterboxBackgroundType() { 563 return mDeviceConfig.getFlagValue(KEY_ENABLE_LETTERBOX_BACKGROUND_WALLPAPER) 564 ? LETTERBOX_BACKGROUND_WALLPAPER : mLetterboxBackgroundType; 565 } 566 567 /** Returns a string representing the given {@link LetterboxBackgroundType}. */ letterboxBackgroundTypeToString( @etterboxBackgroundType int backgroundType)568 static String letterboxBackgroundTypeToString( 569 @LetterboxBackgroundType int backgroundType) { 570 switch (backgroundType) { 571 case LETTERBOX_BACKGROUND_SOLID_COLOR: 572 return "LETTERBOX_BACKGROUND_SOLID_COLOR"; 573 case LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND: 574 return "LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND"; 575 case LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING: 576 return "LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING"; 577 case LETTERBOX_BACKGROUND_WALLPAPER: 578 return "LETTERBOX_BACKGROUND_WALLPAPER"; 579 default: 580 return "unknown=" + backgroundType; 581 } 582 } 583 584 @LetterboxBackgroundType readLetterboxBackgroundTypeFromConfig(Context context)585 private static int readLetterboxBackgroundTypeFromConfig(Context context) { 586 int backgroundType = context.getResources().getInteger( 587 com.android.internal.R.integer.config_letterboxBackgroundType); 588 return backgroundType == LETTERBOX_BACKGROUND_SOLID_COLOR 589 || backgroundType == LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND 590 || backgroundType == LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING 591 || backgroundType == LETTERBOX_BACKGROUND_WALLPAPER 592 ? backgroundType : LETTERBOX_BACKGROUND_SOLID_COLOR; 593 } 594 595 /** 596 * Overrides alpha of a black scrim shown over wallpaper for {@link 597 * #LETTERBOX_BACKGROUND_WALLPAPER} option returned from {@link getLetterboxBackgroundType()}. 598 * 599 * <p>If given value is < 0 or >= 1, both it and a value of {@link 600 * com.android.internal.R.dimen.config_letterboxBackgroundWallaperDarkScrimAlpha} are ignored 601 * and 0.0 (transparent) is instead. 602 */ setLetterboxBackgroundWallpaperDarkScrimAlpha(float alpha)603 void setLetterboxBackgroundWallpaperDarkScrimAlpha(float alpha) { 604 mLetterboxBackgroundWallpaperDarkScrimAlpha = alpha; 605 } 606 607 /** 608 * Resets alpha of a black scrim shown over wallpaper letterbox background to {@link 609 * com.android.internal.R.dimen.config_letterboxBackgroundWallaperDarkScrimAlpha}. 610 */ resetLetterboxBackgroundWallpaperDarkScrimAlpha()611 void resetLetterboxBackgroundWallpaperDarkScrimAlpha() { 612 mLetterboxBackgroundWallpaperDarkScrimAlpha = mContext.getResources().getFloat( 613 com.android.internal.R.dimen.config_letterboxBackgroundWallaperDarkScrimAlpha); 614 } 615 616 /** 617 * Gets alpha of a black scrim shown over wallpaper letterbox background. 618 */ getLetterboxBackgroundWallpaperDarkScrimAlpha()619 float getLetterboxBackgroundWallpaperDarkScrimAlpha() { 620 return mLetterboxBackgroundWallpaperDarkScrimAlpha; 621 } 622 623 /** 624 * Overrides blur radius for {@link #LETTERBOX_BACKGROUND_WALLPAPER} option from 625 * {@link getLetterboxBackgroundType()}. 626 * 627 * <p> If given value <= 0, both it and a value of {@link 628 * com.android.internal.R.dimen.config_letterboxBackgroundWallpaperBlurRadius} are ignored 629 * and 0 is used instead. 630 */ setLetterboxBackgroundWallpaperBlurRadiusPx(int radius)631 void setLetterboxBackgroundWallpaperBlurRadiusPx(int radius) { 632 mLetterboxBackgroundWallpaperBlurRadiusPx = radius; 633 } 634 635 /** 636 * Resets blur raidus for {@link #LETTERBOX_BACKGROUND_WALLPAPER} option returned by {@link 637 * getLetterboxBackgroundType()} to {@link 638 * com.android.internal.R.dimen.config_letterboxBackgroundWallpaperBlurRadius}. 639 */ resetLetterboxBackgroundWallpaperBlurRadiusPx()640 void resetLetterboxBackgroundWallpaperBlurRadiusPx() { 641 mLetterboxBackgroundWallpaperBlurRadiusPx = mContext.getResources().getDimensionPixelSize( 642 com.android.internal.R.dimen.config_letterboxBackgroundWallpaperBlurRadius); 643 } 644 645 /** 646 * Gets blur raidus for {@link #LETTERBOX_BACKGROUND_WALLPAPER} option returned by {@link 647 * getLetterboxBackgroundType()}. 648 */ getLetterboxBackgroundWallpaperBlurRadiusPx()649 int getLetterboxBackgroundWallpaperBlurRadiusPx() { 650 return mLetterboxBackgroundWallpaperBlurRadiusPx; 651 } 652 653 /* 654 * Gets horizontal position of a center of the letterboxed app window specified 655 * in {@link com.android.internal.R.dimen.config_letterboxHorizontalPositionMultiplier} 656 * or via an ADB command. 0 corresponds to the left side of the screen and 1 to the 657 * right side. 658 */ getLetterboxHorizontalPositionMultiplier(boolean isInBookMode)659 float getLetterboxHorizontalPositionMultiplier(boolean isInBookMode) { 660 if (isInBookMode) { 661 if (mLetterboxBookModePositionMultiplier < 0.0f 662 || mLetterboxBookModePositionMultiplier > 1.0f) { 663 Slog.w(TAG, 664 "mLetterboxBookModePositionMultiplier out of bounds (isInBookMode=true): " 665 + mLetterboxBookModePositionMultiplier); 666 // Default to left position if invalid value is provided. 667 return 0.0f; 668 } else { 669 return mLetterboxBookModePositionMultiplier; 670 } 671 } else { 672 if (mLetterboxHorizontalPositionMultiplier < 0.0f 673 || mLetterboxHorizontalPositionMultiplier > 1.0f) { 674 Slog.w(TAG, 675 "mLetterboxBookModePositionMultiplier out of bounds (isInBookMode=false):" 676 + mLetterboxBookModePositionMultiplier); 677 // Default to central position if invalid value is provided. 678 return 0.5f; 679 } else { 680 return mLetterboxHorizontalPositionMultiplier; 681 } 682 } 683 } 684 685 /* 686 * Gets vertical position of a center of the letterboxed app window specified 687 * in {@link com.android.internal.R.dimen.config_letterboxVerticalPositionMultiplier} 688 * or via an ADB command. 0 corresponds to the top side of the screen and 1 to the 689 * bottom side. 690 */ getLetterboxVerticalPositionMultiplier(boolean isInTabletopMode)691 float getLetterboxVerticalPositionMultiplier(boolean isInTabletopMode) { 692 if (isInTabletopMode) { 693 return (mLetterboxTabletopModePositionMultiplier < 0.0f 694 || mLetterboxTabletopModePositionMultiplier > 1.0f) 695 // Default to top position if invalid value is provided. 696 ? 0.0f : mLetterboxTabletopModePositionMultiplier; 697 } else { 698 return (mLetterboxVerticalPositionMultiplier < 0.0f 699 || mLetterboxVerticalPositionMultiplier > 1.0f) 700 // Default to central position if invalid value is provided. 701 ? 0.5f : mLetterboxVerticalPositionMultiplier; 702 } 703 } 704 705 /** 706 * Overrides horizontal position of a center of the letterboxed app window. If given value < 0 707 * or > 1, then it and a value of {@link 708 * com.android.internal.R.dimen.config_letterboxHorizontalPositionMultiplier} are ignored and 709 * central position (0.5) is used. 710 */ setLetterboxHorizontalPositionMultiplier(float multiplier)711 void setLetterboxHorizontalPositionMultiplier(float multiplier) { 712 mLetterboxHorizontalPositionMultiplier = multiplier; 713 } 714 715 /** 716 * Overrides vertical position of a center of the letterboxed app window. If given value < 0 717 * or > 1, then it and a value of {@link 718 * com.android.internal.R.dimen.config_letterboxVerticalPositionMultiplier} are ignored and 719 * central position (0.5) is used. 720 */ setLetterboxVerticalPositionMultiplier(float multiplier)721 void setLetterboxVerticalPositionMultiplier(float multiplier) { 722 mLetterboxVerticalPositionMultiplier = multiplier; 723 } 724 725 /** 726 * Resets horizontal position of a center of the letterboxed app window to {@link 727 * com.android.internal.R.dimen.config_letterboxHorizontalPositionMultiplier}. 728 */ resetLetterboxHorizontalPositionMultiplier()729 void resetLetterboxHorizontalPositionMultiplier() { 730 mLetterboxHorizontalPositionMultiplier = mContext.getResources().getFloat( 731 com.android.internal.R.dimen.config_letterboxHorizontalPositionMultiplier); 732 } 733 734 /** 735 * Resets vertical position of a center of the letterboxed app window to {@link 736 * com.android.internal.R.dimen.config_letterboxVerticalPositionMultiplier}. 737 */ resetLetterboxVerticalPositionMultiplier()738 void resetLetterboxVerticalPositionMultiplier() { 739 mLetterboxVerticalPositionMultiplier = mContext.getResources().getFloat( 740 com.android.internal.R.dimen.config_letterboxVerticalPositionMultiplier); 741 } 742 743 /* 744 * Whether horizontal reachability repositioning is allowed for letterboxed fullscreen apps in 745 * landscape device orientation. 746 */ getIsHorizontalReachabilityEnabled()747 boolean getIsHorizontalReachabilityEnabled() { 748 return mIsHorizontalReachabilityEnabled; 749 } 750 751 /* 752 * Whether vertical reachability repositioning is allowed for letterboxed fullscreen apps in 753 * portrait device orientation. 754 */ getIsVerticalReachabilityEnabled()755 boolean getIsVerticalReachabilityEnabled() { 756 return mIsVerticalReachabilityEnabled; 757 } 758 759 /* 760 * Whether automatic horizontal reachability repositioning in book mode is allowed for 761 * letterboxed fullscreen apps in landscape device orientation. 762 */ getIsAutomaticReachabilityInBookModeEnabled()763 boolean getIsAutomaticReachabilityInBookModeEnabled() { 764 return mIsAutomaticReachabilityInBookModeEnabled; 765 } 766 767 /** 768 * Overrides whether horizontal reachability repositioning is allowed for letterboxed fullscreen 769 * apps in landscape device orientation. 770 */ setIsHorizontalReachabilityEnabled(boolean enabled)771 void setIsHorizontalReachabilityEnabled(boolean enabled) { 772 mIsHorizontalReachabilityEnabled = enabled; 773 } 774 775 /** 776 * Overrides whether vertical reachability repositioning is allowed for letterboxed fullscreen 777 * apps in portrait device orientation. 778 */ setIsVerticalReachabilityEnabled(boolean enabled)779 void setIsVerticalReachabilityEnabled(boolean enabled) { 780 mIsVerticalReachabilityEnabled = enabled; 781 } 782 783 /** 784 * Overrides whether automatic horizontal reachability repositioning in book mode is allowed for 785 * letterboxed fullscreen apps in landscape device orientation. 786 */ setIsAutomaticReachabilityInBookModeEnabled(boolean enabled)787 void setIsAutomaticReachabilityInBookModeEnabled(boolean enabled) { 788 mIsAutomaticReachabilityInBookModeEnabled = enabled; 789 } 790 791 /** 792 * Resets whether horizontal reachability repositioning is allowed for letterboxed fullscreen 793 * apps in landscape device orientation to 794 * {@link R.bool.config_letterboxIsHorizontalReachabilityEnabled}. 795 */ resetIsHorizontalReachabilityEnabled()796 void resetIsHorizontalReachabilityEnabled() { 797 mIsHorizontalReachabilityEnabled = mContext.getResources().getBoolean( 798 R.bool.config_letterboxIsHorizontalReachabilityEnabled); 799 } 800 801 /** 802 * Resets whether vertical reachability repositioning is allowed for letterboxed fullscreen apps 803 * in portrait device orientation to 804 * {@link R.bool.config_letterboxIsVerticalReachabilityEnabled}. 805 */ resetIsVerticalReachabilityEnabled()806 void resetIsVerticalReachabilityEnabled() { 807 mIsVerticalReachabilityEnabled = mContext.getResources().getBoolean( 808 R.bool.config_letterboxIsVerticalReachabilityEnabled); 809 } 810 811 /** 812 * Resets whether automatic horizontal reachability repositioning in book mode is 813 * allowed for letterboxed fullscreen apps in landscape device orientation to 814 * {@link R.bool.config_letterboxIsAutomaticReachabilityInBookModeEnabled}. 815 */ resetEnabledAutomaticReachabilityInBookMode()816 void resetEnabledAutomaticReachabilityInBookMode() { 817 mIsAutomaticReachabilityInBookModeEnabled = mContext.getResources().getBoolean( 818 R.bool.config_letterboxIsAutomaticReachabilityInBookModeEnabled); 819 } 820 821 /* 822 * Gets default horizontal position of the letterboxed app window when horizontal reachability 823 * is enabled. 824 * 825 * <p> Specified in {@link R.integer.config_letterboxDefaultPositionForHorizontalReachability} 826 * or via an ADB command. 827 */ 828 @LetterboxHorizontalReachabilityPosition getDefaultPositionForHorizontalReachability()829 int getDefaultPositionForHorizontalReachability() { 830 return mDefaultPositionForHorizontalReachability; 831 } 832 833 /* 834 * Gets default vertical position of the letterboxed app window when vertical reachability is 835 * enabled. 836 * 837 * <p> Specified in {@link R.integer.config_letterboxDefaultPositionForVerticalReachability} or 838 * via an ADB command. 839 */ 840 @LetterboxVerticalReachabilityPosition getDefaultPositionForVerticalReachability()841 int getDefaultPositionForVerticalReachability() { 842 return mDefaultPositionForVerticalReachability; 843 } 844 845 /** 846 * Overrides default horizontal position of the letterboxed app window when horizontal 847 * reachability is enabled. 848 */ setDefaultPositionForHorizontalReachability( @etterboxHorizontalReachabilityPosition int position)849 void setDefaultPositionForHorizontalReachability( 850 @LetterboxHorizontalReachabilityPosition int position) { 851 mDefaultPositionForHorizontalReachability = position; 852 } 853 854 /** 855 * Overrides default vertical position of the letterboxed app window when vertical 856 * reachability is enabled. 857 */ setDefaultPositionForVerticalReachability( @etterboxVerticalReachabilityPosition int position)858 void setDefaultPositionForVerticalReachability( 859 @LetterboxVerticalReachabilityPosition int position) { 860 mDefaultPositionForVerticalReachability = position; 861 } 862 863 /** 864 * Resets default horizontal position of the letterboxed app window when horizontal reachability 865 * is enabled to {@link R.integer.config_letterboxDefaultPositionForHorizontalReachability}. 866 */ resetDefaultPositionForHorizontalReachability()867 void resetDefaultPositionForHorizontalReachability() { 868 mDefaultPositionForHorizontalReachability = 869 readLetterboxHorizontalReachabilityPositionFromConfig(mContext, 870 false /* forBookMode */); 871 } 872 873 /** 874 * Resets default vertical position of the letterboxed app window when vertical reachability 875 * is enabled to {@link R.integer.config_letterboxDefaultPositionForVerticalReachability}. 876 */ resetDefaultPositionForVerticalReachability()877 void resetDefaultPositionForVerticalReachability() { 878 mDefaultPositionForVerticalReachability = 879 readLetterboxVerticalReachabilityPositionFromConfig(mContext, 880 false /* forTabletopMode */); 881 } 882 883 /** 884 * Overrides persistent horizontal position of the letterboxed app window when horizontal 885 * reachability is enabled. 886 */ setPersistentLetterboxPositionForHorizontalReachability(boolean forBookMode, @LetterboxHorizontalReachabilityPosition int position)887 void setPersistentLetterboxPositionForHorizontalReachability(boolean forBookMode, 888 @LetterboxHorizontalReachabilityPosition int position) { 889 mLetterboxConfigurationPersister.setLetterboxPositionForHorizontalReachability( 890 forBookMode, position); 891 } 892 893 /** 894 * Overrides persistent vertical position of the letterboxed app window when vertical 895 * reachability is enabled. 896 */ setPersistentLetterboxPositionForVerticalReachability(boolean forTabletopMode, @LetterboxVerticalReachabilityPosition int position)897 void setPersistentLetterboxPositionForVerticalReachability(boolean forTabletopMode, 898 @LetterboxVerticalReachabilityPosition int position) { 899 mLetterboxConfigurationPersister.setLetterboxPositionForVerticalReachability( 900 forTabletopMode, position); 901 } 902 903 /** 904 * Resets persistent horizontal position of the letterboxed app window when horizontal 905 * reachability 906 * is enabled to default position. 907 */ resetPersistentLetterboxPositionForHorizontalReachability()908 void resetPersistentLetterboxPositionForHorizontalReachability() { 909 mLetterboxConfigurationPersister.setLetterboxPositionForHorizontalReachability( 910 false /* forBookMode */, 911 readLetterboxHorizontalReachabilityPositionFromConfig(mContext, 912 false /* forBookMode */)); 913 mLetterboxConfigurationPersister.setLetterboxPositionForHorizontalReachability( 914 true /* forBookMode */, 915 readLetterboxHorizontalReachabilityPositionFromConfig(mContext, 916 true /* forBookMode */)); 917 } 918 919 /** 920 * Resets persistent vertical position of the letterboxed app window when vertical reachability 921 * is 922 * enabled to default position. 923 */ resetPersistentLetterboxPositionForVerticalReachability()924 void resetPersistentLetterboxPositionForVerticalReachability() { 925 mLetterboxConfigurationPersister.setLetterboxPositionForVerticalReachability( 926 false /* forTabletopMode */, 927 readLetterboxVerticalReachabilityPositionFromConfig(mContext, 928 false /* forTabletopMode */)); 929 mLetterboxConfigurationPersister.setLetterboxPositionForVerticalReachability( 930 true /* forTabletopMode */, 931 readLetterboxVerticalReachabilityPositionFromConfig(mContext, 932 true /* forTabletopMode */)); 933 } 934 935 @LetterboxHorizontalReachabilityPosition readLetterboxHorizontalReachabilityPositionFromConfig(Context context, boolean forBookMode)936 private static int readLetterboxHorizontalReachabilityPositionFromConfig(Context context, 937 boolean forBookMode) { 938 int position = context.getResources().getInteger( 939 forBookMode 940 ? R.integer.config_letterboxDefaultPositionForBookModeReachability 941 : R.integer.config_letterboxDefaultPositionForHorizontalReachability); 942 return position == LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_LEFT 943 || position == LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_CENTER 944 || position == LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_RIGHT 945 ? position : LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_CENTER; 946 } 947 948 @LetterboxVerticalReachabilityPosition readLetterboxVerticalReachabilityPositionFromConfig(Context context, boolean forTabletopMode)949 private static int readLetterboxVerticalReachabilityPositionFromConfig(Context context, 950 boolean forTabletopMode) { 951 int position = context.getResources().getInteger( 952 forTabletopMode 953 ? R.integer.config_letterboxDefaultPositionForTabletopModeReachability 954 : R.integer.config_letterboxDefaultPositionForVerticalReachability); 955 return position == LETTERBOX_VERTICAL_REACHABILITY_POSITION_TOP 956 || position == LETTERBOX_VERTICAL_REACHABILITY_POSITION_CENTER 957 || position == LETTERBOX_VERTICAL_REACHABILITY_POSITION_BOTTOM 958 ? position : LETTERBOX_VERTICAL_REACHABILITY_POSITION_CENTER; 959 } 960 961 /* 962 * Gets horizontal position of a center of the letterboxed app window when reachability 963 * is enabled specified. 0 corresponds to the left side of the screen and 1 to the right side. 964 * 965 * <p>The position multiplier is changed after each double tap in the letterbox area. 966 */ getHorizontalMultiplierForReachability(boolean isDeviceInBookMode)967 float getHorizontalMultiplierForReachability(boolean isDeviceInBookMode) { 968 final int letterboxPositionForHorizontalReachability = 969 mLetterboxConfigurationPersister.getLetterboxPositionForHorizontalReachability( 970 isDeviceInBookMode); 971 switch (letterboxPositionForHorizontalReachability) { 972 case LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_LEFT: 973 return 0.0f; 974 case LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_CENTER: 975 return 0.5f; 976 case LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_RIGHT: 977 return 1.0f; 978 default: 979 throw new AssertionError( 980 "Unexpected letterbox position type: " 981 + letterboxPositionForHorizontalReachability); 982 } 983 } 984 985 /* 986 * Gets vertical position of a center of the letterboxed app window when reachability 987 * is enabled specified. 0 corresponds to the top side of the screen and 1 to the bottom side. 988 * 989 * <p>The position multiplier is changed after each double tap in the letterbox area. 990 */ getVerticalMultiplierForReachability(boolean isDeviceInTabletopMode)991 float getVerticalMultiplierForReachability(boolean isDeviceInTabletopMode) { 992 final int letterboxPositionForVerticalReachability = 993 mLetterboxConfigurationPersister.getLetterboxPositionForVerticalReachability( 994 isDeviceInTabletopMode); 995 switch (letterboxPositionForVerticalReachability) { 996 case LETTERBOX_VERTICAL_REACHABILITY_POSITION_TOP: 997 return 0.0f; 998 case LETTERBOX_VERTICAL_REACHABILITY_POSITION_CENTER: 999 return 0.5f; 1000 case LETTERBOX_VERTICAL_REACHABILITY_POSITION_BOTTOM: 1001 return 1.0f; 1002 default: 1003 throw new AssertionError( 1004 "Unexpected letterbox position type: " 1005 + letterboxPositionForVerticalReachability); 1006 } 1007 } 1008 1009 /* 1010 * Gets the horizontal position of the letterboxed app window when horizontal reachability is 1011 * enabled. 1012 */ 1013 @LetterboxHorizontalReachabilityPosition getLetterboxPositionForHorizontalReachability(boolean isInFullScreenBookMode)1014 int getLetterboxPositionForHorizontalReachability(boolean isInFullScreenBookMode) { 1015 return mLetterboxConfigurationPersister.getLetterboxPositionForHorizontalReachability( 1016 isInFullScreenBookMode); 1017 } 1018 1019 /* 1020 * Gets the vertical position of the letterboxed app window when vertical reachability is 1021 * enabled. 1022 */ 1023 @LetterboxVerticalReachabilityPosition getLetterboxPositionForVerticalReachability(boolean isInFullScreenTabletopMode)1024 int getLetterboxPositionForVerticalReachability(boolean isInFullScreenTabletopMode) { 1025 return mLetterboxConfigurationPersister.getLetterboxPositionForVerticalReachability( 1026 isInFullScreenTabletopMode); 1027 } 1028 1029 /** Returns a string representing the given {@link LetterboxHorizontalReachabilityPosition}. */ letterboxHorizontalReachabilityPositionToString( @etterboxHorizontalReachabilityPosition int position)1030 static String letterboxHorizontalReachabilityPositionToString( 1031 @LetterboxHorizontalReachabilityPosition int position) { 1032 switch (position) { 1033 case LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_LEFT: 1034 return "LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_LEFT"; 1035 case LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_CENTER: 1036 return "LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_CENTER"; 1037 case LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_RIGHT: 1038 return "LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_RIGHT"; 1039 default: 1040 throw new AssertionError( 1041 "Unexpected letterbox position type: " + position); 1042 } 1043 } 1044 1045 /** Returns a string representing the given {@link LetterboxVerticalReachabilityPosition}. */ letterboxVerticalReachabilityPositionToString( @etterboxVerticalReachabilityPosition int position)1046 static String letterboxVerticalReachabilityPositionToString( 1047 @LetterboxVerticalReachabilityPosition int position) { 1048 switch (position) { 1049 case LETTERBOX_VERTICAL_REACHABILITY_POSITION_TOP: 1050 return "LETTERBOX_VERTICAL_REACHABILITY_POSITION_TOP"; 1051 case LETTERBOX_VERTICAL_REACHABILITY_POSITION_CENTER: 1052 return "LETTERBOX_VERTICAL_REACHABILITY_POSITION_CENTER"; 1053 case LETTERBOX_VERTICAL_REACHABILITY_POSITION_BOTTOM: 1054 return "LETTERBOX_VERTICAL_REACHABILITY_POSITION_BOTTOM"; 1055 default: 1056 throw new AssertionError( 1057 "Unexpected letterbox position type: " + position); 1058 } 1059 } 1060 1061 /** 1062 * Changes letterbox position for horizontal reachability to the next available one on the 1063 * right side. 1064 */ movePositionForHorizontalReachabilityToNextRightStop(boolean isDeviceInBookMode)1065 void movePositionForHorizontalReachabilityToNextRightStop(boolean isDeviceInBookMode) { 1066 updatePositionForHorizontalReachability(isDeviceInBookMode, prev -> Math.min( 1067 prev + (isDeviceInBookMode ? 2 : 1), // Move 2 stops in book mode to avoid center. 1068 LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_RIGHT)); 1069 } 1070 1071 /** 1072 * Changes letterbox position for horizontal reachability to the next available one on the left 1073 * side. 1074 */ movePositionForHorizontalReachabilityToNextLeftStop(boolean isDeviceInBookMode)1075 void movePositionForHorizontalReachabilityToNextLeftStop(boolean isDeviceInBookMode) { 1076 updatePositionForHorizontalReachability(isDeviceInBookMode, prev -> Math.max( 1077 prev - (isDeviceInBookMode ? 2 : 1), 0)); // Move 2 stops in book mode to avoid 1078 // center. 1079 } 1080 1081 /** 1082 * Changes letterbox position for vertical reachability to the next available one on the bottom 1083 * side. 1084 */ movePositionForVerticalReachabilityToNextBottomStop(boolean isDeviceInTabletopMode)1085 void movePositionForVerticalReachabilityToNextBottomStop(boolean isDeviceInTabletopMode) { 1086 updatePositionForVerticalReachability(isDeviceInTabletopMode, prev -> Math.min( 1087 prev + (isDeviceInTabletopMode ? 2 : 1), // Move 2 stops in tabletop mode to avoid 1088 // center. 1089 LETTERBOX_VERTICAL_REACHABILITY_POSITION_BOTTOM)); 1090 } 1091 1092 /** 1093 * Changes letterbox position for vertical reachability to the next available one on the top 1094 * side. 1095 */ movePositionForVerticalReachabilityToNextTopStop(boolean isDeviceInTabletopMode)1096 void movePositionForVerticalReachabilityToNextTopStop(boolean isDeviceInTabletopMode) { 1097 updatePositionForVerticalReachability(isDeviceInTabletopMode, prev -> Math.max( 1098 prev - (isDeviceInTabletopMode ? 2 : 1), 0)); // Move 2 stops in tabletop mode to 1099 // avoid center. 1100 } 1101 1102 /** 1103 * Whether education is allowed for letterboxed fullscreen apps. 1104 */ getIsEducationEnabled()1105 boolean getIsEducationEnabled() { 1106 return mIsEducationEnabled; 1107 } 1108 1109 /** 1110 * Overrides whether education is allowed for letterboxed fullscreen apps. 1111 */ setIsEducationEnabled(boolean enabled)1112 void setIsEducationEnabled(boolean enabled) { 1113 mIsEducationEnabled = enabled; 1114 } 1115 1116 /** 1117 * Resets whether education is allowed for letterboxed fullscreen apps to 1118 * {@link R.bool.config_letterboxIsEducationEnabled}. 1119 */ resetIsEducationEnabled()1120 void resetIsEducationEnabled() { 1121 mIsEducationEnabled = mContext.getResources().getBoolean( 1122 R.bool.config_letterboxIsEducationEnabled); 1123 } 1124 1125 /** 1126 * Whether using split screen aspect ratio as a default aspect ratio for unresizable apps. 1127 */ getIsSplitScreenAspectRatioForUnresizableAppsEnabled()1128 boolean getIsSplitScreenAspectRatioForUnresizableAppsEnabled() { 1129 return mIsSplitScreenAspectRatioForUnresizableAppsEnabled; 1130 } 1131 1132 /** 1133 * Whether using display aspect ratio as a default aspect ratio for all letterboxed apps. 1134 */ getIsDisplayAspectRatioEnabledForFixedOrientationLetterbox()1135 boolean getIsDisplayAspectRatioEnabledForFixedOrientationLetterbox() { 1136 return mIsDisplayAspectRatioEnabledForFixedOrientationLetterbox; 1137 } 1138 1139 /** 1140 * Overrides whether using split screen aspect ratio as a default aspect ratio for unresizable 1141 * apps. 1142 */ setIsSplitScreenAspectRatioForUnresizableAppsEnabled(boolean enabled)1143 void setIsSplitScreenAspectRatioForUnresizableAppsEnabled(boolean enabled) { 1144 mIsSplitScreenAspectRatioForUnresizableAppsEnabled = enabled; 1145 } 1146 1147 /** 1148 * Overrides whether using display aspect ratio as a default aspect ratio for all letterboxed 1149 * apps. 1150 */ setIsDisplayAspectRatioEnabledForFixedOrientationLetterbox(boolean enabled)1151 void setIsDisplayAspectRatioEnabledForFixedOrientationLetterbox(boolean enabled) { 1152 mIsDisplayAspectRatioEnabledForFixedOrientationLetterbox = enabled; 1153 } 1154 1155 /** 1156 * Resets whether using split screen aspect ratio as a default aspect ratio for unresizable 1157 * apps {@link R.bool.config_letterboxIsSplitScreenAspectRatioForUnresizableAppsEnabled}. 1158 */ resetIsSplitScreenAspectRatioForUnresizableAppsEnabled()1159 void resetIsSplitScreenAspectRatioForUnresizableAppsEnabled() { 1160 mIsSplitScreenAspectRatioForUnresizableAppsEnabled = mContext.getResources().getBoolean( 1161 R.bool.config_letterboxIsSplitScreenAspectRatioForUnresizableAppsEnabled); 1162 } 1163 1164 /** 1165 * Resets whether using display aspect ratio as a default aspect ratio for all letterboxed 1166 * apps {@link R.bool.config_letterboxIsDisplayAspectRatioForFixedOrientationLetterboxEnabled}. 1167 */ resetIsDisplayAspectRatioEnabledForFixedOrientationLetterbox()1168 void resetIsDisplayAspectRatioEnabledForFixedOrientationLetterbox() { 1169 mIsDisplayAspectRatioEnabledForFixedOrientationLetterbox = mContext.getResources() 1170 .getBoolean(R.bool 1171 .config_letterboxIsDisplayAspectRatioForFixedOrientationLetterboxEnabled); 1172 } 1173 isTranslucentLetterboxingEnabled()1174 boolean isTranslucentLetterboxingEnabled() { 1175 return mTranslucentLetterboxingOverrideEnabled 1176 || mDeviceConfig.getFlagValue(KEY_ENABLE_LETTERBOX_TRANSLUCENT_ACTIVITY); 1177 } 1178 setTranslucentLetterboxingOverrideEnabled( boolean translucentLetterboxingOverrideEnabled)1179 void setTranslucentLetterboxingOverrideEnabled( 1180 boolean translucentLetterboxingOverrideEnabled) { 1181 mTranslucentLetterboxingOverrideEnabled = translucentLetterboxingOverrideEnabled; 1182 } 1183 1184 /** 1185 * Resets whether we use the constraints override strategy for letterboxing when dealing 1186 * with translucent activities 1187 * {@link mDeviceConfig.getFlagValue(KEY_ENABLE_LETTERBOX_TRANSLUCENT_ACTIVITY)}. 1188 */ resetTranslucentLetterboxingEnabled()1189 void resetTranslucentLetterboxingEnabled() { 1190 setTranslucentLetterboxingOverrideEnabled(false); 1191 } 1192 1193 /** Calculates a new letterboxPositionForHorizontalReachability value and updates the store */ updatePositionForHorizontalReachability(boolean isDeviceInBookMode, Function<Integer, Integer> newHorizonalPositionFun)1194 private void updatePositionForHorizontalReachability(boolean isDeviceInBookMode, 1195 Function<Integer, Integer> newHorizonalPositionFun) { 1196 final int letterboxPositionForHorizontalReachability = 1197 mLetterboxConfigurationPersister.getLetterboxPositionForHorizontalReachability( 1198 isDeviceInBookMode); 1199 final int nextHorizontalPosition = newHorizonalPositionFun.apply( 1200 letterboxPositionForHorizontalReachability); 1201 mLetterboxConfigurationPersister.setLetterboxPositionForHorizontalReachability( 1202 isDeviceInBookMode, nextHorizontalPosition); 1203 } 1204 1205 /** Calculates a new letterboxPositionForVerticalReachability value and updates the store */ updatePositionForVerticalReachability(boolean isDeviceInTabletopMode, Function<Integer, Integer> newVerticalPositionFun)1206 private void updatePositionForVerticalReachability(boolean isDeviceInTabletopMode, 1207 Function<Integer, Integer> newVerticalPositionFun) { 1208 final int letterboxPositionForVerticalReachability = 1209 mLetterboxConfigurationPersister.getLetterboxPositionForVerticalReachability( 1210 isDeviceInTabletopMode); 1211 final int nextVerticalPosition = newVerticalPositionFun.apply( 1212 letterboxPositionForVerticalReachability); 1213 mLetterboxConfigurationPersister.setLetterboxPositionForVerticalReachability( 1214 isDeviceInTabletopMode, nextVerticalPosition); 1215 } 1216 1217 /** Whether fake sending focus is enabled for unfocused apps in splitscreen */ isCompatFakeFocusEnabled()1218 boolean isCompatFakeFocusEnabled() { 1219 return mDeviceConfig.getFlagValue(KEY_ENABLE_COMPAT_FAKE_FOCUS); 1220 } 1221 1222 /** 1223 * Whether should ignore app requested orientation in response to an app calling 1224 * {@link android.app.Activity#setRequestedOrientation}. See {@link 1225 * LetterboxUiController#shouldIgnoreRequestedOrientation} for details. 1226 */ isPolicyForIgnoringRequestedOrientationEnabled()1227 boolean isPolicyForIgnoringRequestedOrientationEnabled() { 1228 return mIsPolicyForIgnoringRequestedOrientationEnabled; 1229 } 1230 1231 /** 1232 * Whether we should use split screen aspect ratio for the activity when camera compat treatment 1233 * is enabled and activity is connected to the camera in fullscreen. 1234 */ isCameraCompatSplitScreenAspectRatioEnabled()1235 boolean isCameraCompatSplitScreenAspectRatioEnabled() { 1236 return mIsCameraCompatSplitScreenAspectRatioEnabled; 1237 } 1238 1239 /** 1240 * @return Whether camera compatibility treatment is currently enabled. 1241 */ isCameraCompatTreatmentEnabled()1242 boolean isCameraCompatTreatmentEnabled() { 1243 return mDeviceConfig.getFlagValue(KEY_ENABLE_CAMERA_COMPAT_TREATMENT); 1244 } 1245 1246 /** 1247 * @return Whether camera compatibility treatment is enabled at build time. This is used when 1248 * we need to safely initialize a component before the {@link DeviceConfig} flag value is 1249 * available. 1250 */ isCameraCompatTreatmentEnabledAtBuildTime()1251 boolean isCameraCompatTreatmentEnabledAtBuildTime() { 1252 return mDeviceConfig.isBuildTimeFlagEnabled(KEY_ENABLE_CAMERA_COMPAT_TREATMENT); 1253 } 1254 1255 /** Whether camera compatibility refresh is enabled. */ isCameraCompatRefreshEnabled()1256 boolean isCameraCompatRefreshEnabled() { 1257 return mIsCameraCompatTreatmentRefreshEnabled; 1258 } 1259 1260 /** Overrides whether camera compatibility treatment is enabled. */ setCameraCompatRefreshEnabled(boolean enabled)1261 void setCameraCompatRefreshEnabled(boolean enabled) { 1262 mIsCameraCompatTreatmentRefreshEnabled = enabled; 1263 } 1264 1265 /** 1266 * Resets whether camera compatibility treatment is enabled to {@code true}. 1267 */ resetCameraCompatRefreshEnabled()1268 void resetCameraCompatRefreshEnabled() { 1269 mIsCameraCompatTreatmentRefreshEnabled = true; 1270 } 1271 1272 /** 1273 * Whether activity "refresh" in camera compatibility treatment should happen using the 1274 * "stopped -> resumed" cycle rather than "paused -> resumed" cycle. 1275 */ isCameraCompatRefreshCycleThroughStopEnabled()1276 boolean isCameraCompatRefreshCycleThroughStopEnabled() { 1277 return mIsCameraCompatRefreshCycleThroughStopEnabled; 1278 } 1279 1280 /** 1281 * Overrides whether activity "refresh" in camera compatibility treatment should happen using 1282 * "stopped -> resumed" cycle rather than "paused -> resumed" cycle. 1283 */ setCameraCompatRefreshCycleThroughStopEnabled(boolean enabled)1284 void setCameraCompatRefreshCycleThroughStopEnabled(boolean enabled) { 1285 mIsCameraCompatRefreshCycleThroughStopEnabled = enabled; 1286 } 1287 1288 /** 1289 * Resets whether activity "refresh" in camera compatibility treatment should happen using 1290 * "stopped -> resumed" cycle rather than "paused -> resumed" cycle to {@code true}. 1291 */ resetCameraCompatRefreshCycleThroughStopEnabled()1292 void resetCameraCompatRefreshCycleThroughStopEnabled() { 1293 mIsCameraCompatRefreshCycleThroughStopEnabled = true; 1294 } 1295 1296 /** 1297 * Checks whether rotation compat policy for immersive apps that prevents auto rotation 1298 * into non-optimal screen orientation while in fullscreen is enabled at build time. This is 1299 * used when we need to safely initialize a component before the {@link DeviceConfig} flag 1300 * value is available. 1301 * 1302 * <p>This is needed because immersive apps, such as games, are often not optimized for all 1303 * orientations and can have a poor UX when rotated. Additionally, some games rely on sensors 1304 * for the gameplay so users can trigger such rotations accidentally when auto rotation is on. 1305 */ isDisplayRotationImmersiveAppCompatPolicyEnabledAtBuildTime()1306 boolean isDisplayRotationImmersiveAppCompatPolicyEnabledAtBuildTime() { 1307 return mDeviceConfig.isBuildTimeFlagEnabled( 1308 KEY_ENABLE_DISPLAY_ROTATION_IMMERSIVE_APP_COMPAT_POLICY); 1309 } 1310 1311 /** 1312 * Checks whether rotation compat policy for immersive apps that prevents auto rotation 1313 * into non-optimal screen orientation while in fullscreen is currently enabled. 1314 * 1315 * <p>This is needed because immersive apps, such as games, are often not optimized for all 1316 * orientations and can have a poor UX when rotated. Additionally, some games rely on sensors 1317 * for the gameplay so users can trigger such rotations accidentally when auto rotation is on. 1318 */ isDisplayRotationImmersiveAppCompatPolicyEnabled()1319 boolean isDisplayRotationImmersiveAppCompatPolicyEnabled() { 1320 return mDeviceConfig.getFlagValue(KEY_ENABLE_DISPLAY_ROTATION_IMMERSIVE_APP_COMPAT_POLICY); 1321 } 1322 1323 /** 1324 * Whether per-app user aspect ratio override settings is enabled 1325 */ isUserAppAspectRatioSettingsEnabled()1326 boolean isUserAppAspectRatioSettingsEnabled() { 1327 return mUserAppAspectRatioSettingsOverrideEnabled 1328 || mDeviceConfig.getFlagValue(KEY_ENABLE_USER_ASPECT_RATIO_SETTINGS); 1329 } 1330 setUserAppAspectRatioSettingsOverrideEnabled(boolean enabled)1331 void setUserAppAspectRatioSettingsOverrideEnabled(boolean enabled) { 1332 mUserAppAspectRatioSettingsOverrideEnabled = enabled; 1333 } 1334 1335 /** 1336 * Resets whether per-app user aspect ratio override settings is enabled 1337 * {@code mDeviceConfig.getFlagValue(KEY_ENABLE_USER_ASPECT_RATIO_SETTINGS)}. 1338 */ resetUserAppAspectRatioSettingsEnabled()1339 void resetUserAppAspectRatioSettingsEnabled() { 1340 setUserAppAspectRatioSettingsOverrideEnabled(false); 1341 } 1342 1343 /** 1344 * Whether fullscreen option in per-app user aspect ratio settings is enabled 1345 */ isUserAppAspectRatioFullscreenEnabled()1346 boolean isUserAppAspectRatioFullscreenEnabled() { 1347 return isUserAppAspectRatioSettingsEnabled() 1348 && (mUserAppAspectRatioFullscreenOverrideEnabled 1349 || mDeviceConfig.getFlagValue(KEY_ENABLE_USER_ASPECT_RATIO_FULLSCREEN)); 1350 } 1351 setUserAppAspectRatioFullscreenOverrideEnabled(boolean enabled)1352 void setUserAppAspectRatioFullscreenOverrideEnabled(boolean enabled) { 1353 mUserAppAspectRatioFullscreenOverrideEnabled = enabled; 1354 } 1355 resetUserAppAspectRatioFullscreenEnabled()1356 void resetUserAppAspectRatioFullscreenEnabled() { 1357 setUserAppAspectRatioFullscreenOverrideEnabled(false); 1358 } 1359 } 1360