Support wide screen devices in RVCVRecordActivity

Devices that had a screen that was wider than tall would display the
camera preview incorrectly, making the test difficult to pass. By
checking the device orientation and the camera rotation, the camera
preview and UI elements are able to be properly displayed to the user.

Bug: 120665489
Test: Verified test displays correctly on Pixel 3
Test: Verified test displays correctly on Chromebook in landscape mode
Test: Verified test displays correctly on Chromebook in portrait mode
Change-Id: I850f824011b5a94f6206d215c1e410d4239e070a
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/MotionIndicatorView.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/MotionIndicatorView.java
index 14784dd..4160572 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/MotionIndicatorView.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/MotionIndicatorView.java
@@ -26,6 +26,7 @@
 import android.hardware.SensorManager;
 import android.util.AttributeSet;
 import android.util.Log;
+import android.view.Surface;
 import android.view.View;
 
 /**
@@ -89,6 +90,8 @@
 
     private boolean mXEnabled, mYEnabled, mZEnabled;
 
+    private boolean mIsDeviceRotated = false;
+
     /**
      * Constructor
      * @param context
@@ -146,6 +149,15 @@
     }
 
     /**
+     * Set the device's current rotation
+     * @param rotation Surface.ROTATION_0, Surface.ROTATION_90, Surface.ROTATION_180, or
+     *                 Surface.ROTATION_270
+     */
+    public void setDeviceRotation(int rotation) {
+        mIsDeviceRotated = (rotation == Surface.ROTATION_90 || rotation == Surface.ROTATION_270);
+    }
+
+    /**
      * Set the active axis for display
      *
      * @param axis AXIS_X, AXIS_Y, AXIS_Z for x, y, z axis indicators, or AXIS_ALL for all three.
@@ -181,16 +193,22 @@
         mXSize = w;
         mYSize = h;
 
-        mZBoundOut = new RectF(w/2-w/2.5f, h/2-w/2.5f, w/2+w/2.5f, h/2+w/2.5f);
+        float halfSideLength = 0.4f * Math.min(w, h);
+        float leftSide = w/2 - halfSideLength;
+        float topSide = h/2 - halfSideLength;
+        float rightSide = w/2 + halfSideLength;
+        float bottomSide = h/2 + halfSideLength;
+
+        mZBoundOut = new RectF(leftSide, topSide, rightSide, bottomSide);
         mZBoundOut2 = new RectF(
-                w/2-w/2.5f-ZRING_CURSOR_ADD, h/2-w/2.5f-ZRING_CURSOR_ADD,
-                w/2+w/2.5f+ZRING_CURSOR_ADD, h/2+w/2.5f+ZRING_CURSOR_ADD);
+                leftSide-ZRING_CURSOR_ADD, topSide-ZRING_CURSOR_ADD,
+                rightSide+ZRING_CURSOR_ADD, bottomSide+ZRING_CURSOR_ADD);
         mZBoundIn = new RectF(
-                w/2-w/2.5f+ZRING_WIDTH, h/2-w/2.5f+ZRING_WIDTH,
-                w/2+w/2.5f-ZRING_WIDTH, h/2+w/2.5f-ZRING_WIDTH);
+                leftSide+ZRING_WIDTH, topSide+ZRING_WIDTH,
+                rightSide-ZRING_WIDTH, bottomSide-ZRING_WIDTH);
         mZBoundIn2 = new RectF(
-                w/2-w/2.5f+ZRING_WIDTH+ZRING_CURSOR_ADD, h/2-w/2.5f+ZRING_WIDTH+ZRING_CURSOR_ADD,
-                w/2+w/2.5f-ZRING_WIDTH-ZRING_CURSOR_ADD, h/2+w/2.5f-ZRING_WIDTH-ZRING_CURSOR_ADD);
+                leftSide+ZRING_WIDTH+ZRING_CURSOR_ADD, topSide+ZRING_WIDTH+ZRING_CURSOR_ADD,
+                rightSide-ZRING_WIDTH-ZRING_CURSOR_ADD, bottomSide-ZRING_WIDTH-ZRING_CURSOR_ADD);
 
         if (LOCAL_LOGV) Log.v(TAG, "New view size = ("+w+", "+h+")");
     }
@@ -209,8 +227,14 @@
         p.setColor(Color.YELLOW);
         canvas.drawRect(10,10, 50, 50, p);
 
-        if (mXEnabled && mXCovered != null) {
-            int xNStep = mXCovered.getNSteps() + 4; // two on each side as a buffer
+        // In order to determine which progress bar to draw, the device's rotation must be accounted
+        // for since the accelerometer rotates with the display.
+        boolean drawX = (mXEnabled && !mIsDeviceRotated) || (mYEnabled && mIsDeviceRotated);
+        boolean drawY = (mYEnabled && !mIsDeviceRotated) || (mXEnabled && mIsDeviceRotated);
+
+        if (drawX && mXCovered != null) {
+            RangeCoveredRegister covered = mIsDeviceRotated ? mYCovered : mXCovered;
+            int xNStep = covered.getNSteps() + 4; // two on each side as a buffer
             int xStepSize = mXSize * 3/4 / xNStep;
             int xLeft = mXSize * 1/8 + (mXSize * 3/4 % xNStep)/2;
 
@@ -219,8 +243,8 @@
                     xLeft+xStepSize*xNStep-1, XBAR_WIDTH+XBAR_MARGIN, mRangePaint);
 
             // covered range
-            for (i=0; i<mXCovered.getNSteps(); ++i) {
-                if (mXCovered.isCovered(i)) {
+            for (i=0; i<covered.getNSteps(); ++i) {
+                if (covered.isCovered(i)) {
                     canvas.drawRect(
                             xLeft+xStepSize*(i+2), XBAR_MARGIN,
                             xLeft+xStepSize*(i+3)-1, XBAR_WIDTH + XBAR_MARGIN,
@@ -235,12 +259,14 @@
                     xLeft+xStepSize*(xNStep-2)+3, XBAR_WIDTH+XBAR_MARGIN, mLimitPaint);
 
             // cursor
-            t = (int)(xLeft+xStepSize*(mXCovered.getLastValue()+2));
+            t = (int)(xLeft+xStepSize*(covered.getLastValue()+2));
             canvas.drawRect(t-4, XBAR_MARGIN-XBAR_CURSOR_ADD, t+3,
                     XBAR_WIDTH+XBAR_MARGIN+XBAR_CURSOR_ADD, mCursorPaint);
         }
-        if (mYEnabled && mYCovered != null) {
-            int yNStep = mYCovered.getNSteps() + 4; // two on each side as a buffer
+
+        if (drawY && mYCovered != null) {
+            RangeCoveredRegister covered = mIsDeviceRotated ? mXCovered : mYCovered;
+            int yNStep = covered.getNSteps() + 4; // two on each side as a buffer
             int yStepSize = mYSize * 3/4 / yNStep;
             int yLeft = mYSize * 1/8 + (mYSize * 3/4 % yNStep)/2;
 
@@ -249,8 +275,8 @@
                     YBAR_WIDTH+YBAR_MARGIN, yLeft+yStepSize*yNStep-1, mRangePaint);
 
             // covered range
-            for (i=0; i<mYCovered.getNSteps(); ++i) {
-                if (mYCovered.isCovered(i)) {
+            for (i=0; i<covered.getNSteps(); ++i) {
+                if (covered.isCovered(i)) {
                     canvas.drawRect(
                             YBAR_MARGIN, yLeft+yStepSize*(i+2),
                             YBAR_WIDTH + YBAR_MARGIN, yLeft+yStepSize*(i+3)-1,
@@ -265,7 +291,7 @@
                     YBAR_WIDTH + YBAR_MARGIN, yLeft + yStepSize * (yNStep - 2) + 3, mLimitPaint);
 
             // cursor
-            t = (int)(yLeft+yStepSize*(mYCovered.getLastValue()+2));
+            t = (int)(yLeft+yStepSize*(covered.getLastValue()+2));
             canvas.drawRect( YBAR_MARGIN-YBAR_CURSOR_ADD, t-4,
                     YBAR_WIDTH+YBAR_MARGIN+YBAR_CURSOR_ADD, t+3, mCursorPaint);
         }
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/RVCVCameraPreview.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/RVCVCameraPreview.java
index bd463af..10d1865 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/RVCVCameraPreview.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/RVCVCameraPreview.java
@@ -21,9 +21,11 @@
 import android.hardware.Camera;
 import android.util.AttributeSet;
 import android.util.Log;
+import android.view.Surface;
 import android.view.SurfaceHolder;
 import android.view.SurfaceView;
 import android.view.ViewGroup;
+import android.view.WindowManager;
 
 import java.io.IOException;
 import java.lang.Math;
@@ -36,10 +38,11 @@
     private static final String TAG = "RVCVCameraPreview";
     private static final boolean LOCAL_LOGD = true;
 
+    private Context mContext = null;
     private SurfaceHolder mHolder;
     private Camera mCamera;
-    private float mAspect;
-    private int mRotation;
+    private float mCameraAspectRatio = 0;
+    private int mCameraRotation = 0;
     private boolean mCheckStartTest = false;
     private boolean mPreviewStarted = false;
 
@@ -51,6 +54,7 @@
      */
     public RVCVCameraPreview(Context context) {
         super(context);
+        mContext = context;
         mCamera = null;
         initSurface();
     }
@@ -62,12 +66,13 @@
      */
     public RVCVCameraPreview(Context context, AttributeSet attrs) {
         super(context, attrs);
+        mContext = context;
     }
 
     public void init(Camera camera, float aspectRatio, int rotation)  {
         this.mCamera = camera;
-        mAspect = aspectRatio;
-        mRotation = rotation;
+        mCameraAspectRatio = aspectRatio;
+        mCameraRotation = rotation;
         initSurface();
     }
 
@@ -111,7 +116,11 @@
             // preview surface or camera does not exist
             return;
         }
-        if (adjustLayoutParamsIfNeeded()) {
+
+        int totalRotation = getRequiredRotation();
+        mCamera.setDisplayOrientation(totalRotation);
+
+        if (adjustLayoutParamsIfNeeded(totalRotation)) {
             // Wait on next surfaceChanged() call before proceeding
             Log.d(TAG, "Waiting on surface change before starting preview");
             return;
@@ -127,7 +136,6 @@
         }
         mCheckStartTest = false;
 
-        mCamera.setDisplayOrientation(mRotation);
         try {
             mCamera.setPreviewDisplay(holder);
             mCamera.startPreview();
@@ -142,23 +150,70 @@
     }
 
     /**
+     * Determine the rotation required to display the camera's preview on the screen as large as
+     * possible. This function combines the device's current rotation from its default orientation
+     * and the rotation of the camera.
+     */
+    private int getRequiredRotation() {
+        WindowManager windowManager =
+                (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
+        int deviceRotation = 0;
+        if (windowManager != null) {
+            switch (windowManager.getDefaultDisplay().getRotation()) {
+                case Surface.ROTATION_0:
+                    deviceRotation = 0;
+                    break;
+                case Surface.ROTATION_90:
+                    deviceRotation = 270;
+                    break;
+                case Surface.ROTATION_180:
+                    deviceRotation = 180;
+                    break;
+                case Surface.ROTATION_270:
+                    deviceRotation = 90;
+                    break;
+                default:
+                    deviceRotation = 0;
+                    break;
+            }
+        } else {
+            Log.w(TAG, "Unable to get device rotation, preview may be skewed.");
+        }
+
+        return (mCameraRotation + deviceRotation) % 360;
+    }
+
+    /**
      * Resize the layout to more closely match the desired aspect ratio, if necessary.
      *
      * @return true if we updated the layout params, false if the params look good
      */
-    private boolean adjustLayoutParamsIfNeeded() {
+    private boolean adjustLayoutParamsIfNeeded(int totalRotation) {
+        // Determine the maximum size layout that maintains the camera's preview aspect ratio
+        float cameraAspect = mCameraAspectRatio;
+
+        // Check the camera and device rotation and invert the aspect ratio if the device is not
+        // rotated at 0 or 180 degrees.
+        if (totalRotation % 180 != 0) {
+            // The device is rotated, so the screen should be the inverse of the aspect ratio
+            cameraAspect = 1.0f / mCameraAspectRatio;
+        }
+
+        // Only adjust if there is at least 1% error between the aspects
         ViewGroup.LayoutParams layoutParams = getLayoutParams();
         int curWidth = getWidth();
         int curHeight = getHeight();
-        float curAspect = (float)curHeight / (float)curWidth;
-        float aspectDelta = Math.abs(mAspect - curAspect);
-        if ((aspectDelta / mAspect) >= 0.01) {
-            if (curAspect > mAspect) {
-                layoutParams.height = (int)Math.round(curWidth * mAspect);
+        float curAspect = (float)curWidth / (float)curHeight;
+        float aspectDelta = Math.abs(cameraAspect - curAspect);
+        if ((aspectDelta / cameraAspect) >= 0.01) {
+            if (cameraAspect > curAspect) {
+                // Camera preview is wider than the current layout. Need to shorten the current layout
                 layoutParams.width = curWidth;
+                layoutParams.height = (int)(curWidth / cameraAspect);
             } else {
+                // Camera preview taller than the current layout. Need to narrow the current layout
+                layoutParams.width = (int)(curHeight * cameraAspect);
                 layoutParams.height = curHeight;
-                layoutParams.width = (int)Math.round(curHeight / mAspect);
             }
 
             if (layoutParams.height != curHeight || layoutParams.width != curWidth) {
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/RVCVRecordActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/RVCVRecordActivity.java
index 8199736..4f92b64 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/RVCVRecordActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/RVCVRecordActivity.java
@@ -33,7 +33,9 @@
 import android.os.Environment;
 import android.util.JsonWriter;
 import android.util.Log;
+import android.view.Surface;
 import android.view.Window;
+import android.view.WindowManager;
 import android.widget.ImageView;
 import android.widget.Toast;
 
@@ -70,6 +72,7 @@
     private RVSensorLogger          mRVSensorLogger;
     private CoverageManager         mCoverManager;
     private CameraContext mCameraContext;
+    private int mDeviceRotation = Surface.ROTATION_0;
 
     public static final int AXIS_NONE = 0;
     public static final int AXIS_ALL = SensorManager.AXIS_X +
@@ -119,6 +122,12 @@
 
         // locate views
         mIndicatorView = (MotionIndicatorView) findViewById(R.id.cam_indicator);
+        WindowManager windowManager =
+                (WindowManager)getSystemService(Context.WINDOW_SERVICE);
+        if (windowManager != null) {
+            mDeviceRotation = windowManager.getDefaultDisplay().getRotation();
+            mIndicatorView.setDeviceRotation(mDeviceRotation);
+        }
 
         initStoragePath();
     }
@@ -224,6 +233,9 @@
 
         if (axis >=SensorManager.AXIS_X && axis <=SensorManager.AXIS_Z) {
             imageView.setImageResource(prompts[axis-1]);
+            if (mDeviceRotation != Surface.ROTATION_0 && mDeviceRotation != Surface.ROTATION_180) {
+                imageView.setRotation(90);
+            }
             mIndicatorView.enableAxis(axis);
             mRVSensorLogger.updateRegister(mCoverManager.getAxis(axis), axis);
             notifyPrompt(axis);