Commit 185020db authored by Chris Scott's avatar Chris Scott

Implement Android SharedPreferences for configuration. This allows much...

Implement Android SharedPreferences for configuration.  This allows much simpler configuration of auto-starting the device at device BOOT, since the BootReceiver can now read its configuration from SharedPreferences.  This also allows the plugin to always add the android permission BOOT_COMPLETED and teh state of @config startOnBoot will determine whether to start the service.  Better error-handling on params/headers config.  Tip-of-the-hat to Nicholas Felmlee for all this
parent 8ef8ff1d
...@@ -179,8 +179,9 @@ var app = { ...@@ -179,8 +179,9 @@ var app = {
locationUpdateInterval: 5000, locationUpdateInterval: 5000,
activityRecognitionInterval: 10000, activityRecognitionInterval: 10000,
stopTimeout: 0, stopTimeout: 0,
forceReload: true, // <-- If the user closes the app **while location-tracking is started** , reboot app (WARNING: possibly distruptive to user) forceReload: true, // <-- [Android] If the user closes the app **while location-tracking is started** , reboot app (WARNING: possibly distruptive to user)
stopOnTerminate: false, // <-- Allow the background-service to run headless when user closes the app. stopOnTerminate: false, // <-- [Android] Allow the background-service to run headless when user closes the app.
startOnBoot: true, // <-- [Android] Auto start background-service in headless mode when device is powered-up.
activityType: 'AutomotiveNavigation' 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
......
...@@ -58,9 +58,7 @@ ...@@ -58,9 +58,7 @@
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WAKE_LOCK" /> <uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="com.google.android.gms.permission.ACTIVITY_RECOGNITION" /> <uses-permission android:name="com.google.android.gms.permission.ACTIVITY_RECOGNITION" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<!-- Autorun your app on device BOOT. UNCOMMENT TO ENABLE AUTO-BOOT -->
<!-- <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /> -->
</config-file> </config-file>
<config-file target="res/xml/config.xml" parent="/*"> <config-file target="res/xml/config.xml" parent="/*">
......
...@@ -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.SharedPreferences;
import android.location.Location; import android.location.Location;
import android.util.Log; import android.util.Log;
...@@ -93,11 +94,9 @@ public class BackgroundGeolocationPlugin extends CordovaPlugin { ...@@ -93,11 +94,9 @@ public class BackgroundGeolocationPlugin extends CordovaPlugin {
callbackContext.success(); callbackContext.success();
} }
} else if (ACTION_SET_CONFIG.equalsIgnoreCase(action)) { } else if (ACTION_SET_CONFIG.equalsIgnoreCase(action)) {
activity.stopService(backgroundServiceIntent);
result = applyConfig(data); result = applyConfig(data);
// TODO reconfigure Service // TODO reconfigure Service
if (result) { if (result) {
activity.startService(backgroundServiceIntent);
callbackContext.success(); callbackContext.success();
} else { } else {
callbackContext.error("- Configuration error!"); callbackContext.error("- Configuration error!");
...@@ -110,47 +109,65 @@ public class BackgroundGeolocationPlugin extends CordovaPlugin { ...@@ -110,47 +109,65 @@ public class BackgroundGeolocationPlugin extends CordovaPlugin {
} }
private boolean applyConfig(JSONArray data) { private boolean applyConfig(JSONArray data) {
Activity activity = this.cordova.getActivity();
try { try {
JSONObject config = data.getJSONObject(0); JSONObject config = data.getJSONObject(0);
Log.i(TAG, "- configure: " + config.toString()); Log.i(TAG, "- configure: " + config.toString());
backgroundServiceIntent.putExtra("isMoving", isMoving); SharedPreferences settings = activity.getSharedPreferences(TAG, 0);
SharedPreferences.Editor editor = settings.edit();
editor.putBoolean("isMoving", isMoving);
if (config.has("distanceFilter")) { if (config.has("distanceFilter")) {
backgroundServiceIntent.putExtra("distanceFilter", (float) config.getInt("distanceFilter")); editor.putFloat("distanceFilter", config.getInt("distanceFilter"));
} }
if (config.has("desiredAccuracy")) { if (config.has("desiredAccuracy")) {
backgroundServiceIntent.putExtra("desiredAccuracy", config.getInt("desiredAccuracy")); editor.putInt("desiredAccuracy", config.getInt("desiredAccuracy"));
} }
if (config.has("locationUpdateInterval")) { if (config.has("locationUpdateInterval")) {
backgroundServiceIntent.putExtra("locationUpdateInterval", config.getInt("locationUpdateInterval")); editor.putInt("locationUpdateInterval", config.getInt("locationUpdateInterval"));
} }
if (config.has("activityRecognitionInterval")) { if (config.has("activityRecognitionInterval")) {
backgroundServiceIntent.putExtra("activityRecognitionInterval", config.getInt("activityRecognitionInterval")); editor.putInt("activityRecognitionInterval", config.getInt("activityRecognitionInterval"));
} }
if (config.has("stopTimeout")) { if (config.has("stopTimeout")) {
backgroundServiceIntent.putExtra("stopTimeout", config.getLong("stopTimeout")); editor.putLong("stopTimeout", config.getLong("stopTimeout"));
} }
if (config.has("debug")) { if (config.has("debug")) {
backgroundServiceIntent.putExtra("debug", config.getBoolean("debug")); editor.putBoolean("debug", config.getBoolean("debug"));
} }
if (config.has("stopOnTerminate")) { if (config.has("stopOnTerminate")) {
stopOnTerminate = config.getBoolean("stopOnTerminate"); editor.putBoolean("stopOnTerminate", config.getBoolean("stopOnTerminate"));
backgroundServiceIntent.putExtra("stopOnTerminate", config.getBoolean("stopOnTerminate")); }
if (config.has("startOnBoot")) {
editor.putBoolean("startOnBoot", config.getBoolean("startOnBoot"));
} }
if (config.has("forceReload")) { if (config.has("forceReload")) {
backgroundServiceIntent.putExtra("forceReload", config.getBoolean("forceReload")); editor.putBoolean("forceReload", config.getBoolean("forceReload"));
} }
if (config.has("url")) { if (config.has("url")) {
backgroundServiceIntent.putExtra("url", config.getString("url")); editor.putString("url", config.getString("url"));
} }
if (config.has("params")) { if (config.has("params")) {
backgroundServiceIntent.putExtra("params", config.getString("params")); try {
editor.putString("params", config.getJSONObject("params").toString());
} catch (JSONException e) {
Log.w(TAG, "- Failed to parse #params to JSONObject. Ignored");
}
} }
if (config.has("headers")) { if (config.has("headers")) {
backgroundServiceIntent.putExtra("headers", config.getString("headers")); try {
editor.putString("headers", config.getJSONObject("headers").toString());
} catch (JSONException e) {
Log.w(TAG, "- Failed to parse #headers to JSONObject. Ignored");
}
} }
editor.commit();
return true; return true;
} catch (JSONException e) { } catch (JSONException e) {
Log.w(TAG, e);
return false; return false;
} }
} }
......
...@@ -25,6 +25,7 @@ import android.app.PendingIntent; ...@@ -25,6 +25,7 @@ import android.app.PendingIntent;
import android.app.Service; import android.app.Service;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.location.Location; import android.location.Location;
import android.media.AudioManager; import android.media.AudioManager;
...@@ -58,19 +59,19 @@ public class BackgroundGeolocationService extends Service implements GoogleApiCl ...@@ -58,19 +59,19 @@ public class BackgroundGeolocationService extends Service implements GoogleApiCl
/** /**
* @config {Integer} desiredAccuracy * @config {Integer} desiredAccuracy
*/ */
private Integer desiredAccuracy = 10; private Integer desiredAccuracy = 10;
/** /**
* @config {Float} distanceFilter * @config {Float} distanceFilter
*/ */
private Float distanceFilter = (float) 50; private Float distanceFilter = 50f;
/** /**
* @config {Boolean} isDebugging * @config {Boolean} isDebugging
*/ */
private Boolean isDebugging = false; private Boolean isDebugging = false;
/** /**
* @config {Boolean} stopOnTerminate * @config {Boolean} stopOnTerminate
*/ */
private Boolean stopOnTerminate = false; private Boolean stopOnTerminate = false;
// Android-only config // Android-only config
/** /**
...@@ -92,21 +93,21 @@ public class BackgroundGeolocationService extends Service implements GoogleApiCl ...@@ -92,21 +93,21 @@ public class BackgroundGeolocationService extends Service implements GoogleApiCl
/** /**
* @config {Integer} stopTimeout The time to wait after ARS STILL to turn of GPS * @config {Integer} stopTimeout The time to wait after ARS STILL to turn of GPS
*/ */
private long stopTimeout = 0; private long stopTimeout = 0;
// HTTP config // HTTP config
/** /**
* @config {String} url For sending location to your server * @config {String} url For sending location to your server
*/ */
private String url = null; private String url = null;
/** /**
* @config {JSONObject} params For sending location to your server * @config {JSONObject} params For sending location to your server
*/ */
private JSONObject params = new JSONObject(); private JSONObject params = new JSONObject();
/** /**
* @config {JSONObject} headers For sending location to your server * @config {JSONObject} headers For sending location to your server
*/ */
private JSONObject headers = new JSONObject(); private JSONObject headers = new JSONObject();
// Flags // Flags
private Boolean isEnabled = false; private Boolean isEnabled = false;
...@@ -123,31 +124,38 @@ public class BackgroundGeolocationService extends Service implements GoogleApiCl ...@@ -123,31 +124,38 @@ public class BackgroundGeolocationService extends Service implements GoogleApiCl
instance = this; instance = this;
EventBus.getDefault().register(this); EventBus.getDefault().register(this);
// Load config settings
SharedPreferences settings = getSharedPreferences(TAG, 0);
isEnabled = true; isEnabled = true;
stopOnTerminate = intent.getBooleanExtra("stopOnTerminate", true);
isDebugging = intent.getBooleanExtra("debug", false); isDebugging = settings.getBoolean("debug", false);
distanceFilter = intent.getFloatExtra("distanceFilter", 50); distanceFilter = settings.getFloat("distanceFilter", 50);
desiredAccuracy = intent.getIntExtra("desiredAccuracy", 10); desiredAccuracy = settings.getInt("desiredAccuracy", 10);
locationUpdateInterval = intent.getIntExtra("locationUpdateInterval", 30000); locationUpdateInterval = settings.getInt("locationUpdateInterval", 30000);
activityRecognitionInterval = intent.getIntExtra("activityRecognitionInterval", 60000); activityRecognitionInterval = settings.getInt("activityRecognitionInterval", 60000);
stopTimeout = intent.getLongExtra("stopTimeout", 0); stopTimeout = settings.getLong("stopTimeout", 0);
forceReload = intent.getBooleanExtra("forceReload", false); stopOnTerminate = settings.getBoolean("stopOnTerminate", true);
isMoving = intent.getBooleanExtra("isMoving", false); forceReload = settings.getBoolean("forceReload", false);
isMoving = settings.getBoolean("isMoving", false);
// HTTP Configuration // HTTP Configuration
url = intent.getStringExtra("url"); url = settings.getString("url", null);
try { if (settings.contains("params")) {
if (intent.hasExtra("params")) { try {
params = new JSONObject(intent.getStringExtra("params")); params = new JSONObject(settings.getString("params", "{}"));
} catch (JSONException e) {
Log.w(TAG, "- Faile to parse #params to JSONObject");
} }
if (intent.hasExtra("headers")) { }
headers = new JSONObject(intent.getStringExtra("headers")); if (settings.contains("headers")) {
try {
headers = new JSONObject(settings.getString("headers", "{}"));
} catch (JSONException e) {
Log.w(TAG, "- Failed to parse #headers to JSONObject");
} }
} catch (JSONException e) {
e.printStackTrace();
} }
Log.i(TAG, "----------------------------------------"); Log.i(TAG, "----------------------------------------");
Log.i(TAG, "- Start BackgroundGeolocationService"); Log.i(TAG, "- Start BackgroundGeolocationService");
Log.i(TAG, " debug: " + isDebugging); Log.i(TAG, " debug: " + isDebugging);
...@@ -159,6 +167,7 @@ public class BackgroundGeolocationService extends Service implements GoogleApiCl ...@@ -159,6 +167,7 @@ public class BackgroundGeolocationService extends Service implements GoogleApiCl
Log.i(TAG, " stopOnTerminate: " + stopOnTerminate); Log.i(TAG, " stopOnTerminate: " + stopOnTerminate);
Log.i(TAG, " forceReload: " + forceReload); Log.i(TAG, " forceReload: " + forceReload);
Log.i(TAG, " isMoving: " + isMoving); Log.i(TAG, " isMoving: " + isMoving);
Log.i(TAG, "----------------------------------------"); Log.i(TAG, "----------------------------------------");
// For debug sounds, turn on ToneGenerator. // For debug sounds, turn on ToneGenerator.
...@@ -182,7 +191,7 @@ public class BackgroundGeolocationService extends Service implements GoogleApiCl ...@@ -182,7 +191,7 @@ public class BackgroundGeolocationService extends Service implements GoogleApiCl
Log.e(TAG, "- GooglePlayServices unavailable"); Log.e(TAG, "- GooglePlayServices unavailable");
} }
return Service.START_REDELIVER_INTENT; return Service.START_STICKY;
} }
@Override @Override
...@@ -370,7 +379,8 @@ public class BackgroundGeolocationService extends Service implements GoogleApiCl ...@@ -370,7 +379,8 @@ public class BackgroundGeolocationService extends Service implements GoogleApiCl
private Integer getLocationUpdateInterval() { private Integer getLocationUpdateInterval() {
// TODO Can add intelligence here based upon currentActivity. // TODO Can add intelligence here based upon currentActivity.
return locationUpdateInterval; SharedPreferences settings = getSharedPreferences(TAG, 0);
return settings.getInt("locationUpdateInterval", locationUpdateInterval);
} }
private Integer getFastestLocationUpdateInterval() { private Integer getFastestLocationUpdateInterval() {
...@@ -499,7 +509,8 @@ public class BackgroundGeolocationService extends Service implements GoogleApiCl ...@@ -499,7 +509,8 @@ public class BackgroundGeolocationService extends Service implements GoogleApiCl
} }
private void requestActivityUpdates() { private void requestActivityUpdates() {
ActivityRecognition.ActivityRecognitionApi.requestActivityUpdates(googleApiClient, activityRecognitionInterval, activityRecognitionPI); SharedPreferences settings = getSharedPreferences(TAG, 0);
ActivityRecognition.ActivityRecognitionApi.requestActivityUpdates(googleApiClient, settings.getInt("activityRecognitionInterval", activityRecognitionInterval), activityRecognitionPI);
} }
private void removeActivityUpdates() { private void removeActivityUpdates() {
...@@ -509,12 +520,14 @@ public class BackgroundGeolocationService extends Service implements GoogleApiCl ...@@ -509,12 +520,14 @@ 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
SharedPreferences settings = getSharedPreferences(TAG, 0);
// Configure LocationRequest // Configure LocationRequest
locationRequest = LocationRequest.create() locationRequest = LocationRequest.create()
.setPriority(translateDesiredAccuracy(desiredAccuracy)) .setPriority(translateDesiredAccuracy(settings.getInt("desiredAccuracy", desiredAccuracy)))
.setInterval(getLocationUpdateInterval()) .setInterval(getLocationUpdateInterval())
.setFastestInterval(getFastestLocationUpdateInterval()) .setFastestInterval(getFastestLocationUpdateInterval())
.setSmallestDisplacement(distanceFilter); .setSmallestDisplacement(settings.getFloat("distanceFilter", distanceFilter));
LocationServices.FusedLocationApi.requestLocationUpdates(googleApiClient, locationRequest, locationUpdatePI); LocationServices.FusedLocationApi.requestLocationUpdates(googleApiClient, locationRequest, locationUpdatePI);
} }
......
...@@ -3,6 +3,7 @@ package com.transistorsoft.cordova.bggeo; ...@@ -3,6 +3,7 @@ package com.transistorsoft.cordova.bggeo;
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.content.SharedPreferences;
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 boot receiver is meant to handle the case where device is first booted after power up.
...@@ -11,45 +12,18 @@ import android.util.Log; ...@@ -11,45 +12,18 @@ import android.util.Log;
* *
*/ */
public class BootReceiver extends BroadcastReceiver { public class BootReceiver extends BroadcastReceiver {
private static final String TAG = "BackgroundGeolocation"; private static final String TAG = "BackgroundGeolocation";
/**
* 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 long stopTimeout = 0;
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 booting service"); SharedPreferences settings = context.getSharedPreferences(TAG, 0);
Intent backgroundServiceIntent = new Intent(context, BackgroundGeolocationService.class);
// Configure background geolocation service params.
backgroundServiceIntent.putExtra("distanceFilter", distanceFilter);
backgroundServiceIntent.putExtra("desiredAccuracy", desiredAccuracy);
backgroundServiceIntent.putExtra("locationUpdateInterval", locationUpdateInterval);
backgroundServiceIntent.putExtra("activityRecognitionInterval", activityRecognitionInterval);
backgroundServiceIntent.putExtra("stopTimeout", stopTimeout);
backgroundServiceIntent.putExtra("debug", debug);
backgroundServiceIntent.putExtra("stopOnTerminate", stopOnTerminate);
backgroundServiceIntent.putExtra("forceReload", forceReload);
backgroundServiceIntent.putExtra("url", url);
backgroundServiceIntent.putExtra("params", params);
backgroundServiceIntent.putExtra("headers", headers);
boolean startOnBoot = settings.getBoolean("startOnBoot", false);
if (!startOnBoot) {
return;
}
Log.i(TAG, "- BootReceiver booting service");
// Start the service. // Start the service.
context.startService(backgroundServiceIntent); context.startService(new Intent(context, BackgroundGeolocationService.class));
} }
} }
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