| /* |
| * Copyright (C) 2013 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.testingcamera2.v1; |
| |
| import android.Manifest; |
| import android.app.Activity; |
| import android.content.pm.PackageManager; |
| import android.content.res.Configuration; |
| import android.graphics.Bitmap; |
| import android.graphics.BitmapFactory; |
| import android.graphics.ImageFormat; |
| import android.hardware.camera2.CameraCharacteristics; |
| import android.hardware.camera2.CameraCaptureSession; |
| import android.hardware.camera2.CameraDevice; |
| import android.hardware.camera2.CaptureFailure; |
| import android.hardware.camera2.CaptureRequest; |
| import android.hardware.camera2.CaptureResult; |
| import android.hardware.camera2.TotalCaptureResult; |
| import android.media.Image; |
| import android.media.MediaMuxer; |
| import android.os.AsyncTask; |
| import android.os.Bundle; |
| import android.os.Handler; |
| import android.util.Log; |
| import android.util.Range; |
| import android.view.OrientationEventListener; |
| import android.view.SurfaceHolder; |
| import android.view.SurfaceView; |
| import android.view.View; |
| import android.view.ViewGroup.LayoutParams; |
| import android.view.WindowManager; |
| import android.widget.AdapterView; |
| import android.widget.AdapterView.OnItemSelectedListener; |
| import android.widget.ArrayAdapter; |
| import android.widget.Button; |
| import android.widget.CompoundButton; |
| import android.widget.CheckBox; |
| import android.widget.ImageView; |
| import android.widget.RadioGroup; |
| import android.widget.SeekBar; |
| import android.widget.SeekBar.OnSeekBarChangeListener; |
| import android.widget.Spinner; |
| import android.widget.TextView; |
| import android.widget.ToggleButton; |
| |
| import java.nio.ByteBuffer; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Set; |
| |
| import com.android.testingcamera2.R; |
| |
| public class TestingCamera2 extends Activity implements SurfaceHolder.Callback { |
| |
| private static final String TAG = "TestingCamera2"; |
| private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE); |
| private CameraOps mCameraOps; |
| private String[] mAvailableCameraIds; |
| private String mCameraId; |
| private static final int mSeekBarMax = 100; |
| private static final long MAX_EXPOSURE = 200000000L; // 200ms |
| private static final long MIN_EXPOSURE = 100000L; // 100us |
| private static final long MAX_FRAME_DURATION = 1000000000L; // 1s |
| // Manual control change step size |
| private static final int STEP_SIZE = 100; |
| // Min and max sensitivity ISO values |
| private static final int MIN_SENSITIVITY = 100; |
| private static final int MAX_SENSITIVITY = 1600; |
| private static final int ORIENTATION_UNINITIALIZED = -1; |
| |
| private int mLastOrientation = ORIENTATION_UNINITIALIZED; |
| private OrientationEventListener mOrientationEventListener; |
| private SurfaceView mPreviewView; |
| private SurfaceView mPreviewView2; |
| private ImageView mStillView; |
| |
| private SurfaceHolder mCurrentPreviewHolder = null; |
| private SurfaceHolder mCurrentPreviewHolder2 = null; |
| |
| private Spinner mCameraIdSpinner; |
| private OnItemSelectedListener mCameraIdSelectionListener; |
| private Button mInfoButton; |
| private Button mFlushButton; |
| private ToggleButton mFocusLockToggle; |
| private Spinner mFocusModeSpinner; |
| private CheckBox mUseMediaCodecCheckBox; |
| |
| private SeekBar mSensitivityBar; |
| private SeekBar mExposureBar; |
| private SeekBar mFrameDurationBar; |
| |
| private TextView mSensitivityInfoView; |
| private TextView mExposureInfoView; |
| private TextView mFrameDurationInfoView; |
| private TextView mCaptureResultView; |
| private ToggleButton mRecordingToggle; |
| private ToggleButton mManualCtrlToggle; |
| |
| private CameraControls mCameraControl = null; |
| private final Set<View> mManualControls = new HashSet<View>(); |
| private final Set<View> mAutoControls = new HashSet<View>(); |
| |
| private static final int PERMISSIONS_REQUEST_CAMERA = 1; |
| |
| Handler mMainHandler; |
| boolean mUseMediaCodec; |
| |
| @Override |
| public void onCreate(Bundle savedInstanceState) { |
| super.onCreate(savedInstanceState); |
| |
| getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, |
| WindowManager.LayoutParams.FLAG_FULLSCREEN); |
| |
| setContentView(R.layout.main); |
| |
| mPreviewView = (SurfaceView) findViewById(R.id.preview_view); |
| mPreviewView.getHolder().addCallback(this); |
| |
| mPreviewView2 = (SurfaceView) findViewById(R.id.preview_view2); |
| mPreviewView2.getHolder().addCallback(this); |
| |
| mStillView = (ImageView) findViewById(R.id.still_view); |
| |
| mCameraIdSpinner = (Spinner) findViewById(R.id.camera_id_spinner); |
| mInfoButton = (Button) findViewById(R.id.info_button); |
| mInfoButton.setOnClickListener(mInfoButtonListener); |
| mFlushButton = (Button) findViewById(R.id.flush_button); |
| mFlushButton.setOnClickListener(mFlushButtonListener); |
| |
| mFocusLockToggle = (ToggleButton) findViewById(R.id.focus_button); |
| mFocusLockToggle.setOnClickListener(mFocusLockToggleListener); |
| mFocusModeSpinner = (Spinner) findViewById(R.id.focus_mode_spinner); |
| mAutoControls.add(mFocusLockToggle); |
| |
| mRecordingToggle = (ToggleButton) findViewById(R.id.start_recording); |
| mRecordingToggle.setOnClickListener(mRecordingToggleListener); |
| mUseMediaCodecCheckBox = (CheckBox) findViewById(R.id.use_media_codec); |
| mUseMediaCodecCheckBox.setOnCheckedChangeListener(mUseMediaCodecListener); |
| mUseMediaCodecCheckBox.setChecked(mUseMediaCodec); |
| |
| mManualCtrlToggle = (ToggleButton) findViewById(R.id.manual_control); |
| mManualCtrlToggle.setOnClickListener(mControlToggleListener); |
| |
| mSensitivityBar = (SeekBar) findViewById(R.id.sensitivity_seekbar); |
| mSensitivityBar.setOnSeekBarChangeListener(mSensitivitySeekBarListener); |
| mSensitivityBar.setMax(mSeekBarMax); |
| mManualControls.add(mSensitivityBar); |
| |
| mExposureBar = (SeekBar) findViewById(R.id.exposure_time_seekbar); |
| mExposureBar.setOnSeekBarChangeListener(mExposureSeekBarListener); |
| mExposureBar.setMax(mSeekBarMax); |
| mManualControls.add(mExposureBar); |
| |
| mFrameDurationBar = (SeekBar) findViewById(R.id.frame_duration_seekbar); |
| mFrameDurationBar.setOnSeekBarChangeListener(mFrameDurationSeekBarListener); |
| mFrameDurationBar.setMax(mSeekBarMax); |
| mManualControls.add(mFrameDurationBar); |
| |
| mSensitivityInfoView = (TextView) findViewById(R.id.sensitivity_bar_label); |
| mExposureInfoView = (TextView) findViewById(R.id.exposure_time_bar_label); |
| mFrameDurationInfoView = (TextView) findViewById(R.id.frame_duration_bar_label); |
| mCaptureResultView = (TextView) findViewById(R.id.capture_result_info_label); |
| |
| enableManualControls(false); |
| mCameraControl = new CameraControls(); |
| |
| // Get UI handler |
| mMainHandler = new Handler(); |
| |
| try { |
| mCameraOps = CameraOps.create(this, mCameraOpsListener, mMainHandler); |
| } catch(ApiFailureException e) { |
| logException("Cannot create camera ops!",e); |
| } |
| |
| // Map available camera Ids to camera id spinner dropdown list of strings |
| try { |
| mAvailableCameraIds = mCameraOps.getDevices(); |
| if (mAvailableCameraIds == null || mAvailableCameraIds.length == 0) { |
| throw new ApiFailureException("no devices"); |
| } |
| } catch(ApiFailureException e) { |
| logException("CameraOps::getDevices failed!",e); |
| } |
| mCameraId = mAvailableCameraIds[0]; |
| ArrayAdapter<String> cameraIdDataAdapter = new ArrayAdapter<>(TestingCamera2.this, |
| android.R.layout.simple_spinner_item, mAvailableCameraIds); |
| mCameraIdSpinner.setAdapter(cameraIdDataAdapter); |
| |
| /* |
| * Change the camera ID and update preview when camera ID spinner's selected item changes |
| */ |
| mCameraIdSpinner.setOnItemSelectedListener(mCameraIdSelectedListener); |
| |
| mOrientationEventListener = new OrientationEventListener(this) { |
| @Override |
| public void onOrientationChanged(int orientation) { |
| if (orientation == ORIENTATION_UNKNOWN) { |
| orientation = 0; |
| } |
| mCameraOps.updateOrientation(orientation); |
| } |
| }; |
| mOrientationEventListener.enable(); |
| // Process the initial configuration (for i.e. initial orientation) |
| // We need this because #onConfigurationChanged doesn't get called when the app launches |
| maybeUpdateConfiguration(getResources().getConfiguration()); |
| } |
| |
| @Override |
| public void onResume() { |
| super.onResume(); |
| if (VERBOSE) Log.v(TAG, String.format("onResume")); |
| |
| if ((checkSelfPermission(Manifest.permission.CAMERA) |
| != PackageManager.PERMISSION_GRANTED ) |
| || (checkSelfPermission(Manifest.permission.RECORD_AUDIO) |
| != PackageManager.PERMISSION_GRANTED) |
| || (checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) |
| != PackageManager.PERMISSION_GRANTED)) { |
| Log.i(TAG, "Requested camera/video permissions"); |
| requestPermissions(new String[] { |
| Manifest.permission.CAMERA, |
| Manifest.permission.RECORD_AUDIO, |
| Manifest.permission.WRITE_EXTERNAL_STORAGE}, |
| PERMISSIONS_REQUEST_CAMERA); |
| return; |
| } |
| |
| setUpPreview(); |
| } |
| |
| @Override |
| public void onRequestPermissionsResult(int requestCode, String[] permissions, |
| int[] grantResults) { |
| if (requestCode == PERMISSIONS_REQUEST_CAMERA) { |
| for (int i = 0; i < grantResults.length; i++) { |
| if (grantResults[i] == PackageManager.PERMISSION_DENIED) { |
| Log.i(TAG, "At least one permission denied, can't continue: " + permissions[i]); |
| finish(); |
| return; |
| } |
| } |
| |
| Log.i(TAG, "All permissions granted"); |
| setUpPreview(); |
| } |
| } |
| |
| private void setUpPreview() { |
| try { |
| mCameraOps.minimalPreviewConfig(mCameraId, mPreviewView.getHolder(), |
| mPreviewView2.getHolder()); |
| mCurrentPreviewHolder = mPreviewView.getHolder(); |
| mCurrentPreviewHolder2 = mPreviewView2.getHolder(); |
| } catch (ApiFailureException e) { |
| logException("Can't configure preview surface: ",e); |
| } |
| } |
| |
| @Override |
| public void onPause() { |
| super.onPause(); |
| try { |
| if (VERBOSE) Log.v(TAG, String.format("onPause")); |
| |
| mCameraOps.closeDevice(); |
| } catch (ApiFailureException e) { |
| logException("Can't close device: ",e); |
| } |
| mCurrentPreviewHolder = null; |
| mCurrentPreviewHolder2 = null; |
| } |
| |
| @Override |
| protected void onDestroy() { |
| mOrientationEventListener.disable(); |
| super.onDestroy(); |
| } |
| |
| @Override |
| public void onConfigurationChanged(Configuration newConfig) { |
| super.onConfigurationChanged(newConfig); |
| |
| if (VERBOSE) { |
| Log.v(TAG, String.format("onConfiguredChanged: orientation %x", |
| newConfig.orientation)); |
| } |
| |
| maybeUpdateConfiguration(newConfig); |
| } |
| |
| private void maybeUpdateConfiguration(Configuration newConfig) { |
| if (VERBOSE) { |
| Log.v(TAG, String.format("handleConfiguration: orientation %x", |
| newConfig.orientation)); |
| } |
| |
| if (mLastOrientation != newConfig.orientation) { |
| mLastOrientation = newConfig.orientation; |
| updatePreviewOrientation(); |
| } |
| } |
| |
| private void updatePreviewOrientation() { |
| LayoutParams params = mPreviewView.getLayoutParams(); |
| LayoutParams params2 = mPreviewView2.getLayoutParams(); |
| int width = params.width; |
| int height = params.height; |
| |
| if (VERBOSE) { |
| Log.v(TAG, String.format( |
| "onConfiguredChanged: current layout is %dx%d", width, |
| height)); |
| } |
| /** |
| * Force wide aspect ratios for landscape |
| * Force narrow aspect ratios for portrait |
| */ |
| if (mLastOrientation == Configuration.ORIENTATION_LANDSCAPE) { |
| if (height > width) { |
| int tmp = width; |
| width = height; |
| height = tmp; |
| } |
| } else if (mLastOrientation == Configuration.ORIENTATION_PORTRAIT) { |
| if (width > height) { |
| int tmp = width; |
| width = height; |
| height = tmp; |
| } |
| } |
| |
| if (width != params.width && height != params.height) { |
| if (VERBOSE) { |
| Log.v(TAG, String.format( |
| "onConfiguredChanged: updating preview size to %dx%d", width, |
| height)); |
| } |
| params.width = width; |
| params.height = height; |
| mPreviewView.setLayoutParams(params); |
| |
| params2.width = width; |
| params2.height = height; |
| mPreviewView2.setLayoutParams(params2); |
| } |
| } |
| |
| /** SurfaceHolder.Callback methods */ |
| @Override |
| public void surfaceChanged(SurfaceHolder holder, |
| int format, |
| int width, |
| int height) { |
| if (VERBOSE) { |
| Log.v(TAG, String.format("surfaceChanged: format %x, width %d, height %d", format, |
| width, height)); |
| } |
| if ((mCurrentPreviewHolder != null && holder == mCurrentPreviewHolder) || |
| (mCurrentPreviewHolder2 != null && holder == mCurrentPreviewHolder2)) { |
| try { |
| mCameraOps.minimalPreview(mCurrentPreviewHolder, mCurrentPreviewHolder2, |
| mCameraControl); |
| } catch (ApiFailureException e) { |
| logException("Can't start minimal preview: ", e); |
| } |
| } |
| } |
| |
| @Override |
| public void surfaceCreated(SurfaceHolder holder) { |
| |
| } |
| |
| @Override |
| public void surfaceDestroyed(SurfaceHolder holder) { |
| } |
| |
| private final Button.OnClickListener mInfoButtonListener = new Button.OnClickListener() { |
| @Override |
| public void onClick(View v) { |
| final Handler uiHandler = new Handler(); |
| AsyncTask.execute(new Runnable() { |
| @Override |
| public void run() { |
| try { |
| mCameraOps.minimalJpegCapture(mCaptureCallback, mCaptureResultListener, |
| uiHandler, mCameraControl); |
| if (mCurrentPreviewHolder != null && mCurrentPreviewHolder2 != null) { |
| mCameraOps.minimalPreview(mCurrentPreviewHolder, |
| mCurrentPreviewHolder2, mCameraControl); |
| } |
| } catch (ApiFailureException e) { |
| logException("Can't take a JPEG! ", e); |
| } |
| } |
| }); |
| } |
| }; |
| |
| private final Button.OnClickListener mFlushButtonListener = new Button.OnClickListener() { |
| @Override |
| public void onClick(View v) { |
| AsyncTask.execute(new Runnable() { |
| @Override |
| public void run() { |
| try { |
| mCameraOps.flush(); |
| } catch (ApiFailureException e) { |
| logException("Can't flush!", e); |
| } |
| } |
| }); |
| } |
| }; |
| |
| /** |
| * UI controls enable/disable for all manual controls |
| */ |
| private void enableManualControls(boolean enabled) { |
| for (View v : mManualControls) { |
| v.setEnabled(enabled); |
| } |
| |
| for (View v : mAutoControls) { |
| v.setEnabled(!enabled); |
| } |
| } |
| |
| private final CameraOps.CaptureCallback mCaptureCallback = new CameraOps.CaptureCallback() { |
| @Override |
| public void onCaptureAvailable(Image capture) { |
| if (capture.getFormat() != ImageFormat.JPEG) { |
| Log.e(TAG, "Unexpected format: " + capture.getFormat()); |
| return; |
| } |
| ByteBuffer jpegBuffer = capture.getPlanes()[0].getBuffer(); |
| byte[] jpegData = new byte[jpegBuffer.capacity()]; |
| jpegBuffer.get(jpegData); |
| |
| Bitmap b = BitmapFactory.decodeByteArray(jpegData, 0, jpegData.length); |
| mStillView.setImageBitmap(b); |
| } |
| }; |
| |
| // TODO: this callback is not called for each capture, need figure out why. |
| private final CameraOps.CaptureResultListener mCaptureResultListener = |
| new CameraOps.CaptureResultListener() { |
| |
| @Override |
| public void onCaptureStarted(CameraCaptureSession session, CaptureRequest request, |
| long timestamp, long frameNumber) { |
| } |
| |
| @Override |
| public void onCaptureCompleted( |
| CameraCaptureSession session, CaptureRequest request, |
| TotalCaptureResult result) { |
| Log.i(TAG, "Capture result is available"); |
| Integer reqCtrlMode; |
| Integer resCtrlMode; |
| if (request == null || result ==null) { |
| Log.e(TAG, "request/result is invalid"); |
| return; |
| } |
| Log.i(TAG, "Capture complete"); |
| final StringBuffer info = new StringBuffer("Capture Result:\n"); |
| |
| reqCtrlMode = request.get(CaptureRequest.CONTROL_MODE); |
| resCtrlMode = result.get(CaptureResult.CONTROL_MODE); |
| info.append("Control mode: request " + reqCtrlMode + ". result " + resCtrlMode); |
| info.append("\n"); |
| |
| Integer reqSen = request.get(CaptureRequest.SENSOR_SENSITIVITY); |
| Integer resSen = result.get(CaptureResult.SENSOR_SENSITIVITY); |
| info.append("Sensitivity: request " + reqSen + ". result " + resSen); |
| info.append("\n"); |
| |
| Long reqExp = request.get(CaptureRequest.SENSOR_EXPOSURE_TIME); |
| Long resExp = result.get(CaptureResult.SENSOR_EXPOSURE_TIME); |
| info.append("Exposure: request " + reqExp + ". result " + resExp); |
| info.append("\n"); |
| |
| Long reqFD = request.get(CaptureRequest.SENSOR_FRAME_DURATION); |
| Long resFD = result.get(CaptureResult.SENSOR_FRAME_DURATION); |
| info.append("Frame duration: request " + reqFD + ". result " + resFD); |
| info.append("\n"); |
| |
| List<CaptureResult.Key<?>> resultKeys = result.getKeys(); |
| if (VERBOSE) { |
| CaptureResult.Key<?>[] arrayKeys = |
| resultKeys.toArray(new CaptureResult.Key<?>[0]); |
| Log.v(TAG, "onCaptureCompleted - dumping keys: " + |
| Arrays.toString(arrayKeys)); |
| } |
| info.append("Total keys: " + resultKeys.size()); |
| info.append("\n"); |
| |
| if (mMainHandler != null) { |
| mMainHandler.post (new Runnable() { |
| @Override |
| public void run() { |
| // Update UI for capture result |
| mCaptureResultView.setText(info); |
| } |
| }); |
| } |
| } |
| |
| @Override |
| public void onCaptureFailed(CameraCaptureSession session, CaptureRequest request, |
| CaptureFailure failure) { |
| Log.e(TAG, "Capture failed"); |
| } |
| }; |
| |
| private void logException(String msg, Throwable e) { |
| Log.e(TAG, msg + Log.getStackTraceString(e)); |
| } |
| |
| private RadioGroup getRadioFmt() { |
| return (RadioGroup)findViewById(R.id.radio_fmt); |
| } |
| |
| private int getOutputFormat() { |
| RadioGroup fmt = getRadioFmt(); |
| switch (fmt.getCheckedRadioButtonId()) { |
| case R.id.radio_mp4: |
| return MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4; |
| |
| case R.id.radio_webm: |
| return MediaMuxer.OutputFormat.MUXER_OUTPUT_WEBM; |
| |
| default: |
| throw new IllegalStateException("Checked button unrecognized."); |
| } |
| } |
| |
| private final OnSeekBarChangeListener mSensitivitySeekBarListener = |
| new OnSeekBarChangeListener() { |
| |
| @Override |
| public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { |
| Range<Integer> defaultRange = new Range<Integer>(MIN_SENSITIVITY, |
| MAX_SENSITIVITY); |
| CameraCharacteristics properties = mCameraOps.getCameraCharacteristics(); |
| Range<Integer> sensitivityRange = properties.get( |
| CameraCharacteristics.SENSOR_INFO_SENSITIVITY_RANGE); |
| if (sensitivityRange == null || sensitivityRange.getLower() > MIN_SENSITIVITY || |
| sensitivityRange.getUpper() < MAX_SENSITIVITY) { |
| Log.e(TAG, "unable to get sensitivity range, use default range"); |
| sensitivityRange = defaultRange; |
| } |
| int min = sensitivityRange.getLower(); |
| int max = sensitivityRange.getUpper(); |
| float progressFactor = progress / (float)mSeekBarMax; |
| int curSensitivity = (int) (min + (max - min) * progressFactor); |
| curSensitivity = (curSensitivity / STEP_SIZE ) * STEP_SIZE; |
| mCameraControl.getManualControls().setSensitivity(curSensitivity); |
| // Update the sensitivity info |
| StringBuffer info = new StringBuffer("Sensitivity(ISO):"); |
| info.append("" + curSensitivity); |
| mSensitivityInfoView.setText(info); |
| mCameraOps.updatePreview(mCameraControl); |
| } |
| |
| @Override |
| public void onStartTrackingTouch(SeekBar seekBar) { |
| } |
| |
| @Override |
| public void onStopTrackingTouch(SeekBar seekBar) { |
| } |
| }; |
| |
| private final OnSeekBarChangeListener mExposureSeekBarListener = |
| new OnSeekBarChangeListener() { |
| |
| @Override |
| public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { |
| Range<Long> defaultRange = new Range<Long>(MIN_EXPOSURE, MAX_EXPOSURE); |
| CameraCharacteristics properties = mCameraOps.getCameraCharacteristics(); |
| Range<Long> exposureRange = properties.get( |
| CameraCharacteristics.SENSOR_INFO_EXPOSURE_TIME_RANGE); |
| // Not enforce the max value check here, most of the devices don't support |
| // larger than 30s exposure time |
| if (exposureRange == null || exposureRange.getLower() > MIN_EXPOSURE || |
| exposureRange.getUpper() < 0) { |
| exposureRange = defaultRange; |
| Log.e(TAG, "exposure time range is invalid, use default range"); |
| } |
| long min = exposureRange.getLower(); |
| long max = exposureRange.getUpper(); |
| float progressFactor = progress / (float)mSeekBarMax; |
| long curExposureTime = (long) (min + (max - min) * progressFactor); |
| mCameraControl.getManualControls().setExposure(curExposureTime); |
| // Update the sensitivity info |
| StringBuffer info = new StringBuffer("Exposure Time:"); |
| info.append("" + curExposureTime / 1000000.0 + "ms"); |
| mExposureInfoView.setText(info); |
| mCameraOps.updatePreview(mCameraControl); |
| } |
| |
| @Override |
| public void onStartTrackingTouch(SeekBar seekBar) { |
| } |
| |
| @Override |
| public void onStopTrackingTouch(SeekBar seekBar) { |
| } |
| }; |
| |
| private final OnSeekBarChangeListener mFrameDurationSeekBarListener = |
| new OnSeekBarChangeListener() { |
| |
| @Override |
| public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { |
| CameraCharacteristics properties = mCameraOps.getCameraCharacteristics(); |
| Long frameDurationMax = properties.get( |
| CameraCharacteristics.SENSOR_INFO_MAX_FRAME_DURATION); |
| if (frameDurationMax == null || frameDurationMax <= 0) { |
| frameDurationMax = MAX_FRAME_DURATION; |
| Log.e(TAG, "max frame duration is invalid, set to " + frameDurationMax); |
| } |
| // Need calculate from different resolution, hard code to 10ms for now. |
| long min = 10000000L; |
| long max = frameDurationMax; |
| float progressFactor = progress / (float)mSeekBarMax; |
| long curFrameDuration = (long) (min + (max - min) * progressFactor); |
| mCameraControl.getManualControls().setFrameDuration(curFrameDuration); |
| // Update the sensitivity info |
| StringBuffer info = new StringBuffer("Frame Duration:"); |
| info.append("" + curFrameDuration / 1000000.0 + "ms"); |
| mFrameDurationInfoView.setText(info); |
| mCameraOps.updatePreview(mCameraControl); |
| } |
| |
| @Override |
| public void onStartTrackingTouch(SeekBar seekBar) { |
| } |
| |
| @Override |
| public void onStopTrackingTouch(SeekBar seekBar) { |
| } |
| }; |
| |
| private final View.OnClickListener mControlToggleListener = |
| new View.OnClickListener() { |
| @Override |
| public void onClick(View v) { |
| boolean enableManual; |
| if (mManualCtrlToggle.isChecked()) { |
| enableManual = true; |
| } else { |
| enableManual = false; |
| } |
| mCameraControl.getManualControls().setManualControlEnabled(enableManual); |
| enableManualControls(enableManual); |
| mCameraOps.updatePreview(mCameraControl); |
| } |
| }; |
| |
| private final View.OnClickListener mRecordingToggleListener = |
| new View.OnClickListener() { |
| @Override |
| public void onClick(View v) { |
| if (mRecordingToggle.isChecked()) { |
| try { |
| Log.i(TAG, "start recording, useMediaCodec = " + mUseMediaCodec); |
| RadioGroup fmt = getRadioFmt(); |
| fmt.setActivated(false); |
| mCameraOps.startRecording( |
| /* applicationContext */ TestingCamera2.this, |
| /* useMediaCodec */ mUseMediaCodec, |
| /* outputFormat */ getOutputFormat()); |
| } catch (ApiFailureException e) { |
| logException("Failed to start recording", e); |
| } |
| } else { |
| try { |
| mCameraOps.stopRecording(TestingCamera2.this); |
| getRadioFmt().setActivated(true); |
| } catch (ApiFailureException e) { |
| logException("Failed to stop recording", e); |
| } |
| } |
| } |
| }; |
| |
| private final View.OnClickListener mFocusLockToggleListener = |
| new View.OnClickListener() { |
| @Override |
| public void onClick(View v) { |
| if (VERBOSE) { |
| Log.v(TAG, "focus_lock#onClick - start"); |
| } |
| |
| CameraAutoFocusControls afControls = mCameraControl.getAfControls(); |
| |
| if (mFocusLockToggle.isChecked()) { |
| Log.i(TAG, "lock focus"); |
| |
| afControls.setPendingTriggerStart(); |
| } else { |
| Log.i(TAG, "unlock focus"); |
| |
| afControls.setPendingTriggerCancel(); |
| } |
| |
| mCameraOps.updatePreview(mCameraControl); |
| |
| if (VERBOSE) { |
| Log.v(TAG, "focus_lock#onClick - end"); |
| } |
| } |
| }; |
| |
| private final CompoundButton.OnCheckedChangeListener mUseMediaCodecListener = |
| new CompoundButton.OnCheckedChangeListener() { |
| @Override |
| public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { |
| mUseMediaCodec = isChecked; |
| } |
| }; |
| |
| private final OnItemSelectedListener mCameraIdSelectedListener = |
| new OnItemSelectedListener() { |
| @Override |
| public void onItemSelected(AdapterView<?> parent, View view, int position, |
| long id) { |
| String cameraId = mAvailableCameraIds[position]; |
| |
| if (cameraId != mCameraId) { |
| Log.i(TAG, "Switch to camera " + cameraId); |
| try { |
| mCameraOps.minimalPreviewConfig(cameraId, mPreviewView.getHolder(), |
| mPreviewView2.getHolder()); |
| |
| if (mCurrentPreviewHolder != null && mCurrentPreviewHolder2 != null) { |
| mCameraOps.minimalPreview(mCurrentPreviewHolder, |
| mCurrentPreviewHolder2, mCameraControl); |
| } |
| } catch (ApiFailureException e) { |
| logException("Can't configure preview surface: ", e); |
| } |
| mCameraId = cameraId; |
| } |
| } |
| |
| @Override |
| public void onNothingSelected(AdapterView<?> parent) { |
| // Do nothing |
| } |
| }; |
| |
| private final CameraOps.Listener mCameraOpsListener = new CameraOps.Listener() { |
| @Override |
| public void onCameraOpened(String cameraId, CameraCharacteristics characteristics) { |
| /* |
| * Populate dynamic per-camera settings |
| */ |
| |
| // Map available AF Modes -> AF mode spinner dropdown list of strings |
| int[] availableAfModes = |
| characteristics.get(CameraCharacteristics.CONTROL_AF_AVAILABLE_MODES); |
| |
| String[] allAfModes = getResources().getStringArray(R.array.focus_mode_spinner_arrays); |
| |
| final List<String> afModeList = new ArrayList<>(); |
| final int[] afModePositions = new int[availableAfModes.length]; |
| |
| int i = 0; |
| for (int mode : availableAfModes) { |
| afModeList.add(allAfModes[mode]); |
| afModePositions[i++] = mode; |
| } |
| |
| ArrayAdapter<String> dataAdapter = new ArrayAdapter<>(TestingCamera2.this, |
| android.R.layout.simple_spinner_item, afModeList); |
| dataAdapter.setDropDownViewResource( |
| android.R.layout.simple_spinner_dropdown_item); |
| mFocusModeSpinner.setAdapter(dataAdapter); |
| |
| /* |
| * Change the AF mode and update preview when AF spinner's selected item changes |
| */ |
| mFocusModeSpinner.setOnItemSelectedListener(new OnItemSelectedListener() { |
| |
| @Override |
| public void onItemSelected(AdapterView<?> parent, View view, int position, |
| long id) { |
| int afMode = afModePositions[position]; |
| |
| Log.i(TAG, "Change auto-focus mode to " + afModeList.get(position) |
| + " " + afMode); |
| |
| mCameraControl.getAfControls().setAfMode(afMode); |
| |
| mCameraOps.updatePreview(mCameraControl); |
| } |
| |
| @Override |
| public void onNothingSelected(AdapterView<?> parent) { |
| // Do nothing |
| } |
| }); |
| } |
| }; |
| } |