fix snapshot orientation when device orientation is locked (#232)

* use device orientation on android only with auto orientation

* guard against missing playSoundOnCapture and quality props in options

* add video orientation support

* use device orientation on ios only with auto orientation
This commit is contained in:
Radu-Marius Popovici 2016-05-10 21:05:03 +03:00 committed by Zack Story
parent f9137824a0
commit 0c3dba1ff6
5 changed files with 83 additions and 38 deletions

View File

@ -132,6 +132,10 @@ public class RCTCamera {
return result;
}
public int getOrientation() {
return _orientation;
}
public void setOrientation(int orientation) {
if (_orientation == orientation) {
return;

View File

@ -13,6 +13,7 @@ import android.os.Environment;
import android.provider.MediaStore;
import android.util.Base64;
import android.util.Log;
import android.view.Surface;
import com.facebook.react.bridge.*;
import javax.annotation.Nullable;
@ -36,11 +37,11 @@ public class RCTCameraModule extends ReactContextBaseJavaModule {
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_CAPTURE_TARGET_TEMP = 3;
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_ORIENTATION_AUTO = Integer.MAX_VALUE;
public static final int RCT_CAMERA_ORIENTATION_PORTRAIT = Surface.ROTATION_0;
public static final int RCT_CAMERA_ORIENTATION_PORTRAIT_UPSIDE_DOWN = Surface.ROTATION_180;
public static final int RCT_CAMERA_ORIENTATION_LANDSCAPE_LEFT = Surface.ROTATION_90;
public static final int RCT_CAMERA_ORIENTATION_LANDSCAPE_RIGHT = Surface.ROTATION_270;
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;
@ -176,16 +177,21 @@ public class RCTCameraModule extends ReactContextBaseJavaModule {
@ReactMethod
public void capture(final ReadableMap options, final Promise promise) {
_sensorOrientationChecker.onResume();
_sensorOrientationChecker.registerOrientationListener(new RCTSensorOrientationListener() {
@Override
public void orientationEvent() {
int deviceOrientation = _sensorOrientationChecker.getOrientation();
_sensorOrientationChecker.unregisterOrientationListener();
_sensorOrientationChecker.onPause();
captureWithOrientation(options, promise, deviceOrientation);
}
});
int orientation = options.hasKey("orientation") ? options.getInt("orientation") : RCTCamera.getInstance().getOrientation();
if (orientation == RCT_CAMERA_ORIENTATION_AUTO) {
_sensorOrientationChecker.onResume();
_sensorOrientationChecker.registerOrientationListener(new RCTSensorOrientationListener() {
@Override
public void orientationEvent() {
int deviceOrientation = _sensorOrientationChecker.getOrientation();
_sensorOrientationChecker.unregisterOrientationListener();
_sensorOrientationChecker.onPause();
captureWithOrientation(options, promise, deviceOrientation);
}
});
} else {
captureWithOrientation(options, promise, orientation);
}
}
public void captureWithOrientation(final ReadableMap options, final Promise promise, int deviceOrientation) {
@ -195,8 +201,16 @@ public class RCTCameraModule extends ReactContextBaseJavaModule {
return;
}
if (options.hasKey("playSoundOnCapture") && options.getBoolean("playSoundOnCapture")) {
MediaActionSound sound = new MediaActionSound();
sound.play(MediaActionSound.SHUTTER_CLICK);
}
if (options.hasKey("quality")) {
RCTCamera.getInstance().setCaptureQuality(options.getInt("type"), options.getString("quality"));
}
RCTCamera.getInstance().adjustCameraRotationToDeviceOrientation(options.getInt("type"), deviceOrientation);
RCTCamera.getInstance().setCaptureQuality(options.getInt("type"), options.getString("quality"));
camera.takePicture(null, null, new Camera.PictureCallback() {
@Override
public void onPictureTaken(byte[] data, Camera camera) {

View File

@ -9,6 +9,7 @@ import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.view.Surface;
import com.facebook.react.bridge.ReactApplicationContext;
@ -51,13 +52,13 @@ public class RCTSensorOrientationChecker {
float y = event.values[1];
if (x<5 && x>-5 && y > 5)
mOrientation = 0;
mOrientation = Surface.ROTATION_0; // portrait
else if (x<-5 && y<5 && y>-5)
mOrientation = 3;
mOrientation = Surface.ROTATION_270; // right
else if (x<5 && x>-5 && y<-5)
mOrientation = 2;
mOrientation = Surface.ROTATION_180; // upside down
else if (x>5 && y<5 && y>-5)
mOrientation = 1;
mOrientation = Surface.ROTATION_90; // left
if (mListener != null) {
mListener.orientationEvent();

View File

@ -29,13 +29,15 @@
- (void)setOrientation:(NSInteger)orientation
{
[self.manager changeOrientation:orientation];
if (orientation == RCTCameraOrientationAuto) {
[self.manager changeOrientation:[UIApplication sharedApplication].statusBarOrientation];
[self changePreviewOrientation:[UIApplication sharedApplication].statusBarOrientation];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(orientationChanged:) name:UIDeviceOrientationDidChangeNotification object:nil];
}
else {
[[NSNotificationCenter defaultCenter]removeObserver:self name:UIDeviceOrientationDidChangeNotification object:nil];
[self.manager changeOrientation:orientation];
[self changePreviewOrientation:orientation];
}
}
@ -109,7 +111,7 @@
- (void)orientationChanged:(NSNotification *)notification{
UIInterfaceOrientation orientation = [[UIApplication sharedApplication] statusBarOrientation];
[self.manager changeOrientation:orientation];
[self changePreviewOrientation:orientation];
}
@ -177,5 +179,11 @@
}
}
- (void)changePreviewOrientation:(NSInteger)orientation
{
if (self.manager.previewLayer.connection.isVideoOrientationSupported) {
self.manager.previewLayer.connection.videoOrientation = orientation;
}
}
@end

View File

@ -303,9 +303,6 @@ RCT_EXPORT_METHOD(checkAudioAuthorizationStatus:(RCTPromiseResolveBlock)resolve
RCT_EXPORT_METHOD(changeOrientation:(NSInteger)orientation) {
[self setOrientation:orientation];
if (self.previewLayer.connection.isVideoOrientationSupported) {
self.previewLayer.connection.videoOrientation = orientation;
}
}
RCT_EXPORT_METHOD(capture:(NSDictionary *)options
@ -408,7 +405,6 @@ RCT_EXPORT_METHOD(hasFlash:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRej
});
}]];
[self.previewLayer.connection setVideoOrientation:self.orientation];
[self.session startRunning];
});
}
@ -484,8 +480,20 @@ RCT_EXPORT_METHOD(hasFlash:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRej
});
}
- (void)captureStill:(NSInteger)target options:(NSDictionary *)options resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject
{
AVCaptureVideoOrientation orientation = options[@"orientation"] != nil ? [options[@"orientation"] integerValue] : self.orientation;
if (orientation == RCTCameraOrientationAuto) {
[self.sensorOrientationChecker getDeviceOrientationWithBlock:^(UIInterfaceOrientation orientation) {
[self captureStill:target options:options orientation:[self.sensorOrientationChecker convertToAVCaptureVideoOrientation: orientation] resolve:resolve reject:reject];
}];
} else {
[self captureStill:target options:options orientation:orientation resolve:resolve reject:reject];
}
}
- (void)captureStill:(NSInteger)target options:(NSDictionary *)options resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject {
- (void)captureStill:(NSInteger)target options:(NSDictionary *)options orientation:(AVCaptureVideoOrientation)orientation resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject
{
dispatch_async(self.sessionQueue, ^{
#if TARGET_IPHONE_SIMULATOR
CGSize size = CGSizeMake(720, 1280);
@ -513,8 +521,7 @@ RCT_EXPORT_METHOD(hasFlash:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRej
NSData *imageData = UIImageJPEGRepresentation(image, 1.0);
[self saveImage:imageData target:target metadata:nil resolve:resolve reject:reject];
#else
[self.sensorOrientationChecker getDeviceOrientationWithBlock:^(UIInterfaceOrientation orientation) {
[[self.stillImageOutput connectionWithMediaType:AVMediaTypeVideo] setVideoOrientation:[self.sensorOrientationChecker convertToAVCaptureVideoOrientation: orientation]];
[[self.stillImageOutput connectionWithMediaType:AVMediaTypeVideo] setVideoOrientation:orientation];
[self.stillImageOutput captureStillImageAsynchronouslyFromConnection:[self.stillImageOutput connectionWithMediaType:AVMediaTypeVideo] completionHandler:^(CMSampleBufferRef imageDataSampleBuffer, NSError *error) {
@ -577,7 +584,6 @@ RCT_EXPORT_METHOD(hasFlash:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRej
reject(RCTErrorUnspecified, nil, RCTErrorWithMessage(error.description));
}
}];
}];
#endif
});
}
@ -659,8 +665,20 @@ RCT_EXPORT_METHOD(hasFlash:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRej
return rotatedImage;
}
-(void)captureVideo:(NSInteger)target options:(NSDictionary *)options resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject {
-(void)captureVideo:(NSInteger)target options:(NSDictionary *)options resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject
{
AVCaptureVideoOrientation orientation = options[@"orientation"] != nil ? [options[@"orientation"] integerValue] : self.orientation;
if (orientation == RCTCameraOrientationAuto) {
[self.sensorOrientationChecker getDeviceOrientationWithBlock:^(UIInterfaceOrientation orientation) {
[self captureVideo:target options:options orientation:[self.sensorOrientationChecker convertToAVCaptureVideoOrientation: orientation] resolve:resolve reject:reject];
}];
} else {
[self captureVideo:target options:options orientation:orientation resolve:resolve reject:reject];
}
}
-(void)captureVideo:(NSInteger)target options:(NSDictionary *)options orientation:(AVCaptureVideoOrientation)orientation resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject
{
if (self.movieFileOutput.recording) {
reject(RCTErrorUnspecified, nil, RCTErrorWithMessage(@"Already recording"));
return;
@ -678,18 +696,18 @@ RCT_EXPORT_METHOD(hasFlash:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRej
}
dispatch_async(self.sessionQueue, ^{
[[self.movieFileOutput connectionWithMediaType:AVMediaTypeVideo] setVideoOrientation:self.previewLayer.connection.videoOrientation];
[[self.movieFileOutput connectionWithMediaType:AVMediaTypeVideo] setVideoOrientation:orientation];
//Create temporary URL to record to
NSString *outputPath = [[NSString alloc] initWithFormat:@"%@%@", NSTemporaryDirectory(), @"output.mov"];
NSURL *outputURL = [[NSURL alloc] initFileURLWithPath:outputPath];
NSFileManager *fileManager = [NSFileManager defaultManager];
if ([fileManager fileExistsAtPath:outputPath]) {
NSError *error;
if ([fileManager removeItemAtPath:outputPath error:&error] == NO) {
reject(RCTErrorUnspecified, nil, RCTErrorWithMessage(error.description));
return;
}
NSError *error;
if ([fileManager removeItemAtPath:outputPath error:&error] == NO) {
reject(RCTErrorUnspecified, nil, RCTErrorWithMessage(error.description));
return;
}
}
//Start recording