From 5a408b70b7e2e359fb200ec09ecbb4ead4a6a2d4 Mon Sep 17 00:00:00 2001 From: Bruno Lemos Date: Mon, 12 Mar 2018 16:41:33 -0300 Subject: [PATCH] [iOS] Make GoogleMobileVision framework optional --- README.md | 44 ++++++++---------------- ios/FaceDetector/RNFaceDetectorManager.h | 5 +++ ios/FaceDetector/RNFaceDetectorManager.m | 2 ++ ios/FaceDetector/RNFaceDetectorModule.h | 2 ++ ios/FaceDetector/RNFaceDetectorModule.m | 2 ++ ios/FaceDetector/RNFaceDetectorUtils.h | 2 ++ ios/FaceDetector/RNFaceDetectorUtils.m | 2 ++ ios/FaceDetector/RNFaceEncoder.h | 2 ++ ios/FaceDetector/RNFaceEncoder.m | 2 ++ ios/RN/RNCamera.m | 15 +++++++- ios/RN/RNCameraManager.m | 4 +++ src/RNCamera.js | 12 +++---- 12 files changed, 57 insertions(+), 37 deletions(-) diff --git a/README.md b/README.md index 147a3cc..a150e46 100644 --- a/README.md +++ b/README.md @@ -84,35 +84,15 @@ pod 'react-native-camera', path: '../node_modules/react-native-camera' 1. `npm install react-native-camera --save` 2. In XCode, in the project navigator, right click `Libraries` ➜ `Add Files to [your project's name]` 3. Go to `node_modules` ➜ `react-native-camera` and add `RNCamera.xcodeproj` -4. In XCode, in the project navigator, select your project. Add `libRNCamera.a` to your project's `Build Phases` ➜ `Link Binary With Libraries` -5. Click `RNCamera.xcodeproj` in the project navigator and go the `Build Settings` tab. Make sure 'All' is toggled on (instead of 'Basic'). In the `Search Paths` section, look for `Header Search Paths` and make sure it contains both `$(SRCROOT)/../../react-native/React` and `$(SRCROOT)/../../../React` - mark both as `recursive`. +4. Expand the `RNCamera.xcodeproj` ➜ `Products` folder +5. In XCode, in the project navigator, select your project. Add `libRNCamera.a` to your project's `Build Phases` ➜ `Link Binary With Libraries` +6. Click `RNCamera.xcodeproj` in the project navigator and go the `Build Settings` tab. Make sure 'All' is toggled on (instead of 'Basic'). In the `Search Paths` section, look for `Header Search Paths` and make sure it contains both `$(SRCROOT)/../../react-native/React` and `$(SRCROOT)/../../../React` - mark both as `recursive`. -### Face Detection Steps +##### Face Detection Steps Face Detecion is optional on iOS. If you want it, you are going to need to install Google Mobile Vision frameworks in your project, as mentioned in the next section. -##### No Face Detection steps - -If you do not need it and do not want to install the GMV frameworks, open your app xcode project, on the Project Navigator, expand the RNCamera project, right click on the FaceDetector folder and delete it (move to trash, if you want). If you keep that folder and do not follow the GMV installation setps, your project will not compile. - -If you want to make this automatic, you can add a postinstall script to your app `package.json`. Inside the `postinstall_project` there is a xcode project ready with the folder removed (we opened xcode, removed the folder from the project and copied the resulting project file). The post install script is: -``` -#!/bin/bash -echo "Creating project without FaceDetector" -if [ -e node_modules/react-native-camera/ios/FaceDetector ] ; then - rm -rf node_modules/react-native-camera/ios/FaceDetector -fi -cp node_modules/react-native-camera/postinstall_project/projectWithoutFaceDetection.pbxproj node_modules/react-native-camera/ios/RNCamera.xcodeproj/project.pbxproj -``` - -And add something like this to the `scripts` section in your `package.json`: - -*Note:* The face detection code is excluded by default for the **CocoaPods** installation. -``` -"postinstall": "./scripts/post.sh", -``` - -##### Installing GMV frameworks +###### Installing GMV frameworks GMV (Google Mobile Vision) is used for Face detection by the iOS RNCamera. You have to link the google frameworks to your project to successfully compile the RNCamera project. 1. If using **CocoaPods** modify the dependency towards `react-native-camera` in your @@ -150,7 +130,7 @@ Google Symbol Utilities: https://www.gstatic.com/cpdc/dbffca986f6337f8-GoogleSym #### Android 1. `npm install react-native-camera --save` -2. Open up `android/app/src/main/java/[...]/MainApplication.java +2. Open up `android/app/src/main/java/[...]/MainApplication.java` - Add `import org.reactnative.camera.RNCameraPackage;` to the imports at the top of the file - Add `new RNCameraPackage()` to the list returned by the `getPackages()` method. Add a comma to the previous item if there's already something there. @@ -164,13 +144,17 @@ Google Symbol Utilities: https://www.gstatic.com/cpdc/dbffca986f6337f8-GoogleSym 4. Insert the following lines inside the dependencies block in `android/app/build.gradle`: ```gradle - compile (project(':react-native-camera')) { + compile (project(':react-native-camera')) { exclude group: "com.google.android.gms" - } - compile ("com.google.android.gms:play-services-vision:10.2.0") { - force = true; + compile 'com.android.support:exifinterface:25.+' + compile ('com.google.android.gms:play-services-vision:10.2.0') { + force = true + } } ``` + + > You may need to use different versions, e.g. `27.+` instead of `25.+` and `11.8.0` instead of `10.2.0`. + 5. Declare the permissions in your Android Manifest (required for `video recording` feature) ```java diff --git a/ios/FaceDetector/RNFaceDetectorManager.h b/ios/FaceDetector/RNFaceDetectorManager.h index 74b2b4f..ccefad2 100644 --- a/ios/FaceDetector/RNFaceDetectorManager.h +++ b/ios/FaceDetector/RNFaceDetectorManager.h @@ -8,13 +8,17 @@ #import #import +#if __has_include() #import #import +#endif + @protocol RNFaceDetectorDelegate - (void)onFacesDetected:(NSArray *)faces; @end +#if __has_include() @interface RNFaceDetectorManager : NSObject - (NSDictionary *)constantsToExport; @@ -31,3 +35,4 @@ - (void)stopFaceDetection; @end +#endif diff --git a/ios/FaceDetector/RNFaceDetectorManager.m b/ios/FaceDetector/RNFaceDetectorManager.m index b67e53d..5c9bf61 100644 --- a/ios/FaceDetector/RNFaceDetectorManager.m +++ b/ios/FaceDetector/RNFaceDetectorManager.m @@ -5,6 +5,7 @@ // Created by Joao Guilherme Daros Fidelis on 21/01/18. // +#if __has_include() #import #import "RNCamera.h" #import "RNFaceEncoder.h" @@ -272,3 +273,4 @@ static NSDictionary *defaultFaceDetectorOptions = nil; } @end +#endif diff --git a/ios/FaceDetector/RNFaceDetectorModule.h b/ios/FaceDetector/RNFaceDetectorModule.h index ccab2d7..b72589c 100644 --- a/ios/FaceDetector/RNFaceDetectorModule.h +++ b/ios/FaceDetector/RNFaceDetectorModule.h @@ -6,7 +6,9 @@ // #import +#if __has_include() #import +#endif @interface RNFaceDetectorModule : NSObject @end diff --git a/ios/FaceDetector/RNFaceDetectorModule.m b/ios/FaceDetector/RNFaceDetectorModule.m index 452004a..0ebeadf 100644 --- a/ios/FaceDetector/RNFaceDetectorModule.m +++ b/ios/FaceDetector/RNFaceDetectorModule.m @@ -5,6 +5,7 @@ // Created by Joao Guilherme Daros Fidelis on 21/01/18. // +#if __has_include() #import "RNFaceDetectorModule.h" #import "RNFaceEncoder.h" #import "RNFileSystem.h" @@ -193,3 +194,4 @@ RCT_EXPORT_METHOD(detectFaces:(nonnull NSDictionary *)options } @end +#endif diff --git a/ios/FaceDetector/RNFaceDetectorUtils.h b/ios/FaceDetector/RNFaceDetectorUtils.h index f39a071..6fca28e 100644 --- a/ios/FaceDetector/RNFaceDetectorUtils.h +++ b/ios/FaceDetector/RNFaceDetectorUtils.h @@ -5,6 +5,7 @@ // Created by Joao Guilherme Daros Fidelis on 21/01/18. // +#if __has_include() #import #import #import @@ -33,3 +34,4 @@ typedef NS_ENUM(NSInteger, RNFaceDetectionClassifications) { + (CGAffineTransform)transformFromDeviceOutput:(GMVDataOutput *)dataOutput toInterfaceVideoOrientation:(AVCaptureVideoOrientation)interfaceVideoOrientation; @end +#endif diff --git a/ios/FaceDetector/RNFaceDetectorUtils.m b/ios/FaceDetector/RNFaceDetectorUtils.m index 8affbec..f1cbedc 100644 --- a/ios/FaceDetector/RNFaceDetectorUtils.m +++ b/ios/FaceDetector/RNFaceDetectorUtils.m @@ -5,6 +5,7 @@ // Created by Joao Guilherme Daros Fidelis on 21/01/18. // +#if __has_include() #import "RNCameraUtils.h" #import "RNFaceDetectorUtils.h" #import "RNFaceDetectorPointTransformCalculator.h" @@ -75,3 +76,4 @@ NSString *const RNGMVDataOutputHeightKey = @"Height"; } @end +#endif diff --git a/ios/FaceDetector/RNFaceEncoder.h b/ios/FaceDetector/RNFaceEncoder.h index ffec26b..f809574 100644 --- a/ios/FaceDetector/RNFaceEncoder.h +++ b/ios/FaceDetector/RNFaceEncoder.h @@ -6,6 +6,7 @@ // #import +#if __has_include() #import @interface RNFaceEncoder : NSObject @@ -15,3 +16,4 @@ - (NSDictionary *)encode:(GMVFaceFeature *)face; @end +#endif diff --git a/ios/FaceDetector/RNFaceEncoder.m b/ios/FaceDetector/RNFaceEncoder.m index cdd00f5..0152403 100644 --- a/ios/FaceDetector/RNFaceEncoder.m +++ b/ios/FaceDetector/RNFaceEncoder.m @@ -7,6 +7,7 @@ #import "RNFaceEncoder.h" +#if __has_include() #define cDefaultFloatComparisonEpsilon 0.0001 #define cModEqualFloatsWithEpsilon(dividend, divisor, modulo, epsilon) \ fabs( fmod(dividend, divisor) - modulo ) < epsilon @@ -117,3 +118,4 @@ cModEqualFloatsWithEpsilon(dividend, divisor, modulo, cDefaultFloatComparisonEps } @end +#endif diff --git a/ios/RN/RNCamera.m b/ios/RN/RNCamera.m index e81d252..5eede8b 100644 --- a/ios/RN/RNCamera.m +++ b/ios/RN/RNCamera.m @@ -284,6 +284,7 @@ static NSDictionary *defaultFaceDetectorOptions = nil; [device unlockForConfiguration]; } +#if __has_include() - (void)updateFaceDetecting:(id)faceDetecting { [_faceDetectorManager setIsEnabled:faceDetecting]; @@ -303,6 +304,7 @@ static NSDictionary *defaultFaceDetectorOptions = nil; { [_faceDetectorManager setClassificationsDetected:requestedClassifications]; } +#endif - (void)takePicture:(NSDictionary *)options resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject { @@ -382,7 +384,9 @@ static NSDictionary *defaultFaceDetectorOptions = nil; // At the time of writing AVCaptureMovieFileOutput and AVCaptureVideoDataOutput (> GMVDataOutput) // cannot coexist on the same AVSession (see: https://stackoverflow.com/a/4986032/1123156). // We stop face detection here and restart it in when AVCaptureMovieFileOutput finishes recording. +#if __has_include() [_faceDetectorManager stopFaceDetection]; +#endif [self setupMovieFileCapture]; } @@ -443,7 +447,9 @@ static NSDictionary *defaultFaceDetectorOptions = nil; self.stillImageOutput = stillImageOutput; } +#if __has_include() [_faceDetectorManager maybeStartFaceDetectionOnSession:_session withPreviewLayer:_previewLayer]; +#endif [self setupOrDisableBarcodeScanner]; __weak RNCamera *weakSelf = self; @@ -469,7 +475,9 @@ static NSDictionary *defaultFaceDetectorOptions = nil; return; #endif dispatch_async(self.sessionQueue, ^{ +#if __has_include() [_faceDetectorManager stopFaceDetection]; +#endif [self.previewLayer removeFromSuperlayer]; [self.session commitConfiguration]; [self.session stopRunning]; @@ -720,11 +728,14 @@ static NSDictionary *defaultFaceDetectorOptions = nil; } self.videoRecordedResolve = nil; self.videoRecordedReject = nil; - + [self cleanupMovieFileCapture]; + +#if __has_include() // If face detection has been running prior to recording to file // we reenable it here (see comment in -record). [_faceDetectorManager maybeStartFaceDetectionOnSession:_session withPreviewLayer:_previewLayer]; +#endif if (self.session.sessionPreset != AVCaptureSessionPresetHigh) { [self updateSessionPreset:AVCaptureSessionPresetHigh]; @@ -738,11 +749,13 @@ static NSDictionary *defaultFaceDetectorOptions = nil; Class faceDetectorManagerClass = NSClassFromString(@"RNFaceDetectorManager"); Class faceDetectorManagerStubClass = NSClassFromString(@"RNFaceDetectorManagerStub"); +#if __has_include() if (faceDetectorManagerClass) { return [[faceDetectorManagerClass alloc] initWithSessionQueue:_sessionQueue delegate:self]; } else if (faceDetectorManagerStubClass) { return [[faceDetectorManagerStubClass alloc] init]; } +#endif return nil; } diff --git a/ios/RN/RNCameraManager.m b/ios/RN/RNCameraManager.m index 3ff8f41..ccb5de7 100644 --- a/ios/RN/RNCameraManager.m +++ b/ios/RN/RNCameraManager.m @@ -86,11 +86,15 @@ RCT_EXPORT_VIEW_PROPERTY(onFacesDetected, RCTDirectEventBlock); + (NSDictionary *)faceDetectorConstants { +#if __has_include() #if __has_include("RNFaceDetectorManager.h") return [RNFaceDetectorManager constants]; #else return [RNFaceDetectorManagerStub constants]; #endif +#else + return [NSDictionary new]; +#endif } RCT_CUSTOM_VIEW_PROPERTY(type, NSInteger, RNCamera) diff --git a/src/RNCamera.js b/src/RNCamera.js index 829e47f..dc59898 100644 --- a/src/RNCamera.js +++ b/src/RNCamera.js @@ -106,9 +106,9 @@ export default class Camera extends React.Component { flashMode: CameraManager.FlashMode, autoFocus: CameraManager.AutoFocus, whiteBalance: CameraManager.WhiteBalance, - faceDetectionMode: CameraManager.FaceDetection.Mode, - faceDetectionLandmarks: CameraManager.FaceDetection.Landmarks, - faceDetectionClassifications: CameraManager.FaceDetection.Classifications, + faceDetectionMode: (CameraManager.FaceDetection || {}).Mode, + faceDetectionLandmarks: (CameraManager.FaceDetection || {}).Landmarks, + faceDetectionClassifications: (CameraManager.FaceDetection || {}).Classifications, }; static propTypes = { @@ -144,10 +144,10 @@ export default class Camera extends React.Component { autoFocus: CameraManager.AutoFocus.on, flashMode: CameraManager.FlashMode.off, whiteBalance: CameraManager.WhiteBalance.auto, - faceDetectionMode: CameraManager.FaceDetection.fast, + faceDetectionMode: (CameraManager.FaceDetection || {}).fast, barCodeTypes: Object.values(CameraManager.BarCodeType), - faceDetectionLandmarks: CameraManager.FaceDetection.Landmarks.none, - faceDetectionClassifications: CameraManager.FaceDetection.Classifications.none, + faceDetectionLandmarks: ((CameraManager.FaceDetection || {}).Landmarks || {}).none, + faceDetectionClassifications: ((CameraManager.FaceDetection || {}).Classifications || {}).none, permissionDialogTitle: '', permissionDialogMessage: '', notAuthorizedView: (