Home | History | Annotate | Download | only in v1
      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