react-native-biometric-verifier 0.0.48 → 0.0.49
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/package.json +1 -1
- package/src/hooks/useGeolocation.js +140 -107
- package/src/index.js +21 -288
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@ import { Platform, PermissionsAndroid } from 'react-native';
|
|
|
3
3
|
import Geolocation from 'react-native-geolocation-service';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
|
-
* Geolocation Hook for
|
|
6
|
+
* FAST + ACCURATE Geolocation Hook (Optimized for Verification)
|
|
7
7
|
*/
|
|
8
8
|
export const useGeolocation = (notifyMessage) => {
|
|
9
9
|
const locationWatchId = useRef(null);
|
|
@@ -36,7 +36,7 @@ export const useGeolocation = (notifyMessage) => {
|
|
|
36
36
|
|
|
37
37
|
return granted;
|
|
38
38
|
} catch (err) {
|
|
39
|
-
console.error('[
|
|
39
|
+
console.error('[Geo Permissions]', err);
|
|
40
40
|
return false;
|
|
41
41
|
}
|
|
42
42
|
}, [notifyMessage]);
|
|
@@ -52,90 +52,127 @@ export const useGeolocation = (notifyMessage) => {
|
|
|
52
52
|
}, []);
|
|
53
53
|
|
|
54
54
|
/* -------------------------------------------------------------------------- */
|
|
55
|
-
/*
|
|
55
|
+
/* FAST + ACCURATE LOCATION FETCH */
|
|
56
56
|
/* -------------------------------------------------------------------------- */
|
|
57
57
|
const getCurrentLocation = useCallback((options = {}) => {
|
|
58
|
-
return new Promise(
|
|
59
|
-
let
|
|
60
|
-
let
|
|
58
|
+
return new Promise((resolve, reject) => {
|
|
59
|
+
let samples = [];
|
|
60
|
+
let resolved = false;
|
|
61
61
|
|
|
62
62
|
const defaultOptions = {
|
|
63
63
|
enableHighAccuracy: true,
|
|
64
|
-
timeout:
|
|
65
|
-
maximumAge:
|
|
64
|
+
timeout: 8000, // ⏱ fast
|
|
65
|
+
maximumAge: 2000, // allow cached fix
|
|
66
66
|
forceRequestLocation: true,
|
|
67
67
|
showLocationDialog: true,
|
|
68
|
+
interval: 800,
|
|
69
|
+
fastestInterval: 500,
|
|
70
|
+
distanceFilter: 0,
|
|
68
71
|
...options,
|
|
69
72
|
};
|
|
70
73
|
|
|
71
74
|
try {
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
75
|
+
/* ---------------- FAST FIRST FIX ---------------- */
|
|
76
|
+
Geolocation.getCurrentPosition(
|
|
77
|
+
(pos) => {
|
|
78
|
+
if (!pos?.coords || resolved) return;
|
|
79
|
+
|
|
80
|
+
const {
|
|
81
|
+
latitude,
|
|
82
|
+
longitude,
|
|
83
|
+
accuracy,
|
|
84
|
+
altitude = 0,
|
|
85
|
+
speed = 0,
|
|
86
|
+
heading = 0,
|
|
87
|
+
} = pos.coords;
|
|
88
|
+
|
|
89
|
+
// Accept immediately if good enough for verification
|
|
90
|
+
if (accuracy <= 15) {
|
|
91
|
+
resolved = true;
|
|
92
|
+
resolve({
|
|
93
|
+
latitude,
|
|
94
|
+
longitude,
|
|
95
|
+
altitude,
|
|
96
|
+
accuracy,
|
|
97
|
+
speed,
|
|
98
|
+
heading,
|
|
99
|
+
timestamp: pos.timestamp,
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
samples.push({
|
|
104
|
+
latitude,
|
|
105
|
+
longitude,
|
|
106
|
+
altitude,
|
|
107
|
+
accuracy,
|
|
108
|
+
speed,
|
|
109
|
+
heading,
|
|
110
|
+
timestamp: pos.timestamp,
|
|
111
|
+
});
|
|
112
|
+
},
|
|
113
|
+
(err) => {
|
|
114
|
+
console.warn('[Geo Fast Fix Failed]', err);
|
|
115
|
+
},
|
|
116
|
+
defaultOptions
|
|
117
|
+
);
|
|
118
|
+
|
|
119
|
+
/* ---------------- BACKGROUND IMPROVEMENT ---------------- */
|
|
120
|
+
locationWatchId.current = Geolocation.watchPosition(
|
|
121
|
+
(p) => {
|
|
122
|
+
if (!p?.coords) return;
|
|
123
|
+
|
|
124
|
+
const {
|
|
125
|
+
latitude,
|
|
126
|
+
longitude,
|
|
127
|
+
accuracy,
|
|
128
|
+
altitude = 0,
|
|
129
|
+
speed = 0,
|
|
130
|
+
heading = 0,
|
|
131
|
+
} = p.coords;
|
|
132
|
+
|
|
133
|
+
/* -------- LIGHT NOISE FILTERS -------- */
|
|
134
|
+
if (accuracy > 40) return;
|
|
135
|
+
if (speed > 50) return;
|
|
136
|
+
|
|
137
|
+
samples.push({
|
|
138
|
+
latitude,
|
|
139
|
+
longitude,
|
|
140
|
+
altitude,
|
|
141
|
+
accuracy,
|
|
142
|
+
speed,
|
|
143
|
+
heading,
|
|
144
|
+
timestamp: p.timestamp,
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
if (samples.length > 8) samples.shift();
|
|
148
|
+
|
|
149
|
+
samples.sort((a, b) => a.accuracy - b.accuracy);
|
|
150
|
+
const best = samples[0];
|
|
151
|
+
|
|
152
|
+
// Upgrade accuracy silently
|
|
153
|
+
if (best.accuracy <= 6 && !resolved) {
|
|
154
|
+
resolved = true;
|
|
120
155
|
stopLocationWatching();
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
156
|
+
resolve(best);
|
|
157
|
+
}
|
|
158
|
+
},
|
|
159
|
+
(err) => {
|
|
160
|
+
console.error('[Geo Watch Error]', err);
|
|
161
|
+
stopLocationWatching();
|
|
162
|
+
},
|
|
163
|
+
defaultOptions
|
|
164
|
+
);
|
|
165
|
+
|
|
166
|
+
/* ---------------- HARD STOP ---------------- */
|
|
167
|
+
setTimeout(() => {
|
|
168
|
+
if (!resolved && samples.length) {
|
|
169
|
+
resolved = true;
|
|
170
|
+
stopLocationWatching();
|
|
171
|
+
samples.sort((a, b) => a.accuracy - b.accuracy);
|
|
172
|
+
resolve(samples[0]);
|
|
173
|
+
}
|
|
174
|
+
}, 5000);
|
|
137
175
|
} catch (err) {
|
|
138
|
-
console.error('[Geolocation] Failed to get location:', err);
|
|
139
176
|
stopLocationWatching();
|
|
140
177
|
notifyMessage?.('Failed to get location', 'error');
|
|
141
178
|
reject(err);
|
|
@@ -150,28 +187,31 @@ export const useGeolocation = (notifyMessage) => {
|
|
|
150
187
|
(lat1, lon1, lat2, lon2, alt1 = 0, alt2 = 0) => {
|
|
151
188
|
try {
|
|
152
189
|
if (
|
|
153
|
-
typeof lat1 !== 'number' ||
|
|
154
|
-
typeof
|
|
155
|
-
|
|
190
|
+
typeof lat1 !== 'number' ||
|
|
191
|
+
typeof lon1 !== 'number' ||
|
|
192
|
+
typeof lat2 !== 'number' ||
|
|
193
|
+
typeof lon2 !== 'number'
|
|
156
194
|
) return Infinity;
|
|
157
195
|
|
|
158
|
-
const toRad =
|
|
159
|
-
const R = 6371e3;
|
|
196
|
+
const toRad = deg => deg * Math.PI / 180;
|
|
197
|
+
const R = 6371e3;
|
|
198
|
+
|
|
160
199
|
const φ1 = toRad(lat1);
|
|
161
200
|
const φ2 = toRad(lat2);
|
|
162
201
|
const Δφ = toRad(lat2 - lat1);
|
|
163
202
|
const Δλ = toRad(lon2 - lon1);
|
|
164
203
|
|
|
165
|
-
const a =
|
|
166
|
-
|
|
167
|
-
|
|
204
|
+
const a =
|
|
205
|
+
Math.sin(Δφ / 2) ** 2 +
|
|
206
|
+
Math.cos(φ1) * Math.cos(φ2) *
|
|
207
|
+
Math.sin(Δλ / 2) ** 2;
|
|
208
|
+
|
|
168
209
|
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
|
|
169
210
|
const surfaceDistance = R * c;
|
|
170
211
|
|
|
171
|
-
const
|
|
172
|
-
return Math.sqrt(surfaceDistance ** 2 +
|
|
173
|
-
} catch
|
|
174
|
-
console.error('[Geolocation Distance] Error:', err);
|
|
212
|
+
const heightDiff = Math.abs(alt1 - alt2);
|
|
213
|
+
return Math.sqrt(surfaceDistance ** 2 + heightDiff ** 2);
|
|
214
|
+
} catch {
|
|
175
215
|
return Infinity;
|
|
176
216
|
}
|
|
177
217
|
},
|
|
@@ -179,25 +219,20 @@ export const useGeolocation = (notifyMessage) => {
|
|
|
179
219
|
);
|
|
180
220
|
|
|
181
221
|
const calculateSafeDistance = useCallback(
|
|
182
|
-
(
|
|
222
|
+
(p1, p2) => {
|
|
183
223
|
try {
|
|
184
|
-
const lat1 =
|
|
185
|
-
|
|
186
|
-
const
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
const
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
const alt2 = point2.altitude || point2.alt || 0;
|
|
197
|
-
|
|
198
|
-
return calculateEnhancedDistance(lat1, lon1, lat2, lon2, alt1, alt2);
|
|
199
|
-
} catch (err) {
|
|
200
|
-
console.error('[Geolocation SafeDistance] Error:', err);
|
|
224
|
+
const lat1 = p1.latitude ?? p1.lat;
|
|
225
|
+
const lon1 = p1.longitude ?? p1.lng ?? p1.lon;
|
|
226
|
+
const alt1 = p1.altitude ?? p1.alt ?? 0;
|
|
227
|
+
|
|
228
|
+
const lat2 = p2.latitude ?? p2.lat;
|
|
229
|
+
const lon2 = p2.longitude ?? p2.lng ?? p2.lon;
|
|
230
|
+
const alt2 = p2.altitude ?? p2.alt ?? 0;
|
|
231
|
+
|
|
232
|
+
return calculateEnhancedDistance(
|
|
233
|
+
lat1, lon1, lat2, lon2, alt1, alt2
|
|
234
|
+
);
|
|
235
|
+
} catch {
|
|
201
236
|
return Infinity;
|
|
202
237
|
}
|
|
203
238
|
},
|
|
@@ -205,15 +240,13 @@ export const useGeolocation = (notifyMessage) => {
|
|
|
205
240
|
);
|
|
206
241
|
|
|
207
242
|
/* -------------------------------------------------------------------------- */
|
|
208
|
-
/*
|
|
243
|
+
/* API */
|
|
209
244
|
/* -------------------------------------------------------------------------- */
|
|
210
|
-
const getCurrentWatchId = useCallback(() => locationWatchId.current, []);
|
|
211
|
-
|
|
212
245
|
return {
|
|
213
246
|
requestLocationPermission,
|
|
214
247
|
getCurrentLocation,
|
|
215
248
|
stopLocationWatching,
|
|
216
|
-
getCurrentWatchId,
|
|
249
|
+
getCurrentWatchId: () => locationWatchId.current,
|
|
217
250
|
calculateEnhancedDistance,
|
|
218
251
|
calculateSafeDistance,
|
|
219
252
|
};
|
package/src/index.js
CHANGED
|
@@ -18,11 +18,9 @@ import {
|
|
|
18
18
|
} from "react-native";
|
|
19
19
|
import Icon from "react-native-vector-icons/MaterialIcons";
|
|
20
20
|
|
|
21
|
-
// Custom hooks
|
|
21
|
+
// Custom hooks - Removed unnecessary ones
|
|
22
22
|
import { useCountdown } from "./hooks/useCountdown";
|
|
23
23
|
import { useGeolocation } from "./hooks/useGeolocation";
|
|
24
|
-
import { useWifiService } from "./hooks/useWifiService";
|
|
25
|
-
import { useBluetoothService } from "./hooks/useBluetoothService";
|
|
26
24
|
import { useImageProcessing } from "./hooks/useImageProcessing";
|
|
27
25
|
import { useNotifyMessage } from "./hooks/useNotifyMessage";
|
|
28
26
|
import { useSafeCallback } from "./hooks/useSafeCallback";
|
|
@@ -54,13 +52,12 @@ const BiometricModal = forwardRef(({
|
|
|
54
52
|
navigation,
|
|
55
53
|
MaxDistanceMeters = 30,
|
|
56
54
|
duration = 100,
|
|
57
|
-
useWiFiFingerprinting = true,
|
|
58
55
|
}, ref) => {
|
|
59
56
|
// Custom hooks - Initialize notification hook first
|
|
60
57
|
const { notification, fadeAnim, slideAnim, notifyMessage, clearNotification } = useNotifyMessage();
|
|
61
58
|
const { countdown, startCountdown, resetCountdown, pauseCountdown, resumeCountdown } = useCountdown(duration);
|
|
62
59
|
|
|
63
|
-
//
|
|
60
|
+
// Only keep geolocation hook
|
|
64
61
|
const {
|
|
65
62
|
requestLocationPermission,
|
|
66
63
|
getCurrentLocation,
|
|
@@ -68,25 +65,10 @@ const BiometricModal = forwardRef(({
|
|
|
68
65
|
calculateSafeDistance,
|
|
69
66
|
} = useGeolocation(notifyMessage);
|
|
70
67
|
|
|
71
|
-
const {
|
|
72
|
-
requestWifiPermissions,
|
|
73
|
-
scanWifiFingerprint,
|
|
74
|
-
matchWifiFingerprint,
|
|
75
|
-
getLocationWithWifi,
|
|
76
|
-
} = useWifiService(notifyMessage);
|
|
77
|
-
|
|
78
|
-
const {
|
|
79
|
-
requestBluetoothPermission,
|
|
80
|
-
startBluetoothScan,
|
|
81
|
-
stopBluetoothScan,
|
|
82
|
-
nearbyDevices,
|
|
83
|
-
clearDevices,
|
|
84
|
-
} = useBluetoothService(notifyMessage);
|
|
85
|
-
|
|
86
68
|
const { convertImageToBase64 } = useImageProcessing();
|
|
87
69
|
const safeCallback = useSafeCallback(callback, notifyMessage);
|
|
88
70
|
|
|
89
|
-
// State
|
|
71
|
+
// State - Simplified
|
|
90
72
|
const [modalVisible, setModalVisible] = useState(false);
|
|
91
73
|
const [cameraType, setCameraType] = useState("back");
|
|
92
74
|
const [state, setState] = useState({
|
|
@@ -96,7 +78,7 @@ const BiometricModal = forwardRef(({
|
|
|
96
78
|
employeeData: null,
|
|
97
79
|
animationState: Global.AnimationStates.qrScan,
|
|
98
80
|
qrData: null,
|
|
99
|
-
|
|
81
|
+
// Removed: wifiReferenceScan
|
|
100
82
|
});
|
|
101
83
|
|
|
102
84
|
// Refs
|
|
@@ -105,8 +87,7 @@ const BiometricModal = forwardRef(({
|
|
|
105
87
|
const responseRef = useRef(null);
|
|
106
88
|
const processedRef = useRef(false);
|
|
107
89
|
const resetTimeoutRef = useRef(null);
|
|
108
|
-
|
|
109
|
-
const wifiScanTimeoutRef = useRef(null);
|
|
90
|
+
// Removed: bleScanTimeoutRef, wifiScanTimeoutRef
|
|
110
91
|
|
|
111
92
|
// Animation values
|
|
112
93
|
const iconScaleAnim = useRef(new Animated.Value(1)).current;
|
|
@@ -120,7 +101,7 @@ const BiometricModal = forwardRef(({
|
|
|
120
101
|
getStatus: () => state,
|
|
121
102
|
}));
|
|
122
103
|
|
|
123
|
-
// Cleanup on unmount
|
|
104
|
+
// Cleanup on unmount - Simplified
|
|
124
105
|
useEffect(() => {
|
|
125
106
|
return () => {
|
|
126
107
|
mountedRef.current = false;
|
|
@@ -129,20 +110,10 @@ const BiometricModal = forwardRef(({
|
|
|
129
110
|
clearTimeout(resetTimeoutRef.current);
|
|
130
111
|
}
|
|
131
112
|
|
|
132
|
-
if (bleScanTimeoutRef.current) {
|
|
133
|
-
clearTimeout(bleScanTimeoutRef.current);
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
if (wifiScanTimeoutRef.current) {
|
|
137
|
-
clearTimeout(wifiScanTimeoutRef.current);
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
stopBluetoothScan();
|
|
141
113
|
stopLocationWatching();
|
|
142
|
-
clearDevices();
|
|
143
114
|
clearNotification();
|
|
144
115
|
};
|
|
145
|
-
}, [
|
|
116
|
+
}, [stopLocationWatching, clearNotification]);
|
|
146
117
|
|
|
147
118
|
// Update dataRef when data changes
|
|
148
119
|
useEffect(() => {
|
|
@@ -202,7 +173,7 @@ const BiometricModal = forwardRef(({
|
|
|
202
173
|
}
|
|
203
174
|
}, [animateIcon, pauseCountdown, resumeCountdown]);
|
|
204
175
|
|
|
205
|
-
// Reset state helper
|
|
176
|
+
// Reset state helper - Simplified
|
|
206
177
|
const resetState = useCallback(() => {
|
|
207
178
|
if (onclose) {
|
|
208
179
|
onclose(false);
|
|
@@ -215,22 +186,19 @@ const BiometricModal = forwardRef(({
|
|
|
215
186
|
employeeData: null,
|
|
216
187
|
animationState: Global.AnimationStates.qrScan,
|
|
217
188
|
qrData: null,
|
|
218
|
-
wifiReferenceScan: null,
|
|
219
189
|
});
|
|
220
190
|
|
|
221
191
|
setModalVisible(false);
|
|
222
192
|
processedRef.current = false;
|
|
223
193
|
resetCountdown();
|
|
224
|
-
stopBluetoothScan();
|
|
225
194
|
stopLocationWatching();
|
|
226
|
-
clearDevices();
|
|
227
195
|
clearNotification();
|
|
228
196
|
|
|
229
197
|
if (resetTimeoutRef.current) {
|
|
230
198
|
clearTimeout(resetTimeoutRef.current);
|
|
231
199
|
resetTimeoutRef.current = null;
|
|
232
200
|
}
|
|
233
|
-
}, [resetCountdown,
|
|
201
|
+
}, [resetCountdown, stopLocationWatching, clearNotification, onclose]);
|
|
234
202
|
|
|
235
203
|
// Error handler
|
|
236
204
|
const handleProcessError = useCallback(
|
|
@@ -276,108 +244,7 @@ const BiometricModal = forwardRef(({
|
|
|
276
244
|
return true;
|
|
277
245
|
}, [apiurl, handleProcessError]);
|
|
278
246
|
|
|
279
|
-
|
|
280
|
-
* WiFi fingerprint scanning with validation
|
|
281
|
-
*/
|
|
282
|
-
const performWiFiScan = useCallback(async () => {
|
|
283
|
-
if (!useWiFiFingerprinting || Platform.OS !== 'android') {
|
|
284
|
-
return { scan: [], score: 0, isMatch: false };
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
try {
|
|
288
|
-
updateState({ loadingType: Global.LoadingTypes.wifiScan });
|
|
289
|
-
|
|
290
|
-
// Request WiFi permissions if needed
|
|
291
|
-
const hasWifiPermission = await requestWifiPermissions();
|
|
292
|
-
if (!hasWifiPermission) {
|
|
293
|
-
return { scan: [], score: 0, isMatch: false };
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
// Perform WiFi scan using the WiFi service hook
|
|
297
|
-
const wifiScan = await scanWifiFingerprint();
|
|
298
|
-
|
|
299
|
-
if (wifiScan.length === 0) {
|
|
300
|
-
notifyMessage("No WiFi networks detected", "warning");
|
|
301
|
-
return { scan: [], score: 0, isMatch: false };
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
// If we have a reference scan, match against it
|
|
305
|
-
if (state.wifiReferenceScan && state.wifiReferenceScan.length > 0) {
|
|
306
|
-
const matchResult = matchWifiFingerprint(wifiScan, state.wifiReferenceScan);
|
|
307
|
-
const isMatch = matchResult.score >= 3; // Threshold of 3 matching APs
|
|
308
|
-
|
|
309
|
-
if (isMatch) {
|
|
310
|
-
notifyMessage(`WiFi fingerprint match: ${matchResult.score.toFixed(1)}`, "success");
|
|
311
|
-
} else {
|
|
312
|
-
notifyMessage(`Weak WiFi match: ${matchResult.score.toFixed(1)}`, "warning");
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
return {
|
|
316
|
-
scan: wifiScan,
|
|
317
|
-
score: matchResult.score,
|
|
318
|
-
isMatch,
|
|
319
|
-
matchDetails: matchResult
|
|
320
|
-
};
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
// Return scan without matching
|
|
324
|
-
return { scan: wifiScan, score: 0, isMatch: false };
|
|
325
|
-
} catch (error) {
|
|
326
|
-
console.warn("WiFi scan failed:", error);
|
|
327
|
-
notifyMessage("WiFi scan unavailable", "warning");
|
|
328
|
-
return { scan: [], score: 0, isMatch: false };
|
|
329
|
-
}
|
|
330
|
-
}, [
|
|
331
|
-
useWiFiFingerprinting,
|
|
332
|
-
requestWifiPermissions,
|
|
333
|
-
scanWifiFingerprint,
|
|
334
|
-
matchWifiFingerprint,
|
|
335
|
-
state.wifiReferenceScan,
|
|
336
|
-
notifyMessage,
|
|
337
|
-
updateState
|
|
338
|
-
]);
|
|
339
|
-
|
|
340
|
-
/**
|
|
341
|
-
* Find consistent BLE devices across multiple samples
|
|
342
|
-
*/
|
|
343
|
-
const findConsistentBLEDevices = useCallback((samples) => {
|
|
344
|
-
const deviceMap = new Map();
|
|
345
|
-
|
|
346
|
-
samples.forEach((sample, sampleIndex) => {
|
|
347
|
-
sample.forEach(device => {
|
|
348
|
-
if (!deviceMap.has(device.id)) {
|
|
349
|
-
deviceMap.set(device.id, {
|
|
350
|
-
...device,
|
|
351
|
-
distances: [],
|
|
352
|
-
samples: 0
|
|
353
|
-
});
|
|
354
|
-
}
|
|
355
|
-
const entry = deviceMap.get(device.id);
|
|
356
|
-
entry.distances.push(parseFloat(device.distance));
|
|
357
|
-
entry.samples++;
|
|
358
|
-
});
|
|
359
|
-
});
|
|
360
|
-
|
|
361
|
-
// Filter for devices seen in at least 2 samples
|
|
362
|
-
return Array.from(deviceMap.values())
|
|
363
|
-
.filter(device => device.samples >= 2)
|
|
364
|
-
.map(device => ({
|
|
365
|
-
...device,
|
|
366
|
-
avgDistance: device.distances.reduce((a, b) => a + b, 0) / device.distances.length,
|
|
367
|
-
stdDev: calculateStdDev(device.distances)
|
|
368
|
-
}))
|
|
369
|
-
.filter(device => device.stdDev < 2);
|
|
370
|
-
}, []);
|
|
371
|
-
|
|
372
|
-
/**
|
|
373
|
-
* Calculate standard deviation
|
|
374
|
-
*/
|
|
375
|
-
const calculateStdDev = (array) => {
|
|
376
|
-
const n = array.length;
|
|
377
|
-
const mean = array.reduce((a, b) => a + b) / n;
|
|
378
|
-
return Math.sqrt(array.map(x => Math.pow(x - mean, 2)).reduce((a, b) => a + b) / n);
|
|
379
|
-
};
|
|
380
|
-
|
|
247
|
+
// Simplified QR scanning handler - GPS + Key verification only
|
|
381
248
|
const handleQRScanned = useCallback(
|
|
382
249
|
async (qrCodeData) => {
|
|
383
250
|
if (!validateApiUrl()) return;
|
|
@@ -389,7 +256,7 @@ const BiometricModal = forwardRef(({
|
|
|
389
256
|
});
|
|
390
257
|
|
|
391
258
|
try {
|
|
392
|
-
// 1. Request
|
|
259
|
+
// 1. Request location permission
|
|
393
260
|
updateState({ loadingType: Global.LoadingTypes.locationPermission });
|
|
394
261
|
const hasLocationPermission = await requestLocationPermission();
|
|
395
262
|
if (!hasLocationPermission) {
|
|
@@ -397,11 +264,6 @@ const BiometricModal = forwardRef(({
|
|
|
397
264
|
return;
|
|
398
265
|
}
|
|
399
266
|
|
|
400
|
-
const hasBluetoothPermission = await requestBluetoothPermission();
|
|
401
|
-
if (!hasBluetoothPermission) {
|
|
402
|
-
notifyMessage("Bluetooth scanning may not work", "warning");
|
|
403
|
-
}
|
|
404
|
-
|
|
405
267
|
// 2. Parse QR data with validation
|
|
406
268
|
const qrString = typeof qrCodeData === "object" ? qrCodeData?.data : qrCodeData;
|
|
407
269
|
if (!qrString || typeof qrString !== "string") {
|
|
@@ -430,38 +292,12 @@ const BiometricModal = forwardRef(({
|
|
|
430
292
|
return;
|
|
431
293
|
}
|
|
432
294
|
|
|
433
|
-
// 4.
|
|
434
|
-
let wifiFingerprint = [];
|
|
435
|
-
let wifiMatchScore = 0;
|
|
436
|
-
let isWiFiMatch = false;
|
|
437
|
-
let wifiMatchDetails = null;
|
|
438
|
-
|
|
439
|
-
if (useWiFiFingerprinting && Platform.OS === 'android') {
|
|
440
|
-
try {
|
|
441
|
-
const wifiResult = await performWiFiScan();
|
|
442
|
-
wifiFingerprint = wifiResult.scan;
|
|
443
|
-
|
|
444
|
-
// Store as reference for later matching
|
|
445
|
-
if (wifiFingerprint.length > 0) {
|
|
446
|
-
updateState({ wifiReferenceScan: wifiFingerprint });
|
|
447
|
-
}
|
|
448
|
-
} catch (wifiError) {
|
|
449
|
-
console.warn('WiFi scan failed:', wifiError);
|
|
450
|
-
notifyMessage("WiFi scan failed, continuing without WiFi verification", "warning");
|
|
451
|
-
}
|
|
452
|
-
}
|
|
453
|
-
|
|
454
|
-
// 5. Get high-accuracy location
|
|
295
|
+
// 4. Get high-accuracy location
|
|
455
296
|
updateState({ loadingType: Global.LoadingTypes.gettingLocation });
|
|
456
297
|
|
|
457
298
|
let location;
|
|
458
299
|
try {
|
|
459
|
-
|
|
460
|
-
if (useWiFiFingerprinting && wifiFingerprint.length > 0) {
|
|
461
|
-
location = await getLocationWithWifi({ getCurrentLocation });
|
|
462
|
-
} else {
|
|
463
|
-
location = await getCurrentLocation();
|
|
464
|
-
}
|
|
300
|
+
location = await getCurrentLocation();
|
|
465
301
|
} catch (locationError) {
|
|
466
302
|
console.error('Location error:', locationError);
|
|
467
303
|
handleProcessError("Failed to get location. Please try again.");
|
|
@@ -486,45 +322,7 @@ const BiometricModal = forwardRef(({
|
|
|
486
322
|
notifyMessage(`Location accuracy is ${location.accuracy.toFixed(1)}m. For best results, move to an open area.`, 'warning');
|
|
487
323
|
}
|
|
488
324
|
|
|
489
|
-
//
|
|
490
|
-
if (useWiFiFingerprinting && Platform.OS === 'android' && state.wifiReferenceScan) {
|
|
491
|
-
try {
|
|
492
|
-
const wifiResult = await performWiFiScan();
|
|
493
|
-
wifiFingerprint = wifiResult.scan;
|
|
494
|
-
wifiMatchScore = wifiResult.score;
|
|
495
|
-
isWiFiMatch = wifiResult.isMatch;
|
|
496
|
-
wifiMatchDetails = wifiResult.matchDetails;
|
|
497
|
-
} catch (wifiError) {
|
|
498
|
-
console.warn('Second WiFi scan failed:', wifiError);
|
|
499
|
-
// Continue without WiFi match
|
|
500
|
-
}
|
|
501
|
-
}
|
|
502
|
-
|
|
503
|
-
// 7. Enhanced BLE scanning (optional)
|
|
504
|
-
updateState({ loadingType: Global.LoadingTypes.bleScan });
|
|
505
|
-
|
|
506
|
-
let consistentDevices = [];
|
|
507
|
-
let isBLENearby = false;
|
|
508
|
-
|
|
509
|
-
try {
|
|
510
|
-
startBluetoothScan();
|
|
511
|
-
|
|
512
|
-
// Collect BLE data with multiple samples
|
|
513
|
-
const bleSamples = [];
|
|
514
|
-
for (let i = 0; i < 3; i++) {
|
|
515
|
-
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
516
|
-
bleSamples.push([...nearbyDevices]);
|
|
517
|
-
}
|
|
518
|
-
|
|
519
|
-
consistentDevices = findConsistentBLEDevices(bleSamples);
|
|
520
|
-
isBLENearby = consistentDevices.length > 0 && consistentDevices.some(d => d.avgDistance <= 5);
|
|
521
|
-
} catch (bleError) {
|
|
522
|
-
console.warn("BLE scanning failed:", bleError);
|
|
523
|
-
} finally {
|
|
524
|
-
stopBluetoothScan();
|
|
525
|
-
}
|
|
526
|
-
|
|
527
|
-
// 8. Calculate distance using safe calculation
|
|
325
|
+
// 5. Calculate distance using safe calculation
|
|
528
326
|
updateState({ loadingType: Global.LoadingTypes.calculateDistance });
|
|
529
327
|
|
|
530
328
|
const distance = calculateSafeDistance(
|
|
@@ -540,20 +338,8 @@ const BiometricModal = forwardRef(({
|
|
|
540
338
|
|
|
541
339
|
const distanceWithinThreshold = distance <= MaxDistanceMeters;
|
|
542
340
|
|
|
543
|
-
//
|
|
544
|
-
|
|
545
|
-
let verificationMethod = "GPS+Key";
|
|
546
|
-
|
|
547
|
-
// Add WiFi fingerprint verification if enabled and we have a reference
|
|
548
|
-
if (useWiFiFingerprinting && state.wifiReferenceScan && state.wifiReferenceScan.length > 0) {
|
|
549
|
-
verificationMethod = isWiFiMatch ? "GPS+WiFi+Key" : "GPS+Key";
|
|
550
|
-
|
|
551
|
-
if (isWiFiMatch) {
|
|
552
|
-
// Strong WiFi verification achieved
|
|
553
|
-
} else if (wifiFingerprint.length > 0) {
|
|
554
|
-
notifyMessage(`WiFi environment changed (score: ${wifiMatchScore.toFixed(1)}). Using GPS verification.`, "warning");
|
|
555
|
-
}
|
|
556
|
-
}
|
|
341
|
+
// Simple verification criteria - GPS + Key only
|
|
342
|
+
const verificationPassed = distanceWithinThreshold && qrDepKey === depkey;
|
|
557
343
|
|
|
558
344
|
if (verificationPassed) {
|
|
559
345
|
const locationDetails = {
|
|
@@ -571,20 +357,11 @@ const BiometricModal = forwardRef(({
|
|
|
571
357
|
heading: location.heading || 0,
|
|
572
358
|
timestamp: location.timestamp || Date.now()
|
|
573
359
|
},
|
|
574
|
-
wifiFingerprint: {
|
|
575
|
-
accessPoints: wifiFingerprint,
|
|
576
|
-
matchScore: wifiMatchScore,
|
|
577
|
-
isMatch: isWiFiMatch,
|
|
578
|
-
enabled: useWiFiFingerprinting,
|
|
579
|
-
matchDetails: wifiMatchDetails,
|
|
580
|
-
referenceScanTimestamp: state.wifiReferenceScan?.[0]?.timestamp || Date.now()
|
|
581
|
-
},
|
|
582
360
|
distanceMeters: distance,
|
|
583
361
|
accuracyBuffer: MaxDistanceMeters,
|
|
584
362
|
verified: true,
|
|
585
|
-
verificationMethod:
|
|
363
|
+
verificationMethod: "GPS+Key",
|
|
586
364
|
verifiedAt: new Date().toISOString(),
|
|
587
|
-
bleDevicesDetected: consistentDevices,
|
|
588
365
|
locationMethod: location.provider || 'unknown',
|
|
589
366
|
qrData: qrString,
|
|
590
367
|
qrKey: qrDepKey
|
|
@@ -599,52 +376,34 @@ const BiometricModal = forwardRef(({
|
|
|
599
376
|
loadingType: Global.LoadingTypes.none,
|
|
600
377
|
});
|
|
601
378
|
|
|
602
|
-
|
|
603
|
-
if (useWiFiFingerprinting && isWiFiMatch) {
|
|
604
|
-
successMessage += `, Strong WiFi match: ${wifiMatchScore.toFixed(1)}`;
|
|
605
|
-
} else if (useWiFiFingerprinting && wifiFingerprint.length > 0) {
|
|
606
|
-
successMessage += `, WiFi environment: ${wifiFingerprint.length} APs detected`;
|
|
607
|
-
}
|
|
608
|
-
|
|
379
|
+
const successMessage = `Location verified! Distance: ${distance.toFixed(1)}m (±${Math.max(location.accuracy || 0, qrAccuracy).toFixed(1)}m)`;
|
|
609
380
|
notifyMessage(successMessage, "success");
|
|
610
381
|
setTimeout(() => startFaceRecognition(), 1200);
|
|
611
382
|
} else {
|
|
612
383
|
let errorMsg = `Verification failed: ${distance.toFixed(1)}m away`;
|
|
613
384
|
if (qrDepKey !== depkey) errorMsg += " (Key mismatch)";
|
|
614
|
-
if (useWiFiFingerprinting && wifiFingerprint.length > 0) {
|
|
615
|
-
errorMsg += ` (WiFi environment: ${wifiFingerprint.length} APs)`;
|
|
616
|
-
}
|
|
617
385
|
handleProcessError(errorMsg);
|
|
618
386
|
}
|
|
619
387
|
} catch (error) {
|
|
620
|
-
console.error("
|
|
621
|
-
handleProcessError("Unable to verify location
|
|
388
|
+
console.error("Location verification failed:", error);
|
|
389
|
+
handleProcessError("Unable to verify location. Please try again.", error);
|
|
622
390
|
}
|
|
623
391
|
},
|
|
624
392
|
[
|
|
625
393
|
validateApiUrl,
|
|
626
394
|
updateState,
|
|
627
395
|
requestLocationPermission,
|
|
628
|
-
requestBluetoothPermission,
|
|
629
396
|
getCurrentLocation,
|
|
630
|
-
getLocationWithWifi,
|
|
631
|
-
startBluetoothScan,
|
|
632
|
-
stopBluetoothScan,
|
|
633
|
-
nearbyDevices,
|
|
634
|
-
findConsistentBLEDevices,
|
|
635
397
|
notifyMessage,
|
|
636
398
|
handleProcessError,
|
|
637
399
|
startFaceRecognition,
|
|
638
400
|
depkey,
|
|
639
401
|
MaxDistanceMeters,
|
|
640
402
|
calculateSafeDistance,
|
|
641
|
-
useWiFiFingerprinting,
|
|
642
|
-
performWiFiScan,
|
|
643
|
-
state.wifiReferenceScan,
|
|
644
403
|
]
|
|
645
404
|
);
|
|
646
405
|
|
|
647
|
-
// Face scan upload - SECOND STEP
|
|
406
|
+
// Face scan upload - SECOND STEP (simplified, no WiFi revalidation)
|
|
648
407
|
const uploadFaceScan = useCallback(
|
|
649
408
|
async (selfie) => {
|
|
650
409
|
if (!validateApiUrl()) return;
|
|
@@ -655,25 +414,6 @@ const BiometricModal = forwardRef(({
|
|
|
655
414
|
return;
|
|
656
415
|
}
|
|
657
416
|
|
|
658
|
-
// Optional: Re-validate WiFi fingerprint during face scan
|
|
659
|
-
let wifiRevalidation = null;
|
|
660
|
-
if (useWiFiFingerprinting && Platform.OS === 'android' && state.wifiReferenceScan) {
|
|
661
|
-
const wifiResult = await performWiFiScan();
|
|
662
|
-
if (wifiResult.scan.length > 0) {
|
|
663
|
-
wifiRevalidation = {
|
|
664
|
-
scan: wifiResult.scan,
|
|
665
|
-
score: wifiResult.score,
|
|
666
|
-
isMatch: wifiResult.isMatch,
|
|
667
|
-
referenceScanLength: state.wifiReferenceScan.length,
|
|
668
|
-
timestamp: new Date().toISOString()
|
|
669
|
-
};
|
|
670
|
-
|
|
671
|
-
if (!wifiResult.isMatch) {
|
|
672
|
-
notifyMessage("Warning: WiFi environment changed since QR scan", "warning");
|
|
673
|
-
}
|
|
674
|
-
}
|
|
675
|
-
}
|
|
676
|
-
|
|
677
417
|
const currentData = dataRef.current;
|
|
678
418
|
|
|
679
419
|
if (!currentData) {
|
|
@@ -729,7 +469,6 @@ const BiometricModal = forwardRef(({
|
|
|
729
469
|
...responseRef.current,
|
|
730
470
|
...response.data?.data || {},
|
|
731
471
|
faceRecognition: response.data?.data || null,
|
|
732
|
-
wifiRevalidation: wifiRevalidation,
|
|
733
472
|
};
|
|
734
473
|
|
|
735
474
|
updateState({
|
|
@@ -775,10 +514,7 @@ const BiometricModal = forwardRef(({
|
|
|
775
514
|
safeCallback,
|
|
776
515
|
handleProcessError,
|
|
777
516
|
state.qrData,
|
|
778
|
-
state.wifiReferenceScan,
|
|
779
517
|
apiurl,
|
|
780
|
-
useWiFiFingerprinting,
|
|
781
|
-
performWiFiScan,
|
|
782
518
|
]
|
|
783
519
|
);
|
|
784
520
|
|
|
@@ -877,9 +613,6 @@ const BiometricModal = forwardRef(({
|
|
|
877
613
|
<View style={styles.titleContainer}>
|
|
878
614
|
<Text style={styles.title}>Biometric Verification</Text>
|
|
879
615
|
<Text style={styles.subtitle}>{state.currentStep}</Text>
|
|
880
|
-
{useWiFiFingerprinting && Platform.OS === 'android' && (
|
|
881
|
-
<Text style={styles.wifiIndicator}>WiFi Fingerprinting: Active</Text>
|
|
882
|
-
)}
|
|
883
616
|
</View>
|
|
884
617
|
</View>
|
|
885
618
|
)}
|