1 /* 2 * Copyright (C) 2024 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 android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; 20 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; 21 import static android.content.res.Configuration.ORIENTATION_PORTRAIT; 22 import static android.content.res.Configuration.ORIENTATION_UNDEFINED; 23 import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER; 24 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING; 25 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; 26 import static android.window.DesktopModeFlags.EXCLUDE_CAPTION_FROM_APP_BOUNDS; 27 28 import static com.android.server.wm.AppCompatConfiguration.LETTERBOX_BACKGROUND_WALLPAPER; 29 import static com.android.server.wm.AppCompatConfiguration.letterboxBackgroundTypeToString; 30 import static com.android.server.wm.AppCompatLetterboxUtils.calculateLetterboxInnerBounds; 31 import static com.android.server.wm.AppCompatLetterboxUtils.calculateLetterboxOuterBounds; 32 import static com.android.server.wm.AppCompatLetterboxUtils.calculateLetterboxPosition; 33 34 import android.annotation.NonNull; 35 import android.annotation.Nullable; 36 import android.content.res.Configuration.Orientation; 37 import android.graphics.Point; 38 import android.graphics.Rect; 39 import android.view.SurfaceControl; 40 41 import com.android.internal.annotations.VisibleForTesting; 42 import com.android.internal.statusbar.LetterboxDetails; 43 import com.android.server.wm.AppCompatConfiguration.LetterboxBackgroundType; 44 import com.android.window.flags.Flags; 45 46 import java.io.PrintWriter; 47 48 /** 49 * Encapsulates the logic for the Letterboxing policy. 50 */ 51 class AppCompatLetterboxPolicy { 52 53 private static final int DIFF_TOLERANCE_PX = 1; 54 55 @NonNull 56 private final ActivityRecord mActivityRecord; 57 @NonNull 58 private final AppCompatLetterboxPolicyState mLetterboxPolicyState; 59 @NonNull 60 private final AppCompatRoundedCorners mAppCompatRoundedCorners; 61 @NonNull 62 private final AppCompatConfiguration mAppCompatConfiguration; 63 // Convenience temporary object to save allocation when calculating Rect. 64 @NonNull 65 private final Rect mTmpRect = new Rect(); 66 67 private boolean mLastShouldShowLetterboxUi; 68 69 // Whether the activity is eligible to be letterboxed for fixed orientation with respect to its 70 // requested orientation, even when it's letterbox for another reason (e.g., size compat mode) 71 // and therefore #isLetterboxedForFixedOrientationAndAspectRatio returns false. 72 private boolean mIsEligibleForFixedOrientationLetterbox; 73 AppCompatLetterboxPolicy(@onNull ActivityRecord activityRecord, @NonNull AppCompatConfiguration appCompatConfiguration)74 AppCompatLetterboxPolicy(@NonNull ActivityRecord activityRecord, 75 @NonNull AppCompatConfiguration appCompatConfiguration) { 76 mActivityRecord = activityRecord; 77 mLetterboxPolicyState = Flags.appCompatRefactoring() ? new ShellLetterboxPolicyState() 78 : new LegacyLetterboxPolicyState(); 79 // TODO (b/358334569) Improve cutout logic dependency on app compat. 80 mAppCompatRoundedCorners = new AppCompatRoundedCorners(mActivityRecord, 81 this::ieEligibleForRoundedCorners); 82 mAppCompatConfiguration = appCompatConfiguration; 83 } 84 resetFixedOrientationLetterboxEligibility()85 void resetFixedOrientationLetterboxEligibility() { 86 mIsEligibleForFixedOrientationLetterbox = false; 87 } 88 89 /** Cleans up {@link Letterbox} if it exists.*/ stop()90 void stop() { 91 mLetterboxPolicyState.stop(); 92 } 93 94 /** @return {@code true} if the letterbox policy is running and the activity letterboxed. */ isRunning()95 boolean isRunning() { 96 return mLetterboxPolicyState.isRunning(); 97 } 98 onMovedToDisplay(int displayId)99 void onMovedToDisplay(int displayId) { 100 mLetterboxPolicyState.onMovedToDisplay(displayId); 101 } 102 103 /** Gets the letterbox insets. The insets will be empty if there is no letterbox. */ 104 @NonNull getLetterboxInsets()105 Rect getLetterboxInsets() { 106 return mLetterboxPolicyState.getLetterboxInsets(); 107 } 108 109 /** Gets the inner bounds of letterbox. The bounds will be empty if there is no letterbox. */ getLetterboxInnerBounds(@onNull Rect outBounds)110 void getLetterboxInnerBounds(@NonNull Rect outBounds) { 111 mLetterboxPolicyState.getLetterboxInnerBounds(outBounds); 112 } 113 114 /** 115 * Checks if the current activity is eligible to be letterboxed because of a fixed orientation. 116 * 117 * @param forcedOrientation The requeste orientation 118 * @param parentOrientation The orientation of the parent container. 119 * @return {@code true} if the activity can be letterboxed because of the requested fixed 120 * orientation. 121 */ resolveFixedOrientationLetterboxEligibility(@rientation int forcedOrientation, @Orientation int parentOrientation)122 boolean resolveFixedOrientationLetterboxEligibility(@Orientation int forcedOrientation, 123 @Orientation int parentOrientation) { 124 mIsEligibleForFixedOrientationLetterbox = forcedOrientation != ORIENTATION_UNDEFINED 125 && forcedOrientation != parentOrientation; 126 return mIsEligibleForFixedOrientationLetterbox; 127 } 128 129 /** 130 * Whether this activity is eligible for letterbox eduction. 131 * 132 * <p>Conditions that need to be met: 133 * 134 * <ul> 135 * <li>{@link AppCompatConfiguration#getIsEducationEnabled} is true. 136 * <li>The activity is eligible for fixed orientation letterbox. 137 * <li>The activity is in fullscreen. 138 * <li>The activity is portrait-only. 139 * <li>The activity doesn't have a starting window (education should only be displayed 140 * once the starting window is removed in {@link ActivityRecord#removeStartingWindow}). 141 * </ul> 142 */ isEligibleForLetterboxEducation()143 boolean isEligibleForLetterboxEducation() { 144 return mAppCompatConfiguration.getIsEducationEnabled() 145 && mIsEligibleForFixedOrientationLetterbox 146 && mActivityRecord.getWindowingMode() == WINDOWING_MODE_FULLSCREEN 147 && mActivityRecord.getRequestedConfigurationOrientation() == ORIENTATION_PORTRAIT 148 && mActivityRecord.mStartingWindow == null; 149 } 150 151 @Nullable getLetterboxDetails()152 LetterboxDetails getLetterboxDetails() { 153 final WindowState w = mActivityRecord.findMainWindow(); 154 if (!isRunning() || w == null || w.isLetterboxedForDisplayCutout()) { 155 return null; 156 } 157 final Rect letterboxInnerBounds = new Rect(); 158 final Rect letterboxOuterBounds = new Rect(); 159 mLetterboxPolicyState.getLetterboxInnerBounds(letterboxInnerBounds); 160 mLetterboxPolicyState.getLetterboxOuterBounds(letterboxOuterBounds); 161 162 if (letterboxInnerBounds.isEmpty() || letterboxOuterBounds.isEmpty()) { 163 return null; 164 } 165 166 return new LetterboxDetails( 167 letterboxInnerBounds, 168 letterboxOuterBounds, 169 w.mAttrs.insetsFlags.appearance 170 ); 171 } 172 173 /** 174 * @return {@code true} if bar shown within a given rectangle is allowed to be fully transparent 175 * when the current activity is displayed. 176 */ isFullyTransparentBarAllowed(@onNull Rect rect)177 boolean isFullyTransparentBarAllowed(@NonNull Rect rect) { 178 return mLetterboxPolicyState.isFullyTransparentBarAllowed(rect); 179 } 180 181 /** 182 * Updates the letterbox surfaces in case this is needed. 183 * 184 * @param winHint The WindowState for the letterboxed Activity. 185 * @param t The current Transaction. 186 * @param inputT The pending transaction used for the input surface. 187 */ updateLetterboxSurfaceIfNeeded(@onNull WindowState winHint, @NonNull SurfaceControl.Transaction t, @NonNull SurfaceControl.Transaction inputT)188 void updateLetterboxSurfaceIfNeeded(@NonNull WindowState winHint, 189 @NonNull SurfaceControl.Transaction t, 190 @NonNull SurfaceControl.Transaction inputT) { 191 mLetterboxPolicyState.updateLetterboxSurfaceIfNeeded(winHint, t, inputT); 192 } 193 updateLetterboxSurfaceIfNeeded(@onNull WindowState winHint)194 void updateLetterboxSurfaceIfNeeded(@NonNull WindowState winHint) { 195 mLetterboxPolicyState.updateLetterboxSurfaceIfNeeded(winHint, 196 mActivityRecord.getSyncTransaction(), mActivityRecord.getPendingTransaction()); 197 } 198 start(@onNull WindowState w)199 void start(@NonNull WindowState w) { 200 if (shouldNotLayoutLetterbox(w)) { 201 return; 202 } 203 mAppCompatRoundedCorners.updateRoundedCornersIfNeeded(w); 204 updateWallpaperForLetterbox(w); 205 if (shouldShowLetterboxUi(w)) { 206 mLetterboxPolicyState.layoutLetterboxIfNeeded(w); 207 } else { 208 mLetterboxPolicyState.hide(); 209 } 210 } 211 212 @VisibleForTesting shouldShowLetterboxUi(@onNull WindowState mainWindow)213 boolean shouldShowLetterboxUi(@NonNull WindowState mainWindow) { 214 if (mActivityRecord.mAppCompatController.getOrientationOverrides() 215 .getIsRelaunchingAfterRequestedOrientationChanged()) { 216 return mLastShouldShowLetterboxUi; 217 } 218 219 final boolean shouldShowLetterboxUi = 220 (mActivityRecord.isVisible() 221 || mActivityRecord.isVisibleRequested()) 222 && mainWindow.areAppWindowBoundsLetterboxed() 223 // Check for FLAG_SHOW_WALLPAPER explicitly instead of using 224 // WindowContainer#showWallpaper because the later will return true when 225 // this activity is using blurred wallpaper for letterbox background. 226 && (mainWindow.mAttrs.flags & FLAG_SHOW_WALLPAPER) == 0; 227 228 mLastShouldShowLetterboxUi = shouldShowLetterboxUi; 229 230 return shouldShowLetterboxUi; 231 } 232 233 @VisibleForTesting 234 @Nullable getCropBoundsIfNeeded(@onNull final WindowState mainWindow)235 Rect getCropBoundsIfNeeded(@NonNull final WindowState mainWindow) { 236 return mAppCompatRoundedCorners.getCropBoundsIfNeeded(mainWindow); 237 } 238 239 /** 240 * Returns rounded corners radius the letterboxed activity should have based on override in 241 * R.integer.config_letterboxActivityCornersRadius or min device bottom corner radii. 242 * Device corners can be different on the right and left sides, but we use the same radius 243 * for all corners for consistency and pick a minimal bottom one for consistency with a 244 * taskbar rounded corners. 245 * 246 * @param mainWindow The {@link WindowState} to consider for the rounded corners calculation. 247 */ getRoundedCornersRadius(@onNull final WindowState mainWindow)248 int getRoundedCornersRadius(@NonNull final WindowState mainWindow) { 249 return mAppCompatRoundedCorners.getRoundedCornersRadius(mainWindow); 250 } 251 dump(@onNull PrintWriter pw, @NonNull String prefix)252 void dump(@NonNull PrintWriter pw, @NonNull String prefix) { 253 final WindowState mainWin = mActivityRecord.findMainWindow(); 254 if (mainWin == null) { 255 return; 256 } 257 boolean areBoundsLetterboxed = mainWin.areAppWindowBoundsLetterboxed(); 258 pw.println(prefix + "areBoundsLetterboxed=" + areBoundsLetterboxed); 259 pw.println(prefix + "isLetterboxRunning=" + isRunning()); 260 if (!areBoundsLetterboxed) { 261 return; 262 } 263 pw.println(prefix + " letterboxReason=" 264 + AppCompatUtils.getLetterboxReasonString(mActivityRecord, mainWin)); 265 mActivityRecord.mAppCompatController.getReachabilityPolicy().dump(pw, prefix); 266 final AppCompatLetterboxOverrides letterboxOverride = mActivityRecord.mAppCompatController 267 .getLetterboxOverrides(); 268 pw.println(prefix + " letterboxBackgroundColor=" + Integer.toHexString( 269 letterboxOverride.getLetterboxBackgroundColor().toArgb())); 270 pw.println(prefix + " letterboxBackgroundType=" 271 + letterboxBackgroundTypeToString(letterboxOverride.getLetterboxBackgroundType())); 272 pw.println(prefix + " letterboxCornerRadius=" + getRoundedCornersRadius(mainWin)); 273 if (letterboxOverride.getLetterboxBackgroundType() == LETTERBOX_BACKGROUND_WALLPAPER) { 274 pw.println(prefix + " isLetterboxWallpaperBlurSupported=" 275 + letterboxOverride.isLetterboxWallpaperBlurSupported()); 276 pw.println(prefix + " letterboxBackgroundWallpaperDarkScrimAlpha=" 277 + letterboxOverride.getLetterboxWallpaperDarkScrimAlpha()); 278 pw.println(prefix + " letterboxBackgroundWallpaperBlurRadius=" 279 + letterboxOverride.getLetterboxWallpaperBlurRadiusPx()); 280 } 281 mAppCompatConfiguration.dump(pw, prefix); 282 } 283 updateWallpaperForLetterbox(@onNull WindowState mainWindow)284 private void updateWallpaperForLetterbox(@NonNull WindowState mainWindow) { 285 final AppCompatLetterboxOverrides letterboxOverrides = mActivityRecord 286 .mAppCompatController.getLetterboxOverrides(); 287 final @LetterboxBackgroundType int letterboxBackgroundType = 288 letterboxOverrides.getLetterboxBackgroundType(); 289 boolean wallpaperShouldBeShown = 290 letterboxBackgroundType == LETTERBOX_BACKGROUND_WALLPAPER 291 // Don't use wallpaper as a background if letterboxed for display cutout. 292 && isLetterboxedNotForDisplayCutout(mainWindow) 293 // Check that dark scrim alpha or blur radius are provided 294 && (letterboxOverrides.getLetterboxWallpaperBlurRadiusPx() > 0 295 || letterboxOverrides.getLetterboxWallpaperDarkScrimAlpha() > 0) 296 // Check that blur is supported by a device if blur radius is provided. 297 && (letterboxOverrides.getLetterboxWallpaperBlurRadiusPx() <= 0 298 || letterboxOverrides.isLetterboxWallpaperBlurSupported()); 299 if (letterboxOverrides.checkWallpaperBackgroundForLetterbox(wallpaperShouldBeShown)) { 300 mActivityRecord.requestUpdateWallpaperIfNeeded(); 301 } 302 } 303 ieEligibleForRoundedCorners(@onNull WindowState mainWindow)304 private boolean ieEligibleForRoundedCorners(@NonNull WindowState mainWindow) { 305 return isLetterboxedNotForDisplayCutout(mainWindow) 306 && !isFreeformActivityMatchParentAppBoundsHeight(); 307 } 308 isLetterboxedNotForDisplayCutout(@onNull WindowState mainWindow)309 private boolean isLetterboxedNotForDisplayCutout(@NonNull WindowState mainWindow) { 310 return shouldShowLetterboxUi(mainWindow) 311 && !mainWindow.isLetterboxedForDisplayCutout(); 312 } 313 isFreeformActivityMatchParentAppBoundsHeight()314 private boolean isFreeformActivityMatchParentAppBoundsHeight() { 315 if (!EXCLUDE_CAPTION_FROM_APP_BOUNDS.isTrue()) { 316 return false; 317 } 318 final Task task = mActivityRecord.getTask(); 319 if (task == null) { 320 return false; 321 } 322 final Rect parentAppBounds = task.getWindowConfiguration().getAppBounds(); 323 if (parentAppBounds == null) { 324 return false; 325 } 326 327 mLetterboxPolicyState.getLetterboxInnerBounds(mTmpRect); 328 final int diff = parentAppBounds.height() - mTmpRect.height(); 329 // Compare bounds with tolerance of 1 px to account for rounding error calculations. 330 return task.getWindowingMode() == WINDOWING_MODE_FREEFORM && diff <= DIFF_TOLERANCE_PX; 331 } 332 shouldNotLayoutLetterbox(@ullable WindowState w)333 private static boolean shouldNotLayoutLetterbox(@Nullable WindowState w) { 334 if (w == null) { 335 return true; 336 } 337 final int type = w.mAttrs.type; 338 // Allow letterbox to be displayed early for base application or application starting 339 // windows even if it is not on the top z order to prevent flickering when the 340 // letterboxed window is brought to the top 341 return (type != TYPE_BASE_APPLICATION && type != TYPE_APPLICATION_STARTING) 342 || w.mAnimatingExit; 343 } 344 345 /** 346 * Existing {@link AppCompatLetterboxPolicyState} implementation. 347 * TODO(b/375339716): Clean code for legacy implementation. 348 */ 349 private class LegacyLetterboxPolicyState implements AppCompatLetterboxPolicyState { 350 351 @Nullable 352 private Letterbox mLetterbox; 353 354 @Override layoutLetterboxIfNeeded(@onNull WindowState w)355 public void layoutLetterboxIfNeeded(@NonNull WindowState w) { 356 if (!isRunning()) { 357 final AppCompatLetterboxOverrides letterboxOverrides = mActivityRecord 358 .mAppCompatController.getLetterboxOverrides(); 359 final AppCompatReachabilityPolicy reachabilityPolicy = mActivityRecord 360 .mAppCompatController.getReachabilityPolicy(); 361 mLetterbox = new Letterbox(() -> mActivityRecord.makeChildSurface(null), 362 mActivityRecord.mWmService.mTransactionFactory, 363 reachabilityPolicy, letterboxOverrides); 364 mActivityRecord.mAppCompatController.getReachabilityPolicy() 365 .setLetterboxInnerBoundsSupplier(mLetterbox::getInnerFrame); 366 } 367 final Point letterboxPosition = new Point(); 368 calculateLetterboxPosition(mActivityRecord, letterboxPosition); 369 final Rect spaceToFill = new Rect(); 370 calculateLetterboxOuterBounds(mActivityRecord, spaceToFill); 371 final Rect innerFrame = new Rect(); 372 calculateLetterboxInnerBounds(mActivityRecord, w, innerFrame); 373 mLetterbox.layout(spaceToFill, innerFrame, letterboxPosition); 374 if (mActivityRecord.mAppCompatController.getReachabilityOverrides() 375 .isDoubleTapEvent()) { 376 // We need to notify Shell that letterbox position has changed. 377 mActivityRecord.getTask().dispatchTaskInfoChangedIfNeeded(true /* force */); 378 } 379 } 380 381 /** 382 * @return {@code true} if the policy is running and so if the current activity is 383 * letterboxed. 384 */ 385 @Override isRunning()386 public boolean isRunning() { 387 return mLetterbox != null; 388 } 389 390 @Override onMovedToDisplay(int displayId)391 public void onMovedToDisplay(int displayId) { 392 if (isRunning()) { 393 mLetterbox.onMovedToDisplay(displayId); 394 } 395 } 396 397 /** Cleans up {@link Letterbox} if it exists.*/ 398 @Override stop()399 public void stop() { 400 if (isRunning()) { 401 mLetterbox.destroy(); 402 mLetterbox = null; 403 } 404 mActivityRecord.mAppCompatController.getReachabilityPolicy() 405 .setLetterboxInnerBoundsSupplier(null); 406 } 407 408 @Override updateLetterboxSurfaceIfNeeded(@onNull WindowState winHint, @NonNull SurfaceControl.Transaction t, @NonNull SurfaceControl.Transaction inputT)409 public void updateLetterboxSurfaceIfNeeded(@NonNull WindowState winHint, 410 @NonNull SurfaceControl.Transaction t, 411 @NonNull SurfaceControl.Transaction inputT) { 412 if (shouldNotLayoutLetterbox(winHint)) { 413 return; 414 } 415 start(winHint); 416 if (isRunning() && mLetterbox.needsApplySurfaceChanges()) { 417 mLetterbox.applySurfaceChanges(t, inputT, winHint); 418 } 419 } 420 421 @Override hide()422 public void hide() { 423 if (isRunning()) { 424 mLetterbox.hide(); 425 } 426 } 427 428 /** Gets the letterbox insets. The insets will be empty if there is no letterbox. */ 429 @Override 430 @NonNull getLetterboxInsets()431 public Rect getLetterboxInsets() { 432 if (isRunning()) { 433 return mLetterbox.getInsets(); 434 } else { 435 return new Rect(); 436 } 437 } 438 439 /** Gets the inner bounds of letterbox. The bounds will be empty with no letterbox. */ 440 @Override getLetterboxInnerBounds(@onNull Rect outBounds)441 public void getLetterboxInnerBounds(@NonNull Rect outBounds) { 442 if (isRunning()) { 443 outBounds.set(mLetterbox.getInnerFrame()); 444 final WindowState w = mActivityRecord.findMainWindow(); 445 if (w != null) { 446 AppCompatUtils.adjustBoundsForTaskbar(w, outBounds); 447 } 448 } else { 449 outBounds.setEmpty(); 450 } 451 } 452 453 /** Gets the outer bounds of letterbox. The bounds will be empty with no letterbox. */ 454 @Override getLetterboxOuterBounds(@onNull Rect outBounds)455 public void getLetterboxOuterBounds(@NonNull Rect outBounds) { 456 if (isRunning()) { 457 outBounds.set(mLetterbox.getOuterFrame()); 458 } else { 459 outBounds.setEmpty(); 460 } 461 } 462 463 /** 464 * @return {@code true} if bar shown within a given rectangle is allowed to be fully 465 * transparent when the current activity is displayed. 466 */ 467 @Override isFullyTransparentBarAllowed(@onNull Rect rect)468 public boolean isFullyTransparentBarAllowed(@NonNull Rect rect) { 469 return !isRunning() || mLetterbox.notIntersectsOrFullyContains(rect); 470 } 471 } 472 473 /** 474 * {@link AppCompatLetterboxPolicyState} implementation for the letterbox presentation on shell. 475 */ 476 private class ShellLetterboxPolicyState implements AppCompatLetterboxPolicyState { 477 478 private final Rect mInnerBounds = new Rect(); 479 private final Rect mOuterBounds = new Rect(); 480 private final Point mLetterboxPosition = new Point(); 481 private boolean mRunning; 482 483 @Override layoutLetterboxIfNeeded(@onNull WindowState w)484 public void layoutLetterboxIfNeeded(@NonNull WindowState w) { 485 mRunning = true; 486 calculateLetterboxPosition(mActivityRecord, mLetterboxPosition); 487 calculateLetterboxOuterBounds(mActivityRecord, mOuterBounds); 488 calculateLetterboxInnerBounds(mActivityRecord, w, mInnerBounds); 489 mActivityRecord.mAppCompatController.getReachabilityPolicy() 490 .setLetterboxInnerBoundsSupplier(() -> mInnerBounds); 491 } 492 493 @Override isRunning()494 public boolean isRunning() { 495 return mRunning; 496 } 497 498 @Override onMovedToDisplay(int displayId)499 public void onMovedToDisplay(int displayId) { 500 // TODO(b/374918469): Handle Display Change for Letterbox in Shell 501 } 502 503 @Override stop()504 public void stop() { 505 if (!isRunning()) { 506 return; 507 } 508 mRunning = false; 509 mLetterboxPosition.set(0, 0); 510 mInnerBounds.setEmpty(); 511 mOuterBounds.setEmpty(); 512 mActivityRecord.mAppCompatController.getReachabilityPolicy() 513 .setLetterboxInnerBoundsSupplier(null); 514 } 515 516 @Override hide()517 public void hide() { 518 if (!isRunning()) { 519 return; 520 } 521 mLetterboxPosition.set(0, 0); 522 mInnerBounds.setEmpty(); 523 mOuterBounds.setEmpty(); 524 } 525 526 @NonNull 527 @Override getLetterboxInsets()528 public Rect getLetterboxInsets() { 529 if (isRunning()) { 530 return new Rect( 531 Math.max(0, mInnerBounds.left - mOuterBounds.left), 532 Math.max(0, mOuterBounds.top - mInnerBounds.top), 533 Math.max(0, mOuterBounds.right - mInnerBounds.right), 534 Math.max(0, mInnerBounds.bottom - mOuterBounds.bottom) 535 ); 536 } 537 return new Rect(); 538 } 539 540 @Override getLetterboxInnerBounds(@onNull Rect outBounds)541 public void getLetterboxInnerBounds(@NonNull Rect outBounds) { 542 if (isRunning()) { 543 outBounds.set(mInnerBounds); 544 final WindowState w = mActivityRecord.findMainWindow(); 545 if (w != null) { 546 AppCompatUtils.adjustBoundsForTaskbar(w, outBounds); 547 } 548 } else { 549 outBounds.setEmpty(); 550 } 551 } 552 553 @Override getLetterboxOuterBounds(@onNull Rect outBounds)554 public void getLetterboxOuterBounds(@NonNull Rect outBounds) { 555 if (isRunning()) { 556 outBounds.set(mOuterBounds); 557 } else { 558 outBounds.setEmpty(); 559 } 560 } 561 562 @Override updateLetterboxSurfaceIfNeeded(@onNull WindowState winHint, @NonNull SurfaceControl.Transaction t, @NonNull SurfaceControl.Transaction inputT)563 public void updateLetterboxSurfaceIfNeeded(@NonNull WindowState winHint, 564 @NonNull SurfaceControl.Transaction t, 565 @NonNull SurfaceControl.Transaction inputT) { 566 567 if (shouldNotLayoutLetterbox(winHint)) { 568 return; 569 } 570 start(winHint); 571 } 572 573 @Override isFullyTransparentBarAllowed(@onNull Rect rect)574 public boolean isFullyTransparentBarAllowed(@NonNull Rect rect) { 575 // TODO(b/374921442) Handle Transparent Activities Letterboxing in Shell. 576 // At the moment Shell handles letterbox with a single surface. This would make 577 // notIntersectsOrFullyContains() to return false in the existing Letterbox 578 // implementation. 579 // Note: Previous implementation is 580 // !isRunning() || mLetterbox.notIntersectsOrFullyContains(rect); 581 return !isRunning(); 582 } 583 } 584 } 585