Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix the support for coarse location #207

Merged
merged 2 commits into from
Oct 28, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import com.google.android.gms.common.GoogleApiAvailability;

import java.util.ArrayList;
import java.util.Objects;

public class GeolocationModule extends ReactContextBaseJavaModule {

Expand All @@ -49,13 +50,13 @@ public String getName() {

public void setConfiguration(ReadableMap config) {
mConfiguration = Configuration.fromReactMap(config);
onConfigutationChange(mConfiguration);
onConfigurationChange(mConfiguration);
}

private void onConfigutationChange(Configuration config) {
if (config.locationProvider == "android" && mLocationManager instanceof PlayServicesLocationManager) {
private void onConfigurationChange(Configuration config) {
if (Objects.equals(config.locationProvider, "android") && mLocationManager instanceof PlayServicesLocationManager) {
mLocationManager = new AndroidLocationManager(mLocationManager.mReactContext);
} else if (config.locationProvider == "playServices" && mLocationManager instanceof AndroidLocationManager) {
} else if (Objects.equals(config.locationProvider, "playServices") && mLocationManager instanceof AndroidLocationManager) {
mLocationManager = new PlayServicesLocationManager(mLocationManager.mReactContext);
}
}
Expand All @@ -68,7 +69,7 @@ public void requestAuthorization(final Callback success, final Callback error) {
final PermissionsModule perms = getReactApplicationContext().getNativeModule(PermissionsModule.class);
ArrayList<String> permissions = new ArrayList<>();
permissions.add(Manifest.permission.ACCESS_COARSE_LOCATION);
permissions.add( Manifest.permission.ACCESS_FINE_LOCATION);
permissions.add(Manifest.permission.ACCESS_FINE_LOCATION);
ReadableArray permissionsArray = JavaOnlyArray.from(permissions);

final Callback onPermissionGranted = args -> {
Expand All @@ -94,7 +95,7 @@ public void requestAuthorization(final Callback success, final Callback error) {
}
};

perms.checkPermission(Manifest.permission.ACCESS_FINE_LOCATION, new PromiseImpl(onPermissionChecked, onPermissionCheckFailed));
perms.checkPermission(Manifest.permission.ACCESS_FINE_LOCATION, new PromiseImpl(onPermissionChecked, args -> perms.checkPermission(Manifest.permission.ACCESS_COARSE_LOCATION, new PromiseImpl(onPermissionChecked, onPermissionCheckFailed))));
return;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,58 +2,89 @@

import android.annotation.SuppressLint;
import android.location.Location;
import android.os.Build;
import android.os.Looper;
import android.util.Log;

import androidx.annotation.RequiresApi;

import com.facebook.react.bridge.Callback;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.common.SystemClock;
import com.facebook.react.modules.core.DeviceEventManagerModule;
import com.google.android.gms.location.FusedLocationProviderClient;
import com.google.android.gms.location.LocationAvailability;
import com.google.android.gms.location.LocationCallback;
import com.google.android.gms.location.LocationRequest;
import com.google.android.gms.location.LocationResult;
import com.google.android.gms.location.LocationServices;
import com.google.android.gms.location.LocationSettingsRequest;
import com.google.android.gms.location.LocationSettingsResponse;
import com.google.android.gms.location.Priority;
import com.google.android.gms.location.SettingsClient;
import com.google.android.gms.tasks.OnSuccessListener;

import java.util.function.Consumer;
import java.util.function.Function;

@SuppressLint("MissingPermission")
public class PlayServicesLocationManager extends BaseLocationManager {
private FusedLocationProviderClient mFusedLocationClient;
private LocationCallback mLocationCallback;
private LocationCallback mSingleLocationCallback;
private SettingsClient mLocationServicesSettingsClient;

protected PlayServicesLocationManager(ReactApplicationContext reactContext) {
super(reactContext);
mFusedLocationClient = LocationServices.getFusedLocationProviderClient(reactContext.getCurrentActivity());
mLocationCallback = new LocationCallback() {
@Override
public void onLocationResult(LocationResult locationResult) {
if (locationResult == null) {
emitError(PositionError.POSITION_UNAVAILABLE, "No location provided by FusedLocationProviderClient.");
return;
}
for (Location location : locationResult.getLocations()) {
mReactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
.emit("geolocationDidChange", locationToMap(location));
}
}
};
mFusedLocationClient = LocationServices.getFusedLocationProviderClient(reactContext);
mLocationServicesSettingsClient = LocationServices.getSettingsClient(reactContext);
}

@Override
public void getCurrentLocationData(ReadableMap options, Callback success, Callback error) {
AndroidLocationManager.LocationOptions locationOptions = AndroidLocationManager.LocationOptions.fromReactMap(options);

try {
mFusedLocationClient.getCurrentLocation(locationOptions.highAccuracy ? Priority.PRIORITY_HIGH_ACCURACY : Priority.PRIORITY_LOW_POWER, null)
mFusedLocationClient.getLastLocation()
.addOnSuccessListener(mReactContext.getCurrentActivity(), location -> {
if (location != null) {
if ((SystemClock.currentTimeMillis() - location.getTime()) < locationOptions.maximumAge) {
success.invoke(locationToMap(location));
} else {
error.invoke(PositionError.buildError(
PositionError.POSITION_UNAVAILABLE, "Last found location is older than maximumAge (FusedLocationProvider/lastLocation).")
);
}
} else {
error.invoke(PositionError.buildError(
PositionError.POSITION_UNAVAILABLE, "No location provided by FusedLocationProviderClient.")
);
mSingleLocationCallback = new LocationCallback() {
@Override
public void onLocationResult(LocationResult locationResult) {
if (locationResult == null) {
emitError(PositionError.POSITION_UNAVAILABLE, "No location provided (FusedLocationProvider/lastLocation).");
return;
}

AndroidLocationManager.LocationOptions locationOptions = AndroidLocationManager.LocationOptions.fromReactMap(options);
Location location = locationResult.getLastLocation();
if ((SystemClock.currentTimeMillis() - location.getTime()) < locationOptions.maximumAge) {
success.invoke(locationToMap(location));
} else {
emitError(PositionError.POSITION_UNAVAILABLE, "Last found location is older than maximumAge (FusedLocationProvider/lastLocation).");
}

mFusedLocationClient.removeLocationUpdates(mSingleLocationCallback);
mSingleLocationCallback = null;
}

@Override
public void onLocationAvailability(LocationAvailability locationAvailability) {
if (!locationAvailability.isLocationAvailable()) {
emitError(PositionError.POSITION_UNAVAILABLE, "Location not available (FusedLocationProvider/lastLocation).");
}
}
};
checkLocationSettings(options, mSingleLocationCallback);
}
});
} catch (SecurityException e) {
Expand All @@ -63,6 +94,41 @@ public void getCurrentLocationData(ReadableMap options, Callback success, Callba

@Override
public void startObserving(ReadableMap options) {
mLocationCallback = new LocationCallback() {
@Override
public void onLocationResult(LocationResult locationResult) {
if (locationResult == null) {
emitError(PositionError.POSITION_UNAVAILABLE, "No location provided (FusedLocationProvider/observer).");
return;
}

AndroidLocationManager.LocationOptions locationOptions = AndroidLocationManager.LocationOptions.fromReactMap(options);
Location location = locationResult.getLastLocation();
if ((SystemClock.currentTimeMillis() - location.getTime()) < locationOptions.maximumAge) {
mReactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
.emit("geolocationDidChange", locationToMap(locationResult.getLastLocation()));
} else {
emitError(PositionError.POSITION_UNAVAILABLE, "Last found location is older than maximumAge (FusedLocationProvider/observer).");
}
}

@Override
public void onLocationAvailability(LocationAvailability locationAvailability) {
if (!locationAvailability.isLocationAvailable()) {
emitError(PositionError.POSITION_UNAVAILABLE, "Location not available (FusedLocationProvider).");
}
}
};

checkLocationSettings(options, mLocationCallback);
}

@Override
public void stopObserving() {
mFusedLocationClient.removeLocationUpdates(mLocationCallback);
}

private void checkLocationSettings(ReadableMap options, LocationCallback locationCallback) {
LocationOptions locationOptions = LocationOptions.fromReactMap(options);
LocationRequest locationRequest = LocationRequest.create();
locationRequest.setInterval(locationOptions.interval);
Expand All @@ -74,15 +140,21 @@ public void startObserving(ReadableMap options) {
locationRequest.setPriority(
locationOptions.highAccuracy ? LocationRequest.PRIORITY_HIGH_ACCURACY : LocationRequest.PRIORITY_LOW_POWER
);

LocationSettingsRequest.Builder builder = new LocationSettingsRequest.Builder();
builder.addLocationRequest(locationRequest);
LocationSettingsRequest locationSettingsRequest = builder.build();

mLocationServicesSettingsClient.checkLocationSettings(locationSettingsRequest)
.addOnSuccessListener(locationSettingsResponse -> requestLocationUpdates(locationRequest, locationCallback))
.addOnFailureListener(err -> emitError(PositionError.POSITION_UNAVAILABLE, "Location not available (FusedLocationProvider/settings)."));
}

private void requestLocationUpdates(LocationRequest locationRequest, LocationCallback locationCallback) {
try {
mFusedLocationClient.requestLocationUpdates(locationRequest, mLocationCallback, Looper.getMainLooper());
mFusedLocationClient.requestLocationUpdates(locationRequest, locationCallback, Looper.getMainLooper());
} catch (SecurityException e) {
throw e;
}
}

@Override
public void stopObserving() {
mFusedLocationClient.removeLocationUpdates(mLocationCallback);
}
}
3 changes: 2 additions & 1 deletion example/android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,8 @@ import org.apache.tools.ant.taskdefs.condition.Os
*/

project.ext.react = [
enableHermes: false, // clean and rebuild if changing
enableHermes: true, // clean and rebuild if changing
entryFile: "index.tsx"
]

apply from: "../../node_modules/react-native/react.gradle"
Expand Down
3 changes: 2 additions & 1 deletion example/src/configs/SetConfiguration.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,9 @@ export default function SetConfigurationExample() {
Geolocation.setRNConfiguration({
skipPermissionRequests,
authorizationLevel,
locationProvider,
});
}, [skipPermissionRequests, authorizationLevel]);
}, [skipPermissionRequests, authorizationLevel, locationProvider]);

return (
<View>
Expand Down
5 changes: 1 addition & 4 deletions example/src/examples/GetCurrentLocation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

'use strict';

import React, { useState, useEffect } from 'react';
import React, { useState } from 'react';
import { StyleSheet, Text, View, Alert, Button } from 'react-native';
import Geolocation from '@react-native-community/geolocation';

Expand All @@ -25,9 +25,6 @@ export default function GetCurrentLocationExample() {
};

const [position, setPosition] = useState<string | null>(null);
useEffect(() => {
getCurrentPosition();
}, []);

return (
<View>
Expand Down
2 changes: 0 additions & 2 deletions example/src/examples/WatchPosition.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,6 @@ export default function WatchPositionExample() {
const [position, setPosition] = useState<string | null>(null);
const [subscriptionId, setSubscriptionId] = useState<number | null>(null);
useEffect(() => {
watchPosition();

return () => {
clearWatch();
};
Expand Down