Skip to content

Commit

Permalink
Added support for Activity Recognition
Browse files Browse the repository at this point in the history
Using broadcast receiver instead of service for activity recognition.
Polished activity recognition sample.
  • Loading branch information
cyrixmorten authored and mcharmas committed Jan 28, 2015
1 parent f0c543e commit 89efbfa
Show file tree
Hide file tree
Showing 17 changed files with 449 additions and 129 deletions.
21 changes: 21 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ What can you do with that?
* subscribe for location updates
* manage geofences
* geocode location to list of addresses
* activity recognition

How does the API look like?
----------------------------
Expand Down Expand Up @@ -69,6 +70,26 @@ When you are done (for example in ```onStop()```) remember to unsubscribe.
subscription.unsubscribe();
```

### Subscribing for Activity Recognition

Getting activity recognition is just as simple

```java

ReactiveLocationProvider locationProvider = new ReactiveLocationProvider(context);
Subscription subscription = activityUpdatesObservable = locationProvider.getDetectedActivity(0) // detectionIntervalMillis
.filter(...) // you can filter location updates
.map(...) // you can map location to sth different
.flatMap(...) // or event flat map
... // and do everything else that is provided by RxJava
.subscribe(new Action1<DetectedActivity>() {
@Override
public void call(DetectedActivity detectedActivity) {
doSthImportantWithObtainedActivity(detectedActivity);
}
});
```

### Geocode location

Do you need address for location?
Expand Down
6 changes: 3 additions & 3 deletions android-reactive-location/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="pl.charmas.android.reactivelocation" >
<application/>
<manifest package="pl.charmas.android.reactivelocation">

<application></application>
</manifest>
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@
import android.location.Address;
import android.location.Location;

import com.google.android.gms.location.ActivityRecognitionResult;
import com.google.android.gms.location.GeofencingRequest;
import com.google.android.gms.location.LocationRequest;

import java.util.List;

import pl.charmas.android.reactivelocation.observables.activity.ActivityUpdatesObservable;
import pl.charmas.android.reactivelocation.observables.geocode.GeodecodeObservable;
import pl.charmas.android.reactivelocation.observables.geofence.AddGeofenceObservable;
import pl.charmas.android.reactivelocation.observables.geofence.AddGeofenceResult;
Expand Down Expand Up @@ -92,7 +94,7 @@ public Observable<List<Address>> getGeocodeObservable(double lat, double lng, in
* Every exception is delivered by {@link rx.Observer#onError(Throwable)}.
*
* @param geofenceTransitionPendingIntent pending intent to register on geofence transition
* @param request list of request to add
* @param request list of request to add
* @return observable that adds request
*/
public Observable<AddGeofenceResult> addGeofences(PendingIntent geofenceTransitionPendingIntent, GeofencingRequest request) {
Expand Down Expand Up @@ -134,4 +136,15 @@ public Observable<RemoveGeofencesResult.PendingIntentRemoveGeofenceResult> remov
public Observable<RemoveGeofencesResult.RequestIdsRemoveGeofenceResult> removeGeofences(List<String> requestIds) {
return RemoveGeofenceObservable.createObservable(ctx, requestIds);
}


/**
* Observable that can be used to observe activity provided by Actity Recognition mechanism.
*
* @param detectIntervalMiliseconds detecion interval
* @return observable that provides activity recognition
*/
public Observable<ActivityRecognitionResult> getDetectedActivity(int detectIntervalMiliseconds) {
return ActivityUpdatesObservable.createObservable(ctx, detectIntervalMiliseconds);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package pl.charmas.android.reactivelocation.observables;

import android.content.Context;

import com.google.android.gms.location.ActivityRecognition;


public abstract class BaseActivityObservable<T> extends BaseObservable<T> {


protected BaseActivityObservable(Context ctx) {
super(ctx, ActivityRecognition.API);
}

}
Original file line number Diff line number Diff line change
@@ -1,90 +1,89 @@
package pl.charmas.android.reactivelocation.observables;

import android.content.Context;
import android.os.Bundle;

import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.location.LocationServices;

import rx.Observable;
import rx.Observer;
import rx.Subscriber;
import rx.functions.Action0;
import rx.subscriptions.Subscriptions;

//public abstract class BaseLocationObservable<T> implements Observable.OnSubscribe<T> {
//
// private final Context ctx;
//
// protected BaseLocationObservable(Context ctx) {
// this.ctx = ctx;
// }
//
// @Override
// public void call(Subscriber<? super T> subscriber) {
// final LocationConnectionCallbacks locationConnectionCallbacks = new LocationConnectionCallbacks(subscriber);
// final GoogleApiClient apiClient = new GoogleApiClient.Builder(ctx)
// .addApi(LocationServices.API)
// .addConnectionCallbacks(locationConnectionCallbacks)
// .addOnConnectionFailedListener(locationConnectionCallbacks)
// .build();
// locationConnectionCallbacks.setClient(apiClient);
//
// try {
// apiClient.connect();
// } catch (Throwable ex) {
// subscriber.onError(ex);
// }
//
// subscriber.add(Subscriptions.create(new Action0() {
// @Override
// public void call() {
// if (apiClient.isConnected() || apiClient.isConnecting()) {
// onUnsubscribed(apiClient);
// apiClient.disconnect();
// }
// }
// }));
// }
//
// protected void onUnsubscribed(GoogleApiClient locationClient) {
// }
//
// protected abstract void onGoogleApiClientReady(GoogleApiClient apiClient, Observer<? super T> observer);
//
// private class LocationConnectionCallbacks implements
// GoogleApiClient.ConnectionCallbacks,
// GoogleApiClient.OnConnectionFailedListener {
// final private Observer<? super T> observer;
// private GoogleApiClient apiClient;
//
// private LocationConnectionCallbacks(Observer<? super T> observer) {
// this.observer = observer;
// }
//
// @Override
// public void onConnected(Bundle bundle) {
// try {
// onGoogleApiClientReady(apiClient, observer);
// } catch (Throwable ex) {
// observer.onError(ex);
// }
// }
//
// @Override
// public void onConnectionSuspended(int cause) {
// observer.onError(new LocationConnectionSuspendedException(cause));
// }
//
// @Override
// public void onConnectionFailed(ConnectionResult connectionResult) {
// observer.onError(new LocationConnectionException("Error connecting to LocationClient.", connectionResult));
// }
//
// public void setClient(GoogleApiClient client) {
// this.apiClient = client;
// }
// }
//}
public abstract class BaseLocationObservable<T> extends BaseObservable<T> {

public abstract class BaseLocationObservable<T> implements Observable.OnSubscribe<T> {

private final Context ctx;

protected BaseLocationObservable(Context ctx) {
this.ctx = ctx;
}

@Override
public void call(Subscriber<? super T> subscriber) {
final LocationConnectionCallbacks locationConnectionCallbacks = new LocationConnectionCallbacks(subscriber);
final GoogleApiClient apiClient = new GoogleApiClient.Builder(ctx)
.addApi(LocationServices.API)
.addConnectionCallbacks(locationConnectionCallbacks)
.addOnConnectionFailedListener(locationConnectionCallbacks)
.build();
locationConnectionCallbacks.setClient(apiClient);

try {
apiClient.connect();
} catch (Throwable ex) {
subscriber.onError(ex);
}

subscriber.add(Subscriptions.create(new Action0() {
@Override
public void call() {
if (apiClient.isConnected() || apiClient.isConnecting()) {
onUnsubscribed(apiClient);
apiClient.disconnect();
}
}
}));
}

protected void onUnsubscribed(GoogleApiClient locationClient) {
super(ctx, LocationServices.API);
}

protected abstract void onLocationClientReady(GoogleApiClient apiClient, Observer<? super T> observer);

private class LocationConnectionCallbacks implements
GoogleApiClient.ConnectionCallbacks,
GoogleApiClient.OnConnectionFailedListener {
final private Observer<? super T> observer;
private GoogleApiClient apiClient;

private LocationConnectionCallbacks(Observer<? super T> observer) {
this.observer = observer;
}

@Override
public void onConnected(Bundle bundle) {
try {
onLocationClientReady(apiClient, observer);
} catch (Throwable ex) {
observer.onError(ex);
}
}

@Override
public void onConnectionSuspended(int cause) {
observer.onError(new LocationConnectionSuspendedException(cause));
}

@Override
public void onConnectionFailed(ConnectionResult connectionResult) {
observer.onError(new LocationConnectionException("Error connecting to LocationClient.", connectionResult));
}

public void setClient(GoogleApiClient client) {
this.apiClient = client;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
package pl.charmas.android.reactivelocation.observables;

import android.content.Context;
import android.os.Bundle;

import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.api.Api;
import com.google.android.gms.common.api.GoogleApiClient;

import java.util.Arrays;
import java.util.List;

import rx.Observable;
import rx.Observer;
import rx.Subscriber;
import rx.functions.Action0;
import rx.subscriptions.Subscriptions;


public abstract class BaseObservable<T> implements Observable.OnSubscribe<T> {

private static final String TAG = BaseObservable.class.getSimpleName();

private final Context ctx;
private final List<Api<? extends Api.ApiOptions.NotRequiredOptions>> services;


protected BaseObservable(Context ctx, Api<? extends Api.ApiOptions.NotRequiredOptions>... services) {
this.ctx = ctx;
this.services = Arrays.asList(services);
}

@Override
public void call(Subscriber<? super T> subscriber) {

final GoogleApiClient apiClient = createApiClient(subscriber);
try {
apiClient.connect();
} catch (Throwable ex) {
subscriber.onError(ex);
}

subscriber.add(Subscriptions.create(new Action0() {
@Override
public void call() {
if (apiClient.isConnected() || apiClient.isConnecting()) {
onUnsubscribed(apiClient);
apiClient.disconnect();
}
}
}));
}


protected GoogleApiClient createApiClient(Subscriber<? super T> subscriber) {

ApiClientConnectionCallbacks apiClientConnectionCallbacks = new ApiClientConnectionCallbacks(subscriber);

GoogleApiClient.Builder apiClientBuilder = new GoogleApiClient.Builder(ctx);


for (Api<? extends Api.ApiOptions.NotRequiredOptions> service : services) {
apiClientBuilder.addApi(service);
}

apiClientBuilder.addConnectionCallbacks(apiClientConnectionCallbacks);
apiClientBuilder.addOnConnectionFailedListener(apiClientConnectionCallbacks);

GoogleApiClient apiClient = apiClientBuilder.build();

apiClientConnectionCallbacks.setClient(apiClient);

return apiClient;

}

protected void onUnsubscribed(GoogleApiClient locationClient) {
}

protected abstract void onGoogleApiClientReady(GoogleApiClient apiClient, Observer<? super T> observer);

private class ApiClientConnectionCallbacks implements
GoogleApiClient.ConnectionCallbacks,
GoogleApiClient.OnConnectionFailedListener {

final private Observer<? super T> observer;

private GoogleApiClient apiClient;

private ApiClientConnectionCallbacks(Observer<? super T> observer) {
this.observer = observer;
}

@Override
public void onConnected(Bundle bundle) {
try {
onGoogleApiClientReady(apiClient, observer);
} catch (Throwable ex) {
observer.onError(ex);
}
}

@Override
public void onConnectionSuspended(int cause) {
observer.onError(new LocationConnectionSuspendedException(cause));
}

@Override
public void onConnectionFailed(ConnectionResult connectionResult) {
observer.onError(new LocationConnectionException("Error connecting to GoogleApiClient.", connectionResult));
}

public void setClient(GoogleApiClient client) {
this.apiClient = client;
}
}

}
Loading

0 comments on commit 89efbfa

Please sign in to comment.