diff --git a/camerakit/src/main/api16/com/flurgle/camerakit/Camera1.java b/camerakit/src/main/api16/com/flurgle/camerakit/Camera1.java index ff0db475..e14a04ae 100644 --- a/camerakit/src/main/api16/com/flurgle/camerakit/Camera1.java +++ b/camerakit/src/main/api16/com/flurgle/camerakit/Camera1.java @@ -5,7 +5,9 @@ import android.graphics.YuvImage; import android.hardware.Camera; import android.media.CamcorderProfile; import android.media.MediaRecorder; +import android.os.Build; import android.os.Handler; +import android.support.annotation.NonNull; import android.util.Log; import android.support.annotation.Nullable; 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_OFF; 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.SESSION_TYPE_PICTURE; +import static com.flurgle.camerakit.CameraKit.Constants.SESSION_TYPE_VIDEO; @SuppressWarnings("deprecation") class Camera1 extends CameraImpl { @@ -53,10 +55,10 @@ class Camera1 extends CameraImpl { @Facing private int mFacing; @Flash private int mFlash; @Focus private int mFocus; - @Method private int mMethod; @ZoomMode private int mZoom; @VideoQuality private int mVideoQuality; @WhiteBalance private int mWhiteBalance; + @SessionType private int mSessionType; private double mLatitude; private double mLongitude; @@ -70,7 +72,7 @@ class Camera1 extends CameraImpl { public void onPreviewSurfaceChanged() { if (mCamera != null) { setupPreview(); - collectCameraSizes(); + computeCameraSizes(); adjustCameraParameters(); } } @@ -125,6 +127,18 @@ class Camera1 extends CameraImpl { this.mDeviceOrientation = deviceOrientation; } + + @Override + void setSessionType(@SessionType int sessionType) { + if (sessionType != mSessionType) { + mSessionType = sessionType; + if (isCameraOpened()) { + stop(); + start(); + } + } + } + @Override void setLocation(double latitude, double longitude) { mLatitude = latitude; @@ -234,11 +248,6 @@ class Camera1 extends CameraImpl { } } - @Override - void setMethod(@Method int method) { - this.mMethod = method; - } - @Override void setZoom(@ZoomMode int zoom) { this.mZoom = zoom; @@ -251,8 +260,8 @@ class Camera1 extends CameraImpl { @Override void captureImage() { - switch (mMethod) { - case CAPTURE_METHOD_STANDARD: + switch (mSessionType) { + case SESSION_TYPE_PICTURE: // Null check required for camera here as is briefly null when View is detached if (!capturingImage && mCamera != null) { // Set boolean to wait for image callback @@ -270,13 +279,14 @@ class Camera1 extends CameraImpl { camera.startPreview(); } }); - } - else { + } else { Log.w(TAG, "Unable, waiting for picture to be taken"); } 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... mCamera.setOneShotPreviewCallback(new Camera.PreviewCallback() { @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 Size getCaptureSize() { return mCaptureSize; @@ -346,7 +340,7 @@ class Camera1 extends CameraImpl { mCameraParameters = mCamera.getParameters(); collectCameraProperties(); - collectCameraSizes(); + computeCameraSizes(); adjustCameraParameters(); mCamera.setDisplayOrientation(computeCameraToDisplayOffset()); mCameraListener.onCameraOpened(); @@ -401,22 +395,22 @@ class Camera1 extends CameraImpl { * dimensions. * But when it does, the {@link PreviewImpl.OnPreviewSurfaceChangedCallback} should be called, * and this should be refreshed. - * - * - * TODO: either camera or video. */ - private void collectCameraSizes() { + private void computeCameraSizes() { + mCameraParameters.setRecordingHint(mSessionType == SESSION_TYPE_VIDEO); List previewSizes = sizesFromList(mCameraParameters.getSupportedPreviewSizes()); - List 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); - mPreviewSize = computePreviewSize(previewSizes, mCaptureSize, surfaceSize); + if (mSessionType == SESSION_TYPE_PICTURE) { + // Choose the max size. + List captureSizes = sizesFromList(mCameraParameters.getSupportedPictureSizes()); + mCaptureSize = Collections.max(captureSizes); + } 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() { mMediaRecorder = new MediaRecorder(); mCamera.unlock(); @@ -455,75 +480,57 @@ class Camera1 extends CameraImpl { mVideoFile = new File(mPreview.getView().getContext().getExternalFilesDir(null), "video.mp4"); mMediaRecorder.setOutputFile(mVideoFile.getAbsolutePath()); - mMediaRecorder.setOrientationHint(computeCameraToDisplayOffset()); - mMediaRecorder.setVideoSize(mCaptureSize.getWidth(), mCaptureSize.getHeight()); + mMediaRecorder.setOrientationHint(computeCameraToDisplayOffset()); // TODO is this correct? Should we use exif orientation? Maybe not. + // 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) { - CamcorderProfile camcorderProfile = null; switch (videoQuality) { - case CameraKit.Constants.VIDEO_QUALITY_QVGA: - if (CamcorderProfile.hasProfile(mCameraId, CamcorderProfile.QUALITY_QVGA)) { - camcorderProfile = CamcorderProfile.get(mCameraId, CamcorderProfile.QUALITY_QVGA); - } else { - return getCamcorderProfile(CameraKit.Constants.VIDEO_QUALITY_LOWEST); + case CameraKit.Constants.VIDEO_QUALITY_HIGHEST: + return CamcorderProfile.get(mCameraId, CamcorderProfile.QUALITY_HIGH); + + case CameraKit.Constants.VIDEO_QUALITY_2160P: + 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: - if (CamcorderProfile.hasProfile(mCameraId, CamcorderProfile.QUALITY_480P)) { - camcorderProfile = CamcorderProfile.get(mCameraId, CamcorderProfile.QUALITY_480P); - } else { - return getCamcorderProfile(CameraKit.Constants.VIDEO_QUALITY_QVGA); + case CameraKit.Constants.VIDEO_QUALITY_1080P: + if (CamcorderProfile.hasProfile(mCameraId, CamcorderProfile.QUALITY_1080P)) { + return CamcorderProfile.get(mCameraId, CamcorderProfile.QUALITY_1080P); } - break; + // Don't break. case CameraKit.Constants.VIDEO_QUALITY_720P: if (CamcorderProfile.hasProfile(mCameraId, CamcorderProfile.QUALITY_720P)) { - camcorderProfile = CamcorderProfile.get(mCameraId, CamcorderProfile.QUALITY_720P); - } else { - return getCamcorderProfile(CameraKit.Constants.VIDEO_QUALITY_480P); + return CamcorderProfile.get(mCameraId, CamcorderProfile.QUALITY_720P); } - break; + // Don't break. - case CameraKit.Constants.VIDEO_QUALITY_1080P: - if (CamcorderProfile.hasProfile(mCameraId, CamcorderProfile.QUALITY_1080P)) { - camcorderProfile = CamcorderProfile.get(mCameraId, CamcorderProfile.QUALITY_1080P); - } else { - return getCamcorderProfile(CameraKit.Constants.VIDEO_QUALITY_720P); + case CameraKit.Constants.VIDEO_QUALITY_480P: + if (CamcorderProfile.hasProfile(mCameraId, CamcorderProfile.QUALITY_480P)) { + return CamcorderProfile.get(mCameraId, CamcorderProfile.QUALITY_480P); } - break; + // Don't break. - case CameraKit.Constants.VIDEO_QUALITY_2160P: - try { - camcorderProfile = CamcorderProfile.get(mCameraId, CamcorderProfile.QUALITY_2160P); - } catch (Exception e) { - return getCamcorderProfile(CameraKit.Constants.VIDEO_QUALITY_HIGHEST); + case CameraKit.Constants.VIDEO_QUALITY_QVGA: + if (CamcorderProfile.hasProfile(mCameraId, CamcorderProfile.QUALITY_QVGA)) { + return CamcorderProfile.get(mCameraId, CamcorderProfile.QUALITY_QVGA); } - break; - - case CameraKit.Constants.VIDEO_QUALITY_HIGHEST: - camcorderProfile = CamcorderProfile.get(mCameraId, CamcorderProfile.QUALITY_HIGH); - break; + // Don't break. case CameraKit.Constants.VIDEO_QUALITY_LOWEST: - camcorderProfile = CamcorderProfile.get(mCameraId, CamcorderProfile.QUALITY_LOW); - break; + default: + // Fallback to lowest. + return CamcorderProfile.get(mCameraId, CamcorderProfile.QUALITY_LOW); } - - return camcorderProfile; } + void setTapToAutofocusListener(Camera.AutoFocusCallback callback) { if (this.mFocus != 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. */ + @Nullable private static List sizesFromList(List sizes) { if (sizes == null) return null; List result = new ArrayList<>(sizes.size()); @@ -688,7 +696,7 @@ class Camera1 extends CameraImpl { List bigEnoughAndConsistent = new ArrayList<>(5); final AspectRatio targetRatio = AspectRatio.of(captureSize.getWidth(), captureSize.getHeight()); - final Size targetSize = captureSize; + final Size targetSize = surfaceSize; for (Size size : sizes) { AspectRatio ratio = AspectRatio.of(size.getWidth(), size.getHeight()); if (ratio.equals(targetRatio)) { diff --git a/camerakit/src/main/api21/com/flurgle/camerakit/Camera2.java b/camerakit/src/main/api21/com/flurgle/camerakit/Camera2.java index 940d5441..80f03b9d 100644 --- a/camerakit/src/main/api21/com/flurgle/camerakit/Camera2.java +++ b/camerakit/src/main/api21/com/flurgle/camerakit/Camera2.java @@ -147,7 +147,7 @@ class Camera2 extends CameraImpl { } @Override - void setMethod(@Method int method) { + void setSessionType(@SessionType int sessionType) { } diff --git a/camerakit/src/main/base/com/flurgle/camerakit/CameraImpl.java b/camerakit/src/main/base/com/flurgle/camerakit/CameraImpl.java index 8e285301..c36e2286 100644 --- a/camerakit/src/main/base/com/flurgle/camerakit/CameraImpl.java +++ b/camerakit/src/main/base/com/flurgle/camerakit/CameraImpl.java @@ -21,10 +21,10 @@ abstract class CameraImpl { abstract void setFacing(@Facing int facing); abstract void setFlash(@Flash int flash); abstract void setFocus(@Focus int focus); - abstract void setMethod(@Method int method); abstract void setZoom(@ZoomMode int zoom); abstract void setVideoQuality(@VideoQuality int videoQuality); abstract void setWhiteBalance(@WhiteBalance int whiteBalance); + abstract void setSessionType(@SessionType int sessionType); abstract void setLocation(double latitude, double longitude); abstract void captureImage(); diff --git a/camerakit/src/main/base/com/flurgle/camerakit/TextureViewPreview.java b/camerakit/src/main/base/com/flurgle/camerakit/TextureViewPreview.java index 29f66f1d..f8ace3a4 100644 --- a/camerakit/src/main/base/com/flurgle/camerakit/TextureViewPreview.java +++ b/camerakit/src/main/base/com/flurgle/camerakit/TextureViewPreview.java @@ -12,6 +12,7 @@ import android.view.ViewGroup; class TextureViewPreview extends PreviewImpl { private final TextureView mTextureView; + private Surface mSurface; TextureViewPreview(Context context, ViewGroup parent) { final View view = View.inflate(context, R.layout.texture_view, parent); // MATCH_PARENT @@ -45,7 +46,10 @@ class TextureViewPreview extends PreviewImpl { @Override Surface getSurface() { - return new Surface(getSurfaceTexture()); + if (mSurface == null) { // Check if valid? + mSurface = new Surface(getSurfaceTexture()); + } + return mSurface; } @Override diff --git a/camerakit/src/main/java/com/flurgle/camerakit/CameraKit.java b/camerakit/src/main/java/com/flurgle/camerakit/CameraKit.java index c491be43..058f6ada 100644 --- a/camerakit/src/main/java/com/flurgle/camerakit/CameraKit.java +++ b/camerakit/src/main/java/com/flurgle/camerakit/CameraKit.java @@ -32,8 +32,11 @@ public class CameraKit { public static final int ZOOM_OFF = 0; public static final int ZOOM_PINCH = 1; - public static final int CAPTURE_METHOD_STANDARD = 0; - public static final int CAPTURE_METHOD_FRAME = 1; + public static final int SESSION_TYPE_PICTURE = 0; + 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 * * 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+. * On older APIs, this is not important, since the Camera permission is already * 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_720P = 1; @@ -72,10 +79,11 @@ public class CameraKit { static final int DEFAULT_FLASH = Constants.FLASH_OFF; static final int DEFAULT_FOCUS = Constants.FOCUS_CONTINUOUS; static final int DEFAULT_ZOOM = Constants.ZOOM_OFF; - static final int DEFAULT_METHOD = Constants.CAPTURE_METHOD_STANDARD; - static final int DEFAULT_PERMISSIONS = Constants.PERMISSIONS_PICTURE; + @Deprecated static final int DEFAULT_METHOD = Constants.CAPTURE_METHOD_STANDARD; + @Deprecated static final int DEFAULT_PERMISSIONS = Constants.PERMISSIONS_PICTURE; static final int DEFAULT_VIDEO_QUALITY = Constants.VIDEO_QUALITY_480P; 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 boolean DEFAULT_CROP_OUTPUT = false; static final boolean DEFAULT_ADJUST_VIEW_BOUNDS = false; diff --git a/camerakit/src/main/java/com/flurgle/camerakit/CameraView.java b/camerakit/src/main/java/com/flurgle/camerakit/CameraView.java index f6f575e1..1a899bf1 100644 --- a/camerakit/src/main/java/com/flurgle/camerakit/CameraView.java +++ b/camerakit/src/main/java/com/flurgle/camerakit/CameraView.java @@ -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_ON; 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.PERMISSIONS_VIDEO; +import static com.flurgle.camerakit.CameraKit.Constants.SESSION_TYPE_PICTURE; +import static com.flurgle.camerakit.CameraKit.Constants.SESSION_TYPE_VIDEO; /** * 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; @Flash private int mFlash; @Focus private int mFocus; - @Method private int mMethod; + // @Method private int mMethod; @ZoomMode private int mZoom; - @Permissions private int mPermissions; + // @Permissions private int mPermissions; + @SessionType private int mSessionType; @VideoQuality private int mVideoQuality; @WhiteBalance private int mWhiteBalance; private int mJpegQuality; private boolean mCropOutput; private boolean mAdjustViewBounds; - private CameraListenerMiddleWare mCameraListener; + private CameraListenerWrapper mCameraListener; private OrientationHelper mOrientationHelper; private CameraImpl mCameraImpl; 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); mFlash = a.getInteger(R.styleable.CameraView_cameraFlash, CameraKit.Defaults.DEFAULT_FLASH); 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); - mPermissions = a.getInteger(R.styleable.CameraView_cameraPermissionPolicy, CameraKit.Defaults.DEFAULT_PERMISSIONS); mWhiteBalance = a.getInteger(R.styleable.CameraView_cameraWhiteBalance, CameraKit.Defaults.DEFAULT_WHITE_BALANCE); mVideoQuality = a.getInteger(R.styleable.CameraView_cameraVideoQuality, CameraKit.Defaults.DEFAULT_VIDEO_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); mCameraImpl = new Camera1(mCameraListener, mPreviewImpl); @@ -135,9 +137,10 @@ public class CameraView extends FrameLayout implements LifecycleObserver { setFacing(mFacing); setFlash(mFlash); setFocus(mFocus); - setCaptureMethod(mMethod); + setSessionType(mSessionType); + // setCaptureMethod(mMethod); + // setPermissionPolicy(mPermissions); setZoom(mZoom); - setPermissionPolicy(mPermissions); setVideoQuality(mVideoQuality); setWhiteBalance(mWhiteBalance); @@ -311,32 +314,70 @@ public class CameraView extends FrameLayout implements LifecycleObserver { // Already started, do nothing. 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 audioCheck = ContextCompat.checkSelfPermission(getContext(), Manifest.permission.RECORD_AUDIO); - switch (mPermissions) { - case PERMISSIONS_VIDEO: + switch (mSessionType) { + case SESSION_TYPE_VIDEO: if (cameraCheck != PackageManager.PERMISSION_GRANTED || audioCheck != PackageManager.PERMISSION_GRANTED) { requestPermissions(true, true); - return; + return false; } break; - case PERMISSIONS_PICTURE: + case SESSION_TYPE_PICTURE: if (cameraCheck != PackageManager.PERMISSION_GRANTED) { requestPermissions(true, false); - return; + return false; } break; } + return true; + } - mIsStarted = true; - run(new Runnable() { - @Override - public void run() { - mCameraImpl.start(); + + /** + * If mSessionType == SESSION_TYPE_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 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() { @@ -349,10 +390,17 @@ public class CameraView extends FrameLayout implements LifecycleObserver { } public void destroy() { + mCameraListener = new CameraListenerWrapper(); // Release inner listener. // This might be useless, but no time to think about it now. sWorkerHandler = null; } + + /** + * If present, returns a collection of extra properties from the current camera + * session. + * @return an ExtraProperties object. + */ @Nullable public ExtraProperties getExtraProperties() { return mCameraImpl.getExtraProperties(); @@ -386,11 +434,15 @@ public class CameraView extends FrameLayout implements LifecycleObserver { 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) { this.mFacing = facing; 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) { this.mFlash = flash; mCameraImpl.setFlash(flash); } + + /** + * Gets the current flash mode. + * @return a flash mode + */ @Flash public int getFlash() { 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) { this.mFocus = focus; if (this.mFocus == CameraKit.Constants.FOCUS_TAP_WITH_MARKER) { @@ -421,23 +510,54 @@ public class CameraView extends FrameLayout implements LifecycleObserver { mCameraImpl.setFocus(mFocus); } + + /** + * This does nothing. + * @param method no-op + * @deprecated + */ + @Deprecated 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. - * @param permissions desired policy, either picture or video. + * This does nothing. + * @param permissions no-op + * @deprecated */ + @Deprecated 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) { @@ -445,6 +565,13 @@ public class CameraView extends FrameLayout implements LifecycleObserver { mCameraImpl.setVideoQuality(mVideoQuality); } + public void setZoom(@ZoomMode int zoom) { + this.mZoom = zoom; + mCameraImpl.setZoom(mZoom); + } + + + public void setJpegQuality(int jpegQuality) { this.mJpegQuality = jpegQuality; } @@ -453,6 +580,13 @@ public class CameraView extends FrameLayout implements LifecycleObserver { 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 public int toggleFacing() { switch (mFacing) { @@ -488,8 +622,15 @@ public class CameraView extends FrameLayout implements LifecycleObserver { 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) { - this.mCameraListener.setCameraListener(cameraListener); + this.mCameraListener.wrapListener(cameraListener); } 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 public void onCameraOpened() { super.onCameraOpened(); - getCameraListener().onCameraOpened(); + if (mWrappedListener == null) return; + mWrappedListener.onCameraOpened(); } @Override public void onCameraClosed() { super.onCameraClosed(); - getCameraListener().onCameraClosed(); + if (mWrappedListener == null) return; + mWrappedListener.onCameraClosed(); } @Override public void onPictureTaken(byte[] jpeg) { super.onPictureTaken(jpeg); + if (mWrappedListener == null) return; if (mCropOutput) { AspectRatio outputRatio = AspectRatio.of(getWidth(), getHeight()); - getCameraListener().onPictureTaken(new CenterCrop(jpeg, outputRatio, mJpegQuality).getJpeg()); + mWrappedListener.onPictureTaken(new CenterCrop(jpeg, outputRatio, mJpegQuality).getJpeg()); } else { - getCameraListener().onPictureTaken(jpeg); + mWrappedListener.onPictureTaken(jpeg); } } @Override public void onPictureTaken(YuvImage yuv) { super.onPictureTaken(yuv); + if (mWrappedListener == null) return; if (mCropOutput) { AspectRatio outputRatio = AspectRatio.of(getWidth(), getHeight()); - getCameraListener().onPictureTaken(new CenterCrop(yuv, outputRatio, mJpegQuality).getJpeg()); + mWrappedListener.onPictureTaken(new CenterCrop(yuv, outputRatio, mJpegQuality).getJpeg()); } else { ByteArrayOutputStream out = new ByteArrayOutputStream(); yuv.compressToJpeg(new Rect(0, 0, yuv.getWidth(), yuv.getHeight()), mJpegQuality, out); - getCameraListener().onPictureTaken(out.toByteArray()); + mWrappedListener.onPictureTaken(out.toByteArray()); } } @Override public void onVideoTaken(File video) { super.onVideoTaken(video); - getCameraListener().onVideoTaken(video); - } - - public void setCameraListener(@Nullable CameraListener cameraListener) { - this.mCameraListener = cameraListener; + mWrappedListener.onVideoTaken(video); } - @NonNull - public CameraListener getCameraListener() { - return mCameraListener != null ? mCameraListener : new CameraListener() { - }; + private void wrapListener(@Nullable CameraListener cameraListener) { + mWrappedListener = 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. - } - } - } } diff --git a/camerakit/src/main/res/values/attrs.xml b/camerakit/src/main/res/values/attrs.xml index 0b193219..63a65b84 100644 --- a/camerakit/src/main/res/values/attrs.xml +++ b/camerakit/src/main/res/values/attrs.xml @@ -43,6 +43,11 @@ + + + + + diff --git a/camerakit/src/main/types/com/flurgle/camerakit/Method.java b/camerakit/src/main/types/com/flurgle/camerakit/Method.java index 8c471cca..d7afa1a8 100644 --- a/camerakit/src/main/types/com/flurgle/camerakit/Method.java +++ b/camerakit/src/main/types/com/flurgle/camerakit/Method.java @@ -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_FRAME; +@Deprecated @Retention(RetentionPolicy.SOURCE) @IntDef({CAPTURE_METHOD_STANDARD, CAPTURE_METHOD_FRAME}) public @interface Method { diff --git a/camerakit/src/main/types/com/flurgle/camerakit/Permissions.java b/camerakit/src/main/types/com/flurgle/camerakit/Permissions.java index 6060c3b3..cabf7160 100644 --- a/camerakit/src/main/types/com/flurgle/camerakit/Permissions.java +++ b/camerakit/src/main/types/com/flurgle/camerakit/Permissions.java @@ -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_VIDEO; +@Deprecated @Retention(RetentionPolicy.SOURCE) @IntDef({PERMISSIONS_VIDEO, PERMISSIONS_PICTURE}) public @interface Permissions {