pull/59/merge
Johnson145 8 years ago committed by GitHub
commit 5781857de7
  1. 72
      README.md
  2. 143
      cameraview/src/main/java/com/otaliastudios/cameraview/Camera1.java
  3. 16
      cameraview/src/main/java/com/otaliastudios/cameraview/CameraConfigurationFailedException.java
  4. 16
      cameraview/src/main/java/com/otaliastudios/cameraview/CameraController.java
  5. 15
      cameraview/src/main/java/com/otaliastudios/cameraview/CameraException.java
  6. 13
      cameraview/src/main/java/com/otaliastudios/cameraview/CameraListener.java
  7. 17
      cameraview/src/main/java/com/otaliastudios/cameraview/CameraUnavailableException.java
  8. 62
      cameraview/src/main/java/com/otaliastudios/cameraview/CameraView.java
  9. 15
      cameraview/src/main/java/com/otaliastudios/cameraview/CapturingFailedException.java
  10. 17
      cameraview/src/main/java/com/otaliastudios/cameraview/CapturingImageFailedException.java
  11. 17
      cameraview/src/main/java/com/otaliastudios/cameraview/CapturingPictureFailedException.java
  12. 17
      cameraview/src/main/java/com/otaliastudios/cameraview/CapturingSnapshotFailedException.java
  13. 16
      cameraview/src/main/java/com/otaliastudios/cameraview/CapturingVideoFailedException.java

@ -148,6 +148,74 @@ camera.postDelayed(new Runnable() {
``` ```
### Error Handling
#### Default Handler
The default implementation will just throw all exceptions to prevent missing error handling.
#### Basic Custom Handler
You can implement custom error handling by overriding the ``onError`` listener.
As soon as this method was overridden at least once (without calling the super method or re-throwing the exception),
the default behavior will be disabled. So pay attention to not swallowing any exceptions.
```java
camera.addCameraListener(new CameraListener() {
@Override
public void onError(CameraException exception) {
throw exception;
}
});
```
#### Advanced Custom Handler
Furthermore, you can distinguish different error types. E.g. some of them imply that the
CameraView should be disabled or restarted while others may be ignored in some use cases.
```java
camera.addCameraListener(new CameraListener() {
@Override
public void onError(CameraException exception) {
if (exception instanceof CameraUnavailableException) {
// the exception prevents the camera from being used. The cause may be
// temporary or permanent. You should restart the camera or deactivate any
// user interaction with the camera.
}
else if (exception instanceof CameraConfigurationFailedException) {
// The previously started setting change failed,
// but the camera should be still available.
}
else if (exception instanceof CapturingFailedException) {
// The previously started capturing (of any kind) failed, but the camera
// should be still available.
if (exception instanceof CapturingImageFailedException) {
// the previously started image capturing failed (snapshot or
// "real picture"), but the camera should be still available.
if (exception instanceof CapturingPictureFailedException) {
// The previously started picture capturing failed, but the camera
// should be still available. This exception does not handle failed
// snapshots.
}
else if (exception instanceof CapturingSnapshotFailedException) {
// The previously started snapshot capturing failed, but the camera
// should be still available. This exception does not handle failed
// "real picture" capturing.
}
}
else if (exception instanceof CapturingVideoFailedException) {
// The previously started video capturing failed, but the camera
// should be still available.
}
}
else {
throw exception;
}
}
});
```
### Other camera events ### Other camera events
Make sure you can react to different camera events by setting up one or more `CameraListener` instances. All these are executed on the UI thread. Make sure you can react to different camera events by setting up one or more `CameraListener` instances. All these are executed on the UI thread.
@ -221,7 +289,6 @@ camera.addCameraListener(new CameraListener() {
*/ */
@Override @Override
public void onExposureCorrectionChanged(float newValue, float[] bounds, PointF[] fingers) {} public void onExposureCorrectionChanged(float newValue, float[] bounds, PointF[] fingers) {}
}); });
``` ```
@ -542,6 +609,7 @@ This is what was done since the library was forked. I have kept the original str
- *Tests!* - *Tests!*
- *`CameraLogger` APIs for logging and bug reports* - *`CameraLogger` APIs for logging and bug reports*
- *Better threading, start() in worker thread and callbacks in UI* - *Better threading, start() in worker thread and callbacks in UI*
- *Custom error handling: prevent app crashes (caused by inevitable errors) by simply overriding a listener*
These are still things that need to be done, off the top of my head: These are still things that need to be done, off the top of my head:
@ -550,7 +618,7 @@ These are still things that need to be done, off the top of my head:
- [ ] add a `setPreferredAspectRatio` API to choose the capture size. Preview size will adapt, and then, if let free, the CameraView will adapt as well - [ ] add a `setPreferredAspectRatio` API to choose the capture size. Preview size will adapt, and then, if let free, the CameraView will adapt as well
- [ ] animate grid lines similar to stock camera app - [ ] animate grid lines similar to stock camera app
- [ ] add onRequestPermissionResults for easy permission callback - [ ] add onRequestPermissionResults for easy permission callback
- [ ] better error handling, maybe with a onError(e) method in the public listener, or have each public method return a boolean - [ ] better error handling, maybe extending the current onError(e) method to handle more use cases (e.g. the ones only caught by a boolean return value)
- [ ] decent code coverage - [ ] decent code coverage
## Device-specific issues ## Device-specific issues

@ -19,6 +19,9 @@ import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import static android.hardware.Camera.CAMERA_ERROR_SERVER_DIED;
import static android.hardware.Camera.CAMERA_ERROR_UNKNOWN;
@SuppressWarnings("deprecation") @SuppressWarnings("deprecation")
class Camera1 extends CameraController { class Camera1 extends CameraController {
@ -37,6 +40,7 @@ class Camera1 extends CameraController {
private Runnable mPostFocusResetRunnable = new Runnable() { private Runnable mPostFocusResetRunnable = new Runnable() {
@Override @Override
public void run() { public void run() {
try {
if (!isCameraAvailable()) return; if (!isCameraAvailable()) return;
mCamera.cancelAutoFocus(); mCamera.cancelAutoFocus();
synchronized (mLock) { synchronized (mLock) {
@ -47,6 +51,15 @@ class Camera1 extends CameraController {
mCamera.setParameters(params); mCamera.setParameters(params);
} }
} }
catch (Exception e) {
// at least setParameters may fail.
// problem may be device-specific to the Samsung Galaxy J5
// TODO why does it fail occasionally and is it possible to prevent such errors?
CameraException cameraException = new CameraConfigurationFailedException("Failed to " +
"reset auto focus.", e);
mCameraCallbacks.onError(cameraException);
}
}
}; };
private Mapper mMapper = new Mapper.Mapper1(); private Mapper mMapper = new Mapper.Mapper1();
@ -75,8 +88,9 @@ class Camera1 extends CameraController {
try { try {
setup(); setup();
} catch (Exception e) { } catch (Exception e) {
LOG.w("onSurfaceAvailable:", "Exception while binding camera to preview.", e); CameraException cameraException = new CameraUnavailableException(
throw new RuntimeException(e); "onSurfaceAvailable: Exception while binding camera to preview.", e);
mCameraCallbacks.onError(cameraException);
} }
} }
}); });
@ -164,6 +178,38 @@ class Camera1 extends CameraController {
if (collectCameraId()) { if (collectCameraId()) {
mCamera = Camera.open(mCameraId); mCamera = Camera.open(mCameraId);
/**
* attach Android native error listener for the Camera1 API
* TODO it's not yet sure how the caught errors interact with the exceptions caught
* outside of the following handler. Furthermore, for most errors it's not known whether
* they are crucial or not. Therefore, such errors are handled as low-priority
* CameraConfigurationFailedExceptions for now.
*/
mCamera.setErrorCallback(new Camera.ErrorCallback() {
@Override
public void onError(int errorCode, Camera camera) {
// extend error information by known error codes
CameraException cameraException;
if (errorCode == CAMERA_ERROR_SERVER_DIED) {
cameraException = new CameraUnavailableException(
"Media server died. In this case, the application must release the" +
" Camera object and instantiate a new one.");
}
else if (errorCode == CAMERA_ERROR_UNKNOWN) {
cameraException = new CameraConfigurationFailedException(
"Unspecified camera error.");
}
else {
cameraException = new CameraConfigurationFailedException(
"Received camera error code: " + errorCode);
}
// redirect error
mCameraCallbacks.onError(cameraException);
}
});
// Set parameters that might have been set before the camera was opened. // Set parameters that might have been set before the camera was opened.
synchronized (mLock) { synchronized (mLock) {
LOG.i("onStart:", "Applying default parameters."); LOG.i("onStart:", "Applying default parameters.");
@ -250,6 +296,7 @@ class Camera1 extends CameraController {
@Override @Override
void setLocation(Location location) { void setLocation(Location location) {
try {
Location oldLocation = mLocation; Location oldLocation = mLocation;
mLocation = location; mLocation = location;
if (isCameraAvailable()) { if (isCameraAvailable()) {
@ -259,6 +306,12 @@ class Camera1 extends CameraController {
} }
} }
} }
catch (Exception e) {
CameraException cameraException =
new CameraConfigurationFailedException("Failed to set the location.", e);
mCameraCallbacks.onError(cameraException);
}
}
private boolean mergeLocation(Camera.Parameters params, Location oldLocation) { private boolean mergeLocation(Camera.Parameters params, Location oldLocation) {
if (mLocation != null) { if (mLocation != null) {
@ -288,6 +341,7 @@ class Camera1 extends CameraController {
@Override @Override
void setWhiteBalance(WhiteBalance whiteBalance) { void setWhiteBalance(WhiteBalance whiteBalance) {
try {
WhiteBalance old = mWhiteBalance; WhiteBalance old = mWhiteBalance;
mWhiteBalance = whiteBalance; mWhiteBalance = whiteBalance;
if (isCameraAvailable()) { if (isCameraAvailable()) {
@ -297,6 +351,13 @@ class Camera1 extends CameraController {
} }
} }
} }
catch (Exception e) {
// TODO handle, !mergeWhiteBalance, too?
CameraException cameraException =
new CameraConfigurationFailedException("Failed to set the white balance.", e);
mCameraCallbacks.onError(cameraException);
}
}
private boolean mergeWhiteBalance(Camera.Parameters params, WhiteBalance oldWhiteBalance) { private boolean mergeWhiteBalance(Camera.Parameters params, WhiteBalance oldWhiteBalance) {
if (mOptions.supports(mWhiteBalance)) { if (mOptions.supports(mWhiteBalance)) {
@ -309,6 +370,7 @@ class Camera1 extends CameraController {
@Override @Override
void setHdr(Hdr hdr) { void setHdr(Hdr hdr) {
try {
Hdr old = mHdr; Hdr old = mHdr;
mHdr = hdr; mHdr = hdr;
if (isCameraAvailable()) { if (isCameraAvailable()) {
@ -318,6 +380,13 @@ class Camera1 extends CameraController {
} }
} }
} }
catch (Exception e) {
// TODO handle, !mergeHdr, too?
CameraException cameraException =
new CameraConfigurationFailedException("Failed to set hdr.", e);
mCameraCallbacks.onError(cameraException);
}
}
private boolean mergeHdr(Camera.Parameters params, Hdr oldHdr) { private boolean mergeHdr(Camera.Parameters params, Hdr oldHdr) {
if (mOptions.supports(mHdr)) { if (mOptions.supports(mHdr)) {
@ -341,6 +410,7 @@ class Camera1 extends CameraController {
@Override @Override
void setFlash(Flash flash) { void setFlash(Flash flash) {
try {
Flash old = mFlash; Flash old = mFlash;
mFlash = flash; mFlash = flash;
if (isCameraAvailable()) { if (isCameraAvailable()) {
@ -350,6 +420,13 @@ class Camera1 extends CameraController {
} }
} }
} }
catch (Exception e) {
// TODO handle, !mergeFlash, too?
CameraException cameraException =
new CameraConfigurationFailedException("Failed to set flash.", e);
mCameraCallbacks.onError(cameraException);
}
}
private boolean mergeFlash(Camera.Parameters params, Flash oldFlash) { private boolean mergeFlash(Camera.Parameters params, Flash oldFlash) {
@ -391,6 +468,8 @@ class Camera1 extends CameraController {
@Override @Override
void setVideoQuality(VideoQuality videoQuality) { void setVideoQuality(VideoQuality videoQuality) {
try {
if (mIsCapturingVideo) { if (mIsCapturingVideo) {
// TODO: actually any call to getParameters() could fail while recording a video. // TODO: actually any call to getParameters() could fail while recording a video.
// See. https://stackoverflow.com/questions/14941625/correct-handling-of-exception-getparameters-failed-empty-parameters // See. https://stackoverflow.com/questions/14941625/correct-handling-of-exception-getparameters-failed-empty-parameters
@ -416,6 +495,12 @@ class Camera1 extends CameraController {
LOG.i("setVideoQuality:", "previewSize:", mPreviewSize); LOG.i("setVideoQuality:", "previewSize:", mPreviewSize);
} }
} }
catch (Exception e) {
CameraException cameraException =
new CameraConfigurationFailedException("Failed to set video quality.", e);
mCameraCallbacks.onError(cameraException);
}
}
@Override @Override
boolean capturePicture() { boolean capturePicture() {
@ -425,6 +510,7 @@ class Camera1 extends CameraController {
if (!mOptions.isVideoSnapshotSupported()) return false; if (!mOptions.isVideoSnapshotSupported()) return false;
} }
try {
// Set boolean to wait for image callback // Set boolean to wait for image callback
mIsCapturingImage = true; mIsCapturingImage = true;
final int exifRotation = computeExifRotation(); final int exifRotation = computeExifRotation();
@ -456,6 +542,12 @@ class Camera1 extends CameraController {
}); });
return true; return true;
} }
catch (Exception e) {
CameraException cameraException = new CapturingPictureFailedException("Capturing a picture failed.", e);
mCameraCallbacks.onError(cameraException);
return false;
}
}
@Override @Override
@ -468,6 +560,8 @@ class Camera1 extends CameraController {
capturePicture(); capturePicture();
return false; return false;
} }
try {
mIsCapturingImage = true; mIsCapturingImage = true;
mCamera.setOneShotPreviewCallback(new Camera.PreviewCallback() { mCamera.setOneShotPreviewCallback(new Camera.PreviewCallback() {
@Override @Override
@ -500,6 +594,12 @@ class Camera1 extends CameraController {
}); });
return true; return true;
} }
catch (Exception e) {
CameraException cameraException = new CapturingSnapshotFailedException("Capturing a snapshot failed.", e);
mCameraCallbacks.onError(cameraException);
return false;
}
}
@Override @Override
boolean shouldFlipSizes() { boolean shouldFlipSizes() {
@ -619,14 +719,20 @@ class Camera1 extends CameraController {
mMediaRecorder.start(); mMediaRecorder.start();
return true; return true;
} catch (Exception e) { } catch (Exception e) {
LOG.e("Error while starting MediaRecorder. Swallowing.", e); CameraException cameraException =
new CapturingVideoFailedException("Error while starting MediaRecorder. " +
"Swallowing.", e);
mCameraCallbacks.onError(cameraException);
mVideoFile = null; mVideoFile = null;
mCamera.lock(); mCamera.lock();
endVideo(); endVideo();
return false; return false;
} }
} else { } else {
throw new IllegalStateException("Can't record video while session type is picture"); CameraException cameraException =
new CapturingVideoFailedException("Can't record video while session type is picture");
mCameraCallbacks.onError(cameraException);
return false;
} }
} }
@ -736,6 +842,7 @@ class Camera1 extends CameraController {
@Override @Override
boolean setZoom(float zoom) { boolean setZoom(float zoom) {
try {
if (!isCameraAvailable()) return false; if (!isCameraAvailable()) return false;
if (!mOptions.isZoomSupported()) return false; if (!mOptions.isZoomSupported()) return false;
synchronized (mLock) { synchronized (mLock) {
@ -746,10 +853,19 @@ class Camera1 extends CameraController {
} }
return true; return true;
} }
catch (Exception e) {
CameraException cameraException =
new CameraConfigurationFailedException("Failed to set zoom.", e);
mCameraCallbacks.onError(cameraException);
return false;
}
}
@Override @Override
boolean setExposureCorrection(float EVvalue) { boolean setExposureCorrection(float EVvalue) {
try {
if (!isCameraAvailable()) return false; if (!isCameraAvailable()) return false;
if (!mOptions.isExposureCorrectionSupported()) return false; if (!mOptions.isExposureCorrectionSupported()) return false;
float max = mOptions.getExposureCorrectionMaxValue(); float max = mOptions.getExposureCorrectionMaxValue();
@ -763,6 +879,14 @@ class Camera1 extends CameraController {
} }
return true; return true;
} }
catch (Exception e) {
CameraException cameraException =
new CameraConfigurationFailedException("Failed to set exposure correction.", e);
mCameraCallbacks.onError(cameraException);
return false;
}
}
// ----------------- // -----------------
// Tap to focus stuff. // Tap to focus stuff.
@ -772,6 +896,8 @@ class Camera1 extends CameraController {
boolean startAutoFocus(@Nullable final Gesture gesture, PointF point) { boolean startAutoFocus(@Nullable final Gesture gesture, PointF point) {
if (!isCameraAvailable()) return false; if (!isCameraAvailable()) return false;
if (!mOptions.isAutoFocusSupported()) return false; if (!mOptions.isAutoFocusSupported()) return false;
try {
final PointF p = new PointF(point.x, point.y); // copy. final PointF p = new PointF(point.x, point.y); // copy.
List<Camera.Area> meteringAreas2 = computeMeteringAreas(p.x, p.y); List<Camera.Area> meteringAreas2 = computeMeteringAreas(p.x, p.y);
List<Camera.Area> meteringAreas1 = meteringAreas2.subList(0, 1); List<Camera.Area> meteringAreas1 = meteringAreas2.subList(0, 1);
@ -798,6 +924,15 @@ class Camera1 extends CameraController {
} }
return true; return true;
} }
catch (Exception e) {
// at least getParameters and setParameters may fail.
// TODO why do they fail and is it possible to prevent such errors?
CameraException cameraException = new CameraConfigurationFailedException("Failed to " +
"start auto focus.", e);
mCameraCallbacks.onError(cameraException);
return false;
}
}
private List<Camera.Area> computeMeteringAreas(double viewClickX, double viewClickY) { private List<Camera.Area> computeMeteringAreas(double viewClickX, double viewClickY) {

@ -0,0 +1,16 @@
package com.otaliastudios.cameraview;
/**
* An object of this class describes an error that occurred during the normal runtime of the camera.
* The previously started setting change failed, but the camera should be still available.
*/
public class CameraConfigurationFailedException extends CameraException {
CameraConfigurationFailedException(String message) {
super(message);
}
CameraConfigurationFailedException(String message, Throwable cause) {
super(message, cause);
}
}

@ -104,8 +104,9 @@ abstract class CameraController implements CameraPreview.SurfaceCallback {
mCameraCallbacks.dispatchOnCameraOpened(mOptions); mCameraCallbacks.dispatchOnCameraOpened(mOptions);
} catch (Exception e) { } catch (Exception e) {
LOG.e("Error while starting the camera engine.", e); CameraException cameraException =
throw new RuntimeException(e); new CameraUnavailableException("Error while starting the camera engine.", e);
mCameraCallbacks.onError(cameraException);
} }
} }
}); });
@ -130,8 +131,9 @@ abstract class CameraController implements CameraPreview.SurfaceCallback {
mCameraCallbacks.dispatchOnCameraClosed(); mCameraCallbacks.dispatchOnCameraClosed();
} catch (Exception e) { } catch (Exception e) {
LOG.e("Error while stopping the camera engine.", e); CameraException cameraException =
throw new RuntimeException(e); new CameraUnavailableException("Error while stopping the camera engine.", e);
mCameraCallbacks.onError(cameraException);
} }
} }
}); });
@ -180,9 +182,9 @@ abstract class CameraController implements CameraPreview.SurfaceCallback {
mCameraCallbacks.dispatchOnCameraOpened(mOptions); mCameraCallbacks.dispatchOnCameraOpened(mOptions);
} catch (Exception e) { } catch (Exception e) {
LOG.e("Error while restarting the camera engine.", e); CameraException cameraException =
throw new RuntimeException(e); new CameraUnavailableException("Error while restarting the camera engine.", e);
mCameraCallbacks.onError(cameraException);
} }
} }
}); });

@ -0,0 +1,15 @@
package com.otaliastudios.cameraview;
/**
* An object of this class describes an error that occurred during the normal runtime of the camera.
*/
public abstract class CameraException extends RuntimeException {
CameraException(String message) {
super(message);
}
CameraException(String message, Throwable cause) {
super(message, cause);
}
}

@ -130,4 +130,17 @@ public abstract class CameraListener {
} }
/**
* Notifies that an error occurred in any of the previously called methods.
*
* The default implementation will just throw the original exception again to prevent missing
* error handling. Override this method without calling the super method in order to implement
* custom error handling.
*
* @param exception the caught exception
*/
@UiThread
public void onError(CameraException exception) {
throw exception;
}
} }

@ -0,0 +1,17 @@
package com.otaliastudios.cameraview;
/**
* An object of this class describes an error that occurred during the normal runtime of the camera.
* It prevents the camera from being used. The cause may be temporary or permanent. You should
* restart the camera or deactivate any user interaction with the camera.
*/
public class CameraUnavailableException extends CameraException {
CameraUnavailableException(String message) {
super(message);
}
CameraUnavailableException(String message, Throwable cause) {
super(message, cause);
}
}

@ -559,9 +559,10 @@ public class CameraView extends FrameLayout {
return; return;
} }
} }
LOG.e("Permission error:", "When the session type is set to video,", CameraException cameraException = new CameraUnavailableException("Permission " +
"the RECORD_AUDIO permission should be added to the app manifest file."); "error: When the session type is set to video, the RECORD_AUDIO " +
throw new IllegalStateException(CameraLogger.lastMessage); "permission should be added to the app manifest file.");
mCameraCallbacks.onError(cameraException);
} catch (PackageManager.NameNotFoundException e) { } catch (PackageManager.NameNotFoundException e) {
// Not possible. // Not possible.
} }
@ -1004,7 +1005,11 @@ public class CameraView extends FrameLayout {
*/ */
public void setJpegQuality(int jpegQuality) { public void setJpegQuality(int jpegQuality) {
if (jpegQuality <= 0 || jpegQuality > 100) { if (jpegQuality <= 0 || jpegQuality > 100) {
throw new IllegalArgumentException("JPEG quality should be > 0 and <= 100"); IllegalArgumentException illegalArgumentException = new
IllegalArgumentException("JPEG quality should be > 0 and <= 100");
CameraException cameraException = new CameraConfigurationFailedException("Could not" +
" set JpegQuality", illegalArgumentException);
mCameraCallbacks.onError(cameraException);
} }
mJpegQuality = jpegQuality; mJpegQuality = jpegQuality;
} }
@ -1167,15 +1172,16 @@ public class CameraView extends FrameLayout {
* so callers should ensure they have appropriate permissions to write to the file. * so callers should ensure they have appropriate permissions to write to the file.
* Recording will be automatically stopped after durationMillis, unless * Recording will be automatically stopped after durationMillis, unless
* {@link #stopCapturingVideo()} is not called meanwhile. * {@link #stopCapturingVideo()} is not called meanwhile.
* Triggers error handler, if durationMillis is less than 500 milliseconds.
* *
* @param file a file where the video will be saved * @param file a file where the video will be saved
* @param durationMillis video max duration * @param durationMillis video max duration
*
* @throws IllegalArgumentException if durationMillis is less than 500 milliseconds
*/ */
public void startCapturingVideo(File file, long durationMillis) { public void startCapturingVideo(File file, long durationMillis) {
if (durationMillis < 500) { if (durationMillis < 500) {
throw new IllegalArgumentException("Video duration can't be < 500 milliseconds"); CameraException cameraException = new CapturingVideoFailedException("Video duration" +
" can't be < 500 milliseconds");
mCameraCallbacks.onError(cameraException);
} }
startCapturingVideo(file); startCapturingVideo(file);
mUiHandler.postDelayed(new Runnable() { mUiHandler.postDelayed(new Runnable() {
@ -1310,6 +1316,7 @@ public class CameraView extends FrameLayout {
void dispatchOnFocusEnd(@Nullable Gesture trigger, boolean success, PointF where); void dispatchOnFocusEnd(@Nullable Gesture trigger, boolean success, PointF where);
void dispatchOnZoomChanged(final float newValue, final PointF[] fingers); void dispatchOnZoomChanged(final float newValue, final PointF[] fingers);
void dispatchOnExposureCorrectionChanged(float newValue, float[] bounds, PointF[] fingers); void dispatchOnExposureCorrectionChanged(float newValue, float[] bounds, PointF[] fingers);
void onError(CameraException exception);
} }
private class Callbacks implements CameraCallbacks { private class Callbacks implements CameraCallbacks {
@ -1557,6 +1564,47 @@ public class CameraView extends FrameLayout {
} }
}); });
} }
/**
* Log and redirect the given error information to the CameraListeners.
*
* @param exception the error cause
*/
@Override
public void onError(final CameraException exception) {
// log
LOG.e(exception.getMessage(), exception.getCause());
// redirect
mUiHandler.post(new Runnable() {
@Override
public void run() {
// all error listeners will be called, but at most one of them should actually
// throw an exception
int count = 0;
for (CameraListener listener : mListeners) {
try {
listener.onError(exception);
} catch (CameraException ce) {
// if a custom error handler caused a new exception, we throw the new
// one instead of the original one
if (ce == exception) {
count++;
}
else {
throw ce;
}
}
}
// the original exception is only thrown, if all existing listeners threw it
if (count == mListeners.size()) {
throw exception;
}
}
});
}
} }
//endregion //endregion

@ -0,0 +1,15 @@
package com.otaliastudios.cameraview;
/**
* An object of this class describes an error that occurred during the normal runtime of the camera.
* The previously started capturing failed, but the camera should be still available.
*/
public abstract class CapturingFailedException extends CameraException {
CapturingFailedException(String message) {
super(message);
}
CapturingFailedException(String message, Throwable cause) {
super(message, cause);
}
}

@ -0,0 +1,17 @@
package com.otaliastudios.cameraview;
/**
* An object of this class describes an error that occurred during the normal runtime of the camera.
* The previously started image capturing failed (snapshot or "real picture"), but the camera should
* be still available.
*/
public abstract class CapturingImageFailedException extends CapturingFailedException {
CapturingImageFailedException(String message) {
super(message);
}
CapturingImageFailedException(String message, Throwable cause) {
super(message, cause);
}
}

@ -0,0 +1,17 @@
package com.otaliastudios.cameraview;
/**
* An object of this class describes an error that occurred during the normal runtime of the camera.
* The previously started picture capturing failed, but the camera should be still available.
* This exception does not handle failed snapshots.
*/
public class CapturingPictureFailedException extends CapturingImageFailedException {
CapturingPictureFailedException(String message) {
super(message);
}
CapturingPictureFailedException(String message, Throwable cause) {
super(message, cause);
}
}

@ -0,0 +1,17 @@
package com.otaliastudios.cameraview;
/**
* An object of this class describes an error that occurred during the normal runtime of the camera.
* The previously started snapshot capturing failed, but the camera should be still available.
* This exception does not handle failed "real picture" capturing.
*/
public class CapturingSnapshotFailedException extends CapturingImageFailedException {
CapturingSnapshotFailedException(String message) {
super(message);
}
CapturingSnapshotFailedException(String message, Throwable cause) {
super(message, cause);
}
}

@ -0,0 +1,16 @@
package com.otaliastudios.cameraview;
/**
* An object of this class describes an error that occurred during the normal runtime of the camera.
* The previously started video capturing failed, but the camera should be still available.
*/
public class CapturingVideoFailedException extends CapturingFailedException {
CapturingVideoFailedException(String message) {
super(message);
}
CapturingVideoFailedException(String message, Throwable cause) {
super(message, cause);
}
}
Loading…
Cancel
Save