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