camerakit/src/main/java/com/camerakit/CameraKitView.java
package com.camerakit;
import android.Manifest;
import android.app.Activity;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.pm.PackageManager;
import android.content.res.TypedArray;
import android.os.Build;
import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
import androidx.annotation.RestrictTo;
import androidx.annotation.RestrictTo.Scope;
import android.util.AttributeSet;
import android.util.Log;
import android.view.ViewGroup;
import com.camerakit.type.CameraFacing;
import com.camerakit.type.CameraFlash;
import com.camerakit.type.CameraSize;
import jpegkit.Jpeg;
import org.jetbrains.annotations.NotNull;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
/**
* CameraKitView provides a high-level, easy to implement, and safe to use way to work with
* the Android camera.
*
* @since v1.0.0
*/
public class CameraKitView extends GestureLayout {
/**
* Request code for a runtime permissions intent.
*/
private static final int PERMISSION_REQUEST_CODE = 99107;
/**
* Flag for handling requesting the {@link android.Manifest.permission#CAMERA}
* permission.
*/
public static final int PERMISSION_CAMERA = 1;
/**
* Flag for handling requesting the {@link android.Manifest.permission#RECORD_AUDIO}
* permission.
*/
public static final int PERMISSION_MICROPHONE = 1 << 1;
/**
* Flag for handling requesting the {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE}
* permission.
*/
public static final int PERMISSION_STORAGE = 1 << 2;
/**
* Flag for handling requesting the {@link android.Manifest.permission#ACCESS_FINE_LOCATION}
* permission.
*/
public static final int PERMISSION_LOCATION = 1 << 3;
/**
* Represents manifest runtime-permissions that may be used.
*/
@RestrictTo(Scope.LIBRARY_GROUP)
@Retention(RetentionPolicy.SOURCE)
@IntDef(flag = true,
value = {PERMISSION_CAMERA, PERMISSION_MICROPHONE, PERMISSION_STORAGE, PERMISSION_LOCATION})
@interface Permission {
}
/**
*
*/
public interface CameraListener {
/**
*
*/
void onOpened();
/**
*
*/
void onClosed();
}
/**
*
*/
public interface PreviewListener {
/**
*
*/
void onStart();
/**
*
*/
void onStop();
}
/**
*
*/
public interface ErrorListener {
/**
* @param view
* @param error
*/
void onError(CameraKitView view, CameraException error);
}
/**
*
*/
public interface GestureListener {
/**
* @param view
* @param x
* @param y
*/
void onTap(CameraKitView view, float x, float y);
/**
* @param view
* @param x
* @param y
*/
void onLongTap(CameraKitView view, float x, float y);
/**
* @param view
* @param x
* @param y
*/
void onDoubleTap(CameraKitView view, float x, float y);
/**
* @param view
* @param ds
* @param dsx
* @param dsy
*/
void onPinch(CameraKitView view, float ds, float dsx, float dsy);
}
/**
*
*/
public interface PermissionsListener {
void onPermissionsSuccess();
void onPermissionsFailure();
}
/**
*
*/
public interface ImageCallback {
/**
* @param view
* @param jpeg
*/
void onImage(CameraKitView view, byte[] jpeg);
}
/**
*
*/
public interface VideoCallback {
/**
* @param view
* @param video
*/
void onVideo(CameraKitView view, Object video);
}
public interface FrameCallback {
/**
* @param view
* @param jpeg
*/
void onFrame(CameraKitView view, byte[] jpeg);
}
private boolean mAdjustViewBounds;
private float mAspectRatio;
private int mFacing;
private int mFlash;
private int mFocus;
private float mZoomFactor;
private int mSensorPreset;
private int mPreviewEffect;
private int mPermissions;
private float mImageMegaPixels;
private int mImageJpegQuality;
private GestureListener mGestureListener;
private CameraListener mCameraListener;
private PreviewListener mPreviewListener;
private ErrorListener mErrorListener;
private PermissionsListener mPermissionsListener;
private static CameraFacing cameraFacing;
private static CameraFlash cameraFlash;
private CameraPreview mCameraPreview;
public CameraKitView(Context context) {
super(context);
obtainAttributes(context, null);
}
public CameraKitView(Context context, AttributeSet attrs) {
super(context, attrs);
obtainAttributes(context, attrs);
}
public CameraKitView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
obtainAttributes(context, attrs);
}
private void obtainAttributes(Context context, AttributeSet attrs) {
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CameraKitView);
mAdjustViewBounds = a.getBoolean(R.styleable.CameraKitView_android_adjustViewBounds, false);
mAspectRatio = a.getFloat(R.styleable.CameraKitView_camera_aspectRatio, -1f);
mFacing = a.getInteger(R.styleable.CameraKitView_camera_facing, CameraKit.FACING_BACK);
if (cameraFacing == CameraFacing.FRONT) {
mFacing = CameraKit.FACING_FRONT;
}
mFlash = a.getInteger(R.styleable.CameraKitView_camera_flash, CameraKit.FLASH_OFF);
if (cameraFlash == CameraFlash.ON) {
mFlash = CameraKit.FLASH_ON;
}
mFocus = a.getInteger(R.styleable.CameraKitView_camera_focus, CameraKit.FOCUS_AUTO);
mZoomFactor = a.getFloat(R.styleable.CameraKitView_camera_zoomFactor, 1.0f);
mPermissions = a.getInteger(R.styleable.CameraKitView_camera_permissions, PERMISSION_CAMERA);
mImageMegaPixels = a.getFloat(R.styleable.CameraKitView_camera_imageMegaPixels, 2f);
mImageJpegQuality = a.getInteger(R.styleable.CameraKitView_camera_imageJpegQuality, 100);
a.recycle();
mCameraPreview = new CameraPreview(getContext());
addView(mCameraPreview);
mCameraPreview.setListener(new CameraPreview.Listener() {
@Override
public void onCameraOpened() {
if (mCameraListener != null) {
post(new Runnable() {
@Override
public void run() {
mCameraListener.onOpened();
}
});
}
}
@Override
public void onCameraClosed() {
if (mCameraListener != null) {
post(new Runnable() {
@Override
public void run() {
mCameraListener.onClosed();
}
});
}
}
@Override
public void onPreviewStarted() {
if (mPreviewListener != null) {
post(new Runnable() {
@Override
public void run() {
mPreviewListener.onStart();
}
});
}
}
@Override
public void onPreviewStopped() {
if (mPreviewListener != null) {
post(new Runnable() {
@Override
public void run() {
mPreviewListener.onStop();
}
});
}
}
});
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if (mAdjustViewBounds) {
ViewGroup.LayoutParams layoutParams = getLayoutParams();
if (layoutParams.width == WRAP_CONTENT && layoutParams.height == WRAP_CONTENT) {
throw new CameraException("android:adjustViewBounds=true while both layout_width and layout_height are setView to wrap_content - only 1 is allowed.");
} else if (layoutParams.width == WRAP_CONTENT) {
int width = 0;
int height = MeasureSpec.getSize(heightMeasureSpec);
if (mAspectRatio > 0) {
width = (int) (height * mAspectRatio);
widthMeasureSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY);
} else if (mCameraPreview != null && mCameraPreview.getSurfaceSize().area() > 0) {
CameraSize previewSize = mCameraPreview.getSurfaceSize();
width = (int) (((float) height / (float) previewSize.getHeight()) * previewSize.getWidth());
widthMeasureSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY);
}
} else if (layoutParams.height == WRAP_CONTENT) {
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = 0;
if (mAspectRatio > 0) {
height = (int) (width * mAspectRatio);
heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
} else if (mCameraPreview != null && mCameraPreview.getSurfaceSize().area() > 0) {
CameraSize previewSize = mCameraPreview.getSurfaceSize();
height = (int) (((float) width / (float) previewSize.getWidth()) * previewSize.getHeight());
heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
}
}
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
@Override
protected void onTap(float x, float y) {
if (mGestureListener != null) {
mGestureListener.onTap(this, x, y);
}
}
@Override
protected void onLongTap(float x, float y) {
if (mGestureListener != null) {
mGestureListener.onLongTap(this, x, y);
}
}
@Override
protected void onDoubleTap(float x, float y) {
if (mGestureListener != null) {
mGestureListener.onDoubleTap(this, x, y);
}
}
@Override
protected void onPinch(float ds, float dsx, float dsy) {
if (mGestureListener != null) {
mGestureListener.onPinch(this, ds, dsx, dsy);
}
}
public void onStart() {
if (isInEditMode()) {
return;
}
List<String> missingPermissions = getMissingPermissions();
if (Build.VERSION.SDK_INT >= 23 && missingPermissions.size() > 0) {
Activity activity = null;
Context context = getContext();
while (context instanceof ContextWrapper) {
if (context instanceof Activity) {
activity = (Activity) context;
}
context = ((ContextWrapper) context).getBaseContext();
}
if (activity != null) {
List<String> requestPermissions = new ArrayList<>();
List<String> rationalePermissions = new ArrayList<>();
for (String permission : missingPermissions) {
if (!activity.shouldShowRequestPermissionRationale(permission)) {
requestPermissions.add(permission);
} else {
rationalePermissions.add(permission);
}
}
if (requestPermissions.size() > 0) {
activity.requestPermissions(requestPermissions.toArray(new String[requestPermissions.size()]), PERMISSION_REQUEST_CODE);
}
if (rationalePermissions.size() > 0 && mPermissionsListener != null) {
mPermissionsListener.onPermissionsFailure();
}
}
return;
}
if (mPermissionsListener != null) {
mPermissionsListener.onPermissionsSuccess();
}
setFlash(mFlash);
setImageMegaPixels(mImageMegaPixels);
cameraFacing = getFacing() == CameraKit.FACING_BACK ? CameraFacing.BACK : CameraFacing.FRONT;
mCameraPreview.start(cameraFacing);
}
public void onStop() {
if (isInEditMode()) {
return;
}
mCameraPreview.stop();
}
public void onResume() {
if (isInEditMode()) {
return;
}
mCameraPreview.resume();
}
/**
*
*/
public void onPause() {
if (isInEditMode()) {
return;
}
mCameraPreview.pause();
}
/**
* @param callback
*/
public void captureImage(final ImageCallback callback) {
mCameraPreview.capturePhoto(new CameraPreview.PhotoCallback() {
@Override
public void onCapture(@NotNull final byte[] jpeg) {
post(new Runnable() {
@Override
public void run() {
callback.onImage(CameraKitView.this, jpeg);
}
});
}
});
}
/**
*
*/
public void startVideo() {
}
/**
*
*/
public void stopVideo() {
}
/**
* @param callback
*/
public void captureVideo(VideoCallback callback) {
}
/**
*
*/
public void captureFrame(FrameCallback callback) {
}
/**
*
*/
public void setFrameCallback(FrameCallback callback) {
}
/**
* @return
*/
private List<String> getMissingPermissions() {
List<String> manifestPermissions = new ArrayList<>();
if (Build.VERSION.SDK_INT < 23) {
return manifestPermissions;
}
if ((mPermissions | PERMISSION_CAMERA) == mPermissions) {
String manifestPermission = Manifest.permission.CAMERA;
if (getContext().checkSelfPermission(manifestPermission) == PackageManager.PERMISSION_DENIED) {
manifestPermissions.add(manifestPermission);
}
}
if ((mPermissions | PERMISSION_MICROPHONE) == mPermissions) {
String manifestPermission = Manifest.permission.RECORD_AUDIO;
if (getContext().checkSelfPermission(manifestPermission) == PackageManager.PERMISSION_DENIED) {
manifestPermissions.add(manifestPermission);
}
}
if ((mPermissions | PERMISSION_STORAGE) == mPermissions) {
String manifestPermission = Manifest.permission.WRITE_EXTERNAL_STORAGE;
if (getContext().checkSelfPermission(manifestPermission) == PackageManager.PERMISSION_DENIED) {
manifestPermissions.add(manifestPermission);
}
}
if ((mPermissions | PERMISSION_LOCATION) == mPermissions) {
String manifestPermission = Manifest.permission.ACCESS_FINE_LOCATION;
if (getContext().checkSelfPermission(manifestPermission) == PackageManager.PERMISSION_DENIED) {
manifestPermissions.add(manifestPermission);
}
}
return manifestPermissions;
}
public void setPermissionsListener(PermissionsListener permissionsListener) {
mPermissionsListener = permissionsListener;
}
/**
*/
public void requestPermissions(Activity activity) {
if (Build.VERSION.SDK_INT >= 23) {
List<String> manifestPermissions = getMissingPermissions();
if (manifestPermissions.size() > 0) {
activity.requestPermissions(manifestPermissions.toArray(new String[manifestPermissions.size()]), PERMISSION_REQUEST_CODE);
}
}
}
/**
* @param requestCode
* @param permissions
* @param grantResults
*/
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
if (requestCode == PERMISSION_REQUEST_CODE) {
int approvedPermissions = 0;
int deniedPermissions = 0;
for (int i = 0; i < permissions.length; i++) {
int flag = 0;
switch (permissions[i]) {
case Manifest.permission.CAMERA: {
flag = PERMISSION_CAMERA;
break;
}
case Manifest.permission.RECORD_AUDIO: {
flag = PERMISSION_MICROPHONE;
break;
}
case Manifest.permission.WRITE_EXTERNAL_STORAGE: {
flag = PERMISSION_STORAGE;
break;
}
}
if (grantResults[i] == PackageManager.PERMISSION_GRANTED) {
approvedPermissions = approvedPermissions | flag;
} else {
deniedPermissions = deniedPermissions | flag;
}
}
onStart();
}
}
/**
* @param adjustViewBounds
*/
public void setAdjustViewBounds(boolean adjustViewBounds) {
mAdjustViewBounds = adjustViewBounds;
}
/**
* @return
* @see #setAdjustViewBounds(boolean)
*/
public boolean getAdjustViewBounds() {
return mAdjustViewBounds;
}
/**
* @param aspectRatio
*/
public void setAspectRatio(float aspectRatio) {
this.mAspectRatio = aspectRatio;
}
/**
* @return
* @see #setAspectRatio(float)
*/
public float getAspectRatio() {
return mAspectRatio;
}
/**
* @param facing one of {@link CameraKit.Facing}'s constants.
* @see CameraKit#FACING_BACK
* @see CameraKit#FACING_FRONT
*/
public void setFacing(@CameraKit.Facing int facing) {
mFacing = facing;
switch (mCameraPreview.getLifecycleState()) {
case PAUSED:
case STARTED: {
onStop();
onStart();
break;
}
case RESUMED: {
onStop();
onStart();
onResume();
break;
}
}
}
/**
* @return one of {@link CameraKit.Facing}'s constants.
* @see #setFacing(int)
*/
@CameraKit.Facing
public int getFacing() {
return mFacing;
}
/**
*
*/
public void toggleFacing() {
if (getFacing() == CameraKit.FACING_BACK) {
setFacing(CameraKit.FACING_FRONT);
} else {
setFacing(CameraKit.FACING_BACK);
}
}
/**
* @param flash one of {@link CameraKit.Flash}'s constants.
* @see CameraKit#FLASH_OFF
* @see CameraKit#FLASH_ON
* @see CameraKit#FLASH_AUTO
* @see CameraKit#FLASH_TORCH
*/
public void setFlash(@CameraKit.Flash int flash) {
mFlash = flash;
try {
switch (flash) {
case CameraKit.FLASH_OFF: {
cameraFlash = CameraFlash.OFF;
break;
}
case CameraKit.FLASH_ON: {
cameraFlash = CameraFlash.ON;
break;
}
case CameraKit.FLASH_AUTO: {
throw new CameraException("FLASH_AUTO is not supported in this version of CameraKit.");
}
case CameraKit.FLASH_TORCH: {
throw new CameraException("FLASH_TORCH is not supported in this version of CameraKit.");
}
}
} catch(CameraException exception) {
Log.e("CameraException: Flash", exception.getMessage());
return;
}
mCameraPreview.setFlash(cameraFlash);
}
/**
* Determine if device is capable of flash
* @return boolean if device is capable of flash
*/
public boolean hasFlash() {
return mCameraPreview.hasFlash();
}
/**
* Get supported flash types on device
* @return array of supported flash types
*/
public CameraFlash[] getSupportedFlashTypes() {
return mCameraPreview.getSupportedFlashTypes();
}
/**
* @return one of {@link CameraKit.Flash}'s constants.
* @see #setFlash(int)
*/
@CameraKit.Flash
public int getFlash() {
return mFlash;
}
/**
* @param focus one of {@link CameraKit.Focus}'s constants.
* @see CameraKit#FOCUS_OFF
* @see CameraKit#FOCUS_AUTO
* @see CameraKit#FOCUS_CONTINUOUS
*/
public void setFocus(@CameraKit.Focus int focus) {
mFocus = focus;
}
/**
* @return one of {@link CameraKit.Focus}'s constants.
* @see #setFocus(int)
*/
@CameraKit.Focus
public int getFocus() {
return mFocus;
}
/**
* @param zoomFactor
*/
public void setZoomFactor(float zoomFactor) {
mZoomFactor = zoomFactor;
}
/**
* @return
* @see #setZoomFactor(float)
*/
public float getZoomFactor() {
return mZoomFactor;
}
/**
* @param sensorPreset one of {@link CameraKit.SensorPreset}'s constants.
* @see CameraKit#SENSOR_PRESET_NONE
* @see CameraKit#SENSOR_PRESET_ACTION
* @see CameraKit#SENSOR_PRESET_PORTRAIT
* @see CameraKit#SENSOR_PRESET_LANDSCAPE
* @see CameraKit#SENSOR_PRESET_NIGHT
* @see CameraKit#SENSOR_PRESET_NIGHT_PORTRAIT
* @see CameraKit#SENSOR_PRESET_THEATRE
* @see CameraKit#SENSOR_PRESET_BEACH
* @see CameraKit#SENSOR_PRESET_SNOW
* @see CameraKit#SENSOR_PRESET_SUNSET
* @see CameraKit#SENSOR_PRESET_STEADYPHOTO
* @see CameraKit#SENSOR_PRESET_FIREWORKS
* @see CameraKit#SENSOR_PRESET_SPORTS
* @see CameraKit#SENSOR_PRESET_PARTY
* @see CameraKit#SENSOR_PRESET_CANDLELIGHT
* @see CameraKit#SENSOR_PRESET_BARCODE
*/
public void setSensorPreset(@CameraKit.SensorPreset int sensorPreset) {
mSensorPreset = sensorPreset;
}
/**
* @return one of {@link CameraKit.SensorPreset}'s constants.
* @see #setSensorPreset(int)
*/
@CameraKit.SensorPreset
public int getSensorPreset() {
return mSensorPreset;
}
/**
* @param previewEffect one of {@link CameraKit.PreviewEffect}'s constants.
* @see CameraKit#PREVIEW_EFFECT_NONE
* @see CameraKit#PREVIEW_EFFECT_MONO
* @see CameraKit#PREVIEW_EFFECT_SOLARIZE
* @see CameraKit#PREVIEW_EFFECT_SEPIA
* @see CameraKit#PREVIEW_EFFECT_POSTERIZE
* @see CameraKit#PREVIEW_EFFECT_WHITEBOARD
* @see CameraKit#PREVIEW_EFFECT_BLACKBOARD
* @see CameraKit#PREVIEW_EFFECT_AQUA
*/
public void setPreviewEffect(@CameraKit.PreviewEffect int previewEffect) {
mPreviewEffect = previewEffect;
}
/**
* @return one of {@link CameraKit.PreviewEffect}'s constants.
* @see #setPreviewEffect(int)
*/
@CameraKit.PreviewEffect
public int getPreviewEffect() {
return mPreviewEffect;
}
/**
* @param permissions
*/
public void setPermissions(@Permission int permissions) {
mPermissions = permissions;
}
/**
* @return
* @see #setPermissions(int)
*/
@Permission
public int getPermissions() {
return mPermissions;
}
public void setImageMegaPixels(float imageMegaPixels) {
mImageMegaPixels = imageMegaPixels;
mCameraPreview.setImageMegaPixels(mImageMegaPixels);
}
public float getImageMegaPixels() {
return mImageMegaPixels;
}
/**
* @param gestureListener
*/
public void setGestureListener(GestureListener gestureListener) {
mGestureListener = gestureListener;
}
/**
* @return
* @see #setGestureListener(GestureListener)
*/
public GestureListener getGestureListener() {
return mGestureListener;
}
/**
* This adapter class provides empty implementations of the methods from {@link GestureListener}.
* Any custom listener that cares only about a subset of the methods of this listener can
* simply subclass this adapter class instead of implementing the interface directly.
*/
public static class GestureListenerAdapter implements GestureListener {
/**
* @see GestureListener#onTap(CameraKitView, float, float)
*/
@Override
public void onTap(CameraKitView view, float x, float y) {
}
/**
* @see GestureListener#onLongTap(CameraKitView, float, float)
*/
@Override
public void onLongTap(CameraKitView view, float x, float y) {
}
/**
* @see GestureListener#onDoubleTap(CameraKitView, float, float)
*/
@Override
public void onDoubleTap(CameraKitView view, float x, float y) {
}
/**
* @see GestureListener#onPinch(CameraKitView, float, float, float)
*/
@Override
public void onPinch(CameraKitView view, float ds, float dsx, float dsy) {
}
}
/**
* @param cameraListener
*/
public void setCameraListener(CameraListener cameraListener) {
mCameraListener = cameraListener;
}
/**
* @return CameraListener
*/
public CameraListener getCameraListener() {
return mCameraListener;
}
/**
* Delete CameraListener
*/
public void removeCameraListener() {
mCameraListener = null;
}
/**
* @param previewListener
*/
public void setPreviewListener(PreviewListener previewListener) {
mPreviewListener = previewListener;
}
/**
* @return PreviewListener
*/
public PreviewListener getPreviewListener() {
return mPreviewListener;
}
/**
* Delete PreviewListener
*/
public void removePreviewListener() {
mPreviewListener = null;
}
/**
* @param errorListener
*/
public void setErrorListener(ErrorListener errorListener) {
mErrorListener = errorListener;
}
/**
* @return ErrorListener
* @see #setErrorListener(ErrorListener)
*/
public ErrorListener getErrorListener() {
return mErrorListener;
}
/**
* Delete ErrorListener
*/
public void removeErrorListener() {
mErrorListener = null;
}
public CameraSize getPreviewResolution() {
if (mCameraPreview.getPreviewSize().area() == 0) {
return null;
}
return mCameraPreview.getPreviewSize();
}
public CameraSize getPhotoResolution() {
if (mCameraPreview.getPhotoSize().area() == 0) {
return null;
}
return mCameraPreview.getPhotoSize();
}
/**
*
*/
public interface JpegCallback {
void onJpeg(Jpeg jpeg);
}
/**
*
*/
public static class Size implements Comparable<Size> {
private final int mWidth;
private final int mHeight;
public Size(int width, int height) {
mWidth = width;
mHeight = height;
}
public int getWidth() {
return mWidth;
}
public int getHeight() {
return mHeight;
}
@Override
public boolean equals(Object o) {
if (o == null) {
return false;
} else if (this == o) {
return true;
} else if (o instanceof Size) {
Size size = (Size) o;
return mWidth == size.mWidth && mHeight == size.mHeight;
} else {
return false;
}
}
@Override
public String toString() {
return mWidth + "x" + mHeight;
}
@Override
public int hashCode() {
return mHeight ^ ((mWidth << (Integer.SIZE / 2)) | (mWidth >>> (Integer.SIZE / 2)));
}
@Override
public int compareTo(@NonNull Size another) {
return mWidth * mHeight - another.mWidth * another.mHeight;
}
}
/**
*
*/
public static class CameraException extends RuntimeException {
public CameraException() {
super();
}
public CameraException(String message) {
super(message);
}
public CameraException(String message, Throwable cause) {
super(message, cause);
}
public boolean isFatal() {
return false;
}
}
}