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