Home | History | Annotate | Download | only in testingcamera
      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
    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
    175     public void onResume() {
    176         super.onResume();
    177         log("onResume: Setting up camera");
    178         mPreviewHolder = null;
    179         setUpCamera();
    180     }
    181 
    182     @Override
    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 */
    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 
    206     public void surfaceCreated(SurfaceHolder holder) {
    207 
    208     }
    209 
    210     public void surfaceDestroyed(SurfaceHolder holder) {
    211         mPreviewHolder = null;
    212     }
    213 
    214     /** UI controls enable/disable */
    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 
    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 
    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 
    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 
    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 
    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 
    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;
    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 
    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 
    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 
    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 
    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 */
    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 */
    808     void log(String logLine) {
    809         Log.d(TAG, logLine);
    810         logAndScrollToBottom(logLine, mLogIndent);
    811     }
    812 
    813     void logE(String logLine) {
    814         Log.e(TAG, logLine);
    815         logAndScrollToBottom(logLine, mLogIndent + "!!! ");
    816     }
    817 
    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 }