Add public callbacks for auto focus events, to let people draw

pull/1/head
Mattia Iavarone 7 years ago
parent 3f67e02d1e
commit 900e71dfa3
  1. 14
      README.md
  2. 53
      camerakit/src/main/api16/com/flurgle/camerakit/Camera1.java
  3. 4
      camerakit/src/main/base/com/flurgle/camerakit/CameraImpl.java
  4. 27
      camerakit/src/main/java/com/flurgle/camerakit/CameraListener.java
  5. 24
      camerakit/src/main/java/com/flurgle/camerakit/CameraView.java

@ -173,6 +173,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

@ -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<Camera.Area> meteringAreas2 = computeMeteringAreas(event.getX(), event.getY());
final float x = event.getX();
final float y = event.getY();
List<Camera.Area> meteringAreas2 = computeMeteringAreas(x, y);
List<Camera.Area> 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<Camera.Area> 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);

@ -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);
}

@ -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) {
}
}

@ -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);
}

Loading…
Cancel
Save