diff --git a/README.md b/README.md index f4f616c3..8aab1012 100644 --- a/README.md +++ b/README.md @@ -172,6 +172,12 @@ camera.setCameraListener(new CameraListener() { @Override public void onVideoTaken(File video) {} + + @Override + public void onFocusStart(float x, float y) {} + + @Override + public void onFocusEnd(boolean successful, float x, float y) {} }); ``` @@ -387,14 +393,14 @@ These are things that need to be done, off the top of my head: - [x] fix CropOutput class presumably not working on rotated pictures - [x] test video and 'frame' capture behavior, I expect some bugs there - [x] simple APIs to draw grid lines -- [ ] animate grid lines similar to stock camera app -- [ ] check focus, not sure it exposes the right part of the image +- [x] check focus, not sure it exposes the right part of the image - [x] replace setCameraListener() with addCameraListener() -- [ ] add a `sizingMethod` API to choose the capture size? Could be `max`, `4:3`, `16:9`... Right now it's `max` +- [x] better threading, for example ensure callbacks are called in the ui thread - [ ] pinch to zoom support - [ ] exposure correction APIs +- [ ] add a `sizingMethod` API to choose the capture size? Could be `max`, `4:3`, `16:9`... Right now it's `max` - [ ] `Camera2` integration +- [ ] animate grid lines similar to stock camera app - [ ] add onRequestPermissionResults for easy permission callback - [ ] better error handling, maybe with a onError(e) method in the public listener, or have each public method return a boolean -- [x] better threading, for example ensure callbacks are called in the ui thread diff --git a/camerakit/src/main/api16/com/flurgle/camerakit/Camera1.java b/camerakit/src/main/api16/com/flurgle/camerakit/Camera1.java index 59f01ba3..91f7e907 100644 --- a/camerakit/src/main/api16/com/flurgle/camerakit/Camera1.java +++ b/camerakit/src/main/api16/com/flurgle/camerakit/Camera1.java @@ -40,13 +40,11 @@ class Camera1 extends CameraImpl { private Size mCaptureSize; private MediaRecorder mMediaRecorder; private File mVideoFile; - private Camera.AutoFocusCallback mAutofocusCallback; private int mDisplayOffset; private int mDeviceOrientation; private int mSensorOffset; - @ZoomMode private int mZoom; private double mLatitude; private double mLongitude; private boolean mFocusOnTap; @@ -84,7 +82,7 @@ class Camera1 extends CameraImpl { Size newSize = computePreviewSize(); if (!newSize.equals(mPreviewSize)) { mPreviewSize = newSize; - mCameraListener.onCameraPreviewSizeChanged(); + mCameraCallbacks.onCameraPreviewSizeChanged(); synchronized (mLock) { mCamera.stopPreview(); Camera.Parameters params = mCamera.getParameters(); @@ -121,7 +119,7 @@ class Camera1 extends CameraImpl { boolean invertPreviewSizes = shouldFlipSizes(); // mDisplayOffset % 180 != 0; mCaptureSize = computeCaptureSize(); mPreviewSize = computePreviewSize(); - mCameraListener.onCameraPreviewSizeChanged(); + mCameraCallbacks.onCameraPreviewSizeChanged(); mPreview.setDesiredSize( invertPreviewSizes ? mPreviewSize.getHeight() : mPreviewSize.getWidth(), invertPreviewSizes ? mPreviewSize.getWidth() : mPreviewSize.getHeight() @@ -158,7 +156,7 @@ class Camera1 extends CameraImpl { mCamera.setDisplayOrientation(computeSensorToDisplayOffset()); // <- not allowed during preview if (shouldSetup()) setup(); collectExtraProperties(); - mCameraListener.dispatchOnCameraOpened(); + mCameraCallbacks.dispatchOnCameraOpened(); } } @@ -170,7 +168,7 @@ class Camera1 extends CameraImpl { if (mIsCapturingVideo) endVideo(); mCamera.stopPreview(); mCamera.release(); - mCameraListener.dispatchOnCameraClosed(); + mCameraCallbacks.dispatchOnCameraClosed(); } mCamera = null; mPreviewSize = null; @@ -345,7 +343,6 @@ class Camera1 extends CameraImpl { @Override void setZoomMode(@ZoomMode int zoom) { - this.mZoom = zoom; } @Override @@ -403,7 +400,7 @@ class Camera1 extends CameraImpl { public void onPictureTaken(byte[] data, Camera camera) { mIsCapturingImage = false; camera.startPreview(); // This is needed, read somewhere in the docs. - mCameraListener.processImage(data, consistentWithView, exifFlip); + mCameraCallbacks.processImage(data, consistentWithView, exifFlip); } }); } @@ -443,7 +440,7 @@ class Camera1 extends CameraImpl { final boolean consistentWithView = (sensorToDevice + sensorToDisplay + 180) % 180 == 0; byte[] rotatedData = RotationHelper.rotate(data, preWidth, preHeight, sensorToDevice); YuvImage yuv = new YuvImage(rotatedData, format, postWidth, postHeight, null); - mCameraListener.processSnapshot(yuv, consistentWithView, exifFlip); + mCameraCallbacks.processSnapshot(yuv, consistentWithView, exifFlip); mIsCapturingImage = false; } }).start(); @@ -591,7 +588,7 @@ class Camera1 extends CameraImpl { mMediaRecorder.release(); mMediaRecorder = null; if (mVideoFile != null) { - mCameraListener.dispatchOnVideoTaken(mVideoFile); + mCameraCallbacks.dispatchOnVideoTaken(mVideoFile); mVideoFile = null; } } @@ -670,7 +667,9 @@ class Camera1 extends CameraImpl { if (!mFocusOnTap) return; if (mCamera == null) return; if (event.getAction() != MotionEvent.ACTION_UP) return; - List meteringAreas2 = computeMeteringAreas(event.getX(), event.getY()); + final float x = event.getX(); + final float y = event.getY(); + List meteringAreas2 = computeMeteringAreas(x, y); List meteringAreas1 = meteringAreas2.subList(0, 1); synchronized (mLock) { Camera.Parameters parameters = mCamera.getParameters(); @@ -682,10 +681,12 @@ class Camera1 extends CameraImpl { if (maxAE > 0) parameters.setMeteringAreas(maxAE > 1 ? meteringAreas2 : meteringAreas1); parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO); mCamera.setParameters(parameters); + mCameraCallbacks.dispatchOnFocusStart(x, y); mCamera.autoFocus(new Camera.AutoFocusCallback() { @Override public void onAutoFocus(boolean success, Camera camera) { - resetFocus(success, camera); + mCameraCallbacks.dispatchOnFocusEnd(success, x, y); + postResetFocus(); } }); } @@ -693,25 +694,21 @@ class Camera1 extends CameraImpl { } - - private void resetFocus(final boolean success, final Camera camera) { + private void postResetFocus() { mFocusHandler.removeCallbacksAndMessages(null); mFocusHandler.postDelayed(new Runnable() { @Override public void run() { - if (camera != null) { - camera.cancelAutoFocus(); - synchronized (mLock) { - Camera.Parameters params = camera.getParameters(); - if (!params.getFocusMode().equals(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE)) { - params.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE); - params.setFocusAreas(null); - params.setMeteringAreas(null); - mCamera.setParameters(params); - } + if (!isCameraOpened()) return; + mCamera.cancelAutoFocus(); + synchronized (mLock) { + Camera.Parameters params = mCamera.getParameters(); + if (!params.getFocusMode().equals(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE)) { + params.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE); + params.setFocusAreas(null); + params.setMeteringAreas(null); + mCamera.setParameters(params); } - - if (mAutofocusCallback != null) mAutofocusCallback.onAutoFocus(success, camera); } } }, DELAY_MILLIS_BEFORE_RESETTING_FOCUS); @@ -720,7 +717,7 @@ class Camera1 extends CameraImpl { private List computeMeteringAreas(double viewClickX, double viewClickY) { - // Event came in view coordinates. As far as I know, we must rotate to sensor coordinates. + // Event came in view coordinates. We must rotate to sensor coordinates. // First, rescale to the -1000 ... 1000 range. int displayToSensor = -computeSensorToDisplayOffset(); double viewWidth = mPreview.getView().getWidth(); @@ -750,7 +747,7 @@ class Camera1 extends CameraImpl { private Rect computeMeteringArea(double centerX, double centerY, double size) { - double delta = size/2d; + double delta = size / 2d; int top = (int) Math.max(centerY - delta, -1000); int bottom = (int) Math.min(centerY + delta, 1000); int left = (int) Math.max(centerX - delta, -1000); diff --git a/camerakit/src/main/base/com/flurgle/camerakit/CameraImpl.java b/camerakit/src/main/base/com/flurgle/camerakit/CameraImpl.java index b9b67023..afba5222 100644 --- a/camerakit/src/main/base/com/flurgle/camerakit/CameraImpl.java +++ b/camerakit/src/main/base/com/flurgle/camerakit/CameraImpl.java @@ -8,7 +8,7 @@ import java.io.File; abstract class CameraImpl implements PreviewImpl.SurfaceCallback { - protected final CameraView.CameraCallbacks mCameraListener; + protected final CameraView.CameraCallbacks mCameraCallbacks; protected final PreviewImpl mPreview; @Facing protected int mFacing; @@ -19,7 +19,7 @@ abstract class CameraImpl implements PreviewImpl.SurfaceCallback { @SessionType protected int mSessionType; CameraImpl(CameraView.CameraCallbacks callback, PreviewImpl preview) { - mCameraListener = callback; + mCameraCallbacks = callback; mPreview = preview; mPreview.setSurfaceCallback(this); } diff --git a/camerakit/src/main/java/com/flurgle/camerakit/CameraListener.java b/camerakit/src/main/java/com/flurgle/camerakit/CameraListener.java index f4da5fd0..19141e33 100644 --- a/camerakit/src/main/java/com/flurgle/camerakit/CameraListener.java +++ b/camerakit/src/main/java/com/flurgle/camerakit/CameraListener.java @@ -22,4 +22,31 @@ public abstract class CameraListener { } + + /** + * Notifies that user tapped on screen at position given by x and y, + * and the autofocus is trying to focus around that area. + * This can be used to draw things on screen. + * + * @param x coordinate with respect to CameraView.getWidth() + * @param y coordinate with respect to CameraView.getHeight() + */ + public void onFocusStart(float x, float y) { + + } + + + /** + * Notifies that a tap-to-focus event just ended, and the camera converged + * to a new focus (and possibly exposure and white balance). + * This might succeed or not. + * + * @param successful whether camera succeeded + * @param x coordinate with respect to CameraView.getWidth() + * @param y coordinate with respect to CameraView.getHeight() + */ + public void onFocusEnd(boolean successful, float x, float y) { + + } + } \ No newline at end of file diff --git a/camerakit/src/main/java/com/flurgle/camerakit/CameraView.java b/camerakit/src/main/java/com/flurgle/camerakit/CameraView.java index a90f9279..a194c150 100644 --- a/camerakit/src/main/java/com/flurgle/camerakit/CameraView.java +++ b/camerakit/src/main/java/com/flurgle/camerakit/CameraView.java @@ -1135,6 +1135,30 @@ public class CameraView extends FrameLayout implements LifecycleObserver { } + public void dispatchOnFocusStart(final float x, final float y) { + uiHandler.post(new Runnable() { + @Override + public void run() { + for (CameraListener listener : mListeners) { + listener.onFocusStart(x, y); + } + } + }); + } + + + public void dispatchOnFocusEnd(final boolean success, final float x, final float y) { + uiHandler.post(new Runnable() { + @Override + public void run() { + for (CameraListener listener : mListeners) { + listener.onFocusEnd(success, x, y); + } + } + }); + } + + private void addListener(@NonNull CameraListener cameraListener) { mListeners.add(cameraListener); }