@trustchex/react-native-sdk 1.409.0 → 1.472.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/android/src/main/java/com/trustchex/reactnativesdk/TrustchexSDKModule.kt +2 -8
- package/android/src/main/java/com/trustchex/reactnativesdk/camera/TrustchexCameraView.kt +102 -4
- package/ios/Camera/TrustchexCameraView.swift +151 -18
- package/ios/Permission/PermissionModule.m +22 -0
- package/ios/Permission/PermissionModule.swift +67 -0
- package/lib/module/Screens/Debug/NFCScanTestScreen.js +635 -0
- package/lib/module/Screens/Dynamic/ContractAcceptanceScreen.js +1 -4
- package/lib/module/Screens/Dynamic/IdentityDocumentEIDScanningScreen.js +17 -4
- package/lib/module/Screens/Dynamic/LivenessDetectionScreen.js +125 -25
- package/lib/module/Screens/Dynamic/VerbalConsentScreen.js +1079 -0
- package/lib/module/Screens/Dynamic/VideoCallScreen.js +10 -13
- package/lib/module/Screens/Static/ResultScreen.js +131 -22
- package/lib/module/Screens/Static/VerificationSessionCheckScreen.js +8 -0
- package/lib/module/Shared/Animations/recording.json +1 -0
- package/lib/module/Shared/Components/DebugNavigationPanel.js +69 -71
- package/lib/module/Shared/Components/EIDScanner.js +222 -115
- package/lib/module/Shared/Components/FaceCamera.js +14 -8
- package/lib/module/Shared/Components/IdentityDocumentCamera.flows.js +5 -3
- package/lib/module/Shared/Components/IdentityDocumentCamera.js +60 -42
- package/lib/module/Shared/Components/IdentityDocumentCamera.utils.js +13 -4
- package/lib/module/Shared/Components/NavigationManager.js +24 -16
- package/lib/module/Shared/EIDReader/aesSecureMessagingWrapper.js +51 -0
- package/lib/module/Shared/EIDReader/apduLevelPACECapable.js +3 -0
- package/lib/module/Shared/EIDReader/bacKey.js +16 -2
- package/lib/module/Shared/EIDReader/eidReader.js +354 -13
- package/lib/module/Shared/EIDReader/eidService.js +25 -1
- package/lib/module/Shared/EIDReader/nfcManagerCardService.js +4 -7
- package/lib/module/Shared/EIDReader/paceInfo.js +85 -0
- package/lib/module/Shared/EIDReader/paceKeySpec.js +51 -0
- package/lib/module/Shared/EIDReader/protocol/paceAPDUSender.js +100 -0
- package/lib/module/Shared/EIDReader/protocol/paceProtocol.js +655 -0
- package/lib/module/Shared/EIDReader/protocol/paceResult.js +37 -0
- package/lib/module/Shared/EIDReader/secureMessagingWrapper.js +27 -4
- package/lib/module/Shared/EIDReader/smartcards/commandAPDU.js +2 -1
- package/lib/module/Shared/EIDReader/tlv/tlv.helpers.js +1 -1
- package/lib/module/Shared/EIDReader/tlv/tlv.utils.js +6 -3
- package/lib/module/Shared/EIDReader/utils/aesCrypto.utils.js +189 -0
- package/lib/module/Shared/Libs/PERMISSIONS_MANAGER.md +268 -0
- package/lib/module/Shared/Libs/analytics.utils.js +4 -0
- package/lib/module/Shared/Libs/contains.js +1 -40
- package/lib/module/Shared/Libs/country-display.utils.js +34 -0
- package/lib/module/Shared/Libs/demo.utils.js +8 -0
- package/lib/module/Shared/Libs/index.js +20 -0
- package/lib/module/Shared/Libs/mrz.utils.js +3 -2
- package/lib/module/Shared/Libs/permissions.utils.js +199 -0
- package/lib/module/Shared/Libs/status-bar.utils.js +4 -2
- package/lib/module/Shared/Types/analytics.types.js +2 -0
- package/lib/module/Translation/Resources/en.js +42 -2
- package/lib/module/Translation/Resources/tr.js +42 -2
- package/lib/module/Trustchex.js +54 -20
- package/lib/module/version.js +1 -1
- package/lib/typescript/src/Screens/Debug/NFCScanTestScreen.d.ts +3 -0
- package/lib/typescript/src/Screens/Debug/NFCScanTestScreen.d.ts.map +1 -0
- package/lib/typescript/src/Screens/Dynamic/ContractAcceptanceScreen.d.ts.map +1 -1
- package/lib/typescript/src/Screens/Dynamic/IdentityDocumentEIDScanningScreen.d.ts.map +1 -1
- package/lib/typescript/src/Screens/Dynamic/LivenessDetectionScreen.d.ts.map +1 -1
- package/lib/typescript/src/Screens/Dynamic/VerbalConsentScreen.d.ts +3 -0
- package/lib/typescript/src/Screens/Dynamic/VerbalConsentScreen.d.ts.map +1 -0
- package/lib/typescript/src/Screens/Dynamic/VideoCallScreen.d.ts.map +1 -1
- package/lib/typescript/src/Screens/Static/ResultScreen.d.ts.map +1 -1
- package/lib/typescript/src/Screens/Static/VerificationSessionCheckScreen.d.ts.map +1 -1
- package/lib/typescript/src/Shared/Components/DebugNavigationPanel.d.ts.map +1 -1
- package/lib/typescript/src/Shared/Components/EIDScanner.d.ts.map +1 -1
- package/lib/typescript/src/Shared/Components/FaceCamera.d.ts.map +1 -1
- package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.d.ts.map +1 -1
- package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.flows.d.ts +1 -1
- package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.flows.d.ts.map +1 -1
- package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.utils.d.ts +5 -0
- package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.utils.d.ts.map +1 -1
- package/lib/typescript/src/Shared/Components/NavigationManager.d.ts.map +1 -1
- package/lib/typescript/src/Shared/EIDReader/aesSecureMessagingWrapper.d.ts +18 -0
- package/lib/typescript/src/Shared/EIDReader/aesSecureMessagingWrapper.d.ts.map +1 -0
- package/lib/typescript/src/Shared/EIDReader/apduLevelPACECapable.d.ts +23 -0
- package/lib/typescript/src/Shared/EIDReader/apduLevelPACECapable.d.ts.map +1 -0
- package/lib/typescript/src/Shared/EIDReader/bacKey.d.ts +6 -0
- package/lib/typescript/src/Shared/EIDReader/bacKey.d.ts.map +1 -1
- package/lib/typescript/src/Shared/EIDReader/eidReader.d.ts.map +1 -1
- package/lib/typescript/src/Shared/EIDReader/eidService.d.ts +9 -0
- package/lib/typescript/src/Shared/EIDReader/eidService.d.ts.map +1 -1
- package/lib/typescript/src/Shared/EIDReader/nfcManagerCardService.d.ts.map +1 -1
- package/lib/typescript/src/Shared/EIDReader/paceInfo.d.ts +50 -0
- package/lib/typescript/src/Shared/EIDReader/paceInfo.d.ts.map +1 -0
- package/lib/typescript/src/Shared/EIDReader/paceKeySpec.d.ts +30 -0
- package/lib/typescript/src/Shared/EIDReader/paceKeySpec.d.ts.map +1 -0
- package/lib/typescript/src/Shared/EIDReader/protocol/paceAPDUSender.d.ts +17 -0
- package/lib/typescript/src/Shared/EIDReader/protocol/paceAPDUSender.d.ts.map +1 -0
- package/lib/typescript/src/Shared/EIDReader/protocol/paceProtocol.d.ts +105 -0
- package/lib/typescript/src/Shared/EIDReader/protocol/paceProtocol.d.ts.map +1 -0
- package/lib/typescript/src/Shared/EIDReader/protocol/paceResult.d.ts +24 -0
- package/lib/typescript/src/Shared/EIDReader/protocol/paceResult.d.ts.map +1 -0
- package/lib/typescript/src/Shared/EIDReader/secureMessagingWrapper.d.ts +15 -0
- package/lib/typescript/src/Shared/EIDReader/secureMessagingWrapper.d.ts.map +1 -1
- package/lib/typescript/src/Shared/EIDReader/smartcards/commandAPDU.d.ts.map +1 -1
- package/lib/typescript/src/Shared/EIDReader/tlv/tlv.utils.d.ts.map +1 -1
- package/lib/typescript/src/Shared/EIDReader/utils/aesCrypto.utils.d.ts +39 -0
- package/lib/typescript/src/Shared/EIDReader/utils/aesCrypto.utils.d.ts.map +1 -0
- package/lib/typescript/src/Shared/Libs/analytics.utils.d.ts.map +1 -1
- package/lib/typescript/src/Shared/Libs/contains.d.ts +0 -7
- package/lib/typescript/src/Shared/Libs/contains.d.ts.map +1 -1
- package/lib/typescript/src/Shared/Libs/country-display.utils.d.ts +2 -0
- package/lib/typescript/src/Shared/Libs/country-display.utils.d.ts.map +1 -0
- package/lib/typescript/src/Shared/Libs/demo.utils.d.ts.map +1 -1
- package/lib/typescript/src/Shared/Libs/http-client.d.ts +1 -1
- package/lib/typescript/src/Shared/Libs/http-client.d.ts.map +1 -1
- package/lib/typescript/src/Shared/Libs/index.d.ts +20 -0
- package/lib/typescript/src/Shared/Libs/index.d.ts.map +1 -0
- package/lib/typescript/src/Shared/Libs/mrz.utils.d.ts.map +1 -1
- package/lib/typescript/src/Shared/Libs/permissions.utils.d.ts +58 -0
- package/lib/typescript/src/Shared/Libs/permissions.utils.d.ts.map +1 -0
- package/lib/typescript/src/Shared/Libs/status-bar.utils.d.ts.map +1 -1
- package/lib/typescript/src/Shared/Types/analytics.types.d.ts +2 -0
- package/lib/typescript/src/Shared/Types/analytics.types.d.ts.map +1 -1
- package/lib/typescript/src/Shared/Types/identificationInfo.d.ts +10 -1
- package/lib/typescript/src/Shared/Types/identificationInfo.d.ts.map +1 -1
- package/lib/typescript/src/Translation/Resources/en.d.ts +41 -1
- package/lib/typescript/src/Translation/Resources/en.d.ts.map +1 -1
- package/lib/typescript/src/Translation/Resources/tr.d.ts +41 -1
- package/lib/typescript/src/Translation/Resources/tr.d.ts.map +1 -1
- package/lib/typescript/src/Trustchex.d.ts.map +1 -1
- package/lib/typescript/src/version.d.ts +1 -1
- package/package.json +7 -4
- package/src/Screens/Debug/NFCScanTestScreen.tsx +692 -0
- package/src/Screens/Dynamic/ContractAcceptanceScreen.tsx +1 -4
- package/src/Screens/Dynamic/IdentityDocumentEIDScanningScreen.tsx +21 -4
- package/src/Screens/Dynamic/LivenessDetectionScreen.tsx +165 -24
- package/src/Screens/Dynamic/VerbalConsentScreen.tsx +1401 -0
- package/src/Screens/Dynamic/VideoCallScreen.tsx +11 -20
- package/src/Screens/Static/ResultScreen.tsx +187 -31
- package/src/Screens/Static/VerificationSessionCheckScreen.tsx +9 -0
- package/src/Shared/Animations/recording.json +1 -0
- package/src/Shared/Components/DebugNavigationPanel.tsx +73 -48
- package/src/Shared/Components/EIDScanner.tsx +280 -119
- package/src/Shared/Components/FaceCamera.tsx +19 -16
- package/src/Shared/Components/IdentityDocumentCamera.flows.ts +7 -4
- package/src/Shared/Components/IdentityDocumentCamera.tsx +206 -191
- package/src/Shared/Components/IdentityDocumentCamera.utils.ts +13 -4
- package/src/Shared/Components/NavigationManager.tsx +27 -18
- package/src/Shared/EIDReader/aesSecureMessagingWrapper.ts +69 -0
- package/src/Shared/EIDReader/apduLevelPACECapable.ts +34 -0
- package/src/Shared/EIDReader/bacKey.ts +24 -8
- package/src/Shared/EIDReader/eidReader.ts +398 -12
- package/src/Shared/EIDReader/eidService.ts +49 -1
- package/src/Shared/EIDReader/nfcManagerCardService.ts +4 -6
- package/src/Shared/EIDReader/paceInfo.ts +159 -0
- package/src/Shared/EIDReader/paceKeySpec.ts +56 -0
- package/src/Shared/EIDReader/protocol/paceAPDUSender.ts +163 -0
- package/src/Shared/EIDReader/protocol/paceProtocol.ts +946 -0
- package/src/Shared/EIDReader/protocol/paceResult.ts +62 -0
- package/src/Shared/EIDReader/secureMessagingWrapper.ts +28 -10
- package/src/Shared/EIDReader/smartcards/commandAPDU.ts +2 -1
- package/src/Shared/EIDReader/tlv/tlv.helpers.ts +1 -1
- package/src/Shared/EIDReader/tlv/tlv.utils.ts +8 -5
- package/src/Shared/EIDReader/utils/aesCrypto.utils.ts +217 -0
- package/src/Shared/Libs/PERMISSIONS_MANAGER.md +268 -0
- package/src/Shared/Libs/analytics.utils.ts +4 -0
- package/src/Shared/Libs/contains.ts +0 -53
- package/src/Shared/Libs/country-display.utils.ts +55 -0
- package/src/Shared/Libs/crypto.utils.ts +2 -2
- package/src/Shared/Libs/demo.utils.ts +10 -0
- package/src/Shared/Libs/http-client.ts +12 -4
- package/src/Shared/Libs/index.ts +63 -0
- package/src/Shared/Libs/mrz.utils.ts +3 -2
- package/src/Shared/Libs/permissions.utils.ts +251 -0
- package/src/Shared/Libs/status-bar.utils.ts +4 -2
- package/src/Shared/Services/VideoSessionService.ts +1 -1
- package/src/Shared/Types/analytics.types.ts +2 -0
- package/src/Shared/Types/identificationInfo.ts +11 -0
- package/src/Translation/Resources/en.ts +64 -3
- package/src/Translation/Resources/tr.ts +63 -3
- package/src/Trustchex.tsx +53 -17
- package/src/version.ts +1 -1
|
@@ -30,10 +30,27 @@ const IdentityDocumentEIDScanningScreen = () => {
|
|
|
30
30
|
useState<DocumentScannedData | null>(null);
|
|
31
31
|
const [passportData, setPassportData] =
|
|
32
32
|
useState<DocumentScannedData | null>();
|
|
33
|
-
const [documentNumber, setDocumentNumber] = useState<string>(
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
33
|
+
const [documentNumber, setDocumentNumber] = useState<string | undefined>(
|
|
34
|
+
() => {
|
|
35
|
+
const mrzFields =
|
|
36
|
+
appContext.identificationInfo.scannedDocument?.mrzFields;
|
|
37
|
+
return mrzFields?.documentNumber ?? undefined;
|
|
38
|
+
}
|
|
39
|
+
);
|
|
40
|
+
const [dateOfBirth, setDateOfBirth] = useState<string | undefined>(() => {
|
|
41
|
+
const mrzFields = appContext.identificationInfo.scannedDocument?.mrzFields;
|
|
42
|
+
return mrzFields?.birthDate ?? undefined;
|
|
43
|
+
});
|
|
44
|
+
const [dateOfExpiry, setDateOfExpiry] = useState<string | undefined>(() => {
|
|
45
|
+
const mrzFields = appContext.identificationInfo.scannedDocument?.mrzFields;
|
|
46
|
+
return mrzFields?.expirationDate ?? undefined;
|
|
47
|
+
});
|
|
48
|
+
const [documentType, setDocumentType] = useState<string | undefined>(() => {
|
|
49
|
+
const dc =
|
|
50
|
+
appContext.identificationInfo.scannedDocument?.mrzFields?.documentCode;
|
|
51
|
+
if (!dc) return undefined;
|
|
52
|
+
return dc === 'I' ? 'ID' : dc === 'P' ? 'PASSPORT' : 'UNKNOWN';
|
|
53
|
+
});
|
|
37
54
|
const [isNFCSupported, setIsNFCSupported] = useState<boolean>(false);
|
|
38
55
|
const { t } = useTranslation();
|
|
39
56
|
const [allowedDocumentTypes, setAllowedDocumentTypes] = useState<
|
|
@@ -9,10 +9,13 @@ import React, {
|
|
|
9
9
|
useRef,
|
|
10
10
|
} from 'react';
|
|
11
11
|
import {
|
|
12
|
+
Animated,
|
|
13
|
+
Easing,
|
|
12
14
|
StyleSheet,
|
|
13
15
|
Text,
|
|
14
16
|
View,
|
|
15
17
|
Dimensions,
|
|
18
|
+
Platform,
|
|
16
19
|
Vibration,
|
|
17
20
|
StatusBar,
|
|
18
21
|
} from 'react-native';
|
|
@@ -27,7 +30,7 @@ import NavigationManager, {
|
|
|
27
30
|
type NavigationManagerRef,
|
|
28
31
|
} from '../../Shared/Components/NavigationManager';
|
|
29
32
|
import AppContext from '../../Shared/Contexts/AppContext';
|
|
30
|
-
import {
|
|
33
|
+
import { type Rect } from '../../Shared/Libs/contains';
|
|
31
34
|
import { useTranslation } from 'react-i18next';
|
|
32
35
|
import StyledButton from '../../Shared/Components/StyledButton';
|
|
33
36
|
import LottieView from 'lottie-react-native';
|
|
@@ -43,7 +46,7 @@ import { useKeepAwake } from '../../Shared/Libs/native-keep-awake.utils';
|
|
|
43
46
|
|
|
44
47
|
const { width: windowWidth, height: windowHeight } = Dimensions.get('window');
|
|
45
48
|
|
|
46
|
-
const PREVIEW_SIZE = windowWidth * 0.
|
|
49
|
+
const PREVIEW_SIZE = windowWidth * 0.95;
|
|
47
50
|
const PREVIEW_RECT: Rect = {
|
|
48
51
|
minX: (windowWidth - PREVIEW_SIZE) / 2,
|
|
49
52
|
minY: (windowHeight - PREVIEW_SIZE) / 2,
|
|
@@ -96,10 +99,36 @@ const LivenessDetectionScreen = () => {
|
|
|
96
99
|
const [isRecording, setIsRecording] = useState(false);
|
|
97
100
|
const insets = useSafeAreaInsets();
|
|
98
101
|
const referenceFaceTrackingId = useRef<number | null>(null);
|
|
102
|
+
const recordingDotOpacity = useRef(new Animated.Value(1)).current;
|
|
99
103
|
|
|
100
104
|
// Track screen view and exit
|
|
101
105
|
useScreenTracking('liveness_detection');
|
|
102
106
|
|
|
107
|
+
useEffect(() => {
|
|
108
|
+
if (isRecording) {
|
|
109
|
+
const pulse = Animated.loop(
|
|
110
|
+
Animated.sequence([
|
|
111
|
+
Animated.timing(recordingDotOpacity, {
|
|
112
|
+
toValue: 0.2,
|
|
113
|
+
duration: 600,
|
|
114
|
+
easing: Easing.inOut(Easing.ease),
|
|
115
|
+
useNativeDriver: true,
|
|
116
|
+
}),
|
|
117
|
+
Animated.timing(recordingDotOpacity, {
|
|
118
|
+
toValue: 1,
|
|
119
|
+
duration: 600,
|
|
120
|
+
easing: Easing.inOut(Easing.ease),
|
|
121
|
+
useNativeDriver: true,
|
|
122
|
+
}),
|
|
123
|
+
])
|
|
124
|
+
);
|
|
125
|
+
pulse.start();
|
|
126
|
+
return () => pulse.stop();
|
|
127
|
+
} else {
|
|
128
|
+
recordingDotOpacity.setValue(1);
|
|
129
|
+
}
|
|
130
|
+
}, [isRecording, recordingDotOpacity]);
|
|
131
|
+
|
|
103
132
|
// Configure status bar for white background
|
|
104
133
|
useStatusBarWhiteBackground();
|
|
105
134
|
|
|
@@ -152,7 +181,6 @@ const LivenessDetectionScreen = () => {
|
|
|
152
181
|
},
|
|
153
182
|
FINISH: { instruction: t('livenessDetectionScreen.finish') },
|
|
154
183
|
});
|
|
155
|
-
const [instructionList, setInstructionList] = useState<string[]>([]);
|
|
156
184
|
const [hasGuideShown, setHasGuideShown] = useState(false);
|
|
157
185
|
const stoppingRecordingRef = useRef(false); // Track if we're already stopping to prevent multiple calls
|
|
158
186
|
|
|
@@ -167,7 +195,9 @@ const LivenessDetectionScreen = () => {
|
|
|
167
195
|
} else {
|
|
168
196
|
// Guide screen with white background - use dark icons
|
|
169
197
|
StatusBar.setBarStyle('dark-content', true);
|
|
170
|
-
|
|
198
|
+
if (Platform.OS === 'android') {
|
|
199
|
+
StatusBar.setBackgroundColor('#ffffff', true);
|
|
200
|
+
}
|
|
171
201
|
}
|
|
172
202
|
}, [hasGuideShown]);
|
|
173
203
|
|
|
@@ -189,7 +219,6 @@ const LivenessDetectionScreen = () => {
|
|
|
189
219
|
.sort(() => Math.random() - 0.5);
|
|
190
220
|
il.unshift('START');
|
|
191
221
|
il.push('FINISH');
|
|
192
|
-
setInstructionList(il);
|
|
193
222
|
setInitialState({
|
|
194
223
|
brightnessLow: false,
|
|
195
224
|
faceDetected: false,
|
|
@@ -289,6 +318,14 @@ const LivenessDetectionScreen = () => {
|
|
|
289
318
|
return leftOpen >= 0.8 && rightOpen >= 0.8;
|
|
290
319
|
};
|
|
291
320
|
|
|
321
|
+
const areEyesVisible = (face: Face) => {
|
|
322
|
+
// Relaxed check: eyes just need to be detected (not fully closed).
|
|
323
|
+
// Smiling naturally causes squinting, so a lower threshold is used.
|
|
324
|
+
const leftOpen = face.leftEyeOpenProbability ?? 0;
|
|
325
|
+
const rightOpen = face.rightEyeOpenProbability ?? 0;
|
|
326
|
+
return leftOpen >= 0.3 && rightOpen >= 0.3;
|
|
327
|
+
};
|
|
328
|
+
|
|
292
329
|
const instructionReducer = (
|
|
293
330
|
state: StateType,
|
|
294
331
|
action: PossibleActions
|
|
@@ -526,26 +563,84 @@ const LivenessDetectionScreen = () => {
|
|
|
526
563
|
);
|
|
527
564
|
}
|
|
528
565
|
|
|
529
|
-
//
|
|
530
|
-
//
|
|
531
|
-
|
|
566
|
+
// Map the on-screen circle into frame coordinates.
|
|
567
|
+
// Camera uses FILL_CENTER / resizeAspectFill (cover): uniform scale filling
|
|
568
|
+
// the view, cropping overflow symmetrically.
|
|
569
|
+
const coverScale = Math.max(
|
|
570
|
+
windowWidth / frameWidth,
|
|
571
|
+
windowHeight / frameHeight
|
|
572
|
+
);
|
|
573
|
+
const offsetX = (frameWidth - windowWidth / coverScale) / 2;
|
|
574
|
+
const offsetY = (frameHeight - windowHeight / coverScale) / 2;
|
|
532
575
|
const previewRectInFrame: Rect = {
|
|
533
|
-
minX:
|
|
534
|
-
minY:
|
|
535
|
-
width:
|
|
536
|
-
height:
|
|
576
|
+
minX: PREVIEW_RECT.minX / coverScale + offsetX,
|
|
577
|
+
minY: PREVIEW_RECT.minY / coverScale + offsetY,
|
|
578
|
+
width: PREVIEW_RECT.width / coverScale,
|
|
579
|
+
height: PREVIEW_RECT.height / coverScale,
|
|
537
580
|
};
|
|
581
|
+
const previewSizeInFrame = PREVIEW_RECT.width / coverScale;
|
|
582
|
+
|
|
583
|
+
// Check containment using face center + yaw-stable core radius.
|
|
584
|
+
// When the head turns (yaw), the bounding box grows wider but height stays
|
|
585
|
+
// constant — so min(width, height)/2 gives a stable size that doesn't
|
|
586
|
+
// falsely trigger an outside-circle reset during TURN_HEAD_LEFT/RIGHT.
|
|
587
|
+
const faceCenterX = face.bounds.x + face.bounds.width / 2;
|
|
588
|
+
const faceCenterY = face.bounds.y + face.bounds.height / 2;
|
|
589
|
+
const faceCoreRadius = Math.max(
|
|
590
|
+
0,
|
|
591
|
+
Math.min(face.bounds.width, face.bounds.height) / 2 -
|
|
592
|
+
PREVIEW_EDGE_OFFSET / 2
|
|
593
|
+
);
|
|
594
|
+
const circleCX = previewRectInFrame.minX + previewRectInFrame.width / 2;
|
|
595
|
+
const circleCY = previewRectInFrame.minY + previewRectInFrame.height / 2;
|
|
596
|
+
const circleR = previewSizeInFrame / 2;
|
|
597
|
+
const faceDx = faceCenterX - circleCX;
|
|
598
|
+
const faceDy = faceCenterY - circleCY;
|
|
599
|
+
|
|
600
|
+
const yawAbs = Math.abs(face.yawAngle || 0);
|
|
601
|
+
const yawFactor = Math.min(1, yawAbs / TURN_ANGLE_LIMIT);
|
|
602
|
+
|
|
603
|
+
// General bound tolerance (instruction-agnostic): adapt to head pose and
|
|
604
|
+
// face box deformation so containment remains stable across all actions.
|
|
605
|
+
const faceWidthGrowth = Math.max(
|
|
606
|
+
0,
|
|
607
|
+
face.bounds.width - face.bounds.height
|
|
608
|
+
);
|
|
538
609
|
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
610
|
+
// On iOS, close faces tend to appear larger in-frame and lateral head
|
|
611
|
+
// turns shift center more aggressively. Apply an iOS-only proximity boost
|
|
612
|
+
// to horizontal tolerance to avoid false outside-circle resets.
|
|
613
|
+
const faceRadiusRatio = Math.min(
|
|
614
|
+
1,
|
|
615
|
+
faceCoreRadius / Math.max(1, circleR)
|
|
616
|
+
);
|
|
617
|
+
const iosProximityBoost =
|
|
618
|
+
Platform.OS === 'ios' ? Math.max(0, faceRadiusRatio - 0.5) : 0;
|
|
619
|
+
|
|
620
|
+
const baseToleranceX = previewSizeInFrame * 0.05;
|
|
621
|
+
const yawToleranceX = face.bounds.width * 0.12 * yawFactor;
|
|
622
|
+
const shapeToleranceX = faceWidthGrowth * 0.1;
|
|
623
|
+
const iosToleranceX = face.bounds.width * 0.2 * iosProximityBoost;
|
|
624
|
+
const toleranceX = Math.max(
|
|
625
|
+
0,
|
|
626
|
+
baseToleranceX + yawToleranceX + shapeToleranceX + iosToleranceX,
|
|
627
|
+
face.bounds.width * 0.05
|
|
628
|
+
);
|
|
629
|
+
|
|
630
|
+
const pitchAbs = Math.abs(face.pitchAngle || 0);
|
|
631
|
+
const pitchFactor = Math.min(1, pitchAbs / TURN_ANGLE_LIMIT);
|
|
632
|
+
const toleranceY = Math.max(
|
|
633
|
+
0,
|
|
634
|
+
previewSizeInFrame * 0.03 + face.bounds.height * 0.03 * pitchFactor,
|
|
635
|
+
face.bounds.height * 0.04
|
|
636
|
+
);
|
|
637
|
+
|
|
638
|
+
const allowedRadiusX = Math.max(1, circleR - faceCoreRadius + toleranceX);
|
|
639
|
+
const allowedRadiusY = Math.max(1, circleR - faceCoreRadius + toleranceY);
|
|
640
|
+
const normalizedDistance =
|
|
641
|
+
(faceDx * faceDx) / (allowedRadiusX * allowedRadiusX) +
|
|
642
|
+
(faceDy * faceDy) / (allowedRadiusY * allowedRadiusY);
|
|
643
|
+
const previewContainsFace = normalizedDistance <= 1;
|
|
549
644
|
const multipleFacesDetected = faces.length > 1;
|
|
550
645
|
|
|
551
646
|
if (!isImageBright) {
|
|
@@ -694,11 +789,12 @@ const LivenessDetectionScreen = () => {
|
|
|
694
789
|
instructions.SMILE.minAngle < face.pitchAngle &&
|
|
695
790
|
face.pitchAngle < instructions.SMILE.maxAngle;
|
|
696
791
|
|
|
697
|
-
// Check if smiling with sufficient probability AND looking at camera AND eyes
|
|
792
|
+
// Check if smiling with sufficient probability AND looking at camera AND eyes visible
|
|
793
|
+
// Use relaxed eye check — smiling naturally causes squinting
|
|
698
794
|
if (
|
|
699
795
|
smilingProb >= instructions.SMILE.minProbability &&
|
|
700
796
|
isFacingCamera &&
|
|
701
|
-
|
|
797
|
+
areEyesVisible(face)
|
|
702
798
|
) {
|
|
703
799
|
instructions.SMILE.photo = image;
|
|
704
800
|
dispatch({ type: 'NEXT_INSTRUCTION', payload: 'SMILE' });
|
|
@@ -890,6 +986,24 @@ const LivenessDetectionScreen = () => {
|
|
|
890
986
|
mask="url(#hole-mask)"
|
|
891
987
|
/>
|
|
892
988
|
</Svg>
|
|
989
|
+
{isRecording && (
|
|
990
|
+
<View
|
|
991
|
+
style={[
|
|
992
|
+
styles.recordingIndicatorContainer,
|
|
993
|
+
{ top: PREVIEW_RECT.minY - 40 },
|
|
994
|
+
]}
|
|
995
|
+
>
|
|
996
|
+
<View style={styles.recordingIndicator}>
|
|
997
|
+
<Animated.View
|
|
998
|
+
style={[
|
|
999
|
+
styles.recordingDot,
|
|
1000
|
+
{ opacity: recordingDotOpacity },
|
|
1001
|
+
]}
|
|
1002
|
+
/>
|
|
1003
|
+
<Text style={styles.recordingText}>REC</Text>
|
|
1004
|
+
</View>
|
|
1005
|
+
</View>
|
|
1006
|
+
)}
|
|
893
1007
|
<View
|
|
894
1008
|
style={[
|
|
895
1009
|
styles.instructionsContainerBottom,
|
|
@@ -1014,6 +1128,33 @@ const styles = StyleSheet.create({
|
|
|
1014
1128
|
width: '100%',
|
|
1015
1129
|
zIndex: 1,
|
|
1016
1130
|
},
|
|
1131
|
+
recordingIndicatorContainer: {
|
|
1132
|
+
position: 'absolute',
|
|
1133
|
+
width: '100%',
|
|
1134
|
+
alignItems: 'center',
|
|
1135
|
+
zIndex: 2,
|
|
1136
|
+
},
|
|
1137
|
+
recordingIndicator: {
|
|
1138
|
+
flexDirection: 'row',
|
|
1139
|
+
alignItems: 'center',
|
|
1140
|
+
gap: 8,
|
|
1141
|
+
backgroundColor: 'rgba(0, 0, 0, 0.6)',
|
|
1142
|
+
paddingVertical: 6,
|
|
1143
|
+
paddingHorizontal: 14,
|
|
1144
|
+
borderRadius: 20,
|
|
1145
|
+
},
|
|
1146
|
+
recordingDot: {
|
|
1147
|
+
width: 10,
|
|
1148
|
+
height: 10,
|
|
1149
|
+
borderRadius: 5,
|
|
1150
|
+
backgroundColor: '#ef4444',
|
|
1151
|
+
},
|
|
1152
|
+
recordingText: {
|
|
1153
|
+
color: '#ffffff',
|
|
1154
|
+
fontSize: 14,
|
|
1155
|
+
fontWeight: '700',
|
|
1156
|
+
letterSpacing: 1,
|
|
1157
|
+
},
|
|
1017
1158
|
guide: {
|
|
1018
1159
|
flex: 1,
|
|
1019
1160
|
display: 'flex',
|