Loading core/java/android/app/TaskInfo.java +14 −1 Original line number Diff line number Diff line Loading @@ -213,6 +213,12 @@ public class TaskInfo { */ public boolean topActivityInSizeCompat; /** * Whether the direct top activity is eligible for letterbox education. * @hide */ public boolean topActivityEligibleForLetterboxEducation; /** * Whether this task is resizable. Unlike {@link #resizeMode} (which is what the top activity * supports), this is what the system actually uses for resizability based on other policy and Loading Loading @@ -398,7 +404,8 @@ public class TaskInfo { /** @hide */ public boolean hasCompatUI() { return hasCameraCompatControl() || topActivityInSizeCompat; return hasCameraCompatControl() || topActivityInSizeCompat || topActivityEligibleForLetterboxEducation; } /** Loading Loading @@ -460,6 +467,8 @@ public class TaskInfo { return displayId == that.displayId && taskId == that.taskId && topActivityInSizeCompat == that.topActivityInSizeCompat && topActivityEligibleForLetterboxEducation == that.topActivityEligibleForLetterboxEducation && cameraCompatControlState == that.cameraCompatControlState // Bounds are important if top activity has compat controls. && (!hasCompatUI() || configuration.windowConfiguration.getBounds() Loading Loading @@ -507,6 +516,7 @@ public class TaskInfo { isVisible = source.readBoolean(); isSleeping = source.readBoolean(); topActivityInSizeCompat = source.readBoolean(); topActivityEligibleForLetterboxEducation = source.readBoolean(); mTopActivityLocusId = source.readTypedObject(LocusId.CREATOR); displayAreaFeatureId = source.readInt(); cameraCompatControlState = source.readInt(); Loading Loading @@ -551,6 +561,7 @@ public class TaskInfo { dest.writeBoolean(isVisible); dest.writeBoolean(isSleeping); dest.writeBoolean(topActivityInSizeCompat); dest.writeBoolean(topActivityEligibleForLetterboxEducation); dest.writeTypedObject(mTopActivityLocusId, flags); dest.writeInt(displayAreaFeatureId); dest.writeInt(cameraCompatControlState); Loading Loading @@ -585,6 +596,8 @@ public class TaskInfo { + " isVisible=" + isVisible + " isSleeping=" + isSleeping + " topActivityInSizeCompat=" + topActivityInSizeCompat + " topActivityEligibleForLetterboxEducation= " + topActivityEligibleForLetterboxEducation + " locusId=" + mTopActivityLocusId + " displayAreaFeatureId=" + displayAreaFeatureId + " cameraCompatControlState=" Loading core/res/res/values/config.xml +3 −0 Original line number Diff line number Diff line Loading @@ -5136,6 +5136,9 @@ If given value is outside of this range, the option 1 (center) is assummed. --> <integer name="config_letterboxDefaultPositionForReachability">1</integer> <!-- Whether displaying letterbox education is enabled for letterboxed fullscreen apps. --> <bool name="config_letterboxIsEducationEnabled">false</bool> <!-- Whether a camera compat controller is enabled to allow the user to apply or revert treatment for stretched issues in camera viewfinder. --> <bool name="config_isCameraCompatControlForStretchedIssuesEnabled">false</bool> Loading core/res/res/values/symbols.xml +1 −0 Original line number Diff line number Diff line Loading @@ -4329,6 +4329,7 @@ <java-symbol type="dimen" name="config_letterboxHorizontalPositionMultiplier" /> <java-symbol type="bool" name="config_letterboxIsReachabilityEnabled" /> <java-symbol type="integer" name="config_letterboxDefaultPositionForReachability" /> <java-symbol type="bool" name="config_letterboxIsEducationEnabled" /> <java-symbol type="bool" name="config_isCameraCompatControlForStretchedIssuesEnabled" /> <java-symbol type="bool" name="config_hideDisplayCutoutWithDisplayArea" /> Loading libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java +40 −1 Original line number Diff line number Diff line Loading @@ -362,6 +362,45 @@ public class ShellTaskOrganizerTests { verify(mCompatUI).onCompatInfoChanged(taskInfo1, null /* taskListener */); } @Test public void testOnEligibleForLetterboxEducationActivityChanged() { final RunningTaskInfo taskInfo1 = createTaskInfo(12, WINDOWING_MODE_FULLSCREEN); taskInfo1.displayId = DEFAULT_DISPLAY; taskInfo1.topActivityEligibleForLetterboxEducation = false; final TrackingTaskListener taskListener = new TrackingTaskListener(); mOrganizer.addListenerForType(taskListener, TASK_LISTENER_TYPE_FULLSCREEN); mOrganizer.onTaskAppeared(taskInfo1, null); // Task listener sent to compat UI is null if top activity isn't eligible for letterbox // education. verify(mCompatUI).onCompatInfoChanged(taskInfo1, null /* taskListener */); // Task listener is non-null if top activity is eligible for letterbox education and task // is visible. clearInvocations(mCompatUI); final RunningTaskInfo taskInfo2 = createTaskInfo(taskInfo1.taskId, WINDOWING_MODE_FULLSCREEN); taskInfo2.displayId = taskInfo1.displayId; taskInfo2.topActivityEligibleForLetterboxEducation = true; taskInfo2.isVisible = true; mOrganizer.onTaskInfoChanged(taskInfo2); verify(mCompatUI).onCompatInfoChanged(taskInfo2, taskListener); // Task listener is null if task is invisible. clearInvocations(mCompatUI); final RunningTaskInfo taskInfo3 = createTaskInfo(taskInfo1.taskId, WINDOWING_MODE_FULLSCREEN); taskInfo3.displayId = taskInfo1.displayId; taskInfo3.topActivityEligibleForLetterboxEducation = true; taskInfo3.isVisible = false; mOrganizer.onTaskInfoChanged(taskInfo3); verify(mCompatUI).onCompatInfoChanged(taskInfo3, null /* taskListener */); clearInvocations(mCompatUI); mOrganizer.onTaskVanished(taskInfo1); verify(mCompatUI).onCompatInfoChanged(taskInfo1, null /* taskListener */); } @Test public void testOnCameraCompatActivityChanged() { final RunningTaskInfo taskInfo1 = createTaskInfo(1, WINDOWING_MODE_FULLSCREEN); Loading @@ -375,7 +414,7 @@ public class ShellTaskOrganizerTests { // compat control. verify(mCompatUI).onCompatInfoChanged(taskInfo1, null /* taskListener */); // Task linster is non-null when request a camera compat control for a visible task. // Task listener is non-null when request a camera compat control for a visible task. clearInvocations(mCompatUI); final RunningTaskInfo taskInfo2 = createTaskInfo(taskInfo1.taskId, taskInfo1.getWindowingMode()); Loading services/core/java/com/android/server/wm/ActivityRecord.java +29 −2 Original line number Diff line number Diff line Loading @@ -716,6 +716,11 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A @Nullable private Rect mLetterboxBoundsForFixedOrientationAndAspectRatio; // Whether the activity is eligible to be letterboxed for fixed orientation with respect to its // requested orientation, even when it's letterbox for another reason (e.g., size compat mode) // and therefore #isLetterboxedForFixedOrientationAndAspectRatio returns false. private boolean mIsEligibleForFixedOrientationLetterbox; // State of the Camera app compat control which is used to correct stretched viewfinder // in apps that don't handle all possible configurations and changes between them correctly. @CameraCompatControlState Loading Loading @@ -7626,6 +7631,24 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A return mLetterboxBoundsForFixedOrientationAndAspectRatio != null; } /** * Whether this activity is eligible for letterbox eduction. * * <p>Conditions that need to be met: * * <ul> * <li>{@link LetterboxConfiguration#getIsEducationEnabled} is true. * <li>The activity is eligible for fixed orientation letterbox. * <li>The activity is in fullscreen. * </ul> */ // TODO(b/215316431): Add tests boolean isEligibleForLetterboxEducation() { return mWmService.mLetterboxConfiguration.getIsEducationEnabled() && mIsEligibleForFixedOrientationLetterbox && getWindowingMode() == WINDOWING_MODE_FULLSCREEN; } /** * In some cases, applying insets to bounds changes the orientation. For example, if a * close-to-square display rotates to portrait to respect a portrait orientation activity, after Loading Loading @@ -7688,6 +7711,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A private void resolveFixedOrientationConfiguration(@NonNull Configuration newParentConfig, int windowingMode) { mLetterboxBoundsForFixedOrientationAndAspectRatio = null; mIsEligibleForFixedOrientationLetterbox = false; final Rect parentBounds = newParentConfig.windowConfiguration.getBounds(); final Rect stableBounds = new Rect(); // If orientation is respected when insets are applied, then stableBounds will be empty. Loading Loading @@ -7727,8 +7751,11 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // make it fit the available bounds by scaling down its bounds. final int forcedOrientation = getRequestedConfigurationOrientation(); if (forcedOrientation == ORIENTATION_UNDEFINED || (forcedOrientation == parentOrientation && orientationRespectedWithInsets)) { mIsEligibleForFixedOrientationLetterbox = forcedOrientation != ORIENTATION_UNDEFINED && forcedOrientation != parentOrientation; if (!mIsEligibleForFixedOrientationLetterbox && (forcedOrientation == ORIENTATION_UNDEFINED || orientationRespectedWithInsets)) { return; } Loading Loading
core/java/android/app/TaskInfo.java +14 −1 Original line number Diff line number Diff line Loading @@ -213,6 +213,12 @@ public class TaskInfo { */ public boolean topActivityInSizeCompat; /** * Whether the direct top activity is eligible for letterbox education. * @hide */ public boolean topActivityEligibleForLetterboxEducation; /** * Whether this task is resizable. Unlike {@link #resizeMode} (which is what the top activity * supports), this is what the system actually uses for resizability based on other policy and Loading Loading @@ -398,7 +404,8 @@ public class TaskInfo { /** @hide */ public boolean hasCompatUI() { return hasCameraCompatControl() || topActivityInSizeCompat; return hasCameraCompatControl() || topActivityInSizeCompat || topActivityEligibleForLetterboxEducation; } /** Loading Loading @@ -460,6 +467,8 @@ public class TaskInfo { return displayId == that.displayId && taskId == that.taskId && topActivityInSizeCompat == that.topActivityInSizeCompat && topActivityEligibleForLetterboxEducation == that.topActivityEligibleForLetterboxEducation && cameraCompatControlState == that.cameraCompatControlState // Bounds are important if top activity has compat controls. && (!hasCompatUI() || configuration.windowConfiguration.getBounds() Loading Loading @@ -507,6 +516,7 @@ public class TaskInfo { isVisible = source.readBoolean(); isSleeping = source.readBoolean(); topActivityInSizeCompat = source.readBoolean(); topActivityEligibleForLetterboxEducation = source.readBoolean(); mTopActivityLocusId = source.readTypedObject(LocusId.CREATOR); displayAreaFeatureId = source.readInt(); cameraCompatControlState = source.readInt(); Loading Loading @@ -551,6 +561,7 @@ public class TaskInfo { dest.writeBoolean(isVisible); dest.writeBoolean(isSleeping); dest.writeBoolean(topActivityInSizeCompat); dest.writeBoolean(topActivityEligibleForLetterboxEducation); dest.writeTypedObject(mTopActivityLocusId, flags); dest.writeInt(displayAreaFeatureId); dest.writeInt(cameraCompatControlState); Loading Loading @@ -585,6 +596,8 @@ public class TaskInfo { + " isVisible=" + isVisible + " isSleeping=" + isSleeping + " topActivityInSizeCompat=" + topActivityInSizeCompat + " topActivityEligibleForLetterboxEducation= " + topActivityEligibleForLetterboxEducation + " locusId=" + mTopActivityLocusId + " displayAreaFeatureId=" + displayAreaFeatureId + " cameraCompatControlState=" Loading
core/res/res/values/config.xml +3 −0 Original line number Diff line number Diff line Loading @@ -5136,6 +5136,9 @@ If given value is outside of this range, the option 1 (center) is assummed. --> <integer name="config_letterboxDefaultPositionForReachability">1</integer> <!-- Whether displaying letterbox education is enabled for letterboxed fullscreen apps. --> <bool name="config_letterboxIsEducationEnabled">false</bool> <!-- Whether a camera compat controller is enabled to allow the user to apply or revert treatment for stretched issues in camera viewfinder. --> <bool name="config_isCameraCompatControlForStretchedIssuesEnabled">false</bool> Loading
core/res/res/values/symbols.xml +1 −0 Original line number Diff line number Diff line Loading @@ -4329,6 +4329,7 @@ <java-symbol type="dimen" name="config_letterboxHorizontalPositionMultiplier" /> <java-symbol type="bool" name="config_letterboxIsReachabilityEnabled" /> <java-symbol type="integer" name="config_letterboxDefaultPositionForReachability" /> <java-symbol type="bool" name="config_letterboxIsEducationEnabled" /> <java-symbol type="bool" name="config_isCameraCompatControlForStretchedIssuesEnabled" /> <java-symbol type="bool" name="config_hideDisplayCutoutWithDisplayArea" /> Loading
libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java +40 −1 Original line number Diff line number Diff line Loading @@ -362,6 +362,45 @@ public class ShellTaskOrganizerTests { verify(mCompatUI).onCompatInfoChanged(taskInfo1, null /* taskListener */); } @Test public void testOnEligibleForLetterboxEducationActivityChanged() { final RunningTaskInfo taskInfo1 = createTaskInfo(12, WINDOWING_MODE_FULLSCREEN); taskInfo1.displayId = DEFAULT_DISPLAY; taskInfo1.topActivityEligibleForLetterboxEducation = false; final TrackingTaskListener taskListener = new TrackingTaskListener(); mOrganizer.addListenerForType(taskListener, TASK_LISTENER_TYPE_FULLSCREEN); mOrganizer.onTaskAppeared(taskInfo1, null); // Task listener sent to compat UI is null if top activity isn't eligible for letterbox // education. verify(mCompatUI).onCompatInfoChanged(taskInfo1, null /* taskListener */); // Task listener is non-null if top activity is eligible for letterbox education and task // is visible. clearInvocations(mCompatUI); final RunningTaskInfo taskInfo2 = createTaskInfo(taskInfo1.taskId, WINDOWING_MODE_FULLSCREEN); taskInfo2.displayId = taskInfo1.displayId; taskInfo2.topActivityEligibleForLetterboxEducation = true; taskInfo2.isVisible = true; mOrganizer.onTaskInfoChanged(taskInfo2); verify(mCompatUI).onCompatInfoChanged(taskInfo2, taskListener); // Task listener is null if task is invisible. clearInvocations(mCompatUI); final RunningTaskInfo taskInfo3 = createTaskInfo(taskInfo1.taskId, WINDOWING_MODE_FULLSCREEN); taskInfo3.displayId = taskInfo1.displayId; taskInfo3.topActivityEligibleForLetterboxEducation = true; taskInfo3.isVisible = false; mOrganizer.onTaskInfoChanged(taskInfo3); verify(mCompatUI).onCompatInfoChanged(taskInfo3, null /* taskListener */); clearInvocations(mCompatUI); mOrganizer.onTaskVanished(taskInfo1); verify(mCompatUI).onCompatInfoChanged(taskInfo1, null /* taskListener */); } @Test public void testOnCameraCompatActivityChanged() { final RunningTaskInfo taskInfo1 = createTaskInfo(1, WINDOWING_MODE_FULLSCREEN); Loading @@ -375,7 +414,7 @@ public class ShellTaskOrganizerTests { // compat control. verify(mCompatUI).onCompatInfoChanged(taskInfo1, null /* taskListener */); // Task linster is non-null when request a camera compat control for a visible task. // Task listener is non-null when request a camera compat control for a visible task. clearInvocations(mCompatUI); final RunningTaskInfo taskInfo2 = createTaskInfo(taskInfo1.taskId, taskInfo1.getWindowingMode()); Loading
services/core/java/com/android/server/wm/ActivityRecord.java +29 −2 Original line number Diff line number Diff line Loading @@ -716,6 +716,11 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A @Nullable private Rect mLetterboxBoundsForFixedOrientationAndAspectRatio; // Whether the activity is eligible to be letterboxed for fixed orientation with respect to its // requested orientation, even when it's letterbox for another reason (e.g., size compat mode) // and therefore #isLetterboxedForFixedOrientationAndAspectRatio returns false. private boolean mIsEligibleForFixedOrientationLetterbox; // State of the Camera app compat control which is used to correct stretched viewfinder // in apps that don't handle all possible configurations and changes between them correctly. @CameraCompatControlState Loading Loading @@ -7626,6 +7631,24 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A return mLetterboxBoundsForFixedOrientationAndAspectRatio != null; } /** * Whether this activity is eligible for letterbox eduction. * * <p>Conditions that need to be met: * * <ul> * <li>{@link LetterboxConfiguration#getIsEducationEnabled} is true. * <li>The activity is eligible for fixed orientation letterbox. * <li>The activity is in fullscreen. * </ul> */ // TODO(b/215316431): Add tests boolean isEligibleForLetterboxEducation() { return mWmService.mLetterboxConfiguration.getIsEducationEnabled() && mIsEligibleForFixedOrientationLetterbox && getWindowingMode() == WINDOWING_MODE_FULLSCREEN; } /** * In some cases, applying insets to bounds changes the orientation. For example, if a * close-to-square display rotates to portrait to respect a portrait orientation activity, after Loading Loading @@ -7688,6 +7711,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A private void resolveFixedOrientationConfiguration(@NonNull Configuration newParentConfig, int windowingMode) { mLetterboxBoundsForFixedOrientationAndAspectRatio = null; mIsEligibleForFixedOrientationLetterbox = false; final Rect parentBounds = newParentConfig.windowConfiguration.getBounds(); final Rect stableBounds = new Rect(); // If orientation is respected when insets are applied, then stableBounds will be empty. Loading Loading @@ -7727,8 +7751,11 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // make it fit the available bounds by scaling down its bounds. final int forcedOrientation = getRequestedConfigurationOrientation(); if (forcedOrientation == ORIENTATION_UNDEFINED || (forcedOrientation == parentOrientation && orientationRespectedWithInsets)) { mIsEligibleForFixedOrientationLetterbox = forcedOrientation != ORIENTATION_UNDEFINED && forcedOrientation != parentOrientation; if (!mIsEligibleForFixedOrientationLetterbox && (forcedOrientation == ORIENTATION_UNDEFINED || orientationRespectedWithInsets)) { return; } Loading