UNPKG

@ha_tecno/react-native-sdk

Version:

React Native SDK for biometric authentication, liveness detection, and fingerprint recognition

237 lines (236 loc) 8.37 kB
"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