diff --git a/ReactAndroid/src/main/java/com/facebook/react/modules/location/BUCK b/ReactAndroid/src/main/java/com/facebook/react/modules/location/BUCK deleted file mode 100644 index f2c28f62e7b..00000000000 --- a/ReactAndroid/src/main/java/com/facebook/react/modules/location/BUCK +++ /dev/null @@ -1,20 +0,0 @@ -load("//tools/build_defs/oss:rn_defs.bzl", "react_native_dep", "react_native_target", "rn_android_library") - -rn_android_library( - name = "location", - srcs = glob(["**/*.java"]), - is_androidx = True, - visibility = [ - "PUBLIC", - ], - deps = [ - react_native_dep("libraries/fbcore/src/main/java/com/facebook/common/logging:logging"), - react_native_dep("third-party/android/support/v4:lib-support-v4"), - 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"), - react_native_target("java/com/facebook/react/common:common"), - react_native_target("java/com/facebook/react/module/annotations:annotations"), - react_native_target("java/com/facebook/react/modules/core:core"), - ], -) diff --git a/ReactAndroid/src/main/java/com/facebook/react/modules/location/LocationModule.java b/ReactAndroid/src/main/java/com/facebook/react/modules/location/LocationModule.java deleted file mode 100644 index 46df8e28c29..00000000000 --- a/ReactAndroid/src/main/java/com/facebook/react/modules/location/LocationModule.java +++ /dev/null @@ -1,377 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -package com.facebook.react.modules.location; - -import android.annotation.SuppressLint; -import android.content.Context; -import android.content.pm.PackageManager; -import android.location.Location; -import android.location.LocationListener; -import android.location.LocationManager; -import android.location.LocationProvider; -import android.os.Build; -import android.os.Bundle; -import android.os.Handler; -import androidx.core.content.ContextCompat; -import com.facebook.common.logging.FLog; -import com.facebook.react.bridge.Arguments; -import com.facebook.react.bridge.Callback; -import com.facebook.react.bridge.ReactApplicationContext; -import com.facebook.react.bridge.ReactContextBaseJavaModule; -import com.facebook.react.bridge.ReactMethod; -import com.facebook.react.bridge.ReadableMap; -import com.facebook.react.bridge.WritableMap; -import com.facebook.react.common.ReactConstants; -import com.facebook.react.common.SystemClock; -import com.facebook.react.module.annotations.ReactModule; -import com.facebook.react.modules.core.DeviceEventManagerModule.RCTDeviceEventEmitter; -import javax.annotation.Nullable; - -/** - * Native module that exposes Geolocation to JS. - */ -@SuppressLint("MissingPermission") -@ReactModule(name = LocationModule.NAME) -public class LocationModule extends ReactContextBaseJavaModule { - - public static final String NAME = "LocationObserver"; - private @Nullable String mWatchedProvider; - private static final float RCT_DEFAULT_LOCATION_ACCURACY = 100; - - private final LocationListener mLocationListener = new LocationListener() { - @Override - public void onLocationChanged(Location location) { - getReactApplicationContext().getJSModule(RCTDeviceEventEmitter.class) - .emit("geolocationDidChange", locationToMap(location)); - } - - @Override - public void onStatusChanged(String provider, int status, Bundle extras) { - if (status == LocationProvider.OUT_OF_SERVICE) { - emitError(PositionError.POSITION_UNAVAILABLE, "Provider " + provider + " is out of service."); - } else if (status == LocationProvider.TEMPORARILY_UNAVAILABLE) { - emitError(PositionError.TIMEOUT, "Provider " + provider + " is temporarily unavailable."); - } - } - - @Override - public void onProviderEnabled(String provider) { } - - @Override - public void onProviderDisabled(String provider) { } - }; - - public LocationModule(ReactApplicationContext reactContext) { - super(reactContext); - } - - @Override - public String getName() { - return NAME; - } - - private static class LocationOptions { - private final long timeout; - private final double maximumAge; - private final boolean highAccuracy; - private final float distanceFilter; - - private LocationOptions( - long timeout, - double maximumAge, - boolean highAccuracy, - float distanceFilter) { - this.timeout = timeout; - this.maximumAge = maximumAge; - this.highAccuracy = highAccuracy; - this.distanceFilter = distanceFilter; - } - - private static LocationOptions fromReactMap(ReadableMap map) { - // precision might be dropped on timeout (double -> int conversion), but that's OK - long timeout = - map.hasKey("timeout") ? (long) map.getDouble("timeout") : Long.MAX_VALUE; - double maximumAge = - map.hasKey("maximumAge") ? map.getDouble("maximumAge") : Double.POSITIVE_INFINITY; - boolean highAccuracy = - map.hasKey("enableHighAccuracy") && map.getBoolean("enableHighAccuracy"); - float distanceFilter = map.hasKey("distanceFilter") ? - (float) map.getDouble("distanceFilter") : - RCT_DEFAULT_LOCATION_ACCURACY; - - return new LocationOptions(timeout, maximumAge, highAccuracy, distanceFilter); - } - } - - /** - * Get the current position. This can return almost immediately if the location is cached or - * request an update, which might take a while. - * - * @param options map containing optional arguments: timeout (millis), maximumAge (millis) and - * highAccuracy (boolean) - */ - @ReactMethod - public void getCurrentPosition( - ReadableMap options, - final Callback success, - Callback error) { - LocationOptions locationOptions = LocationOptions.fromReactMap(options); - - try { - LocationManager locationManager = - (LocationManager) getReactApplicationContext().getSystemService(Context.LOCATION_SERVICE); - String provider = getValidProvider(locationManager, locationOptions.highAccuracy); - if (provider == null) { - error.invoke( - PositionError.buildError( - PositionError.POSITION_UNAVAILABLE, "No location provider available.")); - return; - } - Location location = locationManager.getLastKnownLocation(provider); - if (location != null && (SystemClock.currentTimeMillis() - location.getTime()) < locationOptions.maximumAge) { - success.invoke(locationToMap(location)); - return; - } - - new SingleUpdateRequest(locationManager, provider, locationOptions.timeout, success, error) - .invoke(location); - } catch (SecurityException e) { - throwLocationPermissionMissing(e); - } - } - - /** - * Start listening for location updates. These will be emitted via the - * {@link RCTDeviceEventEmitter} as {@code geolocationDidChange} events. - * - * @param options map containing optional arguments: highAccuracy (boolean) - */ - @ReactMethod - public void startObserving(ReadableMap options) { - if (LocationManager.GPS_PROVIDER.equals(mWatchedProvider)) { - return; - } - LocationOptions locationOptions = LocationOptions.fromReactMap(options); - - try { - LocationManager locationManager = - (LocationManager) getReactApplicationContext().getSystemService(Context.LOCATION_SERVICE); - String provider = getValidProvider(locationManager, locationOptions.highAccuracy); - if (provider == null) { - emitError(PositionError.POSITION_UNAVAILABLE, "No location provider available."); - return; - } - if (!provider.equals(mWatchedProvider)) { - locationManager.removeUpdates(mLocationListener); - locationManager.requestLocationUpdates( - provider, - 1000, - locationOptions.distanceFilter, - mLocationListener); - } - mWatchedProvider = provider; - } catch (SecurityException e) { - throwLocationPermissionMissing(e); - } - } - - /** - * Stop listening for location updates. - * - * NB: this is not balanced with {@link #startObserving}: any number of calls to that method will - * be canceled by just one call to this one. - */ - @ReactMethod - public void stopObserving() { - LocationManager locationManager = - (LocationManager) getReactApplicationContext().getSystemService(Context.LOCATION_SERVICE); - locationManager.removeUpdates(mLocationListener); - mWatchedProvider = null; - } - - @Nullable - private String getValidProvider(LocationManager locationManager, boolean highAccuracy) { - String provider = - highAccuracy ? LocationManager.GPS_PROVIDER : LocationManager.NETWORK_PROVIDER; - if (!locationManager.isProviderEnabled(provider)) { - provider = provider.equals(LocationManager.GPS_PROVIDER) - ? LocationManager.NETWORK_PROVIDER - : LocationManager.GPS_PROVIDER; - if (!locationManager.isProviderEnabled(provider)) { - return null; - } - } - // If it's an enabled provider, but we don't have permissions, ignore it - int finePermission = ContextCompat.checkSelfPermission(getReactApplicationContext(), android.Manifest.permission.ACCESS_FINE_LOCATION); - if (provider.equals(LocationManager.GPS_PROVIDER) && finePermission != PackageManager.PERMISSION_GRANTED) { - return null; - } - return provider; - } - - private static WritableMap locationToMap(Location location) { - WritableMap map = Arguments.createMap(); - WritableMap coords = Arguments.createMap(); - coords.putDouble("latitude", location.getLatitude()); - coords.putDouble("longitude", location.getLongitude()); - coords.putDouble("altitude", location.getAltitude()); - coords.putDouble("accuracy", location.getAccuracy()); - coords.putDouble("heading", location.getBearing()); - coords.putDouble("speed", location.getSpeed()); - map.putMap("coords", coords); - map.putDouble("timestamp", location.getTime()); - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { - map.putBoolean("mocked", location.isFromMockProvider()); - } - - return map; - } - - private void emitError(int code, String message) { - getReactApplicationContext().getJSModule(RCTDeviceEventEmitter.class) - .emit("geolocationError", PositionError.buildError(code, message)); - } - - /** - * Provides a clearer exception message than the default one. - */ - private static void throwLocationPermissionMissing(SecurityException e) { - throw new SecurityException( - "Looks like the app doesn't have the permission to access location.\n" + - "Add the following line to your app's AndroidManifest.xml:\n" + - "", e); - } - - private static class SingleUpdateRequest { - - private final Callback mSuccess; - private final Callback mError; - 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 - public void run() { - synchronized (SingleUpdateRequest.this) { - 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; - } - } - } - }; - private final LocationListener mLocationListener = new LocationListener() { - @Override - public void onLocationChanged(Location location) { - synchronized (SingleUpdateRequest.this) { - if (!mTriggered && isBetterLocation(location, mOldLocation)) { - mSuccess.invoke(locationToMap(location)); - mHandler.removeCallbacks(mTimeoutRunnable); - mTriggered = true; - mLocationManager.removeUpdates(mLocationListener); - } - - mOldLocation = location; - } - } - - @Override - public void onStatusChanged(String provider, int status, Bundle extras) {} - - @Override - public void onProviderEnabled(String provider) {} - - @Override - public void onProviderDisabled(String provider) {} - }; - private boolean mTriggered; - - private SingleUpdateRequest( - LocationManager locationManager, - String provider, - long timeout, - Callback success, - Callback error) { - mLocationManager = locationManager; - mProvider = provider; - mTimeout = timeout; - mSuccess = success; - mError = error; - } - - 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); - } - } -} diff --git a/ReactAndroid/src/main/java/com/facebook/react/modules/location/PositionError.java b/ReactAndroid/src/main/java/com/facebook/react/modules/location/PositionError.java deleted file mode 100644 index b643382c831..00000000000 --- a/ReactAndroid/src/main/java/com/facebook/react/modules/location/PositionError.java +++ /dev/null @@ -1,51 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -package com.facebook.react.modules.location; - -import com.facebook.react.bridge.Arguments; -import com.facebook.react.bridge.WritableMap; - -/** - * @see {https://developer.mozilla.org/en-US/docs/Web/API/PositionError} - */ -public class PositionError { - /** - * The acquisition of the geolocation information failed because - * the page didn't have the permission to do it. - */ - public static int PERMISSION_DENIED = 1; - - /** - * The acquisition of the geolocation failed because at least one - * internal source of position returned an internal error. - */ - public static int POSITION_UNAVAILABLE = 2; - - /** - * The time allowed to acquire the geolocation, defined by - * PositionOptions.timeout information was reached before the information was obtained. - */ - public static int TIMEOUT = 3; - - public static WritableMap buildError(int code, String message) { - WritableMap error = Arguments.createMap(); - error.putInt("code", code); - - if (message != null) { - error.putString("message", message); - } - - /** - * Provide error types in error message. Feature parity with iOS - */ - error.putInt("PERMISSION_DENIED", PERMISSION_DENIED); - error.putInt("POSITION_UNAVAILABLE", POSITION_UNAVAILABLE); - error.putInt("TIMEOUT", TIMEOUT); - return error; - } -} diff --git a/ReactAndroid/src/main/java/com/facebook/react/shell/BUCK b/ReactAndroid/src/main/java/com/facebook/react/shell/BUCK index c136b1beb84..d564204541e 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/shell/BUCK +++ b/ReactAndroid/src/main/java/com/facebook/react/shell/BUCK @@ -34,7 +34,6 @@ rn_android_library( react_native_target("java/com/facebook/react/modules/i18nmanager:i18nmanager"), react_native_target("java/com/facebook/react/modules/image:image"), react_native_target("java/com/facebook/react/modules/intent:intent"), - react_native_target("java/com/facebook/react/modules/location:location"), react_native_target("java/com/facebook/react/modules/netinfo:netinfo"), react_native_target("java/com/facebook/react/modules/network:network"), react_native_target("java/com/facebook/react/modules/permissions:permissions"), diff --git a/ReactAndroid/src/main/java/com/facebook/react/shell/MainReactPackage.java b/ReactAndroid/src/main/java/com/facebook/react/shell/MainReactPackage.java index 31a871d5efa..1e0b35ea29a 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/shell/MainReactPackage.java +++ b/ReactAndroid/src/main/java/com/facebook/react/shell/MainReactPackage.java @@ -28,7 +28,6 @@ import com.facebook.react.modules.fresco.FrescoModule; import com.facebook.react.modules.i18nmanager.I18nManagerModule; import com.facebook.react.modules.image.ImageLoaderModule; import com.facebook.react.modules.intent.IntentModule; -import com.facebook.react.modules.location.LocationModule; import com.facebook.react.modules.netinfo.NetInfoModule; import com.facebook.react.modules.network.NetworkingModule; import com.facebook.react.modules.permissions.PermissionsModule; @@ -88,7 +87,6 @@ import javax.inject.Provider; ImageLoaderModule.class, ImageStoreManager.class, IntentModule.class, - LocationModule.class, NativeAnimatedModule.class, NetworkingModule.class, NetInfoModule.class, @@ -239,14 +237,6 @@ public class MainReactPackage extends LazyReactPackage { return new IntentModule(context); } }), - ModuleSpec.nativeModuleSpec( - LocationModule.class, - new Provider() { - @Override - public NativeModule get() { - return new LocationModule(context); - } - }), ModuleSpec.nativeModuleSpec( NativeAnimatedModule.class, new Provider() {