• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2012 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.testingcamera;
18 
19 import android.app.Activity;
20 import android.app.FragmentManager;
21 import android.hardware.Camera;
22 import android.hardware.Camera.Parameters;
23 import android.media.CamcorderProfile;
24 import android.media.MediaRecorder;
25 import android.media.MediaScannerConnection;
26 import android.net.Uri;
27 import android.os.Bundle;
28 import android.os.Environment;
29 import android.os.Handler;
30 import android.view.View;
31 import android.view.SurfaceHolder;
32 import android.view.SurfaceView;
33 import android.view.View.OnClickListener;
34 import android.widget.AdapterView;
35 import android.widget.AdapterView.OnItemSelectedListener;
36 import android.widget.ArrayAdapter;
37 import android.widget.Button;
38 import android.widget.LinearLayout.LayoutParams;
39 import android.widget.Spinner;
40 import android.widget.TextView;
41 import android.widget.ToggleButton;
42 import android.text.Layout;
43 import android.text.method.ScrollingMovementMethod;
44 import android.util.Log;
45 
46 import java.io.File;
47 import java.io.IOException;
48 import java.io.PrintWriter;
49 import java.io.StringWriter;
50 import java.text.SimpleDateFormat;
51 import java.util.ArrayList;
52 import java.util.Date;
53 import java.util.HashSet;
54 import java.util.List;
55 import java.util.Set;
56 
57 /**
58  * A simple test application for the camera API.
59  *
60  * The goal of this application is to allow all camera API features to be
61  * exercised, and all information provided by the API to be shown.
62  */
63 public class TestingCamera extends Activity implements SurfaceHolder.Callback {
64 
65     /** UI elements */
66     private SurfaceView mPreviewView;
67     private SurfaceHolder mPreviewHolder;
68 
69     private Spinner mCameraSpinner;
70     private Button mInfoButton;
71     private Spinner mPreviewSizeSpinner;
72     private ToggleButton mPreviewToggle;
73     private Spinner mAutofocusModeSpinner;
74     private Button mAutofocusButton;
75     private Button mCancelAutofocusButton;
76     private Spinner mSnapshotSizeSpinner;
77     private Button  mTakePictureButton;
78     private Spinner mCamcorderProfileSpinner;
79     private ToggleButton mRecordToggle;
80 
81     private TextView mLogView;
82 
83     private Set<View> mPreviewOnlyControls = new HashSet<View>();
84 
85     /** Camera state */
86     private int mCameraId = 0;
87     private Camera mCamera;
88     private Camera.Parameters mParams;
89     private List<Camera.Size> mPreviewSizes;
90     private int mPreviewSize = 0;
91     private List<String> mAfModes;
92     private int mAfMode = 0;
93     private List<Camera.Size> mSnapshotSizes;
94     private int mSnapshotSize = 0;
95     private List<CamcorderProfile> mCamcorderProfiles;
96     private int mCamcorderProfile = 0;
97 
98     private MediaRecorder mRecorder;
99     private File mRecordingFile;
100 
101     private static final int CAMERA_UNINITIALIZED = 0;
102     private static final int CAMERA_OPEN = 1;
103     private static final int CAMERA_PREVIEW = 2;
104     private static final int CAMERA_TAKE_PICTURE = 3;
105     private static final int CAMERA_RECORD = 4;
106     private int mState = CAMERA_UNINITIALIZED;
107 
108     /** Misc variables */
109 
110     private static final String TAG = "TestingCamera";
111 
112     /** Activity lifecycle */
113 
114     @Override
onCreate(Bundle savedInstanceState)115     public void onCreate(Bundle savedInstanceState) {
116         super.onCreate(savedInstanceState);
117 
118         setContentView(R.layout.main);
119 
120         mPreviewView = (SurfaceView)findViewById(R.id.preview);
121         mPreviewView.getHolder().addCallback(this);
122 
123         mCameraSpinner = (Spinner) findViewById(R.id.camera_spinner);
124         mCameraSpinner.setOnItemSelectedListener(mCameraSpinnerListener);
125 
126         mInfoButton = (Button) findViewById(R.id.info_button);
127         mInfoButton.setOnClickListener(mInfoButtonListener);
128 
129         mPreviewSizeSpinner = (Spinner) findViewById(R.id.preview_size_spinner);
130         mPreviewSizeSpinner.setOnItemSelectedListener(mPreviewSizeListener);
131 
132         mPreviewToggle = (ToggleButton) findViewById(R.id.start_preview);
133         mPreviewToggle.setOnClickListener(mPreviewToggleListener);
134 
135         mAutofocusModeSpinner = (Spinner) findViewById(R.id.af_mode_spinner);
136         mAutofocusModeSpinner.setOnItemSelectedListener(mAutofocusModeListener);
137 
138         mAutofocusButton = (Button) findViewById(R.id.af_button);
139         mAutofocusButton.setOnClickListener(mAutofocusButtonListener);
140         mPreviewOnlyControls.add(mAutofocusButton);
141 
142         mCancelAutofocusButton = (Button) findViewById(R.id.af_cancel_button);
143         mCancelAutofocusButton.setOnClickListener(mCancelAutofocusButtonListener);
144         mPreviewOnlyControls.add(mCancelAutofocusButton);
145 
146         mSnapshotSizeSpinner = (Spinner) findViewById(R.id.snapshot_size_spinner);
147         mSnapshotSizeSpinner.setOnItemSelectedListener(mSnapshotSizeListener);
148 
149         mTakePictureButton = (Button) findViewById(R.id.take_picture);
150         mTakePictureButton.setOnClickListener(mTakePictureListener);
151         mPreviewOnlyControls.add(mTakePictureButton);
152 
153         mCamcorderProfileSpinner = (Spinner) findViewById(R.id.camcorder_profile_spinner);
154         mCamcorderProfileSpinner.setOnItemSelectedListener(mCamcorderProfileListener);
155 
156         mRecordToggle = (ToggleButton) findViewById(R.id.start_record);
157         mRecordToggle.setOnClickListener(mRecordToggleListener);
158         mPreviewOnlyControls.add(mRecordToggle);
159 
160         mLogView = (TextView) findViewById(R.id.log);
161         mLogView.setMovementMethod(new ScrollingMovementMethod());
162 
163         int numCameras = Camera.getNumberOfCameras();
164         String[] cameraNames = new String[numCameras];
165         for (int i = 0; i < numCameras; i++) {
166             cameraNames[i] = "Camera " + i;
167         }
168 
169         mCameraSpinner.setAdapter(
170                 new ArrayAdapter<String>(this,
171                         R.layout.spinner_item, cameraNames));
172     }
173 
174     @Override
onResume()175     public void onResume() {
176         super.onResume();
177         log("onResume: Setting up camera");
178         mPreviewHolder = null;
179         setUpCamera();
180     }
181 
182     @Override
onPause()183     public void onPause() {
184         super.onPause();
185         log("onPause: Releasing camera");
186         mCamera.release();
187         mState = CAMERA_UNINITIALIZED;
188     }
189 
190     /** SurfaceHolder.Callback methods */
surfaceChanged(SurfaceHolder holder, int format, int width, int height)191     public void surfaceChanged(SurfaceHolder holder,
192             int format,
193             int width,
194             int height) {
195         if (mPreviewHolder != null) return;
196 
197         log("Surface holder available: " + width + " x " + height);
198         mPreviewHolder = holder;
199         try {
200             mCamera.setPreviewDisplay(holder);
201         } catch (IOException e) {
202             logE("Unable to set up preview!");
203         }
204     }
205 
surfaceCreated(SurfaceHolder holder)206     public void surfaceCreated(SurfaceHolder holder) {
207 
208     }
209 
surfaceDestroyed(SurfaceHolder holder)210     public void surfaceDestroyed(SurfaceHolder holder) {
211         mPreviewHolder = null;
212     }
213 
214     /** UI controls enable/disable */
enablePreviewOnlyControls(boolean enabled)215     private void enablePreviewOnlyControls(boolean enabled) {
216         for (View v : mPreviewOnlyControls) {
217                 v.setEnabled(enabled);
218         }
219     }
220 
221     /** UI listeners */
222 
223     private AdapterView.OnItemSelectedListener mCameraSpinnerListener =
224                 new AdapterView.OnItemSelectedListener() {
225         public void onItemSelected(AdapterView<?> parent,
226                         View view, int pos, long id) {
227             if (mCameraId != pos) {
228                 mCameraId = pos;
229                 setUpCamera();
230             }
231         }
232 
233         public void onNothingSelected(AdapterView<?> parent) {
234 
235         }
236     };
237 
238     private OnClickListener mInfoButtonListener = new OnClickListener() {
239         public void onClick(View v) {
240             FragmentManager fm = getFragmentManager();
241             InfoDialogFragment infoDialog = new InfoDialogFragment();
242             infoDialog.updateInfo(mCameraId, mCamera);
243             infoDialog.show(fm, "info_dialog_fragment");
244         }
245     };
246 
247     private AdapterView.OnItemSelectedListener mPreviewSizeListener =
248         new AdapterView.OnItemSelectedListener() {
249         public void onItemSelected(AdapterView<?> parent,
250                 View view, int pos, long id) {
251             if (pos == mPreviewSize) return;
252             if (mState == CAMERA_PREVIEW) {
253                 log("Stopping preview to switch resolutions");
254                 mCamera.stopPreview();
255             }
256 
257             mPreviewSize = pos;
258             int width = mPreviewSizes.get(mPreviewSize).width;
259             int height = mPreviewSizes.get(mPreviewSize).height;
260             mParams.setPreviewSize(width, height);
261 
262             log("Setting preview size to " + width + "x" + height);
263 
264             mCamera.setParameters(mParams);
265 
266             if (mState == CAMERA_PREVIEW) {
267                 log("Restarting preview");
268                 resizePreview(width, height);
269                 mCamera.startPreview();
270             }
271         }
272 
273         public void onNothingSelected(AdapterView<?> parent) {
274 
275         }
276     };
277 
278     private View.OnClickListener mPreviewToggleListener =
279             new View.OnClickListener() {
280         public void onClick(View v) {
281             if (mState == CAMERA_TAKE_PICTURE) {
282                 logE("Can't change preview state while taking picture!");
283                 return;
284             }
285             if (mPreviewToggle.isChecked()) {
286                 log("Starting preview");
287                 resizePreview(mPreviewSizes.get(mPreviewSize).width,
288                         mPreviewSizes.get(mPreviewSize).height);
289                 mCamera.startPreview();
290                 mState = CAMERA_PREVIEW;
291                 enablePreviewOnlyControls(true);
292             } else {
293                 log("Stopping preview");
294                 mCamera.stopPreview();
295                 mState = CAMERA_OPEN;
296 
297                 enablePreviewOnlyControls(false);
298             }
299         }
300     };
301 
302     private OnItemSelectedListener mAutofocusModeListener =
303                 new OnItemSelectedListener() {
304         public void onItemSelected(AdapterView<?> parent,
305                         View view, int pos, long id) {
306             if (pos == mAfMode) return;
307 
308             mAfMode = pos;
309             String focusMode = mAfModes.get(mAfMode);
310             log("Setting focus mode to " + focusMode);
311             if (focusMode == Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE ||
312                         focusMode == Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO) {
313                 mCamera.setAutoFocusMoveCallback(mAutofocusMoveCallback);
314             }
315             mParams.setFocusMode(focusMode);
316 
317             mCamera.setParameters(mParams);
318         }
319 
320         public void onNothingSelected(AdapterView<?> arg0) {
321 
322         }
323     };
324 
325     private OnClickListener mAutofocusButtonListener =
326             new View.OnClickListener() {
327         public void onClick(View v) {
328             log("Triggering autofocus");
329             mCamera.autoFocus(mAutofocusCallback);
330         }
331     };
332 
333     private OnClickListener mCancelAutofocusButtonListener =
334             new View.OnClickListener() {
335         public void onClick(View v) {
336             log("Cancelling autofocus");
337             mCamera.cancelAutoFocus();
338         }
339     };
340 
341     private Camera.AutoFocusCallback mAutofocusCallback =
342             new Camera.AutoFocusCallback() {
343         public void onAutoFocus(boolean success, Camera camera) {
344             log("Autofocus completed: " + (success ? "success" : "failure") );
345         }
346     };
347 
348     private Camera.AutoFocusMoveCallback mAutofocusMoveCallback =
349             new Camera.AutoFocusMoveCallback() {
350         public void onAutoFocusMoving(boolean start, Camera camera) {
351             log("Autofocus movement: " + (start ? "starting" : "stopped") );
352         }
353     };
354 
355     private AdapterView.OnItemSelectedListener mSnapshotSizeListener =
356             new AdapterView.OnItemSelectedListener() {
357         public void onItemSelected(AdapterView<?> parent,
358                 View view, int pos, long id) {
359             if (pos == mSnapshotSize) return;
360 
361             mSnapshotSize = pos;
362             int width = mSnapshotSizes.get(mSnapshotSize).width;
363             int height = mSnapshotSizes.get(mSnapshotSize).height;
364             log("Setting snapshot size to " + width + " x " + height);
365 
366             mParams.setPictureSize(width, height);
367 
368             mCamera.setParameters(mParams);
369         }
370 
371         public void onNothingSelected(AdapterView<?> parent) {
372 
373         }
374     };
375 
376     private View.OnClickListener mTakePictureListener =
377             new View.OnClickListener() {
378         public void onClick(View v) {
379             log("Taking picture");
380             if (mState == CAMERA_PREVIEW) {
381                 mState = CAMERA_TAKE_PICTURE;
382                 enablePreviewOnlyControls(false);
383                 mPreviewToggle.setChecked(false);
384 
385                 mCamera.takePicture(mShutterCb, mRawCb, mPostviewCb, mJpegCb);
386             } else {
387                 logE("Can't take picture while not running preview!");
388             }
389         }
390     };
391 
392     private AdapterView.OnItemSelectedListener mCamcorderProfileListener =
393                 new AdapterView.OnItemSelectedListener() {
394         public void onItemSelected(AdapterView<?> parent,
395                         View view, int pos, long id) {
396             if (pos == mCamcorderProfile) return;
397 
398             log("Setting camcorder profile to " + ((TextView)view).getText());
399             mCamcorderProfile = pos;
400         }
401 
402         public void onNothingSelected(AdapterView<?> parent) {
403 
404         }
405     };
406 
407     private View.OnClickListener mRecordToggleListener =
408             new View.OnClickListener() {
409         public void onClick(View v) {
410             mPreviewToggle.setEnabled(false);
411             if (mState == CAMERA_PREVIEW) {
412                 startRecording();
413             } else if (mState == CAMERA_RECORD) {
414                 stopRecording(false);
415             } else {
416                 logE("Can't toggle recording in current state!");
417             }
418             mPreviewToggle.setEnabled(true);
419         }
420     };
421 
422     private Camera.ShutterCallback mShutterCb = new Camera.ShutterCallback() {
423         public void onShutter() {
424             log("Shutter callback received");
425         }
426     };
427 
428     private Camera.PictureCallback mRawCb = new Camera.PictureCallback() {
429         public void onPictureTaken(byte[] data, Camera camera) {
430             log("Raw callback received");
431         }
432     };
433 
434     private Camera.PictureCallback mPostviewCb = new Camera.PictureCallback() {
435         public void onPictureTaken(byte[] data, Camera camera) {
436             log("Postview callback received");
437         }
438     };
439 
440     private Camera.PictureCallback mJpegCb = new Camera.PictureCallback() {
441         public void onPictureTaken(byte[] data, Camera camera) {
442             log("JPEG picture callback received");
443             FragmentManager fm = getFragmentManager();
444             SnapshotDialogFragment snapshotDialog = new SnapshotDialogFragment();
445 
446             snapshotDialog.updateImage(data);
447             snapshotDialog.show(fm, "snapshot_dialog_fragment");
448 
449             mPreviewToggle.setEnabled(true);
450 
451             mState = CAMERA_OPEN;
452         }
453     };
454 
455     // Internal methods
456 
setUpCamera()457     void setUpCamera() {
458         log("Setting up camera " + mCameraId);
459         logIndent(1);
460         if (mState >= CAMERA_OPEN) {
461             log("Closing old camera");
462             mCamera.release();
463             mState = CAMERA_UNINITIALIZED;
464         }
465         log("Opening camera " + mCameraId);
466         mCamera = Camera.open(mCameraId);
467         mState = CAMERA_OPEN;
468 
469         mParams = mCamera.getParameters();
470 
471         // Set up preview size selection
472 
473         log("Configuring camera");
474         logIndent(1);
475 
476         updatePreviewSizes(mParams);
477         updateAfModes(mParams);
478         updateSnapshotSizes(mParams);
479         updateCamcorderProfile(mCameraId);
480 
481         // Update parameters based on above updates
482         mCamera.setParameters(mParams);
483 
484         if (mPreviewHolder != null) {
485             log("Setting preview display");
486             try {
487                 mCamera.setPreviewDisplay(mPreviewHolder);
488             } catch(IOException e) {
489                 Log.e(TAG, "Unable to set up preview!");
490             }
491         }
492 
493         logIndent(-1);
494 
495         mPreviewToggle.setEnabled(true);
496         mPreviewToggle.setChecked(false);
497         enablePreviewOnlyControls(false);
498 
499         int width = mPreviewSizes.get(mPreviewSize).width;
500         int height = mPreviewSizes.get(mPreviewSize).height;
501         resizePreview(width, height);
502         if (mPreviewToggle.isChecked()) {
503             log("Starting preview" );
504             mCamera.startPreview();
505             mState = CAMERA_PREVIEW;
506         } else {
507             mState = CAMERA_OPEN;
508         }
509         logIndent(-1);
510     }
511 
updateAfModes(Parameters params)512     private void updateAfModes(Parameters params) {
513         mAfModes = params.getSupportedFocusModes();
514 
515         mAutofocusModeSpinner.setAdapter(
516                 new ArrayAdapter<String>(this, R.layout.spinner_item,
517                         mAfModes.toArray(new String[0])));
518 
519         mAfMode = 0;
520 
521         params.setFocusMode(mAfModes.get(mAfMode));
522 
523         log("Setting AF mode to " + mAfModes.get(mAfMode));
524     }
525 
updatePreviewSizes(Camera.Parameters params)526     private void updatePreviewSizes(Camera.Parameters params) {
527         mPreviewSizes = params.getSupportedPreviewSizes();
528 
529         String[] availableSizeNames = new String[mPreviewSizes.size()];
530         int i = 0;
531         for (Camera.Size previewSize: mPreviewSizes) {
532             availableSizeNames[i++] =
533                 Integer.toString(previewSize.width) + " x " +
534                 Integer.toString(previewSize.height);
535         }
536         mPreviewSizeSpinner.setAdapter(
537                 new ArrayAdapter<String>(
538                         this, R.layout.spinner_item, availableSizeNames));
539 
540         mPreviewSize = 0;
541 
542         int width = mPreviewSizes.get(mPreviewSize).width;
543         int height = mPreviewSizes.get(mPreviewSize).height;
544         params.setPreviewSize(width, height);
545         log("Setting preview size to " + width + " x " + height);
546     }
547 
updateSnapshotSizes(Camera.Parameters params)548     private void updateSnapshotSizes(Camera.Parameters params) {
549         String[] availableSizeNames;
550         mSnapshotSizes = params.getSupportedPictureSizes();
551 
552         availableSizeNames = new String[mSnapshotSizes.size()];
553         int i = 0;
554         for (Camera.Size snapshotSize : mSnapshotSizes) {
555             availableSizeNames[i++] =
556                 Integer.toString(snapshotSize.width) + " x " +
557                 Integer.toString(snapshotSize.height);
558         }
559         mSnapshotSizeSpinner.setAdapter(
560                 new ArrayAdapter<String>(
561                         this, R.layout.spinner_item, availableSizeNames));
562 
563         mSnapshotSize = 0;
564 
565         int snapshotWidth = mSnapshotSizes.get(mSnapshotSize).width;
566         int snapshotHeight = mSnapshotSizes.get(mSnapshotSize).height;
567         params.setPictureSize(snapshotWidth, snapshotHeight);
568         log("Setting snapshot size to " + snapshotWidth + " x " + snapshotHeight);
569     }
570 
updateCamcorderProfile(int cameraId)571     private void updateCamcorderProfile(int cameraId) {
572         // Have to query all of these individually,
573         final int PROFILES[] = new int[] {
574             CamcorderProfile.QUALITY_1080P,
575             CamcorderProfile.QUALITY_480P,
576             CamcorderProfile.QUALITY_720P,
577             CamcorderProfile.QUALITY_CIF,
578             CamcorderProfile.QUALITY_HIGH,
579             CamcorderProfile.QUALITY_LOW,
580             CamcorderProfile.QUALITY_QCIF,
581             CamcorderProfile.QUALITY_QVGA,
582             CamcorderProfile.QUALITY_TIME_LAPSE_1080P,
583             CamcorderProfile.QUALITY_TIME_LAPSE_480P,
584             CamcorderProfile.QUALITY_TIME_LAPSE_720P,
585             CamcorderProfile.QUALITY_TIME_LAPSE_CIF,
586             CamcorderProfile.QUALITY_TIME_LAPSE_HIGH,
587             CamcorderProfile.QUALITY_TIME_LAPSE_LOW,
588             CamcorderProfile.QUALITY_TIME_LAPSE_QCIF,
589             CamcorderProfile.QUALITY_TIME_LAPSE_QVGA
590         };
591 
592         final String PROFILE_NAMES[] = new String[] {
593             "1080P",
594             "480P",
595             "720P",
596             "CIF",
597             "HIGH",
598             "LOW",
599             "QCIF",
600             "QVGA",
601             "TIME_LAPSE_1080P",
602             "TIME_LAPSE_480P",
603             "TIME_LAPSE_720P",
604             "TIME_LAPSE_CIF",
605             "TIME_LAPSE_HIGH",
606             "TIME_LAPSE_LOW",
607             "TIME_LAPSE_QCIF",
608             "TIME_LAPSE_QVGA"
609         };
610 
611         List<String> availableCamcorderProfileNames = new ArrayList<String>();
612         mCamcorderProfiles = new ArrayList<CamcorderProfile>();
613 
614         for (int i = 0; i < PROFILES.length; i++) {
615             if (CamcorderProfile.hasProfile(cameraId, PROFILES[i])) {
616                 availableCamcorderProfileNames.add(PROFILE_NAMES[i]);
617                 mCamcorderProfiles.add(CamcorderProfile.get(cameraId, PROFILES[i]));
618             }
619         }
620         String[] nameArray = (String[])availableCamcorderProfileNames.toArray(new String[0]);
621         mCamcorderProfileSpinner.setAdapter(
622                 new ArrayAdapter<String>(
623                         this, R.layout.spinner_item, nameArray));
624 
625         mCamcorderProfile = 0;
626         log("Setting camcorder profile to " + nameArray[mCamcorderProfile]);
627 
628     }
629 
resizePreview(int width, int height)630     void resizePreview(int width, int height) {
631         if (mPreviewHolder != null) {
632             int viewHeight = mPreviewView.getHeight();
633             int viewWidth = (int)(((double)width)/height * viewHeight);
634 
635             mPreviewView.setLayoutParams(
636                 new LayoutParams(viewWidth, viewHeight));
637         }
638 
639     }
640 
641     static final int MEDIA_TYPE_IMAGE = 0;
642     static final int MEDIA_TYPE_VIDEO = 1;
getOutputMediaFile(int type)643     File getOutputMediaFile(int type){
644         // To be safe, you should check that the SDCard is mounted
645         // using Environment.getExternalStorageState() before doing this.
646 
647         String state = Environment.getExternalStorageState();
648         if (!Environment.MEDIA_MOUNTED.equals(state)) {
649                 return null;
650         }
651 
652         File mediaStorageDir = new File(Environment.getExternalStoragePublicDirectory(
653                   Environment.DIRECTORY_DCIM), "TestingCamera");
654         // This location works best if you want the created images to be shared
655         // between applications and persist after your app has been uninstalled.
656 
657         // Create the storage directory if it does not exist
658         if (! mediaStorageDir.exists()){
659             if (! mediaStorageDir.mkdirs()){
660                 logE("Failed to create directory for pictures/video");
661                 return null;
662             }
663         }
664 
665         // Create a media file name
666         String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
667         File mediaFile;
668         if (type == MEDIA_TYPE_IMAGE){
669             mediaFile = new File(mediaStorageDir.getPath() + File.separator +
670             "IMG_"+ timeStamp + ".jpg");
671         } else if(type == MEDIA_TYPE_VIDEO) {
672             mediaFile = new File(mediaStorageDir.getPath() + File.separator +
673             "VID_"+ timeStamp + ".mp4");
674         } else {
675             return null;
676         }
677 
678         return mediaFile;
679     }
680 
notifyMediaScannerOfFile(File newFile, final MediaScannerConnection.OnScanCompletedListener listener)681     void notifyMediaScannerOfFile(File newFile,
682                 final MediaScannerConnection.OnScanCompletedListener listener) {
683         final Handler h = new Handler();
684         MediaScannerConnection.scanFile(this,
685                 new String[] { newFile.toString() },
686                 null,
687                 new MediaScannerConnection.OnScanCompletedListener() {
688                     public void onScanCompleted(final String path, final Uri uri) {
689                         h.post(new Runnable() {
690                             public void run() {
691                                 log("MediaScanner notified: " +
692                                         path + " -> " + uri);
693                                 if (listener != null)
694                                     listener.onScanCompleted(path, uri);
695                             }
696                         });
697                     }
698                 });
699     }
700 
deleteFile(File badFile)701     private void deleteFile(File badFile) {
702         if (badFile.exists()) {
703             boolean success = badFile.delete();
704             if (success) log("Deleted file " + badFile.toString());
705             else log("Unable to delete file " + badFile.toString());
706         }
707     }
708 
startRecording()709     private void startRecording() {
710         log("Starting recording");
711         logIndent(1);
712         log("Configuring MediaRecoder");
713         mCamera.unlock();
714         if (mRecorder != null) {
715             mRecorder.release();
716         }
717         mRecorder = new MediaRecorder();
718         mRecorder.setOnErrorListener(mRecordingErrorListener);
719         mRecorder.setOnInfoListener(mRecordingInfoListener);
720         mRecorder.setCamera(mCamera);
721         mRecorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER);
722         mRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
723         mRecorder.setProfile(mCamcorderProfiles.get(mCamcorderProfile));
724         File outputFile = getOutputMediaFile(MEDIA_TYPE_VIDEO);
725         log("File name:" + outputFile.toString());
726         mRecorder.setOutputFile(outputFile.toString());
727 
728         boolean ready = false;
729         log("Preparing MediaRecorder");
730         try {
731             mRecorder.prepare();
732             ready = true;
733         } catch (Exception e) {
734             StringWriter writer = new StringWriter();
735             e.printStackTrace(new PrintWriter(writer));
736             logE("Exception preparing MediaRecorder:\n" + writer.toString());
737         }
738 
739         if (ready) {
740             try {
741                 log("Starting MediaRecorder");
742                 mRecorder.start();
743                 mState = CAMERA_RECORD;
744                 log("Recording active");
745                 mRecordingFile = outputFile;
746             } catch (Exception e) {
747                 StringWriter writer = new StringWriter();
748                 e.printStackTrace(new PrintWriter(writer));
749                 logE("Exception starting MediaRecorder:\n" + writer.toString());
750             }
751         } else {
752             mPreviewToggle.setChecked(false);
753         }
754         logIndent(-1);
755     }
756 
757     private MediaRecorder.OnErrorListener mRecordingErrorListener =
758             new MediaRecorder.OnErrorListener() {
759         public void onError(MediaRecorder mr, int what, int extra) {
760             logE("MediaRecorder reports error: " + what + ", extra "
761                     + extra);
762             if (mState == CAMERA_RECORD) {
763                 stopRecording(true);
764             }
765         }
766     };
767 
768     private MediaRecorder.OnInfoListener mRecordingInfoListener =
769             new MediaRecorder.OnInfoListener() {
770         public void onInfo(MediaRecorder mr, int what, int extra) {
771             log("MediaRecorder reports info: " + what + ", extra "
772                     + extra);
773         }
774     };
775 
stopRecording(boolean error)776     private void stopRecording(boolean error) {
777         log("Stopping recording");
778         if (mRecorder != null) {
779             mRecorder.stop();
780             mCamera.lock();
781             mState = CAMERA_PREVIEW;
782             if (!error) {
783                 notifyMediaScannerOfFile(mRecordingFile, null);
784             } else {
785                 deleteFile(mRecordingFile);
786             }
787             mRecordingFile = null;
788         } else {
789             logE("Recorder is unexpectedly null!");
790         }
791     }
792 
793     private int mLogIndentLevel = 0;
794     private String mLogIndent = "\t";
795     /** Increment or decrement log indentation level */
logIndent(int delta)796     synchronized void logIndent(int delta) {
797         mLogIndentLevel += delta;
798         if (mLogIndentLevel < 0) mLogIndentLevel = 0;
799         char[] mLogIndentArray = new char[mLogIndentLevel + 1];
800         for (int i = -1; i < mLogIndentLevel; i++) {
801             mLogIndentArray[i + 1] = '\t';
802         }
803         mLogIndent = new String(mLogIndentArray);
804     }
805 
806     SimpleDateFormat mDateFormatter = new SimpleDateFormat("HH:mm:ss.SSS");
807     /** Log both to log text view and to device logcat */
log(String logLine)808     void log(String logLine) {
809         Log.d(TAG, logLine);
810         logAndScrollToBottom(logLine, mLogIndent);
811     }
812 
logE(String logLine)813     void logE(String logLine) {
814         Log.e(TAG, logLine);
815         logAndScrollToBottom(logLine, mLogIndent + "!!! ");
816     }
817 
logAndScrollToBottom(String logLine, String logIndent)818     synchronized private void logAndScrollToBottom(String logLine, String logIndent) {
819         StringBuffer logEntry = new StringBuffer(32);
820         logEntry.append("\n").append(mDateFormatter.format(new Date())).append(logIndent);
821         logEntry.append(logLine);
822         mLogView.append(logEntry);
823         final Layout layout = mLogView.getLayout();
824         if (layout != null){
825             int scrollDelta = layout.getLineBottom(mLogView.getLineCount() - 1)
826                 - mLogView.getScrollY() - mLogView.getHeight();
827             if(scrollDelta > 0) {
828                 mLogView.scrollBy(0, scrollDelta);
829             }
830         }
831     }
832 
833 }