1 /* 2 * Copyright (C) 2013 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.testingcamera2.v1; 18 19 import android.Manifest; 20 import android.app.Activity; 21 import android.content.pm.PackageManager; 22 import android.content.res.Configuration; 23 import android.graphics.Bitmap; 24 import android.graphics.BitmapFactory; 25 import android.graphics.ImageFormat; 26 import android.hardware.camera2.CameraCharacteristics; 27 import android.hardware.camera2.CameraCaptureSession; 28 import android.hardware.camera2.CameraDevice; 29 import android.hardware.camera2.CaptureFailure; 30 import android.hardware.camera2.CaptureRequest; 31 import android.hardware.camera2.CaptureResult; 32 import android.hardware.camera2.TotalCaptureResult; 33 import android.media.Image; 34 import android.media.MediaMuxer; 35 import android.os.AsyncTask; 36 import android.os.Bundle; 37 import android.os.Handler; 38 import android.util.Log; 39 import android.util.Range; 40 import android.view.OrientationEventListener; 41 import android.view.SurfaceHolder; 42 import android.view.SurfaceView; 43 import android.view.View; 44 import android.view.ViewGroup.LayoutParams; 45 import android.view.WindowManager; 46 import android.widget.AdapterView; 47 import android.widget.AdapterView.OnItemSelectedListener; 48 import android.widget.ArrayAdapter; 49 import android.widget.Button; 50 import android.widget.CompoundButton; 51 import android.widget.CheckBox; 52 import android.widget.ImageView; 53 import android.widget.RadioGroup; 54 import android.widget.SeekBar; 55 import android.widget.SeekBar.OnSeekBarChangeListener; 56 import android.widget.Spinner; 57 import android.widget.TextView; 58 import android.widget.ToggleButton; 59 60 import java.nio.ByteBuffer; 61 import java.util.ArrayList; 62 import java.util.Arrays; 63 import java.util.HashSet; 64 import java.util.List; 65 import java.util.Set; 66 67 import com.android.testingcamera2.R; 68 69 public class TestingCamera2 extends Activity implements SurfaceHolder.Callback { 70 71 private static final String TAG = "TestingCamera2"; 72 private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE); 73 private CameraOps mCameraOps; 74 private static final int mSeekBarMax = 100; 75 private static final long MAX_EXPOSURE = 200000000L; // 200ms 76 private static final long MIN_EXPOSURE = 100000L; // 100us 77 private static final long MAX_FRAME_DURATION = 1000000000L; // 1s 78 // Manual control change step size 79 private static final int STEP_SIZE = 100; 80 // Min and max sensitivity ISO values 81 private static final int MIN_SENSITIVITY = 100; 82 private static final int MAX_SENSITIVITY = 1600; 83 private static final int ORIENTATION_UNINITIALIZED = -1; 84 85 private int mLastOrientation = ORIENTATION_UNINITIALIZED; 86 private OrientationEventListener mOrientationEventListener; 87 private SurfaceView mPreviewView; 88 private ImageView mStillView; 89 90 private SurfaceHolder mCurrentPreviewHolder = null; 91 92 private Button mInfoButton; 93 private Button mFlushButton; 94 private ToggleButton mFocusLockToggle; 95 private Spinner mFocusModeSpinner; 96 private CheckBox mUseMediaCodecCheckBox; 97 98 private SeekBar mSensitivityBar; 99 private SeekBar mExposureBar; 100 private SeekBar mFrameDurationBar; 101 102 private TextView mSensitivityInfoView; 103 private TextView mExposureInfoView; 104 private TextView mFrameDurationInfoView; 105 private TextView mCaptureResultView; 106 private ToggleButton mRecordingToggle; 107 private ToggleButton mManualCtrlToggle; 108 109 private CameraControls mCameraControl = null; 110 private final Set<View> mManualControls = new HashSet<View>(); 111 private final Set<View> mAutoControls = new HashSet<View>(); 112 113 private static final int PERMISSIONS_REQUEST_CAMERA = 1; 114 115 Handler mMainHandler; 116 boolean mUseMediaCodec; 117 118 @Override onCreate(Bundle savedInstanceState)119 public void onCreate(Bundle savedInstanceState) { 120 super.onCreate(savedInstanceState); 121 122 getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, 123 WindowManager.LayoutParams.FLAG_FULLSCREEN); 124 125 setContentView(R.layout.main); 126 127 mPreviewView = (SurfaceView) findViewById(R.id.preview_view); 128 mPreviewView.getHolder().addCallback(this); 129 130 mStillView = (ImageView) findViewById(R.id.still_view); 131 132 mInfoButton = (Button) findViewById(R.id.info_button); 133 mInfoButton.setOnClickListener(mInfoButtonListener); 134 mFlushButton = (Button) findViewById(R.id.flush_button); 135 mFlushButton.setOnClickListener(mFlushButtonListener); 136 137 mFocusLockToggle = (ToggleButton) findViewById(R.id.focus_button); 138 mFocusLockToggle.setOnClickListener(mFocusLockToggleListener); 139 mFocusModeSpinner = (Spinner) findViewById(R.id.focus_mode_spinner); 140 mAutoControls.add(mFocusLockToggle); 141 142 mRecordingToggle = (ToggleButton) findViewById(R.id.start_recording); 143 mRecordingToggle.setOnClickListener(mRecordingToggleListener); 144 mUseMediaCodecCheckBox = (CheckBox) findViewById(R.id.use_media_codec); 145 mUseMediaCodecCheckBox.setOnCheckedChangeListener(mUseMediaCodecListener); 146 mUseMediaCodecCheckBox.setChecked(mUseMediaCodec); 147 148 mManualCtrlToggle = (ToggleButton) findViewById(R.id.manual_control); 149 mManualCtrlToggle.setOnClickListener(mControlToggleListener); 150 151 mSensitivityBar = (SeekBar) findViewById(R.id.sensitivity_seekbar); 152 mSensitivityBar.setOnSeekBarChangeListener(mSensitivitySeekBarListener); 153 mSensitivityBar.setMax(mSeekBarMax); 154 mManualControls.add(mSensitivityBar); 155 156 mExposureBar = (SeekBar) findViewById(R.id.exposure_time_seekbar); 157 mExposureBar.setOnSeekBarChangeListener(mExposureSeekBarListener); 158 mExposureBar.setMax(mSeekBarMax); 159 mManualControls.add(mExposureBar); 160 161 mFrameDurationBar = (SeekBar) findViewById(R.id.frame_duration_seekbar); 162 mFrameDurationBar.setOnSeekBarChangeListener(mFrameDurationSeekBarListener); 163 mFrameDurationBar.setMax(mSeekBarMax); 164 mManualControls.add(mFrameDurationBar); 165 166 mSensitivityInfoView = (TextView) findViewById(R.id.sensitivity_bar_label); 167 mExposureInfoView = (TextView) findViewById(R.id.exposure_time_bar_label); 168 mFrameDurationInfoView = (TextView) findViewById(R.id.frame_duration_bar_label); 169 mCaptureResultView = (TextView) findViewById(R.id.capture_result_info_label); 170 171 enableManualControls(false); 172 mCameraControl = new CameraControls(); 173 174 // Get UI handler 175 mMainHandler = new Handler(); 176 177 try { 178 mCameraOps = CameraOps.create(this, mCameraOpsListener, mMainHandler); 179 } catch(ApiFailureException e) { 180 logException("Cannot create camera ops!",e); 181 } 182 183 mOrientationEventListener = new OrientationEventListener(this) { 184 @Override 185 public void onOrientationChanged(int orientation) { 186 if (orientation == ORIENTATION_UNKNOWN) { 187 orientation = 0; 188 } 189 mCameraOps.updateOrientation(orientation); 190 } 191 }; 192 mOrientationEventListener.enable(); 193 // Process the initial configuration (for i.e. initial orientation) 194 // We need this because #onConfigurationChanged doesn't get called when the app launches 195 maybeUpdateConfiguration(getResources().getConfiguration()); 196 } 197 198 @Override onResume()199 public void onResume() { 200 super.onResume(); 201 if (VERBOSE) Log.v(TAG, String.format("onResume")); 202 203 if ((checkSelfPermission(Manifest.permission.CAMERA) 204 != PackageManager.PERMISSION_GRANTED ) 205 || (checkSelfPermission(Manifest.permission.RECORD_AUDIO) 206 != PackageManager.PERMISSION_GRANTED) 207 || (checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) 208 != PackageManager.PERMISSION_GRANTED)) { 209 Log.i(TAG, "Requested camera/video permissions"); 210 requestPermissions(new String[] { 211 Manifest.permission.CAMERA, 212 Manifest.permission.RECORD_AUDIO, 213 Manifest.permission.WRITE_EXTERNAL_STORAGE}, 214 PERMISSIONS_REQUEST_CAMERA); 215 return; 216 } 217 218 setUpPreview(); 219 } 220 221 @Override onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults)222 public void onRequestPermissionsResult(int requestCode, String[] permissions, 223 int[] grantResults) { 224 if (requestCode == PERMISSIONS_REQUEST_CAMERA) { 225 for (int i = 0; i < grantResults.length; i++) { 226 if (grantResults[i] == PackageManager.PERMISSION_DENIED) { 227 Log.i(TAG, "At least one permission denied, can't continue: " + permissions[i]); 228 finish(); 229 return; 230 } 231 } 232 233 Log.i(TAG, "All permissions granted"); 234 setUpPreview(); 235 } 236 } 237 setUpPreview()238 private void setUpPreview() { 239 try { 240 mCameraOps.minimalPreviewConfig(mPreviewView.getHolder()); 241 mCurrentPreviewHolder = mPreviewView.getHolder(); 242 } catch (ApiFailureException e) { 243 logException("Can't configure preview surface: ",e); 244 } 245 } 246 247 @Override onPause()248 public void onPause() { 249 super.onPause(); 250 try { 251 if (VERBOSE) Log.v(TAG, String.format("onPause")); 252 253 mCameraOps.closeDevice(); 254 } catch (ApiFailureException e) { 255 logException("Can't close device: ",e); 256 } 257 mCurrentPreviewHolder = null; 258 } 259 260 @Override onDestroy()261 protected void onDestroy() { 262 mOrientationEventListener.disable(); 263 super.onDestroy(); 264 } 265 266 @Override onConfigurationChanged(Configuration newConfig)267 public void onConfigurationChanged(Configuration newConfig) { 268 super.onConfigurationChanged(newConfig); 269 270 if (VERBOSE) { 271 Log.v(TAG, String.format("onConfiguredChanged: orientation %x", 272 newConfig.orientation)); 273 } 274 275 maybeUpdateConfiguration(newConfig); 276 } 277 maybeUpdateConfiguration(Configuration newConfig)278 private void maybeUpdateConfiguration(Configuration newConfig) { 279 if (VERBOSE) { 280 Log.v(TAG, String.format("handleConfiguration: orientation %x", 281 newConfig.orientation)); 282 } 283 284 if (mLastOrientation != newConfig.orientation) { 285 mLastOrientation = newConfig.orientation; 286 updatePreviewOrientation(); 287 } 288 } 289 updatePreviewOrientation()290 private void updatePreviewOrientation() { 291 LayoutParams params = mPreviewView.getLayoutParams(); 292 int width = params.width; 293 int height = params.height; 294 295 if (VERBOSE) { 296 Log.v(TAG, String.format( 297 "onConfiguredChanged: current layout is %dx%d", width, 298 height)); 299 } 300 /** 301 * Force wide aspect ratios for landscape 302 * Force narrow aspect ratios for portrait 303 */ 304 if (mLastOrientation == Configuration.ORIENTATION_LANDSCAPE) { 305 if (height > width) { 306 int tmp = width; 307 width = height; 308 height = tmp; 309 } 310 } else if (mLastOrientation == Configuration.ORIENTATION_PORTRAIT) { 311 if (width > height) { 312 int tmp = width; 313 width = height; 314 height = tmp; 315 } 316 } 317 318 if (width != params.width && height != params.height) { 319 if (VERBOSE) { 320 Log.v(TAG, String.format( 321 "onConfiguredChanged: updating preview size to %dx%d", width, 322 height)); 323 } 324 params.width = width; 325 params.height = height; 326 327 mPreviewView.setLayoutParams(params); 328 } 329 } 330 331 /** SurfaceHolder.Callback methods */ 332 @Override surfaceChanged(SurfaceHolder holder, int format, int width, int height)333 public void surfaceChanged(SurfaceHolder holder, 334 int format, 335 int width, 336 int height) { 337 if (VERBOSE) { 338 Log.v(TAG, String.format("surfaceChanged: format %x, width %d, height %d", format, 339 width, height)); 340 } 341 if (mCurrentPreviewHolder != null && holder == mCurrentPreviewHolder) { 342 try { 343 mCameraOps.minimalPreview(holder, mCameraControl); 344 } catch (ApiFailureException e) { 345 logException("Can't start minimal preview: ", e); 346 } 347 } 348 } 349 350 @Override surfaceCreated(SurfaceHolder holder)351 public void surfaceCreated(SurfaceHolder holder) { 352 353 } 354 355 @Override surfaceDestroyed(SurfaceHolder holder)356 public void surfaceDestroyed(SurfaceHolder holder) { 357 } 358 359 private final Button.OnClickListener mInfoButtonListener = new Button.OnClickListener() { 360 @Override 361 public void onClick(View v) { 362 final Handler uiHandler = new Handler(); 363 AsyncTask.execute(new Runnable() { 364 @Override 365 public void run() { 366 try { 367 mCameraOps.minimalJpegCapture(mCaptureCallback, mCaptureResultListener, 368 uiHandler, mCameraControl); 369 if (mCurrentPreviewHolder != null) { 370 mCameraOps.minimalPreview(mCurrentPreviewHolder, mCameraControl); 371 } 372 } catch (ApiFailureException e) { 373 logException("Can't take a JPEG! ", e); 374 } 375 } 376 }); 377 } 378 }; 379 380 private final Button.OnClickListener mFlushButtonListener = new Button.OnClickListener() { 381 @Override 382 public void onClick(View v) { 383 AsyncTask.execute(new Runnable() { 384 @Override 385 public void run() { 386 try { 387 mCameraOps.flush(); 388 } catch (ApiFailureException e) { 389 logException("Can't flush!", e); 390 } 391 } 392 }); 393 } 394 }; 395 396 /** 397 * UI controls enable/disable for all manual controls 398 */ enableManualControls(boolean enabled)399 private void enableManualControls(boolean enabled) { 400 for (View v : mManualControls) { 401 v.setEnabled(enabled); 402 } 403 404 for (View v : mAutoControls) { 405 v.setEnabled(!enabled); 406 } 407 } 408 409 private final CameraOps.CaptureCallback mCaptureCallback = new CameraOps.CaptureCallback() { 410 @Override 411 public void onCaptureAvailable(Image capture) { 412 if (capture.getFormat() != ImageFormat.JPEG) { 413 Log.e(TAG, "Unexpected format: " + capture.getFormat()); 414 return; 415 } 416 ByteBuffer jpegBuffer = capture.getPlanes()[0].getBuffer(); 417 byte[] jpegData = new byte[jpegBuffer.capacity()]; 418 jpegBuffer.get(jpegData); 419 420 Bitmap b = BitmapFactory.decodeByteArray(jpegData, 0, jpegData.length); 421 mStillView.setImageBitmap(b); 422 } 423 }; 424 425 // TODO: this callback is not called for each capture, need figure out why. 426 private final CameraOps.CaptureResultListener mCaptureResultListener = 427 new CameraOps.CaptureResultListener() { 428 429 @Override 430 public void onCaptureStarted(CameraCaptureSession session, CaptureRequest request, 431 long timestamp, long frameNumber) { 432 } 433 434 @Override 435 public void onCaptureCompleted( 436 CameraCaptureSession session, CaptureRequest request, 437 TotalCaptureResult result) { 438 Log.i(TAG, "Capture result is available"); 439 Integer reqCtrlMode; 440 Integer resCtrlMode; 441 if (request == null || result ==null) { 442 Log.e(TAG, "request/result is invalid"); 443 return; 444 } 445 Log.i(TAG, "Capture complete"); 446 final StringBuffer info = new StringBuffer("Capture Result:\n"); 447 448 reqCtrlMode = request.get(CaptureRequest.CONTROL_MODE); 449 resCtrlMode = result.get(CaptureResult.CONTROL_MODE); 450 info.append("Control mode: request " + reqCtrlMode + ". result " + resCtrlMode); 451 info.append("\n"); 452 453 Integer reqSen = request.get(CaptureRequest.SENSOR_SENSITIVITY); 454 Integer resSen = result.get(CaptureResult.SENSOR_SENSITIVITY); 455 info.append("Sensitivity: request " + reqSen + ". result " + resSen); 456 info.append("\n"); 457 458 Long reqExp = request.get(CaptureRequest.SENSOR_EXPOSURE_TIME); 459 Long resExp = result.get(CaptureResult.SENSOR_EXPOSURE_TIME); 460 info.append("Exposure: request " + reqExp + ". result " + resExp); 461 info.append("\n"); 462 463 Long reqFD = request.get(CaptureRequest.SENSOR_FRAME_DURATION); 464 Long resFD = result.get(CaptureResult.SENSOR_FRAME_DURATION); 465 info.append("Frame duration: request " + reqFD + ". result " + resFD); 466 info.append("\n"); 467 468 List<CaptureResult.Key<?>> resultKeys = result.getKeys(); 469 if (VERBOSE) { 470 CaptureResult.Key<?>[] arrayKeys = 471 resultKeys.toArray(new CaptureResult.Key<?>[0]); 472 Log.v(TAG, "onCaptureCompleted - dumping keys: " + 473 Arrays.toString(arrayKeys)); 474 } 475 info.append("Total keys: " + resultKeys.size()); 476 info.append("\n"); 477 478 if (mMainHandler != null) { 479 mMainHandler.post (new Runnable() { 480 @Override 481 public void run() { 482 // Update UI for capture result 483 mCaptureResultView.setText(info); 484 } 485 }); 486 } 487 } 488 489 @Override 490 public void onCaptureFailed(CameraCaptureSession session, CaptureRequest request, 491 CaptureFailure failure) { 492 Log.e(TAG, "Capture failed"); 493 } 494 }; 495 logException(String msg, Throwable e)496 private void logException(String msg, Throwable e) { 497 Log.e(TAG, msg + Log.getStackTraceString(e)); 498 } 499 getRadioFmt()500 private RadioGroup getRadioFmt() { 501 return (RadioGroup)findViewById(R.id.radio_fmt); 502 } 503 getOutputFormat()504 private int getOutputFormat() { 505 RadioGroup fmt = getRadioFmt(); 506 switch (fmt.getCheckedRadioButtonId()) { 507 case R.id.radio_mp4: 508 return MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4; 509 510 case R.id.radio_webm: 511 return MediaMuxer.OutputFormat.MUXER_OUTPUT_WEBM; 512 513 default: 514 throw new IllegalStateException("Checked button unrecognized."); 515 } 516 } 517 518 private final OnSeekBarChangeListener mSensitivitySeekBarListener = 519 new OnSeekBarChangeListener() { 520 521 @Override 522 public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { 523 Range<Integer> defaultRange = new Range<Integer>(MIN_SENSITIVITY, 524 MAX_SENSITIVITY); 525 CameraCharacteristics properties = mCameraOps.getCameraCharacteristics(); 526 Range<Integer> sensitivityRange = properties.get( 527 CameraCharacteristics.SENSOR_INFO_SENSITIVITY_RANGE); 528 if (sensitivityRange == null || sensitivityRange.getLower() > MIN_SENSITIVITY || 529 sensitivityRange.getUpper() < MAX_SENSITIVITY) { 530 Log.e(TAG, "unable to get sensitivity range, use default range"); 531 sensitivityRange = defaultRange; 532 } 533 int min = sensitivityRange.getLower(); 534 int max = sensitivityRange.getUpper(); 535 float progressFactor = progress / (float)mSeekBarMax; 536 int curSensitivity = (int) (min + (max - min) * progressFactor); 537 curSensitivity = (curSensitivity / STEP_SIZE ) * STEP_SIZE; 538 mCameraControl.getManualControls().setSensitivity(curSensitivity); 539 // Update the sensitivity info 540 StringBuffer info = new StringBuffer("Sensitivity(ISO):"); 541 info.append("" + curSensitivity); 542 mSensitivityInfoView.setText(info); 543 mCameraOps.updatePreview(mCameraControl); 544 } 545 546 @Override 547 public void onStartTrackingTouch(SeekBar seekBar) { 548 } 549 550 @Override 551 public void onStopTrackingTouch(SeekBar seekBar) { 552 } 553 }; 554 555 private final OnSeekBarChangeListener mExposureSeekBarListener = 556 new OnSeekBarChangeListener() { 557 558 @Override 559 public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { 560 Range<Long> defaultRange = new Range<Long>(MIN_EXPOSURE, MAX_EXPOSURE); 561 CameraCharacteristics properties = mCameraOps.getCameraCharacteristics(); 562 Range<Long> exposureRange = properties.get( 563 CameraCharacteristics.SENSOR_INFO_EXPOSURE_TIME_RANGE); 564 // Not enforce the max value check here, most of the devices don't support 565 // larger than 30s exposure time 566 if (exposureRange == null || exposureRange.getLower() > MIN_EXPOSURE || 567 exposureRange.getUpper() < 0) { 568 exposureRange = defaultRange; 569 Log.e(TAG, "exposure time range is invalid, use default range"); 570 } 571 long min = exposureRange.getLower(); 572 long max = exposureRange.getUpper(); 573 float progressFactor = progress / (float)mSeekBarMax; 574 long curExposureTime = (long) (min + (max - min) * progressFactor); 575 mCameraControl.getManualControls().setExposure(curExposureTime); 576 // Update the sensitivity info 577 StringBuffer info = new StringBuffer("Exposure Time:"); 578 info.append("" + curExposureTime / 1000000.0 + "ms"); 579 mExposureInfoView.setText(info); 580 mCameraOps.updatePreview(mCameraControl); 581 } 582 583 @Override 584 public void onStartTrackingTouch(SeekBar seekBar) { 585 } 586 587 @Override 588 public void onStopTrackingTouch(SeekBar seekBar) { 589 } 590 }; 591 592 private final OnSeekBarChangeListener mFrameDurationSeekBarListener = 593 new OnSeekBarChangeListener() { 594 595 @Override 596 public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { 597 CameraCharacteristics properties = mCameraOps.getCameraCharacteristics(); 598 Long frameDurationMax = properties.get( 599 CameraCharacteristics.SENSOR_INFO_MAX_FRAME_DURATION); 600 if (frameDurationMax == null || frameDurationMax <= 0) { 601 frameDurationMax = MAX_FRAME_DURATION; 602 Log.e(TAG, "max frame duration is invalid, set to " + frameDurationMax); 603 } 604 // Need calculate from different resolution, hard code to 10ms for now. 605 long min = 10000000L; 606 long max = frameDurationMax; 607 float progressFactor = progress / (float)mSeekBarMax; 608 long curFrameDuration = (long) (min + (max - min) * progressFactor); 609 mCameraControl.getManualControls().setFrameDuration(curFrameDuration); 610 // Update the sensitivity info 611 StringBuffer info = new StringBuffer("Frame Duration:"); 612 info.append("" + curFrameDuration / 1000000.0 + "ms"); 613 mFrameDurationInfoView.setText(info); 614 mCameraOps.updatePreview(mCameraControl); 615 } 616 617 @Override 618 public void onStartTrackingTouch(SeekBar seekBar) { 619 } 620 621 @Override 622 public void onStopTrackingTouch(SeekBar seekBar) { 623 } 624 }; 625 626 private final View.OnClickListener mControlToggleListener = 627 new View.OnClickListener() { 628 @Override 629 public void onClick(View v) { 630 boolean enableManual; 631 if (mManualCtrlToggle.isChecked()) { 632 enableManual = true; 633 } else { 634 enableManual = false; 635 } 636 mCameraControl.getManualControls().setManualControlEnabled(enableManual); 637 enableManualControls(enableManual); 638 mCameraOps.updatePreview(mCameraControl); 639 } 640 }; 641 642 private final View.OnClickListener mRecordingToggleListener = 643 new View.OnClickListener() { 644 @Override 645 public void onClick(View v) { 646 if (mRecordingToggle.isChecked()) { 647 try { 648 Log.i(TAG, "start recording, useMediaCodec = " + mUseMediaCodec); 649 RadioGroup fmt = getRadioFmt(); 650 fmt.setActivated(false); 651 mCameraOps.startRecording( 652 /* applicationContext */ TestingCamera2.this, 653 /* useMediaCodec */ mUseMediaCodec, 654 /* outputFormat */ getOutputFormat()); 655 } catch (ApiFailureException e) { 656 logException("Failed to start recording", e); 657 } 658 } else { 659 try { 660 mCameraOps.stopRecording(TestingCamera2.this); 661 getRadioFmt().setActivated(true); 662 } catch (ApiFailureException e) { 663 logException("Failed to stop recording", e); 664 } 665 } 666 } 667 }; 668 669 private final View.OnClickListener mFocusLockToggleListener = 670 new View.OnClickListener() { 671 @Override 672 public void onClick(View v) { 673 if (VERBOSE) { 674 Log.v(TAG, "focus_lock#onClick - start"); 675 } 676 677 CameraAutoFocusControls afControls = mCameraControl.getAfControls(); 678 679 if (mFocusLockToggle.isChecked()) { 680 Log.i(TAG, "lock focus"); 681 682 afControls.setPendingTriggerStart(); 683 } else { 684 Log.i(TAG, "unlock focus"); 685 686 afControls.setPendingTriggerCancel(); 687 } 688 689 mCameraOps.updatePreview(mCameraControl); 690 691 if (VERBOSE) { 692 Log.v(TAG, "focus_lock#onClick - end"); 693 } 694 } 695 }; 696 697 private final CompoundButton.OnCheckedChangeListener mUseMediaCodecListener = 698 new CompoundButton.OnCheckedChangeListener() { 699 @Override 700 public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { 701 mUseMediaCodec = isChecked; 702 } 703 }; 704 705 private final CameraOps.Listener mCameraOpsListener = new CameraOps.Listener() { 706 @Override 707 public void onCameraOpened(String cameraId, CameraCharacteristics characteristics) { 708 /* 709 * Populate dynamic per-camera settings 710 */ 711 712 // Map available AF Modes -> AF mode spinner dropdown list of strings 713 int[] availableAfModes = 714 characteristics.get(CameraCharacteristics.CONTROL_AF_AVAILABLE_MODES); 715 716 String[] allAfModes = getResources().getStringArray(R.array.focus_mode_spinner_arrays); 717 718 final List<String> afModeList = new ArrayList<>(); 719 final int[] afModePositions = new int[availableAfModes.length]; 720 721 int i = 0; 722 for (int mode : availableAfModes) { 723 afModeList.add(allAfModes[mode]); 724 afModePositions[i++] = mode; 725 } 726 727 ArrayAdapter<String> dataAdapter = new ArrayAdapter<>(TestingCamera2.this, 728 android.R.layout.simple_spinner_item, afModeList); 729 dataAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); 730 mFocusModeSpinner.setAdapter(dataAdapter); 731 732 /* 733 * Change the AF mode and update preview when AF spinner's selected item changes 734 */ 735 mFocusModeSpinner.setOnItemSelectedListener(new OnItemSelectedListener() { 736 737 @Override 738 public void onItemSelected(AdapterView<?> parent, View view, int position, 739 long id) { 740 int afMode = afModePositions[position]; 741 742 Log.i(TAG, "Change auto-focus mode to " + afModeList.get(position) 743 + " " + afMode); 744 745 mCameraControl.getAfControls().setAfMode(afMode); 746 747 mCameraOps.updatePreview(mCameraControl); 748 } 749 750 @Override 751 public void onNothingSelected(AdapterView<?> parent) { 752 // Do nothing 753 } 754 }); 755 } 756 }; 757 } 758