@josuelmm/cordova-background-geolocation 3.1.1 → 4.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.npmignore +4 -0
- package/CHANGELOG.md +313 -0
- package/CLAUDE.md +56 -0
- package/HISTORY.md +124 -0
- package/README.md +198 -6
- package/android/CDVBackgroundGeolocation/src/main/java/com/marianhello/bgloc/cordova/ConfigMapper.java +90 -0
- package/android/CDVBackgroundGeolocation/src/main/java/com/tenforwardconsulting/bgloc/cordova/BackgroundGeolocationPlugin.java +362 -1
- package/android/common/src/main/java/com/marianhello/bgloc/BackgroundGeolocationFacade.java +153 -0
- package/android/common/src/main/java/com/marianhello/bgloc/BootCompletedReceiver.java +27 -11
- package/android/common/src/main/java/com/marianhello/bgloc/Config.java +268 -0
- package/android/common/src/main/java/com/marianhello/bgloc/HttpPostService.java +86 -26
- package/android/common/src/main/java/com/marianhello/bgloc/PluginDelegate.java +26 -0
- package/android/common/src/main/java/com/marianhello/bgloc/PostLocationTask.java +48 -5
- package/android/common/src/main/java/com/marianhello/bgloc/data/SessionLocationDAO.java +18 -0
- package/android/common/src/main/java/com/marianhello/bgloc/data/sqlite/SQLiteOpenHelper.java +8 -1
- package/android/common/src/main/java/com/marianhello/bgloc/data/sqlite/SQLiteSessionContract.java +74 -0
- package/android/common/src/main/java/com/marianhello/bgloc/data/sqlite/SQLiteSessionLocationDAO.java +169 -0
- package/android/common/src/main/java/com/marianhello/bgloc/driving/DrivingEventsDetector.java +265 -0
- package/android/common/src/main/java/com/marianhello/bgloc/http/UrlTemplateResolver.java +115 -0
- package/android/common/src/main/java/com/marianhello/bgloc/oem/BatteryOemHelper.java +214 -0
- package/android/common/src/main/java/com/marianhello/bgloc/provider/ActivityRecognitionLocationProvider.java +13 -9
- package/android/common/src/main/java/com/marianhello/bgloc/provider/DistanceFilterLocationProvider.java +29 -40
- package/android/common/src/main/java/com/marianhello/bgloc/provider/RawLocationProvider.java +14 -34
- package/android/common/src/main/java/com/marianhello/bgloc/sensor/SensorFusionDetector.java +199 -0
- package/android/common/src/main/java/com/marianhello/bgloc/service/LocationServiceImpl.java +310 -7
- package/android/common/src/main/java/com/marianhello/bgloc/service/LocationServiceProxy.java +14 -2
- package/android/common/src/main/java/com/marianhello/bgloc/sync/SyncAdapter.java +50 -3
- package/android/dependencies.gradle +0 -3
- package/angular/background-geolocation-events.ts +21 -0
- package/angular/background-geolocation.service.ts +91 -0
- package/angular/dist/background-geolocation-events.d.ts +18 -1
- package/angular/dist/background-geolocation.service.d.ts +40 -0
- package/angular/dist/esm2022/background-geolocation-events.mjs +22 -1
- package/angular/dist/esm2022/background-geolocation.service.mjs +47 -1
- package/angular/dist/fesm2022/josuelmm-cordova-background-geolocation.mjs +67 -0
- package/angular/dist/fesm2022/josuelmm-cordova-background-geolocation.mjs.map +1 -1
- package/ios/CDVBackgroundGeolocation/CDVBackgroundGeolocation.h +4 -0
- package/ios/CDVBackgroundGeolocation/CDVBackgroundGeolocation.m +352 -1
- package/ios/common/BackgroundGeolocation/MAURBackgroundGeolocationFacade.h +26 -0
- package/ios/common/BackgroundGeolocation/MAURBackgroundGeolocationFacade.m +421 -15
- package/ios/common/BackgroundGeolocation/MAURBackgroundSync.h +12 -0
- package/ios/common/BackgroundGeolocation/MAURBackgroundSync.m +83 -5
- package/ios/common/BackgroundGeolocation/MAURConfig.h +15 -0
- package/ios/common/BackgroundGeolocation/MAURConfig.m +100 -3
- package/ios/common/BackgroundGeolocation/MAURDistanceFilterLocationProvider.m +29 -2
- package/ios/common/BackgroundGeolocation/MAURGeolocationOpenHelper.m +12 -3
- package/ios/common/BackgroundGeolocation/MAURPostLocationTask.h +4 -0
- package/ios/common/BackgroundGeolocation/MAURPostLocationTask.m +102 -44
- package/ios/common/BackgroundGeolocation/MAURSensorFusionDetector.h +41 -0
- package/ios/common/BackgroundGeolocation/MAURSensorFusionDetector.m +137 -0
- package/ios/common/BackgroundGeolocation/MAURSessionLocationContract.h +29 -0
- package/ios/common/BackgroundGeolocation/MAURSessionLocationContract.m +31 -0
- package/ios/common/BackgroundGeolocation/MAURSessionLocationDAO.h +25 -0
- package/ios/common/BackgroundGeolocation/MAURSessionLocationDAO.m +153 -0
- package/ios/common/BackgroundGeolocation/MAURUrlTemplateResolver.h +31 -0
- package/ios/common/BackgroundGeolocation/MAURUrlTemplateResolver.m +107 -0
- package/package.json +36 -1
- package/plugin.xml +26 -8
- package/www/BackgroundGeolocation.d.ts +559 -3
- package/www/BackgroundGeolocation.js +78 -1
- package/RELEASE.MD +0 -16
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
#import "MAURPostLocationTask.h"
|
|
18
18
|
#import "MAURSQLiteConfigurationDAO.h"
|
|
19
19
|
#import "MAURSQLiteLocationDAO.h"
|
|
20
|
+
#import "MAURSessionLocationDAO.h"
|
|
20
21
|
#import "MAURBackgroundTaskManager.h"
|
|
21
22
|
#import "MAURLogging.h"
|
|
22
23
|
#import "FMDBLogger.h"
|
|
@@ -28,6 +29,7 @@
|
|
|
28
29
|
#import "MAURUncaughtExceptionLogger.h"
|
|
29
30
|
#import "MAURPostLocationTask.h"
|
|
30
31
|
#import "INTULocationManager.h"
|
|
32
|
+
#import "MAURSensorFusionDetector.h"
|
|
31
33
|
|
|
32
34
|
// error messages
|
|
33
35
|
#define CONFIGURE_ERROR_MSG "Configuration error."
|
|
@@ -45,21 +47,63 @@ static NSString * const TAG = @"BgGeo";
|
|
|
45
47
|
|
|
46
48
|
FMDBLogger *sqliteLogger;
|
|
47
49
|
|
|
48
|
-
@interface MAURBackgroundGeolocationFacade () <MAURProviderDelegate, MAURPostLocationTaskDelegate>
|
|
50
|
+
@interface MAURBackgroundGeolocationFacade () <MAURProviderDelegate, MAURPostLocationTaskDelegate, MAURSensorFusionListener>
|
|
49
51
|
@end
|
|
50
52
|
|
|
53
|
+
// v3.5 Phase 4: notification name for heartbeat events. CDVBackgroundGeolocation observes
|
|
54
|
+
// it to forward into the JS event "heartbeat" with the latest known location.
|
|
55
|
+
NSString * const MAURHeartbeatNotification = @"MAURHeartbeatNotification";
|
|
56
|
+
// v4.0 Phase 6: driver-insight notifications.
|
|
57
|
+
NSString * const MAURTripStartNotification = @"MAURTripStartNotification";
|
|
58
|
+
NSString * const MAURTripEndNotification = @"MAURTripEndNotification";
|
|
59
|
+
NSString * const MAURMovingNotification = @"MAURMovingNotification";
|
|
60
|
+
NSString * const MAURStoppedNotification = @"MAURStoppedNotification";
|
|
61
|
+
NSString * const MAURSpeedingNotification = @"MAURSpeedingNotification";
|
|
62
|
+
NSString * const MAURProviderChangeNotification = @"MAURProviderChangeNotification";
|
|
63
|
+
NSString * const MAURSOSNotification = @"MAURSOSNotification";
|
|
64
|
+
// v4.1
|
|
65
|
+
NSString * const MAURHardBrakeNotification = @"MAURHardBrakeNotification";
|
|
66
|
+
NSString * const MAURRapidAccelerationNotification = @"MAURRapidAccelerationNotification";
|
|
67
|
+
NSString * const MAURSharpTurnNotification = @"MAURSharpTurnNotification";
|
|
68
|
+
NSString * const MAURPossibleCrashNotification = @"MAURPossibleCrashNotification";
|
|
69
|
+
// v4.2
|
|
70
|
+
NSString * const MAURPhoneUsageWhileDrivingNotification = @"MAURPhoneUsageWhileDrivingNotification";
|
|
71
|
+
|
|
51
72
|
@implementation MAURBackgroundGeolocationFacade {
|
|
52
73
|
BOOL isStarted;
|
|
53
74
|
MAUROperationalMode operationMode;
|
|
54
|
-
|
|
75
|
+
|
|
55
76
|
UILocalNotification *localNotification;
|
|
56
|
-
|
|
77
|
+
|
|
57
78
|
// configurable options
|
|
58
79
|
MAURConfig *_config;
|
|
59
|
-
|
|
80
|
+
|
|
60
81
|
MAURLocation *stationaryLocation;
|
|
82
|
+
MAURLocation *lastReceivedLocation; // v3.5 Phase 4: heartbeat payload
|
|
83
|
+
NSTimer *heartbeatTimer; // v3.5 Phase 4
|
|
61
84
|
MAURAbstractLocationProvider<MAURLocationProvider> *locationProvider;
|
|
62
85
|
MAURPostLocationTask *postLocationTask;
|
|
86
|
+
|
|
87
|
+
// v4.0 Phase 6: driver-insights state
|
|
88
|
+
BOOL drIsMoving;
|
|
89
|
+
BOOL drTripActive;
|
|
90
|
+
NSTimeInterval drTripStartedAt;
|
|
91
|
+
double drTripDistanceMeters;
|
|
92
|
+
BOOL drHasPrev;
|
|
93
|
+
double drPrevLat, drPrevLon;
|
|
94
|
+
NSTimeInterval drAboveTripSpeedSince;
|
|
95
|
+
NSTimeInterval drBelowMovingSince;
|
|
96
|
+
BOOL drWasSpeeding;
|
|
97
|
+
NSString *drLastProvider;
|
|
98
|
+
// v4.1 GPS-derived sensor-like state
|
|
99
|
+
double drPrevSpeed;
|
|
100
|
+
NSTimeInterval drPrevSpeedAt;
|
|
101
|
+
double drPrevBearing;
|
|
102
|
+
BOOL drHasPrevBearing;
|
|
103
|
+
NSTimeInterval drPrevBearingAt;
|
|
104
|
+
NSTimeInterval drLastHardBrakeAt, drLastRapidAccelAt, drLastSharpTurnAt, drLastCrashAt;
|
|
105
|
+
// v4.2 sensor fusion
|
|
106
|
+
MAURSensorFusionDetector *sensorFusion;
|
|
63
107
|
}
|
|
64
108
|
|
|
65
109
|
|
|
@@ -135,25 +179,41 @@ FMDBLogger *sqliteLogger;
|
|
|
135
179
|
if (isStarted) {
|
|
136
180
|
// Note: CLLocationManager must be created on a thread with an active run loop (main thread)
|
|
137
181
|
[self runOnMainThread:^{
|
|
138
|
-
|
|
182
|
+
|
|
139
183
|
// requesting new provider
|
|
140
184
|
if (![currentConfig.locationProvider isEqual:_config.locationProvider]) {
|
|
141
185
|
[locationProvider onDestroy]; // destroy current provider
|
|
142
186
|
locationProvider = [self getProvider:_config.locationProvider.intValue error:&error];
|
|
143
187
|
}
|
|
144
|
-
|
|
188
|
+
|
|
145
189
|
if (locationProvider == nil) {
|
|
146
190
|
return;
|
|
147
191
|
}
|
|
148
|
-
|
|
192
|
+
|
|
149
193
|
// trap configuration errors
|
|
150
194
|
if (![locationProvider onConfigure:_config error:&error]) {
|
|
151
195
|
return;
|
|
152
196
|
}
|
|
153
|
-
|
|
197
|
+
|
|
154
198
|
isStarted = [locationProvider onStart:&error];
|
|
155
199
|
locationProvider.delegate = self;
|
|
156
200
|
}];
|
|
201
|
+
|
|
202
|
+
// v4.1: hot-reload heartbeat scheduler if heartbeatInterval changed.
|
|
203
|
+
NSInteger prevHb = currentConfig.heartbeatInterval != nil ? [currentConfig.heartbeatInterval integerValue] : 0;
|
|
204
|
+
NSInteger newHb = _config.heartbeatInterval != nil ? [_config.heartbeatInterval integerValue] : 0;
|
|
205
|
+
if (prevHb != newHb) {
|
|
206
|
+
[self scheduleHeartbeat]; // cancels and reschedules; is a no-op if 0.
|
|
207
|
+
}
|
|
208
|
+
// Driver-insights detector reads `_config.drivingEvents` on every feed; no rebuild needed
|
|
209
|
+
// unless the dictionary identity changed in a way that toggles `enabled`. Always reset
|
|
210
|
+
// accumulators to apply the new thresholds cleanly from this point on.
|
|
211
|
+
if (![[currentConfig.drivingEvents description] isEqualToString:[_config.drivingEvents description]]) {
|
|
212
|
+
[self drivingDetectorReset];
|
|
213
|
+
// v4.2: re-evaluate sensor fusion as well (might have just been enabled/disabled).
|
|
214
|
+
[self configureSensorFusion];
|
|
215
|
+
if (isStarted) [sensorFusion start];
|
|
216
|
+
}
|
|
157
217
|
}
|
|
158
218
|
|
|
159
219
|
if (error != nil) {
|
|
@@ -212,10 +272,16 @@ FMDBLogger *sqliteLogger;
|
|
|
212
272
|
if (outError != nil) {
|
|
213
273
|
*outError = error;
|
|
214
274
|
}
|
|
215
|
-
|
|
275
|
+
|
|
216
276
|
return NO;
|
|
217
277
|
}
|
|
218
|
-
|
|
278
|
+
|
|
279
|
+
// v3.5 Phase 4: schedule heartbeat once provider is up.
|
|
280
|
+
[self scheduleHeartbeat];
|
|
281
|
+
// v4.2 Phase 8: configure & start sensor fusion if requested.
|
|
282
|
+
[self configureSensorFusion];
|
|
283
|
+
[sensorFusion start];
|
|
284
|
+
|
|
219
285
|
return isStarted;
|
|
220
286
|
}
|
|
221
287
|
|
|
@@ -225,20 +291,334 @@ FMDBLogger *sqliteLogger;
|
|
|
225
291
|
- (BOOL) stop:(NSError * __autoreleasing *)outError
|
|
226
292
|
{
|
|
227
293
|
DDLogInfo(@"%@ #stop", TAG);
|
|
228
|
-
|
|
294
|
+
|
|
229
295
|
if (!isStarted) {
|
|
230
296
|
return YES;
|
|
231
297
|
}
|
|
232
|
-
|
|
298
|
+
|
|
299
|
+
// v3.5 Phase 4: cancel heartbeat scheduler.
|
|
300
|
+
[self cancelHeartbeat];
|
|
301
|
+
// v4.0 Phase 6: reset driver-insights state machine.
|
|
302
|
+
[self drivingDetectorReset];
|
|
303
|
+
// v4.2 Phase 8: stop sensor fusion sampling.
|
|
304
|
+
sensorFusion.tripActive = NO;
|
|
305
|
+
[sensorFusion stop];
|
|
306
|
+
|
|
233
307
|
[postLocationTask stop];
|
|
234
|
-
|
|
308
|
+
|
|
235
309
|
[self runOnMainThread:^{
|
|
236
310
|
isStarted = ![locationProvider onStop:outError];
|
|
237
311
|
}];
|
|
238
|
-
|
|
312
|
+
|
|
239
313
|
return isStarted;
|
|
240
314
|
}
|
|
241
315
|
|
|
316
|
+
// v3.5 Phase 4: heartbeat scheduler.
|
|
317
|
+
- (void) scheduleHeartbeat
|
|
318
|
+
{
|
|
319
|
+
[self cancelHeartbeat];
|
|
320
|
+
if (_config == nil || _config.heartbeatInterval == nil) return;
|
|
321
|
+
NSInteger ms = [_config.heartbeatInterval integerValue];
|
|
322
|
+
if (ms <= 0) return;
|
|
323
|
+
NSTimeInterval seconds = ms / 1000.0;
|
|
324
|
+
DDLogDebug(@"%@ scheduling heartbeat every %.2fs", TAG, seconds);
|
|
325
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
326
|
+
heartbeatTimer = [NSTimer scheduledTimerWithTimeInterval:seconds
|
|
327
|
+
target:self
|
|
328
|
+
selector:@selector(onHeartbeatTick:)
|
|
329
|
+
userInfo:nil
|
|
330
|
+
repeats:YES];
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
- (void) cancelHeartbeat
|
|
335
|
+
{
|
|
336
|
+
if (heartbeatTimer != nil) {
|
|
337
|
+
[heartbeatTimer invalidate];
|
|
338
|
+
heartbeatTimer = nil;
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
- (void) onHeartbeatTick:(NSTimer *)timer
|
|
343
|
+
{
|
|
344
|
+
NSDictionary *userInfo = lastReceivedLocation != nil
|
|
345
|
+
? @{ @"location": lastReceivedLocation }
|
|
346
|
+
: @{};
|
|
347
|
+
[[NSNotificationCenter defaultCenter] postNotificationName:MAURHeartbeatNotification
|
|
348
|
+
object:self
|
|
349
|
+
userInfo:userInfo];
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
#pragma mark - v4.0 Phase 6 driver-insights state machine
|
|
353
|
+
|
|
354
|
+
- (void) drivingDetectorReset
|
|
355
|
+
{
|
|
356
|
+
drIsMoving = NO;
|
|
357
|
+
drTripActive = NO;
|
|
358
|
+
drTripStartedAt = 0;
|
|
359
|
+
drTripDistanceMeters = 0;
|
|
360
|
+
drHasPrev = NO;
|
|
361
|
+
drAboveTripSpeedSince = 0;
|
|
362
|
+
drBelowMovingSince = 0;
|
|
363
|
+
drWasSpeeding = NO;
|
|
364
|
+
drLastProvider = nil;
|
|
365
|
+
drPrevSpeed = 0;
|
|
366
|
+
drPrevSpeedAt = 0;
|
|
367
|
+
drPrevBearing = 0;
|
|
368
|
+
drHasPrevBearing = NO;
|
|
369
|
+
drPrevBearingAt = 0;
|
|
370
|
+
drLastHardBrakeAt = drLastRapidAccelAt = drLastSharpTurnAt = drLastCrashAt = 0;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
#pragma mark - v4.2 Phase 8 sensor fusion
|
|
374
|
+
|
|
375
|
+
- (void) configureSensorFusion
|
|
376
|
+
{
|
|
377
|
+
NSDictionary *de = _config.drivingEvents;
|
|
378
|
+
BOOL want = [de isKindOfClass:[NSDictionary class]]
|
|
379
|
+
&& [de[@"enabled"] boolValue]
|
|
380
|
+
&& [de[@"sensorFusion"] boolValue];
|
|
381
|
+
if (!want) {
|
|
382
|
+
[sensorFusion stop];
|
|
383
|
+
sensorFusion = nil;
|
|
384
|
+
return;
|
|
385
|
+
}
|
|
386
|
+
if (sensorFusion == nil) {
|
|
387
|
+
sensorFusion = [[MAURSensorFusionDetector alloc] init];
|
|
388
|
+
sensorFusion.listener = self;
|
|
389
|
+
}
|
|
390
|
+
sensorFusion.enabled = YES;
|
|
391
|
+
if (de[@"crashImpactG"]) sensorFusion.crashImpactG = [de[@"crashImpactG"] doubleValue];
|
|
392
|
+
if (de[@"sensorCrashCooldownMs"]) sensorFusion.crashCooldownMs = [de[@"sensorCrashCooldownMs"] doubleValue];
|
|
393
|
+
if (de[@"phoneUsageWindowMs"]) sensorFusion.phoneUsageWindowMs = [de[@"phoneUsageWindowMs"] doubleValue];
|
|
394
|
+
if (de[@"phoneUsageCooldownMs"]) sensorFusion.phoneUsageCooldownMs = [de[@"phoneUsageCooldownMs"] doubleValue];
|
|
395
|
+
// v4.2 hot-reload: re-inject current trip state + last location so a config change
|
|
396
|
+
// mid-trip starts the sensor pipeline in the correct mode.
|
|
397
|
+
sensorFusion.tripActive = drTripActive;
|
|
398
|
+
sensorFusion.lastLocation = lastReceivedLocation;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
// MAURSensorFusionListener
|
|
402
|
+
- (void) onSensorCrashWithImpactG:(double)impactG location:(MAURLocation *)location
|
|
403
|
+
{
|
|
404
|
+
NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
|
|
405
|
+
if (location != nil) userInfo[@"location"] = location;
|
|
406
|
+
userInfo[@"value"] = @(impactG);
|
|
407
|
+
userInfo[@"source"] = @"sensor";
|
|
408
|
+
[[NSNotificationCenter defaultCenter] postNotificationName:MAURPossibleCrashNotification
|
|
409
|
+
object:self
|
|
410
|
+
userInfo:userInfo];
|
|
411
|
+
}
|
|
412
|
+
- (void) onPhoneUsageWhileDriving:(MAURLocation *)location
|
|
413
|
+
{
|
|
414
|
+
NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
|
|
415
|
+
if (location != nil) userInfo[@"location"] = location;
|
|
416
|
+
[[NSNotificationCenter defaultCenter] postNotificationName:MAURPhoneUsageWhileDrivingNotification
|
|
417
|
+
object:self
|
|
418
|
+
userInfo:userInfo];
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
- (void) drivingDetectorFeed:(MAURLocation *)loc
|
|
422
|
+
{
|
|
423
|
+
if (loc == nil || _config == nil) return;
|
|
424
|
+
BOOL enabled = NO;
|
|
425
|
+
double speedLimit = 0;
|
|
426
|
+
double minMovingSpeed = 1.0;
|
|
427
|
+
NSTimeInterval stoppedDuration = 60.0;
|
|
428
|
+
double minTripSpeed = 3.0;
|
|
429
|
+
NSTimeInterval minTripDuration = 30.0;
|
|
430
|
+
NSDictionary *de = [_config valueForKey:@"drivingEvents"]; // see MAURConfig: provided as NSDictionary
|
|
431
|
+
if ([de isKindOfClass:[NSDictionary class]]) {
|
|
432
|
+
enabled = [[de objectForKey:@"enabled"] boolValue];
|
|
433
|
+
speedLimit = [[de objectForKey:@"speedLimit"] doubleValue];
|
|
434
|
+
if ([de objectForKey:@"minMovingSpeed"]) minMovingSpeed = [[de objectForKey:@"minMovingSpeed"] doubleValue];
|
|
435
|
+
if ([de objectForKey:@"stoppedDuration"]) stoppedDuration = [[de objectForKey:@"stoppedDuration"] doubleValue] / 1000.0;
|
|
436
|
+
if ([de objectForKey:@"minTripSpeed"]) minTripSpeed = [[de objectForKey:@"minTripSpeed"] doubleValue];
|
|
437
|
+
if ([de objectForKey:@"minTripDuration"]) minTripDuration = [[de objectForKey:@"minTripDuration"] doubleValue] / 1000.0;
|
|
438
|
+
}
|
|
439
|
+
if (!enabled) return;
|
|
440
|
+
|
|
441
|
+
NSTimeInterval now = [[NSDate date] timeIntervalSince1970];
|
|
442
|
+
double speed = loc.speed != nil ? [loc.speed doubleValue] : 0.0;
|
|
443
|
+
if (speed < 0) speed = 0;
|
|
444
|
+
|
|
445
|
+
// Provider change
|
|
446
|
+
NSString *provider = loc.provider;
|
|
447
|
+
if (provider != nil && ![provider isEqualToString:drLastProvider]) {
|
|
448
|
+
drLastProvider = provider;
|
|
449
|
+
[[NSNotificationCenter defaultCenter] postNotificationName:MAURProviderChangeNotification
|
|
450
|
+
object:self
|
|
451
|
+
userInfo:@{@"provider": provider}];
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
double curLat = [loc.latitude doubleValue];
|
|
455
|
+
double curLon = [loc.longitude doubleValue];
|
|
456
|
+
if (drHasPrev && drTripActive) {
|
|
457
|
+
drTripDistanceMeters += [self drHaversineFromLat:drPrevLat lon:drPrevLon toLat:curLat lon:curLon];
|
|
458
|
+
}
|
|
459
|
+
drPrevLat = curLat;
|
|
460
|
+
drPrevLon = curLon;
|
|
461
|
+
drHasPrev = YES;
|
|
462
|
+
|
|
463
|
+
BOOL nowMoving = speed >= minMovingSpeed;
|
|
464
|
+
if (nowMoving) {
|
|
465
|
+
drBelowMovingSince = 0;
|
|
466
|
+
if (!drIsMoving) {
|
|
467
|
+
drIsMoving = YES;
|
|
468
|
+
[[NSNotificationCenter defaultCenter] postNotificationName:MAURMovingNotification
|
|
469
|
+
object:self
|
|
470
|
+
userInfo:@{@"location": loc}];
|
|
471
|
+
}
|
|
472
|
+
if (!drTripActive) {
|
|
473
|
+
if (speed >= minTripSpeed) {
|
|
474
|
+
if (drAboveTripSpeedSince == 0) drAboveTripSpeedSince = now;
|
|
475
|
+
if (now - drAboveTripSpeedSince >= minTripDuration) {
|
|
476
|
+
drTripActive = YES;
|
|
477
|
+
drTripStartedAt = now;
|
|
478
|
+
drTripDistanceMeters = 0;
|
|
479
|
+
[[NSNotificationCenter defaultCenter] postNotificationName:MAURTripStartNotification
|
|
480
|
+
object:self
|
|
481
|
+
userInfo:@{@"location": loc}];
|
|
482
|
+
sensorFusion.tripActive = YES;
|
|
483
|
+
}
|
|
484
|
+
} else {
|
|
485
|
+
drAboveTripSpeedSince = 0;
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
} else {
|
|
489
|
+
drAboveTripSpeedSince = 0;
|
|
490
|
+
if (drBelowMovingSince == 0) drBelowMovingSince = now;
|
|
491
|
+
if (drIsMoving && (now - drBelowMovingSince) >= stoppedDuration) {
|
|
492
|
+
drIsMoving = NO;
|
|
493
|
+
[[NSNotificationCenter defaultCenter] postNotificationName:MAURStoppedNotification
|
|
494
|
+
object:self
|
|
495
|
+
userInfo:@{@"location": loc}];
|
|
496
|
+
if (drTripActive) {
|
|
497
|
+
NSTimeInterval durMs = (now - drTripStartedAt) * 1000.0;
|
|
498
|
+
double dist = drTripDistanceMeters;
|
|
499
|
+
drTripActive = NO;
|
|
500
|
+
[[NSNotificationCenter defaultCenter] postNotificationName:MAURTripEndNotification
|
|
501
|
+
object:self
|
|
502
|
+
userInfo:@{
|
|
503
|
+
@"location": loc,
|
|
504
|
+
@"distance": @(dist),
|
|
505
|
+
@"durationMs": @((long long)durMs)
|
|
506
|
+
}];
|
|
507
|
+
sensorFusion.tripActive = NO;
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
if (speedLimit > 0) {
|
|
513
|
+
double kmh = speed * 3.6;
|
|
514
|
+
if (kmh > speedLimit) {
|
|
515
|
+
if (!drWasSpeeding) {
|
|
516
|
+
drWasSpeeding = YES;
|
|
517
|
+
[[NSNotificationCenter defaultCenter] postNotificationName:MAURSpeedingNotification
|
|
518
|
+
object:self
|
|
519
|
+
userInfo:@{
|
|
520
|
+
@"location": loc,
|
|
521
|
+
@"speedKmh": @(kmh),
|
|
522
|
+
@"limitKmh": @(speedLimit)
|
|
523
|
+
}];
|
|
524
|
+
}
|
|
525
|
+
} else {
|
|
526
|
+
drWasSpeeding = NO;
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
// v4.1 GPS-derived sensor-like events
|
|
531
|
+
double hardBrakeMps2 = 3.5, rapidAccelMps2 = 3.5, sharpTurnDegPerSec = 30, crashImpactKmh = 25;
|
|
532
|
+
NSTimeInterval crashWindow = 2.0;
|
|
533
|
+
if ([de isKindOfClass:[NSDictionary class]]) {
|
|
534
|
+
if ([de objectForKey:@"hardBrakeMps2"]) hardBrakeMps2 = [[de objectForKey:@"hardBrakeMps2"] doubleValue];
|
|
535
|
+
if ([de objectForKey:@"rapidAccelMps2"]) rapidAccelMps2 = [[de objectForKey:@"rapidAccelMps2"] doubleValue];
|
|
536
|
+
if ([de objectForKey:@"sharpTurnDegPerSec"]) sharpTurnDegPerSec = [[de objectForKey:@"sharpTurnDegPerSec"] doubleValue];
|
|
537
|
+
if ([de objectForKey:@"crashImpactKmh"]) crashImpactKmh = [[de objectForKey:@"crashImpactKmh"] doubleValue];
|
|
538
|
+
if ([de objectForKey:@"crashWindowMs"]) crashWindow = [[de objectForKey:@"crashWindowMs"] doubleValue] / 1000.0;
|
|
539
|
+
}
|
|
540
|
+
static const NSTimeInterval kCooldown = 4.0;
|
|
541
|
+
|
|
542
|
+
if (drTripActive && drPrevSpeedAt > 0) {
|
|
543
|
+
NSTimeInterval dt = now - drPrevSpeedAt;
|
|
544
|
+
if (dt > 0 && dt <= 5.0) {
|
|
545
|
+
double dv = speed - drPrevSpeed;
|
|
546
|
+
double accel = dv / dt;
|
|
547
|
+
if (hardBrakeMps2 > 0 && accel <= -hardBrakeMps2 && (now - drLastHardBrakeAt) >= kCooldown) {
|
|
548
|
+
drLastHardBrakeAt = now;
|
|
549
|
+
[[NSNotificationCenter defaultCenter] postNotificationName:MAURHardBrakeNotification
|
|
550
|
+
object:self
|
|
551
|
+
userInfo:@{@"location": loc, @"value": @(accel)}];
|
|
552
|
+
}
|
|
553
|
+
if (rapidAccelMps2 > 0 && accel >= rapidAccelMps2 && (now - drLastRapidAccelAt) >= kCooldown) {
|
|
554
|
+
drLastRapidAccelAt = now;
|
|
555
|
+
[[NSNotificationCenter defaultCenter] postNotificationName:MAURRapidAccelerationNotification
|
|
556
|
+
object:self
|
|
557
|
+
userInfo:@{@"location": loc, @"value": @(accel)}];
|
|
558
|
+
}
|
|
559
|
+
if (crashImpactKmh > 0 && dt <= crashWindow) {
|
|
560
|
+
double dropKmh = (drPrevSpeed - speed) * 3.6;
|
|
561
|
+
if (dropKmh >= crashImpactKmh
|
|
562
|
+
&& speed < 1.5
|
|
563
|
+
&& drPrevSpeed * 3.6 >= crashImpactKmh
|
|
564
|
+
&& (now - drLastCrashAt) >= kCooldown) {
|
|
565
|
+
drLastCrashAt = now;
|
|
566
|
+
[[NSNotificationCenter defaultCenter] postNotificationName:MAURPossibleCrashNotification
|
|
567
|
+
object:self
|
|
568
|
+
userInfo:@{@"location": loc, @"value": @(dropKmh), @"source": @"gps"}];
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
// Sharp turn (bearing rate)
|
|
575
|
+
if (sharpTurnDegPerSec > 0 && loc.heading != nil && speed >= 5.0 && drHasPrevBearing) {
|
|
576
|
+
NSTimeInterval dt = now - drPrevBearingAt;
|
|
577
|
+
if (dt > 0 && dt <= 5.0) {
|
|
578
|
+
double bearing = [loc.heading doubleValue];
|
|
579
|
+
double diff = fabs(bearing - drPrevBearing);
|
|
580
|
+
if (diff > 180) diff = 360 - diff;
|
|
581
|
+
double rate = diff / dt;
|
|
582
|
+
if (rate >= sharpTurnDegPerSec && (now - drLastSharpTurnAt) >= kCooldown) {
|
|
583
|
+
drLastSharpTurnAt = now;
|
|
584
|
+
[[NSNotificationCenter defaultCenter] postNotificationName:MAURSharpTurnNotification
|
|
585
|
+
object:self
|
|
586
|
+
userInfo:@{@"location": loc, @"value": @(rate)}];
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
drPrevBearing = [loc.heading doubleValue];
|
|
590
|
+
drPrevBearingAt = now;
|
|
591
|
+
} else if (loc.heading != nil) {
|
|
592
|
+
drPrevBearing = [loc.heading doubleValue];
|
|
593
|
+
drPrevBearingAt = now;
|
|
594
|
+
drHasPrevBearing = YES;
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
drPrevSpeed = speed;
|
|
598
|
+
drPrevSpeedAt = now;
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
- (double) drHaversineFromLat:(double)lat1 lon:(double)lon1 toLat:(double)lat2 lon:(double)lon2
|
|
602
|
+
{
|
|
603
|
+
const double R = 6371000.0;
|
|
604
|
+
double dLat = (lat2 - lat1) * M_PI / 180.0;
|
|
605
|
+
double dLon = (lon2 - lon1) * M_PI / 180.0;
|
|
606
|
+
double a = sin(dLat/2) * sin(dLat/2)
|
|
607
|
+
+ cos(lat1 * M_PI / 180.0) * cos(lat2 * M_PI / 180.0)
|
|
608
|
+
* sin(dLon/2) * sin(dLon/2);
|
|
609
|
+
return 2 * R * asin(sqrt(a));
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
- (void) triggerSOS:(NSDictionary *)payload
|
|
613
|
+
{
|
|
614
|
+
NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
|
|
615
|
+
if (lastReceivedLocation != nil) userInfo[@"location"] = lastReceivedLocation;
|
|
616
|
+
userInfo[@"payload"] = payload != nil ? payload : @{};
|
|
617
|
+
[[NSNotificationCenter defaultCenter] postNotificationName:MAURSOSNotification
|
|
618
|
+
object:self
|
|
619
|
+
userInfo:userInfo];
|
|
620
|
+
}
|
|
621
|
+
|
|
242
622
|
/**
|
|
243
623
|
* toggle between foreground and background operation mode
|
|
244
624
|
*/
|
|
@@ -358,6 +738,26 @@ FMDBLogger *sqliteLogger;
|
|
|
358
738
|
return [locationDAO getLocationsForSync];
|
|
359
739
|
}
|
|
360
740
|
|
|
741
|
+
- (void) startSession
|
|
742
|
+
{
|
|
743
|
+
[[MAURSessionLocationDAO sharedInstance] startSession];
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
- (NSArray<MAURLocation*>*) getSessionLocations
|
|
747
|
+
{
|
|
748
|
+
return [[MAURSessionLocationDAO sharedInstance] getSessionLocations];
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
- (void) clearSession
|
|
752
|
+
{
|
|
753
|
+
[[MAURSessionLocationDAO sharedInstance] clearSession];
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
- (NSInteger) getSessionLocationsCount
|
|
757
|
+
{
|
|
758
|
+
return [[MAURSessionLocationDAO sharedInstance] getSessionLocationsCount];
|
|
759
|
+
}
|
|
760
|
+
|
|
361
761
|
- (BOOL) deleteLocation:(NSNumber*)locationId error:(NSError * __autoreleasing *)outError
|
|
362
762
|
{
|
|
363
763
|
MAURSQLiteLocationDAO* locationDAO = [MAURSQLiteLocationDAO sharedInstance];
|
|
@@ -544,7 +944,13 @@ FMDBLogger *sqliteLogger;
|
|
|
544
944
|
{
|
|
545
945
|
DDLogDebug(@"%@ #onLocationChanged %@", TAG, location);
|
|
546
946
|
stationaryLocation = nil;
|
|
547
|
-
|
|
947
|
+
lastReceivedLocation = location; // v3.5 Phase 4: cached for heartbeat payload
|
|
948
|
+
|
|
949
|
+
// v4.0 Phase 6: feed driver-insights state machine.
|
|
950
|
+
[self drivingDetectorFeed:location];
|
|
951
|
+
// v4.2 Phase 8: keep sensor pipeline aware of the latest fix.
|
|
952
|
+
sensorFusion.lastLocation = location;
|
|
953
|
+
|
|
548
954
|
[postLocationTask add:location];
|
|
549
955
|
|
|
550
956
|
MAURConfig *config = [self getConfig];
|
|
@@ -12,11 +12,22 @@
|
|
|
12
12
|
|
|
13
13
|
@class MAURBackgroundSync;
|
|
14
14
|
|
|
15
|
+
// v3.5 Phase 4: notification names for sync events. The plugin layer observes them
|
|
16
|
+
// via NSNotificationCenter to forward into JS as syncStart / syncSuccess / syncError / syncProgress.
|
|
17
|
+
extern NSString * _Nonnull const MAURBackgroundSyncDidStartNotification;
|
|
18
|
+
extern NSString * _Nonnull const MAURBackgroundSyncDidSucceedNotification;
|
|
19
|
+
extern NSString * _Nonnull const MAURBackgroundSyncDidFailNotification;
|
|
20
|
+
extern NSString * _Nonnull const MAURBackgroundSyncDidProgressNotification;
|
|
21
|
+
|
|
15
22
|
@protocol MAURBackgroundSyncDelegate <NSObject>
|
|
16
23
|
|
|
17
24
|
@optional
|
|
18
25
|
- (void)backgroundSyncRequestedAbortUpdates:(MAURBackgroundSync * _Nonnull)task;
|
|
19
26
|
- (void)backgroundSyncHttpAuthorizationUpdates:(MAURBackgroundSync * _Nonnull)task;
|
|
27
|
+
// v3.5 Phase 4
|
|
28
|
+
- (void)backgroundSyncStarted:(MAURBackgroundSync * _Nonnull)task;
|
|
29
|
+
- (void)backgroundSyncSucceeded:(MAURBackgroundSync * _Nonnull)task locationsSent:(NSInteger)locationsSent;
|
|
30
|
+
- (void)backgroundSyncFailed:(MAURBackgroundSync * _Nonnull)task httpStatus:(NSInteger)httpStatus message:(NSString * _Nullable)message;
|
|
20
31
|
|
|
21
32
|
@end
|
|
22
33
|
|
|
@@ -27,6 +38,7 @@
|
|
|
27
38
|
- (instancetype) init;
|
|
28
39
|
- (NSString*) status;
|
|
29
40
|
- (void) sync:(NSString * _Nonnull)url withTemplate:(id)locationTemplate withHttpHeaders:(NSMutableDictionary * _Nullable)httpHeaders;
|
|
41
|
+
- (void) sync:(NSString * _Nonnull)url withTemplate:(id)locationTemplate withHttpHeaders:(NSMutableDictionary * _Nullable)httpHeaders withMethod:(NSString * _Nullable)method;
|
|
30
42
|
- (void) cancel;
|
|
31
43
|
|
|
32
44
|
@end
|