@josuelmm/cordova-background-geolocation 4.2.3 → 4.5.2

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.
Files changed (103) hide show
  1. package/.npmignore +11 -0
  2. package/CHANGELOG.md +261 -0
  3. package/README.md +306 -115
  4. package/android/CDVBackgroundGeolocation/src/main/java/com/marianhello/bgloc/cordova/ConfigMapper.java +34 -0
  5. package/android/CDVBackgroundGeolocation/src/main/java/com/tenforwardconsulting/bgloc/cordova/BackgroundGeolocationPlugin.java +61 -1
  6. package/android/common/src/main/AndroidManifest.xml +1 -1
  7. package/android/common/src/main/java/com/marianhello/bgloc/BootCompletedReceiver.java +20 -3
  8. package/android/common/src/main/java/com/marianhello/bgloc/Config.java +87 -1
  9. package/android/common/src/main/java/com/marianhello/bgloc/data/BackgroundLocation.java +94 -0
  10. package/android/common/src/main/java/com/marianhello/bgloc/data/ConfigJsonMapper.java +211 -0
  11. package/android/common/src/main/java/com/marianhello/bgloc/data/LocationTemplateFactory.java +6 -0
  12. package/android/common/src/main/java/com/marianhello/bgloc/data/sqlite/SQLiteConfigurationContract.java +5 -1
  13. package/android/common/src/main/java/com/marianhello/bgloc/data/sqlite/SQLiteConfigurationDAO.java +32 -1
  14. package/android/common/src/main/java/com/marianhello/bgloc/data/sqlite/SQLiteLocationContract.java +12 -2
  15. package/android/common/src/main/java/com/marianhello/bgloc/data/sqlite/SQLiteLocationDAO.java +33 -2
  16. package/android/common/src/main/java/com/marianhello/bgloc/data/sqlite/SQLiteOpenHelper.java +15 -1
  17. package/android/common/src/main/java/com/marianhello/bgloc/provider/AbstractLocationProvider.java +48 -1
  18. package/android/common/src/main/java/com/marianhello/bgloc/provider/ActivityRecognitionLocationProvider.java +105 -6
  19. package/android/common/src/main/java/com/marianhello/bgloc/provider/DistanceFilterLocationProvider.java +336 -250
  20. package/android/common/src/main/java/com/marianhello/bgloc/provider/RawLocationProvider.java +69 -19
  21. package/android/common/src/main/java/com/marianhello/bgloc/service/LocationServiceImpl.java +246 -21
  22. package/android/common/src/main/java/com/marianhello/bgloc/service/LocationServiceProxy.java +5 -2
  23. package/android/common/src/main/java/com/marianhello/bgloc/sync/BatchManager.java +46 -13
  24. package/ios/CDVBackgroundGeolocation/CDVBackgroundGeolocation.m +23 -1
  25. package/ios/common/BackgroundGeolocation/MAURActivityLocationProvider.m +208 -70
  26. package/ios/common/BackgroundGeolocation/MAURBackgroundGeolocationFacade.m +132 -5
  27. package/ios/common/BackgroundGeolocation/MAURBackgroundSync.m +20 -0
  28. package/ios/common/BackgroundGeolocation/MAURConfig.h +7 -0
  29. package/ios/common/BackgroundGeolocation/MAURConfig.m +37 -2
  30. package/ios/common/BackgroundGeolocation/MAURConfigurationContract.h +3 -0
  31. package/ios/common/BackgroundGeolocation/MAURConfigurationContract.m +3 -1
  32. package/ios/common/BackgroundGeolocation/MAURDistanceFilterLocationProvider.m +10 -1
  33. package/ios/common/BackgroundGeolocation/MAURGeolocationOpenHelper.m +15 -1
  34. package/ios/common/BackgroundGeolocation/MAURLocation.h +12 -0
  35. package/ios/common/BackgroundGeolocation/MAURLocation.m +33 -4
  36. package/ios/common/BackgroundGeolocation/MAURLocationContract.h +4 -0
  37. package/ios/common/BackgroundGeolocation/MAURLocationContract.m +5 -1
  38. package/ios/common/BackgroundGeolocation/MAURLocationManager.m +19 -1
  39. package/ios/common/BackgroundGeolocation/MAURPostLocationTask.h +9 -0
  40. package/ios/common/BackgroundGeolocation/MAURPostLocationTask.m +59 -1
  41. package/ios/common/BackgroundGeolocation/MAURRawLocationProvider.m +10 -1
  42. package/ios/common/BackgroundGeolocation/MAURSQLiteConfigurationDAO.m +54 -4
  43. package/ios/common/BackgroundGeolocation/MAURSQLiteLocationDAO.h +12 -0
  44. package/ios/common/BackgroundGeolocation/MAURSQLiteLocationDAO.m +125 -5
  45. package/package.json +31 -1
  46. package/plugin.xml +3 -10
  47. package/www/BackgroundGeolocation.d.ts +143 -3
  48. package/www/BackgroundGeolocation.js +11 -4
  49. package/CLAUDE.md +0 -56
  50. package/HISTORY.md +0 -871
  51. package/android/CDVBackgroundGeolocation/src/test/java/com/marianhello/ConfigMapperTest.java +0 -220
  52. package/android/common/src/androidTest/java/com/marianhello/bgloc/BackgroundGeolocationFacadeTest.java +0 -45
  53. package/android/common/src/androidTest/java/com/marianhello/bgloc/BatchManagerTest.java +0 -570
  54. package/android/common/src/androidTest/java/com/marianhello/bgloc/ConfigTest.java +0 -76
  55. package/android/common/src/androidTest/java/com/marianhello/bgloc/ContentProviderLocationDAOTest.java +0 -437
  56. package/android/common/src/androidTest/java/com/marianhello/bgloc/DBLogReaderTest.java +0 -95
  57. package/android/common/src/androidTest/java/com/marianhello/bgloc/LocationContentProviderTest.java +0 -159
  58. package/android/common/src/androidTest/java/com/marianhello/bgloc/LocationServiceProxyTest.java +0 -161
  59. package/android/common/src/androidTest/java/com/marianhello/bgloc/LocationServiceTest.java +0 -247
  60. package/android/common/src/androidTest/java/com/marianhello/bgloc/SQLiteConfigurationDAOTest.java +0 -200
  61. package/android/common/src/androidTest/java/com/marianhello/bgloc/SQLiteLocationDAOTest.java +0 -457
  62. package/android/common/src/androidTest/java/com/marianhello/bgloc/SQLiteLocationDAOThreadTest.java +0 -96
  63. package/android/common/src/androidTest/java/com/marianhello/bgloc/SQLiteOpenHelperTest.java +0 -225
  64. package/android/common/src/androidTest/java/com/marianhello/bgloc/TestPluginDelegate.java +0 -46
  65. package/android/common/src/androidTest/java/com/marianhello/bgloc/TestResourceResolver.java +0 -14
  66. package/android/common/src/androidTest/java/com/marianhello/bgloc/provider/MockLocationProvider.java +0 -50
  67. package/android/common/src/androidTest/java/com/marianhello/bgloc/provider/TestLocationProviderFactory.java +0 -17
  68. package/android/common/src/androidTest/java/com/marianhello/bgloc/sqlite/SQLiteOpenHelper10.java +0 -92
  69. package/android/common/src/androidTest/java/com/marianhello/bgloc/test/LocationProviderTestCase.java +0 -107
  70. package/android/common/src/androidTest/java/com/marianhello/bgloc/test/TestConstants.java +0 -5
  71. package/android/common/src/test/java/com/marianhello/backgroundgeolocation/ArrayListLocationTemplateTest.java +0 -82
  72. package/android/common/src/test/java/com/marianhello/backgroundgeolocation/BackgroundLocationTest.java +0 -128
  73. package/android/common/src/test/java/com/marianhello/backgroundgeolocation/ConfigTest.java +0 -191
  74. package/android/common/src/test/java/com/marianhello/backgroundgeolocation/DBLogReaderTest.java +0 -37
  75. package/android/common/src/test/java/com/marianhello/backgroundgeolocation/HashMapLocationTemplateTest.java +0 -216
  76. package/android/common/src/test/java/com/marianhello/backgroundgeolocation/HttpPostServiceTest.java +0 -223
  77. package/android/common/src/test/java/com/marianhello/backgroundgeolocation/LocationTemplateFactoryTest.java +0 -50
  78. package/android/common/src/test/java/com/marianhello/backgroundgeolocation/PostLocationTaskTest.java +0 -180
  79. package/android/common/src/test/java/com/marianhello/backgroundgeolocation/TestHelper.java +0 -16
  80. package/ios/common/BackgroundGeolocation/SOMotionDetector/CHANGELOG.md +0 -2
  81. package/ios/common/BackgroundGeolocation/SOMotionDetector/LICENSE +0 -21
  82. package/ios/common/BackgroundGeolocation/SOMotionDetector/README.md +0 -135
  83. package/ios/common/BackgroundGeolocation/SOMotionDetector/SOLocationManager.h +0 -80
  84. package/ios/common/BackgroundGeolocation/SOMotionDetector/SOLocationManager.m +0 -147
  85. package/ios/common/BackgroundGeolocation/SOMotionDetector/SOMotionActivity.h +0 -30
  86. package/ios/common/BackgroundGeolocation/SOMotionDetector/SOMotionActivity.m +0 -42
  87. package/ios/common/BackgroundGeolocation/SOMotionDetector/SOMotionDetector.h +0 -99
  88. package/ios/common/BackgroundGeolocation/SOMotionDetector/SOMotionDetector.m +0 -327
  89. package/ios/common/BackgroundGeolocation/SOMotionDetector/SOStepDetector.h +0 -44
  90. package/ios/common/BackgroundGeolocation/SOMotionDetector/SOStepDetector.m +0 -94
  91. package/ios/common/BackgroundGeolocationTests/Info.plist +0 -24
  92. package/ios/common/BackgroundGeolocationTests/MAURBackgroundLocationTest.m +0 -185
  93. package/ios/common/BackgroundGeolocationTests/MAURConfigTest.m +0 -161
  94. package/ios/common/BackgroundGeolocationTests/MAURGeolocationOpenHelperTest.m +0 -102
  95. package/ios/common/BackgroundGeolocationTests/MAURLocationTest.m +0 -216
  96. package/ios/common/BackgroundGeolocationTests/MAURLocationUploaderTest.m +0 -55
  97. package/ios/common/BackgroundGeolocationTests/MAURLogReaderTest.m +0 -43
  98. package/ios/common/BackgroundGeolocationTests/MAURSQLiteConfigurationDAOTest.m +0 -102
  99. package/ios/common/BackgroundGeolocationTests/MAURSQLiteHelperTest.m +0 -41
  100. package/ios/common/BackgroundGeolocationTests/MAURSQLiteLocationDAOTests.m +0 -240
  101. package/ios/common/BackgroundGeolocationTests/MAURSQLiteLocationDAOThreadTest.m +0 -84
  102. package/ios/common/BackgroundGeolocationTests/MAURSQLiteOpenHelperTest.m +0 -144
  103. package/ios/common/scripts/xcode-refactor.js +0 -184
@@ -30,6 +30,15 @@
30
30
 
31
31
  @property (nonatomic, weak) MAURConfig * _Nullable config;
32
32
  @property (nonatomic, weak) id<MAURPostLocationTaskDelegate> _Nullable delegate;
33
+ /** v4.5.1 — pending driving events buffer owned by the facade; the task drains it onto the
34
+ * post-transform location so events fired without a simultaneous fix (provider change,
35
+ * sensor crash, phone usage) survive even if `locationTransform` returns a new instance.
36
+ * Weak ref: if the facade is gone, no flush — by design. */
37
+ @property (nonatomic, weak) NSMutableArray * _Nullable pendingDrivingEventsBuffer;
38
+ /** v4.5.1 — same idea for the battery snapshot block. The facade installs a block that the
39
+ * task invokes AFTER a successful transform, so even when `locationTransform` returns a
40
+ * fresh instance, battery/charging fields land on what actually gets POSTed. */
41
+ @property (nonatomic, copy) void (^ _Nullable attachBatterySnapshot)(MAURLocation * _Nonnull);
33
42
 
34
43
  - (void) add:(MAURLocation * _Nonnull)location;
35
44
  - (void) start;
@@ -83,11 +83,63 @@ static MAURLocationTransform s_locationTransform = nil;
83
83
  MAURLocation *location = inLocation;
84
84
 
85
85
  if (locationTransform != nil) {
86
+ // v4.5.1 — snapshot v4.3+ fields BEFORE transform so they survive a transform
87
+ // that returns a brand new MAURLocation instance (otherwise events/battery would
88
+ // be lost en route to SQLite / backend).
89
+ NSMutableArray *rawEvents = inLocation.drivingEvents;
90
+ NSNumber *rawBattery = inLocation.batteryLevel;
91
+ NSNumber *rawCharging = inLocation.isCharging;
92
+
86
93
  location = locationTransform(location);
87
94
 
88
95
  if (location == nil) {
89
96
  return;
90
97
  }
98
+
99
+ // v4.5.1 — re-attach fields the transform may have dropped. When the transform
100
+ // produced a NEW instance (`location != inLocation`), MERGE rawEvents into the new
101
+ // instance's array instead of overwriting — same semantics as Android
102
+ // `LocationServiceImpl.onLocation` re-attach. If the transform returned the same
103
+ // instance (mutated in place) the rawEvents are already there.
104
+ if (location != inLocation) {
105
+ if (rawEvents != nil && [rawEvents count] > 0) {
106
+ if (location.drivingEvents == nil) {
107
+ location.drivingEvents = [rawEvents mutableCopy];
108
+ } else {
109
+ [location.drivingEvents addObjectsFromArray:rawEvents];
110
+ }
111
+ }
112
+ if (location.batteryLevel == nil) location.batteryLevel = rawBattery;
113
+ if (location.isCharging == nil) location.isCharging = rawCharging;
114
+ }
115
+ }
116
+
117
+ // v4.5.1 — drain pending driving events ONTO the post-transform location. Previously
118
+ // the facade drained them BEFORE [postLocationTask add:], so a transform that returned
119
+ // nil silently lost every buffered event. Now: if transform succeeded we're guaranteed
120
+ // `location != nil` here and the buffer is drained safely.
121
+ NSMutableArray *pendingBuffer = self.pendingDrivingEventsBuffer;
122
+ if (pendingBuffer != nil) {
123
+ @synchronized (pendingBuffer) {
124
+ if ([pendingBuffer count] > 0) {
125
+ NSTimeInterval nowMs = [[NSDate date] timeIntervalSince1970] * 1000.0;
126
+ if (location.drivingEvents == nil) location.drivingEvents = [NSMutableArray array];
127
+ for (NSDictionary *ev in pendingBuffer) {
128
+ NSNumber *t = ev[@"time"];
129
+ NSTimeInterval evMs = t != nil ? [t doubleValue] : nowMs;
130
+ if (nowMs - evMs <= 60000.0) {
131
+ [location.drivingEvents addObject:ev];
132
+ }
133
+ }
134
+ [pendingBuffer removeAllObjects];
135
+ }
136
+ }
137
+ }
138
+ // v4.5.1 — stamp battery snapshot AFTER transform so it lands on the POSTed instance
139
+ // even if the transform created a new one.
140
+ void (^attachBattery)(MAURLocation *) = self.attachBatterySnapshot;
141
+ if (attachBattery != nil) {
142
+ attachBattery(location);
91
143
  }
92
144
 
93
145
  // v3.5 Phase 4: mock location policy. Detection already exists in MAURLocation.simulated.
@@ -255,7 +307,8 @@ static MAURLocationTransform s_locationTransform = nil;
255
307
  return YES;
256
308
  }
257
309
 
258
- if (*outError == nil) {
310
+ // v4.4.1: guard against outError == NULL (defensive — current callers pass &error).
311
+ if (outError == NULL || *outError == nil) {
259
312
  DDLogDebug(@"%@ Server error while posting locations responseCode: %ld", TAG, (long)statusCode);
260
313
  } else {
261
314
  DDLogError(@"%@ Error while posting locations %@", TAG, [*outError localizedDescription]);
@@ -269,6 +322,11 @@ static MAURLocationTransform s_locationTransform = nil;
269
322
  if (![self.config syncEnabled] || ![self.config hasValidSyncUrl]) {
270
323
  return;
271
324
  }
325
+ // v4.5.1 — rescue rows stuck in SyncPending from a previous upload that never completed
326
+ // (app/process killed mid-flight). Anything older than 15 min is safe to revert to
327
+ // PostPending; rows younger than that may still be uploading on a background NSURLSession.
328
+ NSTimeInterval staleCutoff = [[NSDate date] timeIntervalSince1970] - (15 * 60);
329
+ [[MAURSQLiteLocationDAO sharedInstance] restoreStaleSyncLocationsOlderThan:staleCutoff error:nil];
272
330
  // For sync (batch) only static queryParams placeholders apply; per-location templating
273
331
  // belongs in real-time post (httpMode="single" + httpMethod=GET) instead.
274
332
  NSString *resolvedSyncUrl = [MAURUrlTemplateResolver resolve:self.config.syncUrl location:nil queryParams:self.config.queryParams];
@@ -118,11 +118,20 @@ static NSString * const Domain = @"com.marianhello";
118
118
  - (void) onDestroy {
119
119
  DDLogInfo(@"Destroying %@ ", TAG);
120
120
  [self onStop:nil];
121
+
122
+ // v4.5.2: MAURLocationManager is a singleton shared with the other providers.
123
+ // Release our delegate slot so a later provider swap does not leave this
124
+ // (already destroyed) instance as the active delegate.
125
+ if (locationManager != nil && locationManager.delegate == self) {
126
+ locationManager.delegate = nil;
127
+ }
121
128
  }
122
129
 
123
130
  - (void) dealloc
124
131
  {
125
- // locationController.delegate = nil;
132
+ if (locationManager != nil && locationManager.delegate == self) {
133
+ locationManager.delegate = nil;
134
+ }
126
135
  }
127
136
 
128
137
  @end
@@ -88,7 +88,8 @@
88
88
  @COMMA_SEP @CC_COLUMN_NAME_PAUSE_LOCATION_UPDATES
89
89
  @COMMA_SEP @CC_COLUMN_NAME_TEMPLATE
90
90
  @COMMA_SEP @CC_COLUMN_NAME_LAST_UPDATED_AT
91
- @") VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,DateTime('now'))";
91
+ @COMMA_SEP @CC_COLUMN_NAME_CONFIG_JSON
92
+ @") VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,DateTime('now'),?)";
92
93
 
93
94
  [queue inDatabase:^(FMDatabase *database) {
94
95
  success = [database executeUpdate:sql,
@@ -119,7 +120,9 @@
119
120
  [config hasSaveBatteryOnBackground] ? config._saveBatteryOnBackground : @CC_COLUMN_NAME_NULLABLE,
120
121
  [config hasMaxLocations] ? config.maxLocations : @CC_COLUMN_NAME_NULLABLE,
121
122
  [config hasPauseLocationUpdates] ? config._pauseLocationUpdates : @CC_COLUMN_NAME_NULLABLE,
122
- (templateString != nil) ? templateString : @CC_COLUMN_NAME_NULLABLE
123
+ (templateString != nil) ? templateString : @CC_COLUMN_NAME_NULLABLE,
124
+ // v4.5: full Config as JSON for paridad con Android
125
+ [self serializeConfigToJson:config]
123
126
  ];
124
127
 
125
128
  if (success) {
@@ -165,6 +168,7 @@
165
168
  @COMMA_SEP @CC_COLUMN_NAME_MAX_LOCATIONS
166
169
  @COMMA_SEP @CC_COLUMN_NAME_PAUSE_LOCATION_UPDATES
167
170
  @COMMA_SEP @CC_COLUMN_NAME_TEMPLATE
171
+ @COMMA_SEP @CC_COLUMN_NAME_CONFIG_JSON
168
172
  @" FROM " @CC_TABLE_NAME @" WHERE " @CC_COLUMN_NAME_ID @" = 1";
169
173
 
170
174
  [queue inDatabase:^(FMDatabase *database) {
@@ -231,14 +235,60 @@
231
235
  config._template = [NSJSONSerialization JSONObjectWithData:jsonTemplate options:0 error:nil];
232
236
  }
233
237
  }
238
+ // v4.5: rehydrate post-3.2 keys from config_json blob (paridad Android).
239
+ // Index 28 is the new column. Strict NULL check (no NULLHACK sentinel for JSON column).
240
+ if (![rs columnIndexIsNull:28]) {
241
+ NSString *jsonString = [rs stringForColumnIndex:28];
242
+ if (jsonString != nil && jsonString.length > 0) {
243
+ [self applyConfigJson:jsonString to:config];
244
+ }
245
+ }
234
246
  }
235
-
247
+
236
248
  [rs close];
237
249
  }];
238
-
250
+
239
251
  return config;
240
252
  }
241
253
 
254
+ // v4.5: serialize all post-3.2 keys to JSON for storage. Mirrors Android ConfigJsonMapper.
255
+ - (NSString*) serializeConfigToJson:(MAURConfig*)config
256
+ {
257
+ NSMutableDictionary *j = [NSMutableDictionary dictionary];
258
+ if (config.httpMethod != nil) j[@"httpMethod"] = config.httpMethod;
259
+ if (config.syncHttpMethod != nil) j[@"syncHttpMethod"] = config.syncHttpMethod;
260
+ if (config.httpMode != nil) j[@"httpMode"] = config.httpMode;
261
+ if (config.syncMode != nil) j[@"syncMode"] = config.syncMode;
262
+ if (config.queryParams != nil) j[@"queryParams"] = config.queryParams;
263
+ if (config.heartbeatInterval != nil) j[@"heartbeatInterval"] = config.heartbeatInterval;
264
+ if (config.mockLocationPolicy != nil) j[@"mockLocationPolicy"] = config.mockLocationPolicy;
265
+ if (config.drivingEvents != nil) j[@"drivingEvents"] = config.drivingEvents;
266
+ if (config.includeBattery != nil) j[@"includeBattery"] = config.includeBattery;
267
+ if (config._showsBackgroundLocationIndicator != nil) j[@"showsBackgroundLocationIndicator"] = config._showsBackgroundLocationIndicator;
268
+ NSError *err = nil;
269
+ NSData *data = [NSJSONSerialization dataWithJSONObject:j options:0 error:&err];
270
+ if (err != nil || data == nil) return @"";
271
+ return [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
272
+ }
273
+
274
+ - (void) applyConfigJson:(NSString*)jsonString to:(MAURConfig*)config
275
+ {
276
+ NSData *data = [jsonString dataUsingEncoding:NSUTF8StringEncoding];
277
+ NSError *err = nil;
278
+ NSDictionary *j = [NSJSONSerialization JSONObjectWithData:data options:0 error:&err];
279
+ if (err != nil || ![j isKindOfClass:[NSDictionary class]]) return;
280
+ if (j[@"httpMethod"]) config.httpMethod = j[@"httpMethod"];
281
+ if (j[@"syncHttpMethod"]) config.syncHttpMethod = j[@"syncHttpMethod"];
282
+ if (j[@"httpMode"]) config.httpMode = j[@"httpMode"];
283
+ if (j[@"syncMode"]) config.syncMode = j[@"syncMode"];
284
+ if ([j[@"queryParams"] isKindOfClass:[NSDictionary class]]) config.queryParams = [j[@"queryParams"] mutableCopy];
285
+ if (j[@"heartbeatInterval"]) config.heartbeatInterval = j[@"heartbeatInterval"];
286
+ if (j[@"mockLocationPolicy"]) config.mockLocationPolicy = j[@"mockLocationPolicy"];
287
+ if ([j[@"drivingEvents"] isKindOfClass:[NSDictionary class]]) config.drivingEvents = j[@"drivingEvents"];
288
+ if (j[@"includeBattery"] != nil) config.includeBattery = j[@"includeBattery"];
289
+ if (j[@"showsBackgroundLocationIndicator"] != nil) config._showsBackgroundLocationIndicator = j[@"showsBackgroundLocationIndicator"];
290
+ }
291
+
242
292
  - (BOOL) clearDatabase
243
293
  {
244
294
  __block BOOL success;
@@ -28,6 +28,18 @@
28
28
  - (BOOL) deleteAllLocations:(NSError * __autoreleasing *)outError;
29
29
  /** Mark all locations pending sync (PostPending) as deleted. Clears the sync queue without sending. */
30
30
  - (BOOL) deletePendingSyncLocations:(NSError * __autoreleasing *)outError;
31
+ /** v4.5.1 — soft-delete only sync-pending rows whose `recorded_at` is <= cutoff (UNIX seconds).
32
+ * Used after a successful background-sync POST so locations persisted DURING the upload (race
33
+ * window) are NOT incorrectly marked deleted. */
34
+ - (BOOL) deleteSyncedLocationsBefore:(NSTimeInterval)cutoff error:(NSError * __autoreleasing *)outError;
35
+ /** v4.5.1 — undo the in-flight SyncPending state when the upload failed. SyncPending → PostPending
36
+ * so the next sync window re-tries them. Without this, a network failure during background-sync
37
+ * would silently drop every pending location. */
38
+ - (BOOL) restoreFailedSyncLocations:(NSError * __autoreleasing *)outError;
39
+ /** v4.5.1 — recover SyncPending rows that got stuck (app/process killed between getLocationsForSync
40
+ * and the upload's success/failure callback). Rows whose `recorded_at` is older than `cutoff`
41
+ * (UNIX seconds) are restored to PostPending so they get retried. Call before each sync window. */
42
+ - (BOOL) restoreStaleSyncLocationsOlderThan:(NSTimeInterval)cutoff error:(NSError * __autoreleasing *)outError;
31
43
  - (BOOL) clearDatabase;
32
44
  - (NSString*) getDatabaseName;
33
45
  - (NSString*) getDatabasePath;
@@ -94,9 +94,17 @@
94
94
  }
95
95
  [rs close];
96
96
 
97
- sql = @"UPDATE " @LC_TABLE_NAME @" SET " @LC_COLUMN_NAME_STATUS @" = ?";
98
- if (![database executeUpdate:sql, [NSString stringWithFormat:@"%ld", MAURLocationDeleted]]) {
99
- NSLog(@"Deleting all location failed code: %d: message: %@", [database lastErrorCode], [database lastErrorMessage]);
97
+ // v4.5.1 FIX (CRITICAL): mark the rows we just selected as SyncPending — NOT Deleted.
98
+ // The previous code UPDATEd the WHOLE table to Deleted before the upload had even started,
99
+ // losing every fix on HTTP failure / network drop. Now:
100
+ // PostPending → SyncPending (in-flight, do not re-include)
101
+ // on success in the network task: SyncPending → Deleted (deleteSyncedLocationsBefore:)
102
+ // on failure in the network task: SyncPending → PostPending (restoreFailedSyncLocations)
103
+ NSString *upd = @"UPDATE " @LC_TABLE_NAME @" SET " @LC_COLUMN_NAME_STATUS @" = ? WHERE " @LC_COLUMN_NAME_STATUS @" = ?";
104
+ if (![database executeUpdate:upd,
105
+ [NSString stringWithFormat:@"%ld", MAURLocationSyncPending],
106
+ [NSString stringWithFormat:@"%ld", MAURLocationPostPending]]) {
107
+ NSLog(@"Marking PostPending → SyncPending failed code: %d: message: %@", [database lastErrorCode], [database lastErrorMessage]);
100
108
  }
101
109
  }];
102
110
 
@@ -139,7 +147,18 @@
139
147
  @COMMA_SEP @LC_COLUMN_NAME_LOCATION_PROVIDER
140
148
  @COMMA_SEP @LC_COLUMN_NAME_STATUS
141
149
  @COMMA_SEP @LC_COLUMN_NAME_RECORDED_AT
142
- @") VALUES (?,?,?,?,?,?,?,?,?,?,?)";
150
+ @COMMA_SEP @LC_COLUMN_NAME_EVENTS_JSON
151
+ @COMMA_SEP @LC_COLUMN_NAME_BATTERY_LEVEL
152
+ @COMMA_SEP @LC_COLUMN_NAME_IS_CHARGING
153
+ @") VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?)";
154
+
155
+ // v4.5: serialize driving events array to JSON for SQLite storage.
156
+ NSString *eventsJson = nil;
157
+ if (location.drivingEvents != nil && [location.drivingEvents count] > 0) {
158
+ NSError *jerr = nil;
159
+ NSData *jd = [NSJSONSerialization dataWithJSONObject:location.drivingEvents options:0 error:&jerr];
160
+ if (jd != nil) eventsJson = [[NSString alloc] initWithData:jd encoding:NSUTF8StringEncoding];
161
+ }
143
162
 
144
163
  BOOL success = [database executeUpdate:sql,
145
164
  [NSNumber numberWithDouble:[location.time timeIntervalSince1970]],
@@ -152,7 +171,10 @@
152
171
  location.provider ?: [NSNull null],
153
172
  location.locationProvider ?: [NSNull null],
154
173
  location.isValid == YES ? @(1) : @(0),
155
- recordedAt
174
+ recordedAt,
175
+ eventsJson ?: [NSNull null],
176
+ location.batteryLevel ?: [NSNull null],
177
+ location.isCharging ?: [NSNull null]
156
178
  ];
157
179
 
158
180
  if (success) {
@@ -223,8 +245,19 @@
223
245
  @COMMA_SEP @LC_COLUMN_NAME_LOCATION_PROVIDER @EQ_BIND
224
246
  @COMMA_SEP @LC_COLUMN_NAME_STATUS @EQ_BIND
225
247
  @COMMA_SEP @LC_COLUMN_NAME_RECORDED_AT @EQ_BIND
248
+ @COMMA_SEP @LC_COLUMN_NAME_EVENTS_JSON @EQ_BIND
249
+ @COMMA_SEP @LC_COLUMN_NAME_BATTERY_LEVEL @EQ_BIND
250
+ @COMMA_SEP @LC_COLUMN_NAME_IS_CHARGING @EQ_BIND
226
251
  @" WHERE " @LC_COLUMN_NAME_ID @EQ_BIND;
227
252
 
253
+ // v4.5.1: serialize events for UPDATE path so old values don't bleed onto new locations
254
+ NSString *eventsJsonForUpdate = nil;
255
+ if (location.drivingEvents != nil && [location.drivingEvents count] > 0) {
256
+ NSError *jerr = nil;
257
+ NSData *jd = [NSJSONSerialization dataWithJSONObject:location.drivingEvents options:0 error:&jerr];
258
+ if (jd != nil) eventsJsonForUpdate = [[NSString alloc] initWithData:jd encoding:NSUTF8StringEncoding];
259
+ }
260
+
228
261
  BOOL success = [database executeUpdate:sql,
229
262
  [NSNumber numberWithDouble:[location.time timeIntervalSince1970]],
230
263
  location.accuracy,
@@ -237,6 +270,9 @@
237
270
  location.locationProvider ?: [NSNull null],
238
271
  location.isValid == YES ? @(1) : @(0),
239
272
  recordedAt,
273
+ eventsJsonForUpdate ?: [NSNull null],
274
+ location.batteryLevel ?: [NSNull null],
275
+ location.isCharging ?: [NSNull null],
240
276
  locationId
241
277
  ];
242
278
 
@@ -304,6 +340,77 @@
304
340
  return success;
305
341
  }
306
342
 
343
+ - (BOOL) deleteSyncedLocationsBefore:(NSTimeInterval)cutoff error:(NSError * __autoreleasing *)outError
344
+ {
345
+ __block BOOL success = YES;
346
+ // v4.5.1 — operate on SyncPending (the rows the network task is/was uploading), not on
347
+ // PostPending (those are still queued for real-time POST and must NOT be touched).
348
+ NSString *sql = @"UPDATE " @LC_TABLE_NAME
349
+ @" SET " @LC_COLUMN_NAME_STATUS @" = ? "
350
+ @" WHERE " @LC_COLUMN_NAME_STATUS @" = ? AND " @LC_COLUMN_NAME_RECORDED_AT @" <= ?";
351
+ [queue inDatabase:^(FMDatabase *database) {
352
+ if (![database executeUpdate:sql,
353
+ @(MAURLocationDeleted),
354
+ @(MAURLocationSyncPending),
355
+ @(cutoff)]) {
356
+ int errorCode = [database lastErrorCode];
357
+ NSString *errorMessage = [database lastErrorMessage];
358
+ NSLog(@"deleteSyncedLocationsBefore failed code: %d: message: %@", errorCode, errorMessage);
359
+ if (outError != NULL) {
360
+ *outError = [NSError errorWithDomain:Domain code:errorCode userInfo:@{ NSLocalizedDescriptionKey: errorMessage ?: @"" }];
361
+ }
362
+ success = NO;
363
+ }
364
+ }];
365
+ return success;
366
+ }
367
+
368
+ - (BOOL) restoreStaleSyncLocationsOlderThan:(NSTimeInterval)cutoff error:(NSError * __autoreleasing *)outError
369
+ {
370
+ // v4.5.1 — rows left as SyncPending because the previous sync's task was killed
371
+ // (app suspended mid-upload, OS process death, manual kill) never reach their
372
+ // success/failure callback. Call at the start of each sync window to rescue them.
373
+ __block BOOL success = YES;
374
+ NSString *sql = @"UPDATE " @LC_TABLE_NAME
375
+ @" SET " @LC_COLUMN_NAME_STATUS @" = ? "
376
+ @" WHERE " @LC_COLUMN_NAME_STATUS @" = ? AND " @LC_COLUMN_NAME_RECORDED_AT @" < ?";
377
+ [queue inDatabase:^(FMDatabase *database) {
378
+ if (![database executeUpdate:sql,
379
+ @(MAURLocationPostPending),
380
+ @(MAURLocationSyncPending),
381
+ @(cutoff)]) {
382
+ int errorCode = [database lastErrorCode];
383
+ NSString *errorMessage = [database lastErrorMessage];
384
+ NSLog(@"restoreStaleSyncLocations failed code: %d: message: %@", errorCode, errorMessage);
385
+ if (outError != NULL) {
386
+ *outError = [NSError errorWithDomain:Domain code:errorCode userInfo:@{ NSLocalizedDescriptionKey: errorMessage ?: @"" }];
387
+ }
388
+ success = NO;
389
+ }
390
+ }];
391
+ return success;
392
+ }
393
+
394
+ - (BOOL) restoreFailedSyncLocations:(NSError * __autoreleasing *)outError
395
+ {
396
+ // v4.5.1 — undo the in-flight transition: SyncPending → PostPending so the next sync window
397
+ // (or real-time post) re-tries them.
398
+ __block BOOL success = YES;
399
+ NSString *sql = @"UPDATE " @LC_TABLE_NAME @" SET " @LC_COLUMN_NAME_STATUS @" = ? WHERE " @LC_COLUMN_NAME_STATUS @" = ?";
400
+ [queue inDatabase:^(FMDatabase *database) {
401
+ if (![database executeUpdate:sql, @(MAURLocationPostPending), @(MAURLocationSyncPending)]) {
402
+ int errorCode = [database lastErrorCode];
403
+ NSString *errorMessage = [database lastErrorMessage];
404
+ NSLog(@"restoreFailedSyncLocations failed code: %d: message: %@", errorCode, errorMessage);
405
+ if (outError != NULL) {
406
+ *outError = [NSError errorWithDomain:Domain code:errorCode userInfo:@{ NSLocalizedDescriptionKey: errorMessage ?: @"" }];
407
+ }
408
+ success = NO;
409
+ }
410
+ }];
411
+ return success;
412
+ }
413
+
307
414
  - (BOOL) deletePendingSyncLocations:(NSError * __autoreleasing *)outError
308
415
  {
309
416
  __block BOOL success = YES;
@@ -368,6 +475,9 @@
368
475
  @COMMA_SEP @LC_COLUMN_NAME_LOCATION_PROVIDER
369
476
  @COMMA_SEP @LC_COLUMN_NAME_STATUS
370
477
  @COMMA_SEP @LC_COLUMN_NAME_RECORDED_AT
478
+ @COMMA_SEP @LC_COLUMN_NAME_EVENTS_JSON
479
+ @COMMA_SEP @LC_COLUMN_NAME_BATTERY_LEVEL
480
+ @COMMA_SEP @LC_COLUMN_NAME_IS_CHARGING
371
481
  @" FROM " @LC_TABLE_NAME;
372
482
  }
373
483
 
@@ -387,6 +497,16 @@
387
497
  location.isValid = [rs intForColumnIndex:10] == 1 ? YES : NO;
388
498
  NSTimeInterval recordedAt = [rs longForColumnIndex:11];
389
499
  location.recordedAt = [NSDate dateWithTimeIntervalSince1970:recordedAt];
500
+ // v4.5: events / battery / charging
501
+ NSString *eventsJson = [rs stringForColumnIndex:12];
502
+ if (eventsJson != nil && eventsJson.length > 0) {
503
+ NSError *jerr = nil;
504
+ id parsed = [NSJSONSerialization JSONObjectWithData:[eventsJson dataUsingEncoding:NSUTF8StringEncoding] options:NSJSONReadingMutableContainers error:&jerr];
505
+ if ([parsed isKindOfClass:[NSMutableArray class]]) location.drivingEvents = parsed;
506
+ else if ([parsed isKindOfClass:[NSArray class]]) location.drivingEvents = [parsed mutableCopy];
507
+ }
508
+ if (![rs columnIndexIsNull:13]) location.batteryLevel = @([rs intForColumnIndex:13]);
509
+ if (![rs columnIndexIsNull:14]) location.isCharging = @([rs intForColumnIndex:14] == 1);
390
510
  return location;
391
511
  }
392
512
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@josuelmm/cordova-background-geolocation",
3
- "version": "4.2.3",
3
+ "version": "4.5.2",
4
4
  "description": "Cordova Background Geolocation (fork actualizado)",
5
5
  "main": "./www/BackgroundGeolocation.js",
6
6
  "types": "./www/BackgroundGeolocation.d.ts",
@@ -109,6 +109,36 @@
109
109
  "cordova": ">=10.0.0",
110
110
  "cordova-android": ">=12.0.0",
111
111
  "cordova-ios": ">=6.2.0"
112
+ },
113
+ "4.2.4": {
114
+ "cordova": ">=10.0.0",
115
+ "cordova-android": ">=12.0.0",
116
+ "cordova-ios": ">=6.2.0"
117
+ },
118
+ "4.3.0": {
119
+ "cordova": ">=10.0.0",
120
+ "cordova-android": ">=12.0.0",
121
+ "cordova-ios": ">=6.2.0"
122
+ },
123
+ "4.4.0": {
124
+ "cordova": ">=10.0.0",
125
+ "cordova-android": ">=12.0.0",
126
+ "cordova-ios": ">=6.2.0"
127
+ },
128
+ "4.4.1": {
129
+ "cordova": ">=10.0.0",
130
+ "cordova-android": ">=12.0.0",
131
+ "cordova-ios": ">=6.2.0"
132
+ },
133
+ "4.5.0": {
134
+ "cordova": ">=10.0.0",
135
+ "cordova-android": ">=12.0.0",
136
+ "cordova-ios": ">=6.2.0"
137
+ },
138
+ "4.5.1": {
139
+ "cordova": ">=10.0.0",
140
+ "cordova-android": ">=12.0.0",
141
+ "cordova-ios": ">=6.2.0"
112
142
  }
113
143
  }
114
144
  },
package/plugin.xml CHANGED
@@ -2,7 +2,7 @@
2
2
  <plugin xmlns="http://www.phonegap.com/ns/plugins/1.0"
3
3
  xmlns:android="http://schemas.android.com/apk/res/android"
4
4
  id="cordova-background-geolocation"
5
- version="4.2.3">
5
+ version="4.5.2">
6
6
  <name>cordova-background-geolocation</name>
7
7
  <description>Cordova Background Geolocation Plugin</description>
8
8
  <license>Apache-2.0</license>
@@ -59,6 +59,7 @@
59
59
  <source-file src="android/common/src/main/java/com/marianhello/bgloc/data/BackgroundActivity.java" target-dir="src/com/marianhello/bgloc/data" />
60
60
  <source-file src="android/common/src/main/java/com/marianhello/bgloc/data/BackgroundLocation.java" target-dir="src/com/marianhello/bgloc/data" />
61
61
  <source-file src="android/common/src/main/java/com/marianhello/bgloc/data/ConfigurationDAO.java" target-dir="src/com/marianhello/bgloc/data" />
62
+ <source-file src="android/common/src/main/java/com/marianhello/bgloc/data/ConfigJsonMapper.java" target-dir="src/com/marianhello/bgloc/data" />
62
63
  <source-file src="android/common/src/main/java/com/marianhello/bgloc/data/DAOFactory.java" target-dir="src/com/marianhello/bgloc/data" />
63
64
  <source-file src="android/common/src/main/java/com/marianhello/bgloc/data/HashMapLocationTemplate.java" target-dir="src/com/marianhello/bgloc/data" />
64
65
  <source-file src="android/common/src/main/java/com/marianhello/bgloc/data/LocationDAO.java" target-dir="src/com/marianhello/bgloc/data" />
@@ -196,7 +197,7 @@
196
197
  <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
197
198
  <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
198
199
  <uses-permission android:name="android.permission.FOREGROUND_SERVICE_LOCATION" />
199
- <uses-permission android:name="android.hardware.location" />
200
+ <uses-feature android:name="android.hardware.location" android:required="false" />
200
201
  </config-file>
201
202
  <config-file target="res/xml/config.xml" parent="/*">
202
203
  <feature name="BackgroundGeolocation">
@@ -353,14 +354,6 @@
353
354
  <source-file src="ios/common/BackgroundGeolocation/MAURUncaughtExceptionLogger.m" />
354
355
  <header-file src="ios/common/BackgroundGeolocation/Reachability.h" />
355
356
  <source-file src="ios/common/BackgroundGeolocation/Reachability.m" />
356
- <header-file src="ios/common/BackgroundGeolocation/SOMotionDetector/SOLocationManager.h" />
357
- <source-file src="ios/common/BackgroundGeolocation/SOMotionDetector/SOLocationManager.m" />
358
- <header-file src="ios/common/BackgroundGeolocation/SOMotionDetector/SOMotionActivity.h" />
359
- <source-file src="ios/common/BackgroundGeolocation/SOMotionDetector/SOMotionActivity.m" />
360
- <header-file src="ios/common/BackgroundGeolocation/SOMotionDetector/SOMotionDetector.h" />
361
- <source-file src="ios/common/BackgroundGeolocation/SOMotionDetector/SOMotionDetector.m" />
362
- <header-file src="ios/common/BackgroundGeolocation/SOMotionDetector/SOStepDetector.h" />
363
- <source-file src="ios/common/BackgroundGeolocation/SOMotionDetector/SOStepDetector.m" />
364
357
  <header-file src="ios/common/BackgroundGeolocation/SQLQueryBuilder/ext/NSString+ZIMString.h" />
365
358
  <source-file src="ios/common/BackgroundGeolocation/SQLQueryBuilder/ext/NSString+ZIMString.m" />
366
359
  <header-file src="ios/common/BackgroundGeolocation/SQLQueryBuilder/sql/ZIMSqlDataManipulationCommand.h" />