Commit 64a54878 authored by Chris Scott's avatar Chris Scott

conflicts merging old re-factor branch for ios after accepting a few 3rd-party PRs

parents f39d5b14 e7442ca9
......@@ -80,9 +80,10 @@ A full example could be:
desiredAccuracy: 10,
stationaryRadius: 20,
distanceFilter: 30,
debug: true, // <-- enable this hear sounds for background-geolocation life-cycle.
notificationTitle: 'Background tracking', // <-- android only, customize the title of the notification
notificationText: 'ENABLED' // <-- android only, customize the text of the notification
notificationText: 'ENABLED', // <-- android only, customize the text of the notification
activityType: 'AutomotiveNavigation',
debug: true // <-- enable this hear sounds for background-geolocation life-cycle.
});
// Turn ON the background-geolocation system. The user will be tracked whenever they suspend the app.
......@@ -193,6 +194,16 @@ Optional HTTP headers POSTed to your server when persisting locations
On Android devices it is required to have a notification in the drawer because it's a "foreground service". This gives it high priority, decreasing probability of OS killing it. To customize the title and text of the notification, set these options.
#####`@param {Integer} locationTimeout
The minimum time interval between location updates, in seconds. See [Android docs](http://developer.android.com/reference/android/location/LocationManager.html#requestLocationUpdates(long,%20float,%20android.location.Criteria,%20android.app.PendingIntent)) for more information.
### iOS Config
#####`@param {String} activityType [AutomotiveNavigation, OtherNavigation, Fitness, Other]`
Presumably, this affects ios GPS algorithm. See [Apple docs](https://developer.apple.com/library/ios/documentation/CoreLocation/Reference/CLLocationManager_Class/CLLocationManager/CLLocationManager.html#//apple_ref/occ/instp/CLLocationManager/activityType) for more information
## Licence ##
The MIT License
......
......@@ -95,12 +95,16 @@ var app = {
auth_token: 'user_secret_auth_token',
foo: 'bar'
},
headers: {
'X-Foo': 'bar'
},
desiredAccuracy: 10,
stationaryRadius: 20,
distanceFilter: 30,
debug: true, // <-- enable this hear sounds for background-geolocation life-cycle.
notificationTitle: 'Background tracking', // <-- android only, customize the title of the notification
notificationText: 'ENABLED' // <-- android only, customize the text of the notification
notificationTitle: 'Background tracking', // <-- android only, customize the title of the notification
notificationText: 'ENABLED', // <-- android only, customize the text of the notification
activityType: "AutomotiveNavigation", // <-- iOS-only
debug: true // <-- enable this hear sounds for background-geolocation life-cycle.
});
// Turn ON the background-geolocation system. The user will be tracked whenever they suspend the app.
......
......@@ -68,7 +68,9 @@ public class BackgroundGpsPlugin extends CordovaPlugin {
} else if (ACTION_CONFIGURE.equalsIgnoreCase(action)) {
result = true;
try {
// [params, headers url, stationaryRadius, distanceFilter, locationTimeout, desiredAccuracy, debug]);
// Params.
// 0 1 2 3 4 5 6 7 8 8 9
//[params, headers, url, stationaryRadius, distanceFilter, locationTimeout, desiredAccuracy, debug, notificationTitle, notificationText, activityType]
this.params = data.getString(0);
this.headers = data.getString(1);
this.url = data.getString(2);
......
......@@ -33,20 +33,21 @@
NSMutableArray *locationCache;
NSDate *suspendedAt;
BOOL isAcquiringStationaryLocation;
CLLocation *stationaryLocation;
NSInteger stationaryLocationAttempts;
NSInteger maxStationaryLocationAttempts;
CLCircularRegion *stationaryRegion;
NSInteger locationAcquisitionAttempts;
BOOL isAcquiringStationaryLocation;
NSInteger maxStationaryLocationAttempts;
BOOL isAcquiringSpeed;
NSInteger speedAcquisitionAttempts;
NSInteger maxSpeedAcquistionAttempts;
NSInteger stationaryRadius;
NSInteger distanceFilter;
NSInteger locationTimeout;
NSInteger desiredAccuracy;
CLActivityType activityType;
}
- (void)pluginInitialize
......@@ -56,6 +57,7 @@
locationManager = [[CLLocationManager alloc] init];
locationManager.delegate = self;
isMoving = NO;
stationaryLocation = nil;
stationaryRegion = nil;
isDebugging = NO;
......@@ -78,20 +80,23 @@
- (void) configure:(CDVInvokedUrlCommand*)command
{
// in iOS, we call to javascript for HTTP now so token and url should be @deprecated until Android calls out to javascript.
token = [command.arguments objectAtIndex: 0];
url = [command.arguments objectAtIndex: 1];
// Params.
// 0 1 2 3 4 5 6 7 8 8 9
//[params, headers, url, stationaryRadius, distanceFilter, locationTimeout, desiredAccuracy, debug, notificationTitle, notificationText, activityType]
params = [command.arguments objectAtIndex: 0];
headers = [command.arguments objectAtIndex: 1];
url = [command.arguments objectAtIndex: 2];
stationaryRadius = [[command.arguments objectAtIndex: 3] intValue];
distanceFilter = [[command.arguments objectAtIndex: 4] intValue];
locationTimeout = [[command.arguments objectAtIndex: 5] intValue];
desiredAccuracy = [self translateDesiredAccuracy:[[command.arguments objectAtIndex: 6] intValue]];
isDebugging = [[command.arguments objectAtIndex: 7] boolValue];
activityType = [self decodeActivityType:[command.arguments objectAtIndex:9]];
syncCallbackId = command.callbackId;
// Set a movement threshold for new events.
locationManager.activityType = CLActivityTypeOther;
locationManager.activityType = activityType;
locationManager.pausesLocationUpdatesAutomatically = YES;
locationManager.distanceFilter = distanceFilter; // meters
locationManager.desiredAccuracy = desiredAccuracy;
......@@ -103,7 +108,8 @@
NSLog(@" - stationaryRadius: %ld", (long)stationaryRadius);
NSLog(@" - locationTimeout: %ld", (long)locationTimeout);
NSLog(@" - desiredAccuracy: %ld", (long)desiredAccuracy);
NSLog(@" - debug: %hhd", isDebugging);
NSLog(@" - activityType: %@", [command.arguments objectAtIndex:7]);
NSLog(@" - debug: %d", isDebugging);
}
- (void) setConfig:(CDVInvokedUrlCommand*)command
{
......@@ -111,7 +117,7 @@
NSDictionary *config = [command.arguments objectAtIndex:0];
if (config[@"desiredAccuracy"]) {
desiredAccuracy = [self translateDesiredAccuracy:[config[@"desiredAccuracy"] floatValue]];
desiredAccuracy = [self decodeDesiredAccuracy:[config[@"desiredAccuracy"] floatValue]];
NSLog(@" desiredAccuracy: %@", config[@"desiredAccuracy"]);
}
if (config[@"stationaryRadius"]) {
......@@ -122,9 +128,9 @@
distanceFilter = [config[@"distanceFilter"] intValue];
NSLog(@" distanceFilter: %@", config[@"distanceFilter"]);
}
if (config[@"timeout"]) {
locationTimeout = [config[@"timeout"] intValue];
NSLog(@" locationTimeout: %@", config[@"timeout"]);
if (config[@"locationTimeout"]) {
locationTimeout = [config[@"locationTimeout"] intValue];
NSLog(@" locationTimeout: %@", config[@"locationTimeout"]);
}
CDVPluginResult* result = nil;
......@@ -132,7 +138,7 @@
[self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
}
-(NSInteger)translateDesiredAccuracy:(NSInteger)accuracy
-(NSInteger)decodeDesiredAccuracy:(NSInteger)accuracy
{
switch (accuracy) {
case 1000:
......@@ -153,6 +159,19 @@
return accuracy;
}
-(CLActivityType)decodeActivityType:(NSString*)name
{
if ([name caseInsensitiveCompare:@"AutomotiveNavigation"]) {
return CLActivityTypeAutomotiveNavigation;
} else if ([name caseInsensitiveCompare:@"OtherNavigation"]) {
return CLActivityTypeOtherNavigation;
} else if ([name caseInsensitiveCompare:@"Fitness"]) {
return CLActivityTypeFitness;
} else {
return CLActivityTypeOther;
}
}
/**
* Turn on background geolocation
*/
......@@ -177,6 +196,8 @@
{
NSLog(@"- CDVBackgroundGeoLocation stop");
enabled = NO;
isMoving = NO;
[locationManager stopUpdatingLocation];
[locationManager stopMonitoringSignificantLocationChanges];
if (stationaryRegion != nil) {
......@@ -188,6 +209,7 @@
[self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
}
/**
* Change pace to moving/stopped
* @param {Boolean} isMoving
......@@ -195,7 +217,7 @@
- (void) onPaceChange:(CDVInvokedUrlCommand *)command
{
isMoving = [[command.arguments objectAtIndex: 0] boolValue];
NSLog(@"- CDVBackgroundGeoLocation onPaceChange %hhd", isMoving);
NSLog(@"- CDVBackgroundGeoLocation onPaceChange %d", isMoving);
UIApplicationState state = [[UIApplication sharedApplication] applicationState];
if (state == UIApplicationStateBackground) {
[self setPace:isMoving];
......@@ -245,7 +267,7 @@
*/
-(void) onSuspend:(NSNotification *) notification
{
NSLog(@"- CDVBackgroundGeoLocation suspend (enabled? %hhd)", enabled);
NSLog(@"- CDVBackgroundGeoLocation suspend (enabled? %d)", enabled);
suspendedAt = [NSDate date];
if (enabled) {
......@@ -266,8 +288,8 @@
-(void) locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
NSLog(@"- CDVBackgroundGeoLocation didUpdateLocations (isMoving: %hhd)", isMoving);
CLLocation *newLocation = [locations lastObject];
NSLog(@"- CDVBackgroundGeoLocation didUpdateLocations (isMoving: %d)", isMoving);
CLLocation *location = [locations lastObject];
if (!isMoving && !isAcquiringStationaryLocation && !stationaryLocation) {
// Perhaps our GPS signal was interupted, re-acquire a stationaryLocation now.
......@@ -284,78 +306,81 @@
// test the age of the location measurement to determine if the measurement is cached
// in most cases you will not want to rely on cached measurements
NSTimeInterval locationAge = -[newLocation.timestamp timeIntervalSinceNow];
NSTimeInterval locationAge = -[location.timestamp timeIntervalSinceNow];
if (locationAge > 5.0) return;
// test that the horizontal accuracy does not indicate an invalid measurement
if (newLocation.horizontalAccuracy < 0) return;
if (location.horizontalAccuracy < 0) return;
// test the measurement to see if it is more accurate than the previous measurement
if (isAcquiringStationaryLocation) {
NSLog(@"- Acquiring stationary location, accuracy: %f", newLocation.horizontalAccuracy);
if (![self isBestStationaryLocation:newLocation]) {
NSLog(@"- Acquiring stationary location, accuracy: %f", location.horizontalAccuracy);
if (isDebugging) {
AudioServicesPlaySystemSound (acquiringLocationSound);
}
if (stationaryLocation == nil || stationaryLocation.horizontalAccuracy > location.horizontalAccuracy) {
stationaryLocation = location;
}
if (++locationAcquisitionAttempts == maxStationaryLocationAttempts) {
isAcquiringStationaryLocation = NO;
[self startMonitoringStationaryRegion:stationaryLocation];
if (isDebugging) {
AudioServicesPlaySystemSound (acquiringLocationSound);
AudioServicesPlaySystemSound (acquiredLocationSound);
}
} else {
// Unacceptable stationary-location: bail-out and wait for another.
return;
}
} else if (isAcquiringSpeed) {
if (isDebugging) {
AudioServicesPlaySystemSound (acquiredLocationSound);
AudioServicesPlaySystemSound (acquiringLocationSound);
}
if (++locationAcquisitionAttempts == maxSpeedAcquistionAttempts) {
// We should have a good sample for speed now, power down our GPS as configured by user.
isAcquiringSpeed = NO;
[locationManager setDesiredAccuracy:desiredAccuracy];
[locationManager setDistanceFilter:[self calculateDistanceFilter:location.speed]];
[locationManager startUpdatingLocation];
} else {
return;
}
} else if (isMoving) {
// Adjust distanceFilter incrementally based upon current speed
float newDistanceFilter = [self calculateDistanceFilter:location.speed];
if (newDistanceFilter != locationManager.distanceFilter) {
NSLog(@"- CDVBackgroundGeoLocation updated distanceFilter, new: %f, old: %f", newDistanceFilter, locationManager.distanceFilter);
//[locationManager stopUpdatingLocation];
[locationManager setDistanceFilter:newDistanceFilter];
[locationManager startUpdatingLocation];
}
[self startMonitoringStationaryRegion:stationaryLocation];
}
// Bail out if there's already a background-task in-effect.
if (bgTask != UIBackgroundTaskInvalid) {
NSLog(@" Abort: found existing background-task");
return;
}
// Create a background-task and delegate to Javascript for syncing location
bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
[self stopBackgroundTask];
}];
[self.commandDelegate runInBackground:^{
[self sync:newLocation];
[self sync:location];
}];
// Adjust distanceFilter incrementally based upon current speed
if (isMoving)
{
float newDistanceFilter = [self calculateDistanceFilter:newLocation.speed];
if (newDistanceFilter != locationManager.distanceFilter) {
NSLog(@"- CDVBackgroundGeoLocation updated distanceFilter, new: %f, old: %f", newDistanceFilter, locationManager.distanceFilter);
[locationManager stopUpdatingLocation];
locationManager.distanceFilter = newDistanceFilter;
[locationManager startUpdatingLocation];
}
}
}
- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error
{
NSLog(@"- CDVBackgroundGeoLocation locationManager failed: %@", error);
[locationManager stopUpdatingLocation];
isMoving = NO;
isAcquiringStationaryLocation = NO;
stationaryLocation = nil;
[locationManager startMonitoringSignificantLocationChanges];
}
-(BOOL) isBestStationaryLocation:(CLLocation*)location {
stationaryLocationAttempts++;
if (stationaryLocationAttempts == maxStationaryLocationAttempts) {
return true;
}
if (stationaryLocation == nil || stationaryLocation.horizontalAccuracy > location.horizontalAccuracy) {
// store the location as the "best effort"
stationaryLocation = location;
if (location.horizontalAccuracy <= 5.0) {
return true;
}
}
return false;
[locationManager stopUpdatingLocation];
}
/**
......@@ -364,18 +389,7 @@
-(float) calculateDistanceFilter:(float)speed
{
float newDistanceFilter = distanceFilter;
if (isAcquiringSpeed) {
if (++speedAcquisitionAttempts == maxSpeedAcquistionAttempts) {
// We should have a good sample for speed now, power down our GPS as configured by user.
isAcquiringSpeed = NO;
[locationManager stopUpdatingLocation];
locationManager.desiredAccuracy = desiredAccuracy;
[locationManager startUpdatingLocation];
} else {
return newDistanceFilter;
}
}
if (speed > 3.0 && speed < 100) {
if (speed < 100) {
// (rounded-speed-to-nearest-5) / 2)^2
// eg 5.2 becomes (5/2)^2
newDistanceFilter = pow((5.0 * floorf(speed / 5.0 + 0.5f)), 2) + distanceFilter;
......@@ -383,7 +397,6 @@
return (newDistanceFilter < 1000) ? newDistanceFilter : 1000;
}
/**
* We are running in the background if this is being executed.
* We can't assume normal network access.
......@@ -465,29 +478,29 @@
- (void)setPace:(BOOL)value
{
NSLog(@"- CDVBackgroundGeoLocation setPace %d, stationaryRegion? %d", value, stationaryRegion!=nil);
isMoving = value;
isMoving = value;
isAcquiringStationaryLocation = NO;
isAcquiringSpeed = NO;
locationAcquisitionAttempts = 0;
// Kill the current stationary-region.
if (stationaryRegion != nil) {
[locationManager stopMonitoringForRegion:stationaryRegion];
stationaryRegion = nil;
}
if (isDebugging) {
AudioServicesPlaySystemSound (isMoving ? paceChangeYesSound : paceChangeNoSound);
}
if (isMoving) {
if (stationaryRegion != nil) {
[locationManager stopMonitoringForRegion:stationaryRegion];
stationaryRegion = nil;
}
isAcquiringSpeed = YES;
speedAcquisitionAttempts = 0;
[locationManager stopMonitoringSignificantLocationChanges];
locationManager.distanceFilter = distanceFilter;
// Power-up the GPS temporarily until we get a good speed sample.
locationManager.desiredAccuracy = kCLLocationAccuracyBest;
[locationManager startUpdatingLocation];
} else {
// Crank up the GPS power temporarily to get a good fix on our current staionary location in order to set up region-monitoring.
stationaryLocation = nil;
isAcquiringStationaryLocation = YES;
stationaryLocationAttempts = 0;
isAcquiringStationaryLocation = YES;
}
if (isAcquiringSpeed || isAcquiringStationaryLocation) {
// Crank up the GPS power temporarily to get a good fix on our current location
[locationManager stopUpdatingLocation];
locationManager.distanceFilter = kCLDistanceFilterNone;
locationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation;
[locationManager startUpdatingLocation];
......@@ -498,22 +511,20 @@
*/
- (void) startMonitoringStationaryRegion:(CLLocation*)location {
CLLocationCoordinate2D coord = [location coordinate];
NSLog(@"- CDVBackgroundGeoLocation createStationaryRegion (%f,%f)", coord.latitude, coord.longitude);
if (stationaryRegion != nil) {
[locationManager stopMonitoringForRegion:stationaryRegion];
}
isAcquiringStationaryLocation = NO;
[locationManager stopUpdatingLocation];
locationManager.distanceFilter = distanceFilter;
locationManager.desiredAccuracy = desiredAccuracy;
[locationManager startMonitoringSignificantLocationChanges];
stationaryRegion = [[CLCircularRegion alloc] initWithCenter: coord radius:stationaryRadius identifier:@"BackgroundGeoLocation stationary region"];
stationaryRegion.notifyOnExit = YES;
[locationManager startMonitoringForRegion:stationaryRegion];
locationManager.distanceFilter = distanceFilter;
locationManager.desiredAccuracy = desiredAccuracy;
[locationManager stopUpdatingLocation];
}
// If you don't stopMonitorying when application terminates, the app will be awoken still when a
......
......@@ -2,7 +2,7 @@ var exec = require("cordova/exec");
module.exports = {
configure: function(success, failure, config) {
var params = JSON.stringify(config.params || {}),
headers = JSON.stringify(config.headers || {}),
headers = JSON.stringify(config.headers || {}),
url = config.url || 'BackgroundGeoLocation_url',
stationaryRadius = (config.stationaryRadius >= 0) ? config.stationaryRadius : 50, // meters
distanceFilter = (config.distanceFilter >= 0) ? config.distanceFilter : 500, // meters
......@@ -11,12 +11,14 @@ module.exports = {
debug = config.debug || false,
notificationTitle = config.notificationTitle || "Background tracking",
notificationText = config.notificationText || "ENABLED";
activityType = config.activityType || "OTHER";
exec(success || function() {},
failure || function() {},
'BackgroundGeoLocation',
'configure',
[params, headers, url, stationaryRadius, distanceFilter, locationTimeout, desiredAccuracy, debug, notificationTitle, notificationText]);
[params, headers, url, stationaryRadius, distanceFilter, locationTimeout, desiredAccuracy, debug, notificationTitle, notificationText, activityType]
);
},
start: function(success, failure, config) {
exec(success || function() {},
......
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