RxGpsService
An Android service to retrieve GPS locations and route stats using RxJava
Features:
- Runtime permissions
- Highly customizable
- Always alive even if app is closed
- Provides route stats (time, speeds, distance and waypoints)
- Navigation mode auto/manual
- Possibility to stop and resume the chrono at anytime
Setup
Add the JitPack repository in your build.gradle (top level module):
allprojects {
repositories {
jcenter()
maven {
url "https://jitpack.io"
}
}
}
And add next dependencies in the build.gradle of the module:
dependencies {
compile "com.github.miguelbcr:RxGpsService:0.1.0"
compile "io.reactivex:rxjava:1.1.10"
}
Usage
Starting the service
The basic usage is as follow, it will use the default configuration defined in GpsConfig
RxGpsService.builder(getActivity())
.startService(new RxGpsService.Listener() {
@Override
public NotificationCompat.Builder notificationServiceStarted(Context context) {
return new NotificationCompat.Builder(context)
.setContentTitle(context.getString(R.string.app_name))
.setContentText(context.getString(R.string.route_started))
.setSmallIcon(R.drawable.ic_place);
}
@Override
public void onServiceAlreadyStarted() {
// Service is already started.
// Now you can listen for location updates
startListenForLocationUpdates();
}
@Override
public void onNavigationModeChanged(boolean isAuto) {
// Called when gps is switched from auto <-> manual
}
}
);
But you can start the RxGpsService using your custom config:
RxGpsService.builder(getActivity())
.withDebugMode(true)
.withSpeedMinModeAuto(5 / 3.6f)
.withStageDistance(0)
.withDiscardSpeedsAbove(150 / 3.6f)
.withMinDistanceTraveled(10)
.withPriority(LocationRequest.PRIORITY_HIGH_ACCURACY)
.withInterval(10000)
.withFastestInterval(5000)
.withDetailedWaypoints(false)
.startService(new RxGpsService.Listener() {
@Override
public NotificationCompat.Builder notificationServiceStarted(Context context) {
return new NotificationCompat.Builder(context)
.setContentTitle(context.getString(R.string.app_name))
.setContentText(context.getString(R.string.route_started))
.setSmallIcon(R.drawable.ic_place);
}
@Override
public void onServiceAlreadyStarted() {
// Service is already started.
// Now you can listen for location updates
startListenForLocationUpdates();
}
@Override
public void onNavigationModeChanged(boolean isAuto) {
// Called when gps is switched from auto <-> manual
}
}
);
Additionally for NotificationCompat.Builder you can set extra parameters in order to customize the notification created by RxGpsService:
RxGpsService.builder(getActivity())
.startService(new RxGpsService.Listener() {
@Override
public NotificationCompat.Builder notificationServiceStarted(Context context) {
Bundle extras = new Bundle();
extras.putBoolean(RxGpsServiceExtras.SHOW_TIME, true);
extras.putString(RxGpsServiceExtras.TEXT_TIME, context.getString(R.string.time_elapsed));
extras.putBoolean(RxGpsServiceExtras.SHOW_DISTANCE, true);
extras.putString(RxGpsServiceExtras.TEXT_DISTANCE, context.getString(R.string.distance_traveled));
extras.putString(RxGpsServiceExtras.BIG_CONTENT_TITLE, context.getString(R.string.route_details));
return new NotificationCompat.Builder(context)
.setContentTitle(context.getString(R.string.app_name))
.setContentText(context.getString(R.string.route_started))
.setSmallIcon(R.drawable.ic_place)
.setExtras(extras);
}
@Override
public void onServiceAlreadyStarted() {
// Service is already started.
// Now you can listen for location updates
startListenForLocationUpdates();
}
@Override
public void onNavigationModeChanged(boolean isAuto) {
// Called when gps is switched from auto <-> manual
}
}
);
You can see all available options in RxGpsServiceExtras
Listening for location updates
In order to get the latest updated RouteStats object, you will need to subscribe to the RxGpsService#onRouteStatsUpdates() method, which is called every second, because of the chrono, but the RouteStats could vary depending on the GpsConfig used on the RxGpsService.Builder.
public class RouteFragment extends Fragment {
private Subscription subscription;
....
@Override public void onDestroy() {
super.onDestroy();
unsubscribe();
}
private void unsubscribe() {
if (subscription != null) {
subscription.unsubscribe();
subscription = null;
}
}
private void startListenForLocationUpdates() {
if (RxGpsService.isServiceStarted()) {
unsubscribe()
subscription = RxGpsService.instance().onRouteStatsUpdates()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<RouteStats>() {
@Override
public void call(RouteStats routeStats) {
drawUserPath(routeStats);
}
}
, new Action1<Throwable>() {
@Override
public void call(Throwable throwable) {
RxGpsService.stopService(getContext());
unsubscribe();
}
}
);
}
}
...
}
Stopping and resuming the route trip
You can stop or resume the route trip by using RxGpsService#stopChrono() or RxGpsService#playChrono() or you can enable the navigation mode to auto by using RxGpsService#setNavigationModeAuto() which will use builder.withSpeedMinModeAuto()
to stop/resume the chrono if speed if lower/higher than specified respectively. But even with the navigation in on mode auto you can also use RxGpsService#stopChrono() or RxGpsService#playChrono().
Notice that RxGpsService#onRouteStatsUpdates() method will receive updates every seconds even when the RxGpsService#stopChrono() is called, but the RouteStats object will be the same just before when the chrono was stopped. So if you do not want to receive updates you only have to unsubscribe from the RxGpsService#onRouteStatsUpdates() subscription.
Example
You can see a complete example in sample app
A benchmark case
This is a memory size reference for RouteStats running for 10 hour and emitting 1 meaningful waypoint per second:
- Using
builder.withDetailedWaypoints(false)
: ~ 3Mb - Using
builder.withDetailedWaypoints(true)
: ~ 12Mb
Credits
- Runtime permissions: RxPermissions
- Reactive Location: Android-ReactiveLocation
Authors
Miguel García
Another author's libraries:
- RxPaparazzo: RxJava extension for Android to take images using camera and gallery.
- OkAdapters: Wrappers for Android adapters to simply its api at a minimum
Another useful libraries used on the sample app:
- ReactiveCache: A reactive cache for Android and Java which honors the Observable chain.
- Jolyglot: Agnostic Json abstraction to perform data binding operations for Android and Java.
- ACRA: Application Crash Reports for Android