@trustchex/react-native-sdk 1.360.0 → 1.362.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/TrustchexSDK.podspec +3 -1
- package/android/src/main/java/com/trustchex/reactnativesdk/TrustchexSDKPackage.kt +0 -13
- package/android/src/main/java/com/trustchex/reactnativesdk/camera/TrustchexCameraManager.kt +0 -8
- package/android/src/main/java/com/trustchex/reactnativesdk/camera/TrustchexCameraView.kt +59 -39
- package/android/src/main/java/com/trustchex/reactnativesdk/opencv/OpenCVModule.kt +94 -13
- package/ios/Camera/TrustchexCameraManager.m +0 -2
- package/ios/Camera/TrustchexCameraManager.swift +0 -7
- package/ios/Camera/TrustchexCameraView.swift +16 -47
- package/ios/OpenCV/OpenCVHelper.h +17 -0
- package/ios/OpenCV/OpenCVHelper.mm +128 -0
- package/ios/OpenCV/OpenCVModule.h +6 -0
- package/ios/OpenCV/OpenCVModule.mm +141 -0
- package/ios/TrustchexSDK-Bridging-Header.h +8 -0
- package/lib/module/Screens/Debug/MRZTestScreen.js +175 -0
- package/lib/module/Shared/Components/DebugNavigationPanel.js +4 -0
- package/lib/module/Shared/Components/EIDScanner.js +0 -78
- package/lib/module/Shared/Components/FaceCamera.js +6 -3
- package/lib/module/Shared/Components/IdentityDocumentCamera.js +199 -153
- package/lib/module/Shared/Components/QrCodeScannerCamera.js +0 -3
- package/lib/module/Shared/Config/camera-enhancement.config.js +11 -12
- package/lib/module/Shared/Libs/mrz.utils.js +265 -0
- package/lib/module/Trustchex.js +4 -0
- package/lib/module/index.js +1 -0
- package/lib/module/version.js +1 -1
- package/lib/typescript/src/Screens/Debug/MRZTestScreen.d.ts +3 -0
- package/lib/typescript/src/Screens/Debug/MRZTestScreen.d.ts.map +1 -0
- 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 +3 -1
- package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.d.ts.map +1 -1
- package/lib/typescript/src/Shared/Components/QrCodeScannerCamera.d.ts.map +1 -1
- package/lib/typescript/src/Shared/Components/TrustchexCamera.d.ts +0 -19
- package/lib/typescript/src/Shared/Components/TrustchexCamera.d.ts.map +1 -1
- package/lib/typescript/src/Shared/Config/camera-enhancement.config.d.ts +10 -10
- package/lib/typescript/src/Shared/Libs/mrz.utils.d.ts +18 -1
- package/lib/typescript/src/Shared/Libs/mrz.utils.d.ts.map +1 -1
- package/lib/typescript/src/Trustchex.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +3 -0
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/version.d.ts +1 -1
- package/package.json +2 -1
- package/src/Screens/Debug/MRZTestScreen.tsx +209 -0
- package/src/Shared/Components/DebugNavigationPanel.tsx +5 -0
- package/src/Shared/Components/EIDScanner.tsx +0 -53
- package/src/Shared/Components/FaceCamera.tsx +6 -3
- package/src/Shared/Components/IdentityDocumentCamera.tsx +246 -149
- package/src/Shared/Components/QrCodeScannerCamera.tsx +0 -9
- package/src/Shared/Components/TrustchexCamera.tsx +0 -20
- package/src/Shared/Config/camera-enhancement.config.ts +6 -6
- package/src/Shared/Libs/mrz.utils.ts +289 -1
- package/src/Trustchex.tsx +5 -0
- package/src/index.tsx +3 -0
- package/src/version.ts +1 -1
- package/android/src/main/java/com/trustchex/reactnativesdk/mrz/MRZValidationModule.kt +0 -785
- package/android/src/main/java/com/trustchex/reactnativesdk/mrz/MRZValidator.kt +0 -419
- package/ios/MRZValidation.m +0 -39
- package/ios/MRZValidation.swift +0 -802
- package/ios/MRZValidator.swift +0 -466
|
@@ -2,10 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
/* eslint-disable react-native/no-inline-styles */
|
|
4
4
|
import React, { useEffect, useState, useRef, useCallback } from 'react';
|
|
5
|
-
import { View, StyleSheet, Text as TextView, Platform, StatusBar, Vibration, Linking, Image, ActivityIndicator, PermissionsAndroid, Dimensions } from 'react-native';
|
|
5
|
+
import { View, StyleSheet, Text as TextView, Platform, StatusBar, Vibration, Linking, Image, ActivityIndicator, PermissionsAndroid, Dimensions, ScrollView } from 'react-native';
|
|
6
6
|
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
|
7
7
|
import { TrustchexCamera } from "./TrustchexCamera.js";
|
|
8
8
|
import { NativeModules } from 'react-native';
|
|
9
|
+
import mrzUtils from "../Libs/mrz.utils.js";
|
|
9
10
|
import { useKeepAwake } from "../Libs/native-keep-awake.utils.js";
|
|
10
11
|
import { useIsFocused } from '@react-navigation/native';
|
|
11
12
|
import { useTranslation } from 'react-i18next';
|
|
@@ -24,11 +25,12 @@ const HOLOGRAM_IMAGE_COUNT = 12;
|
|
|
24
25
|
const HOLOGRAM_DETECTION_THRESHOLD = 1000; // Lowered for better sensitivity while still filtering noise
|
|
25
26
|
const HOLOGRAM_DETECTION_RETRY_COUNT = 3; // More attempts but faster each (~1s per attempt)
|
|
26
27
|
const SECOND_FACE_DETECTION_RETRY_COUNT = 10;
|
|
27
|
-
const MIN_BRIGHTNESS_THRESHOLD =
|
|
28
|
+
const MIN_BRIGHTNESS_THRESHOLD = 45;
|
|
28
29
|
const MAX_CONSECUTIVE_QUALITY_FAILURES = 30;
|
|
29
30
|
const IdentityDocumentCamera = ({
|
|
30
31
|
onlyMRZScan,
|
|
31
|
-
onIdentityDocumentScanned
|
|
32
|
+
onIdentityDocumentScanned,
|
|
33
|
+
testMode = false
|
|
32
34
|
}) => {
|
|
33
35
|
useKeepAwake();
|
|
34
36
|
const theme = useTheme();
|
|
@@ -84,6 +86,9 @@ const IdentityDocumentCamera = ({
|
|
|
84
86
|
// Barcode caching - persist detected barcode across frames for reliability
|
|
85
87
|
const cachedBarcode = useRef(null);
|
|
86
88
|
|
|
89
|
+
// Test mode tracking
|
|
90
|
+
const [testModeData, setTestModeData] = useState(null);
|
|
91
|
+
|
|
87
92
|
// Helper to compare MRZ field values (ignore raw text variations)
|
|
88
93
|
const areMRZFieldsEqual = useCallback((fields1, fields2) => {
|
|
89
94
|
if (!fields1 || !fields2) return false;
|
|
@@ -355,7 +360,7 @@ const IdentityDocumentCamera = ({
|
|
|
355
360
|
}, 1000);
|
|
356
361
|
}
|
|
357
362
|
}, [setIsTorchOn]);
|
|
358
|
-
const handleFaceAndText = useCallback(async (text, faces, frameWidth, frameHeight, barcode, image, elementsOutside,
|
|
363
|
+
const handleFaceAndText = useCallback(async (text, faces, frameWidth, frameHeight, barcode, image, elementsOutside, scannedText) => {
|
|
359
364
|
const detectDocumentType = (facesParam, ocrText, mrzFields, frameWidthParam, mrzTextParam) => {
|
|
360
365
|
// Relaxed signature detection: matches signature/imza variants and OCR errors
|
|
361
366
|
const hasSignatureMatch = /s[gi]g?n[au]?t[u]?r|imz[a]s?/i.test(ocrText);
|
|
@@ -530,17 +535,44 @@ const IdentityDocumentCamera = ({
|
|
|
530
535
|
setStatus('SEARCHING');
|
|
531
536
|
return;
|
|
532
537
|
}
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
valid:
|
|
541
|
-
fields: null
|
|
538
|
+
|
|
539
|
+
// Use JavaScript MRZ validation with corrections
|
|
540
|
+
// Prefer MRZ-only text if available (from detected MRZ blocks),
|
|
541
|
+
// otherwise fall back to all text (for backward compatibility)
|
|
542
|
+
const textForValidation = scannedText?.mrzOnlyText || text;
|
|
543
|
+
const mrzValidationResult = mrzUtils.validateMRZWithCorrections(textForValidation);
|
|
544
|
+
const parsedMRZData = {
|
|
545
|
+
valid: mrzValidationResult.valid,
|
|
546
|
+
fields: mrzValidationResult.fields || null
|
|
542
547
|
};
|
|
543
|
-
|
|
548
|
+
|
|
549
|
+
// Capture test mode data
|
|
550
|
+
if (testMode && text && text.includes('<')) {
|
|
551
|
+
const mrzOnlyText = scannedText?.mrzOnlyText || text;
|
|
552
|
+
setTestModeData({
|
|
553
|
+
mrzText: mrzOnlyText,
|
|
554
|
+
timestamp: Date.now()
|
|
555
|
+
});
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
// Log MRZ validation details for debugging
|
|
559
|
+
if (isDebugEnabled() && text && text.includes('<')) {
|
|
560
|
+
const mrzLines = text.split('\n').filter(line => line.includes('<') && line.length > 20);
|
|
561
|
+
if (mrzLines.length >= 2) {
|
|
562
|
+
console.log('[MRZ Debug] Raw OCR text lines:', mrzLines.map(l => `"${l}"`));
|
|
563
|
+
console.log('[MRZ Debug] Validation result:', {
|
|
564
|
+
valid: mrzValidationResult.valid,
|
|
565
|
+
format: mrzValidationResult.format,
|
|
566
|
+
documentCode: mrzValidationResult.fields?.documentCode,
|
|
567
|
+
documentNumber: mrzValidationResult.fields?.documentNumber,
|
|
568
|
+
optional1: mrzValidationResult.fields?.optional1,
|
|
569
|
+
error: mrzValidationResult.error
|
|
570
|
+
});
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
// Extract raw MRZ lines from text if validation succeeded
|
|
575
|
+
const mrzText = parsedMRZData.valid ? mrzUtils.fixMRZ(text) : null;
|
|
544
576
|
|
|
545
577
|
// MRZ stability check - require consistent valid reads to avoid OCR noise
|
|
546
578
|
// Compare parsed field values instead of raw text to handle OCR variations in filler characters
|
|
@@ -609,9 +641,17 @@ const IdentityDocumentCamera = ({
|
|
|
609
641
|
// CRITICAL: Only accept MRZ with valid checksums AND consistent reads
|
|
610
642
|
// AND ensure all required fields are present
|
|
611
643
|
const mrzAccepted = parsedMRZData?.valid === true && hasRequiredFields && mrzStableAndValid;
|
|
644
|
+
|
|
645
|
+
// For Turkish ID cards, barcode should match MRZ optional1 (serial number)
|
|
646
|
+
// But some cards have encoding differences, so be lenient
|
|
612
647
|
const barcodeMatchesMRZ = barcodeToUse?.rawValue?.trim() === parsedMRZData?.fields?.optional1?.trim();
|
|
648
|
+
|
|
649
|
+
// If barcode doesn't match exactly, check if it contains the optional1 value
|
|
650
|
+
const barcodeContainsMRZ = barcodeToUse?.rawValue?.includes(parsedMRZData?.fields?.optional1?.trim() || '') || parsedMRZData?.fields?.optional1?.includes(barcodeToUse?.rawValue?.trim() || '');
|
|
651
|
+
|
|
613
652
|
// Require barcode for all documents (no special card fallback)
|
|
614
|
-
|
|
653
|
+
// Accept if exact match OR if one contains the other (handles encoding differences)
|
|
654
|
+
const barcodeAccepted = onlyMRZScan || barcodeMatchesMRZ || !!barcodeToUse?.rawValue && barcodeContainsMRZ;
|
|
615
655
|
|
|
616
656
|
// CRITICAL: Require all document elements to be in frame before accepting
|
|
617
657
|
// For ID_BACK: MRZ + barcode (check directly, not via state to avoid timing issues)
|
|
@@ -652,8 +692,11 @@ const IdentityDocumentCamera = ({
|
|
|
652
692
|
onlyMRZScan,
|
|
653
693
|
hasBarcodeValue: !!barcodeToUse?.rawValue,
|
|
654
694
|
barcodeMatchesMRZ,
|
|
695
|
+
barcodeContainsMRZ,
|
|
655
696
|
mrzOptional1: parsedMRZData?.fields?.optional1,
|
|
656
697
|
barcodeValue: barcodeToUse?.rawValue,
|
|
698
|
+
barcodeValueTrimmed: barcodeToUse?.rawValue?.trim(),
|
|
699
|
+
optional1Trimmed: parsedMRZData?.fields?.optional1?.trim(),
|
|
657
700
|
barcodeSource: barcodeToUse === cachedBarcode.current ? 'cached' : 'current'
|
|
658
701
|
});
|
|
659
702
|
}
|
|
@@ -1158,7 +1201,7 @@ const IdentityDocumentCamera = ({
|
|
|
1158
1201
|
}
|
|
1159
1202
|
setStatus('SCANNING');
|
|
1160
1203
|
}
|
|
1161
|
-
}, [nextStep, frameDimensions, currentHologramImage, currentFaceImage, hasRequiredMRZFields, areMRZFieldsEqual, detectedDocumentType, onlyMRZScan, isTorchOn, setIsTorchOn, setNextStepAndVibrate, onIdentityDocumentScanned, logMRZDetails, logMRZValidationFailure, currentSecondaryFaceImage, detectHologramNative]);
|
|
1204
|
+
}, [nextStep, frameDimensions, currentHologramImage, currentFaceImage, hasRequiredMRZFields, areMRZFieldsEqual, detectedDocumentType, onlyMRZScan, isTorchOn, setIsTorchOn, setNextStepAndVibrate, onIdentityDocumentScanned, logMRZDetails, logMRZValidationFailure, currentSecondaryFaceImage, detectHologramNative, mrzUtils]);
|
|
1162
1205
|
const handleFrame = useCallback(async event => {
|
|
1163
1206
|
if (!isCameraInitialized.current) {
|
|
1164
1207
|
return;
|
|
@@ -1178,7 +1221,15 @@ const IdentityDocumentCamera = ({
|
|
|
1178
1221
|
}
|
|
1179
1222
|
const avgBrightness = brightnessHistory.current.reduce((a, b) => a + b, 0) / brightnessHistory.current.length;
|
|
1180
1223
|
const isOverallBright = avgBrightness >= MIN_BRIGHTNESS_THRESHOLD;
|
|
1181
|
-
|
|
1224
|
+
|
|
1225
|
+
// Check brightness in center region (area of interest)
|
|
1226
|
+
let isRegionBright = false;
|
|
1227
|
+
try {
|
|
1228
|
+
isRegionBright = await OpenCVModule.isRectangularRegionBright(base64Image, Math.round(frame.width * 0.2), Math.round(frame.height * 0.2), Math.round(frame.width * 0.6), Math.round(frame.height * 0.6), MIN_BRIGHTNESS_THRESHOLD);
|
|
1229
|
+
} catch (error) {
|
|
1230
|
+
isRegionBright = isOverallBright;
|
|
1231
|
+
}
|
|
1232
|
+
setIsBrightnessLow(!isRegionBright);
|
|
1182
1233
|
|
|
1183
1234
|
// Check blur only in center region (area of interest) to avoid false positives
|
|
1184
1235
|
// from iOS depth-of-field background blur
|
|
@@ -1204,7 +1255,7 @@ const IdentityDocumentCamera = ({
|
|
|
1204
1255
|
}
|
|
1205
1256
|
|
|
1206
1257
|
// Only proceed if image quality is acceptable
|
|
1207
|
-
const hasAcceptableQuality =
|
|
1258
|
+
const hasAcceptableQuality = isRegionBright && isNotBlurry;
|
|
1208
1259
|
|
|
1209
1260
|
// Store quality metrics in ref for access in handleFaceAndText callback
|
|
1210
1261
|
lastFrameQuality.current = {
|
|
@@ -1245,6 +1296,8 @@ const IdentityDocumentCamera = ({
|
|
|
1245
1296
|
const resultText = textBlocks.map(b => b.text).join('\n');
|
|
1246
1297
|
const scannedText = {
|
|
1247
1298
|
resultText,
|
|
1299
|
+
mrzOnlyText: undefined,
|
|
1300
|
+
// Will be set below if MRZ blocks detected
|
|
1248
1301
|
blocks: textBlocks.map(block => ({
|
|
1249
1302
|
blockText: block.text || '',
|
|
1250
1303
|
blockFrame: block.blockFrame ?? {
|
|
@@ -1415,6 +1468,12 @@ const IdentityDocumentCamera = ({
|
|
|
1415
1468
|
const mrzBlocks = textBlocks.filter(block => block.blockFrame && block.blockFrame.y > bottomHalf && mrzPattern.test(block.text));
|
|
1416
1469
|
console.log('[Debug] MRZ blocks found:', mrzBlocks.length);
|
|
1417
1470
|
if (mrzBlocks.length > 0) {
|
|
1471
|
+
// Extract MRZ-only text from detected blocks (sorted by Y position for correct line order)
|
|
1472
|
+
const sortedMrzBlocks = [...mrzBlocks].sort((a, b) => (a.blockFrame?.y || 0) - (b.blockFrame?.y || 0));
|
|
1473
|
+
scannedText.mrzOnlyText = sortedMrzBlocks.map(b => b.text).join('\n');
|
|
1474
|
+
if (isDebugEnabled()) {
|
|
1475
|
+
console.log('[MRZ Extraction] Using only MRZ blocks:', scannedText.mrzOnlyText.substring(0, 100));
|
|
1476
|
+
}
|
|
1418
1477
|
const minX = Math.min(...mrzBlocks.map(b => b.blockFrame.x));
|
|
1419
1478
|
const minY = Math.min(...mrzBlocks.map(b => b.blockFrame.y));
|
|
1420
1479
|
const maxX = Math.max(...mrzBlocks.map(b => b.blockFrame.x + b.blockFrame.width));
|
|
@@ -1652,7 +1711,7 @@ const IdentityDocumentCamera = ({
|
|
|
1652
1711
|
}
|
|
1653
1712
|
}
|
|
1654
1713
|
setElementsOutsideScanArea(outsideElements);
|
|
1655
|
-
handleFaceAndText(scannedText.resultText ?? '', detectedFaces, frame.width, frame.height, barcodes.length ? barcodes[0] : undefined, base64Image, outsideElements.length > 0,
|
|
1714
|
+
handleFaceAndText(scannedText.resultText ?? '', detectedFaces, frame.width, frame.height, barcodes.length ? barcodes[0] : undefined, base64Image, outsideElements.length > 0, scannedText);
|
|
1656
1715
|
} catch (error) {
|
|
1657
1716
|
console.warn('Frame processing error:', error?.message);
|
|
1658
1717
|
}
|
|
@@ -1737,7 +1796,6 @@ const IdentityDocumentCamera = ({
|
|
|
1737
1796
|
enableFrameProcessing: isActive,
|
|
1738
1797
|
enableFaceDetection: isActive && faceDetectionEnabled,
|
|
1739
1798
|
enableTextRecognition: isActive,
|
|
1740
|
-
enableMrzValidation: isActive,
|
|
1741
1799
|
enableBarcodeScanning: isActive && nextStep === 'SCAN_ID_BACK',
|
|
1742
1800
|
includeBase64: isActive,
|
|
1743
1801
|
targetFps: 10,
|
|
@@ -2176,32 +2234,6 @@ const IdentityDocumentCamera = ({
|
|
|
2176
2234
|
}],
|
|
2177
2235
|
children: `${currentSecondaryFaceImage ? '✓ ' : ''}2nd Face`
|
|
2178
2236
|
})]
|
|
2179
|
-
}), isDebugEnabled() && /*#__PURE__*/_jsxs(View, {
|
|
2180
|
-
style: styles.imageContainer,
|
|
2181
|
-
children: [_currentHologramMaskImage ? /*#__PURE__*/_jsx(Image, {
|
|
2182
|
-
source: {
|
|
2183
|
-
uri: `data:image/jpeg;base64,${_currentHologramMaskImage}`
|
|
2184
|
-
},
|
|
2185
|
-
style: styles.faceImage
|
|
2186
|
-
}) : /*#__PURE__*/_jsx(View, {
|
|
2187
|
-
style: [styles.faceImage, {
|
|
2188
|
-
backgroundColor: '#333',
|
|
2189
|
-
justifyContent: 'center'
|
|
2190
|
-
}],
|
|
2191
|
-
children: /*#__PURE__*/_jsx(TextView, {
|
|
2192
|
-
style: {
|
|
2193
|
-
color: '#666',
|
|
2194
|
-
fontSize: 10,
|
|
2195
|
-
textAlign: 'center'
|
|
2196
|
-
},
|
|
2197
|
-
children: "Waiting..."
|
|
2198
|
-
})
|
|
2199
|
-
}), /*#__PURE__*/_jsx(TextView, {
|
|
2200
|
-
style: [styles.imageContainerText, _currentHologramMaskImage && {
|
|
2201
|
-
color: '#4CAF50'
|
|
2202
|
-
}],
|
|
2203
|
-
children: `${_currentHologramMaskImage ? '✓ ' : ''}Mask`
|
|
2204
|
-
})]
|
|
2205
2237
|
}), isDebugEnabled() && /*#__PURE__*/_jsxs(View, {
|
|
2206
2238
|
style: styles.imageContainer,
|
|
2207
2239
|
children: [currentHologramImage ? /*#__PURE__*/_jsx(Image, {
|
|
@@ -2261,31 +2293,30 @@ const IdentityDocumentCamera = ({
|
|
|
2261
2293
|
children: `${currentHologramImage ? '✓ ' : latestHologramFaceImage ? '⏳ ' : ''}Hologram`
|
|
2262
2294
|
})]
|
|
2263
2295
|
}), isDebugEnabled() && /*#__PURE__*/_jsxs(View, {
|
|
2264
|
-
style: styles.
|
|
2265
|
-
children: [/*#__PURE__*/_jsx(
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
style: styles.
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
}), /*#__PURE__*/_jsx(TextView, {
|
|
2284
|
-
style: styles.debugInfoText,
|
|
2285
|
-
children: `Brightness: ${isBrightnessLow ? '⚠️ LOW' : '✓ OK'}`
|
|
2296
|
+
style: styles.imageContainer,
|
|
2297
|
+
children: [_currentHologramMaskImage ? /*#__PURE__*/_jsx(Image, {
|
|
2298
|
+
source: {
|
|
2299
|
+
uri: `data:image/jpeg;base64,${_currentHologramMaskImage}`
|
|
2300
|
+
},
|
|
2301
|
+
style: styles.faceImage
|
|
2302
|
+
}) : /*#__PURE__*/_jsx(View, {
|
|
2303
|
+
style: [styles.faceImage, {
|
|
2304
|
+
backgroundColor: '#333',
|
|
2305
|
+
justifyContent: 'center'
|
|
2306
|
+
}],
|
|
2307
|
+
children: /*#__PURE__*/_jsx(TextView, {
|
|
2308
|
+
style: {
|
|
2309
|
+
color: '#666',
|
|
2310
|
+
fontSize: 10,
|
|
2311
|
+
textAlign: 'center'
|
|
2312
|
+
},
|
|
2313
|
+
children: "Waiting..."
|
|
2314
|
+
})
|
|
2286
2315
|
}), /*#__PURE__*/_jsx(TextView, {
|
|
2287
|
-
style: styles.
|
|
2288
|
-
|
|
2316
|
+
style: [styles.imageContainerText, _currentHologramMaskImage && {
|
|
2317
|
+
color: '#4CAF50'
|
|
2318
|
+
}],
|
|
2319
|
+
children: `${_currentHologramMaskImage ? '✓ ' : ''}Mask`
|
|
2289
2320
|
})]
|
|
2290
2321
|
})]
|
|
2291
2322
|
})
|
|
@@ -2315,98 +2346,123 @@ const IdentityDocumentCamera = ({
|
|
|
2315
2346
|
loop: true,
|
|
2316
2347
|
autoPlay: true
|
|
2317
2348
|
}) : null
|
|
2318
|
-
}), isDebugEnabled() && /*#__PURE__*/
|
|
2349
|
+
}), isDebugEnabled() && /*#__PURE__*/_jsx(SafeAreaView, {
|
|
2319
2350
|
style: {
|
|
2320
2351
|
position: 'absolute',
|
|
2321
|
-
top:
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
borderWidth: 1,
|
|
2327
|
-
borderColor: '#FF6B6B',
|
|
2328
|
-
maxWidth: 200
|
|
2352
|
+
top: 0,
|
|
2353
|
+
left: 0,
|
|
2354
|
+
right: 0,
|
|
2355
|
+
alignItems: 'center',
|
|
2356
|
+
pointerEvents: 'none'
|
|
2329
2357
|
},
|
|
2330
|
-
children:
|
|
2331
|
-
style: {
|
|
2332
|
-
color: '#FF6B6B',
|
|
2333
|
-
fontSize: 11,
|
|
2334
|
-
fontWeight: 'bold',
|
|
2335
|
-
marginBottom: 6
|
|
2336
|
-
},
|
|
2337
|
-
children: "\uD83D\uDC1B DEBUG MODE"
|
|
2338
|
-
}), /*#__PURE__*/_jsx(TextView, {
|
|
2339
|
-
style: {
|
|
2340
|
-
color: '#88D8B0',
|
|
2341
|
-
fontSize: 9,
|
|
2342
|
-
marginBottom: 2
|
|
2343
|
-
},
|
|
2344
|
-
children: `Step: ${nextStep}`
|
|
2345
|
-
}), /*#__PURE__*/_jsx(TextView, {
|
|
2346
|
-
style: {
|
|
2347
|
-
color: '#88D8B0',
|
|
2348
|
-
fontSize: 9,
|
|
2349
|
-
marginBottom: 2
|
|
2350
|
-
},
|
|
2351
|
-
children: `Status: ${status}`
|
|
2352
|
-
}), /*#__PURE__*/_jsx(TextView, {
|
|
2353
|
-
style: {
|
|
2354
|
-
color: '#88D8B0',
|
|
2355
|
-
fontSize: 9,
|
|
2356
|
-
marginBottom: 2
|
|
2357
|
-
},
|
|
2358
|
-
children: `Doc Type: ${detectedDocumentType}`
|
|
2359
|
-
}), /*#__PURE__*/_jsx(TextView, {
|
|
2358
|
+
children: /*#__PURE__*/_jsxs(View, {
|
|
2360
2359
|
style: {
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
2360
|
+
marginTop: 10,
|
|
2361
|
+
backgroundColor: 'rgba(0, 0, 0, 0.85)',
|
|
2362
|
+
padding: 10,
|
|
2363
|
+
borderRadius: 8,
|
|
2364
|
+
borderWidth: 1,
|
|
2365
|
+
borderColor: '#FF6B6B',
|
|
2366
|
+
minWidth: 200
|
|
2364
2367
|
},
|
|
2365
|
-
children: `Face: ${currentFaceImage ? '✓' : '✗'}`
|
|
2366
|
-
}), !onlyMRZScan && /*#__PURE__*/_jsxs(_Fragment, {
|
|
2367
2368
|
children: [/*#__PURE__*/_jsx(TextView, {
|
|
2369
|
+
style: {
|
|
2370
|
+
color: '#FF6B6B',
|
|
2371
|
+
fontSize: 11,
|
|
2372
|
+
fontWeight: 'bold',
|
|
2373
|
+
marginBottom: 6,
|
|
2374
|
+
textAlign: 'center'
|
|
2375
|
+
},
|
|
2376
|
+
children: "DEBUG MODE"
|
|
2377
|
+
}), /*#__PURE__*/_jsx(TextView, {
|
|
2368
2378
|
style: {
|
|
2369
2379
|
color: '#88D8B0',
|
|
2370
2380
|
fontSize: 9,
|
|
2371
2381
|
marginBottom: 2
|
|
2372
2382
|
},
|
|
2373
|
-
children: `
|
|
2383
|
+
children: `Step: ${nextStep}`
|
|
2374
2384
|
}), /*#__PURE__*/_jsx(TextView, {
|
|
2375
2385
|
style: {
|
|
2376
2386
|
color: '#88D8B0',
|
|
2377
2387
|
fontSize: 9,
|
|
2378
2388
|
marginBottom: 2
|
|
2379
2389
|
},
|
|
2380
|
-
children: `
|
|
2390
|
+
children: `Status: ${status}`
|
|
2391
|
+
}), /*#__PURE__*/_jsx(TextView, {
|
|
2392
|
+
style: {
|
|
2393
|
+
color: '#88D8B0',
|
|
2394
|
+
fontSize: 9,
|
|
2395
|
+
marginBottom: 2
|
|
2396
|
+
},
|
|
2397
|
+
children: `Doc Type: ${detectedDocumentType}`
|
|
2398
|
+
}), /*#__PURE__*/_jsx(TextView, {
|
|
2399
|
+
style: {
|
|
2400
|
+
color: '#88D8B0',
|
|
2401
|
+
fontSize: 9,
|
|
2402
|
+
marginBottom: 2
|
|
2403
|
+
},
|
|
2404
|
+
children: `Brightness: ${isBrightnessLow ? '⚠️ LOW' : '✓'}`
|
|
2405
|
+
}), /*#__PURE__*/_jsx(TextView, {
|
|
2406
|
+
style: {
|
|
2407
|
+
color: '#88D8B0',
|
|
2408
|
+
fontSize: 9,
|
|
2409
|
+
marginBottom: 2
|
|
2410
|
+
},
|
|
2411
|
+
children: `Blur: ${isFrameBlurry ? '⚠️' : '✓'}`
|
|
2412
|
+
}), /*#__PURE__*/_jsx(TextView, {
|
|
2413
|
+
style: {
|
|
2414
|
+
color: '#88D8B0',
|
|
2415
|
+
fontSize: 9,
|
|
2416
|
+
marginBottom: 2
|
|
2417
|
+
},
|
|
2418
|
+
children: `Flash: ${isTorchOn ? '🔦' : '○'}`
|
|
2419
|
+
}), /*#__PURE__*/_jsx(TextView, {
|
|
2420
|
+
style: {
|
|
2421
|
+
color: '#88D8B0',
|
|
2422
|
+
fontSize: 9
|
|
2423
|
+
},
|
|
2424
|
+
children: `Face Detection: ${faceDetectionEnabled ? '✓' : '✗'}`
|
|
2381
2425
|
})]
|
|
2382
|
-
})
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
children: `Blur: ${isFrameBlurry ? '⚠️' : '✓'}`
|
|
2396
|
-
}), /*#__PURE__*/_jsx(TextView, {
|
|
2397
|
-
style: {
|
|
2398
|
-
color: '#88D8B0',
|
|
2399
|
-
fontSize: 9,
|
|
2400
|
-
marginBottom: 2
|
|
2401
|
-
},
|
|
2402
|
-
children: `Flash: ${isTorchOn ? '🔦' : '○'}`
|
|
2403
|
-
}), /*#__PURE__*/_jsx(TextView, {
|
|
2426
|
+
})
|
|
2427
|
+
}), testMode && testModeData && /*#__PURE__*/_jsx(SafeAreaView, {
|
|
2428
|
+
style: {
|
|
2429
|
+
position: 'absolute',
|
|
2430
|
+
bottom: 0,
|
|
2431
|
+
left: 0,
|
|
2432
|
+
right: 0,
|
|
2433
|
+
maxHeight: '40%',
|
|
2434
|
+
backgroundColor: 'rgba(0, 0, 0, 0.95)',
|
|
2435
|
+
borderTopWidth: 2,
|
|
2436
|
+
borderTopColor: '#FFA500'
|
|
2437
|
+
},
|
|
2438
|
+
children: /*#__PURE__*/_jsx(ScrollView, {
|
|
2404
2439
|
style: {
|
|
2405
|
-
|
|
2406
|
-
fontSize: 9
|
|
2440
|
+
flex: 1
|
|
2407
2441
|
},
|
|
2408
|
-
children:
|
|
2409
|
-
|
|
2442
|
+
children: /*#__PURE__*/_jsxs(View, {
|
|
2443
|
+
style: {
|
|
2444
|
+
padding: 10
|
|
2445
|
+
},
|
|
2446
|
+
children: [/*#__PURE__*/_jsx(TextView, {
|
|
2447
|
+
style: {
|
|
2448
|
+
color: '#FFA500',
|
|
2449
|
+
fontSize: 12,
|
|
2450
|
+
fontWeight: 'bold',
|
|
2451
|
+
marginBottom: 8,
|
|
2452
|
+
textAlign: 'center'
|
|
2453
|
+
},
|
|
2454
|
+
children: "MRZ Text Read"
|
|
2455
|
+
}), /*#__PURE__*/_jsx(TextView, {
|
|
2456
|
+
style: {
|
|
2457
|
+
color: '#FFFFFF',
|
|
2458
|
+
fontSize: 9,
|
|
2459
|
+
fontFamily: 'monospace',
|
|
2460
|
+
lineHeight: 16
|
|
2461
|
+
},
|
|
2462
|
+
children: testModeData.mrzText.split('\n').map((line, i) => `Line ${i + 1}: ${line} (${line.length} chars)`).join('\n')
|
|
2463
|
+
})]
|
|
2464
|
+
})
|
|
2465
|
+
})
|
|
2410
2466
|
})]
|
|
2411
2467
|
})]
|
|
2412
2468
|
});
|
|
@@ -2515,7 +2571,7 @@ const styles = StyleSheet.create({
|
|
|
2515
2571
|
display: 'flex',
|
|
2516
2572
|
flexDirection: 'row',
|
|
2517
2573
|
gap: 10,
|
|
2518
|
-
justifyContent: '
|
|
2574
|
+
justifyContent: 'center',
|
|
2519
2575
|
flexWrap: 'wrap'
|
|
2520
2576
|
},
|
|
2521
2577
|
cardDetectionRow: {
|
|
@@ -2555,16 +2611,6 @@ const styles = StyleSheet.create({
|
|
|
2555
2611
|
flexDirection: 'column',
|
|
2556
2612
|
alignItems: 'center'
|
|
2557
2613
|
},
|
|
2558
|
-
debugInfoContainer: {
|
|
2559
|
-
flex: 1,
|
|
2560
|
-
paddingLeft: 10
|
|
2561
|
-
},
|
|
2562
|
-
debugInfoText: {
|
|
2563
|
-
color: 'white',
|
|
2564
|
-
fontSize: 8,
|
|
2565
|
-
fontWeight: 'bold',
|
|
2566
|
-
marginBottom: 2
|
|
2567
|
-
},
|
|
2568
2614
|
guide: {
|
|
2569
2615
|
flex: 1,
|
|
2570
2616
|
display: 'flex',
|
|
@@ -39,12 +39,9 @@ const QrCodeScannerCamera = ({
|
|
|
39
39
|
right: width * (0.97 - marginBuffer),
|
|
40
40
|
bottom: height * (0.65 - marginBuffer)
|
|
41
41
|
};
|
|
42
|
-
console.log(`[QR Frame Check] Frame: ${width}x${height}, Scanning: left=${scanningFrame.left}, top=${scanningFrame.top}, right=${scanningFrame.right}, bottom=${scanningFrame.bottom}`);
|
|
43
|
-
console.log(`[QR Frame Check] Code bbox: left=${boundingBox.left}, top=${boundingBox.top}, right=${boundingBox.right}, bottom=${boundingBox.bottom}`);
|
|
44
42
|
|
|
45
43
|
// Check if entire bounding box is within scanning frame (with margin buffer)
|
|
46
44
|
const isInFrame = boundingBox.left >= scanningFrame.left && boundingBox.top >= scanningFrame.top && boundingBox.right <= scanningFrame.right && boundingBox.bottom <= scanningFrame.bottom;
|
|
47
|
-
console.log(`[QR Frame Check] Is in frame: ${isInFrame}`);
|
|
48
45
|
return isInFrame;
|
|
49
46
|
};
|
|
50
47
|
const handleFrame = useCallback(async event => {
|
|
@@ -14,30 +14,29 @@ export const ENHANCEMENT_CONFIG = {
|
|
|
14
14
|
brightness: {
|
|
15
15
|
thresholds: {
|
|
16
16
|
general: {
|
|
17
|
-
low:
|
|
18
|
-
high:
|
|
19
|
-
target:
|
|
17
|
+
low: 60,
|
|
18
|
+
high: 140,
|
|
19
|
+
target: 100
|
|
20
20
|
},
|
|
21
21
|
faceDetection: {
|
|
22
|
-
low:
|
|
23
|
-
high:
|
|
24
|
-
target:
|
|
22
|
+
low: 70,
|
|
23
|
+
high: 140,
|
|
24
|
+
target: 105
|
|
25
25
|
},
|
|
26
26
|
mrzScanning: {
|
|
27
|
-
low:
|
|
28
|
-
high:
|
|
29
|
-
target:
|
|
27
|
+
low: 65,
|
|
28
|
+
high: 150,
|
|
29
|
+
target: 100
|
|
30
30
|
}
|
|
31
31
|
},
|
|
32
32
|
adaptiveStep: true,
|
|
33
33
|
maxStepSize: 2,
|
|
34
|
-
|
|
35
|
-
hysteresis: 5 // Dead zone to prevent oscillation
|
|
34
|
+
hysteresis: 5
|
|
36
35
|
},
|
|
37
36
|
contrast: {
|
|
38
37
|
enabled: true,
|
|
39
38
|
clahe: {
|
|
40
|
-
clipLimit:
|
|
39
|
+
clipLimit: 1.5,
|
|
41
40
|
tileGridSize: [8, 8]
|
|
42
41
|
},
|
|
43
42
|
applyWhen: {
|