mirror of
https://github.com/status-im/react-native-camera.git
synced 2025-02-24 09:48:17 +00:00
Merge pull request #1600 from react-native-community/feat/preview
Feat/preview
This commit is contained in:
commit
e9cadb7e01
@ -78,8 +78,12 @@ class Camera1 extends CameraViewImpl implements MediaRecorder.OnInfoListener,
|
||||
|
||||
private final SizeMap mPreviewSizes = new SizeMap();
|
||||
|
||||
private boolean mIsPreviewActive = false;
|
||||
|
||||
private final SizeMap mPictureSizes = new SizeMap();
|
||||
|
||||
private Size mPictureSize;
|
||||
|
||||
private AspectRatio mAspectRatio;
|
||||
|
||||
private boolean mShowingPreview;
|
||||
@ -107,6 +111,7 @@ class Camera1 extends CameraViewImpl implements MediaRecorder.OnInfoListener,
|
||||
public void onSurfaceChanged() {
|
||||
if (mCamera != null) {
|
||||
setUpPreview();
|
||||
mIsPreviewActive = false;
|
||||
adjustCameraParameters();
|
||||
}
|
||||
}
|
||||
@ -164,6 +169,7 @@ class Camera1 extends CameraViewImpl implements MediaRecorder.OnInfoListener,
|
||||
final boolean needsToStopPreview = mShowingPreview && Build.VERSION.SDK_INT < 14;
|
||||
if (needsToStopPreview) {
|
||||
mCamera.stopPreview();
|
||||
mIsPreviewActive = false;
|
||||
}
|
||||
mCamera.setPreviewDisplay(mPreview.getSurfaceHolder());
|
||||
if (needsToStopPreview) {
|
||||
@ -179,11 +185,23 @@ class Camera1 extends CameraViewImpl implements MediaRecorder.OnInfoListener,
|
||||
|
||||
private void startCameraPreview() {
|
||||
mCamera.startPreview();
|
||||
mIsPreviewActive = true;
|
||||
if (mIsScanning) {
|
||||
mCamera.setPreviewCallback(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resumePreview() {
|
||||
startCameraPreview();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pausePreview() {
|
||||
mCamera.stopPreview();
|
||||
mIsPreviewActive = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean isCameraOpened() {
|
||||
return mCamera != null;
|
||||
@ -217,6 +235,25 @@ class Camera1 extends CameraViewImpl implements MediaRecorder.OnInfoListener,
|
||||
return idealAspectRatios.ratios();
|
||||
}
|
||||
|
||||
@Override
|
||||
SortedSet<Size> getAvailablePictureSizes(AspectRatio ratio) {
|
||||
return mPictureSizes.sizes(ratio);
|
||||
}
|
||||
|
||||
@Override
|
||||
void setPictureSize(Size size) {
|
||||
mPictureSize = size;
|
||||
if (mCameraParameters != null && mCamera != null) {
|
||||
mCameraParameters.setPictureSize(size.getWidth(), size.getHeight());
|
||||
mCamera.setParameters(mCameraParameters);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
Size getPictureSize() {
|
||||
return mPictureSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean setAspectRatio(AspectRatio ratio) {
|
||||
if (mAspectRatio == null || !isCameraOpened()) {
|
||||
@ -334,6 +371,9 @@ class Camera1 extends CameraViewImpl implements MediaRecorder.OnInfoListener,
|
||||
throw new IllegalStateException(
|
||||
"Camera is not ready. Call start() before takePicture().");
|
||||
}
|
||||
if (!mIsPreviewActive) {
|
||||
throw new IllegalStateException("Preview is paused - resume it before taking a picture.");
|
||||
}
|
||||
if (getAutoFocus()) {
|
||||
mCamera.cancelAutoFocus();
|
||||
mCamera.autoFocus(new Camera.AutoFocusCallback() {
|
||||
@ -355,6 +395,7 @@ class Camera1 extends CameraViewImpl implements MediaRecorder.OnInfoListener,
|
||||
isPictureCaptureInProgress.set(false);
|
||||
camera.cancelAutoFocus();
|
||||
camera.startPreview();
|
||||
mIsPreviewActive = true;
|
||||
if (mIsScanning) {
|
||||
camera.setPreviewCallback(Camera1.this);
|
||||
}
|
||||
@ -403,6 +444,7 @@ class Camera1 extends CameraViewImpl implements MediaRecorder.OnInfoListener,
|
||||
final boolean needsToStopPreview = mShowingPreview && Build.VERSION.SDK_INT < 14;
|
||||
if (needsToStopPreview) {
|
||||
mCamera.stopPreview();
|
||||
mIsPreviewActive = false;
|
||||
}
|
||||
mCamera.setDisplayOrientation(calcDisplayOrientation(displayOrientation));
|
||||
if (needsToStopPreview) {
|
||||
@ -420,6 +462,7 @@ class Camera1 extends CameraViewImpl implements MediaRecorder.OnInfoListener,
|
||||
}
|
||||
|
||||
mCamera.stopPreview();
|
||||
mIsPreviewActive = false;
|
||||
|
||||
if (surfaceTexture == null) {
|
||||
mCamera.setPreviewTexture((SurfaceTexture) mPreview.getSurfaceTexture());
|
||||
@ -504,13 +547,15 @@ class Camera1 extends CameraViewImpl implements MediaRecorder.OnInfoListener,
|
||||
Size size = chooseOptimalSize(sizes);
|
||||
|
||||
// Always re-apply camera parameters
|
||||
// Largest picture size in this ratio
|
||||
final Size pictureSize = mPictureSizes.sizes(mAspectRatio).last();
|
||||
if (mPictureSize == null) {
|
||||
mPictureSize = mPictureSizes.sizes(mAspectRatio).last();
|
||||
}
|
||||
if (mShowingPreview) {
|
||||
mCamera.stopPreview();
|
||||
mIsPreviewActive = false;
|
||||
}
|
||||
mCameraParameters.setPreviewSize(size.getWidth(), size.getHeight());
|
||||
mCameraParameters.setPictureSize(pictureSize.getWidth(), pictureSize.getHeight());
|
||||
mCameraParameters.setPictureSize(mPictureSize.getWidth(), mPictureSize.getHeight());
|
||||
mCameraParameters.setRotation(calcCameraRotation(mDisplayOrientation));
|
||||
setAutoFocusInternal(mAutoFocus);
|
||||
setFlashInternal(mFlash);
|
||||
@ -555,6 +600,7 @@ class Camera1 extends CameraViewImpl implements MediaRecorder.OnInfoListener,
|
||||
if (mCamera != null) {
|
||||
mCamera.release();
|
||||
mCamera = null;
|
||||
mPictureSize = null;
|
||||
mCallback.onCameraClosed();
|
||||
}
|
||||
}
|
||||
@ -646,7 +692,6 @@ class Camera1 extends CameraViewImpl implements MediaRecorder.OnInfoListener,
|
||||
String currentMode = FLASH_MODES.get(mFlash);
|
||||
if (modes == null || !modes.contains(currentMode)) {
|
||||
mCameraParameters.setFlashMode(Camera.Parameters.FLASH_MODE_OFF);
|
||||
mFlash = Constants.FLASH_OFF;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@ -687,7 +732,6 @@ class Camera1 extends CameraViewImpl implements MediaRecorder.OnInfoListener,
|
||||
String currentMode = WB_MODES.get(mWhiteBalance);
|
||||
if (modes == null || !modes.contains(currentMode)) {
|
||||
mCameraParameters.setWhiteBalance(Camera.Parameters.WHITE_BALANCE_AUTO);
|
||||
mWhiteBalance = Constants.WB_AUTO;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
@ -216,6 +216,8 @@ class Camera2 extends CameraViewImpl implements MediaRecorder.OnInfoListener, Me
|
||||
|
||||
private final SizeMap mPictureSizes = new SizeMap();
|
||||
|
||||
private Size mPictureSize;
|
||||
|
||||
private int mFacing;
|
||||
|
||||
private AspectRatio mAspectRatio = Constants.DEFAULT_ASPECT_RATIO;
|
||||
@ -345,6 +347,35 @@ class Camera2 extends CameraViewImpl implements MediaRecorder.OnInfoListener, Me
|
||||
return mPreviewSizes.ratios();
|
||||
}
|
||||
|
||||
@Override
|
||||
SortedSet<Size> getAvailablePictureSizes(AspectRatio ratio) {
|
||||
return mPictureSizes.sizes(ratio);
|
||||
}
|
||||
|
||||
@Override
|
||||
void setPictureSize(Size size) {
|
||||
if (mCaptureSession != null) {
|
||||
try {
|
||||
mCaptureSession.stopRepeating();
|
||||
} catch (CameraAccessException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
mCaptureSession.close();
|
||||
mCaptureSession = null;
|
||||
}
|
||||
if (mStillImageReader != null) {
|
||||
mStillImageReader.close();
|
||||
}
|
||||
mPictureSize = size;
|
||||
prepareStillImageReader();
|
||||
startCaptureSession();
|
||||
}
|
||||
|
||||
@Override
|
||||
Size getPictureSize() {
|
||||
return mPictureSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean setAspectRatio(AspectRatio ratio) {
|
||||
if (ratio != null && mPreviewSizes.isEmpty()) {
|
||||
@ -653,6 +684,9 @@ class Camera2 extends CameraViewImpl implements MediaRecorder.OnInfoListener, Me
|
||||
}
|
||||
mPictureSizes.clear();
|
||||
collectPictureSizes(mPictureSizes, map);
|
||||
if (mPictureSize == null) {
|
||||
mPictureSize = mPictureSizes.sizes(mAspectRatio).last();
|
||||
}
|
||||
for (AspectRatio ratio : mPreviewSizes.ratios()) {
|
||||
if (!mPictureSizes.ratios().contains(ratio)) {
|
||||
mPreviewSizes.remove(ratio);
|
||||
@ -674,8 +708,7 @@ class Camera2 extends CameraViewImpl implements MediaRecorder.OnInfoListener, Me
|
||||
if (mStillImageReader != null) {
|
||||
mStillImageReader.close();
|
||||
}
|
||||
Size largest = mPictureSizes.sizes(mAspectRatio).last();
|
||||
mStillImageReader = ImageReader.newInstance(largest.getWidth(), largest.getHeight(),
|
||||
mStillImageReader = ImageReader.newInstance(mPictureSize.getWidth(), mPictureSize.getHeight(),
|
||||
ImageFormat.JPEG, 1);
|
||||
mStillImageReader.setOnImageAvailableListener(mOnImageAvailableListener, null);
|
||||
}
|
||||
@ -728,6 +761,20 @@ class Camera2 extends CameraViewImpl implements MediaRecorder.OnInfoListener, Me
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resumePreview() {
|
||||
startCaptureSession();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pausePreview() {
|
||||
try {
|
||||
mCaptureSession.stopRepeating();
|
||||
} catch (CameraAccessException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public Surface getPreviewSurface() {
|
||||
if (mPreviewSurface != null) {
|
||||
return mPreviewSurface;
|
||||
|
@ -37,6 +37,7 @@ import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Set;
|
||||
import java.util.SortedSet;
|
||||
|
||||
public class CameraView extends FrameLayout {
|
||||
|
||||
@ -219,6 +220,7 @@ public class CameraView extends FrameLayout {
|
||||
state.zoom = getZoom();
|
||||
state.whiteBalance = getWhiteBalance();
|
||||
state.scanning = getScanning();
|
||||
state.pictureSize = getPictureSize();
|
||||
return state;
|
||||
}
|
||||
|
||||
@ -238,6 +240,7 @@ public class CameraView extends FrameLayout {
|
||||
setZoom(ss.zoom);
|
||||
setWhiteBalance(ss.whiteBalance);
|
||||
setScanning(ss.scanning);
|
||||
setPictureSize(ss.pictureSize);
|
||||
}
|
||||
|
||||
public void setUsingCamera2Api(boolean useCamera2) {
|
||||
@ -403,6 +406,31 @@ public class CameraView extends FrameLayout {
|
||||
return mImpl.getAspectRatio();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all the picture sizes for particular ratio supported by the current camera.
|
||||
*
|
||||
* @param ratio {@link AspectRatio} for which the available image sizes will be returned.
|
||||
*/
|
||||
public SortedSet<Size> getAvailablePictureSizes(@NonNull AspectRatio ratio) {
|
||||
return mImpl.getAvailablePictureSizes(ratio);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the size of taken pictures.
|
||||
*
|
||||
* @param size The {@link Size} to be set.
|
||||
*/
|
||||
public void setPictureSize(@NonNull Size size) {
|
||||
mImpl.setPictureSize(size);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the size of pictures that will be taken.
|
||||
*/
|
||||
public Size getPictureSize() {
|
||||
return mImpl.getPictureSize();
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables or disables the continuous auto-focus mode. When the current camera doesn't support
|
||||
* auto-focus, calling this method will be ignored.
|
||||
@ -495,6 +523,14 @@ public class CameraView extends FrameLayout {
|
||||
mImpl.stopRecording();
|
||||
}
|
||||
|
||||
public void resumePreview() {
|
||||
mImpl.resumePreview();
|
||||
}
|
||||
|
||||
public void pausePreview() {
|
||||
mImpl.pausePreview();
|
||||
}
|
||||
|
||||
public void setPreviewTexture(SurfaceTexture surfaceTexture) {
|
||||
mImpl.setPreviewTexture(surfaceTexture);
|
||||
}
|
||||
@ -591,6 +627,8 @@ public class CameraView extends FrameLayout {
|
||||
|
||||
boolean scanning;
|
||||
|
||||
Size pictureSize;
|
||||
|
||||
@SuppressWarnings("WrongConstant")
|
||||
public SavedState(Parcel source, ClassLoader loader) {
|
||||
super(source);
|
||||
@ -602,6 +640,7 @@ public class CameraView extends FrameLayout {
|
||||
zoom = source.readFloat();
|
||||
whiteBalance = source.readInt();
|
||||
scanning = source.readByte() != 0;
|
||||
pictureSize = source.readParcelable(loader);
|
||||
}
|
||||
|
||||
public SavedState(Parcelable superState) {
|
||||
@ -619,6 +658,7 @@ public class CameraView extends FrameLayout {
|
||||
out.writeFloat(zoom);
|
||||
out.writeInt(whiteBalance);
|
||||
out.writeByte((byte) (scanning ? 1 : 0));
|
||||
out.writeParcelable(pictureSize, flags);
|
||||
}
|
||||
|
||||
public static final Creator<SavedState> CREATOR
|
||||
|
@ -21,6 +21,7 @@ import android.view.View;
|
||||
import android.graphics.SurfaceTexture;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.SortedSet;
|
||||
|
||||
abstract class CameraViewImpl {
|
||||
|
||||
@ -52,6 +53,12 @@ abstract class CameraViewImpl {
|
||||
|
||||
abstract Set<AspectRatio> getSupportedAspectRatios();
|
||||
|
||||
abstract SortedSet<Size> getAvailablePictureSizes(AspectRatio ratio);
|
||||
|
||||
abstract void setPictureSize(Size size);
|
||||
|
||||
abstract Size getPictureSize();
|
||||
|
||||
/**
|
||||
* @return {@code true} if the aspect ratio was changed.
|
||||
*/
|
||||
@ -92,6 +99,10 @@ abstract class CameraViewImpl {
|
||||
|
||||
abstract boolean getScanning();
|
||||
|
||||
abstract public void resumePreview();
|
||||
|
||||
abstract public void pausePreview();
|
||||
|
||||
abstract public void setPreviewTexture(SurfaceTexture surfaceTexture);
|
||||
|
||||
abstract public Size getPreviewSize();
|
||||
|
@ -16,12 +16,14 @@
|
||||
|
||||
package com.google.android.cameraview;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
/**
|
||||
* Immutable class for describing width and height dimensions in pixels.
|
||||
*/
|
||||
public class Size implements Comparable<Size> {
|
||||
public class Size implements Comparable<Size>, Parcelable {
|
||||
|
||||
private final int mWidth;
|
||||
private final int mHeight;
|
||||
@ -37,6 +39,20 @@ public class Size implements Comparable<Size> {
|
||||
mHeight = height;
|
||||
}
|
||||
|
||||
public static Size parse(String s) {
|
||||
int position = s.indexOf('x');
|
||||
if (position == -1) {
|
||||
throw new IllegalArgumentException("Malformed size: " + s);
|
||||
}
|
||||
try {
|
||||
int width = Integer.parseInt(s.substring(0, position));
|
||||
int height = Integer.parseInt(s.substring(position + 1));
|
||||
return new Size(width, height);
|
||||
} catch (NumberFormatException e) {
|
||||
throw new IllegalArgumentException("Malformed size: " + s, e);
|
||||
}
|
||||
}
|
||||
|
||||
public int getWidth() {
|
||||
return mWidth;
|
||||
}
|
||||
@ -76,4 +92,28 @@ public class Size implements Comparable<Size> {
|
||||
return mWidth * mHeight - another.mWidth * another.mHeight;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
dest.writeInt(mWidth);
|
||||
dest.writeInt(mHeight);
|
||||
}
|
||||
|
||||
public static final Parcelable.Creator<Size> CREATOR = new Parcelable.Creator<Size>() {
|
||||
@Override
|
||||
public Size createFromParcel(Parcel source) {
|
||||
int width = source.readInt();
|
||||
int height = source.readInt();
|
||||
return new Size(width, height);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Size[] newArray(int size) {
|
||||
return new Size[size];
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -12,6 +12,7 @@ import org.reactnative.barcodedetector.BarcodeFormatUtils;
|
||||
import org.reactnative.camera.tasks.ResolveTakenPictureAsyncTask;
|
||||
import org.reactnative.camera.utils.ScopedContext;
|
||||
import org.reactnative.facedetector.RNFaceDetector;
|
||||
import com.google.android.cameraview.Size;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
@ -20,6 +21,7 @@ import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.SortedSet;
|
||||
|
||||
public class CameraModule extends ReactContextBaseJavaModule {
|
||||
private static final String TAG = "CameraModule";
|
||||
@ -180,6 +182,48 @@ public class CameraModule extends ReactContextBaseJavaModule {
|
||||
});
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void pausePreview(final int viewTag) {
|
||||
final ReactApplicationContext context = getReactApplicationContext();
|
||||
UIManagerModule uiManager = context.getNativeModule(UIManagerModule.class);
|
||||
uiManager.addUIBlock(new UIBlock() {
|
||||
@Override
|
||||
public void execute(NativeViewHierarchyManager nativeViewHierarchyManager) {
|
||||
final RNCameraView cameraView;
|
||||
|
||||
try {
|
||||
cameraView = (RNCameraView) nativeViewHierarchyManager.resolveView(viewTag);
|
||||
if (cameraView.isCameraOpened()) {
|
||||
cameraView.pausePreview();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void resumePreview(final int viewTag) {
|
||||
final ReactApplicationContext context = getReactApplicationContext();
|
||||
UIManagerModule uiManager = context.getNativeModule(UIManagerModule.class);
|
||||
uiManager.addUIBlock(new UIBlock() {
|
||||
@Override
|
||||
public void execute(NativeViewHierarchyManager nativeViewHierarchyManager) {
|
||||
final RNCameraView cameraView;
|
||||
|
||||
try {
|
||||
cameraView = (RNCameraView) nativeViewHierarchyManager.resolveView(viewTag);
|
||||
if (cameraView.isCameraOpened()) {
|
||||
cameraView.resumePreview();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void takePicture(final ReadableMap options, final int viewTag, final Promise promise) {
|
||||
final ReactApplicationContext context = getReactApplicationContext();
|
||||
@ -221,7 +265,7 @@ public class CameraModule extends ReactContextBaseJavaModule {
|
||||
promise.reject("E_CAMERA_UNAVAILABLE", "Camera is not running");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
promise.reject("E_CAMERA_BAD_VIEWTAG", "recordAsync: Expected a Camera component");
|
||||
promise.reject("E_CAPTURE_FAILED", e.getMessage());
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -274,4 +318,31 @@ public class CameraModule extends ReactContextBaseJavaModule {
|
||||
}
|
||||
});
|
||||
}
|
||||
@ReactMethod
|
||||
public void getAvailablePictureSizes(final String ratio, final int viewTag, final Promise promise) {
|
||||
final ReactApplicationContext context = getReactApplicationContext();
|
||||
UIManagerModule uiManager = context.getNativeModule(UIManagerModule.class);
|
||||
uiManager.addUIBlock(new UIBlock() {
|
||||
@Override
|
||||
public void execute(NativeViewHierarchyManager nativeViewHierarchyManager) {
|
||||
final RNCameraView cameraView;
|
||||
|
||||
try {
|
||||
cameraView = (RNCameraView) nativeViewHierarchyManager.resolveView(viewTag);
|
||||
WritableArray result = Arguments.createArray();
|
||||
if (cameraView.isCameraOpened()) {
|
||||
SortedSet<Size> sizes = cameraView.getAvailablePictureSizes(AspectRatio.parse(ratio));
|
||||
for (Size size : sizes) {
|
||||
result.pushString(size.toString());
|
||||
}
|
||||
promise.resolve(result);
|
||||
} else {
|
||||
promise.reject("E_CAMERA_UNAVAILABLE", "Camera is not running");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
promise.reject("E_CAMERA_BAD_VIEWTAG", "getAvailablePictureSizesAsync: Expected a Camera component");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ import com.facebook.react.uimanager.ThemedReactContext;
|
||||
import com.facebook.react.uimanager.ViewGroupManager;
|
||||
import com.facebook.react.uimanager.annotations.ReactProp;
|
||||
import com.google.android.cameraview.AspectRatio;
|
||||
import com.google.android.cameraview.Size;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@ -21,7 +22,8 @@ public class CameraViewManager extends ViewGroupManager<RNCameraView> {
|
||||
EVENT_ON_BARCODES_DETECTED("onGoogleVisionBarcodesDetected"),
|
||||
EVENT_ON_FACE_DETECTION_ERROR("onFaceDetectionError"),
|
||||
EVENT_ON_BARCODE_DETECTION_ERROR("onGoogleVisionBarcodeDetectionError"),
|
||||
EVENT_ON_TEXT_RECOGNIZED("onTextRecognized");
|
||||
EVENT_ON_TEXT_RECOGNIZED("onTextRecognized"),
|
||||
EVENT_ON_PICTURE_SAVED("onPictureSaved");
|
||||
|
||||
private final String mName;
|
||||
|
||||
@ -99,6 +101,11 @@ public class CameraViewManager extends ViewGroupManager<RNCameraView> {
|
||||
view.setWhiteBalance(whiteBalance);
|
||||
}
|
||||
|
||||
@ReactProp(name = "pictureSize")
|
||||
public void setPictureSize(RNCameraView view, String size) {
|
||||
view.setPictureSize(Size.parse(size));
|
||||
}
|
||||
|
||||
@ReactProp(name = "barCodeTypes")
|
||||
public void setBarCodeTypes(RNCameraView view, ReadableArray barCodeTypes) {
|
||||
if (barCodeTypes == null) {
|
||||
|
@ -34,7 +34,7 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
|
||||
public class RNCameraView extends CameraView implements LifecycleEventListener, BarCodeScannerAsyncTaskDelegate, FaceDetectorAsyncTaskDelegate,
|
||||
BarcodeDetectorAsyncTaskDelegate, TextRecognizerAsyncTaskDelegate {
|
||||
BarcodeDetectorAsyncTaskDelegate, TextRecognizerAsyncTaskDelegate, PictureSavedDelegate {
|
||||
private ThemedReactContext mThemedReactContext;
|
||||
private Queue<Promise> mPictureTakenPromises = new ConcurrentLinkedQueue<>();
|
||||
private Map<Promise, ReadableMap> mPictureTakenOptions = new ConcurrentHashMap<>();
|
||||
@ -86,8 +86,11 @@ public class RNCameraView extends CameraView implements LifecycleEventListener,
|
||||
public void onPictureTaken(CameraView cameraView, final byte[] data) {
|
||||
Promise promise = mPictureTakenPromises.poll();
|
||||
ReadableMap options = mPictureTakenOptions.remove(promise);
|
||||
if (options.hasKey("fastMode") && options.getBoolean("fastMode")) {
|
||||
promise.resolve(null);
|
||||
}
|
||||
final File cacheDirectory = mPictureTakenDirectories.remove(promise);
|
||||
new ResolveTakenPictureAsyncTask(data, promise, options, cacheDirectory).execute();
|
||||
new ResolveTakenPictureAsyncTask(data, promise, options, cacheDirectory, RNCameraView.this).execute();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -223,7 +226,19 @@ public class RNCameraView extends CameraView implements LifecycleEventListener,
|
||||
MediaActionSound sound = new MediaActionSound();
|
||||
sound.play(MediaActionSound.SHUTTER_CLICK);
|
||||
}
|
||||
super.takePicture();
|
||||
try {
|
||||
super.takePicture();
|
||||
} catch (Exception e) {
|
||||
mPictureTakenPromises.remove(promise);
|
||||
mPictureTakenOptions.remove(promise);
|
||||
mPictureTakenDirectories.remove(promise);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPictureSaved(WritableMap response) {
|
||||
RNCameraViewHelper.emitPictureSavedEvent(this, response);
|
||||
}
|
||||
|
||||
public void record(ReadableMap options, final Promise promise, File cacheDirectory) {
|
||||
|
@ -175,6 +175,14 @@ public class RNCameraViewHelper {
|
||||
reactContext.getNativeModule(UIManagerModule.class).getEventDispatcher().dispatchEvent(event);
|
||||
}
|
||||
|
||||
// Picture saved event
|
||||
|
||||
public static void emitPictureSavedEvent(ViewGroup view, WritableMap response) {
|
||||
PictureSavedEvent event = PictureSavedEvent.obtain(view.getId(), response);
|
||||
ReactContext reactContext = (ReactContext) view.getContext();
|
||||
reactContext.getNativeModule(UIManagerModule.class).getEventDispatcher().dispatchEvent(event);
|
||||
}
|
||||
|
||||
// Face detection events
|
||||
|
||||
public static void emitFacesDetectedEvent(
|
||||
|
@ -0,0 +1,46 @@
|
||||
package org.reactnative.camera.events;
|
||||
|
||||
import android.support.v4.util.Pools;
|
||||
|
||||
import com.facebook.react.bridge.WritableMap;
|
||||
import com.facebook.react.uimanager.events.Event;
|
||||
import com.facebook.react.uimanager.events.RCTEventEmitter;
|
||||
|
||||
import org.reactnative.camera.CameraViewManager;
|
||||
|
||||
public class PictureSavedEvent extends Event<PictureSavedEvent> {
|
||||
private static final Pools.SynchronizedPool<PictureSavedEvent> EVENTS_POOL = new Pools.SynchronizedPool<>(5);
|
||||
private PictureSavedEvent() {}
|
||||
|
||||
private WritableMap mResponse;
|
||||
|
||||
public static PictureSavedEvent obtain(int viewTag, WritableMap response) {
|
||||
PictureSavedEvent event = EVENTS_POOL.acquire();
|
||||
if (event == null) {
|
||||
event = new PictureSavedEvent();
|
||||
}
|
||||
event.init(viewTag, response);
|
||||
return event;
|
||||
}
|
||||
|
||||
private void init(int viewTag, WritableMap response) {
|
||||
super.init(viewTag);
|
||||
mResponse = response;
|
||||
}
|
||||
|
||||
@Override
|
||||
public short getCoalescingKey() {
|
||||
int hashCode = mResponse.getMap("data").getString("uri").hashCode() % Short.MAX_VALUE;
|
||||
return (short) hashCode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getEventName() {
|
||||
return CameraViewManager.Events.EVENT_ON_PICTURE_SAVED.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispatch(RCTEventEmitter rctEventEmitter) {
|
||||
rctEventEmitter.receiveEvent(getViewTag(), getEventName(), mResponse);
|
||||
}
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
package org.reactnative.camera.tasks;
|
||||
|
||||
import com.facebook.react.bridge.WritableMap;
|
||||
|
||||
public interface PictureSavedDelegate {
|
||||
void onPictureSaved(WritableMap response);
|
||||
}
|
@ -30,18 +30,14 @@ public class ResolveTakenPictureAsyncTask extends AsyncTask<Void, Void, Writable
|
||||
private ReadableMap mOptions;
|
||||
private File mCacheDirectory;
|
||||
private Bitmap mBitmap;
|
||||
private PictureSavedDelegate mPictureSavedDelegate;
|
||||
|
||||
public ResolveTakenPictureAsyncTask(byte[] imageData, Promise promise, ReadableMap options) {
|
||||
mPromise = promise;
|
||||
mOptions = options;
|
||||
mImageData = imageData;
|
||||
}
|
||||
|
||||
public ResolveTakenPictureAsyncTask(byte[] imageData, Promise promise, ReadableMap options, File cacheDirectory) {
|
||||
public ResolveTakenPictureAsyncTask(byte[] imageData, Promise promise, ReadableMap options, File cacheDirectory, PictureSavedDelegate delegate) {
|
||||
mPromise = promise;
|
||||
mOptions = options;
|
||||
mImageData = imageData;
|
||||
mCacheDirectory = cacheDirectory;
|
||||
mPictureSavedDelegate = delegate;
|
||||
}
|
||||
|
||||
private int getQuality() {
|
||||
@ -231,7 +227,14 @@ public class ResolveTakenPictureAsyncTask extends AsyncTask<Void, Void, Writable
|
||||
|
||||
// If the response is not null everything went well and we can resolve the promise.
|
||||
if (response != null) {
|
||||
mPromise.resolve(response);
|
||||
if (mOptions.hasKey("fastMode") && mOptions.getBoolean("fastMode")) {
|
||||
WritableMap wrapper = Arguments.createMap();
|
||||
wrapper.putInt("id", mOptions.getInt("id"));
|
||||
wrapper.putMap("data", response);
|
||||
mPictureSavedDelegate.onPictureSaved(wrapper);
|
||||
} else {
|
||||
mPromise.resolve(response);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -29,7 +29,9 @@
|
||||
@property (assign, nonatomic) NSInteger autoFocus;
|
||||
@property (assign, nonatomic) float focusDepth;
|
||||
@property (assign, nonatomic) NSInteger whiteBalance;
|
||||
@property (nonatomic, assign, getter=isReadingBarCodes) BOOL barCodeReading;
|
||||
@property (assign, nonatomic) AVCaptureSessionPreset pictureSize;
|
||||
@property (nonatomic, assign) BOOL isReadingBarCodes;
|
||||
@property (nonatomic, assign) BOOL isDetectingFaces;
|
||||
@property(assign, nonatomic) AVVideoCodecType videoCodecType;
|
||||
|
||||
- (id)initWithBridge:(RCTBridge *)bridge;
|
||||
@ -39,6 +41,7 @@
|
||||
- (void)updateFocusDepth;
|
||||
- (void)updateZoom;
|
||||
- (void)updateWhiteBalance;
|
||||
- (void)updatePictureSize;
|
||||
- (void)updateFaceDetecting:(id)isDetectingFaces;
|
||||
- (void)updateFaceDetectionMode:(id)requestedMode;
|
||||
- (void)updateFaceDetectionLandmarks:(id)requestedLandmarks;
|
||||
@ -46,11 +49,14 @@
|
||||
- (void)takePicture:(NSDictionary *)options resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject;
|
||||
- (void)record:(NSDictionary *)options resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject;
|
||||
- (void)stopRecording;
|
||||
- (void)resumePreview;
|
||||
- (void)pausePreview;
|
||||
- (void)setupOrDisableBarcodeScanner;
|
||||
- (void)onReady:(NSDictionary *)event;
|
||||
- (void)onMountingError:(NSDictionary *)event;
|
||||
- (void)onCodeRead:(NSDictionary *)event;
|
||||
- (void)onFacesDetected:(NSDictionary *)event;
|
||||
- (void)onPictureSaved:(NSDictionary *)event;
|
||||
|
||||
@end
|
||||
|
||||
|
@ -21,6 +21,7 @@
|
||||
@property (nonatomic, copy) RCTDirectEventBlock onMountError;
|
||||
@property (nonatomic, copy) RCTDirectEventBlock onBarCodeRead;
|
||||
@property (nonatomic, copy) RCTDirectEventBlock onFacesDetected;
|
||||
@property (nonatomic, copy) RCTDirectEventBlock onPictureSaved;
|
||||
|
||||
@end
|
||||
|
||||
@ -85,6 +86,13 @@ static NSDictionary *defaultFaceDetectorOptions = nil;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)onPictureSaved:(NSDictionary *)event
|
||||
{
|
||||
if (_onPictureSaved) {
|
||||
_onPictureSaved(event);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)layoutSubviews
|
||||
{
|
||||
[super layoutSubviews];
|
||||
@ -210,7 +218,7 @@ static NSDictionary *defaultFaceDetectorOptions = nil;
|
||||
AVCaptureDevice *device = [self.videoCaptureDeviceInput device];
|
||||
NSError *error = nil;
|
||||
|
||||
if (self.autoFocus < 0 || device.focusMode != RNCameraAutoFocusOff || device.position == RNCameraTypeFront) {
|
||||
if (device == nil || self.autoFocus < 0 || device.focusMode != RNCameraAutoFocusOff || device.position == RNCameraTypeFront) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -284,6 +292,11 @@ static NSDictionary *defaultFaceDetectorOptions = nil;
|
||||
[device unlockForConfiguration];
|
||||
}
|
||||
|
||||
- (void)updatePictureSize
|
||||
{
|
||||
[self updateSessionPreset:self.pictureSize];
|
||||
}
|
||||
|
||||
#if __has_include(<GoogleMobileVision/GoogleMobileVision.h>)
|
||||
- (void)updateFaceDetecting:(id)faceDetecting
|
||||
{
|
||||
@ -318,16 +331,24 @@ static NSDictionary *defaultFaceDetectorOptions = nil;
|
||||
[connection setVideoOrientation:orientation];
|
||||
[self.stillImageOutput captureStillImageAsynchronouslyFromConnection:connection completionHandler: ^(CMSampleBufferRef imageSampleBuffer, NSError *error) {
|
||||
if (imageSampleBuffer && !error) {
|
||||
BOOL useFastMode = options[@"fastMode"] && [options[@"fastMode"] boolValue];
|
||||
if (useFastMode) {
|
||||
resolve(nil);
|
||||
}
|
||||
NSData *imageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageSampleBuffer];
|
||||
|
||||
UIImage *takenImage = [UIImage imageWithData:imageData];
|
||||
|
||||
CGRect frame = [_previewLayer metadataOutputRectOfInterestForRect:self.frame];
|
||||
CGImageRef takenCGImage = takenImage.CGImage;
|
||||
size_t width = CGImageGetWidth(takenCGImage);
|
||||
size_t height = CGImageGetHeight(takenCGImage);
|
||||
CGRect cropRect = CGRectMake(frame.origin.x * width, frame.origin.y * height, frame.size.width * width, frame.size.height * height);
|
||||
takenImage = [RNImageUtils cropImage:takenImage toRect:cropRect];
|
||||
CGSize previewSize;
|
||||
if (UIInterfaceOrientationIsPortrait([[UIApplication sharedApplication] statusBarOrientation])) {
|
||||
previewSize = CGSizeMake(self.previewLayer.frame.size.height, self.previewLayer.frame.size.width);
|
||||
} else {
|
||||
previewSize = CGSizeMake(self.previewLayer.frame.size.width, self.previewLayer.frame.size.height);
|
||||
}
|
||||
CGRect cropRect = CGRectMake(0, 0, CGImageGetWidth(takenCGImage), CGImageGetHeight(takenCGImage));
|
||||
CGRect croppedSize = AVMakeRectWithAspectRatioInsideRect(previewSize, cropRect);
|
||||
takenImage = [RNImageUtils cropImage:takenImage toRect:croppedSize];
|
||||
|
||||
if ([options[@"mirrorImage"] boolValue]) {
|
||||
takenImage = [RNImageUtils mirrorImage:takenImage];
|
||||
@ -377,7 +398,11 @@ static NSDictionary *defaultFaceDetectorOptions = nil;
|
||||
[RNImageUtils updatePhotoMetadata:imageSampleBuffer withAdditionalData:@{ @"Orientation": @(imageRotation) } inResponse:response]; // TODO
|
||||
}
|
||||
|
||||
resolve(response);
|
||||
if (useFastMode) {
|
||||
[self onPictureSaved:@{@"data": response, @"id": options[@"id"]}];
|
||||
} else {
|
||||
resolve(response);
|
||||
}
|
||||
} else {
|
||||
reject(@"E_IMAGE_CAPTURE_FAILED", @"Image could not be captured", error);
|
||||
}
|
||||
@ -447,6 +472,16 @@ static NSDictionary *defaultFaceDetectorOptions = nil;
|
||||
[self.movieFileOutput stopRecording];
|
||||
}
|
||||
|
||||
- (void)resumePreview
|
||||
{
|
||||
[[self.previewLayer connection] setEnabled:YES];
|
||||
}
|
||||
|
||||
- (void)pausePreview
|
||||
{
|
||||
[[self.previewLayer connection] setEnabled:NO];
|
||||
}
|
||||
|
||||
- (void)startSession
|
||||
{
|
||||
#if TARGET_IPHONE_SIMULATOR
|
||||
@ -571,10 +606,14 @@ static NSDictionary *defaultFaceDetectorOptions = nil;
|
||||
|
||||
#pragma mark - internal
|
||||
|
||||
- (void)updateSessionPreset:(NSString *)preset
|
||||
- (void)updateSessionPreset:(AVCaptureSessionPreset)preset
|
||||
{
|
||||
#if !(TARGET_IPHONE_SIMULATOR)
|
||||
if (preset) {
|
||||
if (self.isDetectingFaces && [preset isEqual:AVCaptureSessionPresetPhoto]) {
|
||||
RCTLog(@"AVCaptureSessionPresetPhoto not supported during face detection. Falling back to AVCaptureSessionPresetHigh");
|
||||
preset = AVCaptureSessionPresetHigh;
|
||||
}
|
||||
dispatch_async(self.sessionQueue, ^{
|
||||
[self.session beginConfiguration];
|
||||
if ([self.session canSetSessionPreset:preset]) {
|
||||
|
@ -16,6 +16,7 @@ RCT_EXPORT_VIEW_PROPERTY(onCameraReady, RCTDirectEventBlock);
|
||||
RCT_EXPORT_VIEW_PROPERTY(onMountError, RCTDirectEventBlock);
|
||||
RCT_EXPORT_VIEW_PROPERTY(onBarCodeRead, RCTDirectEventBlock);
|
||||
RCT_EXPORT_VIEW_PROPERTY(onFacesDetected, RCTDirectEventBlock);
|
||||
RCT_EXPORT_VIEW_PROPERTY(onPictureSaved, RCTDirectEventBlock);
|
||||
|
||||
+ (BOOL)requiresMainQueueSetup
|
||||
{
|
||||
@ -65,13 +66,13 @@ RCT_EXPORT_VIEW_PROPERTY(onFacesDetected, RCTDirectEventBlock);
|
||||
},
|
||||
@"VideoCodec": [[self class] validCodecTypes],
|
||||
@"BarCodeType" : [[self class] validBarCodeTypes],
|
||||
@"FaceDetection" : [[self class] faceDetectorConstants]
|
||||
@"FaceDetection" : [[self class] faceDetectorConstants]
|
||||
};
|
||||
}
|
||||
|
||||
- (NSArray<NSString *> *)supportedEvents
|
||||
{
|
||||
return @[@"onCameraReady", @"onMountError", @"onBarCodeRead", @"onFacesDetected"];
|
||||
return @[@"onCameraReady", @"onMountError", @"onBarCodeRead", @"onFacesDetected", @"onPictureSaved"];
|
||||
}
|
||||
|
||||
+ (NSDictionary *)validCodecTypes
|
||||
@ -111,6 +112,21 @@ RCT_EXPORT_VIEW_PROPERTY(onFacesDetected, RCTDirectEventBlock);
|
||||
};
|
||||
}
|
||||
|
||||
+ (NSDictionary *)pictureSizes
|
||||
{
|
||||
return @{
|
||||
@"3840x2160" : AVCaptureSessionPreset3840x2160,
|
||||
@"1920x1080" : AVCaptureSessionPreset1920x1080,
|
||||
@"1280x720" : AVCaptureSessionPreset1280x720,
|
||||
@"640x480" : AVCaptureSessionPreset640x480,
|
||||
@"352x288" : AVCaptureSessionPreset352x288,
|
||||
@"Photo" : AVCaptureSessionPresetPhoto,
|
||||
@"High" : AVCaptureSessionPresetHigh,
|
||||
@"Medium" : AVCaptureSessionPresetMedium,
|
||||
@"Low" : AVCaptureSessionPresetLow
|
||||
};
|
||||
}
|
||||
|
||||
+ (NSDictionary *)faceDetectorConstants
|
||||
{
|
||||
#if __has_include(<GoogleMobileVision/GoogleMobileVision.h>)
|
||||
@ -158,12 +174,20 @@ RCT_CUSTOM_VIEW_PROPERTY(zoom, NSNumber, RNCamera)
|
||||
|
||||
RCT_CUSTOM_VIEW_PROPERTY(whiteBalance, NSInteger, RNCamera)
|
||||
{
|
||||
[view setWhiteBalance: [RCTConvert NSInteger:json]];
|
||||
[view setWhiteBalance:[RCTConvert NSInteger:json]];
|
||||
[view updateWhiteBalance];
|
||||
}
|
||||
|
||||
RCT_CUSTOM_VIEW_PROPERTY(pictureSize, NSString *, RNCamera)
|
||||
{
|
||||
[view setPictureSize:[[self class] pictureSizes][[RCTConvert NSString:json]]];
|
||||
[view updatePictureSize];
|
||||
}
|
||||
|
||||
|
||||
RCT_CUSTOM_VIEW_PROPERTY(faceDetectorEnabled, BOOL, RNCamera)
|
||||
{
|
||||
view.isDetectingFaces = [RCTConvert BOOL:json];
|
||||
[view updateFaceDetecting:json];
|
||||
}
|
||||
|
||||
@ -185,7 +209,7 @@ RCT_CUSTOM_VIEW_PROPERTY(faceDetectionClassifications, NSString, RNCamera)
|
||||
RCT_CUSTOM_VIEW_PROPERTY(barCodeScannerEnabled, BOOL, RNCamera)
|
||||
{
|
||||
|
||||
view.barCodeReading = [RCTConvert BOOL:json];
|
||||
view.isReadingBarCodes = [RCTConvert BOOL:json];
|
||||
[view setupOrDisableBarcodeScanner];
|
||||
}
|
||||
|
||||
@ -200,29 +224,37 @@ RCT_REMAP_METHOD(takePicture,
|
||||
resolver:(RCTPromiseResolveBlock)resolve
|
||||
rejecter:(RCTPromiseRejectBlock)reject)
|
||||
{
|
||||
#if TARGET_IPHONE_SIMULATOR
|
||||
NSMutableDictionary *response = [[NSMutableDictionary alloc] init];
|
||||
float quality = [options[@"quality"] floatValue];
|
||||
NSString *path = [RNFileSystem generatePathInDirectory:[[RNFileSystem cacheDirectoryPath] stringByAppendingPathComponent:@"Camera"] withExtension:@".jpg"];
|
||||
UIImage *generatedPhoto = [RNImageUtils generatePhotoOfSize:CGSizeMake(200, 200)];
|
||||
NSData *photoData = UIImageJPEGRepresentation(generatedPhoto, quality);
|
||||
response[@"uri"] = [RNImageUtils writeImage:photoData toPath:path];
|
||||
response[@"width"] = @(generatedPhoto.size.width);
|
||||
response[@"height"] = @(generatedPhoto.size.height);
|
||||
if ([options[@"base64"] boolValue]) {
|
||||
response[@"base64"] = [photoData base64EncodedStringWithOptions:0];
|
||||
}
|
||||
resolve(response);
|
||||
#else
|
||||
[self.bridge.uiManager addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary<NSNumber *, RNCamera *> *viewRegistry) {
|
||||
RNCamera *view = viewRegistry[reactTag];
|
||||
if (![view isKindOfClass:[RNCamera class]]) {
|
||||
RCTLogError(@"Invalid view returned from registry, expecting RNCamera, got: %@", view);
|
||||
} else {
|
||||
#if TARGET_IPHONE_SIMULATOR
|
||||
NSMutableDictionary *response = [[NSMutableDictionary alloc] init];
|
||||
float quality = [options[@"quality"] floatValue];
|
||||
NSString *path = [RNFileSystem generatePathInDirectory:[[RNFileSystem cacheDirectoryPath] stringByAppendingPathComponent:@"Camera"] withExtension:@".jpg"];
|
||||
UIImage *generatedPhoto = [RNImageUtils generatePhotoOfSize:CGSizeMake(200, 200)];
|
||||
BOOL useFastMode = options[@"fastMode"] && [options[@"fastMode"] boolValue];
|
||||
if (useFastMode) {
|
||||
resolve(nil);
|
||||
}
|
||||
NSData *photoData = UIImageJPEGRepresentation(generatedPhoto, quality);
|
||||
response[@"uri"] = [RNImageUtils writeImage:photoData toPath:path];
|
||||
response[@"width"] = @(generatedPhoto.size.width);
|
||||
response[@"height"] = @(generatedPhoto.size.height);
|
||||
if ([options[@"base64"] boolValue]) {
|
||||
response[@"base64"] = [photoData base64EncodedStringWithOptions:0];
|
||||
}
|
||||
if (useFastMode) {
|
||||
[view onPictureSaved:@{@"data": response, @"id": options[@"id"]}];
|
||||
} else {
|
||||
resolve(response);
|
||||
}
|
||||
#else
|
||||
[view takePicture:options resolve:resolve reject:reject];
|
||||
#endif
|
||||
}
|
||||
}];
|
||||
#endif
|
||||
}
|
||||
|
||||
RCT_REMAP_METHOD(record,
|
||||
@ -245,6 +277,36 @@ RCT_REMAP_METHOD(record,
|
||||
}];
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(resumePreview:(nonnull NSNumber *)reactTag)
|
||||
{
|
||||
#if TARGET_IPHONE_SIMULATOR
|
||||
return;
|
||||
#endif
|
||||
[self.bridge.uiManager addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary<NSNumber *, RNCamera *> *viewRegistry) {
|
||||
RNCamera *view = viewRegistry[reactTag];
|
||||
if (![view isKindOfClass:[RNCamera class]]) {
|
||||
RCTLogError(@"Invalid view returned from registry, expecting RNCamera, got: %@", view);
|
||||
} else {
|
||||
[view resumePreview];
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(pausePreview:(nonnull NSNumber *)reactTag)
|
||||
{
|
||||
#if TARGET_IPHONE_SIMULATOR
|
||||
return;
|
||||
#endif
|
||||
[self.bridge.uiManager addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary<NSNumber *, RNCamera *> *viewRegistry) {
|
||||
RNCamera *view = viewRegistry[reactTag];
|
||||
if (![view isKindOfClass:[RNCamera class]]) {
|
||||
RCTLogError(@"Invalid view returned from registry, expecting RNCamera, got: %@", view);
|
||||
} else {
|
||||
[view pausePreview];
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
RCT_REMAP_METHOD(stopRecording, reactTag:(nonnull NSNumber *)reactTag)
|
||||
{
|
||||
[self.bridge.uiManager addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary<NSNumber *, RNCamera *> *viewRegistry) {
|
||||
@ -283,5 +345,14 @@ RCT_EXPORT_METHOD(checkVideoAuthorizationStatus:(RCTPromiseResolveBlock)resolve
|
||||
}];
|
||||
}
|
||||
|
||||
RCT_REMAP_METHOD(getAvailablePictureSizes,
|
||||
ratio:(NSString *)ratio
|
||||
reactTag:(nonnull NSNumber *)reactTag
|
||||
resolver:(RCTPromiseResolveBlock)resolve
|
||||
rejecter:(RCTPromiseRejectBlock)reject)
|
||||
{
|
||||
resolve([[[self class] pictureSizes] allKeys]);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
@ -30,7 +30,7 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
});
|
||||
|
||||
type Orientation = "auto"|"landscapeLeft"|"landscapeRight"|"portrait"|"portraitUpsideDown";
|
||||
type Orientation = 'auto' | 'landscapeLeft' | 'landscapeRight' | 'portrait' | 'portraitUpsideDown';
|
||||
|
||||
type PictureOptions = {
|
||||
quality?: number,
|
||||
@ -82,6 +82,7 @@ type PropsType = typeof View.props & {
|
||||
type?: number | string,
|
||||
onCameraReady?: Function,
|
||||
onBarCodeRead?: Function,
|
||||
onPictureSaved?: Function,
|
||||
onGoogleVisionBarcodesDetected?: Function,
|
||||
faceDetectionMode?: number,
|
||||
flashMode?: number | string,
|
||||
@ -96,6 +97,7 @@ type PropsType = typeof View.props & {
|
||||
captureAudio?: boolean,
|
||||
useCamera2Api?: boolean,
|
||||
playSoundOnCapture?: boolean,
|
||||
pictureSize?: string,
|
||||
};
|
||||
|
||||
type StateType = {
|
||||
@ -176,6 +178,7 @@ export default class Camera extends React.Component<PropsType, StateType> {
|
||||
onMountError: PropTypes.func,
|
||||
onCameraReady: PropTypes.func,
|
||||
onBarCodeRead: PropTypes.func,
|
||||
onPictureSaved: PropTypes.func,
|
||||
onGoogleVisionBarcodesDetected: PropTypes.func,
|
||||
onFacesDetected: PropTypes.func,
|
||||
onTextRecognized: PropTypes.func,
|
||||
@ -195,6 +198,7 @@ export default class Camera extends React.Component<PropsType, StateType> {
|
||||
captureAudio: PropTypes.bool,
|
||||
useCamera2Api: PropTypes.bool,
|
||||
playSoundOnCapture: PropTypes.bool,
|
||||
pictureSize: PropTypes.string,
|
||||
};
|
||||
|
||||
static defaultProps: Object = {
|
||||
@ -226,6 +230,7 @@ export default class Camera extends React.Component<PropsType, StateType> {
|
||||
captureAudio: false,
|
||||
useCamera2Api: false,
|
||||
playSoundOnCapture: false,
|
||||
pictureSize: 'Photo',
|
||||
};
|
||||
|
||||
_cameraRef: ?Object;
|
||||
@ -289,6 +294,12 @@ export default class Camera extends React.Component<PropsType, StateType> {
|
||||
}
|
||||
};
|
||||
|
||||
_onPictureSaved = ({ nativeEvent }) => {
|
||||
if (this.props.onPictureSaved) {
|
||||
this.props.onPictureSaved(nativeEvent);
|
||||
}
|
||||
};
|
||||
|
||||
_onObjectDetected = (callback: ?Function) => ({ nativeEvent }: EventCallbackArgumentsType) => {
|
||||
const { type } = nativeEvent;
|
||||
|
||||
@ -363,6 +374,7 @@ export default class Camera extends React.Component<PropsType, StateType> {
|
||||
onBarCodeRead={this._onObjectDetected(this.props.onBarCodeRead)}
|
||||
onFacesDetected={this._onObjectDetected(this.props.onFacesDetected)}
|
||||
onTextRecognized={this._onObjectDetected(this.props.onTextRecognized)}
|
||||
onPictureSaved={this._onPictureSaved}
|
||||
>
|
||||
{this.renderChildren()}
|
||||
</RNCamera>
|
||||
@ -427,6 +439,7 @@ const RNCamera = requireNativeComponent('RNCamera', Camera, {
|
||||
onBarCodeRead: true,
|
||||
onGoogleVisionBarcodesDetected: true,
|
||||
onCameraReady: true,
|
||||
onPictureSaved: true,
|
||||
onFaceDetected: true,
|
||||
onLayout: true,
|
||||
onMountError: true,
|
||||
|
Loading…
x
Reference in New Issue
Block a user