import PropTypes from 'prop-types'; import React, { Component } from 'react'; import { StyleSheet, Text, View, TouchableOpacity, Image, NativeModules, Platform, SafeAreaView, processColor } from 'react-native'; import _ from 'lodash'; import CameraKitCamera from './../CameraKitCamera'; const IsIOS = Platform.OS === 'ios'; const GalleryManager = IsIOS ? NativeModules.CKGalleryManager : NativeModules.NativeGalleryModule; const FLASH_MODE_AUTO = 'auto'; const FLASH_MODE_ON = 'on'; const FLASH_MODE_OFF = 'off'; const OVERLAY_DEFAULT_COLOR = '#ffffff77'; const OFFSET_FRAME = 30; const FRAME_HEIGHT = 200; export default class CameraScreenBase extends Component { static propTypes = { allowCaptureRetake: PropTypes.bool, }; static defaultProps = { allowCaptureRetake: false, }; constructor(props) { super(props); this.currentFlashArrayPosition = 0; this.flashArray = [{ mode: FLASH_MODE_AUTO, image: _.get(this.props, 'flashImages.auto') }, { mode: FLASH_MODE_ON, image: _.get(this.props, 'flashImages.on') }, { mode: FLASH_MODE_OFF, image: _.get(this.props, 'flashImages.off') } ]; this.state = { captureImages: [], flashData: this.flashArray[this.currentFlashArrayPosition], ratios: [], cameraOptions: {}, ratioArrayPosition: -1, imageCaptured: undefined, captured: false, scannerOptions : {} }; this.onSetFlash = this.onSetFlash.bind(this); this.onSwitchCameraPressed = this.onSwitchCameraPressed.bind(this); } componentDidMount() { const cameraOptions = this.getCameraOptions(); const scannerOptions = this.getScannerOptions(); let ratios = []; if (this.props.cameraRatioOverlay) { ratios = this.props.cameraRatioOverlay.ratios || []; } this.setState({ cameraOptions, scannerOptions, ratios: (ratios || []), ratioArrayPosition: ((ratios.length > 0) ? 0 : -1) }); } isCaptureRetakeMode() { return !!(this.props.allowCaptureRetake && !_.isUndefined(this.state.imageCaptured)); } getCameraOptions() { const cameraOptions = this.props.cameraOptions || { flashMode: 'auto', focusMode: 'on', zoomMode: 'on' }; if (this.props.cameraRatioOverlay) { const overlay = this.props.cameraRatioOverlay; cameraOptions.ratioOverlayColor = overlay.color || OVERLAY_DEFAULT_COLOR; if (overlay.ratios && overlay.ratios.length > 0) { cameraOptions.ratioOverlay = overlay.ratios[0]; } } return cameraOptions; } 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() && this.onSetFlash(FLASH_MODE_AUTO)}> } renderSwitchCameraButton() { return (this.props.cameraFlipImage && !this.isCaptureRetakeMode()) && } renderTopButtons() { return !this.props.hideControls && ( {this.renderFlashButton()} {this.renderSwitchCameraButton()} ); } renderCamera() { return ( { this.isCaptureRetakeMode() ? : this.camera = cam} style={{ flex: 1, justifyContent: 'flex-end' }} cameraOptions={this.state.cameraOptions} showFrame={this.props.showFrame} scanBarcode={this.props.scanBarcode} laserColor={this.props.laserColor} frameColor={this.props.frameColor} onReadCode = {this.props.onReadCode} scannerOptions = {this.state.scannerOptions} /> } ); } numberOfImagesTaken() { const numberTook = this.state.captureImages.length; if (numberTook >= 2) { return numberTook; } else if (this.state.captured) { return '1'; } else { return ''; } } renderCaptureButton() { return (this.props.captureButtonImage && !this.isCaptureRetakeMode()) && this.onCaptureImagePressed()} > {this.numberOfImagesTaken()} } renderRatioStrip() { if (this.state.ratios.length === 0 || this.props.hideControls) { return null; } return ( Your images look best at a {this.state.ratios[0] || ''} ratio this.onRatioButtonPressed()} > {this.state.cameraOptions.ratioOverlay} ); } sendBottomButtonPressedAction(type, captureRetakeMode, image) { if (this.props.onBottomButtonPressed) { this.props.onBottomButtonPressed({ type, captureImages: this.state.captureImages, captureRetakeMode, image }) } } async onButtonPressed(type) { const captureRetakeMode = this.isCaptureRetakeMode(); if (captureRetakeMode) { if (type === 'left') { GalleryManager.deleteTempImage(this.state.imageCaptured.uri); this.setState({ imageCaptured: undefined }); } else if (type === 'right') { const result = await GalleryManager.saveImageURLToCameraRoll(this.state.imageCaptured.uri); const savedImage = { ...this.state.imageCaptured, ...result }; // Note: Can't just return 'result' as on iOS not all data is returned by the native call (just the ID). this.setState({ imageCaptured: undefined, captureImages: _.concat(this.state.captureImages, savedImage) }, () => { this.sendBottomButtonPressedAction(type, captureRetakeMode); }); } } else { this.sendBottomButtonPressedAction(type, captureRetakeMode); } } renderBottomButton(type) { let showButton = true; if (type === 'right') { showButton = this.state.captureImages.length || this.isCaptureRetakeMode(); } if (showButton) { const buttonNameSuffix = this.isCaptureRetakeMode() ? 'CaptureRetakeButtonText' : 'ButtonText'; const buttonText = _(this.props).get(`actions.${type}${buttonNameSuffix}`) return ( this.onButtonPressed(type)} > {buttonText} ); } else { return ( ); } } renderBottomButtons() { return !this.props.hideControls && ( {this.renderBottomButton('left')} {this.renderCaptureButton()} {this.renderBottomButton('right')} ); } onSwitchCameraPressed() { this.camera.changeCamera(); } async onSetFlash() { this.currentFlashArrayPosition = (this.currentFlashArrayPosition + 1) % 3; const newFlashData = this.flashArray[this.currentFlashArrayPosition]; this.setState({ flashData: newFlashData }); this.camera.setFlashMode(newFlashData.mode); } async onCaptureImagePressed() { const shouldSaveToCameraRoll = !this.props.allowCaptureRetake; const image = await this.camera.capture(shouldSaveToCameraRoll); if (this.props.allowCaptureRetake) { this.setState({ imageCaptured: image }); } else { if (image) { this.setState({ captured: true, imageCaptured: image, captureImages: _.concat(this.state.captureImages, image) }); } this.sendBottomButtonPressedAction('capture', false, image); } } onRatioButtonPressed() { const newRatiosArrayPosition = ((this.state.ratioArrayPosition + 1) % this.state.ratios.length); const newCameraOptions = _.update(this.state.cameraOptions, 'ratioOverlay', (val) => this.state.ratios[newRatiosArrayPosition]); this.setState({ ratioArrayPosition: newRatiosArrayPosition, cameraOptions: newCameraOptions }); } render() { throw ('Implemented in CameraKitCameraScreen!'); } } import styleObject from './CameraKitCameraScreenStyleObject'; const styles = StyleSheet.create(_.merge(styleObject, { textStyle: { color: 'white', fontSize: 20 }, ratioBestText: { color: 'white', fontSize: 18, }, ratioText: { color: '#ffc233', fontSize: 18 }, topButtons: { flex: 1, flexDirection: 'row', justifyContent: 'space-between', paddingTop: 8, paddingBottom: 0 }, cameraContainer: { flex: 10, flexDirection: 'column' }, captureButtonContainer: { flex: 1, justifyContent: 'center', alignItems: 'center', }, textNumberContainer: { position: 'absolute', top: 0, left: 0, bottom: 0, right: 0, justifyContent: 'center', alignItems: 'center' }, bottomButton: { flex: 1, flexDirection: 'row', alignItems: 'center', padding: 10 }, bottomContainerGap: { flex: 1, flexDirection: 'row', justifyContent: 'flex-end', alignItems: 'center', padding: 10 } }));