Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit fa068dd8 authored by Mina Karadzic's avatar Mina Karadzic
Browse files

Allow camera compat (simulate requested orientation) for landscape cameras.

Flag: com.android.window.flags.camera_compat_landscape_camera_support
Bug: 390183440
Bug: 432651608
Fixes: 365725400
Test: atest WmTests:AppCompatCameraSimReqOrientationPolicyTests
Test: manual, tested on a landscape camera and display.
Change-Id: Ie18d2ce2b9aad08411652266fe9ecbe1279d0bf0
parent 808f0399
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -100,6 +100,10 @@ class AppCompatCameraRotationState {
    boolean isCameraDeviceNaturalOrientationPortrait() {
        // Per CDD (7.5.5 C-1-1), camera sensor orientation and display natural orientation have to
        // be the same (portrait or landscape).
        // TODO(b/444213250): this is not always correct, for example for some landscape foldables,
        //  natural orientation of some displays and camera sensors may differ. Instead, query the
        //  camera sensors for their natural orientation. Also make sure no camera sensor sandboxing
        //  affects that.
        return getDisplayContentTiedToCamera().getNaturalOrientation() == ORIENTATION_PORTRAIT;
    }

+18 −12
Original line number Diff line number Diff line
@@ -276,8 +276,7 @@ final class AppCompatCameraSimReqOrientationPolicy implements AppCompatCameraSta
                        .setShouldLetterboxForCameraCompat(displayRotation != ROTATION_UNDEFINED)
                        .setRotateAndCropRotation(getCameraRotationFromSandboxedDisplayRotation(
                                displayRotation))
                        // TODO(b/365725400): support landscape cameras.
                        .setShouldOverrideSensorOrientation(false)
                        .setShouldOverrideSensorOrientation(shouldOverrideSensorOrientation())
                        .setShouldAllowTransformInverseDisplay(false);
            } else if (isExternalDisplaySandboxEnabledForActivity(activityRecord)) {
                // Sandbox only display rotation if needed, for external display.
@@ -311,8 +310,9 @@ final class AppCompatCameraSimReqOrientationPolicy implements AppCompatCameraSta
     * Calculates the angle for camera feed rotate-and-crop.
     *
     * <p>Camera apps most commonly calculate the preview rotation with the formula (simplified):
     * {code rotation = cameraSensorRotation - displayRotation}. When display rotation is sandboxed,
     * camera preview needs to be rotated by the same amount to keep the preview upright.
     * {code rotation = cameraSensorRotation - displayRotation}. When display rotation or sensor
     * orientation is sandboxed, camera feed needs to be rotated by the same amount to keep the
     * preview upright.
     */
    private int getCameraRotationFromSandboxedDisplayRotation(@Surface.Rotation int
            displayRotation) {
@@ -320,17 +320,19 @@ final class AppCompatCameraSimReqOrientationPolicy implements AppCompatCameraSta
            return ROTATION_UNDEFINED;
        }
        int realCameraRotation = mCameraDisplayRotationProvider.getCameraDeviceRotation();
        if (displayRotation == realCameraRotation) {
            // No need to rotate and crop, display rotation is unchanged.
            return ROTATION_UNDEFINED;
        }

        // Most apps that assume camera sensor orientation expect portrait camera orientation.
        // If sensor orientation is changed (currently only landscape to portrait is supported),
        // this will affect rotate and crop; otherwise sensorRotationOffset should be 0.
        // The value of sensorRotationOffset is calculated by the difference between the real
        // sensor orientation and sandboxed: 0 for landscape cameras, and 90 for portrait cameras.
        // Camera Framework flips this value based on whether the camera is front or back.
        final int sensorRotationOffset = shouldOverrideSensorOrientation() ? 270 : 0;
        final int displayRotationInDegrees = getRotationToDegrees(displayRotation);
        final int realCameraRotationInDegrees = getRotationToDegrees(realCameraRotation);
        // Feed needs to be rotated by the same amount as the display sandboxing difference, in
        // order to keep the preview upright.
        // Feed needs to be rotated by the same amount as the display sandboxing difference and the
        // camera sensor sandboxing difference, in order to keep the preview upright.
        return getRotationDegreesToEnum((displayRotationInDegrees - realCameraRotationInDegrees
                + 360) % 360);
                + sensorRotationOffset + 360) % 360);
    }

    private static int getRotationToDegrees(@Surface.Rotation int rotation) {
@@ -374,6 +376,10 @@ final class AppCompatCameraSimReqOrientationPolicy implements AppCompatCameraSta
        }
    }

    private boolean shouldOverrideSensorOrientation() {
        return Flags.cameraCompatLandscapeCameraSupport()
                && !mCameraDisplayRotationProvider.isCameraDeviceNaturalOrientationPortrait();
    }
    /**
     * Returns true if letterboxing should be allowed for camera apps, even if otherwise it isn't.
     *
+27 −1
Original line number Diff line number Diff line
@@ -22,7 +22,6 @@ import static android.app.CameraCompatTaskInfo.CAMERA_COMPAT_NONE;
import static android.app.CameraCompatTaskInfo.CAMERA_COMPAT_PORTRAIT_DEVICE_IN_LANDSCAPE;
import static android.app.CameraCompatTaskInfo.CAMERA_COMPAT_PORTRAIT_DEVICE_IN_PORTRAIT;
import static android.app.CameraCompatTaskInfo.CameraCompatMode;
import static android.app.WallpaperManager.ORIENTATION_LANDSCAPE;
import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
@@ -34,6 +33,7 @@ import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_FULL_USER;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
import static android.view.Display.TYPE_EXTERNAL;
import static android.view.Display.TYPE_INTERNAL;
@@ -46,6 +46,7 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.server.wm.AppCompatCameraOverrides.REQUESTED;
import static com.android.server.wm.AppCompatConfiguration.MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO;
import static com.android.window.flags.Flags.FLAG_CAMERA_COMPAT_LANDSCAPE_CAMERA_SUPPORT;
import static com.android.window.flags.Flags.FLAG_CAMERA_COMPAT_UNIFY_CAMERA_POLICIES;
import static com.android.window.flags.Flags.FLAG_ENABLE_CAMERA_COMPAT_COMPATIBILITY_INFO_ROTATE_AND_CROP_BUGFIX;
import static com.android.window.flags.Flags.FLAG_ENABLE_CAMERA_COMPAT_EXTERNAL_DISPLAY_ROTATION_BUGFIX;
@@ -637,6 +638,31 @@ public class AppCompatCameraSimReqOrientationPolicyTests extends WindowTestsBase
        });
    }

    @Test
    @EnableFlags({FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING,
            FLAG_CAMERA_COMPAT_LANDSCAPE_CAMERA_SUPPORT})
    @EnableCompatChanges({OVERRIDE_CAMERA_COMPAT_ENABLE_FREEFORM_WINDOWING_TREATMENT})
    public void testOnCameraOpened_landscapeDisplay_sandboxedToPortrait() {
        runTestScenario((robot) -> {
            robot.configureActivityAndDisplay(SCREEN_ORIENTATION_PORTRAIT, ORIENTATION_LANDSCAPE,
                    WINDOWING_MODE_FREEFORM);
            robot.activity().rotateDisplayForTopActivity(ROTATION_0);

            robot.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);

            // Display rotation for fixed-orientation portrait apps should always be 0.
            robot.assertCompatibilityInfoSentWithDisplayRotation(ROTATION_0);
            // Sensor orientation should change from landscape to portrait.
            robot.assertCompatibilityInfoSentWithSensorOverride(true);
            robot.assertCompatibilityInfoSentWithLetterbox(true);
            // Default is true, and should be disabled (false) for camera compat.
            robot.assertCompatibilityInfoSentWithInverseTransformAllowed(false);
            // Rotate and crop value should offset by the change in sensor orientation:
            // (0 - 90) % 360 = 270.
            robot.assertCompatibilityInfoSentWithRotateAndCrop(ROTATION_270);
        });
    }

    @Test
    @EnableFlags({FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING,
            FLAG_ENABLE_CAMERA_COMPAT_EXTERNAL_DISPLAY_ROTATION_BUGFIX,