diff --git a/README.md b/README.md index ad407e1..57c0719 100644 --- a/README.md +++ b/README.md @@ -67,6 +67,9 @@ import { CameraScreen } from 'react-native-camera-kit'; }} cameraFlipImage={require('path/to/image')} captureButtonImage={require('path/to/image')} + torchOnImage={require('path/to/image')} + torchOffImage={require('path/to/image')} + hideControls={false} // (default false) optional, hides camera controls showCapturedImageCount={false} // (default false) optional, show count for photos taken during that capture session /> ``` @@ -80,14 +83,10 @@ Additionally, the camera screen can be used for barcode scanning ... // Barcode props scanBarcode={true} - laserColor={'blue'} - frameColor={'yellow'} onReadCode={(event) => Alert.alert('Qr code found')} //optional - hideControls={false} //(default false) optional, hide buttons and additional controls on top and bottom of screen showFrame={true} //(default false) optional, show frame with transparent layer (qr code or barcode will be read on this area ONLY), start animation for scanner,that stoped when find any code. Frame always at center of the screen - offsetForScannerFrame={10} //(default 30) optional, offset from left and right side of the screen - heightForScannerFrame={300} //(default 200) optional, change height of the scanner frame - colorForScannerFrame={'red'} //(default white) optional, change colot of the scanner frame + laserColor='red' // (default red) optional, color of laser in scanner frame + frameColor='white' // (default white) optional, color of border of scanner frame /> ``` @@ -114,6 +113,7 @@ import { Camera } from 'react-native-camera-kit'; | `flashMode` | `'on'`/`'off'`/`'auto'` | Camera flash mode. Default: `auto` | | `focusMode` | `'on'`/`'off'` | Camera focus mode. Default: `on` | | `zoomMode` | `'on'`/`'off'` | Enable pinch to zoom camera. Default: `on` | +| `torchMode` | `'on'`/`'off'` | Toggle flash light when camera is active. Default: `off` | | `ratioOverlay` | `['int':'int', ...]` | Show a guiding overlay in the camera preview for the selected ratio. Does not crop image as of v9.0. Example: `['16:9', '1:1', '3:4']` | | `ratioOverlayColor` | Color | Any color with alpha. Default: `'#ffffff77'` | | `resetFocusTimeout` | Number | iOS only. Dismiss tap to focus after this many milliseconds. Default `0` (disabled). Example: `5000` is 5 seconds. | diff --git a/android/src/main/java/com/rncamerakit/CKCamera.kt b/android/src/main/java/com/rncamerakit/CKCamera.kt index 394a62b..57c8e34 100644 --- a/android/src/main/java/com/rncamerakit/CKCamera.kt +++ b/android/src/main/java/com/rncamerakit/CKCamera.kt @@ -192,7 +192,7 @@ class CKCamera(context: ThemedReactContext) : FrameLayout(context), LifecycleObs orientationListener!!.enable() // Contain camera feed image within component bounds, centered - viewFinder.scaleType = PreviewView.ScaleType.FIT_CENTER + viewFinder.scaleType = PreviewView.ScaleType.FILL_CENTER // Tap to focus viewFinder.setOnTouchListener { _, event -> @@ -375,7 +375,6 @@ class CKCamera(context: ThemedReactContext) : FrameLayout(context), LifecycleObs val imageCapture = imageCapture ?: return val camera = camera ?: return when (mode) { - "torch" -> camera.cameraControl.enableTorch(true) "on" -> { camera.cameraControl.enableTorch(false) imageCapture.flashMode = ImageCapture.FLASH_MODE_ON @@ -391,6 +390,22 @@ class CKCamera(context: ThemedReactContext) : FrameLayout(context), LifecycleObs } } + fun setTorchMode(mode: String?) { + val imageCapture = imageCapture ?: return + val camera = camera ?: return + when (mode) { + "on" -> { + camera.cameraControl.enableTorch(true) + } + "off" -> { + camera.cameraControl.enableTorch(false) + } + else -> { // 'auto' and any wrong values + camera.cameraControl.enableTorch(false) + } + } + } + fun setAutoFocus(mode: String = "on") { autoFocus = mode when(mode) { diff --git a/android/src/main/java/com/rncamerakit/CKCameraManager.kt b/android/src/main/java/com/rncamerakit/CKCameraManager.kt index 8024188..ffcca12 100644 --- a/android/src/main/java/com/rncamerakit/CKCameraManager.kt +++ b/android/src/main/java/com/rncamerakit/CKCameraManager.kt @@ -61,6 +61,11 @@ class CKCameraManager : SimpleViewManager() { view.setFlashMode(mode) } + @ReactProp(name = "torchMode") + fun setTorchMode(view: CKCamera, mode: String?) { + view.setTorchMode(mode) + } + @ReactProp(name = "focusMode") fun setFocusMode(view: CKCamera, mode: String) { view.setAutoFocus(mode) diff --git a/example/images/torchOff@3x.png b/example/images/torchOff@3x.png new file mode 100644 index 0000000..ada83b1 Binary files /dev/null and b/example/images/torchOff@3x.png differ diff --git a/example/images/torchOn@3x.png b/example/images/torchOn@3x.png new file mode 100644 index 0000000..f66c412 Binary files /dev/null and b/example/images/torchOn@3x.png differ diff --git a/example/src/BarcodeScreenExample.tsx b/example/src/BarcodeScreenExample.tsx index 4a601a3..bd08eab 100644 --- a/example/src/BarcodeScreenExample.tsx +++ b/example/src/BarcodeScreenExample.tsx @@ -38,16 +38,12 @@ export default class BarcodeScreenExample extends Component { }} scanBarcode showFrame - laserColor={'yellow'} - frameColor={'yellow'} - surfaceColor={'black'} + laserColor="red" + frameColor="white" onReadCode={(event) => { this.setState({ example: CheckingScreen, value: event.nativeEvent.codeStringValue }); }} hideControls - // offsetForScannerFrame = {10} - // heightForScannerFrame = {300} - colorForScannerFrame={'blue'} /> ); } diff --git a/example/src/CameraExample.tsx b/example/src/CameraExample.tsx index feadb27..7ff9482 100644 --- a/example/src/CameraExample.tsx +++ b/example/src/CameraExample.tsx @@ -14,6 +14,7 @@ export default class CameraExample extends Component { flashMode="auto" // on/off/auto(default) focusMode="on" // off/on(default) zoomMode="on" // off/on(default) + torchMode="off" // on/off(default) ratioOverlay="1:1" // optional ratioOverlayColor="#00000077" // optional resetFocusTimeout={0} diff --git a/example/src/CameraScreenExample.tsx b/example/src/CameraScreenExample.tsx index 7d6f7b5..973c54c 100644 --- a/example/src/CameraScreenExample.tsx +++ b/example/src/CameraScreenExample.tsx @@ -25,6 +25,8 @@ export default class CameraScreenExample extends Component { }} cameraFlipImage={require('../images/cameraFlipIcon.png')} captureButtonImage={require('../images/cameraButton.png')} + torchOnImage={require('../images/torchOn.png')} + torchOffImage={require('../images/torchOff.png')} showCapturedImageCount /> ); diff --git a/ios/ReactNativeCameraKit/CKCamera.h b/ios/ReactNativeCameraKit/CKCamera.h index 2da0b8e..5fed471 100644 --- a/ios/ReactNativeCameraKit/CKCamera.h +++ b/ios/ReactNativeCameraKit/CKCamera.h @@ -22,18 +22,6 @@ typedef NS_ENUM(NSInteger, CKCameraType) { @end -typedef NS_ENUM(NSInteger, CKCameraTorchMode) { - CKCameraTorchModeAuto, - CKCameraTorchModeOn, - CKCameraTorchModeOff -}; - -@interface RCTConvert(CKCameraTorchMode) - -+ (CKCameraTorchMode)CKCameraTorchMode:(id)json; - -@end - typedef NS_ENUM(NSInteger, CKCameraFlashMode) { CKCameraFlashModeAuto, CKCameraFlashModeOn, @@ -46,6 +34,16 @@ typedef NS_ENUM(NSInteger, CKCameraFlashMode) { @end +typedef NS_ENUM(NSInteger, CKCameraTorchMode) { + CKCameraTorchModeOn, + CKCameraTorchModeOff +}; + +@interface RCTConvert(CKCameraTorchMode) + ++ (CKCameraTorchMode)CKCameraTorchMode:(id)json; + +@end typedef NS_ENUM(NSInteger, CKCameraFocusMode) { CKCameraFocusModeOn, diff --git a/ios/ReactNativeCameraKit/CKCamera.m b/ios/ReactNativeCameraKit/CKCamera.m index cfb9d33..8e4f2aa 100644 --- a/ios/ReactNativeCameraKit/CKCamera.m +++ b/ios/ReactNativeCameraKit/CKCamera.m @@ -33,12 +33,9 @@ RCT_ENUM_CONVERTER(CKCameraType, (@{ @implementation RCTConvert(CKCameraTorchMode) RCT_ENUM_CONVERTER(CKCameraTorchMode, (@{ - @"auto": @(AVCaptureTorchModeAuto), @"on": @(AVCaptureTorchModeOn), @"off": @(AVCaptureTorchModeOff) }), AVCaptureTorchModeAuto, integerValue) - - @end @implementation RCTConvert(CKCameraFlashMode) @@ -102,13 +99,12 @@ RCT_ENUM_CONVERTER(CKCameraZoomMode, (@{ @property (nonatomic) UIBackgroundTaskIdentifier backgroundRecordingID; // scanner options -@property (nonatomic, strong) NSDictionary *scannerOptions; @property (nonatomic) BOOL showFrame; -@property (nonatomic) UIView *greenScanner; +@property (nonatomic) UIView *scannerView; @property (nonatomic, strong) RCTDirectEventBlock onReadCode; - @property (nonatomic) CGFloat frameOffset; -@property (nonatomic) CGFloat heightFrame; +@property (nonatomic) CGFloat frameHeight; +@property (nonatomic, strong) UIColor *laserColor; @property (nonatomic, strong) UIColor *frameColor; @property (nonatomic) UIView * dataReadingFrame; @@ -208,6 +204,11 @@ RCT_ENUM_CONVERTER(CKCameraZoomMode, (@{ self.zoomMode = CKCameraZoomModeOn; self.flashMode = CKCameraFlashModeAuto; self.focusMode = CKCameraFocusModeOn; + + self.frameColor = [UIColor whiteColor]; + self.laserColor = [UIColor redColor]; + self.frameOffset = 30; + self.frameHeight = 200; } return self; @@ -288,6 +289,18 @@ RCT_ENUM_CONVERTER(CKCameraZoomMode, (@{ } } +- (void)setLaserColor:(UIColor *)color { + if (color != nil) { + _laserColor = color; + } +} + +- (void)setFrameColor:(UIColor *)color { + if (color != nil) { + _frameColor = color; + } +} + -(void)setupCaptureSession { // Setup the capture session. // In general it is not safe to mutate an AVCaptureSession or any of its inputs, outputs, or connections from multiple threads at the same time. @@ -884,29 +897,15 @@ RCT_ENUM_CONVERTER(CKCameraZoomMode, (@{ } } -- (void)setScannerOptions:(NSDictionary *)scannerOptions { - if (scannerOptions[offsetForScannerFrame]) { - self.frameOffset = [scannerOptions[offsetForScannerFrame] floatValue]; - } - if (scannerOptions[heightForScannerFrame]) { - self.heightFrame = [scannerOptions[heightForScannerFrame] floatValue]; - } - if (scannerOptions[colorForFrame]) { - UIColor *acolor = [RCTConvert UIColor:scannerOptions[colorForFrame]]; - self.frameColor = (acolor) ? acolor : [UIColor whiteColor]; - } -} - - (void)addFrameForScanner { CGFloat frameWidth = self.bounds.size.width - 2 * self.frameOffset; if (!self.dataReadingFrame) { - self.dataReadingFrame = [[UIView alloc] initWithFrame:CGRectMake(0, 0, frameWidth, self.heightFrame)]; // + self.dataReadingFrame = [[UIView alloc] initWithFrame:CGRectMake(0, 0, frameWidth, self.frameHeight)]; // self.dataReadingFrame.center = self.center; self.dataReadingFrame.backgroundColor = [UIColor clearColor]; [self createCustomFramesForView:self.dataReadingFrame]; [self addSubview:self.dataReadingFrame]; - [self startAnimatingScanner:self.dataReadingFrame]; [self addVisualEffects:self.dataReadingFrame.frame]; @@ -955,7 +954,6 @@ RCT_ENUM_CONVERTER(CKCameraZoomMode, (@{ UIView * cornerView = [[UIView alloc] initWithFrame:CGRectMake(x, y, width, height)]; cornerView.backgroundColor = self.frameColor; [frameView addSubview:cornerView]; - } } @@ -964,38 +962,37 @@ RCT_ENUM_CONVERTER(CKCameraZoomMode, (@{ topView.backgroundColor = [UIColor colorWithRed:0.0/255.0 green:0.0/255.0 blue:0.0/255.0 alpha:0.4]; [self addSubview:topView]; - UIView *leftSideView = [[UIView alloc] initWithFrame:CGRectMake(0, inputRect.origin.y, self.frameOffset, self.heightFrame)]; //paddingForScanner scannerHeight + UIView *leftSideView = [[UIView alloc] initWithFrame:CGRectMake(0, inputRect.origin.y, self.frameOffset, self.frameHeight)]; //paddingForScanner scannerHeight leftSideView.backgroundColor = [UIColor colorWithRed:0.0/255.0 green:0.0/255.0 blue:0.0/255.0 alpha:0.4]; [self addSubview:leftSideView]; - UIView *rightSideView = [[UIView alloc] initWithFrame:CGRectMake(inputRect.size.width + self.frameOffset, inputRect.origin.y, self.frameOffset, self.heightFrame)]; + UIView *rightSideView = [[UIView alloc] initWithFrame:CGRectMake(inputRect.size.width + self.frameOffset, inputRect.origin.y, self.frameOffset, self.frameHeight)]; rightSideView.backgroundColor = [UIColor colorWithRed:0.0/255.0 green:0.0/255.0 blue:0.0/255.0 alpha:0.4]; [self addSubview:rightSideView]; - UIView *bottomView = [[UIView alloc] initWithFrame:CGRectMake(0, inputRect.origin.y + self.heightFrame, self.frame.size.width, - self.frame.size.height - inputRect.origin.y - self.heightFrame)]; + UIView *bottomView = [[UIView alloc] initWithFrame:CGRectMake(0, inputRect.origin.y + self.frameHeight, self.frame.size.width, + self.frame.size.height - inputRect.origin.y - self.frameHeight)]; bottomView.backgroundColor = [UIColor colorWithRed:0.0/255.0 green:0.0/255.0 blue:0.0/255.0 alpha:0.4]; [self addSubview:bottomView]; - } - (void)startAnimatingScanner:(UIView *)inputView { - if (!self.greenScanner) { - self.greenScanner = [[UIView alloc] initWithFrame:CGRectMake(2, 0, inputView.frame.size.width - 4, 2)]; - self.greenScanner.backgroundColor = [UIColor whiteColor]; + if (!self.scannerView) { + self.scannerView = [[UIView alloc] initWithFrame:CGRectMake(2, 0, inputView.frame.size.width - 4, 2)]; + self.scannerView.backgroundColor = self.laserColor; } - if (self.greenScanner.frame.origin.y != 0) { - [self.greenScanner setFrame:CGRectMake(2, 0, inputView.frame.size.width - 4, 2)]; + if (self.scannerView.frame.origin.y != 0) { + [self.scannerView setFrame:CGRectMake(2, 0, inputView.frame.size.width - 4, 2)]; } - [inputView addSubview:self.greenScanner]; + [inputView addSubview:self.scannerView]; [UIView animateWithDuration:3 delay:0 options:(UIViewAnimationOptionAutoreverse | UIViewAnimationOptionRepeat) animations:^{ CGFloat middleX = inputView.frame.size.width / 2; - self.greenScanner.center = CGPointMake(middleX, inputView.frame.size.height - 1); + self.scannerView.center = CGPointMake(middleX, inputView.frame.size.height - 1); } completion:^(BOOL finished) {}]; } - (void)stopAnimatingScanner { - [self.greenScanner removeFromSuperview]; + [self.scannerView removeFromSuperview]; } //Observer actions diff --git a/ios/ReactNativeCameraKit/CKCameraManager.m b/ios/ReactNativeCameraKit/CKCameraManager.m index 1fdf6ce..9f3e18a 100644 --- a/ios/ReactNativeCameraKit/CKCameraManager.m +++ b/ios/ReactNativeCameraKit/CKCameraManager.m @@ -19,15 +19,16 @@ RCT_EXPORT_MODULE() RCT_EXPORT_VIEW_PROPERTY(cameraType, CKCameraType) RCT_EXPORT_VIEW_PROPERTY(flashMode, CKCameraFlashMode) +RCT_EXPORT_VIEW_PROPERTY(torchMode, CKCameraTorchMode) RCT_EXPORT_VIEW_PROPERTY(focusMode, CKCameraFocusMode) RCT_EXPORT_VIEW_PROPERTY(zoomMode, CKCameraZoomMode) RCT_EXPORT_VIEW_PROPERTY(ratioOverlay, NSString) RCT_EXPORT_VIEW_PROPERTY(ratioOverlayColor, UIColor) RCT_EXPORT_VIEW_PROPERTY(onReadCode, RCTDirectEventBlock) - -RCT_EXPORT_VIEW_PROPERTY(scannerOptions, NSDictionary) RCT_EXPORT_VIEW_PROPERTY(showFrame, BOOL) +RCT_EXPORT_VIEW_PROPERTY(laserColor, UIColor) +RCT_EXPORT_VIEW_PROPERTY(frameColor, UIColor) RCT_EXPORT_VIEW_PROPERTY(resetFocusTimeout, NSInteger) RCT_EXPORT_VIEW_PROPERTY(resetFocusWhenMotionDetected, BOOL) RCT_EXPORT_VIEW_PROPERTY(saveToCameraRoll, BOOL) diff --git a/src/CameraScreen.tsx b/src/CameraScreen.tsx index f54485c..b2c6e62 100644 --- a/src/CameraScreen.tsx +++ b/src/CameraScreen.tsx @@ -9,7 +9,6 @@ import { Dimensions, Platform, SafeAreaView, - processColor, } from 'react-native'; import _ from 'lodash'; import Camera from './Camera'; @@ -17,10 +16,6 @@ import Camera from './Camera'; const FLASH_MODE_AUTO = 'auto'; const FLASH_MODE_ON = 'on'; const FLASH_MODE_OFF = 'off'; -const TORCH_MODE_ON = 'on'; -const TORCH_MODE_OFF = 'off'; -const OFFSET_FRAME = 30; -const FRAME_HEIGHT = 200; const { width, height } = Dimensions.get('window'); @@ -35,18 +30,15 @@ export type Props = { allowCaptureRetake: boolean, cameraRatioOverlay: any, showCapturedImageCount?: boolean, - scannerOptions: any, - offsetForScannerFrame: any, - heightForScannerFrame: any, - colorForScannerFrame: any, + captureButtonImage: any, cameraFlipImage: any, hideControls: any, showFrame: any, - captureButtonImage: any, scanBarcode: any, laserColor: any, frameColor: any, - surfaceColor: any, + torchOnImage: any, + torchOffImage: any, onReadCode: (any) => void; onBottomButtonPressed: (any) => void; } @@ -54,12 +46,11 @@ export type Props = { type State = { captureImages: any[], flashData: any, - torchData: boolean, + torchMode: boolean, ratios: any[], ratioArrayPosition: number, imageCaptured: any, captured: boolean, - scannerOptions: any, cameraType: CameraType, } @@ -97,28 +88,21 @@ export default class CameraScreen extends Component { this.state = { captureImages: [], flashData: this.flashArray[this.currentFlashArrayPosition], - torchData: false, + torchMode: false, ratios: [], ratioArrayPosition: -1, imageCaptured: false, captured: false, - scannerOptions: {}, cameraType: CameraType.Back, }; - - this.onSetFlash = this.onSetFlash.bind(this); - this.onSetTorch = this.onSetTorch.bind(this); - this.onSwitchCameraPressed = this.onSwitchCameraPressed.bind(this); } componentDidMount() { - const scannerOptions = this.getScannerOptions(); let ratios = []; if (this.props.cameraRatioOverlay) { ratios = this.props.cameraRatioOverlay.ratios || []; } this.setState({ - scannerOptions, ratios: ratios || [], ratioArrayPosition: ratios.length > 0 ? 0 : -1, }); @@ -128,18 +112,6 @@ export default class CameraScreen extends Component { return !!(this.props.allowCaptureRetake && !_.isUndefined(this.state.imageCaptured)); } - getScannerOptions() { - const scannerOptions = this.props.scannerOptions || {}; - scannerOptions.offsetFrame = this.props.offsetForScannerFrame || OFFSET_FRAME; - scannerOptions.frameHeight = this.props.heightForScannerFrame || FRAME_HEIGHT; - if (this.props.colorForScannerFrame) { - scannerOptions.colorForFrame = processColor(this.props.colorForScannerFrame); - } else { - scannerOptions.colorForFrame = processColor('white'); - } - return scannerOptions; - } - renderFlashButton() { return ( !this.isCaptureRetakeMode() && ( @@ -154,11 +126,25 @@ export default class CameraScreen extends Component { ); } + renderTorchButton() { + return ( + !this.isCaptureRetakeMode() && ( + this.onSetTorch()}> + + + ) + ); + } + renderSwitchCameraButton() { return ( this.props.cameraFlipImage && !this.isCaptureRetakeMode() && ( - + this.onSwitchCameraPressed()}> { {this.renderFlashButton()} {this.renderSwitchCameraButton()} + {this.renderTorchButton()} ) ); @@ -191,6 +178,7 @@ export default class CameraScreen extends Component { style={{ flex: 1, justifyContent: 'flex-end' }} cameraType={this.state.cameraType} flashMode={this.state.flashData.mode} + torchMode={this.state.torchMode ? 'on' : 'off'} focusMode={this.props.focusMode} zoomMode={this.props.zoomMode} ratioOverlay={this.state.ratios[this.state.ratioArrayPosition]} @@ -199,9 +187,7 @@ export default class CameraScreen extends Component { scanBarcode={this.props.scanBarcode} laserColor={this.props.laserColor} frameColor={this.props.frameColor} - surfaceColor={this.props.surfaceColor} onReadCode={this.props.onReadCode} - scannerOptions={this.state.scannerOptions} /> )} @@ -303,20 +289,18 @@ export default class CameraScreen extends Component { } onSwitchCameraPressed() { - const direction = this.state.type === CameraType.Back ? CameraType.Front : CameraType.Back; - this.setState({ type: direction }); + const direction = this.state.cameraType === CameraType.Back ? CameraType.Front : CameraType.Back; + this.setState({ cameraType: direction }); } - async onSetFlash() { + onSetFlash() { this.currentFlashArrayPosition = (this.currentFlashArrayPosition + 1) % 3; const newFlashData = this.flashArray[this.currentFlashArrayPosition]; this.setState({ flashData: newFlashData }); } onSetTorch() { - const newTorchData = !this.state.torchData; - this.setState({ torchData: newTorchData }); - newTorchData ? this.camera.setTorchMode(TORCH_MODE_ON) : this.camera.setTorchMode(TORCH_MODE_OFF); + this.setState({ torchMode: !this.state.torchMode }); } async onCaptureImagePressed() {