#import "RNCamera.h" #import "RNCameraManager.h" #import "RNFileSystem.h" #import "RNImageUtils.h" #import #import #import #import #import #import @implementation RNCameraManager RCT_EXPORT_MODULE(RNCameraManager); RCT_EXPORT_VIEW_PROPERTY(onCameraReady, RCTDirectEventBlock); RCT_EXPORT_VIEW_PROPERTY(onMountError, RCTDirectEventBlock); RCT_EXPORT_VIEW_PROPERTY(onBarCodeRead, RCTDirectEventBlock); RCT_EXPORT_VIEW_PROPERTY(onFacesDetected, RCTDirectEventBlock); + (BOOL)requiresMainQueueSetup { return YES; } - (UIView *)view { return [[RNCamera alloc] initWithBridge:self.bridge]; } - (NSDictionary *)constantsToExport { return @{ @"Type" : @{@"front" : @(RNCameraTypeFront), @"back" : @(RNCameraTypeBack)}, @"FlashMode" : @{ @"off" : @(RNCameraFlashModeOff), @"on" : @(RNCameraFlashModeOn), @"auto" : @(RNCameraFlashModeAuto), @"torch" : @(RNCameraFlashModeTorch) }, @"AutoFocus" : @{@"on" : @(RNCameraAutoFocusOn), @"off" : @(RNCameraAutoFocusOff)}, @"WhiteBalance" : @{ @"auto" : @(RNCameraWhiteBalanceAuto), @"sunny" : @(RNCameraWhiteBalanceSunny), @"cloudy" : @(RNCameraWhiteBalanceCloudy), @"shadow" : @(RNCameraWhiteBalanceShadow), @"incandescent" : @(RNCameraWhiteBalanceIncandescent), @"fluorescent" : @(RNCameraWhiteBalanceFluorescent) }, @"VideoQuality": @{ @"2160p": @(RNCameraVideo2160p), @"1080p": @(RNCameraVideo1080p), @"720p": @(RNCameraVideo720p), @"480p": @(RNCameraVideo4x3), @"4:3": @(RNCameraVideo4x3), }, @"VideoCodec": [[self class] validCodecTypes], @"BarCodeType" : [[self class] validBarCodeTypes], @"FaceDetection" : [[self class] faceDetectorConstants] }; } - (NSArray *)supportedEvents { return @[@"onCameraReady", @"onMountError", @"onBarCodeRead", @"onFacesDetected"]; } + (NSDictionary *)validCodecTypes { if (@available(iOS 11, *)) { return @{ @"H264": AVVideoCodecTypeH264, @"HVEC": AVVideoCodecTypeHEVC, @"JPEG": AVVideoCodecTypeJPEG, @"AppleProRes422": AVVideoCodecTypeAppleProRes422, @"AppleProRes4444": AVVideoCodecTypeAppleProRes4444 }; } else { return @{ @"H264": AVVideoCodecH264, @"JPEG": AVVideoCodecJPEG }; } } + (NSDictionary *)validBarCodeTypes { return @{ @"upc_e" : AVMetadataObjectTypeUPCECode, @"code39" : AVMetadataObjectTypeCode39Code, @"code39mod43" : AVMetadataObjectTypeCode39Mod43Code, @"ean13" : AVMetadataObjectTypeEAN13Code, @"ean8" : AVMetadataObjectTypeEAN8Code, @"code93" : AVMetadataObjectTypeCode93Code, @"code128" : AVMetadataObjectTypeCode128Code, @"pdf417" : AVMetadataObjectTypePDF417Code, @"qr" : AVMetadataObjectTypeQRCode, @"aztec" : AVMetadataObjectTypeAztecCode, @"interleaved2of5" : AVMetadataObjectTypeInterleaved2of5Code, @"itf14" : AVMetadataObjectTypeITF14Code, @"datamatrix" : AVMetadataObjectTypeDataMatrixCode }; } + (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) { if (view.presetCamera != [RCTConvert NSInteger:json]) { [view setPresetCamera:[RCTConvert NSInteger:json]]; [view updateType]; } } RCT_CUSTOM_VIEW_PROPERTY(flashMode, NSInteger, RNCamera) { [view setFlashMode:[RCTConvert NSInteger:json]]; [view updateFlashMode]; } RCT_CUSTOM_VIEW_PROPERTY(autoFocus, NSInteger, RNCamera) { [view setAutoFocus:[RCTConvert NSInteger:json]]; [view updateFocusMode]; } RCT_CUSTOM_VIEW_PROPERTY(focusDepth, NSNumber, RNCamera) { [view setFocusDepth:[RCTConvert float:json]]; [view updateFocusDepth]; } RCT_CUSTOM_VIEW_PROPERTY(zoom, NSNumber, RNCamera) { [view setZoom:[RCTConvert CGFloat:json]]; [view updateZoom]; } RCT_CUSTOM_VIEW_PROPERTY(whiteBalance, NSInteger, RNCamera) { [view setWhiteBalance: [RCTConvert NSInteger:json]]; [view updateWhiteBalance]; } RCT_CUSTOM_VIEW_PROPERTY(faceDetectorEnabled, BOOL, RNCamera) { [view updateFaceDetecting:json]; } RCT_CUSTOM_VIEW_PROPERTY(faceDetectionMode, NSInteger, RNCamera) { [view updateFaceDetectionMode:json]; } RCT_CUSTOM_VIEW_PROPERTY(faceDetectionLandmarks, NSString, RNCamera) { [view updateFaceDetectionLandmarks:json]; } RCT_CUSTOM_VIEW_PROPERTY(faceDetectionClassifications, NSString, RNCamera) { [view updateFaceDetectionClassifications:json]; } RCT_CUSTOM_VIEW_PROPERTY(barCodeScannerEnabled, BOOL, RNCamera) { view.barCodeReading = [RCTConvert BOOL:json]; [view setupOrDisableBarcodeScanner]; } RCT_CUSTOM_VIEW_PROPERTY(barCodeTypes, NSArray, RNCamera) { [view setBarCodeTypes:[RCTConvert NSArray:json]]; } RCT_REMAP_METHOD(takePicture, options:(NSDictionary *)options reactTag:(nonnull NSNumber *)reactTag 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 *viewRegistry) { RNCamera *view = viewRegistry[reactTag]; if (![view isKindOfClass:[RNCamera class]]) { RCTLogError(@"Invalid view returned from registry, expecting RNCamera, got: %@", view); } else { [view takePicture:options resolve:resolve reject:reject]; } }]; #endif } RCT_REMAP_METHOD(record, withOptions:(NSDictionary *)options reactTag:(nonnull NSNumber *)reactTag resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) { #if TARGET_IPHONE_SIMULATOR reject(@"E_RECORDING_FAILED", @"Video recording is not supported on a simulator.", nil); return; #endif [self.bridge.uiManager addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary *viewRegistry) { RNCamera *view = viewRegistry[reactTag]; if (![view isKindOfClass:[RNCamera class]]) { RCTLogError(@"Invalid view returned from registry, expecting RNCamera, got: %@", view); } else { [view record:options resolve:resolve reject:reject]; } }]; } RCT_REMAP_METHOD(stopRecording, reactTag:(nonnull NSNumber *)reactTag) { [self.bridge.uiManager addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary *viewRegistry) { RNCamera *view = viewRegistry[reactTag]; if (![view isKindOfClass:[RNCamera class]]) { RCTLogError(@"Invalid view returned from registry, expecting RNCamera, got: %@", view); } else { [view stopRecording]; } }]; } RCT_EXPORT_METHOD(checkDeviceAuthorizationStatus:(RCTPromiseResolveBlock)resolve reject:(__unused RCTPromiseRejectBlock)reject) { __block NSString *mediaType = AVMediaTypeVideo; [AVCaptureDevice requestAccessForMediaType:mediaType completionHandler:^(BOOL granted) { if (!granted) { resolve(@(granted)); } else { mediaType = AVMediaTypeAudio; [AVCaptureDevice requestAccessForMediaType:mediaType completionHandler:^(BOOL granted) { resolve(@(granted)); }]; } }]; } RCT_EXPORT_METHOD(checkVideoAuthorizationStatus:(RCTPromiseResolveBlock)resolve reject:(__unused RCTPromiseRejectBlock)reject) { __block NSString *mediaType = AVMediaTypeVideo; [AVCaptureDevice requestAccessForMediaType:mediaType completionHandler:^(BOOL granted) { resolve(@(granted)); }]; } @end