• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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