• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
onCreate(Bundle savedInstanceState)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
onResume()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
onPause()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
onCreateOptionsMenu(Menu menu)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
onOptionsItemSelected(MenuItem item)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  
Preview(Context context)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  
setCamera(Camera camera)183      public void setCamera(Camera camera) {
184          mCamera = camera;
185          if (mCamera != null) {
186              mSupportedPreviewSizes = mCamera.getParameters().getSupportedPreviewSizes();
187              requestLayout();
188          }
189      }
190  
switchCamera(Camera camera)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
onMeasure(int widthMeasureSpec, int heightMeasureSpec)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
onLayout(boolean changed, int l, int t, int r, int b)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  
surfaceCreated(SurfaceHolder holder)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  
surfaceDestroyed(SurfaceHolder holder)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  
getOptimalPreviewSize(List<Size> sizes, int w, int h)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  
surfaceChanged(SurfaceHolder holder, int format, int w, int h)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