Tap to focus setup

pull/1/head
Dylan McIntyre 8 years ago
parent 5d4b8fd90e
commit bb495dc278
  1. 21
      README.md
  2. 43
      camerakit/src/main/java/com/flurgle/camerakit/Camera2.java
  3. 25
      camerakit/src/main/java/com/flurgle/camerakit/CameraKit.java
  4. 43
      camerakit/src/main/java/com/flurgle/camerakit/CameraView.java
  5. 15
      camerakit/src/main/java/com/flurgle/camerakit/Constants.java
  6. 6
      camerakit/src/main/res/values/attrs.xml
  7. 18
      demo/src/main/java/com/flurgle/camerakit/demo/MainActivity.java
  8. 2
      demo/src/main/res/layout/activity_main.xml

@ -19,6 +19,7 @@ Try out all the unique features using the CameraKit Demo from the Google Play st
- [`ckFacing`](#ckfacing) - [`ckFacing`](#ckfacing)
- [`ckFlash`](#ckflash) - [`ckFlash`](#ckflash)
- [`ckPictureMode`](#ckpicturemode) - [`ckPictureMode`](#ckpicturemode)
- [`ckTapToFocus`](#cktaptofocus)
- [Capturing Images](#capturing-images) - [Capturing Images](#capturing-images)
- [Capturing Video](#capturing-video) - [Capturing Video](#capturing-video)
- [Automatic Permissions Behavior](#automatic-permissions-behavior) - [Automatic Permissions Behavior](#automatic-permissions-behavior)
@ -43,6 +44,7 @@ Try out all the unique features using the CameraKit Demo from the Google Play st
- `PICTURE_MODE_QUALITY`: an image captured normally using the camera APIs. - `PICTURE_MODE_QUALITY`: an image captured normally using the camera APIs.
- `PICTURE_MODE_SPEED`: a freeze frame of the `CameraView` preview (similar to SnapChat and Instagram) for devices with slower cameras. - `PICTURE_MODE_SPEED`: a freeze frame of the `CameraView` preview (similar to SnapChat and Instagram) for devices with slower cameras.
- Automatic picture mode determination based on measured speed. - Automatic picture mode determination based on measured speed.
- Built-in tap to focus and auto focus.
## Setup ## Setup
@ -119,7 +121,8 @@ camera.setCameraListener(new CameraListener() {
camerakit:ckCropOutput="true" camerakit:ckCropOutput="true"
camerakit:ckFacing="back" camerakit:ckFacing="back"
camerakit:ckFlash="off" camerakit:ckFlash="off"
camerakit:ckPictureMode="quality" /> camerakit:ckPictureMode="quality"
camerakit:ckTapToFocus="on" />
``` ```
#### `ckCropOutput` #### `ckCropOutput`
@ -148,6 +151,14 @@ camera.setCameraListener(new CameraListener() {
#### `ckPictureMode` #### `ckPictureMode`
| Value | Description |
| --------------| -------------|
| `on` | Tap to focus is enabled and a visible focus circle appears when tapped, similar to the Android built-in camera. |
| `invisible` | Tap to focus is enabled, but no focus circle appears. |
| `off` | Tap to focus is off. |
#### `ckTapToFocus`
| Value | Description | | Value | Description |
| --------------| -------------| | --------------| -------------|
| `quality` | Use normal Android Camera API image capturing. | | `quality` | Use normal Android Camera API image capturing. |
@ -238,12 +249,18 @@ When you use `PICTURE_MODE_SPEED` (`camerakit:ckPictureMode="speed"`), images wi
[Insert GIF] [Insert GIF]
## Auto ### Auto
When you use `PICTURE_MODE_AUTO` (`camerakit:ckPictureMode="speed"`), images will be first be captured using the [quality](#quality) method. If capture consistently takes a long amount of time, the picture mode will fallback to [speed](#speed). When you use `PICTURE_MODE_AUTO` (`camerakit:ckPictureMode="speed"`), images will be first be captured using the [quality](#quality) method. If capture consistently takes a long amount of time, the picture mode will fallback to [speed](#speed).
[Insert GIF] [Insert GIF]
## Tap To Focus
Along with the always on auto-focus, you can enable tap to focus in your `CameraView`. See [`ckTapToFocus`](#cktaptofocus) for usage. You have the option of having it on with a visible focus marker, on with no marker, or off.
[Insert GIF]
## Credits ## Credits
Dylan McIntyre Dylan McIntyre

@ -22,7 +22,6 @@ import android.util.SparseIntArray;
import android.view.Surface; import android.view.Surface;
import android.view.View; import android.view.View;
import java.nio.ByteBuffer;
import java.util.Arrays; import java.util.Arrays;
import java.util.SortedSet; import java.util.SortedSet;
import java.util.TreeSet; import java.util.TreeSet;
@ -37,8 +36,8 @@ public class Camera2 extends CameraViewImpl {
private static final SparseIntArray INTERNAL_FACINGS = new SparseIntArray(); private static final SparseIntArray INTERNAL_FACINGS = new SparseIntArray();
static { static {
INTERNAL_FACINGS.put(Constants.FACING_BACK, CameraCharacteristics.LENS_FACING_BACK); INTERNAL_FACINGS.put(CameraKit.Constants.FACING_BACK, CameraCharacteristics.LENS_FACING_BACK);
INTERNAL_FACINGS.put(Constants.FACING_FRONT, CameraCharacteristics.LENS_FACING_FRONT); INTERNAL_FACINGS.put(CameraKit.Constants.FACING_FRONT, CameraCharacteristics.LENS_FACING_FRONT);
} }
private CameraManager mCameraManager; private CameraManager mCameraManager;
@ -166,7 +165,7 @@ public class Camera2 extends CameraViewImpl {
@Override @Override
void capturePicture() { void capturePicture() {
if (mFacing == INTERNAL_FACINGS.get(Constants.FACING_BACK)) { if (mFacing == INTERNAL_FACINGS.get(CameraKit.Constants.FACING_BACK)) {
lockFocus(); lockFocus();
} else { } else {
captureStillPicture(); captureStillPicture();
@ -198,15 +197,15 @@ public class Camera2 extends CameraViewImpl {
void updateFlash(CaptureRequest.Builder builder) { void updateFlash(CaptureRequest.Builder builder) {
switch (mFlash) { switch (mFlash) {
case Constants.FLASH_OFF: case CameraKit.Constants.FLASH_OFF:
builder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON); builder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON);
builder.set(CaptureRequest.FLASH_MODE, CaptureRequest.FLASH_MODE_OFF); builder.set(CaptureRequest.FLASH_MODE, CaptureRequest.FLASH_MODE_OFF);
break; break;
case Constants.FLASH_ON: case CameraKit.Constants.FLASH_ON:
builder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON_ALWAYS_FLASH); builder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON_ALWAYS_FLASH);
builder.set(CaptureRequest.FLASH_MODE, CaptureRequest.FLASH_MODE_OFF); builder.set(CaptureRequest.FLASH_MODE, CaptureRequest.FLASH_MODE_OFF);
break; break;
case Constants.FLASH_AUTO: case CameraKit.Constants.FLASH_AUTO:
builder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH); builder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);
builder.set(CaptureRequest.FLASH_MODE, CaptureRequest.FLASH_MODE_OFF); builder.set(CaptureRequest.FLASH_MODE, CaptureRequest.FLASH_MODE_OFF);
break; break;
@ -259,7 +258,7 @@ public class Camera2 extends CameraViewImpl {
} }
// The operation can reach here when the only camera device is an external one. // The operation can reach here when the only camera device is an external one.
// We treat it as facing back. // We treat it as facing back.
mFacing = Constants.FACING_BACK; mFacing = CameraKit.Constants.FACING_BACK;
return true; return true;
} catch (CameraAccessException e) { } catch (CameraAccessException e) {
throw new RuntimeException("Failed to get a list of camera devices", e); throw new RuntimeException("Failed to get a list of camera devices", e);
@ -308,7 +307,7 @@ public class Camera2 extends CameraViewImpl {
Size previewSize = getOptimalPreviewSize(); Size previewSize = getOptimalPreviewSize();
AspectRatio aspectRatio = AspectRatio.of(previewSize.getWidth(), previewSize.getHeight()); AspectRatio aspectRatio = AspectRatio.of(previewSize.getWidth(), previewSize.getHeight());
Size bestSize = findSizeClosestTo(1500000, aspectRatio, mCaptureSizes); Size bestSize = findSizeClosestTo(1500000, aspectRatio, mCaptureSizes);
mImageReader = ImageReader.newInstance(bestSize.getWidth(), bestSize.getHeight(), ImageFormat.JPEG, 1); mImageReader = ImageReader.newInstance(bestSize.getWidth(), bestSize.getHeight(), ImageFormat.YUV_420_888, 3);
mImageReader.setOnImageAvailableListener(mOnImageAvailableListener, mBackgroundHandler); mImageReader.setOnImageAvailableListener(mOnImageAvailableListener, mBackgroundHandler);
} }
@ -377,6 +376,7 @@ public class Camera2 extends CameraViewImpl {
try { try {
mPreviewRequestBuilder = mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); mPreviewRequestBuilder = mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
mPreviewRequestBuilder.addTarget(surface); mPreviewRequestBuilder.addTarget(surface);
mPreviewRequestBuilder.addTarget(mImageReader.getSurface());
mCamera.createCaptureSession(Arrays.asList(surface, mImageReader.getSurface()), mSessionCallback, mBackgroundHandler); mCamera.createCaptureSession(Arrays.asList(surface, mImageReader.getSurface()), mSessionCallback, mBackgroundHandler);
} catch (CameraAccessException e) { } catch (CameraAccessException e) {
throw new RuntimeException("Failed to start camera session"); throw new RuntimeException("Failed to start camera session");
@ -406,7 +406,7 @@ public class Camera2 extends CameraViewImpl {
int sensorOrientation = mCameraCharacteristics.get(CameraCharacteristics.SENSOR_ORIENTATION); int sensorOrientation = mCameraCharacteristics.get(CameraCharacteristics.SENSOR_ORIENTATION);
captureRequestBuilder.set(CaptureRequest.JPEG_ORIENTATION, captureRequestBuilder.set(CaptureRequest.JPEG_ORIENTATION,
(sensorOrientation + (sensorOrientation +
mDisplayOrientation * (mFacing == Constants.FACING_FRONT ? 1 : -1) + mDisplayOrientation * (mFacing == CameraKit.Constants.FACING_FRONT ? 1 : -1) +
360) % 360); 360) % 360);
// Stop preview and capture a still picture. // Stop preview and capture a still picture.
mCaptureSession.stopRepeating(); mCaptureSession.stopRepeating();
@ -524,15 +524,20 @@ public class Camera2 extends CameraViewImpl {
@Override @Override
public void onImageAvailable(ImageReader reader) { public void onImageAvailable(ImageReader reader) {
try (Image image = reader.acquireNextImage()) { Image image = reader.acquireLatestImage();
Image.Plane[] planes = image.getPlanes(); // Process the image
if (planes.length > 0) { image.close();
ByteBuffer buffer = planes[0].getBuffer();
byte[] data = new byte[buffer.remaining()]; // try (Image image = reader.acquireNextImage()) {
buffer.get(data); // image.close();
getCameraListener().onPictureTaken(data); // Image.Plane[] planes = image.getPlanes();
} // if (planes.length > 0) {
} // ByteBuffer buffer = planes[0].getBuffer();
// byte[] data = new byte[buffer.remaining()];
// buffer.get(data);
// //getCameraListener().onPictureTaken(data);
// }
// }
} }
}; };

@ -0,0 +1,25 @@
package com.flurgle.camerakit;
public class CameraKit {
public static class Constants {
public static final int FACING_BACK = 0;
public static final int FACING_FRONT = 1;
public static final int FLASH_OFF = 0;
public static final int FLASH_ON = 1;
public static final int FLASH_AUTO = 2;
public static final int PICTURE_MODE_QUALITY = 0;
public static final int PICTURE_MODE_SPEED = 1;
public static final int PICTURE_MODE_AUTO = 2;
public static final int TAP_TO_FOCUS_ON = 0;
public static final int TAP_TO_FOCUS_INVISIBLE = 1;
public static final int TAP_TO_FOCUS_OFF = 2;
}
}

@ -27,33 +27,30 @@ import java.io.File;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
import static com.flurgle.camerakit.CameraKit.Constants.*;
public class CameraView extends FrameLayout { public class CameraView extends FrameLayout {
private static final int PERMISSION_REQUEST_CAMERA = 16; private static final int PERMISSION_REQUEST_CAMERA = 16;
public static final int FACING_BACK = Constants.FACING_BACK;
public static final int FACING_FRONT = Constants.FACING_FRONT;
@IntDef({FACING_BACK, FACING_FRONT}) @IntDef({FACING_BACK, FACING_FRONT})
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
public @interface Facing { @interface Facing {
} }
public static final int FLASH_OFF = Constants.FLASH_OFF;
public static final int FLASH_ON = Constants.FLASH_ON;
public static final int FLASH_AUTO = Constants.FLASH_AUTO;
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
@IntDef({FLASH_OFF, FLASH_ON, FLASH_AUTO}) @IntDef({FLASH_OFF, FLASH_ON, FLASH_AUTO})
public @interface Flash { @interface Flash {
} }
public static final int PICTURE_MODE_QUALITY = Constants.PICTURE_MODE_QUALITY;
public static final int PICTURE_MODE_SPEED = Constants.PICTURE_MODE_SPEED;
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
@IntDef({PICTURE_MODE_QUALITY, PICTURE_MODE_SPEED}) @IntDef({PICTURE_MODE_QUALITY, PICTURE_MODE_SPEED})
public @interface PictureMode { @interface PictureMode {
}
@Retention(RetentionPolicy.SOURCE)
@IntDef({TAP_TO_FOCUS_ON, TAP_TO_FOCUS_INVISIBLE, TAP_TO_FOCUS_OFF})
@interface TapToFocus {
} }
private int mFacing; private int mFacing;
@ -66,6 +63,8 @@ public class CameraView extends FrameLayout {
private boolean mCropOutput; private boolean mCropOutput;
private int mTapToFocus;
private boolean mAdjustViewBounds; private boolean mAdjustViewBounds;
private boolean mWaitingForPermission; private boolean mWaitingForPermission;
@ -90,21 +89,25 @@ public class CameraView extends FrameLayout {
int attr = a.getIndex(i); int attr = a.getIndex(i);
if (attr == R.styleable.CameraView_ckFacing) { if (attr == R.styleable.CameraView_ckFacing) {
mFacing = a.getInteger(R.styleable.CameraView_ckFacing, 0); mFacing = a.getInteger(R.styleable.CameraView_ckFacing, FACING_BACK);
} }
if (attr == R.styleable.CameraView_ckFlash) { if (attr == R.styleable.CameraView_ckFlash) {
mFlash = a.getInteger(R.styleable.CameraView_ckFlash, 0); mFlash = a.getInteger(R.styleable.CameraView_ckFlash, FLASH_OFF);
} }
if (attr == R.styleable.CameraView_ckPictureMode) { if (attr == R.styleable.CameraView_ckPictureMode) {
mPictureMode = a.getInteger(R.styleable.CameraView_ckPictureMode, 0); mPictureMode = a.getInteger(R.styleable.CameraView_ckPictureMode, PICTURE_MODE_QUALITY);
} }
if (attr == R.styleable.CameraView_ckCropOutput) { if (attr == R.styleable.CameraView_ckCropOutput) {
mCropOutput = a.getBoolean(R.styleable.CameraView_ckCropOutput, false); mCropOutput = a.getBoolean(R.styleable.CameraView_ckCropOutput, false);
} }
if (attr == R.styleable.CameraView_ckTapToFocus) {
mTapToFocus = a.getInteger(R.styleable.CameraView_ckTapToFocus, TAP_TO_FOCUS_ON);
}
if (attr == R.styleable.CameraView_android_adjustViewBounds) { if (attr == R.styleable.CameraView_android_adjustViewBounds) {
mAdjustViewBounds = a.getBoolean(R.styleable.CameraView_android_adjustViewBounds, false); mAdjustViewBounds = a.getBoolean(R.styleable.CameraView_android_adjustViewBounds, false);
} }
@ -229,6 +232,10 @@ public class CameraView extends FrameLayout {
this.mCropOutput = cropOutput; this.mCropOutput = cropOutput;
} }
public void setTapToFocus(@TapToFocus int tapToFocus) {
this.mTapToFocus = tapToFocus;
}
public void setCameraListener(CameraListener cameraListener) { public void setCameraListener(CameraListener cameraListener) {
this.mCameraListener = new CameraListenerMiddleWare(cameraListener); this.mCameraListener = new CameraListenerMiddleWare(cameraListener);
mCameraImpl.setCameraListener(mCameraListener); mCameraImpl.setCameraListener(mCameraListener);
@ -348,6 +355,10 @@ public class CameraView extends FrameLayout {
Bitmap bitmap = BitmapFactory.decodeByteArray(picture, 0, picture.length); Bitmap bitmap = BitmapFactory.decodeByteArray(picture, 0, picture.length);
int previewWidth = mCameraImpl.mPreview.getWidth(); int previewWidth = mCameraImpl.mPreview.getWidth();
int previewHeight = mCameraImpl.mPreview.getWidth(); int previewHeight = mCameraImpl.mPreview.getWidth();
AspectRatio viewRatio = AspectRatio.of(getWidth(), getHeight());
AspectRatio previewRatio = AspectRatio.of(previewWidth, previewHeight);
mCameraListener.onPictureTaken(picture); mCameraListener.onPictureTaken(picture);
} else { } else {
mCameraListener.onPictureTaken(picture); mCameraListener.onPictureTaken(picture);

@ -1,15 +0,0 @@
package com.flurgle.camerakit;
public class Constants {
public static final int FACING_BACK = 0;
public static final int FACING_FRONT = 1;
public static final int FLASH_OFF = 0;
public static final int FLASH_ON = 1;
public static final int FLASH_AUTO = 2;
public static final int PICTURE_MODE_QUALITY = 0;
public static final int PICTURE_MODE_SPEED = 1;
}

@ -22,6 +22,12 @@
<attr name="ckCropOutput" format="boolean" /> <attr name="ckCropOutput" format="boolean" />
<attr name="ckTapToFocus" format="enum">
<enum name="on" value="0" />
<enum name="invisible" value="1" />
<enum name="off" value="2" />
</attr>
<attr name="android:adjustViewBounds" /> <attr name="android:adjustViewBounds" />
</declare-styleable> </declare-styleable>

@ -14,9 +14,9 @@ import android.widget.EditText;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Toast; import android.widget.Toast;
import com.flurgle.camerakit.CameraKit;
import com.flurgle.camerakit.CameraListener; import com.flurgle.camerakit.CameraListener;
import com.flurgle.camerakit.CameraView; import com.flurgle.camerakit.CameraView;
import com.flurgle.camerakit.Constants;
import butterknife.BindView; import butterknife.BindView;
import butterknife.ButterKnife; import butterknife.ButterKnife;
@ -57,7 +57,7 @@ public class MainActivity extends AppCompatActivity {
@BindView(R.id.height) @BindView(R.id.height)
EditText height; EditText height;
int pictureMode = Constants.PICTURE_MODE_QUALITY; int pictureMode = CameraKit.Constants.PICTURE_MODE_QUALITY;
boolean blockInvalidation; boolean blockInvalidation;
@ -116,11 +116,11 @@ public class MainActivity extends AppCompatActivity {
@OnClick(R.id.toggleCamera) @OnClick(R.id.toggleCamera)
void toggleCamera() { void toggleCamera() {
switch (camera.toggleFacing()) { switch (camera.toggleFacing()) {
case Constants.FACING_BACK: case CameraKit.Constants.FACING_BACK:
Toast.makeText(this, "Switched to back camera!", Toast.LENGTH_SHORT).show(); Toast.makeText(this, "Switched to back camera!", Toast.LENGTH_SHORT).show();
break; break;
case Constants.FACING_FRONT: case CameraKit.Constants.FACING_FRONT:
Toast.makeText(this, "Switched to front camera!", Toast.LENGTH_SHORT).show(); Toast.makeText(this, "Switched to front camera!", Toast.LENGTH_SHORT).show();
break; break;
} }
@ -129,15 +129,15 @@ public class MainActivity extends AppCompatActivity {
@OnClick(R.id.toggleFlash) @OnClick(R.id.toggleFlash)
void toggleFlash() { void toggleFlash() {
switch (camera.toggleFlash()) { switch (camera.toggleFlash()) {
case Constants.FLASH_ON: case CameraKit.Constants.FLASH_ON:
Toast.makeText(this, "Flash on!", Toast.LENGTH_SHORT).show(); Toast.makeText(this, "Flash on!", Toast.LENGTH_SHORT).show();
break; break;
case Constants.FLASH_OFF: case CameraKit.Constants.FLASH_OFF:
Toast.makeText(this, "Flash off!", Toast.LENGTH_SHORT).show(); Toast.makeText(this, "Flash off!", Toast.LENGTH_SHORT).show();
break; break;
case Constants.FLASH_AUTO: case CameraKit.Constants.FLASH_AUTO:
Toast.makeText(this, "Flash auto!", Toast.LENGTH_SHORT).show(); Toast.makeText(this, "Flash auto!", Toast.LENGTH_SHORT).show();
break; break;
} }
@ -147,7 +147,7 @@ public class MainActivity extends AppCompatActivity {
void modeQuality(CompoundButton buttonCompat, boolean checked) { void modeQuality(CompoundButton buttonCompat, boolean checked) {
modeSpeed.setChecked(false); modeSpeed.setChecked(false);
if (checked) { if (checked) {
camera.setPictureMode(CameraView.PICTURE_MODE_QUALITY); camera.setPictureMode(CameraKit.Constants.PICTURE_MODE_QUALITY);
} }
invalidateParameters(); invalidateParameters();
@ -157,7 +157,7 @@ public class MainActivity extends AppCompatActivity {
void modeSpeed(CompoundButton buttonCompat, boolean checked) { void modeSpeed(CompoundButton buttonCompat, boolean checked) {
modeQuality.setChecked(false); modeQuality.setChecked(false);
if (checked) { if (checked) {
camera.setPictureMode(CameraView.PICTURE_MODE_SPEED); camera.setPictureMode(CameraKit.Constants.PICTURE_MODE_SPEED);
} }
invalidateParameters(); invalidateParameters();

@ -21,7 +21,7 @@
app:ckCropOutput="true" app:ckCropOutput="true"
app:ckFacing="back" app:ckFacing="back"
app:ckFlash="off" app:ckFlash="off"
app:ckPictureMode="quality" /> app:ckPictureMode="speed" />
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"

Loading…
Cancel
Save