TestingCamera.java revision 4ae1a87883045d820ffd6e1b8a95c13b1919e00a
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.graphics.Bitmap; 22import android.graphics.BitmapFactory; 23import android.hardware.Camera; 24import android.media.CamcorderProfile; 25import android.os.Bundle; 26import android.view.View; 27import android.view.SurfaceHolder; 28import android.view.SurfaceView; 29import android.view.View.OnClickListener; 30import android.widget.AdapterView; 31import android.widget.ArrayAdapter; 32import android.widget.Button; 33import android.widget.ImageView; 34import android.widget.LinearLayout.LayoutParams; 35import android.widget.Spinner; 36import android.widget.CompoundButton; 37import android.widget.TextView; 38import android.widget.ToggleButton; 39import android.support.v4.app.FragmentActivity; 40import android.support.v4.app.FragmentManager; 41import android.util.Log; 42 43import java.io.IOException; 44import java.util.ArrayList; 45import java.util.Comparator; 46import java.util.List; 47import java.util.TreeSet; 48 49/** 50 * A simple test application for the camera API. 51 * 52 * The goal of this application is to allow all camera API features to be 53 * excercised, and all information provided by the API to be shown. 54 */ 55public class TestingCamera extends FragmentActivity implements SurfaceHolder.Callback { 56 57 /** UI elements */ 58 private SurfaceView mPreviewView; 59 private SurfaceHolder mPreviewHolder; 60 61 private Spinner mCameraSpinner; 62 private Button mInfoButton; 63 private Spinner mPreviewSizeSpinner; 64 private ToggleButton mPreviewToggle; 65 private Spinner mSnapshotSizeSpinner; 66 private Button mTakePictureButton; 67 private Spinner mCamcorderProfileSpinner; 68 private ToggleButton mRecordToggle; 69 70 /** Camera state */ 71 private int mCameraId = 0; 72 private Camera mCamera; 73 private Camera.Parameters mParams; 74 private List<Camera.Size> mPreviewSizes; 75 private int mPreviewSize = 0; 76 private List<Camera.Size> mSnapshotSizes; 77 private int mSnapshotSize = 0; 78 private List<CamcorderProfile> mCamcorderProfiles; 79 private int mCamcorderProfile = 0; 80 81 private static final int CAMERA_UNINITIALIZED = 0; 82 private static final int CAMERA_OPEN = 1; 83 private static final int CAMERA_PREVIEW = 2; 84 private static final int CAMERA_TAKE_PICTURE = 3; 85 private int mState = CAMERA_UNINITIALIZED; 86 87 /** Misc variables */ 88 89 private static final String TAG = "TestingCamera"; 90 91 // Activity methods 92 @Override 93 public void onCreate(Bundle savedInstanceState) { 94 super.onCreate(savedInstanceState); 95 96 setContentView(R.layout.main); 97 98 mPreviewView = (SurfaceView)findViewById(R.id.preview); 99 mPreviewView.getHolder().addCallback(this); 100 101 mCameraSpinner = (Spinner) findViewById(R.id.camera_spinner); 102 mCameraSpinner.setOnItemSelectedListener(mCameraSpinnerListener); 103 104 mInfoButton = (Button) findViewById(R.id.info_button); 105 mInfoButton.setOnClickListener(mInfoButtonListener); 106 107 mPreviewSizeSpinner = (Spinner) findViewById(R.id.preview_size_spinner); 108 mPreviewSizeSpinner.setOnItemSelectedListener(mPreviewSizeListener); 109 110 mPreviewToggle = (ToggleButton) findViewById(R.id.start_preview); 111 mPreviewToggle.setOnClickListener(mPreviewToggleListener); 112 113 mSnapshotSizeSpinner = (Spinner) findViewById(R.id.snapshot_size_spinner); 114 mSnapshotSizeSpinner.setOnItemSelectedListener(mSnapshotSizeListener); 115 116 mTakePictureButton = (Button) findViewById(R.id.take_picture); 117 mTakePictureButton.setOnClickListener(mTakePictureListener); 118 119 mCamcorderProfileSpinner = (Spinner) findViewById(R.id.camcorder_profile_spinner); 120 mCamcorderProfileSpinner.setOnItemSelectedListener(mCamcorderProfileListener); 121 122 mRecordToggle = (ToggleButton) findViewById(R.id.start_record); 123 mRecordToggle.setOnClickListener(mRecordToggleListener); 124 125 int numCameras = Camera.getNumberOfCameras(); 126 String[] cameraNames = new String[numCameras]; 127 for (int i = 0; i < numCameras; i++) { 128 cameraNames[i] = "Camera " + i; 129 } 130 131 mCameraSpinner.setAdapter( 132 new ArrayAdapter<String>(this, 133 R.layout.spinner_item, cameraNames)); 134 } 135 136 @Override 137 public void onResume() { 138 super.onResume(); 139 mPreviewHolder = null; 140 setUpCamera(); 141 } 142 143 @Override 144 public void onPause() { 145 super.onPause(); 146 147 mCamera.release(); 148 mState = CAMERA_UNINITIALIZED; 149 } 150 151 // SurfaceHolder.Callback methods 152 @Override 153 public void surfaceChanged(SurfaceHolder holder, 154 int format, 155 int width, 156 int height) { 157 if (mPreviewHolder != null) return; 158 159 Log.d(TAG, "Surface holder available: " + width + " x " + height); 160 mPreviewHolder = holder; 161 try { 162 mCamera.setPreviewDisplay(holder); 163 } catch (IOException e) { 164 Log.e(TAG, "Unable to set up preview!"); 165 } 166 resizePreview(mPreviewSizes.get(mPreviewSize).width, 167 mPreviewSizes.get(mPreviewSize).height); 168 } 169 170 @Override 171 public void surfaceCreated(SurfaceHolder holder) { 172 173 } 174 175 @Override 176 public void surfaceDestroyed(SurfaceHolder holder) { 177 178 } 179 180 // UI listeners 181 182 private AdapterView.OnItemSelectedListener mCameraSpinnerListener = 183 new AdapterView.OnItemSelectedListener() { 184 public void onItemSelected(AdapterView<?> parent, 185 View view, int pos, long id) { 186 if (mCameraId != pos) { 187 mCameraId = pos; 188 setUpCamera(); 189 } 190 } 191 192 public void onNothingSelected(AdapterView parent) { 193 194 } 195 }; 196 197 private OnClickListener mInfoButtonListener = new OnClickListener() { 198 199 @Override 200 public void onClick(View v) { 201 FragmentManager fm = getSupportFragmentManager(); 202 InfoDialogFragment infoDialog = new InfoDialogFragment(); 203 infoDialog.updateInfo(mCameraId, mCamera); 204 infoDialog.show(fm, "info_dialog_fragment"); 205 } 206 207 }; 208 209 private AdapterView.OnItemSelectedListener mPreviewSizeListener = 210 new AdapterView.OnItemSelectedListener() { 211 public void onItemSelected(AdapterView<?> parent, 212 View view, int pos, long id) { 213 if (pos == mPreviewSize) return; 214 Log.d(TAG, "Switching preview sizes"); 215 216 if (mState == CAMERA_PREVIEW) { 217 mCamera.stopPreview(); 218 } 219 220 mPreviewSize = pos; 221 int width = mPreviewSizes.get(mPreviewSize).width; 222 int height = mPreviewSizes.get(mPreviewSize).height; 223 mParams.setPreviewSize(width, height); 224 225 mCamera.setParameters(mParams); 226 resizePreview(width, height); 227 228 if (mState == CAMERA_PREVIEW) { 229 mCamera.startPreview(); 230 } 231 } 232 233 public void onNothingSelected(AdapterView parent) { 234 235 } 236 }; 237 238 private View.OnClickListener mPreviewToggleListener = 239 new View.OnClickListener() { 240 public void onClick(View v) { 241 if (mState == CAMERA_TAKE_PICTURE) { 242 Log.e(TAG, "Can't change preview state while taking picture!"); 243 return; 244 } 245 if (mPreviewToggle.isChecked()) { 246 Log.d(TAG, "Starting preview"); 247 248 mCamera.startPreview(); 249 mState = CAMERA_PREVIEW; 250 251 mTakePictureButton.setEnabled(true); 252 } else { 253 Log.d(TAG, "Stopping preview"); 254 mCamera.stopPreview(); 255 mState = CAMERA_OPEN; 256 257 mTakePictureButton.setEnabled(false); 258 } 259 } 260 }; 261 262 private AdapterView.OnItemSelectedListener mSnapshotSizeListener = 263 new AdapterView.OnItemSelectedListener() { 264 public void onItemSelected(AdapterView<?> parent, 265 View view, int pos, long id) { 266 if (pos == mSnapshotSize) return; 267 Log.d(TAG, "Switching snapshot sizes"); 268 269 mSnapshotSize = pos; 270 int width = mSnapshotSizes.get(mSnapshotSize).width; 271 int height = mSnapshotSizes.get(mSnapshotSize).height; 272 mParams.setPictureSize(width, height); 273 274 mCamera.setParameters(mParams); 275 } 276 277 public void onNothingSelected(AdapterView parent) { 278 279 } 280 }; 281 282 private View.OnClickListener mTakePictureListener = 283 new View.OnClickListener() { 284 public void onClick(View v) { 285 Log.d(TAG, "Taking picture"); 286 if (mState == CAMERA_PREVIEW) { 287 mState = CAMERA_TAKE_PICTURE; 288 289 mTakePictureButton.setEnabled(false); 290 mPreviewToggle.setEnabled(false); 291 mPreviewToggle.setChecked(false); 292 293 mCamera.takePicture(mShutterCb, mRawCb, mPostviewCb, mJpegCb); 294 } else { 295 Log.e(TAG, "Can't take picture while not running preview!"); 296 } 297 } 298 }; 299 300 private AdapterView.OnItemSelectedListener mCamcorderProfileListener = 301 new AdapterView.OnItemSelectedListener() { 302 public void onItemSelected(AdapterView<?> parent, 303 View view, int pos, long id) { 304 mCamcorderProfile = pos; 305 } 306 307 public void onNothingSelected(AdapterView parent) { 308 309 } 310 }; 311 312 private View.OnClickListener mRecordToggleListener = 313 new View.OnClickListener() { 314 public void onClick(View v) { 315 } 316 }; 317 318 private Camera.ShutterCallback mShutterCb = new Camera.ShutterCallback() { 319 public void onShutter() { 320 Log.d(TAG, "Shutter cb fired"); 321 } 322 }; 323 324 private Camera.PictureCallback mRawCb = new Camera.PictureCallback() { 325 public void onPictureTaken(byte[] data, Camera camera) { 326 Log.d(TAG, "Raw cb fired"); 327 } 328 }; 329 330 private Camera.PictureCallback mPostviewCb = new Camera.PictureCallback() { 331 public void onPictureTaken(byte[] data, Camera camera) { 332 Log.d(TAG, "Postview cb fired"); 333 } 334 }; 335 336 private Camera.PictureCallback mJpegCb = new Camera.PictureCallback() { 337 public void onPictureTaken(byte[] data, Camera camera) { 338 Log.d(TAG, "JPEG cb fired"); 339 FragmentManager fm = getSupportFragmentManager(); 340 SnapshotDialogFragment snapshotDialog = new SnapshotDialogFragment(); 341 342 Bitmap img = BitmapFactory.decodeByteArray(data, 0, data.length); 343 snapshotDialog.updateImage(img); 344 snapshotDialog.show(fm, "snapshot_dialog_fragment"); 345 346 mPreviewToggle.setEnabled(true); 347 348 mState = CAMERA_OPEN; 349 } 350 }; 351 352 // Internal methods 353 354 void setUpCamera() { 355 Log.d(TAG, "Setting up camera " + mCameraId); 356 if (mState >= CAMERA_OPEN) { 357 Log.d(TAG, "Closing old camera"); 358 mCamera.release(); 359 mState = CAMERA_UNINITIALIZED; 360 } 361 Log.d(TAG, "Opening camera " + mCameraId); 362 mCamera = Camera.open(mCameraId); 363 mState = CAMERA_OPEN; 364 365 mParams = mCamera.getParameters(); 366 367 // Set up preview size selection 368 mPreviewSizes = mParams.getSupportedPreviewSizes(); 369 370 String[] availableSizeNames = new String[mPreviewSizes.size()]; 371 int i = 0; 372 for (Camera.Size previewSize: mPreviewSizes) { 373 availableSizeNames[i++] = 374 Integer.toString(previewSize.width) + " x " + 375 Integer.toString(previewSize.height); 376 } 377 mPreviewSizeSpinner.setAdapter( 378 new ArrayAdapter<String>( 379 this, R.layout.spinner_item, availableSizeNames)); 380 381 mPreviewSize = 0; 382 383 int width = mPreviewSizes.get(mPreviewSize).width; 384 int height = mPreviewSizes.get(mPreviewSize).height; 385 mParams.setPreviewSize(width, height); 386 387 // Set up snapshot size selection 388 389 mSnapshotSizes = mParams.getSupportedPictureSizes(); 390 391 availableSizeNames = new String[mSnapshotSizes.size()]; 392 i = 0; 393 for (Camera.Size snapshotSize : mSnapshotSizes) { 394 availableSizeNames[i++] = 395 Integer.toString(snapshotSize.width) + " x " + 396 Integer.toString(snapshotSize.height); 397 } 398 mSnapshotSizeSpinner.setAdapter( 399 new ArrayAdapter<String>( 400 this, R.layout.spinner_item, availableSizeNames)); 401 402 mSnapshotSize = 0; 403 404 width = mSnapshotSizes.get(mSnapshotSize).width; 405 height = mSnapshotSizes.get(mSnapshotSize).height; 406 mParams.setPictureSize(width, height); 407 408 // Set up camcorder profile selection 409 updateCamcorderProfile(mCameraId); 410 411 // Update parameters based on above defaults 412 413 mCamera.setParameters(mParams); 414 415 if (mPreviewHolder != null) { 416 try { 417 mCamera.setPreviewDisplay(mPreviewHolder); 418 } catch(IOException e) { 419 Log.e(TAG, "Unable to set up preview!"); 420 } 421 } 422 423 mPreviewToggle.setEnabled(true); 424 mPreviewToggle.setChecked(false); 425 mTakePictureButton.setEnabled(false); 426 427 resizePreview(width, height); 428 if (mPreviewToggle.isChecked()) { 429 Log.d(TAG, "Starting preview" ); 430 mCamera.startPreview(); 431 mState = CAMERA_PREVIEW; 432 } 433 } 434 435 private void updateCamcorderProfile(int cameraId) { 436 // Have to query all of these individually, 437 final int PROFILES[] = new int[] { 438 CamcorderProfile.QUALITY_1080P, 439 CamcorderProfile.QUALITY_480P, 440 CamcorderProfile.QUALITY_720P, 441 CamcorderProfile.QUALITY_CIF, 442 CamcorderProfile.QUALITY_HIGH, 443 CamcorderProfile.QUALITY_LOW, 444 CamcorderProfile.QUALITY_QCIF, 445 CamcorderProfile.QUALITY_QVGA, 446 CamcorderProfile.QUALITY_TIME_LAPSE_1080P, 447 CamcorderProfile.QUALITY_TIME_LAPSE_480P, 448 CamcorderProfile.QUALITY_TIME_LAPSE_720P, 449 CamcorderProfile.QUALITY_TIME_LAPSE_CIF, 450 CamcorderProfile.QUALITY_TIME_LAPSE_HIGH, 451 CamcorderProfile.QUALITY_TIME_LAPSE_LOW, 452 CamcorderProfile.QUALITY_TIME_LAPSE_QCIF, 453 CamcorderProfile.QUALITY_TIME_LAPSE_QVGA 454 }; 455 456 final String PROFILE_NAMES[] = new String[] { 457 "1080P", 458 "480P", 459 "720P", 460 "CIF", 461 "HIGH", 462 "LOW", 463 "QCIF", 464 "QVGA", 465 "TIME_LAPSE_1080P", 466 "TIME_LAPSE_480P", 467 "TIME_LAPSE_720P", 468 "TIME_LAPSE_CIF", 469 "TIME_LAPSE_HIGH", 470 "TIME_LAPSE_LOW", 471 "TIME_LAPSE_QCIF", 472 "TIME_LAPSE_QVGA" 473 }; 474 475 List<String> availableCamcorderProfileNames = new ArrayList<String>(); 476 mCamcorderProfiles = new ArrayList<CamcorderProfile>(); 477 478 for (int i = 0; i < PROFILES.length; i++) { 479 if (CamcorderProfile.hasProfile(cameraId, PROFILES[i])) { 480 availableCamcorderProfileNames.add(PROFILE_NAMES[i]); 481 mCamcorderProfiles.add(CamcorderProfile.get(cameraId, PROFILES[i])); 482 } 483 } 484 String[] nameArray = (String[])availableCamcorderProfileNames.toArray(new String[0]); 485 mCamcorderProfileSpinner.setAdapter( 486 new ArrayAdapter<String>( 487 this, R.layout.spinner_item, nameArray)); 488 } 489 490 void resizePreview(int width, int height) { 491 if (mPreviewHolder != null) { 492 int viewHeight = mPreviewView.getHeight(); 493 int viewWidth = (int)(((double)width)/height * viewHeight); 494 495 mPreviewView.setLayoutParams( 496 new LayoutParams(viewWidth, viewHeight)); 497 } 498 499 } 500}