mirror of
https://github.com/status-im/react-native.git
synced 2025-01-14 03:26:07 +00:00
Android: Improve getCurrentPosition API
Summary: We ran into a couple of problems with the implementation of `getCurrentPosition` on Android: - It sometimes returns an inaccurate location - It times out when `enableHighAccuracy` is `true` (#7495) This change improves `getCurrentPosition` for both of the above problems. Instead of calling `requestSingleUpdate` it now calls `requestLocationUpdates` so it can receive multiple locations giving it an opportunity to pick a better one. Unlike `requestSingleUpdate`, this approach doesn't seem to timeout when `enableHighAccuracy` is `true`. **Test plan (required)** Verified in a test app that `getCurrentPosition` returns a good location and doesn't timeout when `enableHighAccuracy` is `true`. Also, my team has been using this change in our app in production. Adam Comella Microsoft Corp. Closes https://github.com/facebook/react-native/pull/15094 Differential Revision: D5632100 Pulled By: hramos fbshipit-source-id: 86e40b01d941a13820cb775bccad7e19dba3d692
This commit is contained in:
parent
400020215f
commit
7e11bad86f
@ -7,6 +7,7 @@ android_library(
|
||||
"PUBLIC",
|
||||
],
|
||||
deps = [
|
||||
react_native_dep("libraries/fbcore/src/main/java/com/facebook/common/logging:logging"),
|
||||
react_native_dep("third-party/java/infer-annotations:infer-annotations"),
|
||||
react_native_dep("third-party/java/jsr-305:jsr-305"),
|
||||
react_native_target("java/com/facebook/react/bridge:bridge"),
|
||||
|
@ -28,6 +28,9 @@ import com.facebook.react.common.SystemClock;
|
||||
import com.facebook.react.module.annotations.ReactModule;
|
||||
import com.facebook.react.modules.core.DeviceEventManagerModule.RCTDeviceEventEmitter;
|
||||
|
||||
import com.facebook.react.common.ReactConstants;
|
||||
import com.facebook.common.logging.FLog;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
@ -129,13 +132,13 @@ public class LocationModule extends ReactContextBaseJavaModule {
|
||||
return;
|
||||
}
|
||||
Location location = locationManager.getLastKnownLocation(provider);
|
||||
if (location != null &&
|
||||
SystemClock.currentTimeMillis() - location.getTime() < locationOptions.maximumAge) {
|
||||
if (location != null && (SystemClock.currentTimeMillis() - location.getTime()) < locationOptions.maximumAge) {
|
||||
success.invoke(locationToMap(location));
|
||||
return;
|
||||
}
|
||||
|
||||
new SingleUpdateRequest(locationManager, provider, locationOptions.timeout, success, error)
|
||||
.invoke();
|
||||
.invoke(location);
|
||||
} catch (SecurityException e) {
|
||||
throwLocationPermissionMissing(e);
|
||||
}
|
||||
@ -246,6 +249,7 @@ public class LocationModule extends ReactContextBaseJavaModule {
|
||||
private final LocationManager mLocationManager;
|
||||
private final String mProvider;
|
||||
private final long mTimeout;
|
||||
private Location mOldLocation;
|
||||
private final Handler mHandler = new Handler();
|
||||
private final Runnable mTimeoutRunnable = new Runnable() {
|
||||
@Override
|
||||
@ -254,6 +258,7 @@ public class LocationModule extends ReactContextBaseJavaModule {
|
||||
if (!mTriggered) {
|
||||
mError.invoke(PositionError.buildError(PositionError.TIMEOUT, "Location request timed out"));
|
||||
mLocationManager.removeUpdates(mLocationListener);
|
||||
FLog.i(ReactConstants.TAG, "LocationModule: Location request timed out");
|
||||
mTriggered = true;
|
||||
}
|
||||
}
|
||||
@ -263,11 +268,14 @@ public class LocationModule extends ReactContextBaseJavaModule {
|
||||
@Override
|
||||
public void onLocationChanged(Location location) {
|
||||
synchronized (SingleUpdateRequest.this) {
|
||||
if (!mTriggered) {
|
||||
if (!mTriggered && isBetterLocation(location, mOldLocation)) {
|
||||
mSuccess.invoke(locationToMap(location));
|
||||
mHandler.removeCallbacks(mTimeoutRunnable);
|
||||
mTriggered = true;
|
||||
mLocationManager.removeUpdates(mLocationListener);
|
||||
}
|
||||
|
||||
mOldLocation = location;
|
||||
}
|
||||
}
|
||||
|
||||
@ -295,9 +303,69 @@ public class LocationModule extends ReactContextBaseJavaModule {
|
||||
mError = error;
|
||||
}
|
||||
|
||||
public void invoke() {
|
||||
mLocationManager.requestSingleUpdate(mProvider, mLocationListener, null);
|
||||
public void invoke(Location location) {
|
||||
mOldLocation = location;
|
||||
mLocationManager.requestLocationUpdates(mProvider, 100, 1, mLocationListener);
|
||||
mHandler.postDelayed(mTimeoutRunnable, mTimeout);
|
||||
}
|
||||
|
||||
private static final int TWO_MINUTES = 1000 * 60 * 2;
|
||||
|
||||
/** Determines whether one Location reading is better than the current Location fix
|
||||
* taken from Android Examples https://developer.android.com/guide/topics/location/strategies.html
|
||||
*
|
||||
* @param location The new Location that you want to evaluate
|
||||
* @param currentBestLocation The current Location fix, to which you want to compare the new one
|
||||
*/
|
||||
private boolean isBetterLocation(Location location, Location currentBestLocation) {
|
||||
if (currentBestLocation == null) {
|
||||
// A new location is always better than no location
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check whether the new location fix is newer or older
|
||||
long timeDelta = location.getTime() - currentBestLocation.getTime();
|
||||
boolean isSignificantlyNewer = timeDelta > TWO_MINUTES;
|
||||
boolean isSignificantlyOlder = timeDelta < -TWO_MINUTES;
|
||||
boolean isNewer = timeDelta > 0;
|
||||
|
||||
// If it's been more than two minutes since the current location, use the new location
|
||||
// because the user has likely moved
|
||||
if (isSignificantlyNewer) {
|
||||
return true;
|
||||
// If the new location is more than two minutes older, it must be worse
|
||||
} else if (isSignificantlyOlder) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check whether the new location fix is more or less accurate
|
||||
int accuracyDelta = (int) (location.getAccuracy() - currentBestLocation.getAccuracy());
|
||||
boolean isLessAccurate = accuracyDelta > 0;
|
||||
boolean isMoreAccurate = accuracyDelta < 0;
|
||||
boolean isSignificantlyLessAccurate = accuracyDelta > 200;
|
||||
|
||||
// Check if the old and new location are from the same provider
|
||||
boolean isFromSameProvider = isSameProvider(location.getProvider(),
|
||||
currentBestLocation.getProvider());
|
||||
|
||||
// Determine location quality using a combination of timeliness and accuracy
|
||||
if (isMoreAccurate) {
|
||||
return true;
|
||||
} else if (isNewer && !isLessAccurate) {
|
||||
return true;
|
||||
} else if (isNewer && !isSignificantlyLessAccurate && isFromSameProvider) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Checks whether two providers are the same */
|
||||
private boolean isSameProvider(String provider1, String provider2) {
|
||||
if (provider1 == null) {
|
||||
return provider2 == null;
|
||||
}
|
||||
return provider1.equals(provider2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user