Last active
April 1, 2020 20:29
-
-
Save mgdiez/f9d816e378bbd601e32fd7e18c46675f to your computer and use it in GitHub Desktop.
Reactive Location Provider using new FusedLocationProviderClient
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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