From 72b8801b69e4dc2f5220066dd20fb292db36f3bc Mon Sep 17 00:00:00 2001 From: tomnatan Date: Thu, 20 Jan 2022 12:09:34 +0000 Subject: [PATCH] [2/n] Letterbox Education: Add information about fixed orientation letterbox to TaskInfo Bug: 207010227 Test: atest WmTests:ActivityRecordTests Test: atest WmTests:SizeCompatTests Test: atest WMShellUnitTests:ShellTaskOrganizerTests Change-Id: I04e82eb494c84bd78c269dd244bdd4a3e4d5fcc8 --- core/java/android/app/TaskInfo.java | 15 ++++++- core/res/res/values/config.xml | 3 ++ core/res/res/values/symbols.xml | 1 + .../wm/shell/ShellTaskOrganizerTests.java | 41 ++++++++++++++++++- .../com/android/server/wm/ActivityRecord.java | 31 +++++++++++++- .../server/wm/LetterboxConfiguration.java | 28 +++++++++++++ .../core/java/com/android/server/wm/Task.java | 3 ++ .../server/wm/WindowManagerShellCommand.java | 36 +++++++++++++++- 8 files changed, 153 insertions(+), 5 deletions(-) diff --git a/core/java/android/app/TaskInfo.java b/core/java/android/app/TaskInfo.java index 18f9379c41110..3d2c03dc4e247 100644 --- a/core/java/android/app/TaskInfo.java +++ b/core/java/android/app/TaskInfo.java @@ -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 @@ -398,7 +404,8 @@ public class TaskInfo { /** @hide */ public boolean hasCompatUI() { - return hasCameraCompatControl() || topActivityInSizeCompat; + return hasCameraCompatControl() || topActivityInSizeCompat + || topActivityEligibleForLetterboxEducation; } /** @@ -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() @@ -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(); @@ -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); @@ -585,6 +596,8 @@ public class TaskInfo { + " isVisible=" + isVisible + " isSleeping=" + isSleeping + " topActivityInSizeCompat=" + topActivityInSizeCompat + + " topActivityEligibleForLetterboxEducation= " + + topActivityEligibleForLetterboxEducation + " locusId=" + mTopActivityLocusId + " displayAreaFeatureId=" + displayAreaFeatureId + " cameraCompatControlState=" diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 249881e7c1262..644c1fa298f10 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -5134,6 +5134,9 @@ If given value is outside of this range, the option 1 (center) is assummed. --> 1 + + false + false diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index bcc3a6df31803..820733cd488c0 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -4323,6 +4323,7 @@ + diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java index 825320b4e7840..a6caefe6d3e71 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java @@ -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); @@ -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()); diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 76434c71d3424..1e356dc9f1bb1 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -715,6 +715,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 @@ -7616,6 +7621,24 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A return mLetterboxBoundsForFixedOrientationAndAspectRatio != null; } + /** + * Whether this activity is eligible for letterbox eduction. + * + *

Conditions that need to be met: + * + *

    + *
  • {@link LetterboxConfiguration#getIsEducationEnabled} is true. + *
  • The activity is eligible for fixed orientation letterbox. + *
  • The activity is in fullscreen. + *
+ */ + // 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 @@ -7678,6 +7701,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. @@ -7717,8 +7741,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; } diff --git a/services/core/java/com/android/server/wm/LetterboxConfiguration.java b/services/core/java/com/android/server/wm/LetterboxConfiguration.java index 1955e30aab302..ad2767c41e82e 100644 --- a/services/core/java/com/android/server/wm/LetterboxConfiguration.java +++ b/services/core/java/com/android/server/wm/LetterboxConfiguration.java @@ -126,6 +126,9 @@ final class LetterboxConfiguration { @LetterboxReachabilityPosition private volatile int mLetterboxPositionForReachability; + // Whether education is allowed for letterboxed fullscreen apps. + private boolean mIsEducationEnabled; + LetterboxConfiguration(Context systemUiContext) { mContext = systemUiContext; mFixedOrientationLetterboxAspectRatio = mContext.getResources().getFloat( @@ -143,6 +146,8 @@ final class LetterboxConfiguration { R.bool.config_letterboxIsReachabilityEnabled); mDefaultPositionForReachability = readLetterboxReachabilityPositionFromConfig(mContext); mLetterboxPositionForReachability = mDefaultPositionForReachability; + mIsEducationEnabled = mContext.getResources().getBoolean( + R.bool.config_letterboxIsEducationEnabled); } /** @@ -501,4 +506,27 @@ final class LetterboxConfiguration { mLetterboxPositionForReachability = Math.max(mLetterboxPositionForReachability - 1, 0); } + /** + * Whether education is allowed for letterboxed fullscreen apps. + */ + boolean getIsEducationEnabled() { + return mIsEducationEnabled; + } + + /** + * Overrides whether education is allowed for letterboxed fullscreen apps. + */ + void setIsEducationEnabled(boolean enabled) { + mIsEducationEnabled = enabled; + } + + /** + * Resets whether education is allowed for letterboxed fullscreen apps to + * {@link R.bool.config_letterboxIsEducationEnabled}. + */ + void resetIsEducationEnabled() { + mIsEducationEnabled = mContext.getResources().getBoolean( + R.bool.config_letterboxIsEducationEnabled); + } + } diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index ff9d9f78a5c29..664035589fea4 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -3434,6 +3434,9 @@ class Task extends TaskFragment { // Whether the direct top activity is in size compat mode on foreground. info.topActivityInSizeCompat = isTopActivityResumed && mReuseActivitiesReport.top.inSizeCompatMode(); + // Whether the direct top activity is eligible for letterbox education. + info.topActivityEligibleForLetterboxEducation = isTopActivityResumed + && mReuseActivitiesReport.top.isEligibleForLetterboxEducation(); // Whether the direct top activity requested showing camera compat control. info.cameraCompatControlState = isTopActivityResumed ? mReuseActivitiesReport.top.getCameraCompatControlState() diff --git a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java index 0f8587c999583..1cf4c1b0fbb23 100644 --- a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java +++ b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java @@ -822,6 +822,29 @@ public class WindowManagerShellCommand extends ShellCommand { return 0; } + private int runSetLetterboxIsEducationEnabled(PrintWriter pw) throws RemoteException { + String arg = getNextArg(); + final boolean enabled; + switch (arg) { + case "true": + case "1": + enabled = true; + break; + case "false": + case "0": + enabled = false; + break; + default: + getErrPrintWriter().println("Error: expected true, 1, false, 0, but got " + arg); + return -1; + } + + synchronized (mInternal.mGlobalLock) { + mLetterboxConfiguration.setIsEducationEnabled(enabled); + } + return 0; + } + private int runSetLetterboxStyle(PrintWriter pw) throws RemoteException { if (peekNextArg() == null) { getErrPrintWriter().println("Error: No arguments provided."); @@ -859,6 +882,9 @@ public class WindowManagerShellCommand extends ShellCommand { case "--defaultPositionForReachability": runSetLetterboxDefaultPositionForReachability(pw); break; + case "--isEducationEnabled": + runSetLetterboxIsEducationEnabled(pw); + break; default: getErrPrintWriter().println( "Error: Unrecognized letterbox style option: " + arg); @@ -903,6 +929,9 @@ public class WindowManagerShellCommand extends ShellCommand { case "defaultPositionForReachability": mLetterboxConfiguration.getDefaultPositionForReachability(); break; + case "isEducationEnabled": + mLetterboxConfiguration.getIsEducationEnabled(); + break; default: getErrPrintWriter().println( "Error: Unrecognized letterbox style option: " + arg); @@ -998,6 +1027,7 @@ public class WindowManagerShellCommand extends ShellCommand { mLetterboxConfiguration.resetLetterboxHorizontalPositionMultiplier(); mLetterboxConfiguration.resetIsReachabilityEnabled(); mLetterboxConfiguration.resetDefaultPositionForReachability(); + mLetterboxConfiguration.resetIsEducationEnabled(); } } @@ -1014,6 +1044,8 @@ public class WindowManagerShellCommand extends ShellCommand { pw.println("Default position for reachability: " + LetterboxConfiguration.letterboxReachabilityPositionToString( mLetterboxConfiguration.getDefaultPositionForReachability())); + pw.println("Is education enabled: " + + mLetterboxConfiguration.getIsEducationEnabled()); pw.println("Background type: " + LetterboxConfiguration.letterboxBackgroundTypeToString( @@ -1154,10 +1186,12 @@ public class WindowManagerShellCommand extends ShellCommand { pw.println(" --defaultPositionForReachability [left|center|right]"); pw.println(" Default horizontal position of app window when reachability is."); pw.println(" enabled."); + pw.println(" --isEducationEnabled [true|1|false|0]"); + pw.println(" Whether education is allowed for letterboxed fullscreen apps."); pw.println(" reset-letterbox-style [aspectRatio|cornerRadius|backgroundType"); pw.println(" |backgroundColor|wallpaperBlurRadius|wallpaperDarkScrimAlpha"); pw.println(" |horizontalPositionMultiplier|isReachabilityEnabled"); - pw.println(" |defaultPositionMultiplierForReachability]"); + pw.println(" isEducationEnabled||defaultPositionMultiplierForReachability]"); pw.println(" Resets overrides to default values for specified properties separated"); pw.println(" by space, e.g. 'reset-letterbox-style aspectRatio cornerRadius'."); pw.println(" If no arguments provided, all values will be reset.");