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