@ha_tecno/react-native-sdk
Version:
React Native SDK for biometric authentication, liveness detection, and fingerprint recognition
237 lines (236 loc) • 8.37 kB
JavaScript
"use strict";
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { ActivityIndicator, Platform, StyleSheet, View } from 'react-native';
import { getModel, useManufacturer } from 'react-native-device-info';
import { Gesture, GestureDetector, GestureHandlerRootView } from 'react-native-gesture-handler';
import { runOnJS } from 'react-native-reanimated';
import { Camera as VisionCamera, useCameraDevice, useCameraFormat, useCameraPermission } from 'react-native-vision-camera';
import { useFocusProcessor } from "../../processors/focusProcessor.js";
import { cameraService } from "../../services/cameraService.js";
import { CaptureButton } from "../CaptureButton/CaptureButton.js";
import { Gabarito } from "../Gabarito/Gabarito.js";
import { VideoButton } from "../VideoButton/VideoButton.js";
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
export const Camera = ({
serviceType,
onCapturePhoto,
onCaptureVideo,
totalRemainingSeconds = 10,
disabled = false,
captureButtonVisible = false,
secondsStartIndicator = 5,
torch = 'on'
}) => {
const [isRecording, setIsRecording] = useState(false);
const [remainingSeconds, setRemainingSeconds] = useState(totalRemainingSeconds);
const [video, setVideo] = useState();
const [deviceConfig, setDeviceConfig] = useState();
const [focusScoreParams, setFocusScoreParams] = useState();
const {
result: manufacturer
} = useManufacturer();
const {
focusProcessor,
focusScore,
focusScoreRef
} = useFocusProcessor();
const cameraRef = useRef(null);
const {
hasPermission,
requestPermission
} = useCameraPermission();
const device = useCameraDevice('back');
const cameraFormat = useCameraFormat(device, [{
photoResolution: {
height: 1280,
width: 720
},
videoResolution: {
height: 1280,
width: 720
},
autoFocusSystem: 'phase-detection'
}]);
const cameraPermission = useCallback(async () => {
if (!hasPermission) {
await requestPermission();
}
}, [hasPermission, requestPermission]);
useEffect(() => {
cameraPermission();
}, [cameraPermission]);
const getCameraDeviceConfig = useCallback(async () => {
const data = await cameraService.getCameraDeviceConfig();
if (data != null) {
const {
deviceParamsConfig
} = data;
const deviceModel = getModel();
const deviceType = Platform.OS;
const paramsConfigDeviceType = deviceParamsConfig?.filter(config => config.deviceType === deviceType && config.serviceType === serviceType);
const configDeviceModel = paramsConfigDeviceType.find(config => config.deviceModels.includes(deviceModel) || config.deviceModels.includes('default'));
setDeviceConfig(configDeviceModel);
}
}, [serviceType]);
const getFocusScoreParams = useCallback(async () => {
const data = await cameraService.getFocusScoreParams();
if (data != null) {
setFocusScoreParams(data);
}
}, []);
useEffect(() => {
getCameraDeviceConfig();
if (serviceType === 'finger') {
getFocusScoreParams();
}
}, [getCameraDeviceConfig, getFocusScoreParams, serviceType]);
useEffect(() => {
if (video && onCaptureVideo) {
onCaptureVideo(video.path);
}
}, [video, onCaptureVideo]);
const intervalRef = useRef(null);
const startTimer = useCallback(() => {
if (intervalRef.current) clearInterval(intervalRef.current);
intervalRef.current = setInterval(() => {
setRemainingSeconds(s => s - 1);
}, 1000);
}, []);
const stopTimer = useCallback(() => {
if (intervalRef.current) {
clearInterval(intervalRef.current);
intervalRef.current = null;
}
setRemainingSeconds(totalRemainingSeconds);
}, [totalRemainingSeconds]);
const startRecording = useCallback(() => {
setIsRecording(true);
startTimer();
cameraRef.current?.startRecording({
flash: 'off',
videoCodec: 'h265',
fileType: 'mp4',
onRecordingFinished: file => setVideo(file),
onRecordingError: error => {
console.error('Recording error:', error);
setIsRecording(false);
stopTimer();
}
});
}, [startTimer, stopTimer]);
const stopRecording = useCallback(() => {
stopTimer();
setIsRecording(false);
cameraRef.current?.stopRecording();
}, [stopTimer]);
useEffect(() => {
if (remainingSeconds === 0 && isRecording) {
stopRecording();
}
}, [remainingSeconds, isRecording, stopRecording]);
const counterRef = useRef(0);
const takePhoto = useCallback(async () => {
counterRef.current = 0;
const photo = await cameraRef.current?.takePhoto();
if (photo && onCapturePhoto && deviceConfig) onCapturePhoto(photo.path, focusScoreRef.current, deviceConfig);
}, [onCapturePhoto, focusScoreRef, deviceConfig]);
const isMotorola = useMemo(() => manufacturer.toLowerCase() === 'motorola', [manufacturer]);
const meetsFocusConditions = useMemo(() => {
if (!focusScoreParams) return false;
const getMinMax = (base, motoBase) => {
const minKey = isMotorola ? `${motoBase}_min` : `${base}_min`;
const maxKey = isMotorola ? `${motoBase}_max` : `${base}_max`;
return [focusScoreParams[minKey] ?? 0, focusScoreParams[maxKey] ?? 0];
};
const [meanMin, meanMax] = getMinMax('mean_score', 'mean_score_moto');
const [stdMin, stdMax] = getMinMax('std_score', 'std_score_moto');
const [varMin, varMax] = getMinMax('var_score', 'var_score_moto');
return focusScore.meanScore >= meanMin && focusScore.meanScore <= meanMax && focusScore.stdScore >= stdMin && focusScore.stdScore <= stdMax && focusScore.varScore >= varMin && focusScore.varScore <= varMax;
}, [focusScore, focusScoreParams, isMotorola]);
useEffect(() => {
if (serviceType === 'finger') {
const interval = setInterval(() => {
if (meetsFocusConditions && !captureButtonVisible && secondsStartIndicator === 0 && !disabled) {
counterRef.current += 1;
if (counterRef.current >= 5) {
takePhoto();
counterRef.current = 0;
}
} else {
counterRef.current = 0;
}
}, 500);
return () => clearInterval(interval);
}
return;
}, [meetsFocusConditions, takePhoto, serviceType, captureButtonVisible, disabled, secondsStartIndicator]);
const focus = useCallback(point => {
const camera = cameraRef.current;
if (camera != null) {
camera.focus(point);
}
}, []);
const gesture = Gesture.Tap().onBegin(({
x,
y
}) => {
runOnJS(focus)({
x,
y
});
});
if (!hasPermission || !device) {
return /*#__PURE__*/_jsx(View, {
style: StyleSheet.absoluteFill,
children: /*#__PURE__*/_jsx(ActivityIndicator, {
size: "large",
color: "#fff"
})
});
}
return /*#__PURE__*/_jsxs(GestureHandlerRootView, {
children: [/*#__PURE__*/_jsx(GestureDetector, {
gesture: gesture,
children: /*#__PURE__*/_jsxs(View, {
style: styles.cameraContainer,
children: [/*#__PURE__*/_jsx(VisionCamera, {
frameProcessor: serviceType === 'finger' ? focusProcessor : undefined,
ref: cameraRef,
style: StyleSheet.absoluteFillObject,
device: device,
format: cameraFormat,
fps: 30,
isActive: true,
torch: torch,
zoom: deviceConfig?.zoom ?? 1,
exposure: deviceConfig?.exposure ?? 0,
photo: serviceType === 'finger',
video: serviceType === 'liveness',
pixelFormat: Platform.OS === 'android' ? 'yuv' : 'rgb'
}), serviceType === 'finger' && /*#__PURE__*/_jsx(Gabarito, {})]
})
}), /*#__PURE__*/_jsx(View, {
style: styles.containerCapture,
children: serviceType === 'finger' ? /*#__PURE__*/_jsx(CaptureButton, {
onPress: takePhoto,
disabled: disabled
}) : /*#__PURE__*/_jsx(VideoButton, {
onPress: startRecording,
disabled: isRecording
})
})]
});
};
const styles = StyleSheet.create({
cameraContainer: {
flex: 1,
justifyContent: 'center'
},
containerCapture: {
position: 'absolute',
width: '100%',
alignItems: 'center',
bottom: Platform.OS === 'ios' ? 50 : 20
}
});
//# sourceMappingURL=Camera.js.map