Compare commits
2 Commits
master
...
feat/hooks
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
55ff9809ac | ||
|
|
e5b2f4e7d5 |
@ -1,5 +1,5 @@
|
||||
/* eslint-disable no-console */
|
||||
import React from 'react';
|
||||
import React, {useState, useReducer, useRef} from 'react';
|
||||
import {
|
||||
StyleSheet,
|
||||
Text,
|
||||
@ -30,61 +30,87 @@ const wbOrder = {
|
||||
|
||||
const landmarkSize = 2;
|
||||
|
||||
export default class CameraScreen extends React.Component {
|
||||
state = {
|
||||
flash: 'off',
|
||||
zoom: 0,
|
||||
autoFocus: 'on',
|
||||
autoFocusPoint: {
|
||||
normalized: { x: 0.5, y: 0.5 }, // normalized values required for autoFocusPointOfInterest
|
||||
drawRectPosition: {
|
||||
x: Dimensions.get('window').width * 0.5 - 32,
|
||||
y: Dimensions.get('window').height * 0.5 - 32,
|
||||
},
|
||||
const stateReducer = (state, action) => {
|
||||
switch (action.type) {
|
||||
case 'toggleFlash':
|
||||
return { state.flash === flashModeOrder[ state.flash] }
|
||||
break;
|
||||
case 'toggleZoom':
|
||||
return { state.flash === flashModeOrder[ state.flash] }
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const initialState = {
|
||||
flash: 'off',
|
||||
zoom: 0,
|
||||
autoFocus: 'on',
|
||||
depth: 0,
|
||||
type: 'back',
|
||||
whiteBalance: 'auto',
|
||||
ratio: '16:9',
|
||||
isRecording: false,
|
||||
canDetectFaces: false,
|
||||
canDetectText: false,
|
||||
canDetectBarcode: false,
|
||||
faces: [],
|
||||
textBlocks: [],
|
||||
barcodes: [],
|
||||
|
||||
recordOptions: {
|
||||
mute: false,
|
||||
maxDuration: 5,
|
||||
quality: RNCamera.Constants.VideoQuality['288p'],
|
||||
},
|
||||
autoFocusPoint: {
|
||||
normalized: { x: 0.5, y: 0.5 }, // normalized values required for autoFocusPointOfInterest
|
||||
drawRectPosition: {
|
||||
x: Dimensions.get('window').width * 0.5 - 32,
|
||||
y: Dimensions.get('window').height * 0.5 - 32,
|
||||
},
|
||||
depth: 0,
|
||||
type: 'back',
|
||||
whiteBalance: 'auto',
|
||||
ratio: '16:9',
|
||||
recordOptions: {
|
||||
mute: false,
|
||||
maxDuration: 5,
|
||||
quality: RNCamera.Constants.VideoQuality['288p'],
|
||||
},
|
||||
isRecording: false,
|
||||
canDetectFaces: false,
|
||||
canDetectText: false,
|
||||
canDetectBarcode: false,
|
||||
faces: [],
|
||||
textBlocks: [],
|
||||
barcodes: [],
|
||||
};
|
||||
},
|
||||
|
||||
toggleFacing() {
|
||||
this.setState({
|
||||
type: this.state.type === 'back' ? 'front' : 'back',
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
|
||||
const CameraScreen = () => {
|
||||
const [state, setState] = useState(initialState);
|
||||
const [recordOptions, togglerecordOptions] = useReducer(reducer, initialState.recordOptions);
|
||||
const [autoFocusPoint, touchToFocus] = useReducer(reducer, initialState.autoFocusPoint);
|
||||
const cameraRef = useRef(null);
|
||||
|
||||
toggleFacing =() =>{
|
||||
setState({
|
||||
...state,
|
||||
type: type === 'back' ? 'front' : 'back',
|
||||
})
|
||||
}
|
||||
|
||||
toggleFlash() {
|
||||
this.setState({
|
||||
flash: flashModeOrder[this.state.flash],
|
||||
});
|
||||
toggleFlash =() => {
|
||||
setState({
|
||||
...state,
|
||||
flash: flashModeOrder[flash]
|
||||
})
|
||||
}
|
||||
|
||||
toggleWB() {
|
||||
this.setState({
|
||||
whiteBalance: wbOrder[this.state.whiteBalance],
|
||||
});
|
||||
toggleWB =() => {
|
||||
setState({
|
||||
...state,
|
||||
whiteBalance: wbOrder[whiteBalance]
|
||||
})
|
||||
}
|
||||
|
||||
toggleFocus() {
|
||||
this.setState({
|
||||
autoFocus: this.state.autoFocus === 'on' ? 'off' : 'on',
|
||||
});
|
||||
toggleFocus =() =>{
|
||||
setState({
|
||||
...state,
|
||||
autoFocus: autoFocus === 'on' ? 'off' : 'on'
|
||||
})
|
||||
}
|
||||
|
||||
touchToFocus(event) {
|
||||
touchToFocus =(event) => {
|
||||
const { pageX, pageY } = event.nativeEvent;
|
||||
const screenWidth = Dimensions.get('window').width;
|
||||
const screenHeight = Dimensions.get('window').height;
|
||||
@ -97,49 +123,56 @@ export default class CameraScreen extends React.Component {
|
||||
x = pageY / screenHeight;
|
||||
y = -(pageX / screenWidth) + 1;
|
||||
}
|
||||
|
||||
this.setState({
|
||||
return {
|
||||
autoFocusPoint: {
|
||||
normalized: { x, y },
|
||||
drawRectPosition: { x: pageX, y: pageY },
|
||||
},
|
||||
};
|
||||
}
|
||||
zoomOut =() =>{
|
||||
setState({
|
||||
...state,
|
||||
zoom: state.zoom - 0.1 < 0 ? 0 : state.zoom - 0.1,
|
||||
})
|
||||
}
|
||||
|
||||
zoomIn =() => {
|
||||
setState({
|
||||
...state,
|
||||
zoom: state.zoom + 0.1 > 1 ? 1 : state.zoom + 0.1,
|
||||
});
|
||||
}
|
||||
|
||||
zoomOut() {
|
||||
this.setState({
|
||||
zoom: this.state.zoom - 0.1 < 0 ? 0 : this.state.zoom - 0.1,
|
||||
});
|
||||
}
|
||||
|
||||
zoomIn() {
|
||||
this.setState({
|
||||
zoom: this.state.zoom + 0.1 > 1 ? 1 : this.state.zoom + 0.1,
|
||||
});
|
||||
}
|
||||
|
||||
setFocusDepth(depth) {
|
||||
this.setState({
|
||||
depth,
|
||||
setFocusDepth = (depth) => {
|
||||
setState({
|
||||
...state,
|
||||
depth: depth
|
||||
});
|
||||
}
|
||||
|
||||
takePicture = async function() {
|
||||
if (this.camera) {
|
||||
const data = await this.camera.takePictureAsync();
|
||||
if (cameraRef) {
|
||||
const data = await cameraRef.takePictureAsync();
|
||||
console.warn('takePicture ', data);
|
||||
}
|
||||
};
|
||||
|
||||
takeVideo = async function() {
|
||||
if (this.camera) {
|
||||
if (cameraRef) {
|
||||
try {
|
||||
const promise = this.camera.recordAsync(this.state.recordOptions);
|
||||
const promise = cameraRef.recordAsync( state.recordOptions);
|
||||
|
||||
if (promise) {
|
||||
this.setState({ isRecording: true });
|
||||
setState({
|
||||
...state,
|
||||
isRecording: true
|
||||
});
|
||||
const data = await promise;
|
||||
this.setState({ isRecording: false });
|
||||
setState({
|
||||
...state,
|
||||
isRecording: false
|
||||
});
|
||||
console.warn('takeVideo', data);
|
||||
}
|
||||
} catch (e) {
|
||||
@ -148,9 +181,19 @@ export default class CameraScreen extends React.Component {
|
||||
}
|
||||
};
|
||||
|
||||
toggle = value => () => this.setState(prevState => ({ [value]: !prevState[value] }));
|
||||
toggle = value => () => {
|
||||
setState({
|
||||
...state,
|
||||
[value]: !prevState[value]
|
||||
})
|
||||
};
|
||||
|
||||
facesDetected = ({ faces }) => this.setState({ faces });
|
||||
facesDetected = ({ faces }) => {
|
||||
setState({
|
||||
...state,
|
||||
faces: faces
|
||||
})
|
||||
};
|
||||
|
||||
renderFace = ({ bounds, faceID, rollAngle, yawAngle }) => (
|
||||
<View
|
||||
@ -175,7 +218,7 @@ export default class CameraScreen extends React.Component {
|
||||
</View>
|
||||
);
|
||||
|
||||
renderLandmarksOfFace(face) {
|
||||
renderLandmarksOfFace =(face) => {
|
||||
const renderLandmark = position =>
|
||||
position && (
|
||||
<View
|
||||
@ -207,19 +250,19 @@ export default class CameraScreen extends React.Component {
|
||||
|
||||
renderFaces = () => (
|
||||
<View style={styles.facesContainer} pointerEvents="none">
|
||||
{this.state.faces.map(this.renderFace)}
|
||||
{ state.faces.map(() => renderFace())}
|
||||
</View>
|
||||
);
|
||||
|
||||
renderLandmarks = () => (
|
||||
<View style={styles.facesContainer} pointerEvents="none">
|
||||
{this.state.faces.map(this.renderLandmarksOfFace)}
|
||||
{ state.faces.map(() => renderLandmarksOfFace())}
|
||||
</View>
|
||||
);
|
||||
|
||||
renderTextBlocks = () => (
|
||||
<View style={styles.facesContainer} pointerEvents="none">
|
||||
{this.state.textBlocks.map(this.renderTextBlock)}
|
||||
{ state.textBlocks.map(() => renderTextBlock())}
|
||||
</View>
|
||||
);
|
||||
|
||||
@ -243,14 +286,22 @@ export default class CameraScreen extends React.Component {
|
||||
|
||||
textRecognized = object => {
|
||||
const { textBlocks } = object;
|
||||
this.setState({ textBlocks });
|
||||
setState({
|
||||
...state,
|
||||
textBlocks: textBlocks
|
||||
});
|
||||
};
|
||||
|
||||
barcodeRecognized = ({ barcodes }) => this.setState({ barcodes });
|
||||
barcodeRecognized = ({ barcodes }) => {
|
||||
setState({
|
||||
...stat,
|
||||
barcodes
|
||||
})
|
||||
};
|
||||
|
||||
renderBarcodes = () => (
|
||||
<View style={styles.facesContainer} pointerEvents="none">
|
||||
{this.state.barcodes.map(this.renderBarcode)}
|
||||
{ state.barcodes.map( renderBarcode)}
|
||||
</View>
|
||||
);
|
||||
|
||||
@ -271,30 +322,30 @@ export default class CameraScreen extends React.Component {
|
||||
</React.Fragment>
|
||||
);
|
||||
|
||||
renderCamera() {
|
||||
const { canDetectFaces, canDetectText, canDetectBarcode } = this.state;
|
||||
renderCamera = () => {
|
||||
const { canDetectFaces, canDetectText, canDetectBarcode } = state;
|
||||
|
||||
const drawFocusRingPosition = {
|
||||
top: this.state.autoFocusPoint.drawRectPosition.y - 32,
|
||||
left: this.state.autoFocusPoint.drawRectPosition.x - 32,
|
||||
top: state.autoFocusPoint.drawRectPosition.y - 32,
|
||||
left: state.autoFocusPoint.drawRectPosition.x - 32,
|
||||
};
|
||||
return (
|
||||
<RNCamera
|
||||
ref={ref => {
|
||||
this.camera = ref;
|
||||
cameraRef = ref;
|
||||
}}
|
||||
style={{
|
||||
flex: 1,
|
||||
justifyContent: 'space-between',
|
||||
}}
|
||||
type={this.state.type}
|
||||
flashMode={this.state.flash}
|
||||
autoFocus={this.state.autoFocus}
|
||||
autoFocusPointOfInterest={this.state.autoFocusPoint.normalized}
|
||||
zoom={this.state.zoom}
|
||||
whiteBalance={this.state.whiteBalance}
|
||||
ratio={this.state.ratio}
|
||||
focusDepth={this.state.depth}
|
||||
type={ state.type}
|
||||
flashMode={ state.flash}
|
||||
autoFocus={ state.autoFocus}
|
||||
autoFocusPointOfInterest={ state.autoFocusPoint.normalized}
|
||||
zoom={ state.zoom}
|
||||
whiteBalance={ state.whiteBalance}
|
||||
ratio={ state.ratio}
|
||||
focusDepth={ state.depth}
|
||||
androidCameraPermissionOptions={{
|
||||
title: 'Permission to use camera',
|
||||
message: 'We need your permission to use your camera',
|
||||
@ -306,13 +357,13 @@ export default class CameraScreen extends React.Component {
|
||||
? RNCamera.Constants.FaceDetection.Landmarks.all
|
||||
: undefined
|
||||
}
|
||||
onFacesDetected={canDetectFaces ? this.facesDetected : null}
|
||||
onTextRecognized={canDetectText ? this.textRecognized : null}
|
||||
onGoogleVisionBarcodesDetected={canDetectBarcode ? this.barcodeRecognized : null}
|
||||
onFacesDetected={canDetectFaces ? facesDetected() : null}
|
||||
onTextRecognized={canDetectText ? textRecognized() : null}
|
||||
onGoogleVisionBarcodesDetected={canDetectBarcode ? barcodeRecognized() : null}
|
||||
>
|
||||
<View style={StyleSheet.absoluteFill}>
|
||||
<View style={[styles.autoFocusBox, drawFocusRingPosition]} />
|
||||
<TouchableWithoutFeedback onPress={this.touchToFocus.bind(this)}>
|
||||
<TouchableWithoutFeedback onPress={() => touchToFocus()}>
|
||||
<View style={{ flex: 1 }} />
|
||||
</TouchableWithoutFeedback>
|
||||
</View>
|
||||
@ -332,14 +383,14 @@ export default class CameraScreen extends React.Component {
|
||||
justifyContent: 'space-around',
|
||||
}}
|
||||
>
|
||||
<TouchableOpacity style={styles.flipButton} onPress={this.toggleFacing.bind(this)}>
|
||||
<TouchableOpacity style={styles.flipButton} onPress={() => toggleFacing()}>
|
||||
<Text style={styles.flipText}> FLIP </Text>
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity style={styles.flipButton} onPress={this.toggleFlash.bind(this)}>
|
||||
<Text style={styles.flipText}> FLASH: {this.state.flash} </Text>
|
||||
<TouchableOpacity style={styles.flipButton} onPress={() => toggleFlash()}>
|
||||
<Text style={styles.flipText}> FLASH: { state.flash} </Text>
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity style={styles.flipButton} onPress={this.toggleWB.bind(this)}>
|
||||
<Text style={styles.flipText}> WB: {this.state.whiteBalance} </Text>
|
||||
<TouchableOpacity style={styles.flipButton} onPress={() => toggleWB()}>
|
||||
<Text style={styles.flipText}> WB: { state.whiteBalance} </Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
<View
|
||||
@ -349,17 +400,17 @@ export default class CameraScreen extends React.Component {
|
||||
justifyContent: 'space-around',
|
||||
}}
|
||||
>
|
||||
<TouchableOpacity onPress={this.toggle('canDetectFaces')} style={styles.flipButton}>
|
||||
<TouchableOpacity onPress={()=> toggle('canDetectFaces')} style={styles.flipButton}>
|
||||
<Text style={styles.flipText}>
|
||||
{!canDetectFaces ? 'Detect Faces' : 'Detecting Faces'}
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity onPress={this.toggle('canDetectText')} style={styles.flipButton}>
|
||||
<TouchableOpacity onPress={()=> toggle('canDetectText')} style={styles.flipButton}>
|
||||
<Text style={styles.flipText}>
|
||||
{!canDetectText ? 'Detect Text' : 'Detecting Text'}
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity onPress={this.toggle('canDetectBarcode')} style={styles.flipButton}>
|
||||
<TouchableOpacity onPress={()=> toggle('canDetectBarcode')} style={styles.flipButton}>
|
||||
<Text style={styles.flipText}>
|
||||
{!canDetectBarcode ? 'Detect Barcode' : 'Detecting Barcode'}
|
||||
</Text>
|
||||
@ -377,9 +428,9 @@ export default class CameraScreen extends React.Component {
|
||||
>
|
||||
<Slider
|
||||
style={{ width: 150, marginTop: 15, alignSelf: 'flex-end' }}
|
||||
onValueChange={this.setFocusDepth.bind(this)}
|
||||
onValueChange={() => setFocusDepth()}
|
||||
step={0.1}
|
||||
disabled={this.state.autoFocus === 'on'}
|
||||
disabled={ state.autoFocus === 'on'}
|
||||
/>
|
||||
</View>
|
||||
<View
|
||||
@ -396,20 +447,20 @@ export default class CameraScreen extends React.Component {
|
||||
{
|
||||
flex: 0.3,
|
||||
alignSelf: 'flex-end',
|
||||
backgroundColor: this.state.isRecording ? 'white' : 'darkred',
|
||||
backgroundColor: state.isRecording ? 'white' : 'darkred',
|
||||
},
|
||||
]}
|
||||
onPress={this.state.isRecording ? () => {} : this.takeVideo.bind(this)}
|
||||
onPress={ state.isRecording ? () => {} : () => takeVideo()}
|
||||
>
|
||||
{this.state.isRecording ? (
|
||||
{ state.isRecording ? (
|
||||
<Text style={styles.flipText}> ☕ </Text>
|
||||
) : (
|
||||
<Text style={styles.flipText}> REC </Text>
|
||||
)}
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
{this.state.zoom !== 0 && (
|
||||
<Text style={[styles.flipText, styles.zoomText]}>Zoom: {this.state.zoom}</Text>
|
||||
{ state.zoom !== 0 && (
|
||||
<Text style={[styles.flipText, styles.zoomText]}>Zoom: { state.zoom}</Text>
|
||||
)}
|
||||
<View
|
||||
style={{
|
||||
@ -421,43 +472,44 @@ export default class CameraScreen extends React.Component {
|
||||
>
|
||||
<TouchableOpacity
|
||||
style={[styles.flipButton, { flex: 0.1, alignSelf: 'flex-end' }]}
|
||||
onPress={this.zoomIn.bind(this)}
|
||||
onPress={() => zoomIn()}
|
||||
>
|
||||
<Text style={styles.flipText}> + </Text>
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity
|
||||
style={[styles.flipButton, { flex: 0.1, alignSelf: 'flex-end' }]}
|
||||
onPress={this.zoomOut.bind(this)}
|
||||
onPress={() => zoomOut()}
|
||||
>
|
||||
<Text style={styles.flipText}> - </Text>
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity
|
||||
style={[styles.flipButton, { flex: 0.25, alignSelf: 'flex-end' }]}
|
||||
onPress={this.toggleFocus.bind(this)}
|
||||
onPress={() => toggleFocus()}
|
||||
>
|
||||
<Text style={styles.flipText}> AF : {this.state.autoFocus} </Text>
|
||||
<Text style={styles.flipText}> AF : { state.autoFocus} </Text>
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity
|
||||
style={[styles.flipButton, styles.picButton, { flex: 0.3, alignSelf: 'flex-end' }]}
|
||||
onPress={this.takePicture.bind(this)}
|
||||
onPress={() => takePicture()}
|
||||
>
|
||||
<Text style={styles.flipText}> SNAP </Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
</View>
|
||||
{!!canDetectFaces && this.renderFaces()}
|
||||
{!!canDetectFaces && this.renderLandmarks()}
|
||||
{!!canDetectText && this.renderTextBlocks()}
|
||||
{!!canDetectBarcode && this.renderBarcodes()}
|
||||
{!!canDetectFaces && renderFaces()}
|
||||
{!!canDetectFaces && renderLandmarks()}
|
||||
{!!canDetectText && renderTextBlocks()}
|
||||
{!!canDetectBarcode && renderBarcodes()}
|
||||
</RNCamera>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
return <View style={styles.container}>{this.renderCamera()}</View>;
|
||||
}
|
||||
return (
|
||||
<View style={styles.container}>{() => renderCamera()}</View>
|
||||
);
|
||||
}
|
||||
|
||||
export default CameraScreen;
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
|
||||
814
src/RNCamera.js
814
src/RNCamera.js
@ -1,5 +1,5 @@
|
||||
// @flow
|
||||
import React from 'react';
|
||||
import React, { useState, useCallback, useMemo, useRef, useEffect } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import {
|
||||
findNodeHandle,
|
||||
@ -342,159 +342,258 @@ const mapValues = (input, mapper) => {
|
||||
return result;
|
||||
};
|
||||
|
||||
export default class Camera extends React.Component<PropsType, StateType> {
|
||||
static Constants = {
|
||||
Type: CameraManager.Type,
|
||||
FlashMode: CameraManager.FlashMode,
|
||||
AutoFocus: CameraManager.AutoFocus,
|
||||
WhiteBalance: CameraManager.WhiteBalance,
|
||||
VideoQuality: CameraManager.VideoQuality,
|
||||
VideoCodec: CameraManager.VideoCodec,
|
||||
BarCodeType: CameraManager.BarCodeType,
|
||||
GoogleVisionBarcodeDetection: CameraManager.GoogleVisionBarcodeDetection,
|
||||
FaceDetection: CameraManager.FaceDetection,
|
||||
CameraStatus,
|
||||
RecordAudioPermissionStatus: RecordAudioPermissionStatusEnum,
|
||||
VideoStabilization: CameraManager.VideoStabilization,
|
||||
Orientation: {
|
||||
auto: 'auto',
|
||||
landscapeLeft: 'landscapeLeft',
|
||||
landscapeRight: 'landscapeRight',
|
||||
portrait: 'portrait',
|
||||
portraitUpsideDown: 'portraitUpsideDown',
|
||||
export const Constants = {
|
||||
Type: CameraManager.Type,
|
||||
FlashMode: CameraManager.FlashMode,
|
||||
AutoFocus: CameraManager.AutoFocus,
|
||||
WhiteBalance: CameraManager.WhiteBalance,
|
||||
VideoQuality: CameraManager.VideoQuality,
|
||||
VideoCodec: CameraManager.VideoCodec,
|
||||
BarCodeType: CameraManager.BarCodeType,
|
||||
GoogleVisionBarcodeDetection: CameraManager.GoogleVisionBarcodeDetection,
|
||||
FaceDetection: CameraManager.FaceDetection,
|
||||
CameraStatus,
|
||||
RecordAudioPermissionStatus: RecordAudioPermissionStatusEnum,
|
||||
VideoStabilization: CameraManager.VideoStabilization,
|
||||
Orientation: {
|
||||
auto: 'auto',
|
||||
landscapeLeft: 'landscapeLeft',
|
||||
landscapeRight: 'landscapeRight',
|
||||
portrait: 'portrait',
|
||||
portraitUpsideDown: 'portraitUpsideDown',
|
||||
},
|
||||
};
|
||||
|
||||
export const ConversionTables = {
|
||||
type: CameraManager.Type,
|
||||
flashMode: CameraManager.FlashMode,
|
||||
exposure: CameraManager.Exposure,
|
||||
autoFocus: CameraManager.AutoFocus,
|
||||
whiteBalance: CameraManager.WhiteBalance,
|
||||
faceDetectionMode: (CameraManager.FaceDetection || {}).Mode,
|
||||
faceDetectionLandmarks: (CameraManager.FaceDetection || {}).Landmarks,
|
||||
faceDetectionClassifications: (CameraManager.FaceDetection || {}).Classifications,
|
||||
googleVisionBarcodeType: (CameraManager.GoogleVisionBarcodeDetection || {}).BarcodeType,
|
||||
videoStabilizationMode: CameraManager.VideoStabilization || {},
|
||||
};
|
||||
|
||||
export default function Camera(props) {
|
||||
let _cameraRef = useRef((ref) => setReference(ref));
|
||||
let _cameraHandle: ?number;
|
||||
let _lastEvents: { [string]: string };
|
||||
let _lastEventsTimes: { [string]: Date };
|
||||
let _isMounted: boolean;
|
||||
|
||||
const [isAuthorized, setIsAuthorized] = useState<boolean>(false);
|
||||
const [isAuthorizationChecked, setIsAuthorizationChecked] = useState<boolean>(false);
|
||||
const [recordAudioPermissionStatus, setRecordAudioPermissionStatus] = useState(
|
||||
RecordAudioPermissionStatusEnum.PENDING_AUTHORIZATION,
|
||||
);
|
||||
|
||||
const convertProp = useMemo((value: *, key: string) => {
|
||||
if (typeof value === 'string' && Camera.ConversionTables[key]) {
|
||||
return Camera.ConversionTables[key][value];
|
||||
}
|
||||
|
||||
return value;
|
||||
}, []);
|
||||
const convertNativeProps = useCallback(
|
||||
(nativeProps: PropsType) => () => {
|
||||
const { children, ...props } = nativeProps;
|
||||
const newProps = mapValues(props, convertProp);
|
||||
|
||||
if (props.onBarCodeRead) {
|
||||
newProps.barCodeScannerEnabled = true;
|
||||
}
|
||||
|
||||
if (props.onGoogleVisionBarcodesDetected) {
|
||||
newProps.googleVisionBarcodeDetectorEnabled = true;
|
||||
}
|
||||
|
||||
if (props.onFacesDetected) {
|
||||
newProps.faceDetectorEnabled = true;
|
||||
}
|
||||
|
||||
if (props.onTextRecognized) {
|
||||
newProps.textRecognizerEnabled = true;
|
||||
}
|
||||
|
||||
if (Platform.OS === 'ios') {
|
||||
delete newProps.googleVisionBarcodeMode;
|
||||
delete newProps.ratio;
|
||||
}
|
||||
|
||||
return newProps;
|
||||
},
|
||||
};
|
||||
|
||||
// Values under keys from this object will be transformed to native options
|
||||
static ConversionTables = {
|
||||
type: CameraManager.Type,
|
||||
flashMode: CameraManager.FlashMode,
|
||||
exposure: CameraManager.Exposure,
|
||||
autoFocus: CameraManager.AutoFocus,
|
||||
whiteBalance: CameraManager.WhiteBalance,
|
||||
faceDetectionMode: (CameraManager.FaceDetection || {}).Mode,
|
||||
faceDetectionLandmarks: (CameraManager.FaceDetection || {}).Landmarks,
|
||||
faceDetectionClassifications: (CameraManager.FaceDetection || {}).Classifications,
|
||||
googleVisionBarcodeType: (CameraManager.GoogleVisionBarcodeDetection || {}).BarcodeType,
|
||||
videoStabilizationMode: CameraManager.VideoStabilization || {},
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
...ViewPropTypes,
|
||||
zoom: PropTypes.number,
|
||||
maxZoom: PropTypes.number,
|
||||
ratio: PropTypes.string,
|
||||
focusDepth: PropTypes.number,
|
||||
onMountError: PropTypes.func,
|
||||
onCameraReady: PropTypes.func,
|
||||
onAudioInterrupted: PropTypes.func,
|
||||
onAudioConnected: PropTypes.func,
|
||||
onStatusChange: PropTypes.func,
|
||||
onBarCodeRead: PropTypes.func,
|
||||
onPictureTaken: PropTypes.func,
|
||||
onPictureSaved: PropTypes.func,
|
||||
onGoogleVisionBarcodesDetected: PropTypes.func,
|
||||
onFacesDetected: PropTypes.func,
|
||||
onTextRecognized: PropTypes.func,
|
||||
onSubjectAreaChanged: PropTypes.func,
|
||||
trackingEnabled: PropTypes.bool,
|
||||
faceDetectionMode: PropTypes.number,
|
||||
faceDetectionLandmarks: PropTypes.number,
|
||||
faceDetectionClassifications: PropTypes.number,
|
||||
barCodeTypes: PropTypes.arrayOf(PropTypes.string),
|
||||
googleVisionBarcodeType: PropTypes.number,
|
||||
googleVisionBarcodeMode: PropTypes.number,
|
||||
type: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
||||
cameraId: PropTypes.string,
|
||||
flashMode: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
||||
exposure: PropTypes.number,
|
||||
whiteBalance: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
||||
autoFocus: PropTypes.oneOfType([PropTypes.string, PropTypes.number, PropTypes.bool]),
|
||||
autoFocusPointOfInterest: PropTypes.shape({ x: PropTypes.number, y: PropTypes.number }),
|
||||
permissionDialogTitle: PropTypes.string,
|
||||
permissionDialogMessage: PropTypes.string,
|
||||
androidCameraPermissionOptions: Rationale,
|
||||
androidRecordAudioPermissionOptions: Rationale,
|
||||
notAuthorizedView: PropTypes.element,
|
||||
pendingAuthorizationView: PropTypes.element,
|
||||
captureAudio: PropTypes.bool,
|
||||
keepAudioSession: PropTypes.bool,
|
||||
useCamera2Api: PropTypes.bool,
|
||||
playSoundOnCapture: PropTypes.bool,
|
||||
videoStabilizationMode: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
||||
pictureSize: PropTypes.string,
|
||||
mirrorVideo: PropTypes.bool,
|
||||
rectOfInterest: PropTypes.any,
|
||||
defaultVideoQuality: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
||||
};
|
||||
|
||||
static defaultProps: Object = {
|
||||
zoom: 0,
|
||||
maxZoom: 0,
|
||||
ratio: '4:3',
|
||||
focusDepth: 0,
|
||||
type: CameraManager.Type.back,
|
||||
cameraId: null,
|
||||
autoFocus: CameraManager.AutoFocus.on,
|
||||
flashMode: CameraManager.FlashMode.off,
|
||||
exposure: -1,
|
||||
whiteBalance: CameraManager.WhiteBalance.auto,
|
||||
faceDetectionMode: (CameraManager.FaceDetection || {}).fast,
|
||||
barCodeTypes: Object.values(CameraManager.BarCodeType),
|
||||
googleVisionBarcodeType: ((CameraManager.GoogleVisionBarcodeDetection || {}).BarcodeType || {})
|
||||
.None,
|
||||
googleVisionBarcodeMode: ((CameraManager.GoogleVisionBarcodeDetection || {}).BarcodeMode || {})
|
||||
.NORMAL,
|
||||
faceDetectionLandmarks: ((CameraManager.FaceDetection || {}).Landmarks || {}).none,
|
||||
faceDetectionClassifications: ((CameraManager.FaceDetection || {}).Classifications || {}).none,
|
||||
permissionDialogTitle: '',
|
||||
permissionDialogMessage: '',
|
||||
androidCameraPermissionOptions: {
|
||||
title: '',
|
||||
message: '',
|
||||
[],
|
||||
);
|
||||
const hasFaCC = useMemo(() => typeof props.children === 'function', [props.children]);
|
||||
const setReference = useCallback(
|
||||
(ref) => {
|
||||
if (ref) {
|
||||
_cameraHandle = findNodeHandle(ref);
|
||||
} else {
|
||||
_cameraRef = null;
|
||||
_cameraHandle = null;
|
||||
}
|
||||
},
|
||||
androidRecordAudioPermissionOptions: {
|
||||
title: '',
|
||||
message: '',
|
||||
[_cameraRef, _cameraHandle],
|
||||
);
|
||||
const _onMountError = useCallback(
|
||||
({ nativeEvent }: EventCallbackArgumentsType) => {
|
||||
if (props.onMountError) {
|
||||
props.onMountError(nativeEvent);
|
||||
}
|
||||
},
|
||||
notAuthorizedView: (
|
||||
<View style={styles.authorizationContainer}>
|
||||
<Text style={styles.notAuthorizedText}>Camera not authorized</Text>
|
||||
</View>
|
||||
),
|
||||
pendingAuthorizationView: (
|
||||
<View style={styles.authorizationContainer}>
|
||||
<ActivityIndicator size="small" />
|
||||
</View>
|
||||
),
|
||||
captureAudio: true,
|
||||
keepAudioSession: false,
|
||||
useCamera2Api: false,
|
||||
playSoundOnCapture: false,
|
||||
pictureSize: 'None',
|
||||
videoStabilizationMode: 0,
|
||||
mirrorVideo: false,
|
||||
[props.onMountError],
|
||||
);
|
||||
const _onCameraReady = useCallback(() => {
|
||||
if (props.onCameraReady) {
|
||||
props.onCameraReady();
|
||||
}
|
||||
}, [props.onCameraReady]);
|
||||
const _onAudioInterrupted = useCallback(() => {
|
||||
if (props.onAudioInterrupted) {
|
||||
props.onAudioInterrupted();
|
||||
}
|
||||
}, [props.onAudioInterrupted]);
|
||||
const _onAudioConnected = useCallback(() => {
|
||||
if (props.onAudioConnected) {
|
||||
props.onAudioConnected();
|
||||
}
|
||||
}, [props.onAudioConnected]);
|
||||
const _onObjectDetected = useCallback(
|
||||
(callback: ?Function) => ({ nativeEvent }: EventCallbackArgumentsType) => {
|
||||
const { type } = nativeEvent;
|
||||
if (
|
||||
_lastEvents[type] &&
|
||||
_lastEventsTimes[type] &&
|
||||
JSON.stringify(nativeEvent) === _lastEvents[type] &&
|
||||
new Date() - _lastEventsTimes[type] < EventThrottleMs
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (callback) {
|
||||
callback(nativeEvent);
|
||||
_lastEventsTimes[type] = new Date();
|
||||
_lastEvents[type] = JSON.stringify(nativeEvent);
|
||||
}
|
||||
},
|
||||
[],
|
||||
);
|
||||
const _onPictureSaved = useCallback(
|
||||
({ nativeEvent }: EventCallbackArgumentsType) => {
|
||||
if (props.onPictureSaved) {
|
||||
props.onPictureSaved(nativeEvent);
|
||||
}
|
||||
},
|
||||
[props.onPictureSaved],
|
||||
);
|
||||
const _onSubjectAreaChanged = useCallback(
|
||||
e => () => {
|
||||
if (props.onSubjectAreaChanged) {
|
||||
props.onSubjectAreaChanged(e);
|
||||
}
|
||||
},
|
||||
[props.onSubjectAreaChanged],
|
||||
);
|
||||
const getStatus = useMemo(() => {
|
||||
if (isAuthorizationChecked === false) {
|
||||
return CameraStatus.PENDING_AUTHORIZATION;
|
||||
}
|
||||
return isAuthorized ? CameraStatus.READY : CameraStatus.NOT_AUTHORIZED;
|
||||
}, [isAuthorizationChecked, isAuthorized]);
|
||||
const renderChildren = useMemo(() => {
|
||||
if (hasFaCC) {
|
||||
return props.children({
|
||||
camera: this,
|
||||
status: getStatus,
|
||||
recordAudioPermissionStatus: recordAudioPermissionStatus,
|
||||
});
|
||||
}
|
||||
return props.children;
|
||||
}, []);
|
||||
|
||||
const { style, ...nativeProps } = convertNativeProps(props);
|
||||
|
||||
const arePermissionsGranted = async () => {
|
||||
const {
|
||||
permissionDialogTitle,
|
||||
permissionDialogMessage,
|
||||
androidCameraPermissionOptions,
|
||||
androidRecordAudioPermissionOptions,
|
||||
} = props;
|
||||
|
||||
let cameraPermissions = androidCameraPermissionOptions;
|
||||
let audioPermissions = androidRecordAudioPermissionOptions;
|
||||
if (permissionDialogTitle || permissionDialogMessage) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn(
|
||||
'permissionDialogTitle and permissionDialogMessage are deprecated. Please use androidCameraPermissionOptions instead.',
|
||||
);
|
||||
cameraPermissions = {
|
||||
...cameraPermissions,
|
||||
title: permissionDialogTitle,
|
||||
message: permissionDialogMessage,
|
||||
};
|
||||
audioPermissions = {
|
||||
...audioPermissions,
|
||||
title: permissionDialogTitle,
|
||||
message: permissionDialogMessage,
|
||||
};
|
||||
}
|
||||
|
||||
const { hasCameraPermissions, hasRecordAudioPermissions } = await requestPermissions(
|
||||
props.captureAudio,
|
||||
CameraManager,
|
||||
cameraPermissions,
|
||||
audioPermissions,
|
||||
);
|
||||
|
||||
const recordAudioPermissionStatus = hasRecordAudioPermissions
|
||||
? RecordAudioPermissionStatusEnum.AUTHORIZED
|
||||
: RecordAudioPermissionStatusEnum.NOT_AUTHORIZED;
|
||||
return { hasCameraPermissions, recordAudioPermissionStatus };
|
||||
};
|
||||
|
||||
_cameraRef: ?Object;
|
||||
_cameraHandle: ?number;
|
||||
_lastEvents: { [string]: string };
|
||||
_lastEventsTimes: { [string]: Date };
|
||||
_isMounted: boolean;
|
||||
const refreshAuthorizationStatus = async () => {
|
||||
const { hasCameraPermissions, recordAudioPermissionStatus } = await arePermissionsGranted();
|
||||
if (_isMounted === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
constructor(props: PropsType) {
|
||||
super(props);
|
||||
this._lastEvents = {};
|
||||
this._lastEventsTimes = {};
|
||||
this._isMounted = true;
|
||||
this.state = {
|
||||
isAuthorized: false,
|
||||
isAuthorizationChecked: false,
|
||||
recordAudioPermissionStatus: RecordAudioPermissionStatusEnum.PENDING_AUTHORIZATION,
|
||||
};
|
||||
}
|
||||
setIsAuthorized(hasCameraPermissions);
|
||||
setIsAuthorizationChecked(true);
|
||||
setRecordAudioPermissionStatus(recordAudioPermissionStatus);
|
||||
};
|
||||
|
||||
async takePictureAsync(options?: PictureOptions) {
|
||||
const _onStatusChange = useCallback(() => {
|
||||
if (props.onStatusChange) {
|
||||
props.onStatusChange({
|
||||
cameraStatus: getStatus(),
|
||||
recordAudioPermissionStatus,
|
||||
});
|
||||
}
|
||||
}, [props.onStatusChange]);
|
||||
|
||||
const stopRecording = useCallback(() => {
|
||||
CameraManager.stopRecording(_cameraHandle);
|
||||
}, [_cameraHandle]);
|
||||
|
||||
const pausePreview = useCallback(() => {
|
||||
CameraManager.pausePreview(_cameraHandle);
|
||||
}, [_cameraHandle]);
|
||||
|
||||
const isRecording = useCallback(() => {
|
||||
return CameraManager.isRecording(_cameraHandle);
|
||||
}, [_cameraHandle]);
|
||||
|
||||
const resumePreview = useCallback(() => {
|
||||
CameraManager.resumePreview(_cameraHandle);
|
||||
}, [_cameraHandle]);
|
||||
|
||||
const takePicture = async (options?: PictureOptions) => {
|
||||
if (!options) {
|
||||
options = {};
|
||||
}
|
||||
@ -519,35 +618,35 @@ export default class Camera extends React.Component<PropsType, StateType> {
|
||||
options.pauseAfterCapture = false;
|
||||
}
|
||||
|
||||
if (!this._cameraHandle) {
|
||||
if (!_cameraHandle) {
|
||||
throw 'Camera handle cannot be null';
|
||||
}
|
||||
|
||||
return await CameraManager.takePicture(options, this._cameraHandle);
|
||||
}
|
||||
return await CameraManager.takePicture(options, _cameraHandle);
|
||||
};
|
||||
|
||||
async getSupportedRatiosAsync() {
|
||||
const getSupportedRatiosAsync = async () => {
|
||||
if (Platform.OS === 'android') {
|
||||
return await CameraManager.getSupportedRatios(this._cameraHandle);
|
||||
return await CameraManager.getSupportedRatios(_cameraHandle);
|
||||
} else {
|
||||
throw new Error('Ratio is not supported on iOS');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
async getCameraIdsAsync() {
|
||||
const getCameraIdsAsync = async () => {
|
||||
if (Platform.OS === 'android') {
|
||||
return await CameraManager.getCameraIds(this._cameraHandle);
|
||||
return await CameraManager.getCameraIds(_cameraHandle);
|
||||
} else {
|
||||
return await CameraManager.getCameraIds(); // iOS does not need a camera instance
|
||||
}
|
||||
}
|
||||
|
||||
getAvailablePictureSizes = async (): string[] => {
|
||||
//$FlowFixMe
|
||||
return await CameraManager.getAvailablePictureSizes(this.props.ratio, this._cameraHandle);
|
||||
};
|
||||
|
||||
async recordAsync(options?: RecordingOptions) {
|
||||
const getAvailablePictureSizes = async (): string[] => {
|
||||
//$FlowFixMe
|
||||
return await CameraManager.getAvailablePictureSizes(props.ratio, _cameraHandle);
|
||||
};
|
||||
|
||||
const recordAsync = async (options?: RecordingOptions) => {
|
||||
if (!options || typeof options !== 'object') {
|
||||
options = {};
|
||||
} else if (typeof options.quality === 'string') {
|
||||
@ -573,8 +672,7 @@ export default class Camera extends React.Component<PropsType, StateType> {
|
||||
}
|
||||
}
|
||||
|
||||
const { recordAudioPermissionStatus } = this.state;
|
||||
const { captureAudio } = this.props;
|
||||
const { captureAudio } = props;
|
||||
|
||||
if (
|
||||
!captureAudio ||
|
||||
@ -593,269 +691,153 @@ export default class Camera extends React.Component<PropsType, StateType> {
|
||||
}
|
||||
}
|
||||
|
||||
return await CameraManager.record(options, this._cameraHandle);
|
||||
}
|
||||
|
||||
stopRecording() {
|
||||
CameraManager.stopRecording(this._cameraHandle);
|
||||
}
|
||||
|
||||
pausePreview() {
|
||||
CameraManager.pausePreview(this._cameraHandle);
|
||||
}
|
||||
|
||||
isRecording() {
|
||||
return CameraManager.isRecording(this._cameraHandle);
|
||||
}
|
||||
|
||||
resumePreview() {
|
||||
CameraManager.resumePreview(this._cameraHandle);
|
||||
}
|
||||
|
||||
_onMountError = ({ nativeEvent }: EventCallbackArgumentsType) => {
|
||||
if (this.props.onMountError) {
|
||||
this.props.onMountError(nativeEvent);
|
||||
}
|
||||
return await CameraManager.record(options, _cameraHandle);
|
||||
};
|
||||
|
||||
_onCameraReady = () => {
|
||||
if (this.props.onCameraReady) {
|
||||
this.props.onCameraReady();
|
||||
}
|
||||
};
|
||||
useEffect(() => {
|
||||
async function getPermissionsAndSetState() {
|
||||
const { hasCameraPermissions, recordAudioPermissionStatus } = await arePermissionsGranted();
|
||||
if (_isMounted === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
_onAudioInterrupted = () => {
|
||||
if (this.props.onAudioInterrupted) {
|
||||
this.props.onAudioInterrupted();
|
||||
}
|
||||
};
|
||||
|
||||
_onAudioConnected = () => {
|
||||
if (this.props.onAudioConnected) {
|
||||
this.props.onAudioConnected();
|
||||
}
|
||||
};
|
||||
|
||||
_onStatusChange = () => {
|
||||
if (this.props.onStatusChange) {
|
||||
this.props.onStatusChange({
|
||||
cameraStatus: this.getStatus(),
|
||||
recordAudioPermissionStatus: this.state.recordAudioPermissionStatus,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
_onPictureSaved = ({ nativeEvent }: EventCallbackArgumentsType) => {
|
||||
if (this.props.onPictureSaved) {
|
||||
this.props.onPictureSaved(nativeEvent);
|
||||
}
|
||||
};
|
||||
|
||||
_onObjectDetected = (callback: ?Function) => ({ nativeEvent }: EventCallbackArgumentsType) => {
|
||||
const { type } = nativeEvent;
|
||||
if (
|
||||
this._lastEvents[type] &&
|
||||
this._lastEventsTimes[type] &&
|
||||
JSON.stringify(nativeEvent) === this._lastEvents[type] &&
|
||||
new Date() - this._lastEventsTimes[type] < EventThrottleMs
|
||||
) {
|
||||
return;
|
||||
setIsAuthorized(hasCameraPermissions);
|
||||
setIsAuthorizationChecked(true);
|
||||
setRecordAudioPermissionStatus(recordAudioPermissionStatus);
|
||||
_onStatusChange();
|
||||
}
|
||||
|
||||
if (callback) {
|
||||
callback(nativeEvent);
|
||||
this._lastEventsTimes[type] = new Date();
|
||||
this._lastEvents[type] = JSON.stringify(nativeEvent);
|
||||
}
|
||||
};
|
||||
getPermissionsAndSetState();
|
||||
|
||||
_onSubjectAreaChanged = e => {
|
||||
if (this.props.onSubjectAreaChanged) {
|
||||
this.props.onSubjectAreaChanged(e);
|
||||
}
|
||||
};
|
||||
return () => {
|
||||
_isMounted = false;
|
||||
};
|
||||
}, []);
|
||||
|
||||
_setReference = (ref: ?Object) => {
|
||||
if (ref) {
|
||||
this._cameraRef = ref;
|
||||
this._cameraHandle = findNodeHandle(ref);
|
||||
} else {
|
||||
this._cameraRef = null;
|
||||
this._cameraHandle = null;
|
||||
}
|
||||
};
|
||||
|
||||
componentWillUnmount() {
|
||||
this._isMounted = false;
|
||||
}
|
||||
|
||||
async arePermissionsGranted() {
|
||||
const {
|
||||
permissionDialogTitle,
|
||||
permissionDialogMessage,
|
||||
androidCameraPermissionOptions,
|
||||
androidRecordAudioPermissionOptions,
|
||||
} = this.props;
|
||||
|
||||
let cameraPermissions = androidCameraPermissionOptions;
|
||||
let audioPermissions = androidRecordAudioPermissionOptions;
|
||||
if (permissionDialogTitle || permissionDialogMessage) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn(
|
||||
'permissionDialogTitle and permissionDialogMessage are deprecated. Please use androidCameraPermissionOptions instead.',
|
||||
);
|
||||
cameraPermissions = {
|
||||
...cameraPermissions,
|
||||
title: permissionDialogTitle,
|
||||
message: permissionDialogMessage,
|
||||
};
|
||||
audioPermissions = {
|
||||
...audioPermissions,
|
||||
title: permissionDialogTitle,
|
||||
message: permissionDialogMessage,
|
||||
};
|
||||
}
|
||||
|
||||
const { hasCameraPermissions, hasRecordAudioPermissions } = await requestPermissions(
|
||||
this.props.captureAudio,
|
||||
CameraManager,
|
||||
cameraPermissions,
|
||||
audioPermissions,
|
||||
if (isAuthorized || hasFaCC) {
|
||||
return (
|
||||
<View style={style}>
|
||||
<RNCamera
|
||||
{...nativeProps}
|
||||
style={StyleSheet.absoluteFill}
|
||||
ref={cameraRef}
|
||||
onMountError={_onMountError}
|
||||
onCameraReady={_onCameraReady}
|
||||
onAudioInterrupted={_onAudioInterrupted}
|
||||
onAudioConnected={_onAudioConnected}
|
||||
onGoogleVisionBarcodesDetected={_onObjectDetected(props.onGoogleVisionBarcodesDetected)}
|
||||
onBarCodeRead={_onObjectDetected(props.onBarCodeRead)}
|
||||
onFacesDetected={_onObjectDetected(props.onFacesDetected)}
|
||||
onTextRecognized={_onObjectDetected(props.onTextRecognized)}
|
||||
onPictureSaved={_onPictureSaved}
|
||||
onSubjectAreaChanged={_onSubjectAreaChanged}
|
||||
/>
|
||||
{renderChildren}
|
||||
</View>
|
||||
);
|
||||
|
||||
const recordAudioPermissionStatus = hasRecordAudioPermissions
|
||||
? RecordAudioPermissionStatusEnum.AUTHORIZED
|
||||
: RecordAudioPermissionStatusEnum.NOT_AUTHORIZED;
|
||||
return { hasCameraPermissions, recordAudioPermissionStatus };
|
||||
}
|
||||
|
||||
async refreshAuthorizationStatus() {
|
||||
const {
|
||||
hasCameraPermissions,
|
||||
recordAudioPermissionStatus,
|
||||
} = await this.arePermissionsGranted();
|
||||
if (this._isMounted === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.setState({
|
||||
isAuthorized: hasCameraPermissions,
|
||||
isAuthorizationChecked: true,
|
||||
recordAudioPermissionStatus,
|
||||
});
|
||||
}
|
||||
|
||||
async componentDidMount() {
|
||||
const {
|
||||
hasCameraPermissions,
|
||||
recordAudioPermissionStatus,
|
||||
} = await this.arePermissionsGranted();
|
||||
if (this._isMounted === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.setState(
|
||||
{
|
||||
isAuthorized: hasCameraPermissions,
|
||||
isAuthorizationChecked: true,
|
||||
recordAudioPermissionStatus,
|
||||
},
|
||||
this._onStatusChange,
|
||||
);
|
||||
}
|
||||
|
||||
getStatus = (): Status => {
|
||||
const { isAuthorized, isAuthorizationChecked } = this.state;
|
||||
if (isAuthorizationChecked === false) {
|
||||
return CameraStatus.PENDING_AUTHORIZATION;
|
||||
}
|
||||
return isAuthorized ? CameraStatus.READY : CameraStatus.NOT_AUTHORIZED;
|
||||
};
|
||||
|
||||
// FaCC = Function as Child Component;
|
||||
hasFaCC = (): * => typeof this.props.children === 'function';
|
||||
|
||||
renderChildren = (): * => {
|
||||
if (this.hasFaCC()) {
|
||||
return this.props.children({
|
||||
camera: this,
|
||||
status: this.getStatus(),
|
||||
recordAudioPermissionStatus: this.state.recordAudioPermissionStatus,
|
||||
});
|
||||
}
|
||||
return this.props.children;
|
||||
};
|
||||
|
||||
render() {
|
||||
const { style, ...nativeProps } = this._convertNativeProps(this.props);
|
||||
|
||||
if (this.state.isAuthorized || this.hasFaCC()) {
|
||||
return (
|
||||
<View style={style}>
|
||||
<RNCamera
|
||||
{...nativeProps}
|
||||
style={StyleSheet.absoluteFill}
|
||||
ref={this._setReference}
|
||||
onMountError={this._onMountError}
|
||||
onCameraReady={this._onCameraReady}
|
||||
onAudioInterrupted={this._onAudioInterrupted}
|
||||
onAudioConnected={this._onAudioConnected}
|
||||
onGoogleVisionBarcodesDetected={this._onObjectDetected(
|
||||
this.props.onGoogleVisionBarcodesDetected,
|
||||
)}
|
||||
onBarCodeRead={this._onObjectDetected(this.props.onBarCodeRead)}
|
||||
onFacesDetected={this._onObjectDetected(this.props.onFacesDetected)}
|
||||
onTextRecognized={this._onObjectDetected(this.props.onTextRecognized)}
|
||||
onPictureSaved={this._onPictureSaved}
|
||||
onSubjectAreaChanged={this._onSubjectAreaChanged}
|
||||
/>
|
||||
{this.renderChildren()}
|
||||
</View>
|
||||
);
|
||||
} else if (!this.state.isAuthorizationChecked) {
|
||||
return this.props.pendingAuthorizationView;
|
||||
} else {
|
||||
return this.props.notAuthorizedView;
|
||||
}
|
||||
}
|
||||
|
||||
_convertNativeProps({ children, ...props }: PropsType) {
|
||||
const newProps = mapValues(props, this._convertProp);
|
||||
|
||||
if (props.onBarCodeRead) {
|
||||
newProps.barCodeScannerEnabled = true;
|
||||
}
|
||||
|
||||
if (props.onGoogleVisionBarcodesDetected) {
|
||||
newProps.googleVisionBarcodeDetectorEnabled = true;
|
||||
}
|
||||
|
||||
if (props.onFacesDetected) {
|
||||
newProps.faceDetectorEnabled = true;
|
||||
}
|
||||
|
||||
if (props.onTextRecognized) {
|
||||
newProps.textRecognizerEnabled = true;
|
||||
}
|
||||
|
||||
if (Platform.OS === 'ios') {
|
||||
delete newProps.googleVisionBarcodeMode;
|
||||
delete newProps.ratio;
|
||||
}
|
||||
|
||||
return newProps;
|
||||
}
|
||||
|
||||
_convertProp(value: *, key: string): * {
|
||||
if (typeof value === 'string' && Camera.ConversionTables[key]) {
|
||||
return Camera.ConversionTables[key][value];
|
||||
}
|
||||
|
||||
return value;
|
||||
} else if (!isAuthorizationChecked) {
|
||||
return props.pendingAuthorizationView;
|
||||
} else {
|
||||
return props.notAuthorizedView;
|
||||
}
|
||||
}
|
||||
|
||||
export const Constants = Camera.Constants;
|
||||
Camera.propTypes = {
|
||||
...ViewPropTypes,
|
||||
zoom: PropTypes.number,
|
||||
maxZoom: PropTypes.number,
|
||||
ratio: PropTypes.string,
|
||||
focusDepth: PropTypes.number,
|
||||
onMountError: PropTypes.func,
|
||||
onCameraReady: PropTypes.func,
|
||||
onAudioInterrupted: PropTypes.func,
|
||||
onAudioConnected: PropTypes.func,
|
||||
onStatusChange: PropTypes.func,
|
||||
onBarCodeRead: PropTypes.func,
|
||||
onPictureTaken: PropTypes.func,
|
||||
onPictureSaved: PropTypes.func,
|
||||
onGoogleVisionBarcodesDetected: PropTypes.func,
|
||||
onFacesDetected: PropTypes.func,
|
||||
onTextRecognized: PropTypes.func,
|
||||
onSubjectAreaChanged: PropTypes.func,
|
||||
trackingEnabled: PropTypes.bool,
|
||||
faceDetectionMode: PropTypes.number,
|
||||
faceDetectionLandmarks: PropTypes.number,
|
||||
faceDetectionClassifications: PropTypes.number,
|
||||
barCodeTypes: PropTypes.arrayOf(PropTypes.string),
|
||||
googleVisionBarcodeType: PropTypes.number,
|
||||
googleVisionBarcodeMode: PropTypes.number,
|
||||
type: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
||||
cameraId: PropTypes.string,
|
||||
flashMode: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
||||
exposure: PropTypes.number,
|
||||
whiteBalance: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
||||
autoFocus: PropTypes.oneOfType([PropTypes.string, PropTypes.number, PropTypes.bool]),
|
||||
autoFocusPointOfInterest: PropTypes.shape({ x: PropTypes.number, y: PropTypes.number }),
|
||||
permissionDialogTitle: PropTypes.string,
|
||||
permissionDialogMessage: PropTypes.string,
|
||||
androidCameraPermissionOptions: Rationale,
|
||||
androidRecordAudioPermissionOptions: Rationale,
|
||||
notAuthorizedView: PropTypes.element,
|
||||
pendingAuthorizationView: PropTypes.element,
|
||||
captureAudio: PropTypes.bool,
|
||||
keepAudioSession: PropTypes.bool,
|
||||
useCamera2Api: PropTypes.bool,
|
||||
playSoundOnCapture: PropTypes.bool,
|
||||
videoStabilizationMode: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
||||
pictureSize: PropTypes.string,
|
||||
mirrorVideo: PropTypes.bool,
|
||||
rectOfInterest: PropTypes.any,
|
||||
defaultVideoQuality: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
||||
};
|
||||
|
||||
Camera.defaultProps = {
|
||||
zoom: 0,
|
||||
maxZoom: 0,
|
||||
ratio: '4:3',
|
||||
focusDepth: 0,
|
||||
type: CameraManager.Type.back,
|
||||
cameraId: null,
|
||||
autoFocus: CameraManager.AutoFocus.on,
|
||||
flashMode: CameraManager.FlashMode.off,
|
||||
exposure: -1,
|
||||
whiteBalance: CameraManager.WhiteBalance.auto,
|
||||
faceDetectionMode: (CameraManager.FaceDetection || {}).fast,
|
||||
barCodeTypes: Object.values(CameraManager.BarCodeType),
|
||||
googleVisionBarcodeType: ((CameraManager.GoogleVisionBarcodeDetection || {}).BarcodeType || {})
|
||||
.None,
|
||||
googleVisionBarcodeMode: ((CameraManager.GoogleVisionBarcodeDetection || {}).BarcodeMode || {})
|
||||
.NORMAL,
|
||||
faceDetectionLandmarks: ((CameraManager.FaceDetection || {}).Landmarks || {}).none,
|
||||
faceDetectionClassifications: ((CameraManager.FaceDetection || {}).Classifications || {}).none,
|
||||
permissionDialogTitle: '',
|
||||
permissionDialogMessage: '',
|
||||
androidCameraPermissionOptions: {
|
||||
title: '',
|
||||
message: '',
|
||||
},
|
||||
androidRecordAudioPermissionOptions: {
|
||||
title: '',
|
||||
message: '',
|
||||
},
|
||||
notAuthorizedView: (
|
||||
<View style={styles.authorizationContainer}>
|
||||
<Text style={styles.notAuthorizedText}>Camera not authorized</Text>
|
||||
</View>
|
||||
),
|
||||
pendingAuthorizationView: (
|
||||
<View style={styles.authorizationContainer}>
|
||||
<ActivityIndicator size="small" />
|
||||
</View>
|
||||
),
|
||||
captureAudio: true,
|
||||
keepAudioSession: false,
|
||||
useCamera2Api: false,
|
||||
playSoundOnCapture: false,
|
||||
pictureSize: 'None',
|
||||
videoStabilizationMode: 0,
|
||||
mirrorVideo: false,
|
||||
};
|
||||
|
||||
const RNCamera = requireNativeComponent('RNCamera', Camera, {
|
||||
nativeOnly: {
|
||||
@ -880,4 +862,4 @@ const RNCamera = requireNativeComponent('RNCamera', Camera, {
|
||||
renderToHardwareTextureAndroid: true,
|
||||
testID: true,
|
||||
},
|
||||
});
|
||||
});
|
||||
@ -1,6 +1,10 @@
|
||||
// @flow
|
||||
import RNCamera, { type Status as _CameraStatus } from './RNCamera';
|
||||
import RNCamera, {
|
||||
type Status as _CameraStatus,
|
||||
Constants as RNCConstants,
|
||||
ConversionTables,
|
||||
} from './RNCamera';
|
||||
import FaceDetector from './FaceDetector';
|
||||
|
||||
export type CameraStatus = _CameraStatus;
|
||||
export { RNCamera, FaceDetector };
|
||||
export { RNCamera, FaceDetector, RNCConstants, ConversionTables };
|
||||
|
||||
70
types/index.d.ts
vendored
70
types/index.d.ts
vendored
@ -94,13 +94,11 @@ type RecordAudioPermissionStatus = Readonly<
|
||||
NOT_AUTHORIZED: 'NOT_AUTHORIZED';
|
||||
}>
|
||||
>;
|
||||
type FaCC = (
|
||||
params: {
|
||||
type FaCC = (params: {
|
||||
camera: RNCamera;
|
||||
status: keyof CameraStatus;
|
||||
recordAudioPermissionStatus: keyof RecordAudioPermissionStatus;
|
||||
},
|
||||
) => JSX.Element;
|
||||
}) => JSX.Element;
|
||||
|
||||
export interface Constants {
|
||||
CameraStatus: CameraStatus;
|
||||
@ -135,7 +133,7 @@ export interface RNCameraProps {
|
||||
autoFocus?: keyof AutoFocus;
|
||||
autoFocusPointOfInterest?: Point;
|
||||
/* iOS only */
|
||||
onSubjectAreaChanged?: (event: { nativeEvent: { prevPoint: { x: number; y: number; } } }) => void;
|
||||
onSubjectAreaChanged?: (event: { nativeEvent: { prevPoint: { x: number; y: number } } }) => void;
|
||||
type?: keyof CameraType;
|
||||
flashMode?: keyof FlashMode;
|
||||
notAuthorizedView?: JSX.Element;
|
||||
@ -178,12 +176,12 @@ export interface RNCameraProps {
|
||||
* @description For Android use `{ width: number, height: number, origin: Array<Point<string>> }`
|
||||
* @description For iOS use `{ origin: Point<string>, size: Size<string> }`
|
||||
*/
|
||||
bounds: { width: number, height: number, origin: Array<Point<string>> } | { origin: Point<string>; size: Size<string> };
|
||||
bounds:
|
||||
| { width: number; height: number; origin: Array<Point<string>> }
|
||||
| { origin: Point<string>; size: Size<string> };
|
||||
}): void;
|
||||
|
||||
onGoogleVisionBarcodesDetected?(event: {
|
||||
barcodes: Barcode[];
|
||||
}): void;
|
||||
onGoogleVisionBarcodesDetected?(event: { barcodes: Barcode[] }): void;
|
||||
|
||||
// -- FACE DETECTION PROPS
|
||||
|
||||
@ -257,9 +255,9 @@ export interface Barcode {
|
||||
firstName?: string;
|
||||
lastName?: string;
|
||||
middleName?: string;
|
||||
prefix?:string;
|
||||
pronounciation?:string;
|
||||
suffix?:string;
|
||||
prefix?: string;
|
||||
pronounciation?: string;
|
||||
suffix?: string;
|
||||
formattedName?: string;
|
||||
};
|
||||
phone?: Phone;
|
||||
@ -298,29 +296,29 @@ export interface Barcode {
|
||||
}
|
||||
|
||||
export type BarcodeType =
|
||||
|"EMAIL"
|
||||
|"PHONE"
|
||||
|"CALENDAR_EVENT"
|
||||
|"DRIVER_LICENSE"
|
||||
|"GEO"
|
||||
|"SMS"
|
||||
|"CONTACT_INFO"
|
||||
|"WIFI"
|
||||
|"TEXT"
|
||||
|"ISBN"
|
||||
|"PRODUCT"
|
||||
|"URL"
|
||||
|'EMAIL'
|
||||
|'PHONE'
|
||||
|'CALENDAR_EVENT'
|
||||
|'DRIVER_LICENSE'
|
||||
|'GEO'
|
||||
|'SMS'
|
||||
|'CONTACT_INFO'
|
||||
|'WIFI'
|
||||
|'TEXT'
|
||||
|'ISBN'
|
||||
|'PRODUCT'
|
||||
|'URL'
|
||||
|
||||
export interface Email {
|
||||
address?: string;
|
||||
body?: string;
|
||||
subject?: string;
|
||||
emailType?: "UNKNOWN" | "Work" | "Home";
|
||||
emailType?: 'UNKNOWN' | 'Work' | 'Home';
|
||||
}
|
||||
|
||||
export interface Phone {
|
||||
number?: string;
|
||||
phoneType?: "UNKNOWN" | "Work" | "Home" | "Fax" | "Mobile";
|
||||
phoneType?: 'UNKNOWN' | 'Work' | 'Home' | 'Fax' | 'Mobile';
|
||||
}
|
||||
|
||||
export interface Face {
|
||||
@ -409,26 +407,8 @@ export interface RecordResponse {
|
||||
codec: VideoCodec[keyof VideoCodec];
|
||||
}
|
||||
|
||||
export class RNCamera extends Component<RNCameraProps & ViewProperties> {
|
||||
static Constants: Constants;
|
||||
export function RNCamera(props: RNCameraProps & ViewProperties): Component;
|
||||
|
||||
_cameraRef: null | NativeMethodsMixinStatic;
|
||||
_cameraHandle: ReturnType<typeof findNodeHandle>;
|
||||
|
||||
takePictureAsync(options?: TakePictureOptions): Promise<TakePictureResponse>;
|
||||
recordAsync(options?: RecordOptions): Promise<RecordResponse>;
|
||||
refreshAuthorizationStatus(): Promise<void>;
|
||||
stopRecording(): void;
|
||||
pausePreview(): void;
|
||||
resumePreview(): void;
|
||||
getAvailablePictureSizes(): Promise<string[]>;
|
||||
|
||||
/** Android only */
|
||||
getSupportedRatiosAsync(): Promise<string[]>;
|
||||
|
||||
/** iOS only */
|
||||
isRecording(): Promise<boolean>;
|
||||
}
|
||||
|
||||
interface DetectionOptions {
|
||||
mode?: keyof FaceDetectionMode;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user