28
28
import com .facebook .react .module .annotations .ReactModule ;
29
29
import com .facebook .react .modules .core .DeviceEventManagerModule .RCTDeviceEventEmitter ;
30
30
31
+ import com .facebook .react .common .ReactConstants ;
32
+ import com .facebook .common .logging .FLog ;
33
+
31
34
import javax .annotation .Nullable ;
32
35
33
36
/**
@@ -129,13 +132,13 @@ public void getCurrentPosition(
129
132
return ;
130
133
}
131
134
Location location = locationManager .getLastKnownLocation (provider );
132
- if (location != null &&
133
- SystemClock .currentTimeMillis () - location .getTime () < locationOptions .maximumAge ) {
135
+ if (location != null && (SystemClock .currentTimeMillis () - location .getTime ()) < locationOptions .maximumAge ) {
134
136
success .invoke (locationToMap (location ));
135
137
return ;
136
138
}
139
+
137
140
new SingleUpdateRequest (locationManager , provider , locationOptions .timeout , success , error )
138
- .invoke ();
141
+ .invoke (location );
139
142
} catch (SecurityException e ) {
140
143
throwLocationPermissionMissing (e );
141
144
}
@@ -246,6 +249,7 @@ private static class SingleUpdateRequest {
246
249
private final LocationManager mLocationManager ;
247
250
private final String mProvider ;
248
251
private final long mTimeout ;
252
+ private Location mOldLocation ;
249
253
private final Handler mHandler = new Handler ();
250
254
private final Runnable mTimeoutRunnable = new Runnable () {
251
255
@ Override
@@ -254,6 +258,7 @@ public void run() {
254
258
if (!mTriggered ) {
255
259
mError .invoke (PositionError .buildError (PositionError .TIMEOUT , "Location request timed out" ));
256
260
mLocationManager .removeUpdates (mLocationListener );
261
+ FLog .i (ReactConstants .TAG , "LocationModule: Location request timed out" );
257
262
mTriggered = true ;
258
263
}
259
264
}
@@ -263,11 +268,14 @@ public void run() {
263
268
@ Override
264
269
public void onLocationChanged (Location location ) {
265
270
synchronized (SingleUpdateRequest .this ) {
266
- if (!mTriggered ) {
271
+ if (!mTriggered && isBetterLocation ( location , mOldLocation ) ) {
267
272
mSuccess .invoke (locationToMap (location ));
268
273
mHandler .removeCallbacks (mTimeoutRunnable );
269
274
mTriggered = true ;
275
+ mLocationManager .removeUpdates (mLocationListener );
270
276
}
277
+
278
+ mOldLocation = location ;
271
279
}
272
280
}
273
281
@@ -295,9 +303,69 @@ private SingleUpdateRequest(
295
303
mError = error ;
296
304
}
297
305
298
- public void invoke () {
299
- mLocationManager .requestSingleUpdate (mProvider , mLocationListener , null );
306
+ public void invoke (Location location ) {
307
+ mOldLocation = location ;
308
+ mLocationManager .requestLocationUpdates (mProvider , 100 , 1 , mLocationListener );
300
309
mHandler .postDelayed (mTimeoutRunnable , mTimeout );
301
310
}
311
+
312
+ private static final int TWO_MINUTES = 1000 * 60 * 2 ;
313
+
314
+ /** Determines whether one Location reading is better than the current Location fix
315
+ * taken from Android Examples https://developer.android.com/guide/topics/location/strategies.html
316
+ *
317
+ * @param location The new Location that you want to evaluate
318
+ * @param currentBestLocation The current Location fix, to which you want to compare the new one
319
+ */
320
+ private boolean isBetterLocation (Location location , Location currentBestLocation ) {
321
+ if (currentBestLocation == null ) {
322
+ // A new location is always better than no location
323
+ return true ;
324
+ }
325
+
326
+ // Check whether the new location fix is newer or older
327
+ long timeDelta = location .getTime () - currentBestLocation .getTime ();
328
+ boolean isSignificantlyNewer = timeDelta > TWO_MINUTES ;
329
+ boolean isSignificantlyOlder = timeDelta < -TWO_MINUTES ;
330
+ boolean isNewer = timeDelta > 0 ;
331
+
332
+ // If it's been more than two minutes since the current location, use the new location
333
+ // because the user has likely moved
334
+ if (isSignificantlyNewer ) {
335
+ return true ;
336
+ // If the new location is more than two minutes older, it must be worse
337
+ } else if (isSignificantlyOlder ) {
338
+ return false ;
339
+ }
340
+
341
+ // Check whether the new location fix is more or less accurate
342
+ int accuracyDelta = (int ) (location .getAccuracy () - currentBestLocation .getAccuracy ());
343
+ boolean isLessAccurate = accuracyDelta > 0 ;
344
+ boolean isMoreAccurate = accuracyDelta < 0 ;
345
+ boolean isSignificantlyLessAccurate = accuracyDelta > 200 ;
346
+
347
+ // Check if the old and new location are from the same provider
348
+ boolean isFromSameProvider = isSameProvider (location .getProvider (),
349
+ currentBestLocation .getProvider ());
350
+
351
+ // Determine location quality using a combination of timeliness and accuracy
352
+ if (isMoreAccurate ) {
353
+ return true ;
354
+ } else if (isNewer && !isLessAccurate ) {
355
+ return true ;
356
+ } else if (isNewer && !isSignificantlyLessAccurate && isFromSameProvider ) {
357
+ return true ;
358
+ }
359
+
360
+ return false ;
361
+ }
362
+
363
+ /** Checks whether two providers are the same */
364
+ private boolean isSameProvider (String provider1 , String provider2 ) {
365
+ if (provider1 == null ) {
366
+ return provider2 == null ;
367
+ }
368
+ return provider1 .equals (provider2 );
369
+ }
302
370
}
303
371
}
0 commit comments