Commit df6233b5 authored by Chris Scott's avatar Chris Scott

Merge pull request #44 from christocracy/expand_persistence_layer

Expand persistence layer
parents 6a205782 ed241710
......@@ -22,7 +22,10 @@ The plugin creates the object `window.plugins.backgroundGeoLocation` with the me
`onStationary(callback, fail)`
`getLocations(callback, fail)`
`sync(callback, fail)`
## Installing the plugin ##
```
......@@ -78,18 +81,20 @@ function onDeviceReady() {
distanceFilter: 50,
disableElasticity: false, // <-- [iOS] Default is 'false'. Set true to disable speed-based distanceFilter elasticity
locationUpdateInterval: 5000,
minimumActivityRecognitionConfidence: 80, // percentage
minimumActivityRecognitionConfidence: 80, // 0-100%. Minimum activity-confidence for a state-change
fastestLocationUpdateInterval: 5000,
activityRecognitionInterval: 10000,
stopTimeout: 0,
forceReload: true, // <-- [Android] If the user closes the app **while location-tracking is started** , reboot app (WARNING: possibly distruptive to user)
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
*/
,url: 'http://posttestserver.com/post.php?dir=cordova-background-geolocation',
url: 'http://posttestserver.com/post.php?dir=cordova-background-geolocation',
batchSync: false, // <-- [Default: false] Set true to sync locations to server in 1 HTTP request.
autoSync: true, // <-- [Default: true] Set true to sync each location to server as it arrives.
maxDaysToPersist: 1, // <-- Maximum days to persist a location in plugin's SQLite database when HTTP fails
headers: {
"X-FOO": "bar"
......@@ -225,6 +230,29 @@ bgGeo.onStationary(function(location) {
});
```
#####`getLocations(callbackFn, failureFn)`
Fetch all the locations currently stored in native plugin's SQLite database. Your ```callbackFn`` will receive an ```Array``` of locations in the 1st parameter. Eg:
```
bgGeo.getLocations(function(locations) {
console.log("locations: ", locations);
});
```
#####`sync(callbackFn, failureFn)`
If the plugin is configured for HTTP with an ```#url``` and ```#autoSync: false```, this method will initiate POSTing the locations currently stored in the native SQLite database to your configured ```#url```. All records in the database will be DELETED. If you configured ```batchSync: true```, all the locations will be sent to your server in a single HTTP POST request, otherwise the plugin will create execute an HTTP post for **each** location in the database (REST-style). Your ```callbackFn``` will be executed and provided with an Array of all the locations from the SQLite database. If the plugin failed to sync to your server (possibly because of no network connection), the ```failureFn``` will be called with an ```errorMessage```. If you are **not** using the HTTP features, ```sync``` is the only way to clear the native SQLite datbase. Eg:
```
bgGeo.sync(function(locations) {
// Here are all the locations from the database. The database is now EMPTY.
console.log('synced locations: ', locations);
}, function(errorMessage) {
console.warn('Sync FAILURE: ', errorMessage);
});
```
## Config
Use the following config-parameters with the #configure method:
......@@ -299,6 +327,14 @@ The plugin can optionally auto-stop monitoring location when some number of minu
By configuring an ```#url```, the plugin will always attempt to HTTP POST the location to your server.
#####`@param {String} batchSync [false]`
Default is ```false```. If you've enabled HTTP feature by configuring an ```#url```, ```batchSync: true``` will POST all the locations currently stored in native SQLite datbase to your server in a single HTTP POST request. With ```batchSync: false```, an HTTP POST request will be initiated for **each** location in database.
#####`@param {String} autoSync [true]`
Default is ```true```. If you've enabeld HTTP feature by configuring an ```#url```, the plugin will attempt to HTTP POST each location to your server as it is recorded. If you set ```autoSync: false```, it's up to you to manually execute the ```#sync``` method to initate the HTTP POST (**NOTE** The plugin will continue to persist **every** recorded location in the SQLite database until you execute ```#sync```).
#####`@param {Object} params`
Optional HTTP params sent along in HTTP request to above ```#url```.
......
......@@ -191,18 +191,20 @@ var app = {
distanceFilter: 50,
disableElasticity: false, // <-- [iOS] Default is 'false'. Set true to disable speed-based distanceFilter elasticity
locationUpdateInterval: 5000,
minimumActivityRecognitionConfidence: 80, // percentage
minimumActivityRecognitionConfidence: 80, // 0-100%. Minimum activity-confidence for a state-change
fastestLocationUpdateInterval: 5000,
activityRecognitionInterval: 10000,
stopTimeout: 0,
forceReload: true, // <-- [Android] If the user closes the app **while location-tracking is started** , reboot app (WARNING: possibly distruptive to user)
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
*/
,url: 'http://posttestserver.com/post.php?dir=cordova-background-geolocation',
url: 'http://posttestserver.com/post.php?dir=cordova-background-geolocation',
batchSync: false, // <-- [Default: false] Set true to sync locations to server in a single HTTP request.
autoSync: true, // <-- [Default: true] Set true to sync each location to server as it arrives.
maxDaysToPersist: 1, // <-- Maximum days to persist a location in plugin's SQLite database when HTTP fails
headers: {
"X-FOO": "bar"
......
......@@ -7,7 +7,7 @@ import org.apache.cordova.PluginResult;
import org.json.JSONArray;
import org.json.JSONObject;
import org.json.JSONException;
import android.os.Bundle;
import com.google.android.gms.location.ActivityRecognitionResult;
import com.google.android.gms.location.DetectedActivity;
import com.transistorsoft.locationmanager.BackgroundGeolocationService;
......@@ -27,13 +27,14 @@ public class CDVBackgroundGeolocation extends CordovaPlugin {
private static CordovaWebView gWebView;
public static Boolean forceReload = false;
public static final String ACTION_START = "start";
public static final String ACTION_STOP = "stop";
public static final String ACTION_START = "start";
public static final String ACTION_STOP = "stop";
public static final String ACTION_ON_PACE_CHANGE = "onPaceChange";
public static final String ACTION_CONFIGURE = "configure";
public static final String ACTION_SET_CONFIG = "setConfig";
public static final String ACTION_CONFIGURE = "configure";
public static final String ACTION_SET_CONFIG = "setConfig";
public static final String ACTION_ON_STATIONARY = "addStationaryRegionListener";
public static final String ACTION_GET_LOCATIONS = "getLocations";
public static final String ACTION_SYNC = "sync";
private Boolean isEnabled = false;
private Boolean stopOnTerminate = false;
......@@ -47,7 +48,11 @@ public class CDVBackgroundGeolocation extends CordovaPlugin {
private CallbackContext locationCallback;
// Called when DetectedActivity is STILL
private CallbackContext stationaryCallback;
private CallbackContext getLocationsCallback;
private CallbackContext syncCallback;
public static boolean isActive() {
return gWebView != null;
}
......@@ -105,6 +110,20 @@ public class CDVBackgroundGeolocation extends CordovaPlugin {
} else if (ACTION_ON_STATIONARY.equalsIgnoreCase(action)) {
result = true;
this.stationaryCallback = callbackContext;
} else if (ACTION_GET_LOCATIONS.equalsIgnoreCase(action)) {
result = true;
Bundle event = new Bundle();
event.putString("name", action);
event.putBoolean("request", true);
getLocationsCallback = callbackContext;
EventBus.getDefault().post(event);
} else if (ACTION_SYNC.equalsIgnoreCase(action)) {
result = true;
Bundle event = new Bundle();
event.putString("name", action);
event.putBoolean("request", true);
syncCallback = callbackContext;
EventBus.getDefault().post(event);
}
return result;
}
......@@ -153,6 +172,9 @@ public class CDVBackgroundGeolocation extends CordovaPlugin {
if (config.has("activityRecognitionInterval")) {
editor.putLong("activityRecognitionInterval", config.getLong("activityRecognitionInterval"));
}
if (config.has("minimumActivityRecognitionConfidence")) {
editor.putInt("minimumActivityRecognitionConfidence", config.getInt("minimumActivityRecognitionConfidence"));
}
if (config.has("stopTimeout")) {
editor.putLong("stopTimeout", config.getLong("stopTimeout"));
}
......@@ -174,6 +196,12 @@ public class CDVBackgroundGeolocation extends CordovaPlugin {
if (config.has("url")) {
editor.putString("url", config.getString("url"));
}
if (config.has("autoSync")) {
editor.putBoolean("autoSync", config.getBoolean("autoSync"));
}
if (config.has("batchSync")) {
editor.putBoolean("batchSync", config.getBoolean("batchSync"));
}
if (config.has("params")) {
try {
editor.putString("params", config.getJSONObject("params").toString());
......@@ -209,6 +237,43 @@ public class CDVBackgroundGeolocation extends CordovaPlugin {
}
}
/**
* EventBus listener for Event Bundle
* @param {Bundle} event
*/
public void onEventMainThread(Bundle event) {
if (event.containsKey("request")) {
return;
}
String name = event.getString("name");
if (ACTION_GET_LOCATIONS.equalsIgnoreCase(name)) {
try {
JSONArray json = new JSONArray(event.getString("data"));
PluginResult result = new PluginResult(PluginResult.Status.OK, json);
runInBackground(getLocationsCallback, result);
} catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} else if (ACTION_SYNC.equalsIgnoreCase(name)) {
Boolean success = event.getBoolean("success");
if (success) {
try {
JSONArray json = new JSONArray(event.getString("data"));
PluginResult result = new PluginResult(PluginResult.Status.OK, json);
runInBackground(syncCallback, result);
} catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} else {
PluginResult result = new PluginResult(PluginResult.Status.IO_EXCEPTION, event.getString("message"));
runInBackground(syncCallback, result);
}
}
}
/**
* EventBus listener for ARS
* @param {ActivityRecognitionResult} result
......@@ -217,7 +282,6 @@ public class CDVBackgroundGeolocation extends CordovaPlugin {
currentActivity = result.getMostProbableActivity();
String activityName = BackgroundGeolocationService.getActivityName(currentActivity.getType());
int confidence = currentActivity.getConfidence();
Log.i(TAG, "----------- currentActivity: " + currentActivity);
}
/**
* EventBus listener
......
......@@ -20,6 +20,7 @@
- (void) setConfig:(CDVInvokedUrlCommand*)command;
- (void) addStationaryRegionListener:(CDVInvokedUrlCommand*)command;
- (void) getStationaryLocation:(CDVInvokedUrlCommand *)command;
- (void) getLocations:(CDVInvokedUrlCommand *)command;
- (void) sync:(CDVInvokedUrlCommand *)command;
@end
......@@ -111,10 +111,36 @@
NSDictionary* location = [bgGeo getStationaryLocation];
CDVPluginResult *result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:location];
result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
[self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
}
/**
* Fetches current stationaryLocation
*/
- (void) getLocations:(CDVInvokedUrlCommand *)command
{
NSArray* locations = [bgGeo getLocations];
CDVPluginResult *result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsArray:locations];
[self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
}
/**
* Fetches current stationaryLocation
*/
- (void) sync:(CDVInvokedUrlCommand *)command
{
NSArray* locations = [bgGeo sync];
if (locations != nil) {
CDVPluginResult *result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsArray:locations];
[self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
} else {
CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:@"Failed to sync to server. Is there a network connection?"];
[self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
}
}
- (void) addStationaryRegionListener:(CDVInvokedUrlCommand*)command
{
if (self.stationaryRegionListeners == nil) {
......
......@@ -4,13 +4,12 @@
@interface TSLocationManager : NSObject <CLLocationManagerDelegate>
@property (strong, nonatomic) NSString *databasePath;
@property (nonatomic) sqlite3 *db;
- (void) configure:(NSDictionary*)config;
- (void) start;
- (void) stop;
- (void) finish;
- (NSArray*) sync;
- (NSArray*) getLocations;
- (void) stopBackgroundTask;
- (void) onPaceChange:(BOOL)value;
- (void) setConfig:(NSDictionary*)command;
......@@ -19,7 +18,6 @@
- (void) onResume:(NSNotification *)notification;
- (void) onAppTerminate;
- (BOOL) isEnabled;
- (NSMutableDictionary*) locationToDictionary:(CLLocation*)location;
- (NSDictionary*) locationToDictionary:(CLLocation*)location;
@end
......@@ -6,7 +6,7 @@
<dict>
<key>Headers/TSLocationManager.h</key>
<data>
IDPizw9H7yMm0B/vqQy2MaU2iCs=
uXAE4LxGF9ekkpUCvnRgTZf4wvM=
</data>
<key>Info.plist</key>
<data>
......@@ -21,7 +21,7 @@
<dict>
<key>Headers/TSLocationManager.h</key>
<data>
IDPizw9H7yMm0B/vqQy2MaU2iCs=
uXAE4LxGF9ekkpUCvnRgTZf4wvM=
</data>
<key>Modules/module.modulemap</key>
<data>
......
......@@ -105,6 +105,47 @@ module.exports = {
'addStationaryRegionListener',
[]);
},
getLocations: function(success, failure) {
if (typeof(success) !== 'function') {
throw "BackgroundGeolocation#getLocations requires a success callback";
}
var me = this;
var mySuccess = function(locations) {
success.call(this, me._setTimestamp(locations));
}
exec(mySuccess,
failure || function() {},
'BackgroundGeoLocation',
'getLocations',
[]);
},
/**
* Signal native plugin to sync locations queue to HTTP
*/
sync: function(success, failure) {
if (typeof(success) !== 'function') {
throw "BackgroundGeolocation#sync requires a success callback";
}
var me = this;
var mySuccess = function(locations) {
success.call(this, me._setTimestamp(locations));
}
exec(mySuccess,
failure || function() {},
'BackgroundGeoLocation',
'sync',
[]);
},
_setTimestamp: function(rs) {
// Transform timestamp to Date instance.
if (typeof(rs) === 'object') {
for (var n=0,len=rs.length;n<len;n++) {
rs[n].timestamp = new Date(rs[n].timestamp);
}
}
return rs;
},
apply: function(destination, source) {
source = source || {};
for (var property in source) {
......
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