Merge "Add an opt-out property for camera compat freeform treatment." into main
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 93eed37..24647f4 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -1199,6 +1199,43 @@
"android.window.PROPERTY_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE";
/**
+ * Application-level [PackageManager][android.content.pm.PackageManager.Property] tag that (when
+ * set to false) informs the system the app has opted out of the camera compatibility treatment
+ * for fixed-orientation apps, which simulates running on a portrait device, in the orientation
+ * requested by the app.
+ *
+ * <p>This treatment aims to mitigate camera issues on large screens, like stretched or sideways
+ * previews. It simulates running on a portrait device by:
+ * <ul>
+ * <li>Letterboxing the app window,
+ * <li>Cropping the camera buffer to match the app's requested orientation,
+ * <li>Setting the camera sensor orientation to portrait.
+ * <li>Setting the display rotation to match the app's requested orientation, given portrait
+ * natural orientation,
+ * <li>Refreshes the activity to trigger new camera setup, with sandboxed values.
+ * </ul>
+ *
+ * <p>To opt out of the camera compatibility treatment, add this property to your app manifest
+ * and set the value to {@code false}.
+ *
+ * <p>Not setting this property at all, or setting this property to {@code true} has no effect.
+ *
+ * <p><b>Syntax:</b>
+ * <pre>
+ * <application>
+ * <property
+ * android:name="android.window.PROPERTY_CAMERA_COMPAT_ALLOW_SIMULATE_REQUESTED_ORIENTATION"
+ * android:value="true|false"/>
+ * </application>
+ * </pre>
+ *
+ * @hide
+ */
+ //TODO(b/394590412): Make this property public.
+ String PROPERTY_CAMERA_COMPAT_ALLOW_SIMULATE_REQUESTED_ORIENTATION =
+ "android.window.PROPERTY_CAMERA_COMPAT_ALLOW_SIMULATE_REQUESTED_ORIENTATION";
+
+ /**
* Application level {@link android.content.pm.PackageManager.Property PackageManager.Property}
* for an app to inform the system that the app should be excluded from the compatibility
* override for orientation set by the device manufacturer. When the orientation override is
diff --git a/services/core/java/com/android/server/wm/AppCompatCameraOverrides.java b/services/core/java/com/android/server/wm/AppCompatCameraOverrides.java
index 47d30c9..5eed5470 100644
--- a/services/core/java/com/android/server/wm/AppCompatCameraOverrides.java
+++ b/services/core/java/com/android/server/wm/AppCompatCameraOverrides.java
@@ -23,6 +23,7 @@
import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_ONLY_FOR_CAMERA;
import static android.content.pm.ActivityInfo.OVERRIDE_ORIENTATION_ONLY_FOR_CAMERA;
import static android.view.WindowManager.PROPERTY_CAMERA_COMPAT_ALLOW_FORCE_ROTATION;
+import static android.view.WindowManager.PROPERTY_CAMERA_COMPAT_ALLOW_SIMULATE_REQUESTED_ORIENTATION;
import static android.view.WindowManager.PROPERTY_CAMERA_COMPAT_ALLOW_REFRESH;
import static android.view.WindowManager.PROPERTY_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE;
import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE;
@@ -32,6 +33,7 @@
import static com.android.server.wm.AppCompatUtils.isChangeEnabled;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import com.android.server.wm.utils.OptPropFactory;
import com.android.window.flags.Flags;
@@ -60,6 +62,8 @@
private final OptPropFactory.OptProp mCameraCompatEnableRefreshViaPauseOptProp;
@NonNull
private final OptPropFactory.OptProp mCameraCompatAllowForceRotationOptProp;
+ @Nullable
+ private final OptPropFactory.OptProp mCameraCompatAllowOrientationTreatmentOptProp;
AppCompatCameraOverrides(@NonNull ActivityRecord activityRecord,
@NonNull AppCompatConfiguration appCompatConfiguration,
@@ -80,6 +84,10 @@
mCameraCompatAllowForceRotationOptProp = optPropBuilder.create(
PROPERTY_CAMERA_COMPAT_ALLOW_FORCE_ROTATION,
isCameraCompatTreatmentEnabled);
+ mCameraCompatAllowOrientationTreatmentOptProp =
+ Flags.enableCameraCompatForDesktopWindowingOptOut() ? optPropBuilder.create(
+ PROPERTY_CAMERA_COMPAT_ALLOW_SIMULATE_REQUESTED_ORIENTATION,
+ isCameraCompatTreatmentEnabled) : null;
}
/**
@@ -168,10 +176,31 @@
* </ul>
*/
boolean shouldApplyFreeformTreatmentForCameraCompat() {
- return Flags.enableCameraCompatForDesktopWindowing() && (isChangeEnabled(mActivityRecord,
- OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT)
- || mActivityRecord.mWmService.mAppCompatConfiguration
- .isCameraCompatFreeformWindowingTreatmentEnabled());
+ return Flags.enableCameraCompatForDesktopWindowing()
+ && (shouldEnableCameraCompatFreeformTreatmentForApp()
+ || shouldEnableCameraCompatFreeformTreatmentForAllApps());
+ }
+
+ private boolean shouldEnableCameraCompatFreeformTreatmentForApp() {
+ if (mCameraCompatAllowOrientationTreatmentOptProp != null) {
+ return mCameraCompatAllowOrientationTreatmentOptProp
+ .shouldEnableWithOptOutOverrideAndProperty(isChangeEnabled(mActivityRecord,
+ OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT));
+ } else {
+ return isChangeEnabled(mActivityRecord,
+ OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT);
+ }
+ }
+
+ /**
+ * Returns whether camera compat treatment should be enabled for all apps targeted for treatment
+ * (usually fixed-orientation apps).
+ *
+ * <p>This can be enabled via adb only.
+ */
+ private boolean shouldEnableCameraCompatFreeformTreatmentForAllApps() {
+ return mActivityRecord.mWmService.mAppCompatConfiguration
+ .isCameraCompatFreeformWindowingTreatmentEnabled();
}
boolean isOverrideOrientationOnlyForCameraEnabled() {
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatCameraOverridesTest.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatCameraOverridesTest.java
index 4ad1cd1..d4be7f8 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppCompatCameraOverridesTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatCameraOverridesTest.java
@@ -23,12 +23,14 @@
import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_ONLY_FOR_CAMERA;
import static android.content.pm.ActivityInfo.OVERRIDE_ORIENTATION_ONLY_FOR_CAMERA;
import static android.view.WindowManager.PROPERTY_CAMERA_COMPAT_ALLOW_FORCE_ROTATION;
+import static android.view.WindowManager.PROPERTY_CAMERA_COMPAT_ALLOW_SIMULATE_REQUESTED_ORIENTATION;
import static android.view.WindowManager.PROPERTY_CAMERA_COMPAT_ALLOW_REFRESH;
import static android.view.WindowManager.PROPERTY_CAMERA_COMPAT_ENABLE_REFRESH_VIA_PAUSE;
import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_MIN_ASPECT_RATIO_OVERRIDE;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.window.flags.Flags.FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING;
+import static com.android.window.flags.Flags.FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING_OPT_OUT;
import android.compat.testing.PlatformCompatChangeRule;
import android.platform.test.annotations.DisableFlags;
@@ -239,6 +241,34 @@
@Test
@EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT})
+ @EnableFlags({FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING,
+ FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING_OPT_OUT})
+ public void testShouldApplyCameraCompatFreeformTreatment_propertyFalse_returnsFalse() {
+ runTestScenario((robot) -> {
+ robot.activity().createActivityWithComponentInNewTask();
+
+ robot.prop().disable(PROPERTY_CAMERA_COMPAT_ALLOW_SIMULATE_REQUESTED_ORIENTATION);
+
+ robot.checkShouldApplyFreeformTreatmentForCameraCompat(false);
+ });
+ }
+
+ @Test
+ @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT})
+ @EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
+ @DisableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING_OPT_OUT)
+ public void testShouldApplyCameraCompatFreeformTreatment_optOutFlagNotEnabled_optOutIgnored() {
+ runTestScenario((robot) -> {
+ robot.activity().createActivityWithComponentInNewTask();
+
+ robot.prop().disable(PROPERTY_CAMERA_COMPAT_ALLOW_SIMULATE_REQUESTED_ORIENTATION);
+
+ robot.checkShouldApplyFreeformTreatmentForCameraCompat(true);
+ });
+ }
+
+ @Test
+ @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT})
@EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
public void testShouldApplyCameraCompatFreeformTreatment_overrideAndFlagEnabled_returnsTrue() {
runTestScenario((robot) -> {