Added sessionType to switch between picture and video. Deprecated permissionsPolicy and captureMethod. Improved video sizing.

pull/1/head
Mattia Iavarone 7 years ago
parent c98ecf7e28
commit c429a99972
  1. 192
      camerakit/src/main/api16/com/flurgle/camerakit/Camera1.java
  2. 2
      camerakit/src/main/api21/com/flurgle/camerakit/Camera2.java
  3. 2
      camerakit/src/main/base/com/flurgle/camerakit/CameraImpl.java
  4. 6
      camerakit/src/main/base/com/flurgle/camerakit/TextureViewPreview.java
  5. 20
      camerakit/src/main/java/com/flurgle/camerakit/CameraKit.java
  6. 272
      camerakit/src/main/java/com/flurgle/camerakit/CameraView.java
  7. 5
      camerakit/src/main/res/values/attrs.xml
  8. 1
      camerakit/src/main/types/com/flurgle/camerakit/Method.java
  9. 1
      camerakit/src/main/types/com/flurgle/camerakit/Permissions.java

@ -5,7 +5,9 @@ import android.graphics.YuvImage;
import android.hardware.Camera; import android.hardware.Camera;
import android.media.CamcorderProfile; import android.media.CamcorderProfile;
import android.media.MediaRecorder; import android.media.MediaRecorder;
import android.os.Build;
import android.os.Handler; import android.os.Handler;
import android.support.annotation.NonNull;
import android.util.Log; import android.util.Log;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.view.MotionEvent; import android.view.MotionEvent;
@ -21,9 +23,9 @@ import java.util.List;
import static com.flurgle.camerakit.CameraKit.Constants.FOCUS_CONTINUOUS; import static com.flurgle.camerakit.CameraKit.Constants.FOCUS_CONTINUOUS;
import static com.flurgle.camerakit.CameraKit.Constants.FOCUS_OFF; import static com.flurgle.camerakit.CameraKit.Constants.FOCUS_OFF;
import static com.flurgle.camerakit.CameraKit.Constants.FOCUS_TAP; import static com.flurgle.camerakit.CameraKit.Constants.FOCUS_TAP;
import static com.flurgle.camerakit.CameraKit.Constants.CAPTURE_METHOD_STANDARD;
import static com.flurgle.camerakit.CameraKit.Constants.CAPTURE_METHOD_FRAME;
import static com.flurgle.camerakit.CameraKit.Constants.FOCUS_TAP_WITH_MARKER; import static com.flurgle.camerakit.CameraKit.Constants.FOCUS_TAP_WITH_MARKER;
import static com.flurgle.camerakit.CameraKit.Constants.SESSION_TYPE_PICTURE;
import static com.flurgle.camerakit.CameraKit.Constants.SESSION_TYPE_VIDEO;
@SuppressWarnings("deprecation") @SuppressWarnings("deprecation")
class Camera1 extends CameraImpl { class Camera1 extends CameraImpl {
@ -53,10 +55,10 @@ class Camera1 extends CameraImpl {
@Facing private int mFacing; @Facing private int mFacing;
@Flash private int mFlash; @Flash private int mFlash;
@Focus private int mFocus; @Focus private int mFocus;
@Method private int mMethod;
@ZoomMode private int mZoom; @ZoomMode private int mZoom;
@VideoQuality private int mVideoQuality; @VideoQuality private int mVideoQuality;
@WhiteBalance private int mWhiteBalance; @WhiteBalance private int mWhiteBalance;
@SessionType private int mSessionType;
private double mLatitude; private double mLatitude;
private double mLongitude; private double mLongitude;
@ -70,7 +72,7 @@ class Camera1 extends CameraImpl {
public void onPreviewSurfaceChanged() { public void onPreviewSurfaceChanged() {
if (mCamera != null) { if (mCamera != null) {
setupPreview(); setupPreview();
collectCameraSizes(); computeCameraSizes();
adjustCameraParameters(); adjustCameraParameters();
} }
} }
@ -125,6 +127,18 @@ class Camera1 extends CameraImpl {
this.mDeviceOrientation = deviceOrientation; this.mDeviceOrientation = deviceOrientation;
} }
@Override
void setSessionType(@SessionType int sessionType) {
if (sessionType != mSessionType) {
mSessionType = sessionType;
if (isCameraOpened()) {
stop();
start();
}
}
}
@Override @Override
void setLocation(double latitude, double longitude) { void setLocation(double latitude, double longitude) {
mLatitude = latitude; mLatitude = latitude;
@ -234,11 +248,6 @@ class Camera1 extends CameraImpl {
} }
} }
@Override
void setMethod(@Method int method) {
this.mMethod = method;
}
@Override @Override
void setZoom(@ZoomMode int zoom) { void setZoom(@ZoomMode int zoom) {
this.mZoom = zoom; this.mZoom = zoom;
@ -251,8 +260,8 @@ class Camera1 extends CameraImpl {
@Override @Override
void captureImage() { void captureImage() {
switch (mMethod) { switch (mSessionType) {
case CAPTURE_METHOD_STANDARD: case SESSION_TYPE_PICTURE:
// Null check required for camera here as is briefly null when View is detached // Null check required for camera here as is briefly null when View is detached
if (!capturingImage && mCamera != null) { if (!capturingImage && mCamera != null) {
// Set boolean to wait for image callback // Set boolean to wait for image callback
@ -270,13 +279,14 @@ class Camera1 extends CameraImpl {
camera.startPreview(); camera.startPreview();
} }
}); });
} } else {
else {
Log.w(TAG, "Unable, waiting for picture to be taken"); Log.w(TAG, "Unable, waiting for picture to be taken");
} }
break; break;
case CAPTURE_METHOD_FRAME: case SESSION_TYPE_VIDEO:
// If we are in a video session, camera captures are fast captures coming
// from the preview stream.
// TODO: will calculateCaptureRotation work here? It's the only usage. Test... // TODO: will calculateCaptureRotation work here? It's the only usage. Test...
mCamera.setOneShotPreviewCallback(new Camera.PreviewCallback() { mCamera.setOneShotPreviewCallback(new Camera.PreviewCallback() {
@Override @Override
@ -293,22 +303,6 @@ class Camera1 extends CameraImpl {
} }
} }
@Override
void startVideo() {
initMediaRecorder();
prepareMediaRecorder();
mMediaRecorder.start();
}
@Override
void endVideo() {
mMediaRecorder.stop();
mMediaRecorder.release();
mMediaRecorder = null;
mCameraListener.onVideoTaken(mVideoFile);
}
@Override @Override
Size getCaptureSize() { Size getCaptureSize() {
return mCaptureSize; return mCaptureSize;
@ -346,7 +340,7 @@ class Camera1 extends CameraImpl {
mCameraParameters = mCamera.getParameters(); mCameraParameters = mCamera.getParameters();
collectCameraProperties(); collectCameraProperties();
collectCameraSizes(); computeCameraSizes();
adjustCameraParameters(); adjustCameraParameters();
mCamera.setDisplayOrientation(computeCameraToDisplayOffset()); mCamera.setDisplayOrientation(computeCameraToDisplayOffset());
mCameraListener.onCameraOpened(); mCameraListener.onCameraOpened();
@ -401,22 +395,22 @@ class Camera1 extends CameraImpl {
* dimensions. * dimensions.
* But when it does, the {@link PreviewImpl.OnPreviewSurfaceChangedCallback} should be called, * But when it does, the {@link PreviewImpl.OnPreviewSurfaceChangedCallback} should be called,
* and this should be refreshed. * and this should be refreshed.
*
*
* TODO: either camera or video.
*/ */
private void collectCameraSizes() { private void computeCameraSizes() {
mCameraParameters.setRecordingHint(mSessionType == SESSION_TYPE_VIDEO);
List<Size> previewSizes = sizesFromList(mCameraParameters.getSupportedPreviewSizes()); List<Size> previewSizes = sizesFromList(mCameraParameters.getSupportedPreviewSizes());
if (mSessionType == SESSION_TYPE_PICTURE) {
// Choose the max size.
List<Size> captureSizes = sizesFromList(mCameraParameters.getSupportedPictureSizes()); List<Size> captureSizes = sizesFromList(mCameraParameters.getSupportedPictureSizes());
/* video stuff: CamcorderProfile camcorderProfile = getCamcorderProfile(mVideoQuality);
mCaptureSize = getSizeWithClosestRatio(
(videoSizes == null || videoSizes.isEmpty()) ? previewSizes : videoSizes,
camcorderProfile.videoFrameWidth, camcorderProfile.videoFrameHeight); */
Size surfaceSize = mPreview.getSurfaceSize();
mCaptureSize = Collections.max(captureSizes); mCaptureSize = Collections.max(captureSizes);
mPreviewSize = computePreviewSize(previewSizes, mCaptureSize, surfaceSize); } else {
// Choose according to developer choice in setVideoQuality.
// The Camcorder internally checks for cameraParameters.getSupportedVideoSizes() etc.
// So its output is our output.
CamcorderProfile camcorderProfile = getCamcorderProfile(mVideoQuality);
mCaptureSize = new Size(camcorderProfile.videoFrameWidth, camcorderProfile.videoFrameHeight);
}
mPreviewSize = computePreviewSize(previewSizes, mCaptureSize, mPreview.getSurfaceSize());
} }
@ -442,6 +436,37 @@ class Camera1 extends CameraImpl {
} }
@Override
void startVideo() {
if (mSessionType == SESSION_TYPE_VIDEO) {
initMediaRecorder();
try {
mMediaRecorder.prepare();
} catch (Exception e) {
e.printStackTrace();
mVideoFile = null;
endVideo();
return;
}
mMediaRecorder.start();
} else {
throw new IllegalStateException("Can't record video while session type is picture");
}
}
@Override
void endVideo() {
mMediaRecorder.stop();
mMediaRecorder.release();
mMediaRecorder = null;
if (mVideoFile != null) {
mCameraListener.onVideoTaken(mVideoFile);
mVideoFile = null;
}
}
private void initMediaRecorder() { private void initMediaRecorder() {
mMediaRecorder = new MediaRecorder(); mMediaRecorder = new MediaRecorder();
mCamera.unlock(); mCamera.unlock();
@ -455,75 +480,57 @@ class Camera1 extends CameraImpl {
mVideoFile = new File(mPreview.getView().getContext().getExternalFilesDir(null), "video.mp4"); mVideoFile = new File(mPreview.getView().getContext().getExternalFilesDir(null), "video.mp4");
mMediaRecorder.setOutputFile(mVideoFile.getAbsolutePath()); mMediaRecorder.setOutputFile(mVideoFile.getAbsolutePath());
mMediaRecorder.setOrientationHint(computeCameraToDisplayOffset()); mMediaRecorder.setOrientationHint(computeCameraToDisplayOffset()); // TODO is this correct? Should we use exif orientation? Maybe not.
mMediaRecorder.setVideoSize(mCaptureSize.getWidth(), mCaptureSize.getHeight()); // Not needed. mMediaRecorder.setPreviewDisplay(mPreview.getSurface());
} }
private void prepareMediaRecorder() {
try {
mMediaRecorder.prepare();
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
@NonNull
private CamcorderProfile getCamcorderProfile(@VideoQuality int videoQuality) { private CamcorderProfile getCamcorderProfile(@VideoQuality int videoQuality) {
CamcorderProfile camcorderProfile = null;
switch (videoQuality) { switch (videoQuality) {
case CameraKit.Constants.VIDEO_QUALITY_QVGA: case CameraKit.Constants.VIDEO_QUALITY_HIGHEST:
if (CamcorderProfile.hasProfile(mCameraId, CamcorderProfile.QUALITY_QVGA)) { return CamcorderProfile.get(mCameraId, CamcorderProfile.QUALITY_HIGH);
camcorderProfile = CamcorderProfile.get(mCameraId, CamcorderProfile.QUALITY_QVGA);
} else { case CameraKit.Constants.VIDEO_QUALITY_2160P:
return getCamcorderProfile(CameraKit.Constants.VIDEO_QUALITY_LOWEST); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP &&
CamcorderProfile.hasProfile(CamcorderProfile.QUALITY_2160P)) {
return CamcorderProfile.get(mCameraId, CamcorderProfile.QUALITY_2160P);
} }
break; // Don't break.
case CameraKit.Constants.VIDEO_QUALITY_480P: case CameraKit.Constants.VIDEO_QUALITY_1080P:
if (CamcorderProfile.hasProfile(mCameraId, CamcorderProfile.QUALITY_480P)) { if (CamcorderProfile.hasProfile(mCameraId, CamcorderProfile.QUALITY_1080P)) {
camcorderProfile = CamcorderProfile.get(mCameraId, CamcorderProfile.QUALITY_480P); return CamcorderProfile.get(mCameraId, CamcorderProfile.QUALITY_1080P);
} else {
return getCamcorderProfile(CameraKit.Constants.VIDEO_QUALITY_QVGA);
} }
break; // Don't break.
case CameraKit.Constants.VIDEO_QUALITY_720P: case CameraKit.Constants.VIDEO_QUALITY_720P:
if (CamcorderProfile.hasProfile(mCameraId, CamcorderProfile.QUALITY_720P)) { if (CamcorderProfile.hasProfile(mCameraId, CamcorderProfile.QUALITY_720P)) {
camcorderProfile = CamcorderProfile.get(mCameraId, CamcorderProfile.QUALITY_720P); return CamcorderProfile.get(mCameraId, CamcorderProfile.QUALITY_720P);
} else {
return getCamcorderProfile(CameraKit.Constants.VIDEO_QUALITY_480P);
} }
break; // Don't break.
case CameraKit.Constants.VIDEO_QUALITY_1080P: case CameraKit.Constants.VIDEO_QUALITY_480P:
if (CamcorderProfile.hasProfile(mCameraId, CamcorderProfile.QUALITY_1080P)) { if (CamcorderProfile.hasProfile(mCameraId, CamcorderProfile.QUALITY_480P)) {
camcorderProfile = CamcorderProfile.get(mCameraId, CamcorderProfile.QUALITY_1080P); return CamcorderProfile.get(mCameraId, CamcorderProfile.QUALITY_480P);
} else {
return getCamcorderProfile(CameraKit.Constants.VIDEO_QUALITY_720P);
} }
break; // Don't break.
case CameraKit.Constants.VIDEO_QUALITY_2160P: case CameraKit.Constants.VIDEO_QUALITY_QVGA:
try { if (CamcorderProfile.hasProfile(mCameraId, CamcorderProfile.QUALITY_QVGA)) {
camcorderProfile = CamcorderProfile.get(mCameraId, CamcorderProfile.QUALITY_2160P); return CamcorderProfile.get(mCameraId, CamcorderProfile.QUALITY_QVGA);
} catch (Exception e) {
return getCamcorderProfile(CameraKit.Constants.VIDEO_QUALITY_HIGHEST);
} }
break; // Don't break.
case CameraKit.Constants.VIDEO_QUALITY_HIGHEST:
camcorderProfile = CamcorderProfile.get(mCameraId, CamcorderProfile.QUALITY_HIGH);
break;
case CameraKit.Constants.VIDEO_QUALITY_LOWEST: case CameraKit.Constants.VIDEO_QUALITY_LOWEST:
camcorderProfile = CamcorderProfile.get(mCameraId, CamcorderProfile.QUALITY_LOW); default:
break; // Fallback to lowest.
return CamcorderProfile.get(mCameraId, CamcorderProfile.QUALITY_LOW);
} }
return camcorderProfile;
} }
void setTapToAutofocusListener(Camera.AutoFocusCallback callback) { void setTapToAutofocusListener(Camera.AutoFocusCallback callback) {
if (this.mFocus != FOCUS_TAP) { if (this.mFocus != FOCUS_TAP) {
throw new IllegalArgumentException("Please set the camera to FOCUS_TAP."); throw new IllegalArgumentException("Please set the camera to FOCUS_TAP.");
@ -663,6 +670,7 @@ class Camera1 extends CameraImpl {
/** /**
* Returns a list of {@link Size} out of Camera.Sizes. * Returns a list of {@link Size} out of Camera.Sizes.
*/ */
@Nullable
private static List<Size> sizesFromList(List<Camera.Size> sizes) { private static List<Size> sizesFromList(List<Camera.Size> sizes) {
if (sizes == null) return null; if (sizes == null) return null;
List<Size> result = new ArrayList<>(sizes.size()); List<Size> result = new ArrayList<>(sizes.size());
@ -688,7 +696,7 @@ class Camera1 extends CameraImpl {
List<Size> bigEnoughAndConsistent = new ArrayList<>(5); List<Size> bigEnoughAndConsistent = new ArrayList<>(5);
final AspectRatio targetRatio = AspectRatio.of(captureSize.getWidth(), captureSize.getHeight()); final AspectRatio targetRatio = AspectRatio.of(captureSize.getWidth(), captureSize.getHeight());
final Size targetSize = captureSize; final Size targetSize = surfaceSize;
for (Size size : sizes) { for (Size size : sizes) {
AspectRatio ratio = AspectRatio.of(size.getWidth(), size.getHeight()); AspectRatio ratio = AspectRatio.of(size.getWidth(), size.getHeight());
if (ratio.equals(targetRatio)) { if (ratio.equals(targetRatio)) {

@ -147,7 +147,7 @@ class Camera2 extends CameraImpl {
} }
@Override @Override
void setMethod(@Method int method) { void setSessionType(@SessionType int sessionType) {
} }

@ -21,10 +21,10 @@ abstract class CameraImpl {
abstract void setFacing(@Facing int facing); abstract void setFacing(@Facing int facing);
abstract void setFlash(@Flash int flash); abstract void setFlash(@Flash int flash);
abstract void setFocus(@Focus int focus); abstract void setFocus(@Focus int focus);
abstract void setMethod(@Method int method);
abstract void setZoom(@ZoomMode int zoom); abstract void setZoom(@ZoomMode int zoom);
abstract void setVideoQuality(@VideoQuality int videoQuality); abstract void setVideoQuality(@VideoQuality int videoQuality);
abstract void setWhiteBalance(@WhiteBalance int whiteBalance); abstract void setWhiteBalance(@WhiteBalance int whiteBalance);
abstract void setSessionType(@SessionType int sessionType);
abstract void setLocation(double latitude, double longitude); abstract void setLocation(double latitude, double longitude);
abstract void captureImage(); abstract void captureImage();

@ -12,6 +12,7 @@ import android.view.ViewGroup;
class TextureViewPreview extends PreviewImpl { class TextureViewPreview extends PreviewImpl {
private final TextureView mTextureView; private final TextureView mTextureView;
private Surface mSurface;
TextureViewPreview(Context context, ViewGroup parent) { TextureViewPreview(Context context, ViewGroup parent) {
final View view = View.inflate(context, R.layout.texture_view, parent); // MATCH_PARENT final View view = View.inflate(context, R.layout.texture_view, parent); // MATCH_PARENT
@ -45,7 +46,10 @@ class TextureViewPreview extends PreviewImpl {
@Override @Override
Surface getSurface() { Surface getSurface() {
return new Surface(getSurfaceTexture()); if (mSurface == null) { // Check if valid?
mSurface = new Surface(getSurfaceTexture());
}
return mSurface;
} }
@Override @Override

@ -32,8 +32,11 @@ public class CameraKit {
public static final int ZOOM_OFF = 0; public static final int ZOOM_OFF = 0;
public static final int ZOOM_PINCH = 1; public static final int ZOOM_PINCH = 1;
public static final int CAPTURE_METHOD_STANDARD = 0; public static final int SESSION_TYPE_PICTURE = 0;
public static final int CAPTURE_METHOD_FRAME = 1; public static final int SESSION_TYPE_VIDEO = 1;
@Deprecated public static final int CAPTURE_METHOD_STANDARD = 0;
@Deprecated public static final int CAPTURE_METHOD_FRAME = 1;
/** /**
@ -41,15 +44,19 @@ public class CameraKit {
* With this configuration, it is mandatory that the developer adds * With this configuration, it is mandatory that the developer adds
* <uses-permission android:name="android.permission.RECORD_AUDIO" /> * <uses-permission android:name="android.permission.RECORD_AUDIO" />
* to its manifest, or the app will crash. * to its manifest, or the app will crash.
*
* @deprecated this does nothing.
*/ */
public static final int PERMISSIONS_VIDEO = 0; @Deprecated public static final int PERMISSIONS_VIDEO = 0;
/** /**
* This means the app will be requesting the Camera permissions on Marshmallow+. * This means the app will be requesting the Camera permissions on Marshmallow+.
* On older APIs, this is not important, since the Camera permission is already * On older APIs, this is not important, since the Camera permission is already
* declared in the manifest by the library. * declared in the manifest by the library.
*
* @deprecated this does nothing.
*/ */
public static final int PERMISSIONS_PICTURE = 1; @Deprecated public static final int PERMISSIONS_PICTURE = 1;
public static final int VIDEO_QUALITY_480P = 0; public static final int VIDEO_QUALITY_480P = 0;
public static final int VIDEO_QUALITY_720P = 1; public static final int VIDEO_QUALITY_720P = 1;
@ -72,10 +79,11 @@ public class CameraKit {
static final int DEFAULT_FLASH = Constants.FLASH_OFF; static final int DEFAULT_FLASH = Constants.FLASH_OFF;
static final int DEFAULT_FOCUS = Constants.FOCUS_CONTINUOUS; static final int DEFAULT_FOCUS = Constants.FOCUS_CONTINUOUS;
static final int DEFAULT_ZOOM = Constants.ZOOM_OFF; static final int DEFAULT_ZOOM = Constants.ZOOM_OFF;
static final int DEFAULT_METHOD = Constants.CAPTURE_METHOD_STANDARD; @Deprecated static final int DEFAULT_METHOD = Constants.CAPTURE_METHOD_STANDARD;
static final int DEFAULT_PERMISSIONS = Constants.PERMISSIONS_PICTURE; @Deprecated static final int DEFAULT_PERMISSIONS = Constants.PERMISSIONS_PICTURE;
static final int DEFAULT_VIDEO_QUALITY = Constants.VIDEO_QUALITY_480P; static final int DEFAULT_VIDEO_QUALITY = Constants.VIDEO_QUALITY_480P;
static final int DEFAULT_WHITE_BALANCE = Constants.WHITE_BALANCE_AUTO; static final int DEFAULT_WHITE_BALANCE = Constants.WHITE_BALANCE_AUTO;
static final int DEFAULT_SESSION_TYPE = Constants.SESSION_TYPE_PICTURE;
static final int DEFAULT_JPEG_QUALITY = 100; static final int DEFAULT_JPEG_QUALITY = 100;
static final boolean DEFAULT_CROP_OUTPUT = false; static final boolean DEFAULT_CROP_OUTPUT = false;
static final boolean DEFAULT_ADJUST_VIEW_BOUNDS = false; static final boolean DEFAULT_ADJUST_VIEW_BOUNDS = false;

@ -39,8 +39,8 @@ import static com.flurgle.camerakit.CameraKit.Constants.FLASH_AUTO;
import static com.flurgle.camerakit.CameraKit.Constants.FLASH_OFF; import static com.flurgle.camerakit.CameraKit.Constants.FLASH_OFF;
import static com.flurgle.camerakit.CameraKit.Constants.FLASH_ON; import static com.flurgle.camerakit.CameraKit.Constants.FLASH_ON;
import static com.flurgle.camerakit.CameraKit.Constants.FLASH_TORCH; import static com.flurgle.camerakit.CameraKit.Constants.FLASH_TORCH;
import static com.flurgle.camerakit.CameraKit.Constants.PERMISSIONS_PICTURE; import static com.flurgle.camerakit.CameraKit.Constants.SESSION_TYPE_PICTURE;
import static com.flurgle.camerakit.CameraKit.Constants.PERMISSIONS_VIDEO; import static com.flurgle.camerakit.CameraKit.Constants.SESSION_TYPE_VIDEO;
/** /**
* The CameraView implements the LifecycleObserver interface for ease of use. To take advantage of * The CameraView implements the LifecycleObserver interface for ease of use. To take advantage of
@ -79,15 +79,16 @@ public class CameraView extends FrameLayout implements LifecycleObserver {
@Facing private int mFacing; @Facing private int mFacing;
@Flash private int mFlash; @Flash private int mFlash;
@Focus private int mFocus; @Focus private int mFocus;
@Method private int mMethod; // @Method private int mMethod;
@ZoomMode private int mZoom; @ZoomMode private int mZoom;
@Permissions private int mPermissions; // @Permissions private int mPermissions;
@SessionType private int mSessionType;
@VideoQuality private int mVideoQuality; @VideoQuality private int mVideoQuality;
@WhiteBalance private int mWhiteBalance; @WhiteBalance private int mWhiteBalance;
private int mJpegQuality; private int mJpegQuality;
private boolean mCropOutput; private boolean mCropOutput;
private boolean mAdjustViewBounds; private boolean mAdjustViewBounds;
private CameraListenerMiddleWare mCameraListener; private CameraListenerWrapper mCameraListener;
private OrientationHelper mOrientationHelper; private OrientationHelper mOrientationHelper;
private CameraImpl mCameraImpl; private CameraImpl mCameraImpl;
private PreviewImpl mPreviewImpl; private PreviewImpl mPreviewImpl;
@ -113,9 +114,10 @@ public class CameraView extends FrameLayout implements LifecycleObserver {
mFacing = a.getInteger(R.styleable.CameraView_cameraFacing, CameraKit.Defaults.DEFAULT_FACING); mFacing = a.getInteger(R.styleable.CameraView_cameraFacing, CameraKit.Defaults.DEFAULT_FACING);
mFlash = a.getInteger(R.styleable.CameraView_cameraFlash, CameraKit.Defaults.DEFAULT_FLASH); mFlash = a.getInteger(R.styleable.CameraView_cameraFlash, CameraKit.Defaults.DEFAULT_FLASH);
mFocus = a.getInteger(R.styleable.CameraView_cameraFocus, CameraKit.Defaults.DEFAULT_FOCUS); mFocus = a.getInteger(R.styleable.CameraView_cameraFocus, CameraKit.Defaults.DEFAULT_FOCUS);
mMethod = a.getInteger(R.styleable.CameraView_cameraCaptureMethod, CameraKit.Defaults.DEFAULT_METHOD); // mMethod = a.getInteger(R.styleable.CameraView_cameraCaptureMethod, CameraKit.Defaults.DEFAULT_METHOD);
// mPermissions = a.getInteger(R.styleable.CameraView_cameraPermissionPolicy, CameraKit.Defaults.DEFAULT_PERMISSIONS);
mSessionType = a.getInteger(R.styleable.CameraView_cameraSessionType, CameraKit.Defaults.DEFAULT_SESSION_TYPE);
mZoom = a.getInteger(R.styleable.CameraView_cameraZoomMode, CameraKit.Defaults.DEFAULT_ZOOM); mZoom = a.getInteger(R.styleable.CameraView_cameraZoomMode, CameraKit.Defaults.DEFAULT_ZOOM);
mPermissions = a.getInteger(R.styleable.CameraView_cameraPermissionPolicy, CameraKit.Defaults.DEFAULT_PERMISSIONS);
mWhiteBalance = a.getInteger(R.styleable.CameraView_cameraWhiteBalance, CameraKit.Defaults.DEFAULT_WHITE_BALANCE); mWhiteBalance = a.getInteger(R.styleable.CameraView_cameraWhiteBalance, CameraKit.Defaults.DEFAULT_WHITE_BALANCE);
mVideoQuality = a.getInteger(R.styleable.CameraView_cameraVideoQuality, CameraKit.Defaults.DEFAULT_VIDEO_QUALITY); mVideoQuality = a.getInteger(R.styleable.CameraView_cameraVideoQuality, CameraKit.Defaults.DEFAULT_VIDEO_QUALITY);
mJpegQuality = a.getInteger(R.styleable.CameraView_cameraJpegQuality, CameraKit.Defaults.DEFAULT_JPEG_QUALITY); mJpegQuality = a.getInteger(R.styleable.CameraView_cameraJpegQuality, CameraKit.Defaults.DEFAULT_JPEG_QUALITY);
@ -126,7 +128,7 @@ public class CameraView extends FrameLayout implements LifecycleObserver {
} }
} }
mCameraListener = new CameraListenerMiddleWare(); mCameraListener = new CameraListenerWrapper();
mPreviewImpl = new TextureViewPreview(context, this); mPreviewImpl = new TextureViewPreview(context, this);
mCameraImpl = new Camera1(mCameraListener, mPreviewImpl); mCameraImpl = new Camera1(mCameraListener, mPreviewImpl);
@ -135,9 +137,10 @@ public class CameraView extends FrameLayout implements LifecycleObserver {
setFacing(mFacing); setFacing(mFacing);
setFlash(mFlash); setFlash(mFlash);
setFocus(mFocus); setFocus(mFocus);
setCaptureMethod(mMethod); setSessionType(mSessionType);
// setCaptureMethod(mMethod);
// setPermissionPolicy(mPermissions);
setZoom(mZoom); setZoom(mZoom);
setPermissionPolicy(mPermissions);
setVideoQuality(mVideoQuality); setVideoQuality(mVideoQuality);
setWhiteBalance(mWhiteBalance); setWhiteBalance(mWhiteBalance);
@ -311,32 +314,70 @@ public class CameraView extends FrameLayout implements LifecycleObserver {
// Already started, do nothing. // Already started, do nothing.
return; return;
} }
checkPermissionPolicyOrThrow();
if (checkPermissions()) {
mIsStarted = true;
run(new Runnable() {
@Override
public void run() {
mCameraImpl.start();
}
});
}
}
/**
* Checks that we have appropriate permissions for this session type.
* Throws if session = audio and manifest did not add the microphone permissions.
* @return true if we can go on, false otherwise.
*/
private boolean checkPermissions() {
checkPermissionsManifestOrThrow();
int cameraCheck = ContextCompat.checkSelfPermission(getContext(), Manifest.permission.CAMERA); int cameraCheck = ContextCompat.checkSelfPermission(getContext(), Manifest.permission.CAMERA);
int audioCheck = ContextCompat.checkSelfPermission(getContext(), Manifest.permission.RECORD_AUDIO); int audioCheck = ContextCompat.checkSelfPermission(getContext(), Manifest.permission.RECORD_AUDIO);
switch (mPermissions) { switch (mSessionType) {
case PERMISSIONS_VIDEO: case SESSION_TYPE_VIDEO:
if (cameraCheck != PackageManager.PERMISSION_GRANTED || audioCheck != PackageManager.PERMISSION_GRANTED) { if (cameraCheck != PackageManager.PERMISSION_GRANTED || audioCheck != PackageManager.PERMISSION_GRANTED) {
requestPermissions(true, true); requestPermissions(true, true);
return; return false;
} }
break; break;
case PERMISSIONS_PICTURE: case SESSION_TYPE_PICTURE:
if (cameraCheck != PackageManager.PERMISSION_GRANTED) { if (cameraCheck != PackageManager.PERMISSION_GRANTED) {
requestPermissions(true, false); requestPermissions(true, false);
return; return false;
} }
break; break;
} }
return true;
}
mIsStarted = true;
run(new Runnable() { /**
@Override * If mSessionType == SESSION_TYPE_VIDEO we will ask for RECORD_AUDIO permission.
public void run() { * If the developer did not add this to its manifest, throw and fire warnings.
mCameraImpl.start(); * (Hoping this is not cought elsewhere... we should test).
*/
private void checkPermissionsManifestOrThrow() {
if (mSessionType == SESSION_TYPE_VIDEO) {
try {
PackageManager manager = getContext().getPackageManager();
PackageInfo info = manager.getPackageInfo(getContext().getPackageName(), PackageManager.GET_PERMISSIONS);
for (String requestedPermission : info.requestedPermissions) {
if (requestedPermission.equals(Manifest.permission.RECORD_AUDIO)) {
return;
}
}
String message = "When the session type is set to video, the RECORD_AUDIO permission " +
"should be added to the application manifest file.";
Log.w(TAG, message);
throw new IllegalStateException(message);
} catch (PackageManager.NameNotFoundException e) {
// Not possible.
}
} }
});
} }
public void stop() { public void stop() {
@ -349,10 +390,17 @@ public class CameraView extends FrameLayout implements LifecycleObserver {
} }
public void destroy() { public void destroy() {
mCameraListener = new CameraListenerWrapper(); // Release inner listener.
// This might be useless, but no time to think about it now. // This might be useless, but no time to think about it now.
sWorkerHandler = null; sWorkerHandler = null;
} }
/**
* If present, returns a collection of extra properties from the current camera
* session.
* @return an ExtraProperties object.
*/
@Nullable @Nullable
public ExtraProperties getExtraProperties() { public ExtraProperties getExtraProperties() {
return mCameraImpl.getExtraProperties(); return mCameraImpl.getExtraProperties();
@ -386,11 +434,15 @@ public class CameraView extends FrameLayout implements LifecycleObserver {
return mWhiteBalance; return mWhiteBalance;
} }
@Facing
public int getFacing() {
return mFacing;
}
/**
* Sets which camera sensor should be used.
*
* @see CameraKit.Constants#FACING_FRONT
* @see CameraKit.Constants#FACING_BACK
*
* @param facing a facing value.
*/
public void setFacing(@Facing final int facing) { public void setFacing(@Facing final int facing) {
this.mFacing = facing; this.mFacing = facing;
run(new Runnable() { run(new Runnable() {
@ -401,16 +453,53 @@ public class CameraView extends FrameLayout implements LifecycleObserver {
}); });
} }
/**
* Gets the facing camera currently being used.
* @return a facing value.
*/
@Facing
public int getFacing() {
return mFacing;
}
/**
* Sets the flash mode.
*
* @see CameraKit.Constants#FLASH_OFF
* @see CameraKit.Constants#FLASH_ON
* @see CameraKit.Constants#FLASH_AUTO
* @see CameraKit.Constants#FLASH_TORCH
* @param flash desired flash mode.
*/
public void setFlash(@Flash int flash) { public void setFlash(@Flash int flash) {
this.mFlash = flash; this.mFlash = flash;
mCameraImpl.setFlash(flash); mCameraImpl.setFlash(flash);
} }
/**
* Gets the current flash mode.
* @return a flash mode
*/
@Flash @Flash
public int getFlash() { public int getFlash() {
return mFlash; return mFlash;
} }
/**
* Sets the current focus behavior.
*
* @see CameraKit.Constants#FOCUS_CONTINUOUS
* @see CameraKit.Constants#FOCUS_OFF
* @see CameraKit.Constants#FOCUS_TAP
* @see CameraKit.Constants#FOCUS_TAP_WITH_MARKER
* @param focus a Focus value.
*/
public void setFocus(@Focus int focus) { public void setFocus(@Focus int focus) {
this.mFocus = focus; this.mFocus = focus;
if (this.mFocus == CameraKit.Constants.FOCUS_TAP_WITH_MARKER) { if (this.mFocus == CameraKit.Constants.FOCUS_TAP_WITH_MARKER) {
@ -421,23 +510,54 @@ public class CameraView extends FrameLayout implements LifecycleObserver {
mCameraImpl.setFocus(mFocus); mCameraImpl.setFocus(mFocus);
} }
/**
* This does nothing.
* @param method no-op
* @deprecated
*/
@Deprecated
public void setCaptureMethod(@Method int method) { public void setCaptureMethod(@Method int method) {
this.mMethod = method;
mCameraImpl.setMethod(mMethod);
} }
public void setZoom(@ZoomMode int zoom) {
this.mZoom = zoom;
mCameraImpl.setZoom(mZoom);
}
/** /**
* Sets permission policy. * This does nothing.
* @param permissions desired policy, either picture or video. * @param permissions no-op
* @deprecated
*/ */
@Deprecated
public void setPermissionPolicy(@Permissions int permissions) { public void setPermissionPolicy(@Permissions int permissions) {
this.mPermissions = permissions; }
checkPermissionPolicyOrThrow();
/**
* Set the current session type to either picture or video.
*
* @see CameraKit.Constants#SESSION_TYPE_PICTURE
* @see CameraKit.Constants#SESSION_TYPE_VIDEO
*
* @param sessionType desired session type.
*/
public void setSessionType(@SessionType int sessionType) {
if (sessionType == mSessionType || !mIsStarted) {
// Check did took place, or will happen on start().
mSessionType = sessionType;
mCameraImpl.setSessionType(sessionType);
} else if (checkPermissions()) {
// Camera is running. CameraImpl setSessionType will do the trick.
mSessionType = sessionType;
mCameraImpl.setSessionType(sessionType);
} else {
// This means that the audio permission is being asked.
// Stop the camera so it can be restarted by the developer onPermissionResult.
// Developer must also set the session type again...
// Not ideal but good for now.
stop();
}
} }
public void setVideoQuality(@VideoQuality int videoQuality) { public void setVideoQuality(@VideoQuality int videoQuality) {
@ -445,6 +565,13 @@ public class CameraView extends FrameLayout implements LifecycleObserver {
mCameraImpl.setVideoQuality(mVideoQuality); mCameraImpl.setVideoQuality(mVideoQuality);
} }
public void setZoom(@ZoomMode int zoom) {
this.mZoom = zoom;
mCameraImpl.setZoom(mZoom);
}
public void setJpegQuality(int jpegQuality) { public void setJpegQuality(int jpegQuality) {
this.mJpegQuality = jpegQuality; this.mJpegQuality = jpegQuality;
} }
@ -453,6 +580,13 @@ public class CameraView extends FrameLayout implements LifecycleObserver {
this.mCropOutput = cropOutput; this.mCropOutput = cropOutput;
} }
/**
* Toggles the facing value between {@link CameraKit.Constants#FACING_BACK}
* and {@link CameraKit.Constants#FACING_FRONT}.
*
* @return the new facing value
*/
@Facing @Facing
public int toggleFacing() { public int toggleFacing() {
switch (mFacing) { switch (mFacing) {
@ -488,8 +622,15 @@ public class CameraView extends FrameLayout implements LifecycleObserver {
return mFlash; return mFlash;
} }
/**
* Sets a {@link CameraListener} instance to be notified of all
* interesting events that will happen during the camera lifecycle.
*
* @param cameraListener a listener for events.
*/
public void setCameraListener(CameraListener cameraListener) { public void setCameraListener(CameraListener cameraListener) {
this.mCameraListener.setCameraListener(cameraListener); this.mCameraListener.wrapListener(cameraListener);
} }
public void captureImage() { public void captureImage() {
@ -544,87 +685,60 @@ public class CameraView extends FrameLayout implements LifecycleObserver {
} }
} }
private class CameraListenerMiddleWare extends CameraListener { private class CameraListenerWrapper extends CameraListener {
private CameraListener mCameraListener; private CameraListener mWrappedListener;
@Override @Override
public void onCameraOpened() { public void onCameraOpened() {
super.onCameraOpened(); super.onCameraOpened();
getCameraListener().onCameraOpened(); if (mWrappedListener == null) return;
mWrappedListener.onCameraOpened();
} }
@Override @Override
public void onCameraClosed() { public void onCameraClosed() {
super.onCameraClosed(); super.onCameraClosed();
getCameraListener().onCameraClosed(); if (mWrappedListener == null) return;
mWrappedListener.onCameraClosed();
} }
@Override @Override
public void onPictureTaken(byte[] jpeg) { public void onPictureTaken(byte[] jpeg) {
super.onPictureTaken(jpeg); super.onPictureTaken(jpeg);
if (mWrappedListener == null) return;
if (mCropOutput) { if (mCropOutput) {
AspectRatio outputRatio = AspectRatio.of(getWidth(), getHeight()); AspectRatio outputRatio = AspectRatio.of(getWidth(), getHeight());
getCameraListener().onPictureTaken(new CenterCrop(jpeg, outputRatio, mJpegQuality).getJpeg()); mWrappedListener.onPictureTaken(new CenterCrop(jpeg, outputRatio, mJpegQuality).getJpeg());
} else { } else {
getCameraListener().onPictureTaken(jpeg); mWrappedListener.onPictureTaken(jpeg);
} }
} }
@Override @Override
public void onPictureTaken(YuvImage yuv) { public void onPictureTaken(YuvImage yuv) {
super.onPictureTaken(yuv); super.onPictureTaken(yuv);
if (mWrappedListener == null) return;
if (mCropOutput) { if (mCropOutput) {
AspectRatio outputRatio = AspectRatio.of(getWidth(), getHeight()); AspectRatio outputRatio = AspectRatio.of(getWidth(), getHeight());
getCameraListener().onPictureTaken(new CenterCrop(yuv, outputRatio, mJpegQuality).getJpeg()); mWrappedListener.onPictureTaken(new CenterCrop(yuv, outputRatio, mJpegQuality).getJpeg());
} else { } else {
ByteArrayOutputStream out = new ByteArrayOutputStream(); ByteArrayOutputStream out = new ByteArrayOutputStream();
yuv.compressToJpeg(new Rect(0, 0, yuv.getWidth(), yuv.getHeight()), mJpegQuality, out); yuv.compressToJpeg(new Rect(0, 0, yuv.getWidth(), yuv.getHeight()), mJpegQuality, out);
getCameraListener().onPictureTaken(out.toByteArray()); mWrappedListener.onPictureTaken(out.toByteArray());
} }
} }
@Override @Override
public void onVideoTaken(File video) { public void onVideoTaken(File video) {
super.onVideoTaken(video); super.onVideoTaken(video);
getCameraListener().onVideoTaken(video); mWrappedListener.onVideoTaken(video);
} }
public void setCameraListener(@Nullable CameraListener cameraListener) { private void wrapListener(@Nullable CameraListener cameraListener) {
this.mCameraListener = cameraListener; mWrappedListener = cameraListener;
} }
@NonNull
public CameraListener getCameraListener() {
return mCameraListener != null ? mCameraListener : new CameraListener() {
};
}
} }
/**
* If mPermissions == PERMISSIONS_VIDEO we will ask for RECORD_AUDIO permission.
* If the developer did not add this to its manifest, throw and fire warnings.
* (Hoping this is not cought elsewhere... we should test).
*/
private void checkPermissionPolicyOrThrow() {
if (mPermissions == PERMISSIONS_VIDEO) {
try {
PackageManager manager = getContext().getPackageManager();
PackageInfo info = manager.getPackageInfo(getContext().getPackageName(), PackageManager.GET_PERMISSIONS);
for (String requestedPermission : info.requestedPermissions) {
if (requestedPermission.equals(Manifest.permission.RECORD_AUDIO)) {
return;
}
}
String message = "When the permission policy is PERMISSION_VIDEO, the RECORD_AUDIO permission " +
"should be added to the application manifest file.";
Log.w(TAG, message);
throw new IllegalStateException(message);
} catch (PackageManager.NameNotFoundException e) {
// Not possible.
}
}
}
} }

@ -43,6 +43,11 @@
<enum name="frame" value="1" /> <enum name="frame" value="1" />
</attr> </attr>
<attr name="cameraSessionType" format="enum">
<enum name="picture" value="0" />
<enum name="video" value="1" />
</attr>
<attr name="cameraPermissionPolicy" format="enum"> <attr name="cameraPermissionPolicy" format="enum">
<enum name="video" value="0" /> <enum name="video" value="0" />
<enum name="picture" value="1" /> <enum name="picture" value="1" />

@ -8,6 +8,7 @@ import java.lang.annotation.RetentionPolicy;
import static com.flurgle.camerakit.CameraKit.Constants.CAPTURE_METHOD_STANDARD; import static com.flurgle.camerakit.CameraKit.Constants.CAPTURE_METHOD_STANDARD;
import static com.flurgle.camerakit.CameraKit.Constants.CAPTURE_METHOD_FRAME; import static com.flurgle.camerakit.CameraKit.Constants.CAPTURE_METHOD_FRAME;
@Deprecated
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
@IntDef({CAPTURE_METHOD_STANDARD, CAPTURE_METHOD_FRAME}) @IntDef({CAPTURE_METHOD_STANDARD, CAPTURE_METHOD_FRAME})
public @interface Method { public @interface Method {

@ -8,6 +8,7 @@ import java.lang.annotation.RetentionPolicy;
import static com.flurgle.camerakit.CameraKit.Constants.PERMISSIONS_PICTURE; import static com.flurgle.camerakit.CameraKit.Constants.PERMISSIONS_PICTURE;
import static com.flurgle.camerakit.CameraKit.Constants.PERMISSIONS_VIDEO; import static com.flurgle.camerakit.CameraKit.Constants.PERMISSIONS_VIDEO;
@Deprecated
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
@IntDef({PERMISSIONS_VIDEO, PERMISSIONS_PICTURE}) @IntDef({PERMISSIONS_VIDEO, PERMISSIONS_PICTURE})
public @interface Permissions { public @interface Permissions {

Loading…
Cancel
Save