Skip to content

Instantly share code, notes, and snippets.

@cyrixmorten
Created October 23, 2015 13:37
Show Gist options
  • Save cyrixmorten/2c3bad07f0848ef5dd50 to your computer and use it in GitHub Desktop.
Save cyrixmorten/2c3bad07f0848ef5dd50 to your computer and use it in GitHub Desktop.
ReactiveLocation in Service
public class FusedLocationTrackerService extends InjectingService {
private static final String TAG = FusedLocationTrackerService.class.getSimpleName();
public FusedLocationTrackerService() {
}
public static void start(Context context) {
Log.e(TAG, "startGPSTrackerService");
context.startService(new Intent(context, FusedLocationTrackerService.class));
}
public static void stop(Context context) {
context.stopService(new Intent(context, FusedLocationTrackerService.class));
}
private static final int DISTANCE_METERS_FOR_GEOFENCEREBUILD = 1500;
private static final int DISTANCE_METERS_FOR_UIUPDATE = 20;
private Subscription locationSubscription;
private Location mLastGeofenceRebuildLocation;
private Location mLastUIUpdateLocation;
private LocationTracker locationTracker;
@Inject
GeofencingModule geofencingModule;
@Inject
GuardCache guardCache;
@Override
public void onCreate() {
Log.d(TAG, "GPSTrackerService onCreate");
super.onCreate();
}
@Override
public int onStartCommand(Intent intent, int flags, final int startId) {
Log.i(TAG, "Starting GPSTrackerService!");
if (hasGooglePlayServices()) {
locationTracker = new LocationTracker();
requestLocationUpdates();
}
return Service.START_STICKY;
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onDestroy() {
Log.i(TAG, "Stopping GPSTrackerService");
if (hasGooglePlayServices()) {
unsubscribeLocationUpdates();
}
super.onDestroy();
}
public void unsubscribeLocationUpdates() {
if (locationSubscription != null && !locationSubscription.isUnsubscribed()) {
Log.i(TAG, "Unsubscribe location updates");
try {
locationSubscription.unsubscribe();
} catch (Exception e) {
e.printStackTrace();
}
locationSubscription = null;
}
}
private boolean hasGooglePlayServices() {
int result = GooglePlayServicesUtil.isGooglePlayServicesAvailable(getApplicationContext());
if (result != ConnectionResult.SUCCESS) {
return false;
}
return true;
}
private void requestLocationUpdates() {
Log.i(TAG, "requestLocationUpdates");
unsubscribeLocationUpdates(); // in case we are already listening
LocationRequest request = LocationRequest.create() //standard GMS LocationRequest
.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY)
.setInterval(10000)
.setFastestInterval(5000);
ReactiveLocationProvider locationProvider = new ReactiveLocationProvider(getApplicationContext());
locationSubscription = locationProvider.getUpdatedLocation(request)
.observeOn(Schedulers.from(Executors.newSingleThreadExecutor()))
.doOnError(new Action1<Throwable>() {
@Override
public void call(Throwable throwable) {
String message = "Error on location init: " + throwable.getMessage();
Log.e(TAG, message, throwable);
Crashlytics.logException(throwable);
}
})
.onErrorReturn(new Func1<Throwable, Location>() {
@Override
public Location call(Throwable throwable) {
Location location = new android.location.Location("");
return location;
}
})
.filter(new Func1<Location, Boolean>() {
@Override
public Boolean call(Location location) {
if (location.getAccuracy() > 30) {
return false;
}
return true;
}
})
.subscribe(new Action1<Location>() {
@Override
public void call(Location location) {
if (guardCache.isLoggedIn()) {
LocationModule.Recent.setLastKnownLocation(location);
if (!rebuildGeofencesIfDistanceThresholdReached(location)) {
updateUIIfDistanceThresholdReached(location);
inspectDistanceToGeofencedTasks(location);
}
Guard guard = guardCache.getLoggedIn();
locationTracker.appendLocation(getApplicationContext(), guard, location);
} else {
stopSelf();
}
}
});
}
private boolean rebuildGeofencesIfDistanceThresholdReached(Location location) {
float distance = Util.distanceMeters(mLastGeofenceRebuildLocation, location);
if (mLastGeofenceRebuildLocation == null || distance >= DISTANCE_METERS_FOR_GEOFENCEREBUILD) {
mLastGeofenceRebuildLocation = location;
RegisterGeofencesIntentService.start(getApplicationContext());
return true;
}
return false;
}
private void updateUIIfDistanceThresholdReached(Location location) {
float distance = Util.distanceMeters(mLastUIUpdateLocation, location);
if (mLastUIUpdateLocation == null || distance >= DISTANCE_METERS_FOR_UIUPDATE) {
mLastUIUpdateLocation = location;
EventBusController.postUIUpdate(location);
}
}
private void inspectDistanceToGeofencedTasks(Location location) {
Set<GSTask> geofencedTasks = GeofencingModule.Recent.getAllGeofencedTasks();
List<String> tasksWithinGeofence = Lists.newArrayList();
List<String> tasksMovedWithinGeofence = Lists.newArrayList();
List<String> tasksMovedOutsideGeofence = Lists.newArrayList();
for (GSTask task: geofencedTasks) {
ParseGeoPoint clientPosition = task.getClientPosition();
float distance = ParseModule.distanceBetweenMeters(location, clientPosition);
int radius = task.getGeofenceStrategy().getGeofenceRadius();
if (distance <= radius) {
tasksWithinGeofence.add(task.getParseObject().getObjectId());
}
if (GeofencingModule.Recent.getWithinGeofence().contains(task)) {
if (distance > radius) {
tasksMovedOutsideGeofence.add(task.getParseObject().getObjectId());
}
} else {
if (distance < radius) {
tasksMovedWithinGeofence.add(task.getParseObject().getObjectId());
}
}
}
geofencingModule.onWithinGeofences(tasksWithinGeofence.toArray(new String[tasksWithinGeofence.size()]));
geofencingModule.onEnteredGeofences(tasksMovedWithinGeofence.toArray(new String[tasksMovedWithinGeofence.size()]), true);
geofencingModule.onExitedGeofences(tasksMovedOutsideGeofence.toArray(new String[tasksMovedOutsideGeofence.size()]), true);
}
}
@sytolk
Copy link

sytolk commented Oct 23, 2015

Line: 128 if (location.getAccuracy() > 30) -its possible to have NullPointer exception here better check if(location!=null) first.
Line: 109 .observeOn(Schedulers.from(Executors.newSingleThreadExecutor())) - I`m not so familiar with RxJava but code is in Service I think that is not need work to be executed on new thread OR ?

This issue is related:
mcharmas/Android-ReactiveLocation#54

How you start this Service? Its have problem when device goes to the deep sleep and I`m thinking about usage of WakefulIntentService.
https://github.com/commonsguy/cwac-wakeful

@farmazon3000
Copy link

@cyrixmorten Can you please give more light on what this Service is intended to do, I'm doing similar stuff, I may share my experience with you.

What is InjectingService? And what is RegisterGeofencesIntentService?

@sytolk
Copy link

sytolk commented Nov 3, 2015

@farmazon3000 InjectingService is simple android Service @cyrixmorten used dependency injection dagger framework.
If I have time I will publish in github new project with examples of service for continuous location monitoring..

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment