Home | History | Annotate | Download | only in graphics
      1 /*
      2  * Copyright (C) 2007 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 
     17 package com.example.android.apis.graphics;
     18 
     19 import android.app.Activity;
     20 import android.app.AlertDialog;
     21 import android.content.Context;
     22 import android.hardware.Camera;
     23 import android.hardware.Camera.CameraInfo;
     24 import android.hardware.Camera.Size;
     25 import android.os.Bundle;
     26 import android.util.Log;
     27 import android.view.Menu;
     28 import android.view.MenuInflater;
     29 import android.view.MenuItem;
     30 import android.view.SurfaceHolder;
     31 import android.view.SurfaceView;
     32 import android.view.View;
     33 import android.view.ViewGroup;
     34 import android.view.Window;
     35 import android.view.WindowManager;
     36 
     37 import java.io.IOException;
     38 import java.util.List;
     39 
     40 // Need the following import to get access to the app resources, since this
     41 // class is in a sub-package.
     42 import com.example.android.apis.R;
     43 
     44 // ----------------------------------------------------------------------
     45 
     46 public class CameraPreview extends Activity {
     47     private Preview mPreview;
     48     Camera mCamera;
     49     int numberOfCameras;
     50     int cameraCurrentlyLocked;
     51 
     52     // The first rear facing camera
     53     int defaultCameraId;
     54 
     55     @Override
     56     protected void onCreate(Bundle savedInstanceState) {
     57         super.onCreate(savedInstanceState);
     58 
     59         // Hide the window title.
     60         requestWindowFeature(Window.FEATURE_NO_TITLE);
     61         getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
     62 
     63         // Create a RelativeLayout container that will hold a SurfaceView,
     64         // and set it as the content of our activity.
     65         mPreview = new Preview(this);
     66         setContentView(mPreview);
     67 
     68         // Find the total number of cameras available
     69         numberOfCameras = Camera.getNumberOfCameras();
     70 
     71         // Find the ID of the default camera
     72         CameraInfo cameraInfo = new CameraInfo();
     73             for (int i = 0; i < numberOfCameras; i++) {
     74                 Camera.getCameraInfo(i, cameraInfo);
     75                 if (cameraInfo.facing == CameraInfo.CAMERA_FACING_BACK) {
     76                     defaultCameraId = i;
     77                 }
     78             }
     79     }
     80 
     81     @Override
     82     protected void onResume() {
     83         super.onResume();
     84 
     85         // Open the default i.e. the first rear facing camera.
     86         mCamera = Camera.open();
     87         cameraCurrentlyLocked = defaultCameraId;
     88         mPreview.setCamera(mCamera);
     89     }
     90 
     91     @Override
     92     protected void onPause() {
     93         super.onPause();
     94 
     95         // Because the Camera object is a shared resource, it's very
     96         // important to release it when the activity is paused.
     97         if (mCamera != null) {
     98             mPreview.setCamera(null);
     99             mCamera.release();
    100             mCamera = null;
    101         }
    102     }
    103 
    104     @Override
    105     public boolean onCreateOptionsMenu(Menu menu) {
    106 
    107         // Inflate our menu which can gather user input for switching camera
    108         MenuInflater inflater = getMenuInflater();
    109         inflater.inflate(R.menu.camera_menu, menu);
    110         return true;
    111     }
    112 
    113     @Override
    114     public boolean onOptionsItemSelected(MenuItem item) {
    115         // Handle item selection
    116         switch (item.getItemId()) {
    117         case R.id.switch_cam:
    118             // check for availability of multiple cameras
    119             if (numberOfCameras == 1) {
    120                 AlertDialog.Builder builder = new AlertDialog.Builder(this);
    121                 builder.setMessage(this.getString(R.string.camera_alert))
    122                        .setNeutralButton("Close", null);
    123                 AlertDialog alert = builder.create();
    124                 alert.show();
    125                 return true;
    126             }
    127 
    128             // OK, we have multiple cameras.
    129             // Release this camera -> cameraCurrentlyLocked
    130             if (mCamera != null) {
    131                 mCamera.stopPreview();
    132                 mPreview.setCamera(null);
    133                 mCamera.release();
    134                 mCamera = null;
    135             }
    136 
    137             // Acquire the next camera and request Preview to reconfigure
    138             // parameters.
    139             mCamera = Camera
    140                     .open((cameraCurrentlyLocked + 1) % numberOfCameras);
    141             cameraCurrentlyLocked = (cameraCurrentlyLocked + 1)
    142                     % numberOfCameras;
    143             mPreview.switchCamera(mCamera);
    144 
    145             // Start the preview
    146             mCamera.startPreview();
    147             return true;
    148         default:
    149             return super.onOptionsItemSelected(item);
    150         }
    151     }
    152 }
    153 
    154 // ----------------------------------------------------------------------
    155 
    156 /**
    157  * A simple wrapper around a Camera and a SurfaceView that renders a centered preview of the Camera
    158  * to the surface. We need to center the SurfaceView because not all devices have cameras that
    159  * support preview sizes at the same aspect ratio as the device's display.
    160  */
    161 class Preview extends ViewGroup implements SurfaceHolder.Callback {
    162     private final String TAG = "Preview";
    163 
    164     SurfaceView mSurfaceView;
    165     SurfaceHolder mHolder;
    166     Size mPreviewSize;
    167     List<Size> mSupportedPreviewSizes;
    168     Camera mCamera;
    169 
    170     Preview(Context context) {
    171         super(context);
    172 
    173         mSurfaceView = new SurfaceView(context);
    174         addView(mSurfaceView);
    175 
    176         // Install a SurfaceHolder.Callback so we get notified when the
    177         // underlying surface is created and destroyed.
    178         mHolder = mSurfaceView.getHolder();
    179         mHolder.addCallback(this);
    180         mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
    181     }
    182 
    183     public void setCamera(Camera camera) {
    184         mCamera = camera;
    185         if (mCamera != null) {
    186             mSupportedPreviewSizes = mCamera.getParameters().getSupportedPreviewSizes();
    187             requestLayout();
    188         }
    189     }
    190 
    191     public void switchCamera(Camera camera) {
    192        setCamera(camera);
    193        try {
    194            camera.setPreviewDisplay(mHolder);
    195        } catch (IOException exception) {
    196            Log.e(TAG, "IOException caused by setPreviewDisplay()", exception);
    197        }
    198        Camera.Parameters parameters = camera.getParameters();
    199        parameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height);
    200        requestLayout();
    201 
    202        camera.setParameters(parameters);
    203     }
    204 
    205     @Override
    206     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    207         // We purposely disregard child measurements because act as a
    208         // wrapper to a SurfaceView that centers the camera preview instead
    209         // of stretching it.
    210         final int width = resolveSize(getSuggestedMinimumWidth(), widthMeasureSpec);
    211         final int height = resolveSize(getSuggestedMinimumHeight(), heightMeasureSpec);
    212         setMeasuredDimension(width, height);
    213 
    214         if (mSupportedPreviewSizes != null) {
    215             mPreviewSize = getOptimalPreviewSize(mSupportedPreviewSizes, width, height);
    216         }
    217     }
    218 
    219     @Override
    220     protected void onLayout(boolean changed, int l, int t, int r, int b) {
    221         if (changed && getChildCount() > 0) {
    222             final View child = getChildAt(0);
    223 
    224             final int width = r - l;
    225             final int height = b - t;
    226 
    227             int previewWidth = width;
    228             int previewHeight = height;
    229             if (mPreviewSize != null) {
    230                 previewWidth = mPreviewSize.width;
    231                 previewHeight = mPreviewSize.height;
    232             }
    233 
    234             // Center the child SurfaceView within the parent.
    235             if (width * previewHeight > height * previewWidth) {
    236                 final int scaledChildWidth = previewWidth * height / previewHeight;
    237                 child.layout((width - scaledChildWidth) / 2, 0,
    238                         (width + scaledChildWidth) / 2, height);
    239             } else {
    240                 final int scaledChildHeight = previewHeight * width / previewWidth;
    241                 child.layout(0, (height - scaledChildHeight) / 2,
    242                         width, (height + scaledChildHeight) / 2);
    243             }
    244         }
    245     }
    246 
    247     public void surfaceCreated(SurfaceHolder holder) {
    248         // The Surface has been created, acquire the camera and tell it where
    249         // to draw.
    250         try {
    251             if (mCamera != null) {
    252                 mCamera.setPreviewDisplay(holder);
    253             }
    254         } catch (IOException exception) {
    255             Log.e(TAG, "IOException caused by setPreviewDisplay()", exception);
    256         }
    257     }
    258 
    259     public void surfaceDestroyed(SurfaceHolder holder) {
    260         // Surface will be destroyed when we return, so stop the preview.
    261         if (mCamera != null) {
    262             mCamera.stopPreview();
    263         }
    264     }
    265 
    266 
    267     private Size getOptimalPreviewSize(List<Size> sizes, int w, int h) {
    268         final double ASPECT_TOLERANCE = 0.1;
    269         double targetRatio = (double) w / h;
    270         if (sizes == null) return null;
    271 
    272         Size optimalSize = null;
    273         double minDiff = Double.MAX_VALUE;
    274 
    275         int targetHeight = h;
    276 
    277         // Try to find an size match aspect ratio and size
    278         for (Size size : sizes) {
    279             double ratio = (double) size.width / size.height;
    280             if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE) continue;
    281             if (Math.abs(size.height - targetHeight) < minDiff) {
    282                 optimalSize = size;
    283                 minDiff = Math.abs(size.height - targetHeight);
    284             }
    285         }
    286 
    287         // Cannot find the one match the aspect ratio, ignore the requirement
    288         if (optimalSize == null) {
    289             minDiff = Double.MAX_VALUE;
    290             for (Size size : sizes) {
    291                 if (Math.abs(size.height - targetHeight) < minDiff) {
    292                     optimalSize = size;
    293                     minDiff = Math.abs(size.height - targetHeight);
    294                 }
    295             }
    296         }
    297         return optimalSize;
    298     }
    299 
    300     public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
    301         // Now that the size is known, set up the camera parameters and begin
    302         // the preview.
    303         Camera.Parameters parameters = mCamera.getParameters();
    304         parameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height);
    305         requestLayout();
    306 
    307         mCamera.setParameters(parameters);
    308         mCamera.startPreview();
    309     }
    310 
    311 }
    312