react-native-biometric-verifier 0.0.47 → 0.0.48

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.
@@ -3,200 +3,218 @@ import { Platform, PermissionsAndroid } from 'react-native';
3
3
  import Geolocation from 'react-native-geolocation-service';
4
4
 
5
5
  /**
6
- * Enhanced Geolocation Hook with better accuracy
6
+ * Geolocation Hook for GPS functionality
7
7
  */
8
8
  export const useGeolocation = (notifyMessage) => {
9
9
  const locationWatchId = useRef(null);
10
10
 
11
- /**
12
- * Request high-accuracy location permissions
13
- */
14
- const requestLocationPermissions = useCallback(async () => {
15
- console.log('[Permissions] Requesting enhanced location permissions...');
16
-
17
- if (Platform.OS === 'android') {
18
- try {
19
- const granted = await PermissionsAndroid.requestMultiple([
20
- PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION,
21
- PermissionsAndroid.PERMISSIONS.ACCESS_COARSE_LOCATION,
22
- ]);
23
-
24
- // Check Android version for background location
25
- if (Platform.Version >= 29) {
26
- await PermissionsAndroid.request(
27
- PermissionsAndroid.PERMISSIONS.ACCESS_BACKGROUND_LOCATION
28
- );
29
- }
11
+ /* -------------------------------------------------------------------------- */
12
+ /* PERMISSIONS */
13
+ /* -------------------------------------------------------------------------- */
14
+ const requestLocationPermission = useCallback(async () => {
15
+ try {
16
+ if (Platform.OS !== 'android') return true;
30
17
 
31
- const allGranted = Object.values(granted).every(
32
- status => status === PermissionsAndroid.RESULTS.GRANTED
33
- );
34
-
35
- if (!allGranted) {
36
- notifyMessage?.('Location permissions are required for accurate positioning', 'warning');
37
- }
38
-
39
- return allGranted;
40
- } catch (error) {
41
- console.error('[Permissions] Error:', error);
42
- notifyMessage?.('Failed to request location permissions', 'error');
43
- return false;
18
+ const permissions = [
19
+ PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION,
20
+ PermissionsAndroid.PERMISSIONS.ACCESS_COARSE_LOCATION,
21
+ ];
22
+
23
+ if (Platform.Version >= 31) {
24
+ permissions.push(PermissionsAndroid.PERMISSIONS.NEARBY_WIFI_DEVICES);
44
25
  }
45
- }
46
- return true; // iOS handles permissions differently
47
- }, [notifyMessage]);
48
26
 
49
- /**
50
- * Get high-accuracy location with multiple attempts
51
- */
52
- const getCurrentLocation = useCallback(
53
- () =>
54
- new Promise(async (resolve, reject) => {
55
- console.log('[Location] Fetching enhanced location...');
56
-
57
- let bestLocation = null;
58
-
59
- const locationOptions = {
60
- enableHighAccuracy: true,
61
- timeout: 20000,
62
- maximumAge: 0,
63
- distanceFilter: 0,
64
- forceRequestLocation: true,
65
- showLocationDialog: true,
66
- };
27
+ const result = await PermissionsAndroid.requestMultiple(permissions);
67
28
 
68
- const watchLocationForAccuracy = () => {
69
- return new Promise((resolveWatch, rejectWatch) => {
70
- let bestAccuracy = Infinity;
71
- let bestCoords = null;
72
- let watchTimer;
73
-
74
- locationWatchId.current = Geolocation.watchPosition(
75
- (position) => {
76
- const { coords } = position;
77
-
78
- // Track best accuracy
79
- if (coords.accuracy < bestAccuracy) {
80
- bestAccuracy = coords.accuracy;
81
- bestCoords = coords;
82
- console.log(`[Location] Improved accuracy: ${coords.accuracy}m`);
83
- }
84
-
85
- // Stop if we reach desired accuracy
86
- if (coords.accuracy <= 5) {
87
- clearTimeout(watchTimer);
88
- Geolocation.clearWatch(locationWatchId.current);
89
- resolveWatch(coords);
90
- }
91
- },
92
- (error) => {
93
- clearTimeout(watchTimer);
94
- Geolocation.clearWatch(locationWatchId.current);
95
- rejectWatch(error);
96
- },
97
- locationOptions
98
- );
99
-
100
- // Timeout after 15 seconds
101
- watchTimer = setTimeout(() => {
102
- Geolocation.clearWatch(locationWatchId.current);
103
- resolveWatch(bestCoords);
104
- }, 15000);
105
- });
106
- };
29
+ const granted = Object.values(result).every(
30
+ status => status === PermissionsAndroid.RESULTS.GRANTED
31
+ );
107
32
 
108
- try {
109
- // First try: Single high-accuracy reading
110
- const singleLocation = await new Promise((resolveSingle, rejectSingle) => {
111
- Geolocation.getCurrentPosition(
112
- resolveSingle,
113
- rejectSingle,
114
- locationOptions
115
- );
116
- });
117
-
118
- bestLocation = singleLocation.coords;
119
- console.log(`[Location] Initial accuracy: ${bestLocation.accuracy}m`);
120
-
121
- // If accuracy > 20m, try watching for improvement
122
- if (bestLocation.accuracy > 20) {
123
- console.log('[Location] Accuracy insufficient, starting watch...');
124
- const watchedCoords = await watchLocationForAccuracy();
125
- if (watchedCoords && watchedCoords.accuracy < bestLocation.accuracy) {
126
- bestLocation = watchedCoords;
127
- }
128
- }
129
-
130
- // Validate location quality
131
- if (!bestLocation || bestLocation.accuracy > 50) {
132
- notifyMessage?.('Location accuracy is low. Please move to an open area.', 'warning');
133
- }
134
-
135
- resolve(bestLocation);
136
- } catch (error) {
137
- console.error('[Location] Error:', error);
138
-
139
- // Provide user-friendly error messages
140
- let errorMessage = 'Failed to get location';
141
- switch (error.code) {
142
- case 1:
143
- errorMessage = 'Location permission denied';
144
- break;
145
- case 2:
146
- errorMessage = 'Location unavailable';
147
- break;
148
- case 3:
149
- errorMessage = 'Location request timed out';
150
- break;
151
- }
152
-
153
- notifyMessage?.(errorMessage, 'error');
154
- reject(new Error(errorMessage));
155
- }
156
- }),
157
- [notifyMessage]
158
- );
33
+ if (!granted) {
34
+ notifyMessage?.('Location permissions missing', 'warning');
35
+ }
159
36
 
160
- /**
161
- * Stop location watching
162
- */
37
+ return granted;
38
+ } catch (err) {
39
+ console.error('[Geolocation Permissions] Error:', err);
40
+ return false;
41
+ }
42
+ }, [notifyMessage]);
43
+
44
+ /* -------------------------------------------------------------------------- */
45
+ /* STOP LOCATION WATCH */
46
+ /* -------------------------------------------------------------------------- */
163
47
  const stopLocationWatching = useCallback(() => {
164
48
  if (locationWatchId.current !== null) {
165
- console.log('[Location] Stopping location watch...');
166
49
  Geolocation.clearWatch(locationWatchId.current);
167
50
  locationWatchId.current = null;
168
51
  }
169
52
  }, []);
170
53
 
171
- /**
172
- * Calculate distance between coordinates using Haversine formula with altitude
173
- */
174
- const calculateEnhancedDistance = useCallback((lat1, lon1, lat2, lon2, alt1 = 0, alt2 = 0) => {
175
- const R = 6371e3; // Earth's radius in meters
176
-
177
- const φ1 = lat1 * Math.PI/180;
178
- const φ2 = lat2 * Math.PI/180;
179
- const Δφ = (lat2-lat1) * Math.PI/180;
180
- const Δλ = (lon2-lon1) * Math.PI/180;
181
-
182
- // Haversine formula for surface distance
183
- const a = Math.sin(Δφ/2) * Math.sin(Δφ/2) +
184
- Math.cos(φ1) * Math.cos(φ2) *
185
- Math.sin(Δλ/2) * Math.sin(Δλ/2);
186
- const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
187
- const surfaceDistance = R * c;
188
-
189
- // Add altitude difference (Pythagorean theorem)
190
- const altitudeDiff = Math.abs(alt1 - alt2);
191
- const totalDistance = Math.sqrt(Math.pow(surfaceDistance, 2) + Math.pow(altitudeDiff, 2));
192
-
193
- return totalDistance;
194
- }, []);
54
+ /* -------------------------------------------------------------------------- */
55
+ /* HIGH ACCURACY GPS */
56
+ /* -------------------------------------------------------------------------- */
57
+ const getCurrentLocation = useCallback((options = {}) => {
58
+ return new Promise(async (resolve, reject) => {
59
+ let bestCoords = null;
60
+ let bestAccuracy = Infinity;
61
+
62
+ const defaultOptions = {
63
+ enableHighAccuracy: true,
64
+ timeout: 20000,
65
+ maximumAge: 0,
66
+ forceRequestLocation: true,
67
+ showLocationDialog: true,
68
+ ...options,
69
+ };
70
+
71
+ try {
72
+ const pos = await new Promise((res, rej) => {
73
+ Geolocation.getCurrentPosition(res, rej, defaultOptions);
74
+ });
75
+
76
+ if (!pos.coords || typeof pos.coords.latitude !== 'number' || typeof pos.coords.longitude !== 'number') {
77
+ reject(new Error('Invalid GPS coordinates received'));
78
+ return;
79
+ }
80
+
81
+ bestCoords = {
82
+ latitude: pos.coords.latitude,
83
+ longitude: pos.coords.longitude,
84
+ altitude: pos.coords.altitude || 0,
85
+ accuracy: pos.coords.accuracy,
86
+ speed: pos.coords.speed || 0,
87
+ heading: pos.coords.heading || 0,
88
+ timestamp: pos.timestamp,
89
+ };
90
+
91
+ bestAccuracy = pos.coords.accuracy;
92
+
93
+ if (bestAccuracy > 15) {
94
+ locationWatchId.current = Geolocation.watchPosition(
95
+ (p) => {
96
+ if (
97
+ p.coords &&
98
+ typeof p.coords.latitude === 'number' &&
99
+ typeof p.coords.longitude === 'number' &&
100
+ p.coords.accuracy < bestAccuracy
101
+ ) {
102
+ bestAccuracy = p.coords.accuracy;
103
+ bestCoords = {
104
+ latitude: p.coords.latitude,
105
+ longitude: p.coords.longitude,
106
+ altitude: p.coords.altitude || 0,
107
+ accuracy: p.coords.accuracy,
108
+ speed: p.coords.speed || 0,
109
+ heading: p.coords.heading || 0,
110
+ timestamp: p.timestamp,
111
+ };
112
+ }
113
+
114
+ if (bestAccuracy <= 5) {
115
+ stopLocationWatching();
116
+ }
117
+ },
118
+ (err) => {
119
+ console.error('[Geolocation Watch Error]:', err);
120
+ stopLocationWatching();
121
+ },
122
+ defaultOptions
123
+ );
124
+
125
+ setTimeout(() => stopLocationWatching(), 12000);
126
+ }
127
+
128
+ resolve({
129
+ latitude: bestCoords.latitude,
130
+ longitude: bestCoords.longitude,
131
+ altitude: bestCoords.altitude,
132
+ accuracy: bestAccuracy,
133
+ speed: bestCoords.speed,
134
+ heading: bestCoords.heading,
135
+ timestamp: bestCoords.timestamp,
136
+ });
137
+ } catch (err) {
138
+ console.error('[Geolocation] Failed to get location:', err);
139
+ stopLocationWatching();
140
+ notifyMessage?.('Failed to get location', 'error');
141
+ reject(err);
142
+ }
143
+ });
144
+ }, [notifyMessage, stopLocationWatching]);
145
+
146
+ /* -------------------------------------------------------------------------- */
147
+ /* DISTANCE CALCULATION */
148
+ /* -------------------------------------------------------------------------- */
149
+ const calculateEnhancedDistance = useCallback(
150
+ (lat1, lon1, lat2, lon2, alt1 = 0, alt2 = 0) => {
151
+ try {
152
+ if (
153
+ typeof lat1 !== 'number' || typeof lon1 !== 'number' ||
154
+ typeof lat2 !== 'number' || typeof lon2 !== 'number' ||
155
+ isNaN(lat1) || isNaN(lon1) || isNaN(lat2) || isNaN(lon2)
156
+ ) return Infinity;
157
+
158
+ const toRad = (deg) => deg * Math.PI / 180;
159
+ const R = 6371e3; // meters
160
+ const φ1 = toRad(lat1);
161
+ const φ2 = toRad(lat2);
162
+ const Δφ = toRad(lat2 - lat1);
163
+ const Δλ = toRad(lon2 - lon1);
164
+
165
+ const a = Math.sin(Δφ / 2) ** 2 +
166
+ Math.cos(φ1) * Math.cos(φ2) *
167
+ Math.sin(Δλ / 2) ** 2;
168
+ const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
169
+ const surfaceDistance = R * c;
170
+
171
+ const heightDifference = Math.abs(alt1 - alt2);
172
+ return Math.sqrt(surfaceDistance ** 2 + heightDifference ** 2);
173
+ } catch (err) {
174
+ console.error('[Geolocation Distance] Error:', err);
175
+ return Infinity;
176
+ }
177
+ },
178
+ []
179
+ );
180
+
181
+ const calculateSafeDistance = useCallback(
182
+ (point1, point2) => {
183
+ try {
184
+ const lat1 = typeof point1.latitude === 'number' ? point1.latitude :
185
+ typeof point1.lat === 'number' ? point1.lat : 0;
186
+ const lon1 = typeof point1.longitude === 'number' ? point1.longitude :
187
+ typeof point1.lng === 'number' ? point1.lng :
188
+ typeof point1.lon === 'number' ? point1.lon : 0;
189
+ const alt1 = point1.altitude || point1.alt || 0;
190
+
191
+ const lat2 = typeof point2.latitude === 'number' ? point2.latitude :
192
+ typeof point2.lat === 'number' ? point2.lat : 0;
193
+ const lon2 = typeof point2.longitude === 'number' ? point2.longitude :
194
+ typeof point2.lng === 'number' ? point2.lng :
195
+ typeof point2.lon === 'number' ? point2.lon : 0;
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);
201
+ return Infinity;
202
+ }
203
+ },
204
+ [calculateEnhancedDistance]
205
+ );
206
+
207
+ /* -------------------------------------------------------------------------- */
208
+ /* GETTERS */
209
+ /* -------------------------------------------------------------------------- */
210
+ const getCurrentWatchId = useCallback(() => locationWatchId.current, []);
195
211
 
196
212
  return {
197
- requestLocationPermission: requestLocationPermissions,
213
+ requestLocationPermission,
198
214
  getCurrentLocation,
199
215
  stopLocationWatching,
216
+ getCurrentWatchId,
200
217
  calculateEnhancedDistance,
218
+ calculateSafeDistance,
201
219
  };
202
- };
220
+ };
@@ -0,0 +1,175 @@
1
+ import { useCallback } from 'react';
2
+ import { Platform, PermissionsAndroid } from 'react-native';
3
+ import WifiManager from 'react-native-wifi-reborn';
4
+
5
+ /**
6
+ * WiFi Service Hook for WiFi scanning and fingerprinting
7
+ */
8
+ export const useWifiService = (notifyMessage) => {
9
+
10
+ /* -------------------------------------------------------------------------- */
11
+ /* WIFI PERMISSIONS */
12
+ /* -------------------------------------------------------------------------- */
13
+ const requestWifiPermissions = useCallback(async () => {
14
+ try {
15
+ if (Platform.OS !== 'android') return false;
16
+
17
+ const permissions = [PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION];
18
+
19
+ if (Platform.Version >= 31) {
20
+ permissions.push(PermissionsAndroid.PERMISSIONS.NEARBY_WIFI_DEVICES);
21
+ }
22
+
23
+ const result = await PermissionsAndroid.requestMultiple(permissions);
24
+
25
+ const granted = Object.values(result).every(
26
+ status => status === PermissionsAndroid.RESULTS.GRANTED
27
+ );
28
+
29
+ if (!granted) {
30
+ notifyMessage?.('WiFi scanning permissions missing', 'warning');
31
+ }
32
+
33
+ return granted;
34
+ } catch (err) {
35
+ console.error('[WiFi Permissions] Error:', err);
36
+ return false;
37
+ }
38
+ }, [notifyMessage]);
39
+
40
+ /* -------------------------------------------------------------------------- */
41
+ /* WIFI SCANNING */
42
+ /* -------------------------------------------------------------------------- */
43
+ const scanWifiFingerprint = useCallback(async () => {
44
+ if (Platform.OS !== 'android') return [];
45
+
46
+ try {
47
+ const hasPermission = await requestWifiPermissions();
48
+ if (!hasPermission) return [];
49
+
50
+ const list = await WifiManager.loadWifiList();
51
+
52
+ const fingerprint = list.map(ap => ({
53
+ bssid: ap.BSSID,
54
+ ssid: ap.SSID || 'Unknown',
55
+ rssi: ap.level,
56
+ frequency: ap.frequency || 0,
57
+ capabilities: ap.capabilities || '',
58
+ timestamp: Date.now(),
59
+ }));
60
+
61
+ return fingerprint;
62
+ } catch (err) {
63
+ console.error('[WiFi] Scan failed:', err);
64
+ notifyMessage?.('WiFi scan failed', 'error');
65
+ return [];
66
+ }
67
+ }, [requestWifiPermissions, notifyMessage]);
68
+
69
+ /* -------------------------------------------------------------------------- */
70
+ /* WIFI FINGERPRINT MATCHING */
71
+ /* -------------------------------------------------------------------------- */
72
+ const matchWifiFingerprint = useCallback((scan, reference, options = {}) => {
73
+ const { rssiThreshold = 10, matchWeight = 1, partialMatchWeight = 0.5 } = options;
74
+
75
+ if (!scan || !reference || !Array.isArray(scan) || !Array.isArray(reference)) return 0;
76
+
77
+ let score = 0;
78
+ let matches = 0;
79
+ const totalPossibleMatches = Math.min(scan.length, reference.length);
80
+
81
+ scan.forEach(ap => {
82
+ const match = reference.find(r => r.bssid === ap.bssid);
83
+ if (match) {
84
+ const diff = Math.abs((match.rssi || 0) - ap.rssi);
85
+ score += diff <= rssiThreshold ? matchWeight : partialMatchWeight;
86
+ matches++;
87
+ }
88
+ });
89
+
90
+ const matchPercentage = totalPossibleMatches > 0 ? (matches / totalPossibleMatches) * 100 : 0;
91
+
92
+ return {
93
+ score,
94
+ matches,
95
+ totalPossibleMatches,
96
+ matchPercentage,
97
+ };
98
+ }, []);
99
+
100
+ /* -------------------------------------------------------------------------- */
101
+ /* WIFI UTILITIES */
102
+ /* -------------------------------------------------------------------------- */
103
+ const getWifiNetworksByStrength = useCallback((networks, limit = 5) => {
104
+ if (!Array.isArray(networks)) return [];
105
+ return networks
106
+ .filter(network => network && typeof network.rssi === 'number')
107
+ .sort((a, b) => b.rssi - a.rssi)
108
+ .slice(0, limit);
109
+ }, []);
110
+
111
+ const filterWifiNetworks = useCallback((networks, options = {}) => {
112
+ const { minRssi = -90, maxRssi = -30, excludeHidden = true } = options;
113
+ if (!Array.isArray(networks)) return [];
114
+
115
+ return networks.filter(network => {
116
+ if (!network || !network.bssid) return false;
117
+ if (network.rssi < minRssi || network.rssi > maxRssi) return false;
118
+ if (excludeHidden && (!network.ssid || network.ssid === '' || network.ssid === '<unknown ssid>')) return false;
119
+ return true;
120
+ });
121
+ }, []);
122
+
123
+ const getCurrentWifiInfo = useCallback(async () => {
124
+ if (Platform.OS !== 'android') return null;
125
+
126
+ try {
127
+ const hasPermission = await requestWifiPermissions();
128
+ if (!hasPermission) return null;
129
+
130
+ const currentWifi = await WifiManager.getCurrentWifiSSID();
131
+ return { ssid: currentWifi, timestamp: Date.now() };
132
+ } catch (err) {
133
+ console.error('[WiFi] Failed to get current WiFi:', err);
134
+ return null;
135
+ }
136
+ }, [requestWifiPermissions]);
137
+
138
+ /* -------------------------------------------------------------------------- */
139
+ /* COMBINED LOCATION SCAN */
140
+ /* -------------------------------------------------------------------------- */
141
+ const getLocationWithWifi = useCallback(async (geolocationHook) => {
142
+ try {
143
+ const wifiPromise = scanWifiFingerprint();
144
+
145
+ let locationResult;
146
+ try {
147
+ locationResult = await geolocationHook.getCurrentLocation();
148
+ } catch {
149
+ locationResult = null; // continue with WiFi only
150
+ }
151
+
152
+ const wifiFingerprint = await wifiPromise;
153
+
154
+ return {
155
+ ...(locationResult || {}),
156
+ wifi: wifiFingerprint,
157
+ timestamp: Date.now(),
158
+ source: locationResult ? 'gps+wifi' : 'wifi-only',
159
+ };
160
+ } catch (err) {
161
+ console.error('[WiFi+Location] Combined scan failed:', err);
162
+ throw err;
163
+ }
164
+ }, [scanWifiFingerprint]);
165
+
166
+ return {
167
+ requestWifiPermissions,
168
+ scanWifiFingerprint,
169
+ getCurrentWifiInfo,
170
+ matchWifiFingerprint,
171
+ getWifiNetworksByStrength,
172
+ filterWifiNetworks,
173
+ getLocationWithWifi,
174
+ };
175
+ };