Commit fbaf7950 authored by Chris Scott's avatar Chris Scott

Implementing geofencing. Refactoring background-task handling

parent b8b92c56
...@@ -22,6 +22,10 @@ The plugin creates the object `window.plugins.backgroundGeoLocation` with the me ...@@ -22,6 +22,10 @@ The plugin creates the object `window.plugins.backgroundGeoLocation` with the me
`onStationary(callback, fail)` `onStationary(callback, fail)`
`addGeofence(callback, fail)`
`onGeofence(callback, fail)`
`getLocations(callback, fail)` `getLocations(callback, fail)`
`sync(callback, fail)` `sync(callback, fail)`
...@@ -49,27 +53,27 @@ The plugin creates the object `window.plugins.backgroundGeoLocation` with the me ...@@ -49,27 +53,27 @@ The plugin creates the object `window.plugins.backgroundGeoLocation` with the me
// As with all Cordova plugins, you must configure within an #deviceready callback. // As with all Cordova plugins, you must configure within an #deviceready callback.
// //
function onDeviceReady() { function onDeviceReady() {
/**
* This would be your own callback for Ajax-requests after POSTing background geolocation to your server.
*/
var yourAjaxCallback = function(response) {
////
// IMPORTANT: You must execute the #finish method here to inform the native plugin that you're finished,
// and the background-task may be completed. You must do this regardless if your HTTP request is successful or not.
// IF YOU DON'T, ios will CRASH YOUR APP for spending too much time in the background.
//
//
bgGeo.finish();
};
/** /**
* This callback will be executed every time a geolocation is recorded in the background. * This callback will be executed every time a geolocation is recorded in the background.
*/ */
var callbackFn = function(location) { var callbackFn = function(location, taskId) {
console.log('[js] BackgroundGeoLocation callback: ' + location.latitude + ',' + location.longitude); console.log('[js] BackgroundGeoLocation callback: ' + location.latitude + ',' + location.longitude);
// Do your HTTP request here to POST location to your server.
// /**
// * This would be your own callback for Ajax-requests after POSTing background geolocation to your server.
* eg:
* $.post({url: url, success: yourAjaxCallback});
*/
var yourAjaxCallback = function(response) {
////
// IMPORTANT: You must execute the #finish, providing the taskId provided to callbackFn above in order to inform the native plugin that you're finished,
// and the background-task may be completed. You must do this regardless if your HTTP request is successful or not.
// IF YOU DON'T, ios will CRASH YOUR APP for spending too much time in the background.
//
//
bgGeo.finish(taskId);
};
yourAjaxCallback.call(this); yourAjaxCallback.call(this);
}; };
...@@ -193,7 +197,11 @@ Keep in mind that it is **not** possible to use ```start()``` at the ```pause``` ...@@ -193,7 +197,11 @@ Keep in mind that it is **not** possible to use ```start()``` at the ```pause```
#####`configure(locationCallback, failureCallback, config)` #####`configure(locationCallback, failureCallback, config)`
Configures the plugin's parameters (@see following [Config](https://github.com/christocracy/cordova-background-geolocation/blob/edge/README.md#config) section for accepted ```config``` params. The ```locationCallback``` will be executed each time a new Geolocation is recorded. Configures the plugin's parameters (@see following [Config](https://github.com/christocracy/cordova-background-geolocation/blob/edge/README.md#config) section for accepted ```config``` params. The ```locationCallback``` will be executed each time a new Geolocation is recorded and provided with the following parameters:
######`@param {Object} location` The Location data
`{"coords":{"latitude":45.5192735,"longitude":-73.6168883,"accuracy":10,"speed":0,"heading":0,"altitude":0},"timestamp":"2015-05-27T18:26:44Z"}`
######`@param {Integer} taskId` The taskId used to send to bgGeo.finish(taskId) in order to signal completion of your callbackFn
#####`setConfig(successFn, failureFn, config)` #####`setConfig(successFn, failureFn, config)`
Reconfigure plugin's configuration (@see followign ##Config## section for accepted ```config``` params. **NOTE** The plugin will continue to send recorded Geolocation to the ```locationCallback``` you provided to ```configure``` method -- use this method only to change configuration params (eg: ```distanceFilter```, ```stationaryRadius```, etc). Reconfigure plugin's configuration (@see followign ##Config## section for accepted ```config``` params. **NOTE** The plugin will continue to send recorded Geolocation to the ```locationCallback``` you provided to ```configure``` method -- use this method only to change configuration params (eg: ```distanceFilter```, ```stationaryRadius```, etc).
...@@ -230,11 +238,54 @@ bgGeo.changePace(false); // <-- Disable aggressive GPS monitoring. Engages stat ...@@ -230,11 +238,54 @@ bgGeo.changePace(false); // <-- Disable aggressive GPS monitoring. Engages stat
``` ```
#####`onStationary(callbackFn, failureFn)` #####`onStationary(callbackFn, failureFn)`
Your ```callbackFn``` will be executed each time the device has entered stationary-monitoring mode. The ```callbackFn``` will be provided with a ```Geolocation``` object as the 1st param, with the usual params (```latitude, longitude, accuracy, speed, bearing, altitude```). Your ```callbackFn``` will be executed each time the device has entered stationary-monitoring mode. The ```callbackFn``` will be provided with a ```Location``` object as the 1st param, with the usual params (```latitude, longitude, accuracy, speed, bearing, altitude```), in addition to a ```taskId``` used to signal that your callback is finished.
``` ```
bgGeo.onStationary(function(location) { bgGeo.onStationary(function(location, taskId) {
console.log('- Device is stopped: ', location.latitude, location.longitude); console.log('- Device is stopped: ', location.latitude, location.longitude);
// The plugin runs your callback in a background-thread:
// you MUST signal to the native plugin when your callback is finished so it can halt the thread.
// IF YOU DON'T, iOS WILL KILL YOUR APP
bgGeo.finish(taskId);
});
```
#####`addGeofence(config, callbackFn, failureFn)`
Adds a geofence to be monitored by the native plugin. Monitoring of a geofence is halted after a crossing occurs. The `config` object accepts the following params.
######`@config {String} identifier The name of your geofence, eg: "Home", "Office"
######`@config {Float} radius The radius (meters) of the geofence. In practice, you should make this >= 100 meters.
######`@config {Float} latitude Latitude of the center-point of the circular geofence.
######`@config {Float} longitude Longitude of the center-point of the circular geofence.
```
bgGeo.addGeofence({
identifier: "Home",
radius: 150,
latitude: 45.51921926,
longitude: -73.61678581
}, function() {
console.log("Successfully added geofence");
}, function(error) {
console.warn("Failed to add geofence", error);
});
```
#####`onGeofence(callbackFn)`
Adds a geofence event-listener. Your supplied callback will be called when any monitored geofence crossing occurs. The `callbackFn` will be provided the following parameters:
######`@param {String} identifier The name of the geofence which generated the crossing event, eg: "Home"
######`@param {Integer} taskId The background taskId which you must send back to the native plugin via `bgGeo.finish(taskId)` in order to signal that your callback is complete.
```
bgGeo.onGeofence(function(identifier, taskId) {
console.log('A geofence has been entered: ' + identifier);
// The plugin runs your callback in a background-thread:
// you MUST signal to the native plugin when your callback is finished so it can halt the thread.
// IF YOU DON'T, iOS WILL KILL YOUR APP
bgGeo.finish(taskId);
}); });
``` ```
......
...@@ -138,26 +138,28 @@ var app = { ...@@ -138,26 +138,28 @@ var app = {
app.onClickHome(); app.onClickHome();
/**
* This would be your own callback for Ajax-requests after POSTing background geolocation to your server.
*/
var yourAjaxCallback = function(response) {
// Very important to call #finish -- it signals to the native plugin that it can destroy the background thread, which your callbackFn is running in.
// IF YOU DON'T, THE OS CAN KILL YOUR APP FOR RUNNING TOO LONG IN THE BACKGROUND
bgGeo.finish();
};
/** /**
* This callback will be executed every time a geolocation is recorded in the background. * This callback will be executed every time a geolocation is recorded in the background.
*/ */
var callbackFn = function(location) { var callbackFn = function(location, taskId) {
console.log('[js] BackgroundGeoLocation callback: ' + JSON.stringify(location)); console.log('[js] BackgroundGeoLocation callback: ' + JSON.stringify(location));
// Update our current-position marker. // Update our current-position marker.
app.setCurrentLocation(location); app.setCurrentLocation(location);
// After you Ajax callback is complete, you MUST signal to the native code, which is running a background-thread, that you're done and it can gracefully kill that thread. // After you Ajax callback is complete, you MUST signal to the native code, which is running a background-thread, that you're done and it can gracefully kill that thread.
yourAjaxCallback.call(this);
/**
* This would be your own callback for Ajax-requests after POSTing background geolocation to your server.
*/
var yourAjaxCallback = function(response) {
// Very important to call #finish -- it signals to the native plugin that it can destroy the background thread, which your callbackFn is running in.
// IF YOU DON'T, THE OS CAN KILL YOUR APP FOR RUNNING TOO LONG IN THE BACKGROUND
bgGeo.finish(taskId);
};
yourAjaxCallback.call(this, {status: 200});
}; };
var failureFn = function(error) { var failureFn = function(error) {
...@@ -165,7 +167,7 @@ var app = { ...@@ -165,7 +167,7 @@ var app = {
}; };
// Only ios emits this stationary event // Only ios emits this stationary event
bgGeo.onStationary(function(location) { bgGeo.onStationary(function(location, taskId) {
console.log('[js] BackgroundGeoLocation onStationary ' + JSON.stringify(location)); console.log('[js] BackgroundGeoLocation onStationary ' + JSON.stringify(location));
app.setCurrentLocation(location); app.setCurrentLocation(location);
...@@ -187,6 +189,8 @@ var app = { ...@@ -187,6 +189,8 @@ var app = {
var center = new google.maps.LatLng(coords.latitude, coords.longitude); var center = new google.maps.LatLng(coords.latitude, coords.longitude);
app.stationaryRadius.setRadius(radius); app.stationaryRadius.setRadius(radius);
app.stationaryRadius.setCenter(center); app.stationaryRadius.setCenter(center);
bgGeo.finish(taskId);
}); });
...@@ -195,22 +199,22 @@ var app = { ...@@ -195,22 +199,22 @@ var app = {
debug: true, // <-- enable this hear sounds for background-geolocation life-cycle. debug: true, // <-- enable this hear sounds for background-geolocation life-cycle.
desiredAccuracy: 0, desiredAccuracy: 0,
stationaryRadius: 50, stationaryRadius: 50,
distanceFilter: 25, distanceFilter: 50,
disableElasticity: false, // <-- [iOS] Default is 'false'. Set true to disable speed-based distanceFilter elasticity disableElasticity: false, // <-- [iOS] Default is 'false'. Set true to disable speed-based distanceFilter elasticity
locationUpdateInterval: 5000, locationUpdateInterval: 5000,
minimumActivityRecognitionConfidence: 80, // 0-100%. Minimum activity-confidence for a state-change minimumActivityRecognitionConfidence: 80, // 0-100%. Minimum activity-confidence for a state-change
fastestLocationUpdateInterval: 5000, fastestLocationUpdateInterval: 5000,
activityRecognitionInterval: 10000, activityRecognitionInterval: 10000,
stopTimeout: 0, stopTimeout: 0,
forceReload: true, // <-- [Android] If the user closes the app **while location-tracking is started** , reboot app (WARNING: possibly distruptive to user) forceReload: false, // <-- [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. stopOnTerminate: true, // <-- [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. startOnBoot: false, // <-- [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
*/ */
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. batchSync: true, // <-- [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. 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 maxDaysToPersist: 1, // <-- Maximum days to persist a location in plugin's SQLite database when HTTP fails
headers: { headers: {
...@@ -221,9 +225,10 @@ var app = { ...@@ -221,9 +225,10 @@ var app = {
} }
}); });
bgGeo.onGeofence(function(identifier) { bgGeo.onGeofence(function(identifier, taskId) {
alert('Enter Geofence: ' + identifier); alert('Enter Geofence: ' + identifier);
console.log('[js] Geofence ENTER: ', identifier); console.log('[js] Geofence ENTER: ', identifier, taskId);
bgGeo.finish(taskId);
}); });
// Add longpress event for adding GeoFence of hard-coded radius 200m. // Add longpress event for adding GeoFence of hard-coded radius 200m.
......
...@@ -30,6 +30,7 @@ ...@@ -30,6 +30,7 @@
<service android:name="com.transistorsoft.locationmanager.BackgroundGeolocationService" /> <service android:name="com.transistorsoft.locationmanager.BackgroundGeolocationService" />
<service android:name="com.transistorsoft.locationmanager.LocationService" /> <service android:name="com.transistorsoft.locationmanager.LocationService" />
<service android:name="com.transistorsoft.locationmanager.ActivityRecognitionService" /> <service android:name="com.transistorsoft.locationmanager.ActivityRecognitionService" />
<service android:name="com.transistorsoft.locationmanager.GeofenceService" />
<receiver android:name="com.transistorsoft.locationmanager.BootReceiver" android:enabled="true" android:exported="false"> <receiver android:name="com.transistorsoft.locationmanager.BootReceiver" android:enabled="true" android:exported="false">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"/> <action android:name="android.intent.action.BOOT_COMPLETED"/>
......
package com.transistorsoft.cordova.bggeo; package com.transistorsoft.cordova.bggeo;
import java.util.List;
import java.util.ArrayList;
import org.apache.cordova.CallbackContext; import org.apache.cordova.CallbackContext;
import org.apache.cordova.CordovaPlugin; import org.apache.cordova.CordovaPlugin;
import org.apache.cordova.CordovaWebView; import org.apache.cordova.CordovaWebView;
...@@ -11,8 +13,8 @@ import android.os.Bundle; ...@@ -11,8 +13,8 @@ import android.os.Bundle;
import com.google.android.gms.location.ActivityRecognitionResult; import com.google.android.gms.location.ActivityRecognitionResult;
import com.google.android.gms.location.DetectedActivity; import com.google.android.gms.location.DetectedActivity;
import com.transistorsoft.locationmanager.BackgroundGeolocationService; import com.transistorsoft.locationmanager.BackgroundGeolocationService;
import com.transistorsoft.locationmanager.BackgroundGeolocationService.PaceChangeEvent; import com.google.android.gms.location.Geofence;
import com.transistorsoft.locationmanager.BackgroundGeolocationService.PausedEvent; import com.google.android.gms.location.GeofencingEvent;
//import com.transistorsoft.locationmanager.BackgroundGeolocationService.StationaryLocation; //import com.transistorsoft.locationmanager.BackgroundGeolocationService.StationaryLocation;
import de.greenrobot.event.EventBus; import de.greenrobot.event.EventBus;
...@@ -37,6 +39,8 @@ public class CDVBackgroundGeolocation extends CordovaPlugin { ...@@ -37,6 +39,8 @@ public class CDVBackgroundGeolocation extends CordovaPlugin {
public static final String ACTION_SYNC = "sync"; public static final String ACTION_SYNC = "sync";
public static final String ACTION_GET_ODOMETER = "getOdometer"; public static final String ACTION_GET_ODOMETER = "getOdometer";
public static final String ACTION_RESET_ODOMETER = "resetOdometer"; public static final String ACTION_RESET_ODOMETER = "resetOdometer";
public static final String ACTION_ADD_GEOFENCE = "addGeofence";
public static final String ACTION_ON_GEOFENCE = "onGeofence";
private Boolean isEnabled = false; private Boolean isEnabled = false;
private Boolean stopOnTerminate = false; private Boolean stopOnTerminate = false;
...@@ -48,16 +52,13 @@ public class CDVBackgroundGeolocation extends CordovaPlugin { ...@@ -48,16 +52,13 @@ public class CDVBackgroundGeolocation extends CordovaPlugin {
// Geolocation callback // Geolocation callback
private CallbackContext locationCallback; private CallbackContext locationCallback;
// Called when DetectedActivity is STILL
private CallbackContext stationaryCallback; private CallbackContext stationaryCallback;
private CallbackContext getLocationsCallback; private CallbackContext getLocationsCallback;
private CallbackContext syncCallback; private CallbackContext syncCallback;
private CallbackContext getOdometerCallback; private CallbackContext getOdometerCallback;
private CallbackContext resetOdometerCallback; private CallbackContext resetOdometerCallback;
private CallbackContext paceChangeCallback;
private List<CallbackContext> geofenceCallbacks = new ArrayList<CallbackContext>();
public static boolean isActive() { public static boolean isActive() {
return gWebView != null; return gWebView != null;
...@@ -98,12 +99,14 @@ public class CDVBackgroundGeolocation extends CordovaPlugin { ...@@ -98,12 +99,14 @@ public class CDVBackgroundGeolocation extends CordovaPlugin {
Log.w(TAG, "- Cannot change pace while disabled"); Log.w(TAG, "- Cannot change pace while disabled");
result = false; result = false;
callbackContext.error("Cannot #changePace while disabled"); callbackContext.error("Cannot #changePace while disabled");
} else { } else {
PaceChangeEvent event = new PaceChangeEvent(data.getBoolean(0));
EventBus.getDefault().post(event);
result = true; result = true;
callbackContext.success(); paceChangeCallback = callbackContext;
Bundle event = new Bundle();
event.putString("name", action);
event.putBoolean("request", true);
event.putBoolean("isMoving", data.getBoolean(0));
EventBus.getDefault().post(event);
} }
} else if (ACTION_SET_CONFIG.equalsIgnoreCase(action)) { } else if (ACTION_SET_CONFIG.equalsIgnoreCase(action)) {
result = applyConfig(data); result = applyConfig(data);
...@@ -144,6 +147,29 @@ public class CDVBackgroundGeolocation extends CordovaPlugin { ...@@ -144,6 +147,29 @@ public class CDVBackgroundGeolocation extends CordovaPlugin {
event.putBoolean("request", true); event.putBoolean("request", true);
resetOdometerCallback = callbackContext; resetOdometerCallback = callbackContext;
EventBus.getDefault().post(event); EventBus.getDefault().post(event);
} else if (ACTION_ADD_GEOFENCE.equalsIgnoreCase(action)) {
result = true;
JSONObject config = data.getJSONObject(0);
try {
Bundle event = new Bundle();
event.putString("name", action);
event.putBoolean("request", true);
event.putFloat("radius", (float) config.getLong("radius"));
event.putDouble("latitude", config.getDouble("latitude"));
event.putDouble("longitude", config.getDouble("longitude"));
event.putString("identifier", config.getString("identifier"));
EventBus.getDefault().post(event);
callbackContext.success();
} catch (JSONException e) {
Log.w(TAG, e);
callbackContext.error("Failed to add geofence");
result = false;
}
} else if (ACTION_ON_GEOFENCE.equalsIgnoreCase(action)) {
result = true;
geofenceCallbacks.add(callbackContext);
} }
return result; return result;
} }
...@@ -296,7 +322,10 @@ public class CDVBackgroundGeolocation extends CordovaPlugin { ...@@ -296,7 +322,10 @@ public class CDVBackgroundGeolocation extends CordovaPlugin {
runInBackground(getOdometerCallback, result); runInBackground(getOdometerCallback, result);
} else if (ACTION_RESET_ODOMETER.equalsIgnoreCase(name)) { } else if (ACTION_RESET_ODOMETER.equalsIgnoreCase(name)) {
PluginResult result = new PluginResult(PluginResult.Status.OK); PluginResult result = new PluginResult(PluginResult.Status.OK);
runInBackground(resetOdometerCallback, result); resetOdometerCallback.sendPluginResult(result);
} else if (ACTION_ON_PACE_CHANGE.equalsIgnoreCase(name)) {
PluginResult result = new PluginResult(PluginResult.Status.OK);
paceChangeCallback.sendPluginResult(result);
} }
} }
...@@ -329,7 +358,30 @@ public class CDVBackgroundGeolocation extends CordovaPlugin { ...@@ -329,7 +358,30 @@ public class CDVBackgroundGeolocation extends CordovaPlugin {
runInBackground(locationCallback, result); runInBackground(locationCallback, result);
} }
} }
/**
* EventBus handler for Geofencing events
*/
public void onEventMainThread(GeofencingEvent geofenceEvent) {
Log.i(TAG, "- Rx GeofencingEvent: " + geofenceEvent);
if (!geofenceCallbacks.isEmpty()) {
for (Geofence geofence : geofenceEvent.getTriggeringGeofences()) {
JSONObject params = new JSONObject();
try {
params.put("identifier", geofence.getRequestId());
} catch (JSONException e) {
e.printStackTrace();
}
PluginResult result = new PluginResult(PluginResult.Status.OK, params);
result.setKeepCallback(true);
for (CallbackContext callback : geofenceCallbacks) {
runInBackground(callback, result);
}
}
}
}
/** /**
* Run a javascript callback in Background * Run a javascript callback in Background
* @param cb * @param cb
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
@interface CDVBackgroundGeolocation : CDVPlugin @interface CDVBackgroundGeolocation : CDVPlugin
@property (nonatomic, strong) NSString* syncCallbackId; @property (nonatomic, strong) NSString* syncCallbackId;
@property (nonatomic, strong) NSString* geofenceCallbackId; @property (nonatomic, strong) NSMutableArray* geofenceListeners;
@property (nonatomic, strong) NSMutableArray* stationaryRegionListeners; @property (nonatomic, strong) NSMutableArray* stationaryRegionListeners;
- (void) configure:(CDVInvokedUrlCommand*)command; - (void) configure:(CDVInvokedUrlCommand*)command;
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
NSDictionary *config; NSDictionary *config;
} }
@synthesize syncCallbackId, geofenceCallbackId, stationaryRegionListeners; @synthesize syncCallbackId, geofenceListeners, stationaryRegionListeners;
- (void)pluginInitialize - (void)pluginInitialize
{ {
...@@ -87,44 +87,61 @@ ...@@ -87,44 +87,61 @@
* location handler from BackgroundGeolocation * location handler from BackgroundGeolocation
*/ */
- (void)onLocationChanged:(NSNotification*)notification { - (void)onLocationChanged:(NSNotification*)notification {
CLLocation *location = notification.object; CLLocation *location = [notification.userInfo objectForKey:@"location"];
NSDictionary *locationData = [bgGeo locationToDictionary:location];
CDVPluginResult* result = nil; NSDictionary *params = @{
result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:locationData]; @"location": [bgGeo locationToDictionary:location],
@"taskId": @([bgGeo createBackgroundTask])
};
CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:params];
[result setKeepCallbackAsBool:YES]; [result setKeepCallbackAsBool:YES];
[self.commandDelegate sendPluginResult:result callbackId:self.syncCallbackId];
[self.commandDelegate runInBackground:^{
[self.commandDelegate sendPluginResult:result callbackId:self.syncCallbackId];
}];
} }
- (void) onStationaryLocation:(NSNotification*)notification - (void) onStationaryLocation:(NSNotification*)notification
{ {
CLLocation *location = notification.object;
NSDictionary *locationData = [bgGeo locationToDictionary:location];
if (![self.stationaryRegionListeners count]) { if (![self.stationaryRegionListeners count]) {
[bgGeo stopBackgroundTask];
return; return;
} }
CLLocation *location = [notification.userInfo objectForKey:@"location"];
CDVPluginResult *result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:locationData]; NSDictionary *locationData = [bgGeo locationToDictionary:location];
[result setKeepCallbackAsBool:YES];
for (NSString *callbackId in self.stationaryRegionListeners) for (NSString *callbackId in self.stationaryRegionListeners) {
{ NSDictionary *params = @{
[self.commandDelegate sendPluginResult:result callbackId:callbackId]; @"location": locationData,
@"taskId": @([bgGeo createBackgroundTask])
};
CDVPluginResult *result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:params];
[result setKeepCallbackAsBool:YES];
[self.commandDelegate runInBackground:^{
[self.commandDelegate sendPluginResult:result callbackId:callbackId];
}];
} }
[bgGeo stopBackgroundTask];
} }
- (void) onEnterGeofence:(NSNotification*)notification - (void) onEnterGeofence:(NSNotification*)notification
{ {
if (self.geofenceCallbackId == nil) { if (![self.geofenceListeners count]) {
return; return;
} }
CLCircularRegion *region = notification.object; CLCircularRegion *region = [notification.userInfo objectForKey:@"geofence"];
CDVPluginResult *result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:region.identifier]; for (NSString *callbackId in self.stationaryRegionListeners) {
[result setKeepCallbackAsBool:YES]; NSDictionary *params = @{
[self.commandDelegate sendPluginResult:result callbackId:self.geofenceCallbackId]; @"identifier": region.identifier,
@"taskId": @([bgGeo createBackgroundTask])
};
CDVPluginResult *result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:params];
[result setKeepCallbackAsBool:YES];
[self.commandDelegate runInBackground:^{
[self.commandDelegate sendPluginResult:result callbackId:callbackId];
}];
}
} }
/** /**
* Fetches current stationaryLocation * Fetches current stationaryLocation
...@@ -187,14 +204,18 @@ ...@@ -187,14 +204,18 @@
- (void) onGeofence:(CDVInvokedUrlCommand*)command - (void) onGeofence:(CDVInvokedUrlCommand*)command
{ {
self.geofenceCallbackId = command.callbackId; if (self.geofenceListeners == nil) {
self.geofenceListeners = [[NSMutableArray alloc] init];
}
[self.geofenceListeners addObject:command.callbackId];
} }
/** /**
* Called by js to signify the end of a background-geolocation event * Called by js to signify the end of a background-geolocation event
*/ */
-(void) finish:(CDVInvokedUrlCommand*)command -(void) finish:(CDVInvokedUrlCommand*)command
{ {
[bgGeo finish]; UIBackgroundTaskIdentifier taskId = [[command.arguments objectAtIndex: 0] integerValue];
[bgGeo stopBackgroundTask:taskId];
} }
/** /**
* If you don't stopMonitoring when application terminates, the app will be awoken still when a * If you don't stopMonitoring when application terminates, the app will be awoken still when a
......
...@@ -9,10 +9,10 @@ ...@@ -9,10 +9,10 @@
- (void) configure:(NSDictionary*)config; - (void) configure:(NSDictionary*)config;
- (void) start; - (void) start;
- (void) stop; - (void) stop;
- (void) finish;
- (NSArray*) sync; - (NSArray*) sync;
- (NSArray*) getLocations; - (NSArray*) getLocations;
- (void) stopBackgroundTask; - (UIBackgroundTaskIdentifier) createBackgroundTask;
- (void) stopBackgroundTask:(UIBackgroundTaskIdentifier)taskId;
- (void) onPaceChange:(BOOL)value; - (void) onPaceChange:(BOOL)value;
- (void) setConfig:(NSDictionary*)command; - (void) setConfig:(NSDictionary*)command;
- (NSDictionary*) getStationaryLocation; - (NSDictionary*) getStationaryLocation;
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
<dict> <dict>
<key>Headers/TSLocationManager.h</key> <key>Headers/TSLocationManager.h</key>
<data> <data>
93eNJDqyN5vuBJn9oYeT7lb2x1w= w7pffjKj41qqtvUN0J6fOruESPM=
</data> </data>
<key>Info.plist</key> <key>Info.plist</key>
<data> <data>
...@@ -21,7 +21,7 @@ ...@@ -21,7 +21,7 @@
<dict> <dict>
<key>Headers/TSLocationManager.h</key> <key>Headers/TSLocationManager.h</key>
<data> <data>
93eNJDqyN5vuBJn9oYeT7lb2x1w= w7pffjKj41qqtvUN0J6fOruESPM=
</data> </data>
<key>Modules/module.modulemap</key> <key>Modules/module.modulemap</key>
<data> <data>
......
...@@ -9,24 +9,30 @@ ...@@ -9,24 +9,30 @@
var exec = require("cordova/exec"); var exec = require("cordova/exec");
module.exports = { module.exports = {
/** /**
* @property {Object} stationaryRegion * @property {Object} stationaryLocation
*/ */
stationaryRegion: null, stationaryLocation: null,
/** /**
* @property {Object} config * @property {Object} config
*/ */
config: {}, config: {},
configure: function(success, failure, config) { configure: function(success, failure, config) {
var me = this;
config = config || {}; config = config || {};
this.config = config; this.config = config;
success = success || function(location) {}; success = success || function(location, taskId) {
var mySuccess = function(location) { me.finish(taskId);
};
var mySuccess = function(params) {
var location = params.location || params;
var taskId = params.taskId || 'task-id-undefined';
// Transform timestamp to Date instance. // Transform timestamp to Date instance.
if (location.timestamp) { if (location.timestamp) {
location.timestamp = new Date(location.timestamp); location.timestamp = new Date(location.timestamp);
} }
success.call(this, location); success.call(this, location, taskId);
} }
exec(mySuccess, exec(mySuccess,
failure || function() {}, failure || function() {},
...@@ -49,12 +55,15 @@ module.exports = { ...@@ -49,12 +55,15 @@ module.exports = {
'stop', 'stop',
[]); []);
}, },
finish: function(success, failure) { finish: function(taskId, success, failure) {
if (!taskId) {
throw "BackgroundGeolocation#finish must now be provided with a taskId as 1st param, eg: bgGeo.finish(taskId). taskId is provided by 2nd param in callback";
}
exec(success || function() {}, exec(success || function() {},
failure || function() {}, failure || function() {},
'BackgroundGeoLocation', 'BackgroundGeoLocation',
'finish', 'finish',
[]); [taskId]);
}, },
changePace: function(isMoving, success, failure) { changePace: function(isMoving, success, failure) {
exec(success || function() {}, exec(success || function() {},
...@@ -94,10 +103,15 @@ module.exports = { ...@@ -94,10 +103,15 @@ module.exports = {
*/ */
onStationary: function(success, failure) { onStationary: function(success, failure) {
var me = this; var me = this;
success = success || function() {}; success = success || function(location, taskId) {
var callback = function(region) { me.finish(taskId);
me.stationaryRegion = region; };
success.apply(me, arguments); var callback = function(params) {
var location = params.location || params,
taskId = params.taskId || 'task-id-undefined';
me.stationaryLocation = location;
success.call(me, location, taskId);
}; };
exec(callback, exec(callback,
failure || function() {}, failure || function() {},
...@@ -180,7 +194,13 @@ module.exports = { ...@@ -180,7 +194,13 @@ module.exports = {
if (!typeof(success) === 'function') { if (!typeof(success) === 'function') {
throw "#onGeofence requires a success callback"; throw "#onGeofence requires a success callback";
} }
exec(success, var me = this;
var mySuccess = function(params) {
var identifier = params.identifier,
taskId = params.taskId || 'task-id-undefined';
success.call(me, identifier, taskId);
}
exec(mySuccess,
failure || function() {}, failure || function() {},
'BackgroundGeoLocation', 'BackgroundGeoLocation',
'onGeofence', 'onGeofence',
......
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