2016-01-05 12:36:05 -08:00
|
|
|
/**
|
|
|
|
|
* Created by Fabrice Armisen (farmisen@gmail.com) on 1/3/16.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
package com.lwansbrough.RCTCamera;
|
|
|
|
|
|
|
|
|
|
import android.content.Context;
|
2017-04-24 09:25:49 -07:00
|
|
|
import android.graphics.Rect;
|
2016-01-05 12:36:05 -08:00
|
|
|
import android.graphics.SurfaceTexture;
|
|
|
|
|
import android.hardware.Camera;
|
2016-12-06 01:58:17 +01:00
|
|
|
import android.view.MotionEvent;
|
2016-01-05 12:36:05 -08:00
|
|
|
import android.view.TextureView;
|
2016-09-15 01:11:11 +02:00
|
|
|
import android.os.AsyncTask;
|
|
|
|
|
|
|
|
|
|
import com.facebook.react.bridge.Arguments;
|
|
|
|
|
import com.facebook.react.bridge.ReactContext;
|
2017-08-20 01:19:18 +03:00
|
|
|
import com.facebook.react.bridge.WritableArray;
|
2016-09-15 01:11:11 +02:00
|
|
|
import com.facebook.react.bridge.WritableMap;
|
|
|
|
|
import com.facebook.react.modules.core.DeviceEventManagerModule;
|
2016-01-05 12:36:05 -08:00
|
|
|
|
2017-04-24 09:25:49 -07:00
|
|
|
import java.util.ArrayList;
|
2016-02-18 12:55:28 +02:00
|
|
|
import java.util.List;
|
2016-09-15 01:11:11 +02:00
|
|
|
import java.util.EnumMap;
|
|
|
|
|
import java.util.EnumSet;
|
|
|
|
|
|
2016-09-15 01:00:27 +02:00
|
|
|
import com.google.zxing.BarcodeFormat;
|
|
|
|
|
import com.google.zxing.BinaryBitmap;
|
|
|
|
|
import com.google.zxing.DecodeHintType;
|
|
|
|
|
import com.google.zxing.MultiFormatReader;
|
|
|
|
|
import com.google.zxing.PlanarYUVLuminanceSource;
|
|
|
|
|
import com.google.zxing.Result;
|
2017-08-20 01:19:18 +03:00
|
|
|
import com.google.zxing.ResultPoint;
|
2016-09-15 01:00:27 +02:00
|
|
|
import com.google.zxing.common.HybridBinarizer;
|
2016-01-05 12:36:05 -08:00
|
|
|
|
2016-09-15 01:11:11 +02:00
|
|
|
class RCTCameraViewFinder extends TextureView implements TextureView.SurfaceTextureListener, Camera.PreviewCallback {
|
2016-01-05 12:36:05 -08:00
|
|
|
private int _cameraType;
|
2016-11-17 22:25:51 -08:00
|
|
|
private int _captureMode;
|
2016-01-05 12:36:05 -08:00
|
|
|
private SurfaceTexture _surfaceTexture;
|
2017-04-24 09:25:49 -07:00
|
|
|
private int _surfaceTextureWidth;
|
|
|
|
|
private int _surfaceTextureHeight;
|
2016-01-05 12:36:05 -08:00
|
|
|
private boolean _isStarting;
|
|
|
|
|
private boolean _isStopping;
|
|
|
|
|
private Camera _camera;
|
2016-12-06 01:58:17 +01:00
|
|
|
private float mFingerSpacing;
|
2016-01-05 12:36:05 -08:00
|
|
|
|
2016-09-15 01:11:11 +02:00
|
|
|
// concurrency lock for barcode scanner to avoid flooding the runtime
|
|
|
|
|
public static volatile boolean barcodeScannerTaskLock = false;
|
|
|
|
|
|
|
|
|
|
// reader instance for the barcode scanner
|
|
|
|
|
private final MultiFormatReader _multiFormatReader = new MultiFormatReader();
|
|
|
|
|
|
2016-01-05 12:36:05 -08:00
|
|
|
public RCTCameraViewFinder(Context context, int type) {
|
|
|
|
|
super(context);
|
|
|
|
|
this.setSurfaceTextureListener(this);
|
|
|
|
|
this._cameraType = type;
|
2016-09-15 21:30:00 +02:00
|
|
|
this.initBarcodeReader(RCTCamera.getInstance().getBarCodeTypes());
|
2016-01-05 12:36:05 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
|
|
|
|
|
_surfaceTexture = surface;
|
2017-04-24 09:25:49 -07:00
|
|
|
_surfaceTextureWidth = width;
|
|
|
|
|
_surfaceTextureHeight = height;
|
2016-01-05 12:36:05 -08:00
|
|
|
startCamera();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
|
2017-04-24 09:25:49 -07:00
|
|
|
_surfaceTextureWidth = width;
|
|
|
|
|
_surfaceTextureHeight = height;
|
2016-01-05 12:36:05 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
|
|
|
|
|
_surfaceTexture = null;
|
2017-04-24 09:25:49 -07:00
|
|
|
_surfaceTextureWidth = 0;
|
|
|
|
|
_surfaceTextureHeight = 0;
|
2016-01-05 12:36:05 -08:00
|
|
|
stopCamera();
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void onSurfaceTextureUpdated(SurfaceTexture surface) {
|
|
|
|
|
}
|
|
|
|
|
|
2016-08-27 20:49:46 -05:00
|
|
|
public double getRatio() {
|
2016-01-05 12:36:05 -08:00
|
|
|
int width = RCTCamera.getInstance().getPreviewWidth(this._cameraType);
|
|
|
|
|
int height = RCTCamera.getInstance().getPreviewHeight(this._cameraType);
|
|
|
|
|
return ((float) width) / ((float) height);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void setCameraType(final int type) {
|
|
|
|
|
if (this._cameraType == type) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
new Thread(new Runnable() {
|
|
|
|
|
@Override
|
|
|
|
|
public void run() {
|
|
|
|
|
stopPreview();
|
|
|
|
|
_cameraType = type;
|
|
|
|
|
startPreview();
|
|
|
|
|
}
|
|
|
|
|
}).start();
|
|
|
|
|
}
|
|
|
|
|
|
2016-11-17 22:25:51 -08:00
|
|
|
public void setCaptureMode(final int captureMode) {
|
|
|
|
|
RCTCamera.getInstance().setCaptureMode(_cameraType, captureMode);
|
|
|
|
|
this._captureMode = captureMode;
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-20 21:31:23 +02:00
|
|
|
public void setCaptureQuality(String captureQuality) {
|
|
|
|
|
RCTCamera.getInstance().setCaptureQuality(_cameraType, captureQuality);
|
|
|
|
|
}
|
|
|
|
|
|
2016-01-05 12:36:05 -08:00
|
|
|
public void setTorchMode(int torchMode) {
|
|
|
|
|
RCTCamera.getInstance().setTorchMode(_cameraType, torchMode);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void setFlashMode(int flashMode) {
|
2016-09-05 19:36:07 +02:00
|
|
|
RCTCamera.getInstance().setFlashMode(_cameraType, flashMode);
|
2016-01-05 12:36:05 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void startPreview() {
|
|
|
|
|
if (_surfaceTexture != null) {
|
|
|
|
|
startCamera();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void stopPreview() {
|
|
|
|
|
if (_camera != null) {
|
|
|
|
|
stopCamera();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
synchronized private void startCamera() {
|
|
|
|
|
if (!_isStarting) {
|
|
|
|
|
_isStarting = true;
|
|
|
|
|
try {
|
|
|
|
|
_camera = RCTCamera.getInstance().acquireCameraInstance(_cameraType);
|
2016-01-28 10:19:22 -08:00
|
|
|
Camera.Parameters parameters = _camera.getParameters();
|
2017-04-24 09:25:49 -07:00
|
|
|
|
|
|
|
|
final boolean isCaptureModeStill = (_captureMode == RCTCameraModule.RCT_CAMERA_CAPTURE_MODE_STILL);
|
|
|
|
|
final boolean isCaptureModeVideo = (_captureMode == RCTCameraModule.RCT_CAMERA_CAPTURE_MODE_VIDEO);
|
|
|
|
|
if (!isCaptureModeStill && !isCaptureModeVideo) {
|
|
|
|
|
throw new RuntimeException("Unsupported capture mode:" + _captureMode);
|
2016-02-18 12:55:28 +02:00
|
|
|
}
|
2017-04-24 09:25:49 -07:00
|
|
|
|
|
|
|
|
// Set auto-focus. Try to set to continuous picture/video, and fall back to general
|
|
|
|
|
// auto if available.
|
|
|
|
|
List<String> focusModes = parameters.getSupportedFocusModes();
|
|
|
|
|
if (isCaptureModeStill && focusModes.contains(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE)) {
|
|
|
|
|
parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);
|
|
|
|
|
} else if (isCaptureModeVideo && focusModes.contains(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO)) {
|
|
|
|
|
parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO);
|
|
|
|
|
} else if (focusModes.contains(Camera.Parameters.FOCUS_MODE_AUTO)) {
|
|
|
|
|
parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-20 21:31:23 +02:00
|
|
|
// set picture size
|
|
|
|
|
// defaults to max available size
|
2016-11-17 22:25:51 -08:00
|
|
|
List<Camera.Size> supportedSizes;
|
2017-04-24 09:25:49 -07:00
|
|
|
if (isCaptureModeStill) {
|
2016-11-17 22:25:51 -08:00
|
|
|
supportedSizes = parameters.getSupportedPictureSizes();
|
2017-04-24 09:25:49 -07:00
|
|
|
} else if (isCaptureModeVideo) {
|
2016-11-17 22:25:51 -08:00
|
|
|
supportedSizes = RCTCamera.getInstance().getSupportedVideoSizes(_camera);
|
|
|
|
|
} else {
|
|
|
|
|
throw new RuntimeException("Unsupported capture mode:" + _captureMode);
|
|
|
|
|
}
|
2016-08-27 20:49:46 -05:00
|
|
|
Camera.Size optimalPictureSize = RCTCamera.getInstance().getBestSize(
|
2016-11-17 22:25:51 -08:00
|
|
|
supportedSizes,
|
2016-08-27 20:49:46 -05:00
|
|
|
Integer.MAX_VALUE,
|
|
|
|
|
Integer.MAX_VALUE
|
|
|
|
|
);
|
2016-03-20 21:31:23 +02:00
|
|
|
parameters.setPictureSize(optimalPictureSize.width, optimalPictureSize.height);
|
|
|
|
|
|
2016-01-28 10:19:22 -08:00
|
|
|
_camera.setParameters(parameters);
|
2016-01-05 12:36:05 -08:00
|
|
|
_camera.setPreviewTexture(_surfaceTexture);
|
|
|
|
|
_camera.startPreview();
|
2016-09-15 01:11:11 +02:00
|
|
|
// send previews to `onPreviewFrame`
|
|
|
|
|
_camera.setPreviewCallback(this);
|
2016-01-05 12:36:05 -08:00
|
|
|
} catch (NullPointerException e) {
|
|
|
|
|
e.printStackTrace();
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
e.printStackTrace();
|
|
|
|
|
stopCamera();
|
|
|
|
|
} finally {
|
|
|
|
|
_isStarting = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
synchronized private void stopCamera() {
|
|
|
|
|
if (!_isStopping) {
|
|
|
|
|
_isStopping = true;
|
|
|
|
|
try {
|
|
|
|
|
if (_camera != null) {
|
|
|
|
|
_camera.stopPreview();
|
2016-09-15 01:11:11 +02:00
|
|
|
// stop sending previews to `onPreviewFrame`
|
2016-09-13 22:17:45 +01:00
|
|
|
_camera.setPreviewCallback(null);
|
2016-01-05 12:36:05 -08:00
|
|
|
RCTCamera.getInstance().releaseCameraInstance(_cameraType);
|
|
|
|
|
_camera = null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
e.printStackTrace();
|
|
|
|
|
} finally {
|
|
|
|
|
_isStopping = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-09-15 01:11:11 +02:00
|
|
|
|
|
|
|
|
/**
|
2016-09-15 21:30:00 +02:00
|
|
|
* Parse barcodes as BarcodeFormat constants.
|
2016-09-15 01:11:11 +02:00
|
|
|
*
|
2017-01-23 15:20:39 +08:00
|
|
|
* Supports all iOS codes except [code39mod43, itf14]
|
2016-09-15 21:30:00 +02:00
|
|
|
*
|
2017-01-23 15:20:39 +08:00
|
|
|
* Additionally supports [codabar, maxicode, rss14, rssexpanded, upca, upceanextension]
|
2016-09-15 01:11:11 +02:00
|
|
|
*/
|
2016-09-15 21:30:00 +02:00
|
|
|
private BarcodeFormat parseBarCodeString(String c) {
|
|
|
|
|
if ("aztec".equals(c)) {
|
|
|
|
|
return BarcodeFormat.AZTEC;
|
|
|
|
|
} else if ("ean13".equals(c)) {
|
|
|
|
|
return BarcodeFormat.EAN_13;
|
|
|
|
|
} else if ("ean8".equals(c)) {
|
|
|
|
|
return BarcodeFormat.EAN_8;
|
|
|
|
|
} else if ("qr".equals(c)) {
|
|
|
|
|
return BarcodeFormat.QR_CODE;
|
|
|
|
|
} else if ("pdf417".equals(c)) {
|
|
|
|
|
return BarcodeFormat.PDF_417;
|
|
|
|
|
} else if ("upce".equals(c)) {
|
|
|
|
|
return BarcodeFormat.UPC_E;
|
|
|
|
|
} else if ("datamatrix".equals(c)) {
|
|
|
|
|
return BarcodeFormat.DATA_MATRIX;
|
|
|
|
|
} else if ("code39".equals(c)) {
|
|
|
|
|
return BarcodeFormat.CODE_39;
|
|
|
|
|
} else if ("code93".equals(c)) {
|
|
|
|
|
return BarcodeFormat.CODE_93;
|
|
|
|
|
} else if ("interleaved2of5".equals(c)) {
|
|
|
|
|
return BarcodeFormat.ITF;
|
|
|
|
|
} else if ("codabar".equals(c)) {
|
|
|
|
|
return BarcodeFormat.CODABAR;
|
|
|
|
|
} else if ("code128".equals(c)) {
|
|
|
|
|
return BarcodeFormat.CODE_128;
|
|
|
|
|
} else if ("maxicode".equals(c)) {
|
|
|
|
|
return BarcodeFormat.MAXICODE;
|
|
|
|
|
} else if ("rss14".equals(c)) {
|
|
|
|
|
return BarcodeFormat.RSS_14;
|
|
|
|
|
} else if ("rssexpanded".equals(c)) {
|
|
|
|
|
return BarcodeFormat.RSS_EXPANDED;
|
|
|
|
|
} else if ("upca".equals(c)) {
|
|
|
|
|
return BarcodeFormat.UPC_A;
|
|
|
|
|
} else if ("upceanextension".equals(c)) {
|
|
|
|
|
return BarcodeFormat.UPC_EAN_EXTENSION;
|
|
|
|
|
} else {
|
|
|
|
|
android.util.Log.v("RCTCamera", "Unsupported code.. [" + c + "]");
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Initialize the barcode decoder.
|
|
|
|
|
*/
|
|
|
|
|
private void initBarcodeReader(List<String> barCodeTypes) {
|
2016-09-15 01:11:11 +02:00
|
|
|
EnumMap<DecodeHintType, Object> hints = new EnumMap<>(DecodeHintType.class);
|
|
|
|
|
EnumSet<BarcodeFormat> decodeFormats = EnumSet.noneOf(BarcodeFormat.class);
|
2016-09-15 21:30:00 +02:00
|
|
|
|
|
|
|
|
if (barCodeTypes != null) {
|
|
|
|
|
for (String code : barCodeTypes) {
|
|
|
|
|
BarcodeFormat format = parseBarCodeString(code);
|
|
|
|
|
if (format != null) {
|
|
|
|
|
decodeFormats.add(format);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-09-15 01:11:11 +02:00
|
|
|
hints.put(DecodeHintType.POSSIBLE_FORMATS, decodeFormats);
|
|
|
|
|
_multiFormatReader.setHints(hints);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Spawn a barcode reader task if
|
|
|
|
|
* - the barcode scanner is enabled (has a onBarCodeRead function)
|
|
|
|
|
* - one isn't already running
|
|
|
|
|
*
|
|
|
|
|
* See {Camera.PreviewCallback}
|
|
|
|
|
*/
|
|
|
|
|
public void onPreviewFrame(byte[] data, Camera camera) {
|
|
|
|
|
if (RCTCamera.getInstance().isBarcodeScannerEnabled() && !RCTCameraViewFinder.barcodeScannerTaskLock) {
|
|
|
|
|
RCTCameraViewFinder.barcodeScannerTaskLock = true;
|
|
|
|
|
new ReaderAsyncTask(camera, data).execute();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private class ReaderAsyncTask extends AsyncTask<Void, Void, Void> {
|
|
|
|
|
private byte[] imageData;
|
|
|
|
|
private final Camera camera;
|
|
|
|
|
|
|
|
|
|
ReaderAsyncTask(Camera camera, byte[] imageData) {
|
|
|
|
|
this.camera = camera;
|
|
|
|
|
this.imageData = imageData;
|
|
|
|
|
}
|
|
|
|
|
|
2017-12-07 22:17:02 +02:00
|
|
|
private Result getBarcode(int width, int height) {
|
|
|
|
|
try{
|
|
|
|
|
PlanarYUVLuminanceSource source = new PlanarYUVLuminanceSource(imageData, width, height, 0, 0, width, height, false);
|
|
|
|
|
BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
|
|
|
|
|
return _multiFormatReader.decodeWithState(bitmap);
|
|
|
|
|
} catch (Throwable t) {
|
|
|
|
|
// meh
|
|
|
|
|
} finally {
|
|
|
|
|
_multiFormatReader.reset();
|
|
|
|
|
}
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private Result getBarcodeAnyOrientation() {
|
|
|
|
|
Camera.Size size = camera.getParameters().getPreviewSize();
|
|
|
|
|
|
|
|
|
|
int width = size.width;
|
|
|
|
|
int height = size.height;
|
|
|
|
|
Result result = getBarcode(width, height);
|
|
|
|
|
if (result != null)
|
|
|
|
|
return result;
|
|
|
|
|
|
|
|
|
|
rotateImage(width, height);
|
|
|
|
|
width = size.height;
|
|
|
|
|
height = size.width;
|
|
|
|
|
|
|
|
|
|
return getBarcode(width, height);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void rotateImage(int width, int height) {
|
|
|
|
|
byte[] rotated = new byte[imageData.length];
|
|
|
|
|
for (int y = 0; y < height; y++) {
|
|
|
|
|
for (int x = 0; x < width; x++) {
|
|
|
|
|
rotated[x * height + height - y - 1] = imageData[x + y * width];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
imageData = rotated;
|
|
|
|
|
}
|
|
|
|
|
|
2016-09-15 01:11:11 +02:00
|
|
|
@Override
|
|
|
|
|
protected Void doInBackground(Void... ignored) {
|
|
|
|
|
if (isCancelled()) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try {
|
2017-12-07 22:17:02 +02:00
|
|
|
// rotate for zxing if orientation is portrait
|
|
|
|
|
Result result = getBarcodeAnyOrientation();
|
|
|
|
|
if (result == null){
|
|
|
|
|
throw new Exception();
|
|
|
|
|
}
|
2016-09-15 01:11:11 +02:00
|
|
|
|
|
|
|
|
ReactContext reactContext = RCTCameraModule.getReactContextSingleton();
|
|
|
|
|
WritableMap event = Arguments.createMap();
|
2017-08-20 01:19:18 +03:00
|
|
|
WritableArray resultPoints = Arguments.createArray();
|
|
|
|
|
ResultPoint[] points = result.getResultPoints();
|
2017-12-07 22:17:02 +02:00
|
|
|
|
2017-08-20 01:19:18 +03:00
|
|
|
if(points != null) {
|
|
|
|
|
for (ResultPoint point : points) {
|
|
|
|
|
WritableMap newPoint = Arguments.createMap();
|
|
|
|
|
newPoint.putString("x", String.valueOf(point.getX()));
|
|
|
|
|
newPoint.putString("y", String.valueOf(point.getY()));
|
|
|
|
|
resultPoints.pushMap(newPoint);
|
|
|
|
|
}
|
|
|
|
|
}
|
2017-12-07 22:17:02 +02:00
|
|
|
|
2017-08-20 01:19:18 +03:00
|
|
|
event.putArray("bounds", resultPoints);
|
2016-09-15 01:11:11 +02:00
|
|
|
event.putString("data", result.getText());
|
|
|
|
|
event.putString("type", result.getBarcodeFormat().toString());
|
|
|
|
|
reactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class).emit("CameraBarCodeReadAndroid", event);
|
|
|
|
|
|
|
|
|
|
} catch (Throwable t) {
|
|
|
|
|
// meh
|
|
|
|
|
} finally {
|
|
|
|
|
_multiFormatReader.reset();
|
|
|
|
|
RCTCameraViewFinder.barcodeScannerTaskLock = false;
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-12-06 01:58:17 +01:00
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public boolean onTouchEvent(MotionEvent event) {
|
|
|
|
|
// Get the pointer ID
|
|
|
|
|
Camera.Parameters params = _camera.getParameters();
|
|
|
|
|
int action = event.getAction();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (event.getPointerCount() > 1) {
|
|
|
|
|
// handle multi-touch events
|
|
|
|
|
if (action == MotionEvent.ACTION_POINTER_DOWN) {
|
|
|
|
|
mFingerSpacing = getFingerSpacing(event);
|
|
|
|
|
} else if (action == MotionEvent.ACTION_MOVE && params.isZoomSupported()) {
|
|
|
|
|
_camera.cancelAutoFocus();
|
|
|
|
|
handleZoom(event, params);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
// handle single touch events
|
|
|
|
|
if (action == MotionEvent.ACTION_UP) {
|
|
|
|
|
handleFocus(event, params);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void handleZoom(MotionEvent event, Camera.Parameters params) {
|
|
|
|
|
int maxZoom = params.getMaxZoom();
|
|
|
|
|
int zoom = params.getZoom();
|
|
|
|
|
float newDist = getFingerSpacing(event);
|
|
|
|
|
if (newDist > mFingerSpacing) {
|
|
|
|
|
//zoom in
|
|
|
|
|
if (zoom < maxZoom)
|
|
|
|
|
zoom++;
|
|
|
|
|
} else if (newDist < mFingerSpacing) {
|
|
|
|
|
//zoom out
|
|
|
|
|
if (zoom > 0)
|
|
|
|
|
zoom--;
|
|
|
|
|
}
|
|
|
|
|
mFingerSpacing = newDist;
|
|
|
|
|
params.setZoom(zoom);
|
|
|
|
|
_camera.setParameters(params);
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-24 09:25:49 -07:00
|
|
|
/**
|
|
|
|
|
* Handles setting focus to the location of the event.
|
|
|
|
|
*
|
|
|
|
|
* Note that this will override the focus mode on the camera to FOCUS_MODE_AUTO if available,
|
|
|
|
|
* even if this was previously something else (such as FOCUS_MODE_CONTINUOUS_*; see also
|
|
|
|
|
* {@link #startCamera()}. However, this makes sense - after the user has initiated any
|
|
|
|
|
* specific focus intent, we shouldn't be refocusing and overriding their request!
|
|
|
|
|
*/
|
2016-12-06 01:58:17 +01:00
|
|
|
public void handleFocus(MotionEvent event, Camera.Parameters params) {
|
|
|
|
|
List<String> supportedFocusModes = params.getSupportedFocusModes();
|
|
|
|
|
if (supportedFocusModes != null && supportedFocusModes.contains(Camera.Parameters.FOCUS_MODE_AUTO)) {
|
2017-04-24 09:25:49 -07:00
|
|
|
// Ensure focus areas are enabled. If max num focus areas is 0, then focus area is not
|
|
|
|
|
// supported, so we cannot do anything here.
|
|
|
|
|
if (params.getMaxNumFocusAreas() == 0) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Cancel any previous focus actions.
|
|
|
|
|
_camera.cancelAutoFocus();
|
|
|
|
|
|
|
|
|
|
// Compute focus area rect.
|
|
|
|
|
Camera.Area focusAreaFromMotionEvent;
|
|
|
|
|
try {
|
|
|
|
|
focusAreaFromMotionEvent = RCTCameraUtils.computeFocusAreaFromMotionEvent(event, _surfaceTextureWidth, _surfaceTextureHeight);
|
|
|
|
|
} catch (final RuntimeException e) {
|
|
|
|
|
e.printStackTrace();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Set focus mode to auto.
|
|
|
|
|
params.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);
|
|
|
|
|
// Set focus area.
|
|
|
|
|
final ArrayList<Camera.Area> focusAreas = new ArrayList<Camera.Area>();
|
|
|
|
|
focusAreas.add(focusAreaFromMotionEvent);
|
|
|
|
|
params.setFocusAreas(focusAreas);
|
|
|
|
|
|
|
|
|
|
// Also set metering area if enabled. If max num metering areas is 0, then metering area
|
|
|
|
|
// is not supported. We can usually safely omit this anyway, though.
|
|
|
|
|
if (params.getMaxNumMeteringAreas() > 0) {
|
|
|
|
|
params.setMeteringAreas(focusAreas);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Set parameters before starting auto-focus.
|
|
|
|
|
_camera.setParameters(params);
|
|
|
|
|
|
|
|
|
|
// Start auto-focus now that focus area has been set. If successful, then can cancel
|
|
|
|
|
// it afterwards. Wrap in try-catch to avoid crashing on merely autoFocus fails.
|
2017-04-05 20:52:51 -03:00
|
|
|
try {
|
|
|
|
|
_camera.autoFocus(new Camera.AutoFocusCallback() {
|
|
|
|
|
@Override
|
2017-04-24 09:25:49 -07:00
|
|
|
public void onAutoFocus(boolean success, Camera camera) {
|
|
|
|
|
if (success) {
|
|
|
|
|
camera.cancelAutoFocus();
|
|
|
|
|
}
|
2017-04-05 20:52:51 -03:00
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
e.printStackTrace();
|
|
|
|
|
}
|
2016-12-06 01:58:17 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** Determine the space between the first two fingers */
|
|
|
|
|
private float getFingerSpacing(MotionEvent event) {
|
|
|
|
|
float x = event.getX(0) - event.getX(1);
|
|
|
|
|
float y = event.getY(0) - event.getY(1);
|
|
|
|
|
return (float) Math.sqrt(x * x + y * y);
|
|
|
|
|
}
|
2017-12-07 22:17:02 +02:00
|
|
|
}
|