From c118ad016b11aa8ac340a533ca547562075f9240 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Mon, 1 May 2017 16:53:59 +0300 Subject: [PATCH] Make things safer 1. Fallback for requested camera 2. Not crashing when taking pictures at the wrong time 3. Callback for state changes - to be able to take actions only when actually ready 4. Query for modes, like is flash available? --- .../barcodereader/ui/camera/CameraSource.java | 132 +++++++++++++++--- 1 file changed, 115 insertions(+), 17 deletions(-) diff --git a/visionSamples/barcode-reader/app/src/main/java/com/google/android/gms/samples/vision/barcodereader/ui/camera/CameraSource.java b/visionSamples/barcode-reader/app/src/main/java/com/google/android/gms/samples/vision/barcodereader/ui/camera/CameraSource.java index 430fd746..3170013b 100644 --- a/visionSamples/barcode-reader/app/src/main/java/com/google/android/gms/samples/vision/barcodereader/ui/camera/CameraSource.java +++ b/visionSamples/barcode-reader/app/src/main/java/com/google/android/gms/samples/vision/barcodereader/ui/camera/CameraSource.java @@ -91,6 +91,14 @@ public class CameraSource { */ private static final float ASPECT_RATIO_TOLERANCE = 0.01f; + public CameraSourceStateListener getStateListener() { + return mStateListener; + } + + public void setStateListener(CameraSourceStateListener stateListener) { + this.mStateListener = stateListener; + } + @StringDef({ Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE, Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO, @@ -122,11 +130,16 @@ public class CameraSource { private int mFacing = CAMERA_FACING_BACK; + private boolean mCameraFallbackAllowed = true; + + private CameraSourceStateListener mStateListener = null; + /** * Rotation of the device, and thus the associated preview images captured from the device. * See {@link Frame.Metadata#getRotation()}. */ private int mRotation; + private int mRequestedCameraId; private Size mPreviewSize; @@ -143,7 +156,9 @@ public class CameraSource { // These instances need to be held onto to avoid GC of their underlying resources. Even though // these aren't used outside of the method that creates them, they still must have hard // references maintained to them. + @SuppressWarnings("FieldCanBeLocal") private SurfaceView mDummySurfaceView; + @SuppressWarnings("FieldCanBeLocal") private SurfaceTexture mDummySurfaceTexture; /** @@ -153,6 +168,8 @@ public class CameraSource { private Thread mProcessingThread; private FrameProcessingRunnable mFrameProcessor; + private boolean mCanTakePicture = false; + /** * Map to convert between a byte array, received from the camera, and its associated byte * buffer. We use byte buffers internally because this is a more efficient way to call into @@ -240,6 +257,16 @@ public Builder setFacing(int facing) { return this; } + /** + * Sets whether fallback from front to back or vice versa is allowed. + * Used in case the requested camera was not available. + * Default: true. + */ + public Builder setCameraFallbackAllowed(boolean allowed) { + mCameraSource.mCameraFallbackAllowed = allowed; + return this; + } + /** * Creates an instance of the camera source. */ @@ -356,6 +383,11 @@ public CameraSource start() throws IOException { mFrameProcessor.setActive(true); mProcessingThread.start(); } + + if (mStateListener != null) { + mStateListener.onCameraSourceStarted(); + } + return this; } @@ -377,10 +409,17 @@ public CameraSource start(SurfaceHolder surfaceHolder) throws IOException { mCamera.setPreviewDisplay(surfaceHolder); mCamera.startPreview(); + mCanTakePicture = true; + mProcessingThread = new Thread(mFrameProcessor); mFrameProcessor.setActive(true); mProcessingThread.start(); } + + if (mStateListener != null) { + mStateListener.onCameraSourceStarted(); + } + return this; } @@ -411,6 +450,8 @@ public void stop() { // clear the buffer to prevent oom exceptions mBytesToByteBuffer.clear(); + mCanTakePicture = false; + if (mCamera != null) { mCamera.stopPreview(); mCamera.setPreviewCallbackWithBuffer(null); @@ -433,6 +474,10 @@ public void stop() { mCamera = null; } } + + if (mStateListener != null) { + mStateListener.onCameraSourceStopped(); + } } /** @@ -450,6 +495,22 @@ public int getCameraFacing() { return mFacing; } + /** + * Sets whether fallback from front to back or vice versa is allowed. + * Used in case the requested camera was not available. + */ + public boolean isCameraFallbackAllowed() { + return mCameraFallbackAllowed; + } + + public boolean isCameraFacingBackAvailable() { + return getIdForRequestedCamera(CAMERA_FACING_BACK) != -1; + } + + public boolean isCameraFacingFrontAvailable() { + return getIdForRequestedCamera(CAMERA_FACING_FRONT) != -1; + } + public int doZoom(float scale) { synchronized (mCameraLock) { if (mCamera == null) { @@ -494,7 +555,10 @@ public int doZoom(float scale) { */ public void takePicture(ShutterCallback shutter, PictureCallback jpeg) { synchronized (mCameraLock) { - if (mCamera != null) { + if (mCamera != null && mCanTakePicture) { + + mCanTakePicture = false; // Preview is suspended until we're done + PictureStartCallback startCallback = new PictureStartCallback(); startCallback.mDelegate = shutter; PictureDoneCallback doneCallback = new PictureDoneCallback(); @@ -535,7 +599,8 @@ public boolean setFocusMode(@FocusMode String mode) { synchronized (mCameraLock) { if (mCamera != null && mode != null) { Camera.Parameters parameters = mCamera.getParameters(); - if (parameters.getSupportedFocusModes().contains(mode)) { + final List supportedFocusModes = parameters.getSupportedFlashModes(); + if (supportedFocusModes != null && supportedFocusModes.contains(mode)) { parameters.setFocusMode(mode); mCamera.setParameters(parameters); mFocusMode = mode; @@ -575,7 +640,8 @@ public boolean setFlashMode(@FlashMode String mode) { synchronized (mCameraLock) { if (mCamera != null && mode != null) { Camera.Parameters parameters = mCamera.getParameters(); - if (parameters.getSupportedFlashModes().contains(mode)) { + final List supportedFlashModes = parameters.getSupportedFlashModes(); + if (supportedFlashModes != null && supportedFlashModes.contains(mode)) { parameters.setFlashMode(mode); mCamera.setParameters(parameters); mFlashMode = mode; @@ -587,6 +653,15 @@ public boolean setFlashMode(@FlashMode String mode) { } } + public boolean isModeSupported(String mode) { + if (mCamera != null) { + Camera.Parameters parameters = mCamera.getParameters(); + final List supportedModes = parameters.getSupportedFlashModes(); + return supportedModes != null && supportedModes.contains(mode); + } + return false; + } + /** * Starts camera auto-focus and registers a callback function to run when * the camera is focused. This method is only valid when preview is active @@ -600,7 +675,7 @@ public boolean setFlashMode(@FlashMode String mode) { *

*

If the current flash mode is not * {@link Camera.Parameters#FLASH_MODE_OFF}, flash may be - * fired during auto-focus, depending on the driver and camera hardware.

+ * fired during auto-focus, depending on the driver and camera hardware.

* * @param cb the callback to run * @see #cancelAutoFocus() @@ -699,6 +774,7 @@ public void onPictureTaken(byte[] data, Camera camera) { synchronized (mCameraLock) { if (mCamera != null) { mCamera.startPreview(); + mCanTakePicture = true; } } } @@ -740,11 +816,23 @@ public void onAutoFocusMoving(boolean start, Camera camera) { */ @SuppressLint("InlinedApi") private Camera createCamera() { - int requestedCameraId = getIdForRequestedCamera(mFacing); - if (requestedCameraId == -1) { + mRequestedCameraId = getIdForRequestedCamera(mFacing); + + if (mRequestedCameraId == -1 && mCameraFallbackAllowed) { + if (mFacing == CAMERA_FACING_BACK) { + mFacing = CAMERA_FACING_FRONT; + } else { + mFacing = CAMERA_FACING_BACK; + } + + mRequestedCameraId = getIdForRequestedCamera(mFacing); + } + + if (mRequestedCameraId == -1) { throw new RuntimeException("Could not find requested camera."); } - Camera camera = Camera.open(requestedCameraId); + + Camera camera = Camera.open(mRequestedCameraId); SizePair sizePair = selectSizePair(camera, mRequestedPreviewWidth, mRequestedPreviewHeight); if (sizePair == null) { @@ -770,11 +858,11 @@ private Camera createCamera() { previewFpsRange[Camera.Parameters.PREVIEW_FPS_MAX_INDEX]); parameters.setPreviewFormat(ImageFormat.NV21); - setRotation(camera, parameters, requestedCameraId); + setRotation(camera, parameters, mRequestedCameraId); if (mFocusMode != null) { - if (parameters.getSupportedFocusModes().contains( - mFocusMode)) { + final List supportedFocusModes = parameters.getSupportedFlashModes(); + if (supportedFocusModes != null && supportedFocusModes.contains(mFocusMode)) { parameters.setFocusMode(mFocusMode); } else { Log.i(TAG, "Camera focus mode: " + mFocusMode + " is not supported on this device."); @@ -785,13 +873,11 @@ private Camera createCamera() { mFocusMode = parameters.getFocusMode(); if (mFlashMode != null) { - if (parameters.getSupportedFlashModes() != null) { - if (parameters.getSupportedFlashModes().contains( - mFlashMode)) { - parameters.setFlashMode(mFlashMode); - } else { - Log.i(TAG, "Camera flash mode: " + mFlashMode + " is not supported on this device."); - } + final List supportedFlashModes = parameters.getSupportedFlashModes(); + if (supportedFlashModes != null && supportedFlashModes.contains(mFlashMode)) { + parameters.setFlashMode(mFlashMode); + } else { + Log.i(TAG, "Camera flash mode: " + mFlashMode + " is not supported on this device."); } } @@ -972,6 +1058,12 @@ private int[] selectPreviewFpsRange(Camera camera, float desiredPreviewFps) { return selectedFpsRange; } + public void updateRotation() { + if (mCamera != null) { + setRotation(mCamera, mCamera.getParameters(), mRequestedCameraId); + } + } + /** * Calculates the correct rotation for the given camera id and sets the rotation in the * parameters. It also sets the camera's display orientation and rotation. @@ -1211,4 +1303,10 @@ public void run() { } } } + + public interface CameraSourceStateListener + { + void onCameraSourceStarted(); + void onCameraSourceStopped(); + } }