• 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.hardware.Camera;
27 import android.hardware.Camera.PictureCallback;
28 import android.hardware.Camera.ShutterCallback;
29 import android.os.Bundle;
30 import android.os.PowerManager;
31 import android.os.PowerManager.WakeLock;
32 import android.util.Log;
33 import android.view.SurfaceHolder;
34 import android.view.SurfaceView;
35 import android.view.View;
36 import android.view.View.OnClickListener;
37 import android.widget.AdapterView;
38 import android.widget.AdapterView.OnItemSelectedListener;
39 import android.widget.ArrayAdapter;
40 import android.widget.Button;
41 import android.widget.Spinner;
42 import android.widget.TextView;
43 import android.widget.Toast;
44 
45 import com.android.cts.verifier.R;
46 import com.android.cts.verifier.TestResult;
47 
48 import java.io.File;
49 import java.io.FileOutputStream;
50 import java.io.IOException;
51 import java.util.ArrayList;
52 import java.util.List;
53 
54 /**
55  * An activity for showing the camera preview and taking a picture.
56  */
57 public class PhotoCaptureActivity extends Activity
58         implements PictureCallback, SurfaceHolder.Callback {
59     private static final String TAG = PhotoCaptureActivity.class.getSimpleName();
60     private static final int FOV_REQUEST_CODE = 1006;
61     private static final String PICTURE_FILENAME = "photo.jpg";
62     private static float mReportedFovDegrees = 0;
63     private float mReportedFovPrePictureTaken = -1;
64 
65     private SurfaceView mPreview;
66     private SurfaceHolder mSurfaceHolder;
67     private Spinner mResolutionSpinner;
68     private List<SelectableResolution> mSupportedResolutions;
69     private ArrayAdapter<SelectableResolution> mAdapter;
70 
71     private int mCameraId;
72     private Camera mCamera;
73     private Size mSurfaceSize;
74     private boolean mCameraInitialized = false;
75     private boolean mPreviewActive = false;
76     private int mResolutionSpinnerIndex = -1;
77     private WakeLock mWakeLock;
78     private long shutterStartTime;
79 
80     private ArrayList<Integer> mPreviewSizeCamerasToProcess = new ArrayList<Integer>();
81 
82     private Dialog mActiveDialog;
83 
84     /**
85      * Selected preview size per camera. If null, preview size should be
86      * automatically detected.
87      */
88     private Size[] mPreviewSizes = null;
89 
getPictureFile(Context context)90     public static File getPictureFile(Context context) {
91         return new File(context.getExternalCacheDir(), PICTURE_FILENAME);
92     }
93 
getReportedFovDegrees()94     public static float getReportedFovDegrees() {
95         return mReportedFovDegrees;
96     }
97 
98     @Override
onCreate(Bundle savedInstanceState)99     protected void onCreate(Bundle savedInstanceState) {
100         super.onCreate(savedInstanceState);
101         setContentView(R.layout.camera_fov_calibration_photo_capture);
102 
103         mPreview = (SurfaceView) findViewById(R.id.camera_fov_camera_preview);
104         mSurfaceHolder = mPreview.getHolder();
105         mSurfaceHolder.addCallback(this);
106 
107         // This is required for older versions of Android hardware.
108         mSurfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
109 
110         TextView textView = (TextView) findViewById(R.id.camera_fov_tap_to_take_photo);
111         textView.setTextColor(Color.WHITE);
112 
113         Button setupButton = (Button) findViewById(R.id.camera_fov_settings_button);
114         setupButton.setOnClickListener(new OnClickListener() {
115 
116             @Override
117             public void onClick(View v) {
118                 startActivity(new Intent(
119                         PhotoCaptureActivity.this, CalibrationPreferenceActivity.class));
120             }
121         });
122 
123         Button changePreviewSizeButton = (Button) findViewById(
124                 R.id.camera_fov_change_preview_size_button);
125         changePreviewSizeButton.setOnClickListener(new OnClickListener() {
126             @Override
127             public void onClick(View v) {
128                 // Stop camera until preview sizes have been obtained.
129                 if (mCamera != null) {
130                     mCamera.stopPreview();
131                     mCamera.release();
132                     mCamera = null;
133                 }
134 
135                 mPreviewSizeCamerasToProcess.clear();
136                 mPreviewSizes =  new Size[Camera.getNumberOfCameras()];
137                 for (int cameraId = 0; cameraId < Camera.getNumberOfCameras(); ++cameraId) {
138                     mPreviewSizeCamerasToProcess.add(cameraId);
139                 }
140                 showNextDialogToChoosePreviewSize();
141             }
142         });
143 
144         View previewView = findViewById(R.id.camera_fov_preview_overlay);
145         previewView.setOnClickListener(new OnClickListener() {
146             @Override
147             public void onClick(View v) {
148                 shutterStartTime = System.currentTimeMillis();
149 
150                 mCamera.takePicture(new ShutterCallback() {
151                     @Override
152                     public void onShutter() {
153                         long dT = System.currentTimeMillis() - shutterStartTime;
154                         Log.d("CTS", "Shutter Lag: " + dT);
155                     }
156                 }, null, PhotoCaptureActivity.this);
157             }
158         });
159 
160         mResolutionSpinner = (Spinner) findViewById(R.id.camera_fov_resolution_selector);
161         mResolutionSpinner.setOnItemSelectedListener(new OnItemSelectedListener() {
162             @Override
163             public void onItemSelected(
164                     AdapterView<?> parent, View view, int position, long id) {
165                 if (mSupportedResolutions != null) {
166                     SelectableResolution resolution = mSupportedResolutions.get(position);
167 
168                     switchToCamera(resolution.cameraId, false);
169 
170                     Camera.Parameters params = mCamera.getParameters();
171                     params.setPictureSize(resolution.width, resolution.height);
172                     mCamera.setParameters(params);
173 
174                     // It should be guaranteed that the FOV is correctly updated after setParameters().
175                     mReportedFovPrePictureTaken = mCamera.getParameters().getHorizontalViewAngle();
176 
177                     mResolutionSpinnerIndex = position;
178                     initializeCamera();
179                 }
180             }
181 
182         @Override
183         public void onNothingSelected(AdapterView<?> arg0) {}
184         });
185     }
186 
187     @Override
onResume()188     protected void onResume() {
189         super.onResume();
190         // Keep the device from going to sleep.
191         PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
192         mWakeLock = pm.newWakeLock(PowerManager.FULL_WAKE_LOCK, TAG);
193         mWakeLock.acquire();
194 
195         if (mSupportedResolutions == null) {
196             mSupportedResolutions = new ArrayList<SelectableResolution>();
197             int numCameras = Camera.getNumberOfCameras();
198             for (int cameraId = 0; cameraId < numCameras; ++cameraId) {
199                 Camera camera = Camera.open(cameraId);
200 
201                 // Get the supported picture sizes and fill the spinner.
202                 List<Camera.Size> supportedSizes =
203                         camera.getParameters().getSupportedPictureSizes();
204                 for (Camera.Size size : supportedSizes) {
205                     mSupportedResolutions.add(
206                             new SelectableResolution(cameraId, size.width, size.height));
207                 }
208                 camera.release();
209             }
210         }
211 
212         // Find the first untested entry.
213         for (mResolutionSpinnerIndex = 0;
214                 mResolutionSpinnerIndex < mSupportedResolutions.size();
215                 mResolutionSpinnerIndex++) {
216             if (!mSupportedResolutions.get(mResolutionSpinnerIndex).tested) {
217                 break;
218             }
219         }
220 
221         mAdapter = new ArrayAdapter<SelectableResolution>(
222                 this, android.R.layout.simple_spinner_dropdown_item,
223                 mSupportedResolutions);
224         mResolutionSpinner.setAdapter(mAdapter);
225 
226         mResolutionSpinner.setSelection(mResolutionSpinnerIndex);
227         setResult(RESULT_CANCELED);
228     }
229 
230     @Override
onPause()231     public void onPause() {
232         if (mCamera != null) {
233             if (mPreviewActive) {
234                 mCamera.stopPreview();
235             }
236 
237             mCamera.release();
238             mCamera = null;
239         }
240         mPreviewActive = false;
241         mWakeLock.release();
242         super.onPause();
243     }
244 
245     @Override
onPictureTaken(byte[] data, Camera camera)246     public void onPictureTaken(byte[] data, Camera camera) {
247         File pictureFile = getPictureFile(this);
248         Camera.Parameters params = mCamera.getParameters();
249         mReportedFovDegrees = params.getHorizontalViewAngle();
250 
251         // Show error if FOV does not match the value reported before takePicture().
252         if (mReportedFovPrePictureTaken != mReportedFovDegrees) {
253             mSupportedResolutions.get(mResolutionSpinnerIndex).tested = true;
254             mSupportedResolutions.get(mResolutionSpinnerIndex).passed = false;
255 
256             AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this);
257             dialogBuilder.setTitle(R.string.camera_fov_reported_fov_problem);
258             dialogBuilder.setNeutralButton(
259                     android.R.string.ok, new DialogInterface.OnClickListener() {
260                 @Override
261                 public void onClick(DialogInterface dialog, int which) {
262                     if (mActiveDialog != null) {
263                         mActiveDialog.dismiss();
264                         mActiveDialog = null;
265                         initializeCamera();
266                     }
267                 }
268             });
269 
270             String message  = getResources().getString(R.string.camera_fov_reported_fov_problem_message);
271             dialogBuilder.setMessage(String.format(message, mReportedFovPrePictureTaken, mReportedFovDegrees));
272             mActiveDialog = dialogBuilder.show();
273             return;
274         }
275 
276         try {
277             FileOutputStream fos = new FileOutputStream(pictureFile);
278             fos.write(data);
279             fos.close();
280             Log.d(TAG, "File saved to " + pictureFile.getAbsolutePath());
281 
282             // Start activity which will use the taken picture to determine the
283             // FOV.
284             startActivityForResult(new Intent(this, DetermineFovActivity.class),
285                     FOV_REQUEST_CODE + mResolutionSpinnerIndex, null);
286         } catch (IOException e) {
287             Log.e(TAG, "Could not save picture file.", e);
288             Toast.makeText(this, "Could not save picture file: " + e.getMessage(),
289                     Toast.LENGTH_LONG).show();
290             return;
291         }
292     }
293 
294     @Override
onActivityResult(int requestCode, int resultCode, Intent data)295     protected void onActivityResult(int requestCode, int resultCode, Intent data) {
296         if (resultCode != RESULT_OK) {
297             return;
298         }
299         int testIndex = requestCode - FOV_REQUEST_CODE;
300         SelectableResolution res = mSupportedResolutions.get(testIndex);
301         res.tested = true;
302         float reportedFOV = CtsTestHelper.getReportedFOV(data);
303         float measuredFOV = CtsTestHelper.getMeasuredFOV(data);
304         res.measuredFOV = measuredFOV;
305         if (CtsTestHelper.isResultPassed(reportedFOV, measuredFOV)) {
306             res.passed = true;
307         }
308 
309         boolean allTested = true;
310         for (int i = 0; i < mSupportedResolutions.size(); i++) {
311             if (!mSupportedResolutions.get(i).tested) {
312                 allTested = false;
313                 break;
314             }
315         }
316         if (!allTested) {
317             mAdapter.notifyDataSetChanged();
318             return;
319         }
320 
321         boolean allPassed = true;
322         for (int i = 0; i < mSupportedResolutions.size(); i++) {
323             if (!mSupportedResolutions.get(i).passed) {
324                 allPassed = false;
325                 break;
326             }
327         }
328         if (allPassed) {
329             TestResult.setPassedResult(this, getClass().getName(),
330                     CtsTestHelper.getTestDetails(mSupportedResolutions));
331         } else {
332             TestResult.setFailedResult(this, getClass().getName(),
333                     CtsTestHelper.getTestDetails(mSupportedResolutions));
334         }
335         finish();
336     }
337 
338     @Override
surfaceChanged( SurfaceHolder holder, int format, int width, int height)339     public void surfaceChanged(
340             SurfaceHolder holder, int format, int width, int height) {
341         mSurfaceSize = new Size(width, height);
342         initializeCamera();
343     }
344 
345     @Override
surfaceCreated(SurfaceHolder holder)346     public void surfaceCreated(SurfaceHolder holder) {
347         // Nothing to do.
348     }
349 
350     @Override
surfaceDestroyed(SurfaceHolder holder)351     public void surfaceDestroyed(SurfaceHolder holder) {
352         // Nothing to do.
353     }
354 
showNextDialogToChoosePreviewSize()355     private void showNextDialogToChoosePreviewSize() {
356         final int cameraId = mPreviewSizeCamerasToProcess.remove(0);
357 
358         Camera camera = Camera.open(cameraId);
359         final List<Camera.Size> sizes = camera.getParameters()
360                 .getSupportedPreviewSizes();
361         String[] choices = new String[sizes.size()];
362         for (int i = 0; i < sizes.size(); ++i) {
363             Camera.Size size = sizes.get(i);
364             choices[i] = size.width + " x " + size.height;
365         }
366 
367         final AlertDialog.Builder builder = new AlertDialog.Builder(this);
368         String dialogTitle = String.format(
369                 getResources().getString(R.string.camera_fov_choose_preview_size_for_camera),
370                 cameraId);
371         builder.setTitle(
372                 dialogTitle).
373                 setOnCancelListener(new DialogInterface.OnCancelListener() {
374                     @Override
375                     public void onCancel(DialogInterface arg0) {
376                         // User cancelled preview size selection.
377                         mPreviewSizes = null;
378                         switchToCamera(mCameraId, true);
379                     }
380                 }).
381                 setSingleChoiceItems(choices, 0, new DialogInterface.OnClickListener() {
382                     @Override
383                     public void onClick(DialogInterface dialog, int which) {
384                         Camera.Size size = sizes.get(which);
385                         mPreviewSizes[cameraId] = new Size(
386                                 size.width, size.height);
387                         dialog.dismiss();
388 
389                         if (mPreviewSizeCamerasToProcess.isEmpty()) {
390                             // We're done, re-initialize camera.
391                             switchToCamera(mCameraId, true);
392                         } else {
393                             // Process other cameras.
394                             showNextDialogToChoosePreviewSize();
395                         }
396                     }
397                 }).create().show();
398         camera.release();
399     }
400 
initializeCamera()401     private void initializeCamera() {
402         if (mCamera == null || mSurfaceHolder.getSurface() == null) {
403             return;
404         }
405 
406         try {
407             mCamera.setPreviewDisplay(mSurfaceHolder);
408         } catch (Throwable t) {
409             Log.e("TAG", "Could not set preview display", t);
410             Toast.makeText(this, t.getMessage(), Toast.LENGTH_LONG).show();
411             return;
412         }
413         Camera.Parameters params = setCameraParams(mCamera);
414 
415         // Either use chosen preview size for current camera or automatically
416         // choose preview size based on view dimensions.
417         Size selectedPreviewSize = (mPreviewSizes != null) ? mPreviewSizes[mCameraId] :
418             getBestPreviewSize(mSurfaceSize.width, mSurfaceSize.height, params);
419         if (selectedPreviewSize != null) {
420             params.setPreviewSize(selectedPreviewSize.width, selectedPreviewSize.height);
421             mCamera.setParameters(params);
422             mCameraInitialized = true;
423         }
424         startPreview();
425     }
426 
startPreview()427     private void startPreview() {
428         if (mCameraInitialized && mCamera != null) {
429             mCamera.startPreview();
430             mPreviewActive = true;
431         }
432     }
433 
switchToCamera(int cameraId, boolean initializeCamera)434     private void switchToCamera(int cameraId, boolean initializeCamera) {
435         if (mCamera != null) {
436             mCamera.stopPreview();
437             mCamera.release();
438         }
439         mCameraId = cameraId;
440         mCamera = Camera.open(cameraId);
441 
442         if (initializeCamera){
443           initializeCamera();
444         }
445     }
446 
447     /**
448      * Get the best supported focus mode.
449      *
450      * @param camera - Android camera object.
451      * @return the best supported focus mode.
452      */
getFocusMode(Camera camera)453     private static String getFocusMode(Camera camera) {
454         List<String> modes = camera.getParameters().getSupportedFocusModes();
455         if (modes != null) {
456             if (modes.contains(Camera.Parameters.FOCUS_MODE_INFINITY)) {
457                 Log.v(TAG, "Using Focus mode infinity");
458                 return Camera.Parameters.FOCUS_MODE_INFINITY;
459             }
460             if (modes.contains(Camera.Parameters.FOCUS_MODE_FIXED)) {
461                 Log.v(TAG, "Using Focus mode fixed");
462                 return Camera.Parameters.FOCUS_MODE_FIXED;
463             }
464         }
465         Log.v(TAG, "Using Focus mode auto.");
466         return Camera.Parameters.FOCUS_MODE_AUTO;
467     }
468 
469     /**
470      * Set the common camera parameters on the given camera and returns the
471      * parameter object for further modification, if needed.
472      */
setCameraParams(Camera camera)473     private static Camera.Parameters setCameraParams(Camera camera) {
474         // The picture size is taken and set from the spinner selection
475         // callback.
476         Camera.Parameters params = camera.getParameters();
477         params.setJpegThumbnailSize(0, 0);
478         params.setJpegQuality(100);
479         params.setFocusMode(getFocusMode(camera));
480         params.setZoom(0);
481         return params;
482     }
483 
getBestPreviewSize( int width, int height, Camera.Parameters parameters)484     private Size getBestPreviewSize(
485             int width, int height, Camera.Parameters parameters) {
486         Size result = null;
487 
488         for (Camera.Size size : parameters.getSupportedPreviewSizes()) {
489             if (size.width <= width && size.height <= height) {
490                 if (result == null) {
491                     result = new Size(size.width, size.height);
492                 } else {
493                     int resultArea = result.width * result.height;
494                     int newArea = size.width * size.height;
495 
496                     if (newArea > resultArea) {
497                         result = new Size(size.width, size.height);
498                     }
499                 }
500             }
501         }
502         return result;
503     }
504 }
505