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

Commit 03728701 authored by Mina Granic's avatar Mina Granic Committed by Android (Google) Code Review
Browse files

Merge "Handle different device/activity orientations for camera compat." into main

parents 4739ab54 93bd245d
Loading
Loading
Loading
Loading
+30 −8
Original line number Diff line number Diff line
@@ -36,20 +36,36 @@ public class CameraCompatTaskInfo implements Parcelable {
    public static final int CAMERA_COMPAT_FREEFORM_NONE = 0;

    /**
     * The value to use when portrait camera compat treatment should be applied to a windowed task.
     * The value to use when camera compat treatment should be applied to an activity requesting
     * portrait orientation, while a device is in landscape. Applies only to freeform tasks.
     */
    public static final int CAMERA_COMPAT_FREEFORM_PORTRAIT = 1;
    public static final int CAMERA_COMPAT_FREEFORM_PORTRAIT_DEVICE_IN_LANDSCAPE = 1;

    /**
     * The value to use when landscape camera compat treatment should be applied to a windowed task.
     * The value to use when camera compat treatment should be applied to an activity requesting
     * landscape orientation, while a device is in landscape. Applies only to freeform tasks.
     */
    public static final int CAMERA_COMPAT_FREEFORM_LANDSCAPE = 2;
    public static final int CAMERA_COMPAT_FREEFORM_LANDSCAPE_DEVICE_IN_LANDSCAPE = 2;

    /**
     * The value to use when camera compat treatment should be applied to an activity requesting
     * portrait orientation, while a device is in portrait. Applies only to freeform tasks.
     */
    public static final int CAMERA_COMPAT_FREEFORM_PORTRAIT_DEVICE_IN_PORTRAIT = 3;

    /**
     * The value to use when camera compat treatment should be applied to an activity requesting
     * landscape orientation, while a device is in portrait. Applies only to freeform tasks.
     */
    public static final int CAMERA_COMPAT_FREEFORM_LANDSCAPE_DEVICE_IN_PORTRAIT = 4;

    @Retention(RetentionPolicy.SOURCE)
    @IntDef(prefix = { "CAMERA_COMPAT_FREEFORM_" }, value = {
            CAMERA_COMPAT_FREEFORM_NONE,
            CAMERA_COMPAT_FREEFORM_PORTRAIT,
            CAMERA_COMPAT_FREEFORM_LANDSCAPE,
            CAMERA_COMPAT_FREEFORM_PORTRAIT_DEVICE_IN_LANDSCAPE,
            CAMERA_COMPAT_FREEFORM_LANDSCAPE_DEVICE_IN_LANDSCAPE,
            CAMERA_COMPAT_FREEFORM_PORTRAIT_DEVICE_IN_PORTRAIT,
            CAMERA_COMPAT_FREEFORM_LANDSCAPE_DEVICE_IN_PORTRAIT,
    })
    public @interface FreeformCameraCompatMode {}

@@ -143,8 +159,14 @@ public class CameraCompatTaskInfo implements Parcelable {
            @FreeformCameraCompatMode int freeformCameraCompatMode) {
        return switch (freeformCameraCompatMode) {
            case CAMERA_COMPAT_FREEFORM_NONE -> "inactive";
            case CAMERA_COMPAT_FREEFORM_PORTRAIT -> "portrait";
            case CAMERA_COMPAT_FREEFORM_LANDSCAPE -> "landscape";
            case CAMERA_COMPAT_FREEFORM_PORTRAIT_DEVICE_IN_LANDSCAPE ->
                    "app-portrait-device-landscape";
            case CAMERA_COMPAT_FREEFORM_LANDSCAPE_DEVICE_IN_LANDSCAPE ->
                    "app-landscape-device-landscape";
            case CAMERA_COMPAT_FREEFORM_PORTRAIT_DEVICE_IN_PORTRAIT ->
                    "app-portrait-device-portrait";
            case CAMERA_COMPAT_FREEFORM_LANDSCAPE_DEVICE_IN_PORTRAIT ->
                    "app-landscape-device-portrait";
            default -> throw new AssertionError(
                    "Unexpected camera compat mode: " + freeformCameraCompatMode);
        };
+38 −6
Original line number Diff line number Diff line
@@ -16,20 +16,28 @@

package com.android.server.wm;

import static android.app.CameraCompatTaskInfo.CAMERA_COMPAT_FREEFORM_LANDSCAPE_DEVICE_IN_LANDSCAPE;
import static android.app.CameraCompatTaskInfo.CAMERA_COMPAT_FREEFORM_LANDSCAPE_DEVICE_IN_PORTRAIT;
import static android.app.CameraCompatTaskInfo.CAMERA_COMPAT_FREEFORM_NONE;
import static android.app.CameraCompatTaskInfo.CAMERA_COMPAT_FREEFORM_PORTRAIT_DEVICE_IN_LANDSCAPE;
import static android.app.CameraCompatTaskInfo.CAMERA_COMPAT_FREEFORM_PORTRAIT_DEVICE_IN_PORTRAIT;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LOCKED;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_NOSENSOR;
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
import static android.content.res.Configuration.ORIENTATION_UNDEFINED;
import static android.view.Surface.ROTATION_0;
import static android.view.Surface.ROTATION_180;

import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.CameraCompatTaskInfo;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.view.DisplayInfo;
import android.view.Surface;

import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.protolog.ProtoLog;
@@ -172,11 +180,35 @@ final class CameraCompatFreeformPolicy implements CameraStateMonitor.CameraCompa
    }

    private static int getCameraCompatMode(@NonNull ActivityRecord topActivity) {
        return switch (topActivity.getRequestedConfigurationOrientation()) {
            case ORIENTATION_PORTRAIT -> CameraCompatTaskInfo.CAMERA_COMPAT_FREEFORM_PORTRAIT;
            case ORIENTATION_LANDSCAPE -> CameraCompatTaskInfo.CAMERA_COMPAT_FREEFORM_LANDSCAPE;
            default -> CameraCompatTaskInfo.CAMERA_COMPAT_FREEFORM_NONE;
        };
        final int appOrientation = topActivity.getRequestedConfigurationOrientation();
        // It is very important to check the original (actual) display rotation, and not the
        // sandboxed rotation that camera compat treatment sets.
        final DisplayInfo displayInfo = topActivity.mWmService.mDisplayManagerInternal
                .getDisplayInfo(topActivity.getDisplayId());
        // This treatment targets only devices with portrait natural orientation, which most tablets
        // have.
        // TODO(b/365725400): handle landscape natural orientation.
        if (displayInfo.getNaturalHeight() > displayInfo.getNaturalWidth()) {
            if (appOrientation == ORIENTATION_PORTRAIT) {
                if (isDisplayRotationPortrait(displayInfo.rotation)) {
                    return CAMERA_COMPAT_FREEFORM_PORTRAIT_DEVICE_IN_PORTRAIT;
                } else {
                    return CAMERA_COMPAT_FREEFORM_PORTRAIT_DEVICE_IN_LANDSCAPE;
                }
            } else if (appOrientation == ORIENTATION_LANDSCAPE) {
                if (isDisplayRotationPortrait(displayInfo.rotation)) {
                    return CAMERA_COMPAT_FREEFORM_LANDSCAPE_DEVICE_IN_PORTRAIT;
                } else {
                    return CAMERA_COMPAT_FREEFORM_LANDSCAPE_DEVICE_IN_LANDSCAPE;
                }
            }
        }

        return CAMERA_COMPAT_FREEFORM_NONE;
    }

    private static boolean isDisplayRotationPortrait(@Surface.Rotation int displayRotation) {
        return displayRotation == ROTATION_0 || displayRotation == ROTATION_180;
    }

    /**
+65 −11
Original line number Diff line number Diff line
@@ -16,11 +16,17 @@

package com.android.server.wm;

import static android.app.CameraCompatTaskInfo.CAMERA_COMPAT_FREEFORM_LANDSCAPE_DEVICE_IN_LANDSCAPE;
import static android.app.CameraCompatTaskInfo.CAMERA_COMPAT_FREEFORM_LANDSCAPE_DEVICE_IN_PORTRAIT;
import static android.app.CameraCompatTaskInfo.CAMERA_COMPAT_FREEFORM_NONE;
import static android.app.CameraCompatTaskInfo.CAMERA_COMPAT_FREEFORM_PORTRAIT_DEVICE_IN_LANDSCAPE;
import static android.app.CameraCompatTaskInfo.CAMERA_COMPAT_FREEFORM_PORTRAIT_DEVICE_IN_PORTRAIT;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.servertransaction.ActivityLifecycleItem.ON_PAUSE;
import static android.app.servertransaction.ActivityLifecycleItem.ON_STOP;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.content.pm.ActivityInfo.OVERRIDE_CAMERA_COMPAT_DISABLE_FREEFORM_WINDOWING_TREATMENT;
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_PORTRAIT;
@@ -33,10 +39,10 @@ import static com.android.window.flags.Flags.FLAG_CAMERA_COMPAT_FOR_FREEFORM;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
@@ -55,6 +61,8 @@ import android.graphics.Rect;
import android.hardware.camera2.CameraManager;
import android.os.Handler;
import android.platform.test.annotations.Presubmit;
import android.view.DisplayInfo;
import android.view.Surface;

import androidx.test.filters.SmallTest;

@@ -134,6 +142,7 @@ public class CameraCompatFreeformPolicyTests extends WindowTestsBase {
                new CameraCompatFreeformPolicy(mDisplayContent, cameraStateMonitor,
                        mActivityRefresher);

        setDisplayRotation(Surface.ROTATION_90);
        mCameraCompatFreeformPolicy.start();
        cameraStateMonitor.startListeningToCameraState();
    }
@@ -162,17 +171,49 @@ public class CameraCompatFreeformPolicyTests extends WindowTestsBase {
    }

    @Test
    public void testCameraConnected_activatesCameraCompatMode() throws Exception {
    public void testCameraConnected_deviceInPortrait_portraitCameraCompatMode() throws Exception {
        configureActivity(SCREEN_ORIENTATION_PORTRAIT);
        setDisplayRotation(Surface.ROTATION_0);
        mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);

        assertInCameraCompatMode();
        assertInCameraCompatMode(CAMERA_COMPAT_FREEFORM_PORTRAIT_DEVICE_IN_PORTRAIT);
        assertActivityRefreshRequested(/* refreshRequested */ false);
    }

    @Test
    public void testCameraConnected_deviceInLandscape_portraitCameraCompatMode() throws Exception {
        configureActivity(SCREEN_ORIENTATION_PORTRAIT);
        setDisplayRotation(Surface.ROTATION_270);
        mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);

        assertInCameraCompatMode(CAMERA_COMPAT_FREEFORM_PORTRAIT_DEVICE_IN_LANDSCAPE);
        assertActivityRefreshRequested(/* refreshRequested */ false);
    }

    @Test
    public void testCameraConnected_deviceInPortrait_landscapeCameraCompatMode() throws Exception {
        configureActivity(SCREEN_ORIENTATION_LANDSCAPE);
        setDisplayRotation(Surface.ROTATION_0);
        mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);

        assertInCameraCompatMode(CAMERA_COMPAT_FREEFORM_LANDSCAPE_DEVICE_IN_PORTRAIT);
        assertActivityRefreshRequested(/* refreshRequested */ false);
    }

    @Test
    public void testCameraConnected_deviceInLandscape_landscapeCameraCompatMode() throws Exception {
        configureActivity(SCREEN_ORIENTATION_LANDSCAPE);
        setDisplayRotation(Surface.ROTATION_270);
        mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);

        assertInCameraCompatMode(CAMERA_COMPAT_FREEFORM_LANDSCAPE_DEVICE_IN_LANDSCAPE);
        assertActivityRefreshRequested(/* refreshRequested */ false);
    }

    @Test
    public void testCameraReconnected_cameraCompatModeAndRefresh() throws Exception {
        configureActivity(SCREEN_ORIENTATION_PORTRAIT);
        setDisplayRotation(Surface.ROTATION_270);

        mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
        callOnActivityConfigurationChanging(mActivity);
@@ -180,7 +221,7 @@ public class CameraCompatFreeformPolicyTests extends WindowTestsBase {
        mCameraAvailabilityCallback.onCameraOpened(CAMERA_ID_1, TEST_PACKAGE_1);
        callOnActivityConfigurationChanging(mActivity);

        assertInCameraCompatMode();
        assertInCameraCompatMode(CAMERA_COMPAT_FREEFORM_PORTRAIT_DEVICE_IN_LANDSCAPE);
        assertActivityRefreshRequested(/* refreshRequested */ true);
    }

@@ -285,16 +326,14 @@ public class CameraCompatFreeformPolicyTests extends WindowTestsBase {
        doReturn(true).when(mActivity).inFreeformWindowingMode();
    }

    private void assertInCameraCompatMode() {
        assertNotEquals(CameraCompatTaskInfo.CAMERA_COMPAT_FREEFORM_NONE,
                mActivity.mAppCompatController.getAppCompatCameraOverrides()
    private void assertInCameraCompatMode(@CameraCompatTaskInfo.FreeformCameraCompatMode int mode) {
        assertEquals(mode, mActivity.mAppCompatController.getAppCompatCameraOverrides()
                        .getFreeformCameraCompatMode());
    }

    private void assertNotInCameraCompatMode() {
        assertEquals(CameraCompatTaskInfo.CAMERA_COMPAT_FREEFORM_NONE,
                mActivity.mAppCompatController.getAppCompatCameraOverrides()
                        .getFreeformCameraCompatMode());
        assertEquals(CAMERA_COMPAT_FREEFORM_NONE, mActivity.mAppCompatController
                .getAppCompatCameraOverrides().getFreeformCameraCompatMode());
    }

    private void assertActivityRefreshRequested(boolean refreshRequested) throws Exception {
@@ -328,4 +367,19 @@ public class CameraCompatFreeformPolicyTests extends WindowTestsBase {
        configuration.windowConfiguration.setAppBounds(bounds);
        return configuration;
    }

    private void setDisplayRotation(@Surface.Rotation int displayRotation) {
        doAnswer(invocation -> {
            DisplayInfo displayInfo = new DisplayInfo();
            mDisplayContent.getDisplay().getDisplayInfo(displayInfo);
            displayInfo.rotation = displayRotation;
            // Set height so that the natural orientation (rotation is 0) is portrait. This is the
            // case for most standard phones and tablets.
            // TODO(b/365725400): handle landscape natural orientation.
            displayInfo.logicalHeight = displayRotation % 180 == 0 ? 800 : 600;
            displayInfo.logicalWidth =  displayRotation % 180 == 0 ? 600 : 800;
            return displayInfo;
        }).when(mDisplayContent.mWmService.mDisplayManagerInternal)
                .getDisplayInfo(anyInt());
    }
}
+5 −4
Original line number Diff line number Diff line
@@ -16,7 +16,9 @@

package com.android.server.wm;


import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.app.CameraCompatTaskInfo.CAMERA_COMPAT_FREEFORM_PORTRAIT_DEVICE_IN_LANDSCAPE;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
@@ -71,7 +73,6 @@ import static org.mockito.Mockito.never;

import android.app.ActivityManager;
import android.app.ActivityOptions;
import android.app.CameraCompatTaskInfo;
import android.app.TaskInfo;
import android.app.WindowConfiguration;
import android.content.ComponentName;
@@ -2025,10 +2026,10 @@ public class TaskTests extends WindowTestsBase {
    public void getTaskInfoPropagatesCameraCompatMode() {
        final Task task = new TaskBuilder(mSupervisor).setCreateActivity(true).build();
        final ActivityRecord activity = task.getTopMostActivity();
        activity.mAppCompatController.getAppCompatCameraOverrides()
                .setFreeformCameraCompatMode(CameraCompatTaskInfo.CAMERA_COMPAT_FREEFORM_PORTRAIT);
        activity.mAppCompatController.getAppCompatCameraOverrides().setFreeformCameraCompatMode(
                CAMERA_COMPAT_FREEFORM_PORTRAIT_DEVICE_IN_LANDSCAPE);

        assertEquals(CameraCompatTaskInfo.CAMERA_COMPAT_FREEFORM_PORTRAIT,
        assertEquals(CAMERA_COMPAT_FREEFORM_PORTRAIT_DEVICE_IN_LANDSCAPE,
                task.getTaskInfo().appCompatTaskInfo.cameraCompatTaskInfo.freeformCameraCompatMode);
    }