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