Commit d16f28c9 authored by Chris Scott's avatar Chris Scott

Implement BOOT_COMPLETED receiver. NOTE: You must manually add the android...

Implement BOOT_COMPLETED receiver.  NOTE:  You must manually add the android permission android.permission.RECEIVE_BOOT_COMPLETED to your AndroidManifest -- the plugin has this element in its plugin.xml purposely commented-out.  Also, when the BackgroundGeolocationService is run due to a BOOT_COMPLETED, there's no way to fetch any kind of user info, such as an authentication_token for your server to authenticate an HTTP request (if you've enabled the HTTP feature).  However, the HTTP request will insert the Android UUID which you *can* use to authenticate the request.  It's up to the developer to map the Android UUID to some particular user on your server.  You may fetch the Android UUID using the standard cordova plugin org.apache.cordova.device (see the documentation there on that API)
parent ca77173d
...@@ -178,10 +178,10 @@ var app = { ...@@ -178,10 +178,10 @@ var app = {
distanceFilter: 50, distanceFilter: 50,
locationUpdateInterval: 5000, locationUpdateInterval: 5000,
activityRecognitionInterval: 10000, activityRecognitionInterval: 10000,
stopTimeout: 1, stopTimeout: 0,
forceReload: true, // <-- If the user closes the app **while location-tracking is started** , reboot app (WARNING: possibly distruptive to user) forceReload: true, // <-- If the user closes the app **while location-tracking is started** , reboot app (WARNING: possibly distruptive to user)
activityType: 'AutomotiveNavigation', stopOnTerminate: false, // <-- Allow the background-service to run headless when user closes the app.
stopOnTerminate: false // <-- Allow the background-service to run headless when user closes the app. activityType: 'AutomotiveNavigation'
/** /**
* HTTP Feature: set an url to allow the native background service to POST locations to your server * HTTP Feature: set an url to allow the native background service to POST locations to your server
* *
......
...@@ -29,6 +29,7 @@ ...@@ -29,6 +29,7 @@
<source-file src="src/android/BackgroundGeolocationService.java" target-dir="src/com/transistorsoft/cordova/bggeo" /> <source-file src="src/android/BackgroundGeolocationService.java" target-dir="src/com/transistorsoft/cordova/bggeo" />
<source-file src="src/android/ActivityRecognitionService.java" target-dir="src/com/transistorsoft/cordova/bggeo" /> <source-file src="src/android/ActivityRecognitionService.java" target-dir="src/com/transistorsoft/cordova/bggeo" />
<source-file src="src/android/LocationService.java" target-dir="src/com/transistorsoft/cordova/bggeo" /> <source-file src="src/android/LocationService.java" target-dir="src/com/transistorsoft/cordova/bggeo" />
<source-file src="src/android/BootReceiver.java" target-dir="src/com/transistorsoft/cordova/bggeo" />
<!-- For SQLite persistence NOT YET IMPLEMENTED <!-- For SQLite persistence NOT YET IMPLEMENTED
<source-file src="src/android/data/LocationDAO.java" target-dir="src/com/tenforwardconsulting/cordova/bgloc/data" /> <source-file src="src/android/data/LocationDAO.java" target-dir="src/com/tenforwardconsulting/cordova/bgloc/data" />
......
...@@ -15,6 +15,7 @@ import com.transistorsoft.cordova.bggeo.BackgroundGeolocationService.StationaryL ...@@ -15,6 +15,7 @@ import com.transistorsoft.cordova.bggeo.BackgroundGeolocationService.StationaryL
import de.greenrobot.event.EventBus; import de.greenrobot.event.EventBus;
import android.app.Activity; import android.app.Activity;
import android.content.Intent; import android.content.Intent;
import android.content.IntentFilter;
import android.location.Location; import android.location.Location;
import android.util.Log; import android.util.Log;
...@@ -48,7 +49,7 @@ public class BackgroundGeolocationPlugin extends CordovaPlugin { ...@@ -48,7 +49,7 @@ public class BackgroundGeolocationPlugin extends CordovaPlugin {
@Override @Override
protected void pluginInitialize() { protected void pluginInitialize() {
gWebView = this.webView; gWebView = this.webView;
// Register for events fired by our IntentService "LocationService" // Register for events fired by our IntentService "LocationService"
EventBus.getDefault().register(this); EventBus.getDefault().register(this);
} }
......
...@@ -35,6 +35,7 @@ import android.os.AsyncTask; ...@@ -35,6 +35,7 @@ import android.os.AsyncTask;
import android.os.Build; import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.os.IBinder; import android.os.IBinder;
import android.provider.Settings.Secure;
import android.util.Log; import android.util.Log;
public class BackgroundGeolocationService extends Service implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener { public class BackgroundGeolocationService extends Service implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener {
...@@ -101,7 +102,7 @@ public class BackgroundGeolocationService extends Service implements GoogleApiCl ...@@ -101,7 +102,7 @@ public class BackgroundGeolocationService extends Service implements GoogleApiCl
/** /**
* @config {JSONObject} headers For sending location to your server * @config {JSONObject} headers For sending location to your server
*/ */
private JSONObject headers = null; private JSONObject headers = new JSONObject();
// Flags // Flags
private Boolean isEnabled = false; private Boolean isEnabled = false;
...@@ -150,6 +151,7 @@ public class BackgroundGeolocationService extends Service implements GoogleApiCl ...@@ -150,6 +151,7 @@ public class BackgroundGeolocationService extends Service implements GoogleApiCl
Log.i(TAG, " locationUpdateInterval: " + locationUpdateInterval); Log.i(TAG, " locationUpdateInterval: " + locationUpdateInterval);
Log.i(TAG, " activityRecognitionInterval: " + activityRecognitionInterval); Log.i(TAG, " activityRecognitionInterval: " + activityRecognitionInterval);
Log.i(TAG, " stopTimeout: " + stopTimeout); Log.i(TAG, " stopTimeout: " + stopTimeout);
Log.i(TAG, " stopOnTerminate: " + stopOnTerminate);
Log.i(TAG, " forceReload: " + forceReload); Log.i(TAG, " forceReload: " + forceReload);
Log.i(TAG, "----------------------------------------"); Log.i(TAG, "----------------------------------------");
...@@ -158,13 +160,6 @@ public class BackgroundGeolocationService extends Service implements GoogleApiCl ...@@ -158,13 +160,6 @@ public class BackgroundGeolocationService extends Service implements GoogleApiCl
toneGenerator = new ToneGenerator(AudioManager.STREAM_NOTIFICATION, 100); toneGenerator = new ToneGenerator(AudioManager.STREAM_NOTIFICATION, 100);
} }
// Configure FusedLocationProvider
locationRequest = LocationRequest.create()
.setPriority(translateDesiredAccuracy(desiredAccuracy))
.setInterval(this.locationUpdateInterval)
.setFastestInterval(30000)
.setSmallestDisplacement(distanceFilter);
// Connect to google-play services. // Connect to google-play services.
if (ConnectionResult.SUCCESS == GooglePlayServicesUtil.isGooglePlayServicesAvailable(this)) { if (ConnectionResult.SUCCESS == GooglePlayServicesUtil.isGooglePlayServicesAvailable(this)) {
Log.i(TAG, "- Connecting to GooglePlayServices..."); Log.i(TAG, "- Connecting to GooglePlayServices...");
...@@ -199,6 +194,13 @@ public class BackgroundGeolocationService extends Service implements GoogleApiCl ...@@ -199,6 +194,13 @@ public class BackgroundGeolocationService extends Service implements GoogleApiCl
public void onConnected(Bundle arg0) { public void onConnected(Bundle arg0) {
Log.i(TAG, "- GooglePlayServices connected"); Log.i(TAG, "- GooglePlayServices connected");
// Configure FusedLocationProvider
locationRequest = LocationRequest.create()
.setPriority(translateDesiredAccuracy(desiredAccuracy))
.setInterval(this.locationUpdateInterval)
.setFastestInterval(30000)
.setSmallestDisplacement(distanceFilter);
Intent arsIntent = new Intent(this, ActivityRecognitionService.class); Intent arsIntent = new Intent(this, ActivityRecognitionService.class);
activityRecognitionPI = PendingIntent.getService(this, 0, arsIntent, PendingIntent.FLAG_UPDATE_CURRENT); activityRecognitionPI = PendingIntent.getService(this, 0, arsIntent, PendingIntent.FLAG_UPDATE_CURRENT);
...@@ -217,12 +219,12 @@ public class BackgroundGeolocationService extends Service implements GoogleApiCl ...@@ -217,12 +219,12 @@ public class BackgroundGeolocationService extends Service implements GoogleApiCl
* @param {PausedEvent} event * @param {PausedEvent} event
*/ */
public void onEventMainThread(PausedEvent event) { public void onEventMainThread(PausedEvent event) {
isPaused = event.isPaused; isPaused = event.isPaused;
if (isPaused) { if (isPaused) {
setPace(isMoving); setPace(isMoving);
} else { } else {
removeLocationUpdates(); removeLocationUpdates();
} }
} }
/** /**
...@@ -231,7 +233,7 @@ public class BackgroundGeolocationService extends Service implements GoogleApiCl ...@@ -231,7 +233,7 @@ public class BackgroundGeolocationService extends Service implements GoogleApiCl
* @param {PaceChangeEvent} event * @param {PaceChangeEvent} event
*/ */
public void onEventMainThread(PaceChangeEvent event) { public void onEventMainThread(PaceChangeEvent event) {
setPace(event.isMoving); setPace(event.isMoving);
} }
/** /**
...@@ -243,6 +245,12 @@ public class BackgroundGeolocationService extends Service implements GoogleApiCl ...@@ -243,6 +245,12 @@ public class BackgroundGeolocationService extends Service implements GoogleApiCl
String probableActivityName = getActivityName(currentActivity.getType()); String probableActivityName = getActivityName(currentActivity.getType());
Log.i(TAG, "- Activity received: " + probableActivityName + ", confidence: " + currentActivity.getConfidence()); Log.i(TAG, "- Activity received: " + probableActivityName + ", confidence: " + currentActivity.getConfidence());
// If configured to stop when user closes app, kill this service.
if (!BackgroundGeolocationPlugin.isActive() && stopOnTerminate) {
stopSelf();
return;
}
boolean wasMoving = isMoving; boolean wasMoving = isMoving;
boolean nowMoving = false; boolean nowMoving = false;
...@@ -408,7 +416,15 @@ public class BackgroundGeolocationService extends Service implements GoogleApiCl ...@@ -408,7 +416,15 @@ public class BackgroundGeolocationService extends Service implements GoogleApiCl
JSONObject data = locationToJson(location); JSONObject data = locationToJson(location);
params.put("location", data); params.put("location", data);
// Append android UUID to params so that server can map the UUID to some user in your database on server.
// If you've configured the plugin to execute on BOOT, there's no way to append your user's auth-token to the params
// since this BackgroundGeolocationService will be running in "headless" mode.
//
// It's up to you to register this UUID with your system. You can fetch this UUID using the
// Cordova Device plugin org.apache.cordova.device http://plugins.cordova.io/#/package/org.apache.cordova.device
params.put("android_id", Secure.getString(this.getContentResolver(), Secure.ANDROID_ID));
Log.i(TAG, "data: " + params.toString()); Log.i(TAG, "data: " + params.toString());
StringEntity se = new StringEntity(params.toString()); StringEntity se = new StringEntity(params.toString());
...@@ -462,7 +478,7 @@ public class BackgroundGeolocationService extends Service implements GoogleApiCl ...@@ -462,7 +478,7 @@ public class BackgroundGeolocationService extends Service implements GoogleApiCl
} }
private void requestLocationUpdates() { private void requestLocationUpdates() {
if (!isPaused || !isEnabled) { return; } // <-- Don't engage GPS when app is in foreground if (!isPaused || !isEnabled) { return; } // <-- Don't engage GPS when app is in foreground
LocationServices.FusedLocationApi.requestLocationUpdates(googleApiClient, locationRequest, locationUpdatePI); LocationServices.FusedLocationApi.requestLocationUpdates(googleApiClient, locationRequest, locationUpdatePI);
} }
...@@ -541,18 +557,18 @@ public class BackgroundGeolocationService extends Service implements GoogleApiCl ...@@ -541,18 +557,18 @@ public class BackgroundGeolocationService extends Service implements GoogleApiCl
} }
public static class PausedEvent { public static class PausedEvent {
public boolean isPaused; public boolean isPaused;
public PausedEvent(boolean paused) { public PausedEvent(boolean paused) {
isPaused = paused; isPaused = paused;
} }
} }
public static class PaceChangeEvent { public static class PaceChangeEvent {
public boolean isMoving; public boolean isMoving;
public PaceChangeEvent(boolean moving) { public PaceChangeEvent(boolean moving) {
isMoving = moving; isMoving = moving;
} }
} }
class StationaryLocation extends Location { class StationaryLocation extends Location {
......
package com.transistorsoft.cordova.bggeo; package com.transistorsoft.cordova.bggeo;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GooglePlayServicesUtil;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.location.ActivityRecognition;
import android.app.PendingIntent;
import android.content.BroadcastReceiver; import android.content.BroadcastReceiver;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.os.Bundle;
import android.util.Log; import android.util.Log;
/** /**
* This boot receiver is meant to handle the case where device is first booted after power up. This will initiate * This boot receiver is meant to handle the case where device is first booted after power up.
* Google Play's ActivityRecognition API, whose events will be sent to BackgroundGeolocationService as usual. * This boot the headless BackgroundGeolocationService as configured by this class.
* @author chris * @author chris scott
* *
*/ */
public class BootReceiver extends BroadcastReceiver implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener { public class BootReceiver extends BroadcastReceiver {
private static final String TAG = "BackgroundGeolocation"; private static final String TAG = "BackgroundGeolocation";
private GoogleApiClient googleApiClient; /**
private PendingIntent locationUpdateService; * Background Geolocation Configuration params.
* If you're auto-running the service on BOOT, you need to manually configure the params here since the foreground app will not have been booted.
*/
private float distanceFilter = 50;
private Integer desiredAccuracy = 0;
private Integer locationUpdateInterval = 5000;
private Integer activityRecognitionInterval = 10000; private Integer activityRecognitionInterval = 10000;
private long stopTimeout = 0;
private PendingResult pendingResult; private boolean debug = true;
private boolean stopOnTerminate = false;
private boolean forceReload = false;
private String url = "http://posttestserver.com/post.php?dir=cordova-background-geolocation";
private String params = "{'foo':'bar'}";
private String headers = "{'X-FOO':'BAR'}";
@Override @Override
public void onReceive(Context context, Intent intent) { public void onReceive(Context context, Intent intent) {
Log.i(TAG, "- BootReceiver auto-running ActivityRecognition system"); Log.i(TAG, "- BootReceiver booting service");
// GoogleApiClient connection is asynchronous process. @see #onConnected Intent backgroundServiceIntent = new Intent(context, BackgroundGeolocationService.class);
pendingResult = goAsync();
// Connect to google-play services. // Configure background geolocation service params.
if (ConnectionResult.SUCCESS == GooglePlayServicesUtil.isGooglePlayServicesAvailable(context)) { backgroundServiceIntent.putExtra("distanceFilter", distanceFilter);
Log.i(TAG, "- Connecting to GooglePlayServices..."); backgroundServiceIntent.putExtra("desiredAccuracy", desiredAccuracy);
backgroundServiceIntent.putExtra("locationUpdateInterval", locationUpdateInterval);
googleApiClient = new GoogleApiClient.Builder(context) backgroundServiceIntent.putExtra("activityRecognitionInterval", activityRecognitionInterval);
.addApi(ActivityRecognition.API) backgroundServiceIntent.putExtra("stopTimeout", stopTimeout);
.addConnectionCallbacks(this) backgroundServiceIntent.putExtra("debug", debug);
.addOnConnectionFailedListener(this) backgroundServiceIntent.putExtra("stopOnTerminate", stopOnTerminate);
.build(); backgroundServiceIntent.putExtra("forceReload", forceReload);
backgroundServiceIntent.putExtra("url", url);
googleApiClient.connect(); backgroundServiceIntent.putExtra("params", params);
} else { backgroundServiceIntent.putExtra("headers", headers);
Log.e(TAG, "- GooglePlayServices unavailable");
}
// This is the IntentService we'll provide to google-play API. // Start the service.
locationUpdateService = PendingIntent.getService(context, 0, new Intent(context, LocationService.class), PendingIntent.FLAG_UPDATE_CURRENT); context.startService(backgroundServiceIntent);
}
private void requestActivityUpdates() {
ActivityRecognition.ActivityRecognitionApi.requestActivityUpdates(googleApiClient, activityRecognitionInterval, locationUpdateService);
} }
@Override
public void onConnectionFailed(ConnectionResult arg0) {
// TODO Auto-generated method stub
pendingResult.finish();
}
@Override
public void onConnected(Bundle arg0) {
requestActivityUpdates();
pendingResult.finish();
}
@Override
public void onConnectionSuspended(int arg0) {
// TODO Auto-generated method stub
pendingResult.finish();
}
} }
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment