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