refactor w/ readme updates

pull/1/head
Dylan McIntyre 8 years ago
parent 31d7cfc082
commit 94f71bd6d8
  1. 114
      README.md
  2. 133
      camerakit/src/main/api16/com/flurgle/camerakit/Camera1.java
  3. 19
      camerakit/src/main/api21/com/flurgle/camerakit/Camera2.java
  4. 14
      camerakit/src/main/base/com/flurgle/camerakit/CameraImpl.java
  5. 30
      camerakit/src/main/java/com/flurgle/camerakit/CameraKit.java
  6. 259
      camerakit/src/main/java/com/flurgle/camerakit/CameraView.java
  7. 34
      camerakit/src/main/res/values/attrs.xml
  8. 8
      camerakit/src/main/types/com/flurgle/camerakit/Focus.java
  9. 6
      camerakit/src/main/types/com/flurgle/camerakit/Method.java
  10. 14
      camerakit/src/main/types/com/flurgle/camerakit/Zoom.java
  11. 16
      demo/src/main/java/com/flurgle/camerakit/demo/MainActivity.java
  12. 4
      demo/src/main/res/layout/activity_main.xml

@ -20,26 +20,24 @@ Try out all the unique features using the CameraKit Demo from the Google Play st
- [Usage](#usage) - [Usage](#usage)
- [Events](#events) - [Events](#events)
- [Extra Attributes](#extra-attributes) - [Extra Attributes](#extra-attributes)
- [`ckCropOutput`](#ckcropoutput)
- [`ckFacing`](#ckfacing) - [`ckFacing`](#ckfacing)
- [`ckFlash`](#ckflash) - [`ckFlash`](#ckflash)
- [`ckCaptureMode`](#ckcapturemode) - [`ckFocus`](#ckfocus)
- [`ckPinchToZoom`](#ckpinchtozoom) - [`ckMethod`](#ckmethod)
- [`ckTapToFocus`](#cktaptofocus) - [`ckZoom`](#ckzoom)
- [`ckAutoFocus`](#ckautofocus) - [`ckCropOutput`](#ckcropoutput)
- [`ckCaptureSize`](#ckcapturesize) - [`ckJpegQuality`](#ckjpegquality)
- [`ckJpegCompression`](#ckjpegcompression)
- [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)
- [Dynamic Sizing Behavior](#dynamic-sizing-behavior) - [Dynamic Sizing Behavior](#dynamic-sizing-behavior)
- [Output Cropping](#output-cropping) - [Output Cropping](#output-cropping)
- [`adjustViewBounds`](#adjustviewbounds) - [`adjustViewBounds`](#adjustviewbounds)
- [Capture Mode Behavior](#capture-mode-behavior) - [Capture Methods](#capture-methods)
- [Quality](#quality) - [Standard](#standard)
- [Speed](#speed) - [Still](#still)
- [Auto](#auto) - [Auto](#auto)
- [Tap To Focus](#tap-to-focus) - [Focus](#focus)
- [Credits](#credits) - [Credits](#credits)
- [License](#license) - [License](#license)
@ -51,9 +49,9 @@ Try out all the unique features using the CameraKit Demo from the Google Play st
- Automatic preview scaling. - Automatic preview scaling.
- Create a `CameraView` of any size (not just presets!). - Create a `CameraView` of any size (not just presets!).
- Automatic output cropping to match your `CameraView` bounds. - Automatic output cropping to match your `CameraView` bounds.
- Multiple capture modes. - Multiple capture methods.
- `PICTURE_MODE_STANDARD`: an image captured normally using the camera APIs. - `METHOD_STANDARD`: an image captured normally using the camera APIs.
- `PICTURE_MODE_STILL`: a freeze frame of the `CameraView` preview (similar to SnapChat and Instagram) for devices with slower cameras. - `METHOD_STILL`: 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. - Built-in tap to focus and auto focus.
- Built-in pinch to zoom. - Built-in pinch to zoom.
@ -74,7 +72,8 @@ To use CameraKit, simply add a `CameraView` to your layout:
<com.flurgle.camerakit.CameraView <com.flurgle.camerakit.CameraView
android:id="@+id/camera" android:id="@+id/camera"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" /> android:layout_height="wrap_content"
android:adjustViewBounds="true" />
``` ```
Make sure you override `onResume` and `onPause` in your activity, and make calls respectively to `CameraView.start()` and `CameraView.stop()`. Make sure you override `onResume` and `onPause` in your activity, and make calls respectively to `CameraView.start()` and `CameraView.stop()`.
@ -129,25 +128,17 @@ camera.setCameraListener(new CameraListener() {
<com.flurgle.camerakit.CameraView xmlns:camerakit="http://schemas.android.com/apk/res-auto" <com.flurgle.camerakit.CameraView xmlns:camerakit="http://schemas.android.com/apk/res-auto"
android:id="@+id/camera" android:id="@+id/camera"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="wrap_content"
camerakit:ckCropOutput="true"
camerakit:ckFacing="back" camerakit:ckFacing="back"
camerakit:ckFlash="off" camerakit:ckFlash="off"
camerakit:ckCaptureMode="quality" camerakit:ckFocus="continuous"
camerakit:ckPinchToZoom="true" camerakit:ckMethod="standard"
camerakit:ckTapToFocus="on" camerakit:ckZoom="pinch"
camerakit:ckAutoFocus="true" camerakit:ckCropOutput="true"
camerakit:ckCaptureSize="8" camerakit:ckJpegQuality="100"
camerakit:ckJpegCompression="100" /> android:adjustViewBounds="true" />
``` ```
#### `ckCropOutput`
| Value | Description |
| --------------| -------------|
| `true` | Crop the output image or video to only contain what can be seen on the `CameraView` preview. |
| `false` | Output the full image or video regardless of what is visible on the `CameraView` preview. |
#### `ckFacing` #### `ckFacing`
| Value | Description | | Value | Description |
@ -163,44 +154,37 @@ camera.setCameraListener(new CameraListener() {
| `on` | Default `CameraView` flash to on. | | `on` | Default `CameraView` flash to on. |
| `auto` | Default `CameraView` flash to automatic. | | `auto` | Default `CameraView` flash to automatic. |
#### `ckCaptureMode` #### `ckFocus`
| Value | Description |
| --------------| -------------|
| `standard` | Use normal Android Camera API image capturing. |
| `still` | Freeze the `CameraView` preview and grab a `Bitmap` of the frame. |
| `auto` (coming soon) | Default picture mode to `standard`, but fallback to `still` if capturing is determined to be too slow. |
#### `ckPinchToZoom`
| Value | Description | | Value | Description |
| --------------| -------------| | --------------| -------------|
| `true` | User can zoom using pinching gestures with their fingers. | | `off` | Tap to focus is enabled and a visible focus circle appears when tapped, similar to the Android built-in camera. |
| `false` | Default pinch to zoom behavior disabled. | | `continuous` | Tap to focus is enabled, but no focus circle appears. |
| `tap` | Tap to focus is off. |
#### `ckTapToFocus` #### `ckMethod`
| Value | Description | | Value | Description |
| --------------| -------------| | --------------| -------------|
| `visible` | Tap to focus is enabled and a visible focus circle appears when tapped, similar to the Android built-in camera. | | `standard` | Use normal Android Camera API image capturing. |
| `invisible` | Tap to focus is enabled, but no focus circle appears. | | `still` | Freeze the `CameraView` preview and grab a `Bitmap` of the frame. |
| `off` | Tap to focus is off. | | `auto` (coming soon) | Default picture mode to `standard`, but fallback to `still` if capturing is determined to be too slow. |
#### `ckAutoFocus` #### `ckZoom`
| Value | Description | | Value | Description |
| --------------| -------------| | --------------| -------------|
| `true` | Continuously allow the `CameraView` preview to adjust focus automatically. | | `off` | User can zoom using pinching gestures with their fingers. |
| `false` | Never adjust focus during preview. | | `pinch` | User can zoom in and out using pinching gestures on the `CameraView`. |
#### `ckCaptureSize` #### `ckCropOutput`
| Value | Description | | Value | Description |
| --------------| -------------| | --------------| -------------|
| `n <= 0` | Capture at the highest quality possible. | | `true` | Crop the output image or video to only contain what can be seen on the `CameraView` preview. |
| `n > 0` | Capture at a size of approximately `n` megapixels. | | `false` | Output the full image or video regardless of what is visible on the `CameraView` preview. |
#### `ckJpegCompression` #### `ckJpegQuality`
| Value | Description | | Value | Description |
| --------------| -------------| | --------------| -------------|
@ -209,7 +193,7 @@ camera.setCameraListener(new CameraListener() {
### Capturing Images ### Capturing Images
To capture an image just call `CameraView.capturePicture()`. Make sure you setup a `CameraListener` to handle the image callback. To capture an image just call `CameraView.captureImage()`. Make sure you setup a `CameraListener` to handle the image callback.
```java ```java
camera.setCameraListener(new CameraListener() { camera.setCameraListener(new CameraListener() {
@ -222,7 +206,7 @@ camera.setCameraListener(new CameraListener() {
} }
}); });
camera.takePicture(); camera.captureImage();
``` ```
### Capturing Video ### Capturing Video
@ -251,7 +235,7 @@ camera.postDelayed(new Runnable() {
You can handle permissions yourself in whatever way you want, but if you make a call to `CameraView.start()` without the `android.permission.CAMERA` permission, an exception would normally be thrown and your app would crash. You can handle permissions yourself in whatever way you want, but if you make a call to `CameraView.start()` without the `android.permission.CAMERA` permission, an exception would normally be thrown and your app would crash.
With CameraKit, we will automatically prompt for the `android.permission.CAMERA` permission if it's not available. If you want to handle it yourself, just make sure you dont call `CameraView.start()` until you acquire the permissions. With CameraKit, we will automatically prompt for the `android.permission.CAMERA` permission if it's not available. If you want to handle it yourself, just make sure you don't call `CameraView.start()` until you acquire the permissions.
![Permissions behavior gif](.repo/permissions.gif) ![Permissions behavior gif](.repo/permissions.gif)
@ -271,35 +255,35 @@ When you capture output you can either capture everything - even what is not vis
You can use a mix of a fixed dimension (a set value or `match_parent`) as well as `wrap_content`. When you do this make sure you set `android:adjustViewBounds="true"` on the `CameraView`. You can use a mix of a fixed dimension (a set value or `match_parent`) as well as `wrap_content`. When you do this make sure you set `android:adjustViewBounds="true"` on the `CameraView`.
When you do this the dimension set to `wrap_content` will automatically align with the true aspect ratio of the preview surface. In this case the whole preview will be visible. When you do this the dimension set to `wrap_content` will automatically align with the true aspect ratio of the preview surface. In this case the whole preview will be visible with no cropping.
## Capture Mode Behavior ## Capture Methods
We decided to add multiple capture modes to CameraKit to allow you to give a better image capturing experience to users with slower cameras when appropriate. We decided to add multiple capture modes to CameraKit to allow you to give a better image capturing experience to users with slower cameras when appropriate.
See [`ckPictureMode`](#ckpicturemode) above for usage. See [`ckMethod`](#ckmethod) above for usage.
### Quality ### Standard
When you use `PICTURE_MODE_QUALITY` (`camerakit:ckPictureMode="quality"`), images will be captured using the normal camera API capture method using the shutter. When you use `METHOD_STANDARD` (`camerakit:ckMethod="standard"`), images will be captured using the normal camera API capture method using the shutter.
[Insert GIF] [Insert GIF]
### Speed ### Still
When you use `PICTURE_MODE_SPEED` (`camerakit:ckPictureMode="speed"`), images will be captured by grabbing a single frame from the preview. This behavior is the same as SnapChat and Instagram. This method has a higher rate of motion blur but can be a better experience for users with slower cameras. When you use `METHOD_STILL` (`camerakit:ckMethod="still"`), images will be captured by grabbing a single frame from the preview. This behavior is the same as SnapChat and Instagram. This method has a higher rate of motion blur but can be a better experience for users with slower cameras.
[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 `METHOD_AUTO` (`camerakit:ckMethod="auto"`), images will be first be captured using the [standard](#standard) method. If capture consistently takes a long amount of time, the picture mode will fallback to [still](#still) capture.
[Insert GIF] [Insert GIF]
## Tap To Focus ## 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. Along with the always on auto-focus, you can enable tap to focus in your `CameraView`. See [`ckFocus`](#ckFocus) for usage. You have the option of having it on with a visible focus marker, on with no marker, or off.
[Insert GIF] [Insert GIF]

@ -11,6 +11,13 @@ import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.TreeSet; import java.util.TreeSet;
import static com.flurgle.camerakit.CameraKit.Constants.FLASH_OFF;
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.METHOD_STANDARD;
import static com.flurgle.camerakit.CameraKit.Constants.METHOD_STILL;
@SuppressWarnings("deprecation") @SuppressWarnings("deprecation")
public class Camera1 extends CameraImpl { public class Camera1 extends CameraImpl {
@ -21,10 +28,22 @@ public class Camera1 extends CameraImpl {
private Size mPreviewSize; private Size mPreviewSize;
private Size mCaptureSize; private Size mCaptureSize;
private int mDisplayOrientation;
@Facing
private int mFacing; private int mFacing;
@Flash
private int mFlash; private int mFlash;
private boolean mContinuousFocus;
private int mDisplayOrientation; @Focus
private int mFocus;
@Method
private int mMethod;
@Zoom
private int mZoom;
Camera1(CameraListener callback, PreviewImpl preview) { Camera1(CameraListener callback, PreviewImpl preview) {
super(callback, preview); super(callback, preview);
@ -57,6 +76,11 @@ public class Camera1 extends CameraImpl {
releaseCamera(); releaseCamera();
} }
@Override
void setDisplayOrientation(int displayOrientation) {
this.mDisplayOrientation = displayOrientation;
}
@Override @Override
void setFacing(@Facing int facing) { void setFacing(@Facing int facing) {
int internalFacing = new ConstantMapper.Facing(facing).map(); int internalFacing = new ConstantMapper.Facing(facing).map();
@ -81,7 +105,7 @@ public class Camera1 extends CameraImpl {
@Override @Override
void setFlash(@Flash int flash) { void setFlash(@Flash int flash) {
if (isCameraOpened()) { if (mCameraParameters != null) {
List<String> flashes = mCameraParameters.getSupportedFlashModes(); List<String> flashes = mCameraParameters.getSupportedFlashModes();
String internalFlash = new ConstantMapper.Flash(flash).map(); String internalFlash = new ConstantMapper.Flash(flash).map();
if (flashes != null && flashes.contains(internalFlash)) { if (flashes != null && flashes.contains(internalFlash)) {
@ -91,7 +115,7 @@ public class Camera1 extends CameraImpl {
String currentFlash = new ConstantMapper.Flash(mFlash).map(); String currentFlash = new ConstantMapper.Flash(mFlash).map();
if (flashes == null || !flashes.contains(currentFlash)) { if (flashes == null || !flashes.contains(currentFlash)) {
mCameraParameters.setFlashMode(Camera.Parameters.FLASH_MODE_OFF); mCameraParameters.setFlashMode(Camera.Parameters.FLASH_MODE_OFF);
mFlash = CameraKit.Constants.FLASH_OFF; mFlash = FLASH_OFF;
} }
} }
@ -102,61 +126,76 @@ public class Camera1 extends CameraImpl {
} }
@Override @Override
void setContinuousFocus(boolean continuousFocus) { void setFocus(@Focus int focus) {
this.mContinuousFocus = continuousFocus; this.mFocus = focus;
if (mCameraParameters != null) { switch (focus) {
final List<String> modes = mCameraParameters.getSupportedFocusModes(); case FOCUS_CONTINUOUS:
if (mContinuousFocus && modes.contains(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE)) { if (mCameraParameters != null) {
mCameraParameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE); final List<String> modes = mCameraParameters.getSupportedFocusModes();
} else if (modes.contains(Camera.Parameters.FOCUS_MODE_FIXED)) { if (modes.contains(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE)) {
mCameraParameters.setFocusMode(Camera.Parameters.FOCUS_MODE_FIXED); mCameraParameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);
} else if (modes.contains(Camera.Parameters.FOCUS_MODE_INFINITY)) { } else {
mCameraParameters.setFocusMode(Camera.Parameters.FOCUS_MODE_INFINITY); setFocus(FOCUS_OFF);
} else { }
mCameraParameters.setFocusMode(modes.get(0)); }
} break;
}
}
@Override case FOCUS_TAP:
void setDisplayOrientation(int displayOrientation) { setFocus(FOCUS_CONTINUOUS);
this.mDisplayOrientation = displayOrientation; break;
}
@Override case FOCUS_OFF:
void focus() { if (mCameraParameters != null) {
mCamera.autoFocus(null); final List<String> modes = mCameraParameters.getSupportedFocusModes();
if (modes.contains(Camera.Parameters.FOCUS_MODE_FIXED)) {
mCameraParameters.setFocusMode(Camera.Parameters.FOCUS_MODE_FIXED);
} else if (modes.contains(Camera.Parameters.FOCUS_MODE_INFINITY)) {
mCameraParameters.setFocusMode(Camera.Parameters.FOCUS_MODE_INFINITY);
} else {
mCameraParameters.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);
}
}
break;
}
} }
@Override @Override
void zoom(float percentZoom) { void setMethod(@Method int method) {
this.mMethod = method;
} }
@Override @Override
void captureStandard() { void setZoom(@Zoom int zoom) {
mCamera.takePicture(null, null, null, new Camera.PictureCallback() { this.mZoom = zoom;
@Override
public void onPictureTaken(byte[] data, Camera camera) {
mCameraListener.onPictureTaken(data);
camera.startPreview();
}
});
} }
@Override @Override
void captureStill() { void captureImage() {
mCamera.setOneShotPreviewCallback(new Camera.PreviewCallback() { switch (mMethod) {
@Override case METHOD_STANDARD:
public void onPreviewFrame(byte[] data, Camera camera) { mCamera.takePicture(null, null, null, new Camera.PictureCallback() {
new Thread(new ProcessStillTask(data, camera, mCameraInfo, new ProcessStillTask.OnStillProcessedListener() {
@Override @Override
public void onStillProcessed(final YuvImage yuv) { public void onPictureTaken(byte[] data, Camera camera) {
mCameraListener.onPictureTaken(yuv); mCameraListener.onPictureTaken(data);
camera.startPreview();
} }
})).start(); });
} break;
});
case METHOD_STILL:
mCamera.setOneShotPreviewCallback(new Camera.PreviewCallback() {
@Override
public void onPreviewFrame(byte[] data, Camera camera) {
new Thread(new ProcessStillTask(data, camera, mCameraInfo, new ProcessStillTask.OnStillProcessedListener() {
@Override
public void onStillProcessed(final YuvImage yuv) {
mCameraListener.onPictureTaken(yuv);
}
})).start();
}
});
break;
}
} }
@Override @Override
@ -297,7 +336,7 @@ public class Camera1 extends CameraImpl {
+ (mFacing == CameraKit.Constants.FACING_FRONT ? 180 : 0) + (mFacing == CameraKit.Constants.FACING_FRONT ? 180 : 0)
); );
setContinuousFocus(mContinuousFocus); setFocus(mFocus);
setFlash(mFlash); setFlash(mFlash);
mCamera.setParameters(mCameraParameters); mCamera.setParameters(mCameraParameters);

@ -17,42 +17,37 @@ class Camera2 extends CameraImpl {
} }
@Override @Override
void setFacing(@Facing int facing) { void setDisplayOrientation(int displayOrientation) {
}
@Override
void setFlash(@Flash int flash) {
} }
@Override @Override
void setContinuousFocus(boolean autoFocus) { void setFacing(@Facing int facing) {
} }
@Override @Override
void setDisplayOrientation(int displayOrientation) { void setFlash(@Flash int flash) {
} }
@Override @Override
void focus() { void setFocus(@Focus int focus) {
} }
@Override @Override
void zoom(float percentZoom) { void setMethod(@Method int method) {
} }
@Override @Override
void captureStandard() { void setZoom(@Zoom int zoom) {
} }
@Override @Override
void captureStill() { void captureImage() {
} }

@ -13,17 +13,15 @@ abstract class CameraImpl {
abstract void start(); abstract void start();
abstract void stop(); abstract void stop();
abstract void setFacing(@Facing int facing);
abstract void setFlash(@Flash int flash);
abstract void setContinuousFocus(boolean autoFocus);
abstract void setDisplayOrientation(int displayOrientation); abstract void setDisplayOrientation(int displayOrientation);
abstract void focus();
abstract void zoom(float percentZoom);
abstract void captureStandard(); abstract void setFacing(@Facing int facing);
abstract void captureStill(); abstract void setFlash(@Flash int flash);
abstract void setFocus(@Focus int focus);
abstract void setMethod(@Method int method);
abstract void setZoom(@Zoom int zoom);
abstract void captureImage();
abstract void startVideo(); abstract void startVideo();
abstract void endVideo(); abstract void endVideo();

@ -13,6 +13,8 @@ public class CameraKit {
public static class Constants { public static class Constants {
public static final int PERMISSION_REQUEST_CAMERA = 16;
public static final int FACING_BACK = 0; public static final int FACING_BACK = 0;
public static final int FACING_FRONT = 1; public static final int FACING_FRONT = 1;
@ -20,15 +22,31 @@ public class CameraKit {
public static final int FLASH_ON = 1; public static final int FLASH_ON = 1;
public static final int FLASH_AUTO = 2; public static final int FLASH_AUTO = 2;
public static final int CAPTURE_MODE_STANDARD = 0; public static final int METHOD_STANDARD = 0;
public static final int CAPTURE_MODE_STILL = 1; public static final int METHOD_STILL = 1;
public static final int PICTURE_MODE_AUTO = 2; public static final int METHOD_AUTO = 2;
public static final int FOCUS_OFF = 0;
public static final int FOCUS_CONTINUOUS = 1;
public static final int FOCUS_TAP = 2;
public static final int TAP_TO_FOCUS_VISIBLE = 0; public static final int ZOOM_OFF = 0;
public static final int TAP_TO_FOCUS_INVISIBLE = 1; public static final int ZOOM_PINCH = 1;
public static final int TAP_TO_FOCUS_OFF = 2;
} }
static class Defaults {
static final int DEFAULT_FACING = Constants.FACING_BACK;
static final int DEFAULT_FLASH = Constants.FLASH_OFF;
static final int DEFAULT_FOCUS = Constants.FOCUS_OFF;
static final int DEFAULT_METHOD = Constants.METHOD_STANDARD;
static final int DEFAULT_ZOOM = Constants.ZOOM_OFF;
static final int DEFAULT_JPEG_QUALITY = 100;
static final boolean DEFAULT_CROP_OUTPUT = false;
static final boolean DEFAULT_ADJUST_VIEW_BOUNDS = false;
}
} }

@ -8,64 +8,47 @@ import android.content.pm.PackageManager;
import android.content.res.TypedArray; import android.content.res.TypedArray;
import android.graphics.Rect; import android.graphics.Rect;
import android.graphics.YuvImage; import android.graphics.YuvImage;
import android.os.Parcel;
import android.os.Parcelable;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.support.v4.app.ActivityCompat; import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat; import android.support.v4.content.ContextCompat;
import android.support.v4.hardware.display.DisplayManagerCompat; import android.support.v4.hardware.display.DisplayManagerCompat;
import android.support.v4.os.ParcelableCompat;
import android.support.v4.os.ParcelableCompatCreatorCallbacks;
import android.support.v4.view.ViewCompat; import android.support.v4.view.ViewCompat;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.view.Display; import android.view.Display;
import android.view.MotionEvent;
import android.view.View;
import android.widget.FrameLayout; import android.widget.FrameLayout;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.File; import java.io.File;
import static com.flurgle.camerakit.CameraKit.Constants.CAPTURE_MODE_STANDARD;
import static com.flurgle.camerakit.CameraKit.Constants.CAPTURE_MODE_STILL;
import static com.flurgle.camerakit.CameraKit.Constants.FACING_BACK; import static com.flurgle.camerakit.CameraKit.Constants.FACING_BACK;
import static com.flurgle.camerakit.CameraKit.Constants.FACING_FRONT; import static com.flurgle.camerakit.CameraKit.Constants.FACING_FRONT;
import static com.flurgle.camerakit.CameraKit.Constants.FLASH_AUTO; 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.TAP_TO_FOCUS_VISIBLE; import static com.flurgle.camerakit.CameraKit.Constants.METHOD_STANDARD;
public class CameraView extends FrameLayout { public class CameraView extends FrameLayout {
private static final int PERMISSION_REQUEST_CAMERA = 16;
private static final int DEFAULT_CAPTURE_SIZE = -1;
private static final int DEFAULT_JPEG_COMPRESSION = 100;
@Facing @Facing
private int mFacing; private int mFacing;
@Flash @Flash
private int mFlash; private int mFlash;
@TapToFocus @Focus
private int mTapToFocus; private int mFocus;
@CaptureMode
private int mCaptureMode;
private boolean mCropOutput;
private boolean mContinuousFocus;
private float mCaptureSize; @Method
private int mMethod;
private int mJpegCompression; @Zoom
private int mZoom;
private int mJpegQuality;
private boolean mCropOutput;
private boolean mAdjustViewBounds; private boolean mAdjustViewBounds;
private boolean mWaitingForPermission;
private CameraListenerMiddleWare mCameraListener; private CameraListenerMiddleWare mCameraListener;
private DisplayOrientationDetector mDisplayOrientationDetector; private DisplayOrientationDetector mDisplayOrientationDetector;
@ -79,8 +62,6 @@ public class CameraView extends FrameLayout {
@SuppressWarnings("all") @SuppressWarnings("all")
public CameraView(@NonNull Context context, @Nullable AttributeSet attrs) { public CameraView(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs); super(context, attrs);
if (attrs != null) { if (attrs != null) {
TypedArray a = context.getTheme().obtainStyledAttributes( TypedArray a = context.getTheme().obtainStyledAttributes(
attrs, attrs,
@ -88,15 +69,15 @@ public class CameraView extends FrameLayout {
0, 0); 0, 0);
try { try {
mFacing = a.getInteger(R.styleable.CameraView_ckFacing, FACING_BACK); mFacing = a.getInteger(R.styleable.CameraView_ckFacing, CameraKit.Defaults.DEFAULT_FACING);
mFlash = a.getInteger(R.styleable.CameraView_ckFlash, FLASH_OFF); mFlash = a.getInteger(R.styleable.CameraView_ckFlash, CameraKit.Defaults.DEFAULT_FLASH);
mTapToFocus = a.getInteger(R.styleable.CameraView_ckTapToFocus, TAP_TO_FOCUS_VISIBLE); mFocus = a.getInteger(R.styleable.CameraView_ckFocus, CameraKit.Defaults.DEFAULT_FOCUS);
mCaptureMode = a.getInteger(R.styleable.CameraView_ckCaptureMode, CAPTURE_MODE_STANDARD); mMethod = a.getInteger(R.styleable.CameraView_ckMethod, CameraKit.Defaults.DEFAULT_METHOD);
mCropOutput = a.getBoolean(R.styleable.CameraView_ckCropOutput, false); mZoom = a.getInteger(R.styleable.CameraView_ckZoom, CameraKit.Defaults.DEFAULT_ZOOM);
mContinuousFocus = a.getBoolean(R.styleable.CameraView_ckContinuousFocus, true);
mCaptureSize = a.getFloat(R.styleable.CameraView_ckCaptureSize, DEFAULT_CAPTURE_SIZE); mJpegQuality = a.getInteger(R.styleable.CameraView_ckJpegQuality, CameraKit.Defaults.DEFAULT_JPEG_QUALITY);
mJpegCompression = a.getInteger(R.styleable.CameraView_ckJpegCompression, DEFAULT_JPEG_COMPRESSION); mCropOutput = a.getBoolean(R.styleable.CameraView_ckCropOutput, CameraKit.Defaults.DEFAULT_CROP_OUTPUT);
mAdjustViewBounds = a.getBoolean(R.styleable.CameraView_android_adjustViewBounds, false); mAdjustViewBounds = a.getBoolean(R.styleable.CameraView_android_adjustViewBounds, CameraKit.Defaults.DEFAULT_ADJUST_VIEW_BOUNDS);
} finally { } finally {
a.recycle(); a.recycle();
} }
@ -109,11 +90,9 @@ public class CameraView extends FrameLayout {
setFacing(mFacing); setFacing(mFacing);
setFlash(mFlash); setFlash(mFlash);
setCaptureMode(mCaptureMode); setFocus(mFocus);
setCropOutput(mCropOutput); setMethod(mMethod);
setTapToFocus(mTapToFocus); setZoom(mZoom);
setContinuousFocus(mContinuousFocus);
setCaptureSize(mCaptureSize);
mDisplayOrientationDetector = new DisplayOrientationDetector(context) { mDisplayOrientationDetector = new DisplayOrientationDetector(context) {
@Override @Override
@ -140,30 +119,34 @@ public class CameraView extends FrameLayout {
} }
@Override @Override
protected Parcelable onSaveInstanceState() { protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
SavedState state = new SavedState(super.onSaveInstanceState()); if (mAdjustViewBounds) {
state.facing = mFacing; Size previewSize = getPreviewSize();
state.flash = mFlash; if (getLayoutParams().width == LayoutParams.WRAP_CONTENT) {
return state; int height = MeasureSpec.getSize(heightMeasureSpec);
} float ratio = (float) height / (float) previewSize.getWidth();
int width = (int) (previewSize.getHeight() * ratio);
@Override super.onMeasure(
protected void onRestoreInstanceState(Parcelable state) { MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
if (!(state instanceof SavedState)) { heightMeasureSpec
super.onRestoreInstanceState(state); );
return; } else if (getLayoutParams().height == LayoutParams.WRAP_CONTENT) {
int width = MeasureSpec.getSize(widthMeasureSpec);
float ratio = (float) width / (float) previewSize.getHeight();
int height = (int) (previewSize.getWidth() * ratio);
super.onMeasure(
widthMeasureSpec,
MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY)
);
}
} else {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
} }
SavedState ss = (SavedState) state;
super.onRestoreInstanceState(ss.getSuperState());
setFacing(ss.facing);
setFlash(ss.flash);
} }
public void start() { public void start() {
int permissionCheck = ContextCompat.checkSelfPermission(getContext(), Manifest.permission.CAMERA); int permissionCheck = ContextCompat.checkSelfPermission(getContext(), Manifest.permission.CAMERA);
if (permissionCheck == PackageManager.PERMISSION_GRANTED) { if (permissionCheck == PackageManager.PERMISSION_GRANTED) {
mWaitingForPermission = false;
mCameraImpl.start(); mCameraImpl.start();
} else { } else {
requestCameraPermission(); requestCameraPermission();
@ -179,6 +162,34 @@ public class CameraView extends FrameLayout {
mCameraImpl.setFacing(facing); mCameraImpl.setFacing(facing);
} }
public void setFlash(@Flash int flash) {
this.mFlash = flash;
mCameraImpl.setFlash(flash);
}
public void setFocus(@Focus int focus) {
this.mFocus = focus;
mCameraImpl.setFocus(mFocus);
}
public void setMethod(@Method int method) {
this.mMethod = method;
mCameraImpl.setMethod(mMethod);
}
public void setZoom(@Zoom int zoom) {
this.mZoom = zoom;
mCameraImpl.setZoom(mZoom);
}
public void setJpegQuality(int jpegQuality) {
this.mJpegQuality = jpegQuality;
}
public void setCropOutput(boolean cropOutput) {
this.mCropOutput = cropOutput;
}
@Facing @Facing
public int toggleFacing() { public int toggleFacing() {
switch (mFacing) { switch (mFacing) {
@ -194,11 +205,6 @@ public class CameraView extends FrameLayout {
return mFacing; return mFacing;
} }
public void setFlash(@Flash int flash) {
this.mFlash = flash;
mCameraImpl.setFlash(flash);
}
@Flash @Flash
public int toggleFlash() { public int toggleFlash() {
switch (mFlash) { switch (mFlash) {
@ -218,50 +224,12 @@ public class CameraView extends FrameLayout {
return mFlash; return mFlash;
} }
public void setCaptureMode(@CaptureMode int captureMode) {
this.mCaptureMode = captureMode;
}
@CaptureMode
public int getCaptureMode() {
return mCaptureMode;
}
public void setCropOutput(boolean cropOutput) {
this.mCropOutput = cropOutput;
}
public void setTapToFocus(@TapToFocus int tapToFocus) {
this.mTapToFocus = tapToFocus;
if (tapToFocus == CameraKit.Constants.TAP_TO_FOCUS_OFF) {
mPreviewImpl.getView().setOnTouchListener(null);
} else {
mPreviewImpl.getView().setOnTouchListener(mTapToFocusOnTouchListener);
}
}
public void setContinuousFocus(boolean continuousFocus) {
this.mContinuousFocus = continuousFocus;
mCameraImpl.setContinuousFocus(continuousFocus);
}
public void setCaptureSize(float captureSize) {
this.mCaptureSize = captureSize;
}
public void setCameraListener(CameraListener cameraListener) { public void setCameraListener(CameraListener cameraListener) {
this.mCameraListener.setCameraListener(cameraListener); this.mCameraListener.setCameraListener(cameraListener);
} }
public void capturePicture() { public void captureImage() {
switch (mCaptureMode) { mCameraImpl.captureImage();
case CAPTURE_MODE_STANDARD:
mCameraImpl.captureStandard();
break;
case CAPTURE_MODE_STILL:
mCameraImpl.captureStill();
break;
}
} }
public void startRecordingVideo() { public void startRecordingVideo() {
@ -291,74 +259,11 @@ public class CameraView extends FrameLayout {
} }
if (activity != null) { if (activity != null) {
ActivityCompat.requestPermissions(activity, new String[]{Manifest.permission.CAMERA, Manifest.permission.RECORD_AUDIO}, PERMISSION_REQUEST_CAMERA); ActivityCompat.requestPermissions(
mWaitingForPermission = true; activity,
} new String[]{Manifest.permission.CAMERA, Manifest.permission.RECORD_AUDIO},
} CameraKit.Constants.PERMISSION_REQUEST_CAMERA);
@Override
public void onWindowFocusChanged(boolean hasWindowFocus) {
super.onWindowFocusChanged(hasWindowFocus);
if (mWaitingForPermission) {
if (ContextCompat.checkSelfPermission(getContext(), Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED) {
start();
}
}
}
private OnTouchListener mTapToFocusOnTouchListener = new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
mCameraImpl.focus();
}
return false;
}
};
protected static class SavedState extends BaseSavedState {
@Facing
private int facing;
@Flash
private int flash;
@SuppressWarnings("WrongConstant")
public SavedState(Parcel source, ClassLoader loader) {
super(source);
facing = source.readInt();
flash = source.readInt();
}
public SavedState(Parcelable superState) {
super(superState);
}
@Override
public void writeToParcel(Parcel out, int flags) {
super.writeToParcel(out, flags);
out.writeInt(facing);
out.writeInt(flash);
} }
public static final Parcelable.Creator<SavedState> CREATOR = ParcelableCompat.newCreator(new ParcelableCompatCreatorCallbacks<SavedState>() {
@Override
public SavedState createFromParcel(Parcel in, ClassLoader loader) {
return new SavedState(in, loader);
}
@Override
public SavedState[] newArray(int size) {
return new SavedState[size];
}
});
} }
private class CameraListenerMiddleWare extends CameraListener { private class CameraListenerMiddleWare extends CameraListener {
@ -381,10 +286,10 @@ public class CameraView extends FrameLayout {
public void onPictureTaken(byte[] jpeg) { public void onPictureTaken(byte[] jpeg) {
super.onPictureTaken(jpeg); super.onPictureTaken(jpeg);
if (mCropOutput) { if (mCropOutput) {
int width = mCaptureMode == CAPTURE_MODE_STANDARD ? mCameraImpl.getCaptureResolution().getWidth() : mCameraImpl.getPreviewResolution().getWidth(); int width = mMethod == METHOD_STANDARD ? mCameraImpl.getCaptureResolution().getWidth() : mCameraImpl.getPreviewResolution().getWidth();
int height = mCaptureMode == CAPTURE_MODE_STANDARD ? mCameraImpl.getCaptureResolution().getHeight() : mCameraImpl.getPreviewResolution().getHeight(); int height = mMethod == METHOD_STANDARD ? mCameraImpl.getCaptureResolution().getHeight() : mCameraImpl.getPreviewResolution().getHeight();
AspectRatio outputRatio = AspectRatio.of(getWidth(), getHeight()); AspectRatio outputRatio = AspectRatio.of(getWidth(), getHeight());
getCameraListener().onPictureTaken(new CenterCrop(jpeg, outputRatio, mJpegCompression).getJpeg()); getCameraListener().onPictureTaken(new CenterCrop(jpeg, outputRatio, mJpegQuality).getJpeg());
} else { } else {
getCameraListener().onPictureTaken(jpeg); getCameraListener().onPictureTaken(jpeg);
} }
@ -395,10 +300,10 @@ public class CameraView extends FrameLayout {
super.onPictureTaken(yuv); super.onPictureTaken(yuv);
if (mCropOutput) { if (mCropOutput) {
AspectRatio outputRatio = AspectRatio.of(getWidth(), getHeight()); AspectRatio outputRatio = AspectRatio.of(getWidth(), getHeight());
getCameraListener().onPictureTaken(new CenterCrop(yuv, outputRatio, mJpegCompression).getJpeg()); getCameraListener().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()), mJpegCompression, out); yuv.compressToJpeg(new Rect(0, 0, yuv.getWidth(), yuv.getHeight()), mJpegQuality, out);
getCameraListener().onPictureTaken(out.toByteArray()); getCameraListener().onPictureTaken(out.toByteArray());
} }
} }

@ -14,27 +14,33 @@
<enum name="auto" value="2" /> <enum name="auto" value="2" />
</attr> </attr>
<attr name="ckCaptureMode" format="enum"> <attr name="ckFocus" format="enum">
<enum name="standard" value="0" /> <enum name="off" value="0" />
<enum name="still" value="1" /> <enum name="continuous" value="1" />
<enum name="auto" value="2" />
</attr>
<attr name="ckCropOutput" format="boolean" /> <!-- TODO: Set to unique value when feature added -->
<enum name="tap" value="1" />
</attr>
<attr name="ckPinchToZoom" format="boolean" /> <attr name="ckZoom" format="enum">
<enum name="off" value="0" />
<attr name="ckTapToFocus" format="enum"> <!-- TODO: Set to unique value when feature added -->
<enum name="visible" value="0" /> <enum name="pinch" value="0" />
<enum name="invisible" value="1" />
<enum name="off" value="2" />
</attr> </attr>
<attr name="ckContinuousFocus" format="boolean" />
<attr name="ckCaptureSize" format="float" /> <attr name="ckMethod" format="enum">
<enum name="standard" value="0" />
<enum name="still" value="1" />
<attr name="ckJpegCompression" format="integer" /> <!-- TODO: Set to unique value when feature added -->
<enum name="auto" value="0" />
</attr>
<attr name="ckJpegQuality" format="integer" />
<attr name="ckCropOutput" format="boolean" />
<attr name="android:adjustViewBounds" /> <attr name="android:adjustViewBounds" />

@ -5,11 +5,11 @@ import android.support.annotation.IntDef;
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.TAP_TO_FOCUS_INVISIBLE; import static com.flurgle.camerakit.CameraKit.Constants.FOCUS_TAP;
import static com.flurgle.camerakit.CameraKit.Constants.TAP_TO_FOCUS_OFF; import static com.flurgle.camerakit.CameraKit.Constants.FOCUS_OFF;
import static com.flurgle.camerakit.CameraKit.Constants.TAP_TO_FOCUS_VISIBLE; import static com.flurgle.camerakit.CameraKit.Constants.FOCUS_CONTINUOUS;
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
@IntDef({TAP_TO_FOCUS_VISIBLE, TAP_TO_FOCUS_INVISIBLE, TAP_TO_FOCUS_OFF}) @IntDef({FOCUS_CONTINUOUS, FOCUS_TAP, FOCUS_OFF})
public @interface Focus { public @interface Focus {
} }

@ -5,10 +5,10 @@ import android.support.annotation.IntDef;
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.CAPTURE_MODE_STANDARD; import static com.flurgle.camerakit.CameraKit.Constants.METHOD_STANDARD;
import static com.flurgle.camerakit.CameraKit.Constants.CAPTURE_MODE_STILL; import static com.flurgle.camerakit.CameraKit.Constants.METHOD_STILL;
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
@IntDef({CAPTURE_MODE_STANDARD, CAPTURE_MODE_STILL}) @IntDef({METHOD_STANDARD, METHOD_STILL})
public @interface Method { public @interface Method {
} }

@ -1,8 +1,14 @@
package com.flurgle.camerakit; package com.flurgle.camerakit;
/** import android.support.annotation.IntDef;
* Created by dylanmcintyre on 2/21/17.
*/
public class Zoom { import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import static com.flurgle.camerakit.CameraKit.Constants.ZOOM_OFF;
import static com.flurgle.camerakit.CameraKit.Constants.ZOOM_PINCH;
@Retention(RetentionPolicy.SOURCE)
@IntDef({ZOOM_OFF, ZOOM_PINCH})
public @interface Zoom {
} }

@ -136,7 +136,7 @@ public class MainActivity extends AppCompatActivity {
startActivity(intent); startActivity(intent);
} }
}); });
camera.capturePicture(); camera.captureImage();
} }
@OnClick(R.id.captureVideo) @OnClick(R.id.captureVideo)
@ -191,10 +191,10 @@ public class MainActivity extends AppCompatActivity {
RadioGroup.OnCheckedChangeListener captureModeChangedListener = new RadioGroup.OnCheckedChangeListener() { RadioGroup.OnCheckedChangeListener captureModeChangedListener = new RadioGroup.OnCheckedChangeListener() {
@Override @Override
public void onCheckedChanged(RadioGroup group, int checkedId) { public void onCheckedChanged(RadioGroup group, int checkedId) {
camera.setCaptureMode( camera.setMethod(
checkedId == R.id.modeCaptureQuality ? checkedId == R.id.modeCaptureQuality ?
CameraKit.Constants.CAPTURE_MODE_STANDARD : CameraKit.Constants.METHOD_STANDARD :
CameraKit.Constants.CAPTURE_MODE_STILL CameraKit.Constants.METHOD_STILL
); );
Toast.makeText(MainActivity.this, "Picture capture set to" + (checkedId == R.id.modeCaptureQuality ? " quality!" : " speed!"), Toast.LENGTH_SHORT).show(); Toast.makeText(MainActivity.this, "Picture capture set to" + (checkedId == R.id.modeCaptureQuality ? " quality!" : " speed!"), Toast.LENGTH_SHORT).show();
@ -215,12 +215,12 @@ public class MainActivity extends AppCompatActivity {
RadioGroup.OnCheckedChangeListener tapToFocusModeChangedListener = new RadioGroup.OnCheckedChangeListener() { RadioGroup.OnCheckedChangeListener tapToFocusModeChangedListener = new RadioGroup.OnCheckedChangeListener() {
@Override @Override
public void onCheckedChanged(RadioGroup group, int checkedId) { public void onCheckedChanged(RadioGroup group, int checkedId) {
camera.setTapToFocus( camera.setFocus(
checkedId == R.id.modeTapToFocusVisible ? checkedId == R.id.modeTapToFocusVisible ?
CameraKit.Constants.TAP_TO_FOCUS_VISIBLE : CameraKit.Constants.FOCUS_CONTINUOUS :
checkedId == R.id.modeTapToFocusInvisible ? checkedId == R.id.modeTapToFocusInvisible ?
CameraKit.Constants.TAP_TO_FOCUS_INVISIBLE : CameraKit.Constants.FOCUS_TAP :
CameraKit.Constants.TAP_TO_FOCUS_OFF CameraKit.Constants.FOCUS_OFF
); );
Toast.makeText(MainActivity.this, "Tap to focus is" + (checkedId == R.id.modeTapToFocusOff ? " off!" : (checkedId == R.id.modeTapToFocusVisible) ? " on and visible!" : " on and invisible!"), Toast.LENGTH_SHORT).show(); Toast.makeText(MainActivity.this, "Tap to focus is" + (checkedId == R.id.modeTapToFocusOff ? " off!" : (checkedId == R.id.modeTapToFocusVisible) ? " on and visible!" : " on and invisible!"), Toast.LENGTH_SHORT).show();

@ -19,11 +19,11 @@
android:layout_height="500dp" android:layout_height="500dp"
android:layout_gravity="center_horizontal" android:layout_gravity="center_horizontal"
app:ckCaptureMode="standard" app:ckCaptureMode="standard"
app:ckContinuousFocus="true"
app:ckCropOutput="false" app:ckCropOutput="false"
app:ckFacing="back" app:ckFacing="back"
app:ckFlash="off" app:ckFlash="off"
app:ckTapToFocus="visible" /> app:ckFocus="continuous"
app:ckJpegQuality="100" />
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"

Loading…
Cancel
Save