Home | History | Annotate | Download | only in sensors
      1 /*
      2  * Copyright (C) 2013 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 package com.android.cts.verifier.sensors;
     17 
     18 // ----------------------------------------------------------------------
     19 
     20 import android.content.Context;
     21 import android.hardware.Camera;
     22 import android.util.AttributeSet;
     23 import android.util.Log;
     24 import android.view.Surface;
     25 import android.view.SurfaceHolder;
     26 import android.view.SurfaceView;
     27 import android.view.ViewGroup;
     28 import android.view.WindowManager;
     29 
     30 import java.io.IOException;
     31 import java.lang.Math;
     32 
     33 import com.android.cts.verifier.sensors.RVCVRecordActivity;
     34 import com.android.cts.verifier.sensors.RVCVRecordActivity.RecordProcedureControllerCallback;
     35 
     36 /** Camera preview class */
     37 public class RVCVCameraPreview extends SurfaceView implements SurfaceHolder.Callback {
     38     private static final String TAG = "RVCVCameraPreview";
     39     private static final boolean LOCAL_LOGD = true;
     40 
     41     private Context mContext = null;
     42     private SurfaceHolder mHolder;
     43     private Camera mCamera;
     44     private float mCameraAspectRatio = 0;
     45     private int mCameraRotation = 0;
     46     private boolean mCheckStartTest = false;
     47     private boolean mPreviewStarted = false;
     48 
     49     private RVCVRecordActivity.RecordProcedureControllerCallback mRecordProcedureControllerCallback;
     50 
     51     /**
     52      * Constructor
     53      * @param context Activity context
     54      */
     55     public RVCVCameraPreview(Context context) {
     56         super(context);
     57         mContext = context;
     58         mCamera = null;
     59         initSurface();
     60     }
     61 
     62     /**
     63      * Constructor
     64      * @param context Activity context
     65      * @param attrs
     66      */
     67     public RVCVCameraPreview(Context context, AttributeSet attrs) {
     68         super(context, attrs);
     69         mContext = context;
     70     }
     71 
     72     public void init(Camera camera, float aspectRatio, int rotation)  {
     73         this.mCamera = camera;
     74         mCameraAspectRatio = aspectRatio;
     75         mCameraRotation = rotation;
     76         initSurface();
     77     }
     78 
     79     private void initSurface() {
     80         // Install a SurfaceHolder.Callback so we get notified when the
     81         // underlying surface is created and destroyed.
     82         mHolder = getHolder();
     83         mHolder.addCallback(this);
     84 
     85         // deprecated
     86         // TODO: update this code to match new API level.
     87         mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
     88     }
     89 
     90     /**
     91      *  SurfaceHolder.Callback
     92      *  Surface is created, it is OK to start the camera preview now.
     93      */
     94     public void surfaceCreated(SurfaceHolder holder) {
     95         // The Surface has been created, now tell the camera where to draw the preview.
     96 
     97         if (mCamera == null) {
     98             // preview camera does not exist
     99             return;
    100         }
    101     }
    102     /**
    103      *  SurfaceHolder.Callback
    104      */
    105     public void surfaceDestroyed(SurfaceHolder holder) {
    106         // empty. Take care of releasing the Camera preview in your activity.
    107     }
    108 
    109     /**
    110      *  SurfaceHolder.Callback
    111      *  Restart camera preview if surface changed
    112      */
    113     public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
    114 
    115         if (mHolder.getSurface() == null || mCamera == null){
    116             // preview surface or camera does not exist
    117             return;
    118         }
    119 
    120         int totalRotation = getRequiredRotation();
    121         mCamera.setDisplayOrientation(totalRotation);
    122 
    123         if (adjustLayoutParamsIfNeeded(totalRotation)) {
    124             // Wait on next surfaceChanged() call before proceeding
    125             Log.d(TAG, "Waiting on surface change before starting preview");
    126             return;
    127         }
    128 
    129         if (mPreviewStarted) {
    130             Log.w(TAG, "Re-starting camera preview");
    131             if (mCheckStartTest && mRecordProcedureControllerCallback != null) {
    132                 mRecordProcedureControllerCallback.stopRecordProcedureController();
    133             }
    134             mCamera.stopPreview();
    135             mPreviewStarted = false;
    136         }
    137         mCheckStartTest = false;
    138 
    139         try {
    140             mCamera.setPreviewDisplay(holder);
    141             mCamera.startPreview();
    142             mPreviewStarted = true;
    143             if (mRecordProcedureControllerCallback != null) {
    144                 mCheckStartTest = true;
    145                 mRecordProcedureControllerCallback.startRecordProcedureController();
    146             }
    147         } catch (IOException e) {
    148             if (LOCAL_LOGD) Log.d(TAG, "Error when starting camera preview: " + e.getMessage());
    149         }
    150     }
    151 
    152     /**
    153      * Determine the rotation required to display the camera's preview on the screen as large as
    154      * possible. This function combines the device's current rotation from its default orientation
    155      * and the rotation of the camera.
    156      */
    157     private int getRequiredRotation() {
    158         WindowManager windowManager =
    159                 (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
    160         int deviceRotation = 0;
    161         if (windowManager != null) {
    162             switch (windowManager.getDefaultDisplay().getRotation()) {
    163                 case Surface.ROTATION_0:
    164                     deviceRotation = 0;
    165                     break;
    166                 case Surface.ROTATION_90:
    167                     deviceRotation = 270;
    168                     break;
    169                 case Surface.ROTATION_180:
    170                     deviceRotation = 180;
    171                     break;
    172                 case Surface.ROTATION_270:
    173                     deviceRotation = 90;
    174                     break;
    175                 default:
    176                     deviceRotation = 0;
    177                     break;
    178             }
    179         } else {
    180             Log.w(TAG, "Unable to get device rotation, preview may be skewed.");
    181         }
    182 
    183         return (mCameraRotation + deviceRotation) % 360;
    184     }
    185 
    186     /**
    187      * Resize the layout to more closely match the desired aspect ratio, if necessary.
    188      *
    189      * @return true if we updated the layout params, false if the params look good
    190      */
    191     private boolean adjustLayoutParamsIfNeeded(int totalRotation) {
    192         // Determine the maximum size layout that maintains the camera's preview aspect ratio
    193         float cameraAspect = mCameraAspectRatio;
    194 
    195         // Check the camera and device rotation and invert the aspect ratio if the device is not
    196         // rotated at 0 or 180 degrees.
    197         if (totalRotation % 180 != 0) {
    198             // The device is rotated, so the screen should be the inverse of the aspect ratio
    199             cameraAspect = 1.0f / mCameraAspectRatio;
    200         }
    201 
    202         // Only adjust if there is at least 1% error between the aspects
    203         ViewGroup.LayoutParams layoutParams = getLayoutParams();
    204         int curWidth = getWidth();
    205         int curHeight = getHeight();
    206         float curAspect = (float)curWidth / (float)curHeight;
    207         float aspectDelta = Math.abs(cameraAspect - curAspect);
    208         if ((aspectDelta / cameraAspect) >= 0.01) {
    209             if (cameraAspect > curAspect) {
    210                 // Camera preview is wider than the current layout. Need to shorten the current layout
    211                 layoutParams.width = curWidth;
    212                 layoutParams.height = (int)(curWidth / cameraAspect);
    213             } else {
    214                 // Camera preview taller than the current layout. Need to narrow the current layout
    215                 layoutParams.width = (int)(curHeight * cameraAspect);
    216                 layoutParams.height = curHeight;
    217             }
    218 
    219             if (layoutParams.height != curHeight || layoutParams.width != curWidth) {
    220                 Log.d(TAG, String.format("Layout (%d, %d) -> (%d, %d)", curWidth, curHeight,
    221                         layoutParams.width, layoutParams.height));
    222                 setLayoutParams(layoutParams);
    223                 return true;
    224             }
    225         }
    226         return false;
    227     }
    228 
    229     public void setRecordProcedureControllerCallback(
    230             RVCVRecordActivity.RecordProcedureControllerCallback callback) {
    231         mRecordProcedureControllerCallback = callback;
    232     }
    233 }
    234