Skip to content

Instantly share code, notes, and snippets.

@aalices
Created October 13, 2017 13:45
Show Gist options
  • Save aalices/1f88844762150b36aa4b3790fdc56b91 to your computer and use it in GitHub Desktop.
Save aalices/1f88844762150b36aa4b3790fdc56b91 to your computer and use it in GitHub Desktop.
CameraScanner
package com.google.android.cameraview;
import android.annotation.TargetApi;
import android.content.Context;
import android.graphics.ImageFormat;
import android.graphics.Rect;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CameraManager;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.CaptureResult;
import android.hardware.camera2.TotalCaptureResult;
import android.hardware.camera2.params.StreamConfigurationMap;
import android.media.CamcorderProfile;
import android.media.Image;
import android.media.ImageReader;
import android.media.MediaRecorder;
import android.support.annotation.NonNull;
import android.util.Log;
import android.util.SparseIntArray;
import android.view.Surface;
import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.Set;
import java.util.SortedSet;
@SuppressWarnings("MissingPermission")
@TargetApi(21)
class Camera2 extends CameraViewImpl implements MediaRecorder.OnInfoListener, MediaRecorder.OnErrorListener {
private static final String TAG = "Camera2";
private static final SparseIntArray INTERNAL_FACINGS = new SparseIntArray();
static {
INTERNAL_FACINGS.put(Constants.FACING_BACK, CameraCharacteristics.LENS_FACING_BACK);
INTERNAL_FACINGS.put(Constants.FACING_FRONT, CameraCharacteristics.LENS_FACING_FRONT);
}
/**
* Max preview width that is guaranteed by Camera2 API
*/
private static final int MAX_PREVIEW_WIDTH = 1920;
/**
* Max preview height that is guaranteed by Camera2 API
*/
private static final int MAX_PREVIEW_HEIGHT = 1080;
private final CameraManager mCameraManager;
private final CameraDevice.StateCallback mCameraDeviceCallback
= new CameraDevice.StateCallback() {
@Override
public void onOpened(@NonNull CameraDevice camera) {
mCamera = camera;
mCallback.onCameraOpened();
startCaptureSession();
}
@Override
public void onClosed(@NonNull CameraDevice camera) {
mCallback.onCameraClosed();
}
@Override
public void onDisconnected(@NonNull CameraDevice camera) {
mCamera = null;
}
@Override
public void onError(@NonNull CameraDevice camera, int error) {
Log.e(TAG, "onError: " + camera.getId() + " (" + error + ")");
mCamera = null;
}
};
private final CameraCaptureSession.StateCallback mSessionCallback
= new CameraCaptureSession.StateCallback() {
@Override
public void onConfigured(@NonNull CameraCaptureSession session) {
if (mCamera == null) {
return;
}
mCaptureSession = session;
try {
mCaptureSession.setRepeatingRequest(mPreviewRequestBuilder.build(),
mCaptureCallback, null);
} catch (CameraAccessException e) {
Log.e(TAG, "Failed to start camera preview because it couldn't access camera", e);
} catch (IllegalStateException e) {
Log.e(TAG, "Failed to start camera preview.", e);
}
}
@Override
public void onConfigureFailed(@NonNull CameraCaptureSession session) {
Log.e(TAG, "Failed to configure capture session.");
}
@Override
public void onClosed(@NonNull CameraCaptureSession session) {
if (mCaptureSession != null && mCaptureSession.equals(session)) {
mCaptureSession = null;
}
}
};
PictureCaptureCallback mCaptureCallback = new PictureCaptureCallback() {
@Override
public void onPrecaptureRequired() {
mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER,
CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_START);
setState(STATE_PRECAPTURE);
try {
mCaptureSession.capture(mPreviewRequestBuilder.build(), this, null);
mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER,
CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_IDLE);
} catch (CameraAccessException e) {
Log.e(TAG, "Failed to run precapture sequence.", e);
}
}
@Override
public void onReady() {
captureStillPicture();
}
};
private final ImageReader.OnImageAvailableListener mOnImageAvailableListener
= new ImageReader.OnImageAvailableListener() {
@Override
public void onImageAvailable(ImageReader reader) {
try (Image image = reader.acquireNextImage()) {
Image.Plane[] planes = image.getPlanes();
if (planes.length > 0) {
ByteBuffer buffer = planes[0].getBuffer();
byte[] data = new byte[buffer.remaining()];
buffer.get(data);
if (image.getFormat() == ImageFormat.JPEG) {
// some callback
} else {
// some callback
}
image.close();
}
}
}
};
private String mCameraId;
private CameraCharacteristics mCameraCharacteristics;
CameraDevice mCamera;
CameraCaptureSession mCaptureSession;
CaptureRequest.Builder mPreviewRequestBuilder;
private ImageReader mStillImageReader;
private ImageReader mScanImageReader;
private int mImageFormat;
private MediaRecorder mMediaRecorder;
private String mVideoPath;
private boolean mIsRecording;
private final SizeMap mPreviewSizes = new SizeMap();
private final SizeMap mPictureSizes = new SizeMap();
private int mFacing;
private AspectRatio mAspectRatio = Constants.DEFAULT_ASPECT_RATIO;
private AspectRatio mInitialRatio;
private boolean mAutoFocus;
private int mFlash;
private int mDisplayOrientation;
Camera2(Callback callback, PreviewImpl preview, Context context) {
super(callback, preview);
mCameraManager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE);
mImageFormat = mIsScanning ? ImageFormat.YUV_420_888 : ImageFormat.JPEG;
mPreview.setCallback(new PreviewImpl.Callback() {
@Override
public void onSurfaceChanged() {
startCaptureSession();
}
@Override
public void onSurfaceDestroyed() {
stop();
}
});
}
@Override
boolean start() {
if (!chooseCameraIdByFacing()) {
mAspectRatio = mInitialRatio;
return false;
}
collectCameraInfo();
setAspectRatio(mInitialRatio);
mInitialRatio = null;
mStillImageReader = prepareImageReader(mStillImageReader, ImageFormat.JPEG);
mScanImageReader = prepareImageReader(mScanImageReader, ImageFormat.YUV_420_888);
startOpeningCamera();
return true;
}
@Override
void stop() {
if (mCaptureSession != null) {
mCaptureSession.close();
mCaptureSession = null;
}
if (mCamera != null) {
mCamera.close();
mCamera = null;
}
if (mStillImageReader != null) {
mStillImageReader.close();
mStillImageReader = null;
}
if (mScanImageReader != null) {
mScanImageReader.close();
mScanImageReader = null;
}
}
@Override
boolean isCameraOpened() {
return mCamera != null;
}
@Override
void setFacing(int facing) {
if (mFacing == facing) {
return;
}
mFacing = facing;
if (isCameraOpened()) {
stop();
start();
}
}
@Override
int getFacing() {
return mFacing;
}
@Override
Set<AspectRatio> getSupportedAspectRatios() {
return mPreviewSizes.ratios();
}
@Override
boolean setAspectRatio(AspectRatio ratio) {
if (ratio != null && mPreviewSizes.isEmpty()) {
mInitialRatio = ratio;
return false;
}
if (ratio == null || ratio.equals(mAspectRatio) ||
!mPreviewSizes.ratios().contains(ratio)) {
// TODO: Better error handling
return false;
}
mAspectRatio = ratio;
prepareImageReader(mStillImageReader, ImageFormat.JPEG);
prepareImageReader(mScanImageReader, ImageFormat.YUV_420_888);
if (mCaptureSession != null) {
mCaptureSession.close();
mCaptureSession = null;
startCaptureSession();
}
return true;
}
@Override
AspectRatio getAspectRatio() {
return mAspectRatio;
}
@Override
void setAutoFocus(boolean autoFocus) {
if (mAutoFocus == autoFocus) {
return;
}
mAutoFocus = autoFocus;
if (mPreviewRequestBuilder != null) {
updateAutoFocus();
if (mCaptureSession != null) {
try {
mCaptureSession.setRepeatingRequest(mPreviewRequestBuilder.build(),
mCaptureCallback, null);
} catch (CameraAccessException e) {
mAutoFocus = !mAutoFocus; // Revert
}
}
}
}
@Override
boolean getAutoFocus() {
return mAutoFocus;
}
@Override
void setFlash(int flash) {
if (mFlash == flash) {
return;
}
int saved = mFlash;
mFlash = flash;
if (mPreviewRequestBuilder != null) {
updateFlash();
if (mCaptureSession != null) {
try {
mCaptureSession.setRepeatingRequest(mPreviewRequestBuilder.build(),
mCaptureCallback, null);
} catch (CameraAccessException e) {
mFlash = saved; // Revert
}
}
}
}
@Override
int getFlash() {
return mFlash;
}
@Override
void takePicture() {
if (mAutoFocus) {
lockFocus();
} else {
captureStillPicture();
}
}
@Override
void setScanning(boolean isScanning) {
if (mIsScanning == isScanning) {
return;
}
mIsScanning = isScanning;
if (!mIsScanning) {
mImageFormat = ImageFormat.JPEG;
} else {
mImageFormat = ImageFormat.YUV_420_888;
}
if (mCaptureSession != null) {
mCaptureSession.close();
mCaptureSession = null;
}
startCaptureSession();
}
@Override
boolean getScanning() {
return mIsScanning;
}
@Override
void setDisplayOrientation(int displayOrientation) {
mDisplayOrientation = displayOrientation;
mPreview.setDisplayOrientation(mDisplayOrientation);
}
/**
* <p>Chooses a camera ID by the specified camera facing ({@link #mFacing}).</p>
* <p>This rewrites {@link #mCameraId}, {@link #mCameraCharacteristics}, and optionally
* {@link #mFacing}.</p>
*/
private boolean chooseCameraIdByFacing() {
try {
int internalFacing = INTERNAL_FACINGS.get(mFacing);
final String[] ids = mCameraManager.getCameraIdList();
if (ids.length == 0) { // No camera
throw new RuntimeException("No camera available.");
}
for (String id : ids) {
CameraCharacteristics characteristics = mCameraManager.getCameraCharacteristics(id);
Integer level = characteristics.get(
CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);
if (level == null ||
level == CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY) {
continue;
}
Integer internal = characteristics.get(CameraCharacteristics.LENS_FACING);
if (internal == null) {
throw new NullPointerException("Unexpected state: LENS_FACING null");
}
if (internal == internalFacing) {
mCameraId = id;
mCameraCharacteristics = characteristics;
return true;
}
}
// Not found
mCameraId = ids[0];
mCameraCharacteristics = mCameraManager.getCameraCharacteristics(mCameraId);
Integer level = mCameraCharacteristics.get(
CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);
if (level == null ||
level == CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY) {
return false;
}
Integer internal = mCameraCharacteristics.get(CameraCharacteristics.LENS_FACING);
if (internal == null) {
throw new NullPointerException("Unexpected state: LENS_FACING null");
}
for (int i = 0, count = INTERNAL_FACINGS.size(); i < count; i++) {
if (INTERNAL_FACINGS.valueAt(i) == internal) {
mFacing = INTERNAL_FACINGS.keyAt(i);
return true;
}
}
// The operation can reach here when the only camera device is an external one.
// We treat it as facing back.
mFacing = Constants.FACING_BACK;
return true;
} catch (CameraAccessException e) {
throw new RuntimeException("Failed to get a list of camera devices", e);
}
}
/**
* <p>Collects some information from {@link #mCameraCharacteristics}.</p>
* <p>This rewrites {@link #mPreviewSizes}, {@link #mPictureSizes}, and optionally,
* {@link #mAspectRatio}.</p>
*/
private void collectCameraInfo() {
StreamConfigurationMap map = mCameraCharacteristics.get(
CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
if (map == null) {
throw new IllegalStateException("Failed to get configuration map: " + mCameraId);
}
mPreviewSizes.clear();
for (android.util.Size size : map.getOutputSizes(mPreview.getOutputClass())) {
int width = size.getWidth();
int height = size.getHeight();
if (width <= MAX_PREVIEW_WIDTH && height <= MAX_PREVIEW_HEIGHT) {
mPreviewSizes.add(new Size(width, height));
}
}
mPictureSizes.clear();
collectPictureSizes(mPictureSizes, map);
for (AspectRatio ratio : mPreviewSizes.ratios()) {
if (!mPictureSizes.ratios().contains(ratio)) {
mPreviewSizes.remove(ratio);
}
}
if (!mPreviewSizes.ratios().contains(mAspectRatio)) {
mAspectRatio = mPreviewSizes.ratios().iterator().next();
}
}
protected void collectPictureSizes(SizeMap sizes, StreamConfigurationMap map) {
for (android.util.Size size : map.getOutputSizes(mImageFormat)) {
mPictureSizes.add(new Size(size.getWidth(), size.getHeight()));
}
}
private ImageReader prepareImageReader(ImageReader reader, int imageFormat) {
if (reader != null) {
reader.close();
}
Size largest = mPictureSizes.sizes(mAspectRatio).last();
reader = ImageReader.newInstance(largest.getWidth(), largest.getHeight(),
imageFormat, 1);
reader.setOnImageAvailableListener(mOnImageAvailableListener, null);
return reader;
}
/**
* <p>Starts opening a camera device.</p>
* <p>The result will be processed in {@link #mCameraDeviceCallback}.</p>
*/
private void startOpeningCamera() {
try {
mCameraManager.openCamera(mCameraId, mCameraDeviceCallback, null);
} catch (CameraAccessException e) {
throw new RuntimeException("Failed to open camera: " + mCameraId, e);
}
}
/**
* <p>Starts a capture session for camera preview.</p>
* <p>This rewrites {@link #mPreviewRequestBuilder}.</p>
* <p>The result will be continuously processed in {@link #mSessionCallback}.</p>
*/
void startCaptureSession() {
if (!isCameraOpened() || !mPreview.isReady() || mStillImageReader == null || mScanImageReader == null) {
return;
}
Size previewSize = chooseOptimalSize();
mPreview.setBufferSize(previewSize.getWidth(), previewSize.getHeight());
Surface surface = mPreview.getSurface();
try {
mPreviewRequestBuilder = mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
mPreviewRequestBuilder.addTarget(surface);
if (mIsScanning) {
mPreviewRequestBuilder.addTarget(mScanImageReader.getSurface());
}
mCamera.createCaptureSession(Arrays.asList(surface, mStillImageReader.getSurface(),
mScanImageReader.getSurface()), mSessionCallback, null);
} catch (CameraAccessException e) {
throw new RuntimeException("Failed to start camera session");
}
}
/**
* Chooses the optimal preview size based on {@link #mPreviewSizes} and the surface size.
*
* @return The picked size for camera preview.
*/
private Size chooseOptimalSize() {
int surfaceLonger, surfaceShorter;
final int surfaceWidth = mPreview.getWidth();
final int surfaceHeight = mPreview.getHeight();
if (surfaceWidth < surfaceHeight) {
surfaceLonger = surfaceHeight;
surfaceShorter = surfaceWidth;
} else {
surfaceLonger = surfaceWidth;
surfaceShorter = surfaceHeight;
}
SortedSet<Size> candidates = mPreviewSizes.sizes(mAspectRatio);
// Pick the smallest of those big enough
for (Size size : candidates) {
if (size.getWidth() >= surfaceLonger && size.getHeight() >= surfaceShorter) {
return size;
}
}
// If no size is big enough, pick the largest one.
return candidates.last();
}
/**
* Updates the internal state of auto-focus to {@link #mAutoFocus}.
*/
void updateAutoFocus() {
if (mAutoFocus) {
int[] modes = mCameraCharacteristics.get(
CameraCharacteristics.CONTROL_AF_AVAILABLE_MODES);
// Auto focus is not supported
if (modes == null || modes.length == 0 ||
(modes.length == 1 && modes[0] == CameraCharacteristics.CONTROL_AF_MODE_OFF)) {
mAutoFocus = false;
mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE,
CaptureRequest.CONTROL_AF_MODE_OFF);
} else {
mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE,
CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
}
} else {
mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE,
CaptureRequest.CONTROL_AF_MODE_OFF);
}
}
/**
* Updates the internal state of flash to {@link #mFlash}.
*/
void updateFlash() {
switch (mFlash) {
case Constants.FLASH_OFF:
mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE,
CaptureRequest.CONTROL_AE_MODE_ON);
mPreviewRequestBuilder.set(CaptureRequest.FLASH_MODE,
CaptureRequest.FLASH_MODE_OFF);
break;
case Constants.FLASH_ON:
mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE,
CaptureRequest.CONTROL_AE_MODE_ON_ALWAYS_FLASH);
mPreviewRequestBuilder.set(CaptureRequest.FLASH_MODE,
CaptureRequest.FLASH_MODE_OFF);
break;
case Constants.FLASH_TORCH:
mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE,
CaptureRequest.CONTROL_AE_MODE_ON);
mPreviewRequestBuilder.set(CaptureRequest.FLASH_MODE,
CaptureRequest.FLASH_MODE_TORCH);
break;
case Constants.FLASH_AUTO:
mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE,
CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);
mPreviewRequestBuilder.set(CaptureRequest.FLASH_MODE,
CaptureRequest.FLASH_MODE_OFF);
break;
case Constants.FLASH_RED_EYE:
mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE,
CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH_REDEYE);
mPreviewRequestBuilder.set(CaptureRequest.FLASH_MODE,
CaptureRequest.FLASH_MODE_OFF);
break;
}
}
/**
* Locks the focus as the first step for a still image capture.
*/
private void lockFocus() {
mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER,
CaptureRequest.CONTROL_AF_TRIGGER_START);
try {
mCaptureCallback.setState(PictureCaptureCallback.STATE_LOCKING);
mCaptureSession.capture(mPreviewRequestBuilder.build(), mCaptureCallback, null);
} catch (CameraAccessException e) {
Log.e(TAG, "Failed to lock focus.", e);
}
}
/**
* Captures a still picture.
*/
void captureStillPicture() {
try {
CaptureRequest.Builder captureRequestBuilder = mCamera.createCaptureRequest(
CameraDevice.TEMPLATE_STILL_CAPTURE);
if (mIsScanning) {
mImageFormat = ImageFormat.JPEG;
captureRequestBuilder.removeTarget(mScanImageReader.getSurface());
}
captureRequestBuilder.addTarget(mStillImageReader.getSurface());
captureRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE,
mPreviewRequestBuilder.get(CaptureRequest.CONTROL_AF_MODE));
switch (mFlash) {
case Constants.FLASH_OFF:
captureRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE,
CaptureRequest.CONTROL_AE_MODE_ON);
captureRequestBuilder.set(CaptureRequest.FLASH_MODE,
CaptureRequest.FLASH_MODE_OFF);
break;
case Constants.FLASH_ON:
captureRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE,
CaptureRequest.CONTROL_AE_MODE_ON_ALWAYS_FLASH);
break;
case Constants.FLASH_TORCH:
captureRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE,
CaptureRequest.CONTROL_AE_MODE_ON);
captureRequestBuilder.set(CaptureRequest.FLASH_MODE,
CaptureRequest.FLASH_MODE_TORCH);
break;
case Constants.FLASH_AUTO:
captureRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE,
CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);
break;
case Constants.FLASH_RED_EYE:
captureRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE,
CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);
break;
}
captureRequestBuilder.set(CaptureRequest.JPEG_ORIENTATION, getOutputRotation());
// Stop preview and capture a still picture.
mCaptureSession.stopRepeating();
mCaptureSession.capture(captureRequestBuilder.build(),
new CameraCaptureSession.CaptureCallback() {
@Override
public void onCaptureCompleted(@NonNull CameraCaptureSession session,
@NonNull CaptureRequest request,
@NonNull TotalCaptureResult result) {
unlockFocus();
}
}, null);
} catch (CameraAccessException e) {
Log.e(TAG, "Cannot capture a still picture.", e);
}
}
private int getOutputRotation() {
@SuppressWarnings("ConstantConditions")
int sensorOrientation = mCameraCharacteristics.get(CameraCharacteristics.SENSOR_ORIENTATION);
return (sensorOrientation +
mDisplayOrientation * (mFacing == Constants.FACING_FRONT ? 1 : -1) +
360) % 360;
}
/**
* Unlocks the auto-focus and restart camera preview. This is supposed to be called after
* capturing a still picture.
*/
void unlockFocus() {
mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER,
CaptureRequest.CONTROL_AF_TRIGGER_CANCEL);
try {
mCaptureSession.capture(mPreviewRequestBuilder.build(), mCaptureCallback, null);
updateAutoFocus();
updateFlash();
if (mIsScanning) {
mImageFormat = ImageFormat.YUV_420_888;
startCaptureSession();
} else {
mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER,
CaptureRequest.CONTROL_AF_TRIGGER_IDLE);
mCaptureSession.setRepeatingRequest(mPreviewRequestBuilder.build(), mCaptureCallback,
null);
mCaptureCallback.setState(PictureCaptureCallback.STATE_PREVIEW);
}
} catch (CameraAccessException e) {
Log.e(TAG, "Failed to restart camera preview.", e);
}
}
/**
* A {@link CameraCaptureSession.CaptureCallback} for capturing a still picture.
*/
private static abstract class PictureCaptureCallback
extends CameraCaptureSession.CaptureCallback {
static final int STATE_PREVIEW = 0;
static final int STATE_LOCKING = 1;
static final int STATE_LOCKED = 2;
static final int STATE_PRECAPTURE = 3;
static final int STATE_WAITING = 4;
static final int STATE_CAPTURING = 5;
private int mState;
PictureCaptureCallback() {
}
void setState(int state) {
mState = state;
}
@Override
public void onCaptureProgressed(@NonNull CameraCaptureSession session,
@NonNull CaptureRequest request, @NonNull CaptureResult partialResult) {
process(partialResult);
}
@Override
public void onCaptureCompleted(@NonNull CameraCaptureSession session,
@NonNull CaptureRequest request, @NonNull TotalCaptureResult result) {
process(result);
}
private void process(@NonNull CaptureResult result) {
switch (mState) {
case STATE_LOCKING: {
Integer af = result.get(CaptureResult.CONTROL_AF_STATE);
if (af == null) {
break;
}
if (af == CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED ||
af == CaptureResult.CONTROL_AF_STATE_NOT_FOCUSED_LOCKED) {
Integer ae = result.get(CaptureResult.CONTROL_AE_STATE);
if (ae == null || ae == CaptureResult.CONTROL_AE_STATE_CONVERGED) {
setState(STATE_CAPTURING);
onReady();
} else {
setState(STATE_LOCKED);
onPrecaptureRequired();
}
}
break;
}
case STATE_PRECAPTURE: {
Integer ae = result.get(CaptureResult.CONTROL_AE_STATE);
if (ae == null || ae == CaptureResult.CONTROL_AE_STATE_PRECAPTURE ||
ae == CaptureRequest.CONTROL_AE_STATE_FLASH_REQUIRED ||
ae == CaptureResult.CONTROL_AE_STATE_CONVERGED) {
setState(STATE_WAITING);
}
break;
}
case STATE_WAITING: {
Integer ae = result.get(CaptureResult.CONTROL_AE_STATE);
if (ae == null || ae != CaptureResult.CONTROL_AE_STATE_PRECAPTURE) {
setState(STATE_CAPTURING);
onReady();
}
break;
}
}
}
/**
* Called when it is ready to take a still picture.
*/
public abstract void onReady();
/**
* Called when it is necessary to run the precapture sequence.
*/
public abstract void onPrecaptureRequired();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment