@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.
Files changed (171) hide show
  1. package/android/src/main/java/com/trustchex/reactnativesdk/TrustchexSDKModule.kt +2 -8
  2. package/android/src/main/java/com/trustchex/reactnativesdk/camera/TrustchexCameraView.kt +102 -4
  3. package/ios/Camera/TrustchexCameraView.swift +151 -18
  4. package/ios/Permission/PermissionModule.m +22 -0
  5. package/ios/Permission/PermissionModule.swift +67 -0
  6. package/lib/module/Screens/Debug/NFCScanTestScreen.js +635 -0
  7. package/lib/module/Screens/Dynamic/ContractAcceptanceScreen.js +1 -4
  8. package/lib/module/Screens/Dynamic/IdentityDocumentEIDScanningScreen.js +17 -4
  9. package/lib/module/Screens/Dynamic/LivenessDetectionScreen.js +125 -25
  10. package/lib/module/Screens/Dynamic/VerbalConsentScreen.js +1079 -0
  11. package/lib/module/Screens/Dynamic/VideoCallScreen.js +10 -13
  12. package/lib/module/Screens/Static/ResultScreen.js +131 -22
  13. package/lib/module/Screens/Static/VerificationSessionCheckScreen.js +8 -0
  14. package/lib/module/Shared/Animations/recording.json +1 -0
  15. package/lib/module/Shared/Components/DebugNavigationPanel.js +69 -71
  16. package/lib/module/Shared/Components/EIDScanner.js +222 -115
  17. package/lib/module/Shared/Components/FaceCamera.js +14 -8
  18. package/lib/module/Shared/Components/IdentityDocumentCamera.flows.js +5 -3
  19. package/lib/module/Shared/Components/IdentityDocumentCamera.js +60 -42
  20. package/lib/module/Shared/Components/IdentityDocumentCamera.utils.js +13 -4
  21. package/lib/module/Shared/Components/NavigationManager.js +24 -16
  22. package/lib/module/Shared/EIDReader/aesSecureMessagingWrapper.js +51 -0
  23. package/lib/module/Shared/EIDReader/apduLevelPACECapable.js +3 -0
  24. package/lib/module/Shared/EIDReader/bacKey.js +16 -2
  25. package/lib/module/Shared/EIDReader/eidReader.js +354 -13
  26. package/lib/module/Shared/EIDReader/eidService.js +25 -1
  27. package/lib/module/Shared/EIDReader/nfcManagerCardService.js +4 -7
  28. package/lib/module/Shared/EIDReader/paceInfo.js +85 -0
  29. package/lib/module/Shared/EIDReader/paceKeySpec.js +51 -0
  30. package/lib/module/Shared/EIDReader/protocol/paceAPDUSender.js +100 -0
  31. package/lib/module/Shared/EIDReader/protocol/paceProtocol.js +655 -0
  32. package/lib/module/Shared/EIDReader/protocol/paceResult.js +37 -0
  33. package/lib/module/Shared/EIDReader/secureMessagingWrapper.js +27 -4
  34. package/lib/module/Shared/EIDReader/smartcards/commandAPDU.js +2 -1
  35. package/lib/module/Shared/EIDReader/tlv/tlv.helpers.js +1 -1
  36. package/lib/module/Shared/EIDReader/tlv/tlv.utils.js +6 -3
  37. package/lib/module/Shared/EIDReader/utils/aesCrypto.utils.js +189 -0
  38. package/lib/module/Shared/Libs/PERMISSIONS_MANAGER.md +268 -0
  39. package/lib/module/Shared/Libs/analytics.utils.js +4 -0
  40. package/lib/module/Shared/Libs/contains.js +1 -40
  41. package/lib/module/Shared/Libs/country-display.utils.js +34 -0
  42. package/lib/module/Shared/Libs/demo.utils.js +8 -0
  43. package/lib/module/Shared/Libs/index.js +20 -0
  44. package/lib/module/Shared/Libs/mrz.utils.js +3 -2
  45. package/lib/module/Shared/Libs/permissions.utils.js +199 -0
  46. package/lib/module/Shared/Libs/status-bar.utils.js +4 -2
  47. package/lib/module/Shared/Types/analytics.types.js +2 -0
  48. package/lib/module/Translation/Resources/en.js +42 -2
  49. package/lib/module/Translation/Resources/tr.js +42 -2
  50. package/lib/module/Trustchex.js +54 -20
  51. package/lib/module/version.js +1 -1
  52. package/lib/typescript/src/Screens/Debug/NFCScanTestScreen.d.ts +3 -0
  53. package/lib/typescript/src/Screens/Debug/NFCScanTestScreen.d.ts.map +1 -0
  54. package/lib/typescript/src/Screens/Dynamic/ContractAcceptanceScreen.d.ts.map +1 -1
  55. package/lib/typescript/src/Screens/Dynamic/IdentityDocumentEIDScanningScreen.d.ts.map +1 -1
  56. package/lib/typescript/src/Screens/Dynamic/LivenessDetectionScreen.d.ts.map +1 -1
  57. package/lib/typescript/src/Screens/Dynamic/VerbalConsentScreen.d.ts +3 -0
  58. package/lib/typescript/src/Screens/Dynamic/VerbalConsentScreen.d.ts.map +1 -0
  59. package/lib/typescript/src/Screens/Dynamic/VideoCallScreen.d.ts.map +1 -1
  60. package/lib/typescript/src/Screens/Static/ResultScreen.d.ts.map +1 -1
  61. package/lib/typescript/src/Screens/Static/VerificationSessionCheckScreen.d.ts.map +1 -1
  62. package/lib/typescript/src/Shared/Components/DebugNavigationPanel.d.ts.map +1 -1
  63. package/lib/typescript/src/Shared/Components/EIDScanner.d.ts.map +1 -1
  64. package/lib/typescript/src/Shared/Components/FaceCamera.d.ts.map +1 -1
  65. package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.d.ts.map +1 -1
  66. package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.flows.d.ts +1 -1
  67. package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.flows.d.ts.map +1 -1
  68. package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.utils.d.ts +5 -0
  69. package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.utils.d.ts.map +1 -1
  70. package/lib/typescript/src/Shared/Components/NavigationManager.d.ts.map +1 -1
  71. package/lib/typescript/src/Shared/EIDReader/aesSecureMessagingWrapper.d.ts +18 -0
  72. package/lib/typescript/src/Shared/EIDReader/aesSecureMessagingWrapper.d.ts.map +1 -0
  73. package/lib/typescript/src/Shared/EIDReader/apduLevelPACECapable.d.ts +23 -0
  74. package/lib/typescript/src/Shared/EIDReader/apduLevelPACECapable.d.ts.map +1 -0
  75. package/lib/typescript/src/Shared/EIDReader/bacKey.d.ts +6 -0
  76. package/lib/typescript/src/Shared/EIDReader/bacKey.d.ts.map +1 -1
  77. package/lib/typescript/src/Shared/EIDReader/eidReader.d.ts.map +1 -1
  78. package/lib/typescript/src/Shared/EIDReader/eidService.d.ts +9 -0
  79. package/lib/typescript/src/Shared/EIDReader/eidService.d.ts.map +1 -1
  80. package/lib/typescript/src/Shared/EIDReader/nfcManagerCardService.d.ts.map +1 -1
  81. package/lib/typescript/src/Shared/EIDReader/paceInfo.d.ts +50 -0
  82. package/lib/typescript/src/Shared/EIDReader/paceInfo.d.ts.map +1 -0
  83. package/lib/typescript/src/Shared/EIDReader/paceKeySpec.d.ts +30 -0
  84. package/lib/typescript/src/Shared/EIDReader/paceKeySpec.d.ts.map +1 -0
  85. package/lib/typescript/src/Shared/EIDReader/protocol/paceAPDUSender.d.ts +17 -0
  86. package/lib/typescript/src/Shared/EIDReader/protocol/paceAPDUSender.d.ts.map +1 -0
  87. package/lib/typescript/src/Shared/EIDReader/protocol/paceProtocol.d.ts +105 -0
  88. package/lib/typescript/src/Shared/EIDReader/protocol/paceProtocol.d.ts.map +1 -0
  89. package/lib/typescript/src/Shared/EIDReader/protocol/paceResult.d.ts +24 -0
  90. package/lib/typescript/src/Shared/EIDReader/protocol/paceResult.d.ts.map +1 -0
  91. package/lib/typescript/src/Shared/EIDReader/secureMessagingWrapper.d.ts +15 -0
  92. package/lib/typescript/src/Shared/EIDReader/secureMessagingWrapper.d.ts.map +1 -1
  93. package/lib/typescript/src/Shared/EIDReader/smartcards/commandAPDU.d.ts.map +1 -1
  94. package/lib/typescript/src/Shared/EIDReader/tlv/tlv.utils.d.ts.map +1 -1
  95. package/lib/typescript/src/Shared/EIDReader/utils/aesCrypto.utils.d.ts +39 -0
  96. package/lib/typescript/src/Shared/EIDReader/utils/aesCrypto.utils.d.ts.map +1 -0
  97. package/lib/typescript/src/Shared/Libs/analytics.utils.d.ts.map +1 -1
  98. package/lib/typescript/src/Shared/Libs/contains.d.ts +0 -7
  99. package/lib/typescript/src/Shared/Libs/contains.d.ts.map +1 -1
  100. package/lib/typescript/src/Shared/Libs/country-display.utils.d.ts +2 -0
  101. package/lib/typescript/src/Shared/Libs/country-display.utils.d.ts.map +1 -0
  102. package/lib/typescript/src/Shared/Libs/demo.utils.d.ts.map +1 -1
  103. package/lib/typescript/src/Shared/Libs/http-client.d.ts +1 -1
  104. package/lib/typescript/src/Shared/Libs/http-client.d.ts.map +1 -1
  105. package/lib/typescript/src/Shared/Libs/index.d.ts +20 -0
  106. package/lib/typescript/src/Shared/Libs/index.d.ts.map +1 -0
  107. package/lib/typescript/src/Shared/Libs/mrz.utils.d.ts.map +1 -1
  108. package/lib/typescript/src/Shared/Libs/permissions.utils.d.ts +58 -0
  109. package/lib/typescript/src/Shared/Libs/permissions.utils.d.ts.map +1 -0
  110. package/lib/typescript/src/Shared/Libs/status-bar.utils.d.ts.map +1 -1
  111. package/lib/typescript/src/Shared/Types/analytics.types.d.ts +2 -0
  112. package/lib/typescript/src/Shared/Types/analytics.types.d.ts.map +1 -1
  113. package/lib/typescript/src/Shared/Types/identificationInfo.d.ts +10 -1
  114. package/lib/typescript/src/Shared/Types/identificationInfo.d.ts.map +1 -1
  115. package/lib/typescript/src/Translation/Resources/en.d.ts +41 -1
  116. package/lib/typescript/src/Translation/Resources/en.d.ts.map +1 -1
  117. package/lib/typescript/src/Translation/Resources/tr.d.ts +41 -1
  118. package/lib/typescript/src/Translation/Resources/tr.d.ts.map +1 -1
  119. package/lib/typescript/src/Trustchex.d.ts.map +1 -1
  120. package/lib/typescript/src/version.d.ts +1 -1
  121. package/package.json +7 -4
  122. package/src/Screens/Debug/NFCScanTestScreen.tsx +692 -0
  123. package/src/Screens/Dynamic/ContractAcceptanceScreen.tsx +1 -4
  124. package/src/Screens/Dynamic/IdentityDocumentEIDScanningScreen.tsx +21 -4
  125. package/src/Screens/Dynamic/LivenessDetectionScreen.tsx +165 -24
  126. package/src/Screens/Dynamic/VerbalConsentScreen.tsx +1401 -0
  127. package/src/Screens/Dynamic/VideoCallScreen.tsx +11 -20
  128. package/src/Screens/Static/ResultScreen.tsx +187 -31
  129. package/src/Screens/Static/VerificationSessionCheckScreen.tsx +9 -0
  130. package/src/Shared/Animations/recording.json +1 -0
  131. package/src/Shared/Components/DebugNavigationPanel.tsx +73 -48
  132. package/src/Shared/Components/EIDScanner.tsx +280 -119
  133. package/src/Shared/Components/FaceCamera.tsx +19 -16
  134. package/src/Shared/Components/IdentityDocumentCamera.flows.ts +7 -4
  135. package/src/Shared/Components/IdentityDocumentCamera.tsx +206 -191
  136. package/src/Shared/Components/IdentityDocumentCamera.utils.ts +13 -4
  137. package/src/Shared/Components/NavigationManager.tsx +27 -18
  138. package/src/Shared/EIDReader/aesSecureMessagingWrapper.ts +69 -0
  139. package/src/Shared/EIDReader/apduLevelPACECapable.ts +34 -0
  140. package/src/Shared/EIDReader/bacKey.ts +24 -8
  141. package/src/Shared/EIDReader/eidReader.ts +398 -12
  142. package/src/Shared/EIDReader/eidService.ts +49 -1
  143. package/src/Shared/EIDReader/nfcManagerCardService.ts +4 -6
  144. package/src/Shared/EIDReader/paceInfo.ts +159 -0
  145. package/src/Shared/EIDReader/paceKeySpec.ts +56 -0
  146. package/src/Shared/EIDReader/protocol/paceAPDUSender.ts +163 -0
  147. package/src/Shared/EIDReader/protocol/paceProtocol.ts +946 -0
  148. package/src/Shared/EIDReader/protocol/paceResult.ts +62 -0
  149. package/src/Shared/EIDReader/secureMessagingWrapper.ts +28 -10
  150. package/src/Shared/EIDReader/smartcards/commandAPDU.ts +2 -1
  151. package/src/Shared/EIDReader/tlv/tlv.helpers.ts +1 -1
  152. package/src/Shared/EIDReader/tlv/tlv.utils.ts +8 -5
  153. package/src/Shared/EIDReader/utils/aesCrypto.utils.ts +217 -0
  154. package/src/Shared/Libs/PERMISSIONS_MANAGER.md +268 -0
  155. package/src/Shared/Libs/analytics.utils.ts +4 -0
  156. package/src/Shared/Libs/contains.ts +0 -53
  157. package/src/Shared/Libs/country-display.utils.ts +55 -0
  158. package/src/Shared/Libs/crypto.utils.ts +2 -2
  159. package/src/Shared/Libs/demo.utils.ts +10 -0
  160. package/src/Shared/Libs/http-client.ts +12 -4
  161. package/src/Shared/Libs/index.ts +63 -0
  162. package/src/Shared/Libs/mrz.utils.ts +3 -2
  163. package/src/Shared/Libs/permissions.utils.ts +251 -0
  164. package/src/Shared/Libs/status-bar.utils.ts +4 -2
  165. package/src/Shared/Services/VideoSessionService.ts +1 -1
  166. package/src/Shared/Types/analytics.types.ts +2 -0
  167. package/src/Shared/Types/identificationInfo.ts +11 -0
  168. package/src/Translation/Resources/en.ts +64 -3
  169. package/src/Translation/Resources/tr.ts +63 -3
  170. package/src/Trustchex.tsx +53 -17
  171. package/src/version.ts +1 -1
@@ -3,13 +3,12 @@
3
3
  import Svg, { Rect as SvgRect, Circle, Mask } from 'react-native-svg';
4
4
  import { useNavigation } from '@react-navigation/native';
5
5
  import React, { useState, useReducer, useContext, useEffect, useCallback, useRef } from 'react';
6
- import { StyleSheet, Text, View, Dimensions, Vibration, StatusBar } from 'react-native';
6
+ import { Animated, Easing, StyleSheet, Text, View, Dimensions, Platform, Vibration, StatusBar } from 'react-native';
7
7
  import { useSafeAreaInsets, SafeAreaView } from 'react-native-safe-area-context';
8
8
  import NativeCircularProgress from "../../Shared/Components/NativeCircularProgress.js";
9
9
  import FaceCamera from "../../Shared/Components/FaceCamera.js";
10
10
  import NavigationManager from "../../Shared/Components/NavigationManager.js";
11
11
  import AppContext from "../../Shared/Contexts/AppContext.js";
12
- import { contains } from "../../Shared/Libs/contains.js";
13
12
  import { useTranslation } from 'react-i18next';
14
13
  import StyledButton from "../../Shared/Components/StyledButton.js";
15
14
  import LottieView from 'lottie-react-native';
@@ -22,7 +21,7 @@ const {
22
21
  width: windowWidth,
23
22
  height: windowHeight
24
23
  } = Dimensions.get('window');
25
- const PREVIEW_SIZE = windowWidth * 0.8;
24
+ const PREVIEW_SIZE = windowWidth * 0.95;
26
25
  const PREVIEW_RECT = {
27
26
  minX: (windowWidth - PREVIEW_SIZE) / 2,
28
27
  minY: (windowHeight - PREVIEW_SIZE) / 2,
@@ -44,9 +43,29 @@ const LivenessDetectionScreen = () => {
44
43
  const [isRecording, setIsRecording] = useState(false);
45
44
  const insets = useSafeAreaInsets();
46
45
  const referenceFaceTrackingId = useRef(null);
46
+ const recordingDotOpacity = useRef(new Animated.Value(1)).current;
47
47
 
48
48
  // Track screen view and exit
49
49
  useScreenTracking('liveness_detection');
50
+ useEffect(() => {
51
+ if (isRecording) {
52
+ const pulse = Animated.loop(Animated.sequence([Animated.timing(recordingDotOpacity, {
53
+ toValue: 0.2,
54
+ duration: 600,
55
+ easing: Easing.inOut(Easing.ease),
56
+ useNativeDriver: true
57
+ }), Animated.timing(recordingDotOpacity, {
58
+ toValue: 1,
59
+ duration: 600,
60
+ easing: Easing.inOut(Easing.ease),
61
+ useNativeDriver: true
62
+ })]));
63
+ pulse.start();
64
+ return () => pulse.stop();
65
+ } else {
66
+ recordingDotOpacity.setValue(1);
67
+ }
68
+ }, [isRecording, recordingDotOpacity]);
50
69
 
51
70
  // Configure status bar for white background
52
71
  useStatusBarWhiteBackground();
@@ -98,7 +117,6 @@ const LivenessDetectionScreen = () => {
98
117
  instruction: t('livenessDetectionScreen.finish')
99
118
  }
100
119
  });
101
- const [instructionList, setInstructionList] = useState([]);
102
120
  const [hasGuideShown, setHasGuideShown] = useState(false);
103
121
  const stoppingRecordingRef = useRef(false); // Track if we're already stopping to prevent multiple calls
104
122
 
@@ -113,7 +131,9 @@ const LivenessDetectionScreen = () => {
113
131
  } else {
114
132
  // Guide screen with white background - use dark icons
115
133
  StatusBar.setBarStyle('dark-content', true);
116
- StatusBar.setBackgroundColor('#ffffff', true);
134
+ if (Platform.OS === 'android') {
135
+ StatusBar.setBackgroundColor('#ffffff', true);
136
+ }
117
137
  }
118
138
  }, [hasGuideShown]);
119
139
  useEffect(() => {
@@ -122,7 +142,6 @@ const LivenessDetectionScreen = () => {
122
142
  instruction === 'LOOK_STRAIGHT_AND_BLINK' || !appContext.currentWorkflowStep?.data?.allowedLivenessInstructionTypes || appContext.currentWorkflowStep?.data?.allowedLivenessInstructionTypes.length === 0 || appContext.currentWorkflowStep?.data?.allowedLivenessInstructionTypes.includes(instruction))).sort(() => Math.random() - 0.5);
123
143
  il.unshift('START');
124
144
  il.push('FINISH');
125
- setInstructionList(il);
126
145
  setInitialState({
127
146
  brightnessLow: false,
128
147
  faceDetected: false,
@@ -195,6 +214,13 @@ const LivenessDetectionScreen = () => {
195
214
  const rightOpen = face.rightEyeOpenProbability ?? 0;
196
215
  return leftOpen >= 0.8 && rightOpen >= 0.8;
197
216
  };
217
+ const areEyesVisible = face => {
218
+ // Relaxed check: eyes just need to be detected (not fully closed).
219
+ // Smiling naturally causes squinting, so a lower threshold is used.
220
+ const leftOpen = face.leftEyeOpenProbability ?? 0;
221
+ const rightOpen = face.rightEyeOpenProbability ?? 0;
222
+ return leftOpen >= 0.3 && rightOpen >= 0.3;
223
+ };
198
224
  const instructionReducer = (state, action) => {
199
225
  switch (action.type) {
200
226
  case 'RESET':
@@ -386,25 +412,56 @@ const LivenessDetectionScreen = () => {
386
412
  });
387
413
  }
388
414
 
389
- // Calculate preview rect in frame coordinates (not screen coordinates)
390
- // Preview circle is 80% of frame width, centered
391
- const previewSizeInFrame = frameWidth * 0.8;
415
+ // Map the on-screen circle into frame coordinates.
416
+ // Camera uses FILL_CENTER / resizeAspectFill (cover): uniform scale filling
417
+ // the view, cropping overflow symmetrically.
418
+ const coverScale = Math.max(windowWidth / frameWidth, windowHeight / frameHeight);
419
+ const offsetX = (frameWidth - windowWidth / coverScale) / 2;
420
+ const offsetY = (frameHeight - windowHeight / coverScale) / 2;
392
421
  const previewRectInFrame = {
393
- minX: (frameWidth - previewSizeInFrame) / 2,
394
- minY: (frameHeight - previewSizeInFrame) / 2,
395
- width: previewSizeInFrame,
396
- height: previewSizeInFrame
397
- };
398
- const faceRectSmaller = {
399
- width: face.bounds.width - PREVIEW_EDGE_OFFSET,
400
- height: face.bounds.height - PREVIEW_EDGE_OFFSET,
401
- minY: face.bounds.y + PREVIEW_EDGE_OFFSET / 2,
402
- minX: face.bounds.x + PREVIEW_EDGE_OFFSET / 2
422
+ minX: PREVIEW_RECT.minX / coverScale + offsetX,
423
+ minY: PREVIEW_RECT.minY / coverScale + offsetY,
424
+ width: PREVIEW_RECT.width / coverScale,
425
+ height: PREVIEW_RECT.height / coverScale
403
426
  };
404
- const previewContainsFace = contains({
405
- outside: previewRectInFrame,
406
- inside: faceRectSmaller
407
- });
427
+ const previewSizeInFrame = PREVIEW_RECT.width / coverScale;
428
+
429
+ // Check containment using face center + yaw-stable core radius.
430
+ // When the head turns (yaw), the bounding box grows wider but height stays
431
+ // constant — so min(width, height)/2 gives a stable size that doesn't
432
+ // falsely trigger an outside-circle reset during TURN_HEAD_LEFT/RIGHT.
433
+ const faceCenterX = face.bounds.x + face.bounds.width / 2;
434
+ const faceCenterY = face.bounds.y + face.bounds.height / 2;
435
+ const faceCoreRadius = Math.max(0, Math.min(face.bounds.width, face.bounds.height) / 2 - PREVIEW_EDGE_OFFSET / 2);
436
+ const circleCX = previewRectInFrame.minX + previewRectInFrame.width / 2;
437
+ const circleCY = previewRectInFrame.minY + previewRectInFrame.height / 2;
438
+ const circleR = previewSizeInFrame / 2;
439
+ const faceDx = faceCenterX - circleCX;
440
+ const faceDy = faceCenterY - circleCY;
441
+ const yawAbs = Math.abs(face.yawAngle || 0);
442
+ const yawFactor = Math.min(1, yawAbs / TURN_ANGLE_LIMIT);
443
+
444
+ // General bound tolerance (instruction-agnostic): adapt to head pose and
445
+ // face box deformation so containment remains stable across all actions.
446
+ const faceWidthGrowth = Math.max(0, face.bounds.width - face.bounds.height);
447
+
448
+ // On iOS, close faces tend to appear larger in-frame and lateral head
449
+ // turns shift center more aggressively. Apply an iOS-only proximity boost
450
+ // to horizontal tolerance to avoid false outside-circle resets.
451
+ const faceRadiusRatio = Math.min(1, faceCoreRadius / Math.max(1, circleR));
452
+ const iosProximityBoost = Platform.OS === 'ios' ? Math.max(0, faceRadiusRatio - 0.5) : 0;
453
+ const baseToleranceX = previewSizeInFrame * 0.05;
454
+ const yawToleranceX = face.bounds.width * 0.12 * yawFactor;
455
+ const shapeToleranceX = faceWidthGrowth * 0.1;
456
+ const iosToleranceX = face.bounds.width * 0.2 * iosProximityBoost;
457
+ const toleranceX = Math.max(0, baseToleranceX + yawToleranceX + shapeToleranceX + iosToleranceX, face.bounds.width * 0.05);
458
+ const pitchAbs = Math.abs(face.pitchAngle || 0);
459
+ const pitchFactor = Math.min(1, pitchAbs / TURN_ANGLE_LIMIT);
460
+ const toleranceY = Math.max(0, previewSizeInFrame * 0.03 + face.bounds.height * 0.03 * pitchFactor, face.bounds.height * 0.04);
461
+ const allowedRadiusX = Math.max(1, circleR - faceCoreRadius + toleranceX);
462
+ const allowedRadiusY = Math.max(1, circleR - faceCoreRadius + toleranceY);
463
+ const normalizedDistance = faceDx * faceDx / (allowedRadiusX * allowedRadiusX) + faceDy * faceDy / (allowedRadiusY * allowedRadiusY);
464
+ const previewContainsFace = normalizedDistance <= 1;
408
465
  const multipleFacesDetected = faces.length > 1;
409
466
  if (!isImageBright) {
410
467
  // Brightness too low - reset progress if we've started the flow
@@ -564,8 +621,9 @@ const LivenessDetectionScreen = () => {
564
621
  // Ensure user is looking at camera (face direction check)
565
622
  const isFacingCamera = instructions.SMILE.minAngle < face.yawAngle && face.yawAngle < instructions.SMILE.maxAngle && instructions.SMILE.minAngle < face.pitchAngle && face.pitchAngle < instructions.SMILE.maxAngle;
566
623
 
567
- // Check if smiling with sufficient probability AND looking at camera AND eyes open
568
- if (smilingProb >= instructions.SMILE.minProbability && isFacingCamera && areEyesOpen(face)) {
624
+ // Check if smiling with sufficient probability AND looking at camera AND eyes visible
625
+ // Use relaxed eye check smiling naturally causes squinting
626
+ if (smilingProb >= instructions.SMILE.minProbability && isFacingCamera && areEyesVisible(face)) {
569
627
  instructions.SMILE.photo = image;
570
628
  dispatch({
571
629
  type: 'NEXT_INSTRUCTION',
@@ -711,6 +769,21 @@ const LivenessDetectionScreen = () => {
711
769
  fill: "white",
712
770
  mask: "url(#hole-mask)"
713
771
  })]
772
+ }), isRecording && /*#__PURE__*/_jsx(View, {
773
+ style: [styles.recordingIndicatorContainer, {
774
+ top: PREVIEW_RECT.minY - 40
775
+ }],
776
+ children: /*#__PURE__*/_jsxs(View, {
777
+ style: styles.recordingIndicator,
778
+ children: [/*#__PURE__*/_jsx(Animated.View, {
779
+ style: [styles.recordingDot, {
780
+ opacity: recordingDotOpacity
781
+ }]
782
+ }), /*#__PURE__*/_jsx(Text, {
783
+ style: styles.recordingText,
784
+ children: "REC"
785
+ })]
786
+ })
714
787
  }), /*#__PURE__*/_jsx(View, {
715
788
  style: [styles.instructionsContainerBottom, {
716
789
  top: PREVIEW_RECT.minY + PREVIEW_SIZE + 20
@@ -836,6 +909,33 @@ const styles = StyleSheet.create({
836
909
  width: '100%',
837
910
  zIndex: 1
838
911
  },
912
+ recordingIndicatorContainer: {
913
+ position: 'absolute',
914
+ width: '100%',
915
+ alignItems: 'center',
916
+ zIndex: 2
917
+ },
918
+ recordingIndicator: {
919
+ flexDirection: 'row',
920
+ alignItems: 'center',
921
+ gap: 8,
922
+ backgroundColor: 'rgba(0, 0, 0, 0.6)',
923
+ paddingVertical: 6,
924
+ paddingHorizontal: 14,
925
+ borderRadius: 20
926
+ },
927
+ recordingDot: {
928
+ width: 10,
929
+ height: 10,
930
+ borderRadius: 5,
931
+ backgroundColor: '#ef4444'
932
+ },
933
+ recordingText: {
934
+ color: '#ffffff',
935
+ fontSize: 14,
936
+ fontWeight: '700',
937
+ letterSpacing: 1
938
+ },
839
939
  guide: {
840
940
  flex: 1,
841
941
  display: 'flex',