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
17package com.android.testingcamera2.v1;
18
19import android.app.Activity;
20import android.content.res.Configuration;
21import android.graphics.Bitmap;
22import android.graphics.BitmapFactory;
23import android.graphics.ImageFormat;
24import android.hardware.camera2.CameraCharacteristics;
25import android.hardware.camera2.CameraCaptureSession;
26import android.hardware.camera2.CameraDevice;
27import android.hardware.camera2.CaptureFailure;
28import android.hardware.camera2.CaptureRequest;
29import android.hardware.camera2.CaptureResult;
30import android.hardware.camera2.TotalCaptureResult;
31import android.media.Image;
32import android.media.MediaMuxer;
33import android.os.AsyncTask;
34import android.os.Bundle;
35import android.os.Handler;
36import android.util.Log;
37import android.util.Range;
38import android.view.OrientationEventListener;
39import android.view.SurfaceHolder;
40import android.view.SurfaceView;
41import android.view.View;
42import android.view.ViewGroup.LayoutParams;
43import android.view.WindowManager;
44import android.widget.AdapterView;
45import android.widget.AdapterView.OnItemSelectedListener;
46import android.widget.ArrayAdapter;
47import android.widget.Button;
48import android.widget.CompoundButton;
49import android.widget.CheckBox;
50import android.widget.ImageView;
51import android.widget.RadioGroup;
52import android.widget.SeekBar;
53import android.widget.SeekBar.OnSeekBarChangeListener;
54import android.widget.Spinner;
55import android.widget.TextView;
56import android.widget.ToggleButton;
57
58import java.nio.ByteBuffer;
59import java.util.ArrayList;
60import java.util.Arrays;
61import java.util.HashSet;
62import java.util.List;
63import java.util.Set;
64
65import com.android.testingcamera2.R;
66
67public 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