UNPKG

react-native-qrcode-package

Version:

This package include QR code generator and QR code Scanner for React Native.

382 lines (354 loc) 10.2 kB
import React, {Component} from 'react'; import PropTypes from 'prop-types'; import { StyleSheet, Dimensions, Vibration, Animated, Easing, View, Text, Platform, TouchableWithoutFeedback, PermissionsAndroid, } from 'react-native'; import {request, PERMISSIONS, RESULTS} from 'react-native-permissions'; import {RNCamera as Camera} from 'react-native-camera'; const CAMERA_FLASH_MODE = Camera.Constants.FlashMode; const CAMERA_FLASH_MODES = [ CAMERA_FLASH_MODE.torch, CAMERA_FLASH_MODE.on, CAMERA_FLASH_MODE.off, CAMERA_FLASH_MODE.auto, ]; export default class ScanQRCode extends Component { static propTypes = { onQRCodeScanned: PropTypes.func.isRequired, vibrateAfterScan: PropTypes.bool, reactivateCamera: PropTypes.bool, reactivateScanTimeout: PropTypes.number, cameraScanTimeout: PropTypes.number, fadeImageIn: PropTypes.bool, showScanMarker: PropTypes.bool, cameraType: PropTypes.oneOf(['front', 'back']), customMarker: PropTypes.element, containerStyle: PropTypes.any, cameraStyle: PropTypes.any, cameraContainerStyle: PropTypes.any, markerStyle: PropTypes.any, topViewStyle: PropTypes.any, bottomViewStyle: PropTypes.any, topContent: PropTypes.oneOfType([PropTypes.element, PropTypes.string]), bottomContent: PropTypes.oneOfType([PropTypes.element, PropTypes.string]), notAuthorizedView: PropTypes.element, permissionDialogTitle: PropTypes.string, permissionDialogMessage: PropTypes.string, buttonPositive: PropTypes.string, checkAndroid6Permissions: PropTypes.bool, lightMode: PropTypes.oneOf(CAMERA_FLASH_MODES), cameraProps: PropTypes.object, cameraTimeoutView: PropTypes.element, onError: PropTypes.func, }; static defaultProps = { onQRCodeScanned: () => {}, reactivateCamera: true, vibrateAfterScan: false, reactivateScanTimeout: 0, cameraScanTimeout: 0, fadeImageIn: true, showScanMarker: false, cameraType: 'back', notAuthorizedView: ( <View style={{flex: 1, alignItems: 'center', justifyContent: 'center'}}> <Text style={{textAlign: 'center', fontSize: 16}}> Camera not authorized </Text> </View> ), pendingAuthorizationView: ( <View style={{flex: 1, alignItems: 'center', justifyContent: 'center'}}> <Text style={{textAlign: 'center', fontSize: 16}}>...</Text> </View> ), permissionDialogTitle: 'Permission', permissionDialogMessage: 'Requires camera permission to scan', buttonPositive: 'OK', checkAndroid6Permissions: false, lightMode: CAMERA_FLASH_MODE.off, cameraProps: {}, cameraTimeoutView: ( <View style={{ flex: 0, alignItems: 'center', justifyContent: 'center', height: Dimensions.get('window').height, width: Dimensions.get('window').width, backgroundColor: 'black', }}> <Text style={{color: 'white'}}>Tap to activate camera</Text> </View> ), }; constructor(props) { super(props); this.state = { scanning: false, isCameraActivated: true, fadeInOpacity: new Animated.Value(0), isAuthorized: false, isAuthorizationChecked: false, disableVibrationByUser: false, }; this.timer = null; this._scannerTimeout = null; this._handleBarCodeRead = this.handleScanCodeRead.bind(this); } componentDidMount() { if (Platform.OS === 'ios') { request(PERMISSIONS.IOS.CAMERA).then(cameraStatus => { this.setState({ isAuthorized: cameraStatus === RESULTS.GRANTED, isAuthorizationChecked: true, }); }); } else if ( Platform.OS === 'android' && this.props.checkAndroid6Permissions ) { PermissionsAndroid.request(PermissionsAndroid.PERMISSIONS.CAMERA, { title: this.props.permissionDialogTitle, message: this.props.permissionDialogMessage, buttonPositive: this.props.buttonPositive, }).then(granted => { const isAuthorized = granted === PermissionsAndroid.RESULTS.GRANTED; this.setState({isAuthorized, isAuthorizationChecked: true}); }); } else { this.setState({isAuthorized: true, isAuthorizationChecked: true}); } if (this.props.fadeImageIn) { Animated.sequence([ Animated.delay(1000), Animated.timing(this.state.fadeInOpacity, { toValue: 1, easing: Easing.inOut(Easing.quad), useNativeDriver: true, }), ]).start(); } } componentWillUnmount() { if (this._scannerTimeout !== null) { clearTimeout(this._scannerTimeout); } if (this.timer !== null) { clearTimeout(this.timer); } this.timer = null; this._scannerTimeout = null; } disable() { this.setState({disableVibrationByUser: true}); } enable() { this.setState({disableVibrationByUser: false}); } setScanState(value) { this.setState({scanning: value}); } setCamera(value) { this.setState( { isCameraActivated: value, scanning: false, fadeInOpacity: new Animated.Value(0), }, () => { if (value && this.props.fadeImageIn) { if (this.props.fadeImageIn) { Animated.sequence([ Animated.delay(10), Animated.timing(this.state.fadeInOpacity, { toValue: 1, easing: Easing.inOut(Easing.quad), useNativeDriver: true, }), ]).start(); } } }, ); } handleScanCodeRead(e) { if (!this.state.scanning && !this.state.disableVibrationByUser) { if (this.props.vibrateAfterScan) { Vibration.vibrate(); } this.setScanState(true); this.handleQRCodeRead(e); if (this.props.reactivateCamera) { this._scannerTimeout = setTimeout( () => this.setScanState(false), this.props.reactivateScanTimeout, ); } } } handleQRCodeRead = e => { const {onQRCodeScanned, onError} = this.props; try { const json = JSON.parse(e.data); onQRCodeScanned(json); } catch (error) { console.error('Something went wrong:', error); if (onError) { onError(error); } } }; renderTopContent() { if (this.props.topContent) { return this.props.topContent; } return null; } renderBottomContent() { if (this.props.bottomContent) { return this.props.bottomContent; } return null; } renderCameraMarker() { if (this.props.showScanMarker) { if (this.props.customMarker) { return this.props.customMarker; } else { return ( <View style={styles.rectangleContainer}> <View style={[ styles.rectangle, this.props.markerStyle ? this.props.markerStyle : null, ]} /> </View> ); } } return null; } renderCameraComponent() { return ( <Camera androidCameraPermissionOptions={{ title: this.props.permissionDialogTitle, message: this.props.permissionDialogMessage, buttonPositive: this.props.buttonPositive, }} style={[styles.camera, this.props.cameraStyle]} onBarCodeRead={this.handleScanCodeRead.bind(this)} type={this.props.cameraType} flashMode={this.props.lightMode} captureAudio={false} {...this.props.cameraProps}> {this.renderCameraMarker()} </Camera> ); } renderCamera() { const { notAuthorizedView, pendingAuthorizationView, cameraType, cameraTimeoutView, } = this.props; if (!this.state.isCameraActivated) { return ( <TouchableWithoutFeedback onPress={() => this.setCamera(true)}> {cameraTimeoutView} </TouchableWithoutFeedback> ); } const {isAuthorized, isAuthorizationChecked} = this.state; if (isAuthorized) { if (this.props.cameraScanTimeout > 0) { this.timer && clearTimeout(this.timer); this.timer = setTimeout( () => this.setCamera(false), this.props.cameraScanTimeout, ); } if (this.props.fadeImageIn) { return ( <Animated.View style={{ opacity: this.state.fadeInOpacity, backgroundColor: 'transparent', height: (this.props.cameraStyle && this.props.cameraStyle.height) || styles.camera.height, }}> {this.renderCameraComponent()} </Animated.View> ); } return this.renderCameraComponent(); } else if (!isAuthorizationChecked) { return pendingAuthorizationView; } else { return notAuthorizedView; } } reactivate() { this.setScanState(false); } render() { return ( <View style={[styles.mainContainer, this.props.containerStyle]}> <View style={[styles.infoView, this.props.topViewStyle]}> {this.renderTopContent()} </View> <View style={this.props.cameraContainerStyle}> {this.renderCamera()} </View> <View style={[styles.infoView, this.props.bottomViewStyle]}> {this.renderBottomContent()} </View> </View> ); } } const styles = StyleSheet.create({ mainContainer: { flex: 1, }, infoView: { flex: 2, justifyContent: 'center', alignItems: 'center', width: Dimensions.get('window').width, }, camera: { flex: 0, alignItems: 'center', justifyContent: 'center', backgroundColor: 'transparent', height: Dimensions.get('window').width, width: Dimensions.get('window').width, }, rectangleContainer: { flex: 1, alignItems: 'center', justifyContent: 'center', backgroundColor: 'transparent', }, rectangle: { height: 250, width: 250, borderWidth: 2, borderColor: '#00FF00', backgroundColor: 'transparent', }, });