• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
17 package com.android.cts.verifier.camera.fov;
18 
19 import android.app.Activity;
20 import android.app.AlertDialog;
21 import android.app.Dialog;
22 import android.content.Context;
23 import android.content.DialogInterface;
24 import android.content.Intent;
25 import android.graphics.Color;
26 import android.graphics.Matrix;
27 import android.graphics.SurfaceTexture;
28 import android.hardware.Camera;
29 import android.hardware.Camera.PictureCallback;
30 import android.hardware.Camera.ShutterCallback;
31 import android.hardware.camera2.CameraAccessException;
32 import android.hardware.camera2.CameraCharacteristics;
33 import android.hardware.camera2.CameraManager;
34 import android.os.Bundle;
35 import android.os.PowerManager;
36 import android.os.PowerManager.WakeLock;
37 import android.util.Log;
38 import android.view.Surface;
39 import android.view.TextureView;
40 import android.view.View;
41 import android.view.View.OnClickListener;
42 import android.widget.AdapterView;
43 import android.widget.AdapterView.OnItemSelectedListener;
44 import android.widget.ArrayAdapter;
45 import android.widget.Button;
46 import android.widget.Spinner;
47 import android.widget.TextView;
48 import android.widget.Toast;
49 
50 import com.android.cts.verifier.R;
51 import com.android.cts.verifier.TestResult;
52 
53 import java.io.File;
54 import java.io.FileOutputStream;
55 import java.io.IOException;
56 import java.util.ArrayList;
57 import java.util.List;
58 
59 /**
60  * An activity for showing the camera preview and taking a picture.
61  */
62 public class PhotoCaptureActivity extends Activity
63         implements PictureCallback, TextureView.SurfaceTextureListener {
64     private static final String TAG = PhotoCaptureActivity.class.getSimpleName();
65     private static final int FOV_REQUEST_CODE = 1006;
66     private static final String PICTURE_FILENAME = "photo.jpg";
67     private static float mReportedFovDegrees = 0;
68     private float mReportedFovPrePictureTaken = -1;
69 
70     private TextureView mPreviewView;
71     private SurfaceTexture mPreviewTexture;
72     private int mPreviewTexWidth;
73     private int mPreviewTexHeight;
74 
75     private Spinner mResolutionSpinner;
76     private List<SelectableResolution> mSupportedResolutions;
77     private ArrayAdapter<SelectableResolution> mAdapter;
78 
79     private SelectableResolution mSelectedResolution;
80     private Camera mCamera;
81     private boolean mCameraInitialized = false;
82     private boolean mPreviewActive = false;
83     private boolean mTakingPicture = false;
84     private int mResolutionSpinnerIndex = -1;
85     private WakeLock mWakeLock;
86     private long shutterStartTime;
87     private int mPreviewOrientation;
88     private int mJpegOrientation;
89 
90     private ArrayList<Integer> mPreviewSizeCamerasToProcess = new ArrayList<Integer>();
91 
92     private Dialog mActiveDialog;
93 
94     /**
95      * Selected preview size per camera. If null, preview size should be
96      * automatically detected.
97      */
98     private Size[] mPreviewSizes = null;
99 
getPictureFile(Context context)100     public static File getPictureFile(Context context) {
101         return new File(context.getExternalCacheDir(), PICTURE_FILENAME);
102     }
103 
getReportedFovDegrees()104     public static float getReportedFovDegrees() {
105         return mReportedFovDegrees;
106     }
107 
108     @Override
onCreate(Bundle savedInstanceState)109     protected void onCreate(Bundle savedInstanceState) {
110         super.onCreate(savedInstanceState);
111         setContentView(R.layout.camera_fov_calibration_photo_capture);
112 
113         int cameraToBeTested = 0;
114         for (int cameraId = 0; cameraId < Camera.getNumberOfCameras(); ++cameraId) {
115             if (!isExternalCamera(cameraId)) {
116                 cameraToBeTested++;
117             }
118         }
119 
120         mPreviewView = (TextureView) findViewById(R.id.camera_fov_camera_preview);
121         mPreviewView.setSurfaceTextureListener(this);
122 
123         TextView textView = (TextView) findViewById(R.id.camera_fov_tap_to_take_photo);
124         textView.setTextColor(Color.WHITE);
125 
126         Button setupButton = (Button) findViewById(R.id.camera_fov_settings_button);
127         setupButton.setOnClickListener(new OnClickListener() {
128 
129             @Override
130             public void onClick(View v) {
131                 startActivity(new Intent(
132                         PhotoCaptureActivity.this, CalibrationPreferenceActivity.class));
133             }
134         });
135 
136         Button changePreviewSizeButton = (Button) findViewById(
137                 R.id.camera_fov_change_preview_size_button);
138         changePreviewSizeButton.setOnClickListener(new OnClickListener() {
139             @Override
140             public void onClick(View v) {
141                 // Stop camera until preview sizes have been obtained.
142                 if (mCamera != null) {
143                     mCamera.stopPreview();
144                     mCamera.release();
145                     mCamera = null;
146                 }
147 
148                 mPreviewSizeCamerasToProcess.clear();
149                 mPreviewSizes =  new Size[Camera.getNumberOfCameras()];
150                 for (int cameraId = 0; cameraId < Camera.getNumberOfCameras(); ++cameraId) {
151                     if (!isExternalCamera(cameraId)) {
152                         mPreviewSizeCamerasToProcess.add(cameraId);
153                     }
154                 }
155                 showNextDialogToChoosePreviewSize();
156             }
157         });
158 
159         View previewView = findViewById(R.id.camera_fov_preview_overlay);
160         previewView.setOnClickListener(new OnClickListener() {
161             @Override
162             public void onClick(View v) {
163                 if (mPreviewActive && !mTakingPicture) {
164                     mTakingPicture = true;
165                     shutterStartTime = System.currentTimeMillis();
166 
167                     mCamera.takePicture(new ShutterCallback() {
168                         @Override
169                         public void onShutter() {
170                             long dT = System.currentTimeMillis() - shutterStartTime;
171                             Log.d("CTS", "Shutter Lag: " + dT);
172                         }
173                     }, null, PhotoCaptureActivity.this);
174                 }
175             }
176         });
177 
178         mResolutionSpinner = (Spinner) findViewById(R.id.camera_fov_resolution_selector);
179         mResolutionSpinner.setOnItemSelectedListener(new OnItemSelectedListener() {
180             @Override
181             public void onItemSelected(
182                     AdapterView<?> parent, View view, int position, long id) {
183                 if (mSupportedResolutions != null) {
184                     SelectableResolution resolution = mSupportedResolutions.get(position);
185                     switchToCamera(resolution, false);
186 
187                     // It should be guaranteed that the FOV is correctly updated after
188                     // setParameters().
189                     mReportedFovPrePictureTaken = getCameraFov(resolution.cameraId);
190 
191                     mResolutionSpinnerIndex = position;
192                     startPreview();
193                 }
194             }
195 
196             @Override
197             public void onNothingSelected(AdapterView<?> arg0) {}
198         });
199 
200         if (cameraToBeTested == 0) {
201             Log.i(TAG, "No cameras needs to be tested. Setting test pass.");
202             Toast.makeText(this, "No cameras needs to be tested. Test pass.",
203                     Toast.LENGTH_LONG).show();
204 
205             TestResult.setPassedResult(this, getClass().getName(),
206                     "All cameras are external, test skipped!");
207             finish();
208         }
209     }
210 
211     @Override
onResume()212     protected void onResume() {
213         super.onResume();
214         // Keep the device from going to sleep.
215         PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
216         mWakeLock = pm.newWakeLock(PowerManager.FULL_WAKE_LOCK, TAG);
217         mWakeLock.acquire();
218 
219         if (mSupportedResolutions == null) {
220             mSupportedResolutions = new ArrayList<SelectableResolution>();
221             int numCameras = Camera.getNumberOfCameras();
222             for (int cameraId = 0; cameraId < numCameras; ++cameraId) {
223                 if (isExternalCamera(cameraId)) {
224                     continue;
225                 }
226 
227                 Camera camera = Camera.open(cameraId);
228 
229                 // Get the supported picture sizes and fill the spinner.
230                 List<Camera.Size> supportedSizes =
231                         camera.getParameters().getSupportedPictureSizes();
232                 for (Camera.Size size : supportedSizes) {
233                     mSupportedResolutions.add(
234                             new SelectableResolution(cameraId, size.width, size.height));
235                 }
236                 camera.release();
237             }
238         }
239 
240         // Find the first untested entry.
241         for (mResolutionSpinnerIndex = 0;
242                 mResolutionSpinnerIndex < mSupportedResolutions.size();
243                 mResolutionSpinnerIndex++) {
244             if (!mSupportedResolutions.get(mResolutionSpinnerIndex).tested) {
245                 break;
246             }
247         }
248 
249         mAdapter = new ArrayAdapter<SelectableResolution>(
250                 this, android.R.layout.simple_spinner_dropdown_item,
251                 mSupportedResolutions);
252         mResolutionSpinner.setAdapter(mAdapter);
253 
254         mResolutionSpinner.setSelection(mResolutionSpinnerIndex);
255         setResult(RESULT_CANCELED);
256     }
257 
258     @Override
onPause()259     public void onPause() {
260         if (mCamera != null) {
261             if (mPreviewActive) {
262                 mCamera.stopPreview();
263             }
264 
265             mCamera.release();
266             mCamera = null;
267         }
268         mPreviewActive = false;
269         mWakeLock.release();
270         super.onPause();
271     }
272 
273     @Override
onPictureTaken(byte[] data, Camera camera)274     public void onPictureTaken(byte[] data, Camera camera) {
275         File pictureFile = getPictureFile(this);
276 
277         mReportedFovDegrees = getCameraFov(mSelectedResolution.cameraId);
278 
279         // Show error if FOV does not match the value reported before takePicture().
280         if (mReportedFovPrePictureTaken != mReportedFovDegrees) {
281             mSupportedResolutions.get(mResolutionSpinnerIndex).tested = true;
282             mSupportedResolutions.get(mResolutionSpinnerIndex).passed = false;
283 
284             AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this);
285             dialogBuilder.setTitle(R.string.camera_fov_reported_fov_problem);
286             dialogBuilder.setNeutralButton(
287                     android.R.string.ok, new DialogInterface.OnClickListener() {
288                 @Override
289                 public void onClick(DialogInterface dialog, int which) {
290                     if (mActiveDialog != null) {
291                         mActiveDialog.dismiss();
292                         mActiveDialog = null;
293                         initializeCamera();
294                     }
295                 }
296             });
297 
298             String message  = getResources().getString(R.string.camera_fov_reported_fov_problem_message);
299             dialogBuilder.setMessage(String.format(message, mReportedFovPrePictureTaken, mReportedFovDegrees));
300             mActiveDialog = dialogBuilder.show();
301             mTakingPicture = false;
302             return;
303         }
304 
305         try {
306             FileOutputStream fos = new FileOutputStream(pictureFile);
307             fos.write(data);
308             fos.close();
309             Log.d(TAG, "File saved to " + pictureFile.getAbsolutePath());
310 
311             // Start activity which will use the taken picture to determine the
312             // FOV.
313             startActivityForResult(new Intent(this, DetermineFovActivity.class),
314                     FOV_REQUEST_CODE + mResolutionSpinnerIndex, null);
315         } catch (IOException e) {
316             Log.e(TAG, "Could not save picture file.", e);
317             Toast.makeText(this, "Could not save picture file: " + e.getMessage(),
318                     Toast.LENGTH_LONG).show();
319         }
320         mTakingPicture = false;
321     }
322 
323     @Override
onActivityResult(int requestCode, int resultCode, Intent data)324     protected void onActivityResult(int requestCode, int resultCode, Intent data) {
325         if (resultCode != RESULT_OK) {
326             return;
327         }
328         int testIndex = requestCode - FOV_REQUEST_CODE;
329         SelectableResolution res = mSupportedResolutions.get(testIndex);
330         res.tested = true;
331         float reportedFOV = CtsTestHelper.getReportedFOV(data);
332         float measuredFOV = CtsTestHelper.getMeasuredFOV(data);
333         res.measuredFOV = measuredFOV;
334         if (CtsTestHelper.isResultPassed(reportedFOV, measuredFOV)) {
335             res.passed = true;
336         }
337 
338         boolean allTested = true;
339         for (int i = 0; i < mSupportedResolutions.size(); i++) {
340             if (!mSupportedResolutions.get(i).tested) {
341                 allTested = false;
342                 break;
343             }
344         }
345         if (!allTested) {
346             mAdapter.notifyDataSetChanged();
347             return;
348         }
349 
350         boolean allPassed = true;
351         for (int i = 0; i < mSupportedResolutions.size(); i++) {
352             if (!mSupportedResolutions.get(i).passed) {
353                 allPassed = false;
354                 break;
355             }
356         }
357         if (allPassed) {
358             TestResult.setPassedResult(this, getClass().getName(),
359                     CtsTestHelper.getTestDetails(mSupportedResolutions));
360         } else {
361             TestResult.setFailedResult(this, getClass().getName(),
362                     CtsTestHelper.getTestDetails(mSupportedResolutions));
363         }
364         finish();
365     }
366 
367     @Override
onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height)368     public void onSurfaceTextureAvailable(SurfaceTexture surface,
369             int width, int height) {
370         mPreviewTexture = surface;
371         mPreviewTexWidth = width;
372         mPreviewTexHeight = height;
373         initializeCamera();
374     }
375 
376     @Override
onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height)377     public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
378         // Ignored, Camera does all the work for us
379     }
380 
381     @Override
onSurfaceTextureDestroyed(SurfaceTexture surface)382     public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
383         return true;
384     }
385 
386     @Override
onSurfaceTextureUpdated(SurfaceTexture surface)387     public void onSurfaceTextureUpdated(SurfaceTexture surface) {
388         // Invoked every time there's a new Camera preview frame
389     }
390 
showNextDialogToChoosePreviewSize()391     private void showNextDialogToChoosePreviewSize() {
392         final int cameraId = mPreviewSizeCamerasToProcess.remove(0);
393 
394         Camera camera = Camera.open(cameraId);
395         final List<Camera.Size> sizes = camera.getParameters()
396                 .getSupportedPreviewSizes();
397         String[] choices = new String[sizes.size()];
398         for (int i = 0; i < sizes.size(); ++i) {
399             Camera.Size size = sizes.get(i);
400             choices[i] = size.width + " x " + size.height;
401         }
402 
403         final AlertDialog.Builder builder = new AlertDialog.Builder(this);
404         String dialogTitle = String.format(
405                 getResources().getString(R.string.camera_fov_choose_preview_size_for_camera),
406                 cameraId);
407         builder.setTitle(
408                 dialogTitle).
409                 setOnCancelListener(new DialogInterface.OnCancelListener() {
410                     @Override
411                     public void onCancel(DialogInterface arg0) {
412                         // User cancelled preview size selection.
413                         mPreviewSizes = null;
414                         switchToCamera(mSelectedResolution, true);
415                     }
416                 }).
417                 setSingleChoiceItems(choices, 0, new DialogInterface.OnClickListener() {
418                     @Override
419                     public void onClick(DialogInterface dialog, int which) {
420                         Camera.Size size = sizes.get(which);
421                         mPreviewSizes[cameraId] = new Size(
422                                 size.width, size.height);
423                         dialog.dismiss();
424 
425                         if (mPreviewSizeCamerasToProcess.isEmpty()) {
426                             // We're done, re-initialize camera.
427                             switchToCamera(mSelectedResolution, true);
428                         } else {
429                             // Process other cameras.
430                             showNextDialogToChoosePreviewSize();
431                         }
432                     }
433                 }).create().show();
434         camera.release();
435     }
436 
initializeCamera()437     private void initializeCamera() {
438         initializeCamera(true);
439     }
440 
setPreviewTransform(Size previewSize)441     private void setPreviewTransform(Size previewSize) {
442         int sensorRotation = mPreviewOrientation;
443         float selectedPreviewAspectRatio;
444         if (sensorRotation == 0 || sensorRotation == 180) {
445             selectedPreviewAspectRatio = (float) previewSize.width
446                 / (float) previewSize.height;
447         } else {
448             selectedPreviewAspectRatio = (float) previewSize.height
449                 / (float) previewSize.width;
450         }
451 
452         Matrix transform = new Matrix();
453         float viewAspectRatio = (float) mPreviewView.getMeasuredWidth()
454                 / (float) mPreviewView.getMeasuredHeight();
455         float scaleX = 1.0f, scaleY = 1.0f;
456         float translateX = 0, translateY = 0;
457         if (selectedPreviewAspectRatio > viewAspectRatio) {
458             scaleY = viewAspectRatio / selectedPreviewAspectRatio;
459             translateY = (float) mPreviewView.getMeasuredHeight() / 2
460                     - (float) mPreviewView.getMeasuredHeight() * scaleY / 2;
461         } else {
462             scaleX = selectedPreviewAspectRatio / viewAspectRatio;
463             translateX = (float) mPreviewView.getMeasuredWidth() / 2
464                     - (float) mPreviewView.getMeasuredWidth() * scaleX / 2;
465         }
466         transform.postScale(scaleX, scaleY);
467         transform.postTranslate(translateX, translateY);
468         mPreviewView.setTransform(transform);
469     }
470 
initializeCamera(boolean startPreviewAfterInit)471     private void initializeCamera(boolean startPreviewAfterInit) {
472         if (mCamera == null || mPreviewTexture == null) {
473             return;
474         }
475 
476         try {
477             mCamera.setPreviewTexture(mPreviewTexture);
478         } catch (Throwable t) {
479             Log.e(TAG, "Could not set preview texture", t);
480             Toast.makeText(this, t.getMessage(), Toast.LENGTH_LONG).show();
481             return;
482         }
483 
484         calculateOrientations(this, mSelectedResolution.cameraId, mCamera);
485         Camera.Parameters params = setCameraParams(mCamera);
486 
487         // Either use chosen preview size for current camera or automatically
488         // choose preview size based on view dimensions.
489         Size selectedPreviewSize = null;
490         if (mPreviewSizes != null) {
491             selectedPreviewSize = mPreviewSizes[mSelectedResolution.cameraId];
492         } else {
493             if (mPreviewOrientation == 0 || mPreviewOrientation == 180) {
494                 selectedPreviewSize = getBestPreviewSize(
495                         mPreviewTexWidth, mPreviewTexHeight, params);
496             } else {
497                 selectedPreviewSize = getBestPreviewSize(
498                         mPreviewTexHeight, mPreviewTexWidth, params);
499             }
500         }
501 
502         if (selectedPreviewSize != null) {
503             params.setPreviewSize(selectedPreviewSize.width, selectedPreviewSize.height);
504             mCamera.setParameters(params);
505             setPreviewTransform(selectedPreviewSize);
506             mCameraInitialized = true;
507         }
508 
509         if (startPreviewAfterInit) {
510             if (selectedPreviewSize == null) {
511                 Log.w(TAG, "Preview started without setting preview size");
512             }
513             startPreview();
514         }
515     }
516 
startPreview()517     private void startPreview() {
518         if (mCameraInitialized && mCamera != null) {
519             mCamera.setDisplayOrientation(mPreviewOrientation);
520             mCamera.startPreview();
521             mPreviewActive = true;
522         }
523     }
524 
switchToCamera(SelectableResolution resolution, boolean startPreview)525     private void switchToCamera(SelectableResolution resolution, boolean startPreview) {
526         if (mCamera != null) {
527             mCamera.stopPreview();
528             mCamera.release();
529         }
530 
531         mSelectedResolution = resolution;
532         mCamera = Camera.open(mSelectedResolution.cameraId);
533 
534         initializeCamera(startPreview);
535     }
536 
537     /**
538      * Get the best supported focus mode.
539      *
540      * @param camera - Android camera object.
541      * @return the best supported focus mode.
542      */
getFocusMode(Camera camera)543     private static String getFocusMode(Camera camera) {
544         List<String> modes = camera.getParameters().getSupportedFocusModes();
545         if (modes != null) {
546             if (modes.contains(Camera.Parameters.FOCUS_MODE_INFINITY)) {
547                 Log.v(TAG, "Using Focus mode infinity");
548                 return Camera.Parameters.FOCUS_MODE_INFINITY;
549             }
550             if (modes.contains(Camera.Parameters.FOCUS_MODE_FIXED)) {
551                 Log.v(TAG, "Using Focus mode fixed");
552                 return Camera.Parameters.FOCUS_MODE_FIXED;
553             }
554         }
555         Log.v(TAG, "Using Focus mode auto.");
556         return Camera.Parameters.FOCUS_MODE_AUTO;
557     }
558 
559     /**
560      * Set the common camera parameters on the given camera and returns the
561      * parameter object for further modification, if needed.
562      */
setCameraParams(Camera camera)563     private Camera.Parameters setCameraParams(Camera camera) {
564         // The picture size is taken and set from the spinner selection
565         // callback.
566         Camera.Parameters params = camera.getParameters();
567         params.setJpegThumbnailSize(0, 0);
568         params.setJpegQuality(100);
569         params.setRotation(mJpegOrientation);
570         params.setFocusMode(getFocusMode(camera));
571         params.setZoom(0);
572         params.setPictureSize(mSelectedResolution.width, mSelectedResolution.height);
573         return params;
574     }
575 
getBestPreviewSize( int width, int height, Camera.Parameters parameters)576     private Size getBestPreviewSize(
577             int width, int height, Camera.Parameters parameters) {
578         Size result = null;
579 
580         for (Camera.Size size : parameters.getSupportedPreviewSizes()) {
581             if (size.width <= width && size.height <= height) {
582                 if (result == null) {
583                     result = new Size(size.width, size.height);
584                 } else {
585                     int resultArea = result.width * result.height;
586                     int newArea = size.width * size.height;
587 
588                     if (newArea > resultArea) {
589                         result = new Size(size.width, size.height);
590                     }
591                 }
592             }
593         }
594         return result;
595     }
596 
getDisplayRotation()597     private int getDisplayRotation() {
598         int displayRotation = getDisplay().getRotation();
599         int displayRotationDegrees = 0;
600         switch (displayRotation) {
601             case Surface.ROTATION_0: displayRotationDegrees = 0; break;
602             case Surface.ROTATION_90: displayRotationDegrees = 90; break;
603             case Surface.ROTATION_180: displayRotationDegrees = 180; break;
604             case Surface.ROTATION_270: displayRotationDegrees = 270; break;
605         }
606         return displayRotationDegrees;
607     }
608 
calculateOrientations(Activity activity, int cameraId, android.hardware.Camera camera)609     private void calculateOrientations(Activity activity,
610             int cameraId, android.hardware.Camera camera) {
611         android.hardware.Camera.CameraInfo info =
612                 new android.hardware.Camera.CameraInfo();
613         android.hardware.Camera.getCameraInfo(cameraId, info);
614 
615         int degrees = getDisplayRotation();
616         if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
617             mJpegOrientation = (info.orientation + degrees) % 360;
618             mPreviewOrientation = (360 - mJpegOrientation) % 360;  // compensate the mirror
619         } else {  // back-facing
620             mJpegOrientation = (info.orientation - degrees + 360) % 360;
621             mPreviewOrientation = mJpegOrientation;
622         }
623     }
624 
isExternalCamera(int cameraId)625     private boolean isExternalCamera(int cameraId) {
626         CameraManager manager = (CameraManager) this.getSystemService(Context.CAMERA_SERVICE);
627         try {
628             String cameraIdStr = manager.getCameraIdList()[cameraId];
629             CameraCharacteristics characteristics =
630                     manager.getCameraCharacteristics(cameraIdStr);
631 
632             if (characteristics.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL) ==
633                             CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_EXTERNAL) {
634                 // External camera doesn't support FOV informations
635                 return true;
636             }
637         } catch (CameraAccessException e) {
638             Toast.makeText(this, "Could not access camera " + cameraId +
639                     ": " + e.getMessage(), Toast.LENGTH_LONG).show();
640         }
641         return false;
642     }
643 
getCameraFov(int cameraId)644     private float getCameraFov(int cameraId) {
645         if (mPreviewOrientation == 0 || mPreviewOrientation == 180) {
646             return mCamera.getParameters().getHorizontalViewAngle();
647         } else {
648             return mCamera.getParameters().getVerticalViewAngle();
649         }
650     }
651 }
652