Skip to content

Commit

Permalink
Fix the support for coarse location (#207)
Browse files Browse the repository at this point in the history
* fix(android): Fix the support for coarse location

* fix(android): Remove comment
  • Loading branch information
michalchudziak authored Oct 28, 2022
1 parent 34ed332 commit 362bf80
Show file tree
Hide file tree
Showing 6 changed files with 108 additions and 38 deletions.
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

0 comments on commit 362bf80

Please sign in to comment.