|
|
@ -1,7 +1,6 @@ |
|
|
|
package com.otaliastudios.cameraview; |
|
|
|
package com.otaliastudios.cameraview; |
|
|
|
|
|
|
|
|
|
|
|
import android.Manifest; |
|
|
|
import android.Manifest; |
|
|
|
import android.annotation.SuppressLint; |
|
|
|
|
|
|
|
import android.annotation.TargetApi; |
|
|
|
import android.annotation.TargetApi; |
|
|
|
import android.app.Activity; |
|
|
|
import android.app.Activity; |
|
|
|
import android.content.Context; |
|
|
|
import android.content.Context; |
|
|
@ -48,31 +47,20 @@ public class CameraView extends FrameLayout { |
|
|
|
private final static int DEFAULT_JPEG_QUALITY = 100; |
|
|
|
private final static int DEFAULT_JPEG_QUALITY = 100; |
|
|
|
private final static boolean DEFAULT_CROP_OUTPUT = false; |
|
|
|
private final static boolean DEFAULT_CROP_OUTPUT = false; |
|
|
|
|
|
|
|
|
|
|
|
private Handler mWorkerHandler; |
|
|
|
// Self managed parameters
|
|
|
|
|
|
|
|
|
|
|
|
private Handler getWorkerHandler() { |
|
|
|
|
|
|
|
synchronized (this) { |
|
|
|
|
|
|
|
if (mWorkerHandler == null) { |
|
|
|
|
|
|
|
HandlerThread workerThread = new HandlerThread("CameraViewWorker"); |
|
|
|
|
|
|
|
workerThread.setDaemon(true); |
|
|
|
|
|
|
|
workerThread.start(); |
|
|
|
|
|
|
|
mWorkerHandler = new Handler(workerThread.getLooper()); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return mWorkerHandler; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private void run(Runnable runnable) { |
|
|
|
|
|
|
|
getWorkerHandler().post(runnable); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private int mJpegQuality; |
|
|
|
private int mJpegQuality; |
|
|
|
private boolean mCropOutput; |
|
|
|
private boolean mCropOutput; |
|
|
|
|
|
|
|
private float mZoomValue; |
|
|
|
|
|
|
|
private float mExposureCorrectionValue; |
|
|
|
|
|
|
|
private HashMap<Gesture, GestureAction> mGestureMap = new HashMap<>(4); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Components
|
|
|
|
private CameraCallbacks mCameraCallbacks; |
|
|
|
private CameraCallbacks mCameraCallbacks; |
|
|
|
private OrientationHelper mOrientationHelper; |
|
|
|
private OrientationHelper mOrientationHelper; |
|
|
|
private CameraController mCameraController; |
|
|
|
private CameraController mCameraController; |
|
|
|
private Preview mPreviewImpl; |
|
|
|
private Preview mPreviewImpl; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Views
|
|
|
|
private GridLinesLayout mGridLinesLayout; |
|
|
|
private GridLinesLayout mGridLinesLayout; |
|
|
|
private PinchGestureLayout mPinchGestureLayout; |
|
|
|
private PinchGestureLayout mPinchGestureLayout; |
|
|
|
private TapGestureLayout mTapGestureLayout; |
|
|
|
private TapGestureLayout mTapGestureLayout; |
|
|
@ -80,10 +68,9 @@ public class CameraView extends FrameLayout { |
|
|
|
private boolean mIsStarted; |
|
|
|
private boolean mIsStarted; |
|
|
|
private boolean mKeepScreenOn; |
|
|
|
private boolean mKeepScreenOn; |
|
|
|
|
|
|
|
|
|
|
|
private float mZoomValue; |
|
|
|
// Threading
|
|
|
|
private float mExposureCorrectionValue; |
|
|
|
private Handler mUiHandler; |
|
|
|
|
|
|
|
private WorkerHandler mWorkerHandler; |
|
|
|
private HashMap<Gesture, GestureAction> mGestureMap = new HashMap<>(4); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public CameraView(@NonNull Context context) { |
|
|
|
public CameraView(@NonNull Context context) { |
|
|
|
super(context, null); |
|
|
|
super(context, null); |
|
|
@ -120,9 +107,14 @@ public class CameraView extends FrameLayout { |
|
|
|
GestureAction scrollVerticalGesture = GestureAction.fromValue(a.getInteger(R.styleable.CameraView_cameraGestureScrollVertical, GestureAction.DEFAULT_SCROLL_VERTICAL.value())); |
|
|
|
GestureAction scrollVerticalGesture = GestureAction.fromValue(a.getInteger(R.styleable.CameraView_cameraGestureScrollVertical, GestureAction.DEFAULT_SCROLL_VERTICAL.value())); |
|
|
|
a.recycle(); |
|
|
|
a.recycle(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Components
|
|
|
|
mCameraCallbacks = new CameraCallbacks(); |
|
|
|
mCameraCallbacks = new CameraCallbacks(); |
|
|
|
mPreviewImpl = new TextureViewPreview(context, this); |
|
|
|
mPreviewImpl = new TextureViewPreview(context, this); |
|
|
|
mCameraController = new Camera1(mCameraCallbacks, mPreviewImpl); |
|
|
|
mCameraController = new Camera1(mCameraCallbacks, mPreviewImpl); |
|
|
|
|
|
|
|
mUiHandler = new Handler(Looper.getMainLooper()); |
|
|
|
|
|
|
|
mWorkerHandler = new WorkerHandler("CameraViewWorker"); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Views
|
|
|
|
mGridLinesLayout = new GridLinesLayout(context); |
|
|
|
mGridLinesLayout = new GridLinesLayout(context); |
|
|
|
mPinchGestureLayout = new PinchGestureLayout(context); |
|
|
|
mPinchGestureLayout = new PinchGestureLayout(context); |
|
|
|
mTapGestureLayout = new TapGestureLayout(context); |
|
|
|
mTapGestureLayout = new TapGestureLayout(context); |
|
|
@ -134,11 +126,11 @@ public class CameraView extends FrameLayout { |
|
|
|
|
|
|
|
|
|
|
|
mIsStarted = false; |
|
|
|
mIsStarted = false; |
|
|
|
|
|
|
|
|
|
|
|
// Self managed
|
|
|
|
// Apply self managed
|
|
|
|
setCropOutput(cropOutput); |
|
|
|
setCropOutput(cropOutput); |
|
|
|
setJpegQuality(jpegQuality); |
|
|
|
setJpegQuality(jpegQuality); |
|
|
|
|
|
|
|
|
|
|
|
// Camera controller params
|
|
|
|
// Apply camera controller params
|
|
|
|
setFacing(facing); |
|
|
|
setFacing(facing); |
|
|
|
setFlash(flash); |
|
|
|
setFlash(flash); |
|
|
|
setSessionType(sessionType); |
|
|
|
setSessionType(sessionType); |
|
|
@ -146,7 +138,7 @@ public class CameraView extends FrameLayout { |
|
|
|
setWhiteBalance(whiteBalance); |
|
|
|
setWhiteBalance(whiteBalance); |
|
|
|
setGrid(grid); |
|
|
|
setGrid(grid); |
|
|
|
|
|
|
|
|
|
|
|
// Gestures
|
|
|
|
// Apply gestures
|
|
|
|
mapGesture(Gesture.TAP, tapGesture); |
|
|
|
mapGesture(Gesture.TAP, tapGesture); |
|
|
|
// mapGesture(Gesture.DOUBLE_TAP, doubleTapGesture);
|
|
|
|
// mapGesture(Gesture.DOUBLE_TAP, doubleTapGesture);
|
|
|
|
mapGesture(Gesture.LONG_TAP, longTapGesture); |
|
|
|
mapGesture(Gesture.LONG_TAP, longTapGesture); |
|
|
@ -497,12 +489,7 @@ public class CameraView extends FrameLayout { |
|
|
|
|
|
|
|
|
|
|
|
if (checkPermissions(getSessionType())) { |
|
|
|
if (checkPermissions(getSessionType())) { |
|
|
|
mIsStarted = true; |
|
|
|
mIsStarted = true; |
|
|
|
run(new Runnable() { |
|
|
|
mCameraController.start(); |
|
|
|
@Override |
|
|
|
|
|
|
|
public void run() { |
|
|
|
|
|
|
|
mCameraController.start(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -584,9 +571,8 @@ public class CameraView extends FrameLayout { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public void destroy() { |
|
|
|
public void destroy() { |
|
|
|
|
|
|
|
// TODO: this is not strictly needed
|
|
|
|
mCameraCallbacks.clearListeners(); // Release inner listener.
|
|
|
|
mCameraCallbacks.clearListeners(); // Release inner listener.
|
|
|
|
// This might be useless, but no time to think about it now.
|
|
|
|
|
|
|
|
mWorkerHandler = null; |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
//endregion
|
|
|
|
//endregion
|
|
|
@ -788,12 +774,7 @@ public class CameraView extends FrameLayout { |
|
|
|
* @param facing a facing value. |
|
|
|
* @param facing a facing value. |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
public void setFacing(final Facing facing) { |
|
|
|
public void setFacing(final Facing facing) { |
|
|
|
run(new Runnable() { |
|
|
|
mCameraController.setFacing(facing); |
|
|
|
@Override |
|
|
|
|
|
|
|
public void run() { |
|
|
|
|
|
|
|
mCameraController.setFacing(facing); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -1214,18 +1195,13 @@ public class CameraView extends FrameLayout { |
|
|
|
|
|
|
|
|
|
|
|
class CameraCallbacks { |
|
|
|
class CameraCallbacks { |
|
|
|
|
|
|
|
|
|
|
|
private ArrayList<CameraListener> mListeners; |
|
|
|
private ArrayList<CameraListener> mListeners = new ArrayList<>(2); |
|
|
|
private Handler uiHandler; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
CameraCallbacks() { |
|
|
|
CameraCallbacks() {} |
|
|
|
mListeners = new ArrayList<>(2); |
|
|
|
|
|
|
|
uiHandler = new Handler(Looper.getMainLooper()); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public void dispatchOnCameraOpened(final CameraOptions options) { |
|
|
|
public void dispatchOnCameraOpened(final CameraOptions options) { |
|
|
|
uiHandler.post(new Runnable() { |
|
|
|
mUiHandler.post(new Runnable() { |
|
|
|
@Override |
|
|
|
@Override |
|
|
|
public void run() { |
|
|
|
public void run() { |
|
|
|
for (CameraListener listener : mListeners) { |
|
|
|
for (CameraListener listener : mListeners) { |
|
|
@ -1237,7 +1213,7 @@ public class CameraView extends FrameLayout { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public void dispatchOnCameraClosed() { |
|
|
|
public void dispatchOnCameraClosed() { |
|
|
|
uiHandler.post(new Runnable() { |
|
|
|
mUiHandler.post(new Runnable() { |
|
|
|
@Override |
|
|
|
@Override |
|
|
|
public void run() { |
|
|
|
public void run() { |
|
|
|
for (CameraListener listener : mListeners) { |
|
|
|
for (CameraListener listener : mListeners) { |
|
|
@ -1253,7 +1229,7 @@ public class CameraView extends FrameLayout { |
|
|
|
// Request a layout pass for onMeasure() to do its stuff.
|
|
|
|
// Request a layout pass for onMeasure() to do its stuff.
|
|
|
|
// Potentially this will change CameraView size, which changes Surface size,
|
|
|
|
// Potentially this will change CameraView size, which changes Surface size,
|
|
|
|
// which triggers a new Preview size. But hopefully it will converge.
|
|
|
|
// which triggers a new Preview size. But hopefully it will converge.
|
|
|
|
uiHandler.post(new Runnable() { |
|
|
|
mUiHandler.post(new Runnable() { |
|
|
|
@Override |
|
|
|
@Override |
|
|
|
public void run() { |
|
|
|
public void run() { |
|
|
|
requestLayout(); |
|
|
|
requestLayout(); |
|
|
@ -1280,7 +1256,7 @@ public class CameraView extends FrameLayout { |
|
|
|
* because it was taken with the front camera. |
|
|
|
* because it was taken with the front camera. |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
public void processImage(final byte[] jpeg, final boolean consistentWithView, final boolean flipHorizontally) { |
|
|
|
public void processImage(final byte[] jpeg, final boolean consistentWithView, final boolean flipHorizontally) { |
|
|
|
getWorkerHandler().post(new Runnable() { |
|
|
|
mWorkerHandler.post(new Runnable() { |
|
|
|
@Override |
|
|
|
@Override |
|
|
|
public void run() { |
|
|
|
public void run() { |
|
|
|
byte[] jpeg2 = jpeg; |
|
|
|
byte[] jpeg2 = jpeg; |
|
|
@ -1300,25 +1276,30 @@ public class CameraView extends FrameLayout { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public void processSnapshot(YuvImage yuv, boolean consistentWithView, boolean flipHorizontally) { |
|
|
|
public void processSnapshot(final YuvImage yuv, final boolean consistentWithView, boolean flipHorizontally) { |
|
|
|
byte[] jpeg; |
|
|
|
mWorkerHandler.post(new Runnable() { |
|
|
|
if (mCropOutput && mPreviewImpl.isCropping()) { |
|
|
|
@Override |
|
|
|
int w = consistentWithView ? getWidth() : getHeight(); |
|
|
|
public void run() { |
|
|
|
int h = consistentWithView ? getHeight() : getWidth(); |
|
|
|
byte[] jpeg; |
|
|
|
AspectRatio targetRatio = AspectRatio.of(w, h); |
|
|
|
if (mCropOutput && mPreviewImpl.isCropping()) { |
|
|
|
jpeg = CropHelper.cropToJpeg(yuv, targetRatio, mJpegQuality); |
|
|
|
int w = consistentWithView ? getWidth() : getHeight(); |
|
|
|
} else { |
|
|
|
int h = consistentWithView ? getHeight() : getWidth(); |
|
|
|
ByteArrayOutputStream out = new ByteArrayOutputStream(); |
|
|
|
AspectRatio targetRatio = AspectRatio.of(w, h); |
|
|
|
yuv.compressToJpeg(new Rect(0, 0, yuv.getWidth(), yuv.getHeight()), mJpegQuality, out); |
|
|
|
jpeg = CropHelper.cropToJpeg(yuv, targetRatio, mJpegQuality); |
|
|
|
jpeg = out.toByteArray(); |
|
|
|
} else { |
|
|
|
} |
|
|
|
ByteArrayOutputStream out = new ByteArrayOutputStream(); |
|
|
|
dispatchOnPictureTaken(jpeg); |
|
|
|
yuv.compressToJpeg(new Rect(0, 0, yuv.getWidth(), yuv.getHeight()), mJpegQuality, out); |
|
|
|
|
|
|
|
jpeg = out.toByteArray(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
dispatchOnPictureTaken(jpeg); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private void dispatchOnPictureTaken(byte[] jpeg) { |
|
|
|
private void dispatchOnPictureTaken(byte[] jpeg) { |
|
|
|
final byte[] data = jpeg; |
|
|
|
final byte[] data = jpeg; |
|
|
|
uiHandler.post(new Runnable() { |
|
|
|
mUiHandler.post(new Runnable() { |
|
|
|
@Override |
|
|
|
@Override |
|
|
|
public void run() { |
|
|
|
public void run() { |
|
|
|
for (CameraListener listener : mListeners) { |
|
|
|
for (CameraListener listener : mListeners) { |
|
|
@ -1330,7 +1311,7 @@ public class CameraView extends FrameLayout { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public void dispatchOnVideoTaken(final File video) { |
|
|
|
public void dispatchOnVideoTaken(final File video) { |
|
|
|
uiHandler.post(new Runnable() { |
|
|
|
mUiHandler.post(new Runnable() { |
|
|
|
@Override |
|
|
|
@Override |
|
|
|
public void run() { |
|
|
|
public void run() { |
|
|
|
for (CameraListener listener : mListeners) { |
|
|
|
for (CameraListener listener : mListeners) { |
|
|
@ -1342,7 +1323,7 @@ public class CameraView extends FrameLayout { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public void dispatchOnFocusStart(@Nullable final Gesture gesture, final PointF point) { |
|
|
|
public void dispatchOnFocusStart(@Nullable final Gesture gesture, final PointF point) { |
|
|
|
uiHandler.post(new Runnable() { |
|
|
|
mUiHandler.post(new Runnable() { |
|
|
|
@Override |
|
|
|
@Override |
|
|
|
public void run() { |
|
|
|
public void run() { |
|
|
|
if (gesture != null && mGestureMap.get(gesture) == GestureAction.FOCUS_WITH_MARKER) { |
|
|
|
if (gesture != null && mGestureMap.get(gesture) == GestureAction.FOCUS_WITH_MARKER) { |
|
|
@ -1359,7 +1340,7 @@ public class CameraView extends FrameLayout { |
|
|
|
|
|
|
|
|
|
|
|
public void dispatchOnFocusEnd(@Nullable final Gesture gesture, final boolean success, |
|
|
|
public void dispatchOnFocusEnd(@Nullable final Gesture gesture, final boolean success, |
|
|
|
final PointF point) { |
|
|
|
final PointF point) { |
|
|
|
uiHandler.post(new Runnable() { |
|
|
|
mUiHandler.post(new Runnable() { |
|
|
|
@Override |
|
|
|
@Override |
|
|
|
public void run() { |
|
|
|
public void run() { |
|
|
|
if (success && mUseSounds) { |
|
|
|
if (success && mUseSounds) { |
|
|
@ -1380,7 +1361,7 @@ public class CameraView extends FrameLayout { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public void dispatchOnOrientationChanged(final int value) { |
|
|
|
public void dispatchOnOrientationChanged(final int value) { |
|
|
|
uiHandler.post(new Runnable() { |
|
|
|
mUiHandler.post(new Runnable() { |
|
|
|
@Override |
|
|
|
@Override |
|
|
|
public void run() { |
|
|
|
public void run() { |
|
|
|
for (CameraListener listener : mListeners) { |
|
|
|
for (CameraListener listener : mListeners) { |
|
|
@ -1392,7 +1373,7 @@ public class CameraView extends FrameLayout { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public void dispatchOnZoomChanged(final float newValue, final PointF[] fingers) { |
|
|
|
public void dispatchOnZoomChanged(final float newValue, final PointF[] fingers) { |
|
|
|
uiHandler.post(new Runnable() { |
|
|
|
mUiHandler.post(new Runnable() { |
|
|
|
@Override |
|
|
|
@Override |
|
|
|
public void run() { |
|
|
|
public void run() { |
|
|
|
for (CameraListener listener : mListeners) { |
|
|
|
for (CameraListener listener : mListeners) { |
|
|
@ -1406,7 +1387,7 @@ public class CameraView extends FrameLayout { |
|
|
|
public void dispatchOnExposureCorrectionChanged(final float newValue, |
|
|
|
public void dispatchOnExposureCorrectionChanged(final float newValue, |
|
|
|
final float[] bounds, |
|
|
|
final float[] bounds, |
|
|
|
final PointF[] fingers) { |
|
|
|
final PointF[] fingers) { |
|
|
|
uiHandler.post(new Runnable() { |
|
|
|
mUiHandler.post(new Runnable() { |
|
|
|
@Override |
|
|
|
@Override |
|
|
|
public void run() { |
|
|
|
public void run() { |
|
|
|
for (CameraListener listener : mListeners) { |
|
|
|
for (CameraListener listener : mListeners) { |
|
|
|