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