Merge pull request #157 from farmisen/droid

Android support.
This commit is contained in:
Loch Wansbrough 2016-01-23 13:11:58 -08:00
commit 743685b48a
11 changed files with 1032 additions and 84 deletions

34
android/build.gradle Executable file
View File

@ -0,0 +1,34 @@
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:1.2.3'
}
}
apply plugin: 'com.android.library'
android {
compileSdkVersion 23
buildToolsVersion "23.0.1"
defaultConfig {
minSdkVersion 16
targetSdkVersion 22
versionCode 1
versionName "1.0"
}
lintOptions {
abortOnError false
}
}
repositories {
mavenCentral()
}
dependencies {
compile "com.facebook.react:react-native:0.17.+"
}

View File

@ -1,4 +1,4 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.lwansbrough.ReactCamera">
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.lwansbrough.RCTCamera">
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.autofocus" />

View File

@ -0,0 +1,201 @@
/**
* Created by Fabrice Armisen (farmisen@gmail.com) on 1/4/16.
*/
package com.lwansbrough.RCTCamera;
import android.hardware.Camera;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class RCTCamera {
private static final RCTCamera ourInstance = new RCTCamera();
private final HashMap<Integer, CameraInfoWrapper> _cameraInfos;
private final HashMap<Integer, Integer> _cameraTypeToIndex;
private final Map<Number, Camera> _cameras;
private int _orientation = -1;
private int _actualDeviceOrientation = 0;
public static RCTCamera getInstance() {
return ourInstance;
}
public Camera acquireCameraInstance(int type) {
if (null == _cameras.get(type) && null != _cameraTypeToIndex.get(type)) {
try {
Camera camera = Camera.open(_cameraTypeToIndex.get(type));
_cameras.put(type, camera);
adjustPreviewLayout(type);
} catch (Exception e) {
System.console().printf("acquireCameraInstance: %s", e.getLocalizedMessage());
}
}
return _cameras.get(type);
}
public void releaseCameraInstance(int type) {
if (null != _cameras.get(type)) {
_cameras.get(type).release();
_cameras.remove(type);
}
}
public int getPreviewWidth(int type) {
CameraInfoWrapper cameraInfo = _cameraInfos.get(type);
if (null == cameraInfo) {
return 0;
}
return cameraInfo.previewWidth;
}
public int getPreviewHeight(int type) {
CameraInfoWrapper cameraInfo = _cameraInfos.get(type);
if (null == cameraInfo) {
return 0;
}
return cameraInfo.previewHeight;
}
public void setOrientation(int orientation) {
if (_orientation == orientation) {
return;
}
_orientation = orientation;
adjustPreviewLayout(RCTCameraModule.RCT_CAMERA_TYPE_FRONT);
adjustPreviewLayout(RCTCameraModule.RCT_CAMERA_TYPE_BACK);
}
public void setActualDeviceOrientation(int actualDeviceOrientation) {
_actualDeviceOrientation = actualDeviceOrientation;
adjustPreviewLayout(RCTCameraModule.RCT_CAMERA_TYPE_FRONT);
adjustPreviewLayout(RCTCameraModule.RCT_CAMERA_TYPE_BACK);
}
public void setTorchMode(int cameraType, int torchMode) {
Camera camera = _cameras.get(cameraType);
if (null == camera) {
return;
}
Camera.Parameters parameters = camera.getParameters();
String value = parameters.getFlashMode();
switch (torchMode) {
case RCTCameraModule.RCT_CAMERA_TORCH_MODE_ON:
value = Camera.Parameters.FLASH_MODE_TORCH;
break;
case RCTCameraModule.RCT_CAMERA_TORCH_MODE_OFF:
value = Camera.Parameters.FLASH_MODE_OFF;
break;
}
List<String> flashModes = parameters.getSupportedFlashModes();
if (flashModes != null && flashModes.contains(value)) {
parameters.setFlashMode(value);
camera.setParameters(parameters);
}
}
public void setFlashMode(int cameraType, int flashMode) {
Camera camera = _cameras.get(cameraType);
if (null == camera) {
return;
}
Camera.Parameters parameters = camera.getParameters();
String value = parameters.getFlashMode();
switch (flashMode) {
case RCTCameraModule.RCT_CAMERA_FLASH_MODE_AUTO:
value = Camera.Parameters.FLASH_MODE_AUTO;
break;
case RCTCameraModule.RCT_CAMERA_FLASH_MODE_ON:
value = Camera.Parameters.FLASH_MODE_ON;
break;
case RCTCameraModule.RCT_CAMERA_FLASH_MODE_OFF:
value = Camera.Parameters.FLASH_MODE_OFF;
break;
}
List<String> flashModes = parameters.getSupportedFlashModes();
if (flashModes != null && flashModes.contains(value)) {
parameters.setFlashMode(value);
camera.setParameters(parameters);
}
}
private void adjustPreviewLayout(int type) {
Camera camera = _cameras.get(type);
if (null == camera) {
return;
}
CameraInfoWrapper cameraInfo = _cameraInfos.get(type);
int rotation = cameraInfo.info.orientation;
if (cameraInfo.info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
rotation = (720 - rotation - _actualDeviceOrientation * 90) % 360;
} else {
rotation = (rotation - _actualDeviceOrientation * 90 + 360) % 360;
}
cameraInfo.rotation = rotation;
// TODO: take in account the _orientation prop
camera.setDisplayOrientation(cameraInfo.rotation);
Camera.Parameters parameters = camera.getParameters();
parameters.setRotation(cameraInfo.rotation);
// set preview size
int width = parameters.getSupportedPreviewSizes().get(0).width;
int height = parameters.getSupportedPreviewSizes().get(0).height;
parameters.setPreviewSize(width, height);
try {
camera.setParameters(parameters);
} catch (Exception e) {
e.printStackTrace();
}
if (cameraInfo.rotation == 0 || cameraInfo.rotation == 180) {
cameraInfo.previewWidth = width;
cameraInfo.previewHeight = height;
} else {
cameraInfo.previewWidth = height;
cameraInfo.previewHeight = width;
}
}
private RCTCamera() {
_cameras = new HashMap<>();
_cameraInfos = new HashMap<>();
_cameraTypeToIndex = new HashMap<>();
// map camera types to camera indexes and collect cameras properties
for (int i = 0; i < Camera.getNumberOfCameras(); i++) {
Camera.CameraInfo info = new Camera.CameraInfo();
Camera.getCameraInfo(i, info);
if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT && _cameraInfos.get(RCTCameraModule.RCT_CAMERA_TYPE_FRONT) == null) {
_cameraInfos.put(RCTCameraModule.RCT_CAMERA_TYPE_FRONT, new CameraInfoWrapper(info));
_cameraTypeToIndex.put(RCTCameraModule.RCT_CAMERA_TYPE_FRONT, i);
acquireCameraInstance(RCTCameraModule.RCT_CAMERA_TYPE_FRONT);
releaseCameraInstance(RCTCameraModule.RCT_CAMERA_TYPE_FRONT);
} else if (info.facing == Camera.CameraInfo.CAMERA_FACING_BACK && _cameraInfos.get(RCTCameraModule.RCT_CAMERA_TYPE_BACK) == null) {
_cameraInfos.put(RCTCameraModule.RCT_CAMERA_TYPE_BACK, new CameraInfoWrapper(info));
_cameraTypeToIndex.put(RCTCameraModule.RCT_CAMERA_TYPE_BACK, i);
acquireCameraInstance(RCTCameraModule.RCT_CAMERA_TYPE_BACK);
releaseCameraInstance(RCTCameraModule.RCT_CAMERA_TYPE_BACK);
}
}
}
private class CameraInfoWrapper {
public final Camera.CameraInfo info;
public int rotation = 0;
public int previewWidth = -1;
public int previewHeight = -1;
public CameraInfoWrapper(Camera.CameraInfo info) {
this.info = info;
}
}
}

View File

@ -0,0 +1,229 @@
/**
* Created by Fabrice Armisen (farmisen@gmail.com) on 1/4/16.
*/
package com.lwansbrough.RCTCamera;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.hardware.Camera;
import android.net.Uri;
import android.os.Environment;
import android.provider.MediaStore;
import android.util.Base64;
import android.util.Log;
import com.facebook.react.bridge.*;
import javax.annotation.Nullable;
import java.io.*;
import java.text.SimpleDateFormat;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
public class RCTCameraModule extends ReactContextBaseJavaModule {
private static final String TAG = "RCTCameraModule";
public static final int RCT_CAMERA_ASPECT_FILL = 0;
public static final int RCT_CAMERA_ASPECT_FIT = 1;
public static final int RCT_CAMERA_ASPECT_STRETCH = 2;
public static final int RCT_CAMERA_CAPTURE_MODE_STILL = 0;
public static final int RCT_CAMERA_CAPTURE_MODE_VIDEO = 1;
public static final int RCT_CAMERA_CAPTURE_TARGET_MEMORY = 0;
public static final int RCT_CAMERA_CAPTURE_TARGET_DISK = 1;
public static final int RCT_CAMERA_CAPTURE_TARGET_CAMERA_ROLL = 2;
public static final int RCT_CAMERA_ORIENTATION_AUTO = 0;
public static final int RCT_CAMERA_ORIENTATION_LANDSCAPE_LEFT = 1;
public static final int RCT_CAMERA_ORIENTATION_LANDSCAPE_RIGHT = 2;
public static final int RCT_CAMERA_ORIENTATION_PORTRAIT = 3;
public static final int RCT_CAMERA_ORIENTATION_PORTRAIT_UPSIDE_DOWN = 4;
public static final int RCT_CAMERA_TYPE_FRONT = 1;
public static final int RCT_CAMERA_TYPE_BACK = 2;
public static final int RCT_CAMERA_FLASH_MODE_OFF = 0;
public static final int RCT_CAMERA_FLASH_MODE_ON = 1;
public static final int RCT_CAMERA_FLASH_MODE_AUTO = 2;
public static final int RCT_CAMERA_TORCH_MODE_OFF = 0;
public static final int RCT_CAMERA_TORCH_MODE_ON = 1;
public static final int RCT_CAMERA_TORCH_MODE_AUTO = 2;
public static final int MEDIA_TYPE_IMAGE = 1;
public static final int MEDIA_TYPE_VIDEO = 2;
private final ReactApplicationContext _reactContext;
public RCTCameraModule(ReactApplicationContext reactContext) {
super(reactContext);
_reactContext = reactContext;
}
@Override
public String getName() {
return "RCTCameraModule";
}
@Nullable
@Override
public Map<String, Object> getConstants() {
return Collections.unmodifiableMap(new HashMap<String, Object>() {
{
put("Aspect", getAspectConstants());
put("Type", getTypeConstants());
put("CaptureMode", getCaptureModeConstants());
put("CaptureTarget", getCaptureTargetConstants());
put("Orientation", getOrientationConstants());
put("FlashMode", getFlashModeConstants());
put("TorchMode", getTorchModeConstants());
}
private Map<String, Object> getAspectConstants() {
return Collections.unmodifiableMap(new HashMap<String, Object>() {
{
put("stretch", RCT_CAMERA_ASPECT_STRETCH);
put("fit", RCT_CAMERA_ASPECT_FIT);
put("fill", RCT_CAMERA_ASPECT_FILL);
}
});
}
private Map<String, Object> getTypeConstants() {
return Collections.unmodifiableMap(new HashMap<String, Object>() {
{
put("front", RCT_CAMERA_TYPE_FRONT);
put("back", RCT_CAMERA_TYPE_BACK);
}
});
}
private Map<String, Object> getCaptureModeConstants() {
return Collections.unmodifiableMap(new HashMap<String, Object>() {
{
put("still", RCT_CAMERA_CAPTURE_MODE_STILL);
put("video", RCT_CAMERA_CAPTURE_MODE_VIDEO);
}
});
}
private Map<String, Object> getCaptureTargetConstants() {
return Collections.unmodifiableMap(new HashMap<String, Object>() {
{
put("memory", RCT_CAMERA_CAPTURE_TARGET_MEMORY);
put("disk", RCT_CAMERA_CAPTURE_TARGET_DISK);
put("cameraRoll", RCT_CAMERA_CAPTURE_TARGET_CAMERA_ROLL);
}
});
}
private Map<String, Object> getOrientationConstants() {
return Collections.unmodifiableMap(new HashMap<String, Object>() {
{
put("auto", RCT_CAMERA_ORIENTATION_AUTO);
put("landscapeLeft", RCT_CAMERA_ORIENTATION_LANDSCAPE_LEFT);
put("landscapeRight", RCT_CAMERA_ORIENTATION_LANDSCAPE_RIGHT);
put("portrait", RCT_CAMERA_ORIENTATION_PORTRAIT);
put("portraitUpsideDown", RCT_CAMERA_ORIENTATION_PORTRAIT_UPSIDE_DOWN);
}
});
}
private Map<String, Object> getFlashModeConstants() {
return Collections.unmodifiableMap(new HashMap<String, Object>() {
{
put("off", RCT_CAMERA_FLASH_MODE_OFF);
put("on", RCT_CAMERA_FLASH_MODE_ON);
put("auto", RCT_CAMERA_FLASH_MODE_AUTO);
}
});
}
private Map<String, Object> getTorchModeConstants() {
return Collections.unmodifiableMap(new HashMap<String, Object>() {
{
put("off", RCT_CAMERA_TORCH_MODE_OFF);
put("on", RCT_CAMERA_TORCH_MODE_ON);
put("auto", RCT_CAMERA_TORCH_MODE_AUTO);
}
});
}
});
}
@ReactMethod
public void capture(final ReadableMap options, final Callback callback) {
Camera camera = RCTCamera.getInstance().acquireCameraInstance(options.getInt("type"));
if (null == camera) {
callback.invoke("No camera found.", null);
return;
}
camera.takePicture(null, null, new Camera.PictureCallback() {
@Override
public void onPictureTaken(byte[] data, Camera camera) {
switch (options.getInt("target")) {
case RCT_CAMERA_CAPTURE_TARGET_MEMORY:
String encoded = Base64.encodeToString(data, Base64.DEFAULT);
callback.invoke(null, encoded);
break;
case RCT_CAMERA_CAPTURE_TARGET_CAMERA_ROLL:
BitmapFactory.Options bitmapOptions = new BitmapFactory.Options();
Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length, bitmapOptions);
String url = MediaStore.Images.Media.insertImage(
_reactContext.getContentResolver(),
bitmap, options.getString("title"),
options.getString("description"));
callback.invoke(null, url);
break;
case RCT_CAMERA_CAPTURE_TARGET_DISK:
File pictureFile = getOutputMediaFile(MEDIA_TYPE_IMAGE);
if (pictureFile == null) {
callback.invoke("Error creating media file.", null);
return;
}
try {
FileOutputStream fos = new FileOutputStream(pictureFile);
fos.write(data);
fos.close();
} catch (FileNotFoundException e) {
callback.invoke("File not found: " + e.getMessage(), null);
} catch (IOException e) {
callback.invoke("Error accessing file: " + e.getMessage(), null);
}
callback.invoke(null, Uri.fromFile(pictureFile).toString());
break;
}
}
});
}
@ReactMethod
public void stopCapture(final ReadableMap options, final Callback callback) {
// TODO: implement video capture
}
private File getOutputMediaFile(int type) {
File mediaStorageDir = new File(Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_PICTURES), "RCTCameraModule");
// Create the storage directory if it does not exist
if (!mediaStorageDir.exists()) {
if (!mediaStorageDir.mkdirs()) {
Log.e(TAG, "failed to create directory:" + mediaStorageDir.getAbsolutePath());
return null;
}
}
// Create a media file name
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
File mediaFile;
if (type == MEDIA_TYPE_IMAGE) {
mediaFile = new File(mediaStorageDir.getPath() + File.separator +
"IMG_" + timeStamp + ".jpg");
} else if (type == MEDIA_TYPE_VIDEO) {
mediaFile = new File(mediaStorageDir.getPath() + File.separator +
"VID_" + timeStamp + ".mp4");
} else {
Log.e(TAG, "Unsupported media type:" + type);
return null;
}
return mediaFile;
}
}

View File

@ -0,0 +1,31 @@
package com.lwansbrough.RCTCamera;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.ViewManager;
import com.facebook.react.bridge.JavaScriptModule;
public class RCTCameraPackage implements ReactPackage {
@Override
public List<NativeModule> createNativeModules(ReactApplicationContext reactApplicationContext) {
return Collections.<NativeModule>singletonList(new RCTCameraModule(reactApplicationContext));
}
@Override
public List<Class<? extends JavaScriptModule>> createJSModules() {
return Collections.emptyList();
}
@Override
public List<ViewManager> createViewManagers(ReactApplicationContext reactApplicationContext) {
//noinspection ArraysAsListWithZeroOrOneArgument
return Collections.<ViewManager>singletonList(new RCTCameraViewManager());
}
}

View File

@ -0,0 +1,149 @@
/**
* Created by Fabrice Armisen (farmisen@gmail.com) on 1/3/16.
*/
package com.lwansbrough.RCTCamera;
import android.content.Context;
import android.graphics.*;
import android.hardware.SensorManager;
import android.view.OrientationEventListener;
import android.view.ViewGroup;
import android.view.WindowManager;
public class RCTCameraView extends ViewGroup {
private final OrientationEventListener _orientationListener;
private final Context _context;
private RCTCameraViewFinder _viewFinder = null;
private int _actualDeviceOrientation = -1;
private int _aspect = RCTCameraModule.RCT_CAMERA_ASPECT_FIT;
private int _torchMode = -1;
private int _flashMode = -1;
public RCTCameraView(Context context) {
super(context);
this._context = context;
setActualDeviceOrientation(context);
_orientationListener = new OrientationEventListener(context, SensorManager.SENSOR_DELAY_NORMAL) {
@Override
public void onOrientationChanged(int orientation) {
if (setActualDeviceOrientation(_context)) {
layoutViewFinder();
}
}
};
if (_orientationListener.canDetectOrientation()) {
_orientationListener.enable();
} else {
_orientationListener.disable();
}
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
layoutViewFinder(left, top, right, bottom);
}
public void setAspect(int aspect) {
this._aspect = aspect;
layoutViewFinder();
}
public void setCameraType(final int type) {
if (null != this._viewFinder) {
this._viewFinder.setCameraType(type);
} else {
_viewFinder = new RCTCameraViewFinder(_context, type);
if (-1 != this._flashMode) {
_viewFinder.setFlashMode(this._flashMode);
}
if (-1 != this._torchMode) {
_viewFinder.setFlashMode(this._torchMode);
}
addView(_viewFinder);
}
}
public void setTorchMode(int torchMode) {
this._torchMode = torchMode;
if (this._viewFinder != null) {
this._viewFinder.setTorchMode(torchMode);
}
}
public void setFlashMode(int flashMode) {
this._flashMode = flashMode;
if (this._viewFinder != null) {
this._viewFinder.setFlashMode(flashMode);
}
}
public void setOrientation(int orientation) {
RCTCamera.getInstance().setOrientation(orientation);
if (this._viewFinder != null) {
layoutViewFinder();
}
}
private boolean setActualDeviceOrientation(Context context) {
int actualDeviceOrientation = ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay().getOrientation();
if (_actualDeviceOrientation != actualDeviceOrientation) {
_actualDeviceOrientation = actualDeviceOrientation;
RCTCamera.getInstance().setActualDeviceOrientation(_actualDeviceOrientation);
return true;
} else {
return false;
}
}
private void layoutViewFinder() {
layoutViewFinder(this.getLeft(), this.getTop(), this.getRight(), this.getBottom());
}
private void layoutViewFinder(int left, int top, int right, int bottom) {
if (null == _viewFinder) {
return;
}
float width = right - left;
float height = bottom - top;
int viewfinderWidth;
int viewfinderHeight;
double ratio;
switch (this._aspect) {
case RCTCameraModule.RCT_CAMERA_ASPECT_FIT:
ratio = this._viewFinder.getRatio();
if (ratio * height > width) {
viewfinderHeight = (int) (width / ratio);
viewfinderWidth = (int) width;
} else {
viewfinderWidth = (int) (ratio * height);
viewfinderHeight = (int) height;
}
break;
case RCTCameraModule.RCT_CAMERA_ASPECT_FILL:
ratio = this._viewFinder.getRatio();
if (ratio * height < width) {
viewfinderHeight = (int) (width / ratio);
viewfinderWidth = (int) width;
} else {
viewfinderWidth = (int) (ratio * height);
viewfinderHeight = (int) height;
}
break;
default:
viewfinderWidth = (int) width;
viewfinderHeight = (int) height;
}
int viewFinderPaddingX = (int) ((width - viewfinderWidth) / 2);
int viewFinderPaddingY = (int) ((height - viewfinderHeight) / 2);
this._viewFinder.layout(viewFinderPaddingX, viewFinderPaddingY, viewFinderPaddingX + viewfinderWidth, viewFinderPaddingY + viewfinderHeight);
this.postInvalidate(this.getLeft(), this.getTop(), this.getRight(), this.getBottom());
}
}

View File

@ -0,0 +1,125 @@
/**
* Created by Fabrice Armisen (farmisen@gmail.com) on 1/3/16.
*/
package com.lwansbrough.RCTCamera;
import android.content.Context;
import android.graphics.SurfaceTexture;
import android.hardware.Camera;
import android.view.TextureView;
class RCTCameraViewFinder extends TextureView implements TextureView.SurfaceTextureListener {
private int _cameraType;
private SurfaceTexture _surfaceTexture;
private boolean _isStarting;
private boolean _isStopping;
private Camera _camera;
public RCTCameraViewFinder(Context context, int type) {
super(context);
this.setSurfaceTextureListener(this);
this._cameraType = type;
}
@Override
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
_surfaceTexture = surface;
startCamera();
}
@Override
public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
}
@Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
_surfaceTexture = null;
stopCamera();
return true;
}
@Override
public void onSurfaceTextureUpdated(SurfaceTexture surface) {
}
public double getRatio() {
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();
}
public void setTorchMode(int torchMode) {
RCTCamera.getInstance().setTorchMode(_cameraType, torchMode);
}
public void setFlashMode(int flashMode) {
RCTCamera.getInstance().setTorchMode(_cameraType, flashMode);
}
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);
_camera.setPreviewTexture(_surfaceTexture);
_camera.startPreview();
} 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();
RCTCamera.getInstance().releaseCameraInstance(_cameraType);
_camera = null;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
_isStopping = false;
}
}
}
}

View File

@ -0,0 +1,58 @@
package com.lwansbrough.RCTCamera;
import android.support.annotation.Nullable;
import com.facebook.react.uimanager.*;
public class RCTCameraViewManager extends SimpleViewManager<RCTCameraView> {
private static final String REACT_CLASS = "RCTCameraView";
@Override
public String getName() {
return REACT_CLASS;
}
@Override
public RCTCameraView createViewInstance(ThemedReactContext context) {
return new RCTCameraView(context);
}
@ReactProp(name = "aspect")
public void setAspect(RCTCameraView view, int aspect) {
view.setAspect(aspect);
}
@ReactProp(name = "captureMode")
public void setCaptureMode(RCTCameraView view, int captureMode) {
// TODO - implement video mode
}
@ReactProp(name = "captureTarget")
public void setCaptureTarget(RCTCameraView view, int captureTarget) {
// No reason to handle this props value here since it's passed again to the RCTCameraModule capture method
}
@ReactProp(name = "type")
public void setType(RCTCameraView view, int type) {
view.setCameraType(type);
}
@ReactProp(name = "torchMode")
public void setTorchMode(RCTCameraView view, int torchMode) {
view.setTorchMode(torchMode);
}
@ReactProp(name = "flashMode")
public void setFlashMode(RCTCameraView view, int flashMode) {
view.setFlashMode(flashMode);
}
@ReactProp(name = "orientation")
public void setOrientation(RCTCameraView view, int orientation) {
view.setOrientation(orientation);
}
@ReactProp(name = "captureAudio")
public void setCaptureAudio(RCTCameraView view, boolean captureAudio) {
// TODO - implement video mode
}
}

View File

@ -1,23 +0,0 @@
package com.lwansbrough.RCTCamera;
import java.util.Arrays;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.ViewManager;
import com.facebook.react.bridge.JavaScriptModule;
public class ReactCamera implements ReactPackage {
@Override
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
return Arrays.<ViewManager>asList(
new ReactCameraManager()
);
}
}

View File

@ -1,51 +0,0 @@
package com.lwansbrough.RCTCamera;
import java.util.Arrays;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.ViewManager;
import com.facebook.react.bridge.JavaScriptModule;
public class ReactCameraManager extends SimpleViewManager<ReactCameraView> {
public static final String REACT_CLASS = "RCTCamera";
@UIProp(UIProp.Type.STRING)
public static final String PROP_SRC = "src";
@UIProp(UIProp.Type.NUMBER)
public static final String PROP_BORDER_RADIUS = "borderRadius";
@UIProp(UIProp.Type.STRING)
public static final String PROP_RESIZE_MODE = ViewProps.RESIZE_MODE;
@Override
public String getName() {
return REACT_CLASS;
}
@Override
public ReactCameraView createViewInstance(ThemedReactContext context) {
return new ReactCameraView(context, Fresco.newDraweeControllerBuilder(), mCallerContext);
}
@Override
public void updateView(final ReactImageView view, final CatalystStylesDiffMap props) {
super.updateView(view, props);
if (props.hasKey(PROP_RESIZE_MODE)) {
view.setScaleType(
ImageResizeMode.toScaleType(props.getString(PROP_RESIZE_MODE)));
}
if (props.hasKey(PROP_SRC)) {
view.setSource(props.getString(PROP_SRC));
}
if (props.hasKey(PROP_BORDER_RADIUS)) {
view.setBorderRadius(props.getFloat(PROP_BORDER_RADIUS, 0.0f));
}
view.maybeUpdateView();
}
}

View File

@ -1,12 +1,207 @@
var { requireNativeComponent, PropTypes } = require('react-native');
var React = require('react-native');
var { View, StyleSheet, requireNativeComponent, PropTypes, NativeModules, DeviceEventEmitter } = React;
var iface = {
name: 'ImageView',
propTypes: {
src: PropTypes.string,
borderRadius: PropTypes.number,
resizeMode: PropTypes.oneOf(['cover', 'contain', 'stretch']),
},
var CAMERA_REF = 'camera';
var constants = {
Aspect: NativeModules.CameraModule.Aspect,
BarCodeType: NativeModules.CameraModule.BarCodeType,
Type: NativeModules.CameraModule.Type,
CaptureMode: NativeModules.CameraModule.CaptureMode,
CaptureTarget: NativeModules.CameraModule.CaptureTarget,
Orientation: NativeModules.CameraModule.Orientation,
FlashMode: NativeModules.CameraModule.FlashMode,
TorchMode: NativeModules.CameraModule.TorchMode
};
module.exports = requireNativeComponent('RCTCamera', iface);
var Camera = React.createClass({
propTypes: {
aspect: PropTypes.oneOfType([
PropTypes.string,
PropTypes.number
]),
captureAudio: PropTypes.bool,
captureMode: PropTypes.oneOfType([
PropTypes.string,
PropTypes.number
]),
captureTarget: PropTypes.oneOfType([
PropTypes.string,
PropTypes.number
]),
type: PropTypes.oneOfType([
PropTypes.string,
PropTypes.number
]),
orientation: PropTypes.oneOfType([
PropTypes.string,
PropTypes.number
]),
flashMode: PropTypes.oneOfType([
PropTypes.string,
PropTypes.number
]),
torchMode: PropTypes.oneOfType([
PropTypes.string,
PropTypes.number
]),
defaultOnFocusComponent: PropTypes.bool,
onFocusChanged: PropTypes.func,
onZoomChanged: PropTypes.func,
...View.propTypes
},
setNativeProps(props) {
this.refs[CAMERA_REF].setNativeProps(props);
},
getDefaultProps() {
return {
aspect: constants.Aspect.fill,
type: constants.Type.back,
orientation: constants.Orientation.auto,
captureAudio: true,
captureMode: constants.CaptureMode.still,
captureTarget: constants.CaptureTarget.cameraRoll,
flashMode: constants.FlashMode.off,
torchMode: constants.TorchMode.off
};
},
getInitialState() {
return {
isAuthorized: false,
isRecording: false
};
},
componentWillMount() {
//// TODO: handle properly Android 6 new permissions style
this.state.isAuthorized = true;
this.setState(this.state);
this.cameraBarCodeReadListener = DeviceEventEmitter.addListener('CameraBarCodeRead', this._onBarCodeRead);
},
componentWillUnmount() {
this.cameraBarCodeReadListener.remove();
if (this.state.isRecording) {
this.stopCapture();
}
},
render() {
var style = [styles.base, this.props.style];
var aspect = this.props.aspect,
type = this.props.type,
orientation = this.props.orientation,
flashMode = this.props.flashMode,
torchMode = this.props.torchMode;
var legacyProps = {
aspect: {
Fill: 'fill',
Fit: 'fit',
Stretch: 'stretch'
},
orientation: {
LandscapeLeft: 'landscapeLeft',
LandscapeRight: 'landscapeRight',
Portrait: 'portrait',
PortraitUpsideDown: 'portraitUpsideDown'
},
type: {
Front: 'front',
Back: 'back'
}
};
if (typeof aspect === 'string') {
aspect = constants.Aspect[aspect];
}
if (typeof flashMode === 'string') {
flashMode = constants.FlashMode[flashMode];
}
if (typeof orientation === 'string') {
orientation = constants.Orientation[orientation];
}
if (typeof torchMode === 'string') {
torchMode = constants.TorchMode[torchMode];
}
if (typeof type === 'string') {
type = constants.Type[type];
}
var nativeProps = Object.assign({}, this.props, {
style,
aspect: aspect,
type: type,
orientation: orientation,
flashMode: flashMode,
torchMode: torchMode
});
return <RCTCameraView ref={CAMERA_REF} {... nativeProps} />;
},
_onBarCodeRead(e) {
this.props.onBarCodeRead && this.props.onBarCodeRead(e);
},
capture(options, cb) {
if (arguments.length == 1) {
cb = options;
options = {};
}
options = Object.assign({}, {
audio: this.props.captureAudio,
mode: this.props.captureMode,
target: this.props.captureTarget,
type: this.props.type
}, options);
if (typeof options.mode === 'string') {
options.mode = constants.CaptureMode[options.mode];
}
if (options.mode === constants.CaptureMode.video) {
options.totalSeconds = (options.totalSeconds > -1 ? options.totalSeconds : -1);
options.preferredTimeScale = options.preferredTimeScale || 30;
this.setState({isRecording: true});
}
if (typeof options.target === 'string') {
options.target = constants.CaptureTarget[options.target];
}
if (typeof options.type === 'string') {
options.type = constants.Type[options.type];
}
NativeModules.CameraModule.capture(options, cb);
},
stopCapture() {
if (this.state.isRecording) {
NativeModules.CameraManager.stopCapture();
this.setState({ isRecording: false });
}
}
});
var RCTCameraView = requireNativeComponent('RCTCameraView', Camera);
var styles = StyleSheet.create({
base: {},
});
Camera.constants = constants;
module.exports = Camera;