Skip to content

Instantly share code, notes, and snippets.

@mgdiez
Last active April 1, 2020 20:29
Show Gist options
  • Save mgdiez/f9d816e378bbd601e32fd7e18c46675f to your computer and use it in GitHub Desktop.
Save mgdiez/f9d816e378bbd601e32fd7e18c46675f to your computer and use it in GitHub Desktop.
Reactive Location Provider using new FusedLocationProviderClient
package *;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.content.IntentSender;
import android.location.Location;
import android.os.Looper;
import android.support.annotation.VisibleForTesting;
import com.google.android.gms.common.api.ApiException;
import com.google.android.gms.common.api.ResolvableApiException;
import com.google.android.gms.location.FusedLocationProviderClient;
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.LocationSettingsStatusCodes;
import com.google.android.gms.location.SettingsClient;
import com.seat.connectedcar.driveapp.core.executor.MainThread;
import rx.Emitter;
import rx.Observable;
import rx.Subscriber;
import rx.Subscription;
import rx.subscriptions.Subscriptions;
/*
* Precondition: User has accepted Location permission
*/
public class RxLocationProvider {
@VisibleForTesting public Subscription locationSubscription = Subscriptions.empty();
@VisibleForTesting public long updateIntervalInMilliseconds;
@VisibleForTesting public long fastestUpdateIntervalInMilliseconds;
@VisibleForTesting public int requestCode;
private Context context;
private Activity activity;
private RxLocationCallback callback;
private FusedLocationProviderClient fusedLocationProviderClient;
private LocationCallback locationCallback;
private SettingsClient settingsClient;
private LocationRequest locationRequest;
private LocationSettingsRequest locationSettingsRequest;
private boolean requestingUpdates = false;
public RxLocationProvider(long updateInterval, long fastestUpdateInterval, Context context,
Activity activity, int requestCode, RxLocationCallback callback) {
this.updateIntervalInMilliseconds = updateInterval;
this.fastestUpdateIntervalInMilliseconds = fastestUpdateInterval;
this.requestCode = requestCode;
this.context = context;
this.activity = activity;
this.callback = callback;
}
private LocationRequest createLocationRequest() {
LocationRequest locationRequest = new LocationRequest();
locationRequest.setInterval(updateIntervalInMilliseconds);
locationRequest.setFastestInterval(fastestUpdateIntervalInMilliseconds);
locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
return locationRequest;
}
private LocationSettingsRequest buildLocationSettingsRequest(LocationRequest locationRequest) {
LocationSettingsRequest.Builder builder = new LocationSettingsRequest.Builder();
builder.addLocationRequest(locationRequest);
return builder.build();
}
public void initialize(RxLocationCallback callback) {
locationSubscription =
createConnectivityObserver().observeOn(new MainThread().getScheduler()).subscribe(callback);
}
private void removeLocationCallback() {
if (!locationSubscription.isUnsubscribed()) {
locationSubscription.unsubscribe();
}
}
private Observable<Location> createConnectivityObserver() {
return Observable.create(emitter -> {
fusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(context);
settingsClient = LocationServices.getSettingsClient(context);
locationRequest = createLocationRequest();
locationSettingsRequest = buildLocationSettingsRequest(locationRequest);
locationCallback = new LocationCallback() {
@Override public void onLocationResult(LocationResult locationResult) {
super.onLocationResult(locationResult);
Location currentLocation = locationResult.getLastLocation();
emitter.onNext(currentLocation);
}
};
emitter.setCancellation(() -> {
fusedLocationProviderClient.removeLocationUpdates(locationCallback)
.addOnCompleteListener(task -> {
requestingUpdates = false;
locationCallback = null;
});
});
}, Emitter.BackpressureMode.BUFFER);
}
public void start() {
if (!isInitialized()) {
initialize(callback);
}
startLocationUpdates(settingsClient, locationSettingsRequest, fusedLocationProviderClient,
locationRequest, locationCallback);
}
public void stop() {
if (isInitialized()) {
removeLocationCallback();
}
}
private boolean isInitialized() {
return settingsClient != null
&& locationSettingsRequest != null
&& fusedLocationProviderClient != null
&& locationRequest != null
&& locationCallback != null
&& callback != null;
}
@SuppressLint("MissingPermission")
private void startLocationUpdates(SettingsClient settingsClient,
LocationSettingsRequest locationSettingsRequest,
FusedLocationProviderClient fusedLocationProviderClient, LocationRequest locationRequest,
LocationCallback locationCallback) {
if (!requestingUpdates) {
requestingUpdates = true;
settingsClient.checkLocationSettings(locationSettingsRequest)
.addOnSuccessListener(
locationSettingsResponse -> fusedLocationProviderClient.requestLocationUpdates(
locationRequest, locationCallback, Looper.myLooper()))
.addOnFailureListener(e -> {
int statusCode = ((ApiException) e).getStatusCode();
switch (statusCode) {
case LocationSettingsStatusCodes.RESOLUTION_REQUIRED:
if (activity != null) {
try {
ResolvableApiException rae = (ResolvableApiException) e;
rae.startResolutionForResult(activity, requestCode);
requestingUpdates = false;
} catch (IntentSender.SendIntentException sie) {
//
}
}
break;
case LocationSettingsStatusCodes.SETTINGS_CHANGE_UNAVAILABLE:
requestingUpdates = false;
}
});
}
}
public abstract static class RxLocationCallback extends Subscriber<Location> {
public abstract void onLocationChange(Location location);
@Override public void onCompleted() {
unsubscribe();
}
@Override public void onError(Throwable e) {
unsubscribe();
}
@Override public void onNext(Location location) {
onLocationChange(location);
}
}
public static class Builder {
private long updateInterval = 10000;
private long fastestUpdateInterval = updateInterval / 2;
private Context context;
private Activity activity = null;
private RxLocationCallback rxLocationCallback;
private int requestCode = 1;
public Builder updateIntervalMilliseconds(long updateInterval) {
this.updateInterval = updateInterval;
return this;
}
public Builder fastestIntervalMilliseconds(long fastestUpdateInterval) {
this.fastestUpdateInterval = fastestUpdateInterval;
return this;
}
public Builder context(Context context) {
this.context = context;
return this;
}
public Builder activity(Activity activity) {
this.activity = activity;
return this;
}
public Builder callback(RxLocationCallback callback) {
rxLocationCallback = callback;
return this;
}
public Builder requestCode(int requestCode) {
this.requestCode = requestCode;
return this;
}
public RxLocationProvider build() {
return new RxLocationProvider(updateInterval, fastestUpdateInterval, context, activity,
requestCode, rxLocationCallback);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment