diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/Camera1.java b/cameraview/src/main/java/com/otaliastudios/cameraview/Camera1.java index 6721572b..9fda44b4 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/Camera1.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/Camera1.java @@ -14,6 +14,7 @@ import android.os.Build; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.annotation.WorkerThread; +import android.util.Log; import android.view.SurfaceHolder; import java.io.File; @@ -73,15 +74,8 @@ class Camera1 extends CameraController implements Camera.PreviewCallback, Camera schedule(null, false, new Runnable() { @Override public void run() { - if (shouldBindToSurface()) { - LOG.i("onSurfaceAvailable:", "Inside handler. About to bind."); - try { - bindToSurface(); - } catch (Exception e) { - LOG.e("onSurfaceAvailable:", "Exception while binding camera to preview.", e); - throw new CameraException(e); - } - } + LOG.i("onSurfaceAvailable:", "Inside handler. About to bind."); + if (shouldBindToSurface()) bindToSurface(); } }); } @@ -126,7 +120,8 @@ class Camera1 extends CameraController implements Camera.PreviewCallback, Camera mCamera.setPreviewTexture((SurfaceTexture) output); } } catch (IOException e) { - throw new CameraException(e); + Log.e("bindToSurface:", "Failed to bind.", e); + throw new CameraException(e, CameraException.REASON_FAILED_TO_START_PREVIEW); } mPictureSize = computePictureSize(); @@ -157,7 +152,12 @@ class Camera1 extends CameraController implements Camera.PreviewCallback, Camera mFrameManager.allocate(ImageFormat.getBitsPerPixel(mPreviewFormat), mPreviewSize); LOG.i(log, "Starting preview with startPreview()."); - mCamera.startPreview(); + try { + mCamera.startPreview(); + } catch (Exception e) { + LOG.e(log, "Failed to start preview.", e); + throw new CameraException(e, CameraException.REASON_FAILED_TO_START_PREVIEW); + } LOG.i(log, "Started preview."); } @@ -169,7 +169,12 @@ class Camera1 extends CameraController implements Camera.PreviewCallback, Camera onStop(); // Should not happen. } if (collectCameraId()) { - mCamera = Camera.open(mCameraId); + try { + mCamera = Camera.open(mCameraId); + } catch (Exception e) { + LOG.e("onStart:", "Failed to connect. Maybe in use by another app?"); + throw new CameraException(e, CameraException.REASON_FAILED_TO_CONNECT); + } mCamera.setErrorCallback(this); // Set parameters that might have been set before the camera was opened. @@ -196,7 +201,6 @@ class Camera1 extends CameraController implements Camera.PreviewCallback, Camera @WorkerThread @Override void onStop() { - Exception error = null; LOG.i("onStop:", "About to clean up."); mHandler.get().removeCallbacks(mPostFocusResetRunnable); mFrameManager.release(); @@ -212,7 +216,6 @@ class Camera1 extends CameraController implements Camera.PreviewCallback, Camera LOG.i("onStop:", "Clean up.", "Stopped preview."); } catch (Exception e) { LOG.w("onStop:", "Clean up.", "Exception while stopping preview.", e); - error = e; } try { @@ -221,7 +224,6 @@ class Camera1 extends CameraController implements Camera.PreviewCallback, Camera LOG.i("onStop:", "Clean up.", "Released camera."); } catch (Exception e) { LOG.w("onStop:", "Clean up.", "Exception while releasing camera.", e); - error = e; } } mExtraProperties = null; @@ -233,7 +235,11 @@ class Camera1 extends CameraController implements Camera.PreviewCallback, Camera mIsCapturingImage = false; mIsCapturingVideo = false; LOG.w("onStop:", "Clean up.", "Returning."); - if (error != null) throw new CameraException(error); + + // We were saving a reference to the exception here and throwing to the user. + // I don't think it's correct. We are closing and have already done our best + // to clean up resources. No need to throw. + // if (error != null) throw new CameraException(error); } private boolean collectCameraId() { @@ -269,7 +275,14 @@ class Camera1 extends CameraController implements Camera.PreviewCallback, Camera } LOG.e("Error inside the onError callback.", error); - throw new CameraException(new RuntimeException(CameraLogger.lastMessage)); + Exception runtime = new RuntimeException(CameraLogger.lastMessage); + int reason; + switch (error) { + case Camera.CAMERA_ERROR_EVICTED: reason = CameraException.REASON_DISCONNECTED; break; + case Camera.CAMERA_ERROR_UNKNOWN: reason = CameraException.REASON_UNKNOWN; break; + default: reason = CameraException.REASON_UNKNOWN; + } + throw new CameraException(runtime, reason); } @Override diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/CameraException.java b/cameraview/src/main/java/com/otaliastudios/cameraview/CameraException.java index bdb11b0d..9b768d8e 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/CameraException.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/CameraException.java @@ -6,7 +6,42 @@ package com.otaliastudios.cameraview; */ public class CameraException extends RuntimeException { + /** + * Unknown error. No further info available. + */ + public static final int REASON_UNKNOWN = 0; + + /** + * We failed to connect to the camera service. + * The camera might be in use by another app. + */ + public static final int REASON_FAILED_TO_CONNECT = 1; + + /** + * Failed to start the camera preview. + * Again, the camera might be in use by another app. + */ + public static final int REASON_FAILED_TO_START_PREVIEW = 2; + + /** + * Camera was forced to disconnect. + * In Camera1, this is thrown when {@link android.hardware.Camera.CAMERA_ERROR_EVICTED} + * is caught. + */ + public static final int REASON_DISCONNECTED = 3; + + private int reason = REASON_UNKNOWN; + CameraException(Throwable cause) { super(cause); } + + CameraException(Throwable cause, int reason) { + super(cause); + this.reason = reason; + } + + public int getReason() { + return reason; + } }