@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.
Files changed (61) hide show
  1. package/.npmignore +4 -0
  2. package/CHANGELOG.md +313 -0
  3. package/CLAUDE.md +56 -0
  4. package/HISTORY.md +124 -0
  5. package/README.md +198 -6
  6. package/android/CDVBackgroundGeolocation/src/main/java/com/marianhello/bgloc/cordova/ConfigMapper.java +90 -0
  7. package/android/CDVBackgroundGeolocation/src/main/java/com/tenforwardconsulting/bgloc/cordova/BackgroundGeolocationPlugin.java +362 -1
  8. package/android/common/src/main/java/com/marianhello/bgloc/BackgroundGeolocationFacade.java +153 -0
  9. package/android/common/src/main/java/com/marianhello/bgloc/BootCompletedReceiver.java +27 -11
  10. package/android/common/src/main/java/com/marianhello/bgloc/Config.java +268 -0
  11. package/android/common/src/main/java/com/marianhello/bgloc/HttpPostService.java +86 -26
  12. package/android/common/src/main/java/com/marianhello/bgloc/PluginDelegate.java +26 -0
  13. package/android/common/src/main/java/com/marianhello/bgloc/PostLocationTask.java +48 -5
  14. package/android/common/src/main/java/com/marianhello/bgloc/data/SessionLocationDAO.java +18 -0
  15. package/android/common/src/main/java/com/marianhello/bgloc/data/sqlite/SQLiteOpenHelper.java +8 -1
  16. package/android/common/src/main/java/com/marianhello/bgloc/data/sqlite/SQLiteSessionContract.java +74 -0
  17. package/android/common/src/main/java/com/marianhello/bgloc/data/sqlite/SQLiteSessionLocationDAO.java +169 -0
  18. package/android/common/src/main/java/com/marianhello/bgloc/driving/DrivingEventsDetector.java +265 -0
  19. package/android/common/src/main/java/com/marianhello/bgloc/http/UrlTemplateResolver.java +115 -0
  20. package/android/common/src/main/java/com/marianhello/bgloc/oem/BatteryOemHelper.java +214 -0
  21. package/android/common/src/main/java/com/marianhello/bgloc/provider/ActivityRecognitionLocationProvider.java +13 -9
  22. package/android/common/src/main/java/com/marianhello/bgloc/provider/DistanceFilterLocationProvider.java +29 -40
  23. package/android/common/src/main/java/com/marianhello/bgloc/provider/RawLocationProvider.java +14 -34
  24. package/android/common/src/main/java/com/marianhello/bgloc/sensor/SensorFusionDetector.java +199 -0
  25. package/android/common/src/main/java/com/marianhello/bgloc/service/LocationServiceImpl.java +310 -7
  26. package/android/common/src/main/java/com/marianhello/bgloc/service/LocationServiceProxy.java +14 -2
  27. package/android/common/src/main/java/com/marianhello/bgloc/sync/SyncAdapter.java +50 -3
  28. package/android/dependencies.gradle +0 -3
  29. package/angular/background-geolocation-events.ts +21 -0
  30. package/angular/background-geolocation.service.ts +91 -0
  31. package/angular/dist/background-geolocation-events.d.ts +18 -1
  32. package/angular/dist/background-geolocation.service.d.ts +40 -0
  33. package/angular/dist/esm2022/background-geolocation-events.mjs +22 -1
  34. package/angular/dist/esm2022/background-geolocation.service.mjs +47 -1
  35. package/angular/dist/fesm2022/josuelmm-cordova-background-geolocation.mjs +67 -0
  36. package/angular/dist/fesm2022/josuelmm-cordova-background-geolocation.mjs.map +1 -1
  37. package/ios/CDVBackgroundGeolocation/CDVBackgroundGeolocation.h +4 -0
  38. package/ios/CDVBackgroundGeolocation/CDVBackgroundGeolocation.m +352 -1
  39. package/ios/common/BackgroundGeolocation/MAURBackgroundGeolocationFacade.h +26 -0
  40. package/ios/common/BackgroundGeolocation/MAURBackgroundGeolocationFacade.m +421 -15
  41. package/ios/common/BackgroundGeolocation/MAURBackgroundSync.h +12 -0
  42. package/ios/common/BackgroundGeolocation/MAURBackgroundSync.m +83 -5
  43. package/ios/common/BackgroundGeolocation/MAURConfig.h +15 -0
  44. package/ios/common/BackgroundGeolocation/MAURConfig.m +100 -3
  45. package/ios/common/BackgroundGeolocation/MAURDistanceFilterLocationProvider.m +29 -2
  46. package/ios/common/BackgroundGeolocation/MAURGeolocationOpenHelper.m +12 -3
  47. package/ios/common/BackgroundGeolocation/MAURPostLocationTask.h +4 -0
  48. package/ios/common/BackgroundGeolocation/MAURPostLocationTask.m +102 -44
  49. package/ios/common/BackgroundGeolocation/MAURSensorFusionDetector.h +41 -0
  50. package/ios/common/BackgroundGeolocation/MAURSensorFusionDetector.m +137 -0
  51. package/ios/common/BackgroundGeolocation/MAURSessionLocationContract.h +29 -0
  52. package/ios/common/BackgroundGeolocation/MAURSessionLocationContract.m +31 -0
  53. package/ios/common/BackgroundGeolocation/MAURSessionLocationDAO.h +25 -0
  54. package/ios/common/BackgroundGeolocation/MAURSessionLocationDAO.m +153 -0
  55. package/ios/common/BackgroundGeolocation/MAURUrlTemplateResolver.h +31 -0
  56. package/ios/common/BackgroundGeolocation/MAURUrlTemplateResolver.m +107 -0
  57. package/package.json +36 -1
  58. package/plugin.xml +26 -8
  59. package/www/BackgroundGeolocation.d.ts +559 -3
  60. package/www/BackgroundGeolocation.js +78 -1
  61. package/RELEASE.MD +0 -16
@@ -0,0 +1,41 @@
1
+ //
2
+ // MAURSensorFusionDetector.h
3
+ // BackgroundGeolocation
4
+ //
5
+ // v4.2 Phase 8 — Real sensor fusion detector for iOS.
6
+ // Uses CMMotionManager to sample userAcceleration (gravity removed) and rotationRate.
7
+ // Refines possibleCrash via accelerometer impact and detects phoneUsageWhileDriving.
8
+ //
9
+
10
+ #ifndef MAURSensorFusionDetector_h
11
+ #define MAURSensorFusionDetector_h
12
+
13
+ #import <Foundation/Foundation.h>
14
+
15
+ @class MAURLocation;
16
+
17
+ @protocol MAURSensorFusionListener <NSObject>
18
+ - (void)onSensorCrashWithImpactG:(double)impactG location:(MAURLocation *)location;
19
+ - (void)onPhoneUsageWhileDriving:(MAURLocation *)location;
20
+ @end
21
+
22
+ @interface MAURSensorFusionDetector : NSObject
23
+
24
+ @property (nonatomic, weak) id<MAURSensorFusionListener> listener;
25
+ @property (nonatomic, assign) BOOL enabled;
26
+ @property (nonatomic, assign) double crashImpactG; // default 3.0
27
+ @property (nonatomic, assign) NSTimeInterval crashCooldownMs; // default 10000
28
+ @property (nonatomic, assign) NSTimeInterval phoneUsageWindowMs; // default 4000
29
+ @property (nonatomic, assign) NSTimeInterval phoneUsageCooldownMs; // default 60000
30
+
31
+ @property (nonatomic, assign) BOOL tripActive;
32
+ @property (nonatomic, strong) MAURLocation *lastLocation;
33
+
34
+ - (instancetype)init;
35
+ - (BOOL)isAvailable;
36
+ - (void)start;
37
+ - (void)stop;
38
+
39
+ @end
40
+
41
+ #endif /* MAURSensorFusionDetector_h */
@@ -0,0 +1,137 @@
1
+ //
2
+ // MAURSensorFusionDetector.m
3
+ // BackgroundGeolocation
4
+ //
5
+ // v4.2 Phase 8 — sensor fusion detector implementation.
6
+ //
7
+
8
+ #import "MAURSensorFusionDetector.h"
9
+ #import <CoreMotion/CoreMotion.h>
10
+ #import <UIKit/UIKit.h>
11
+
12
+ static const double kJitterGyroRadS = 0.7; // ~40 deg/s
13
+ static const double kJitterAccelMps2 = 0.5;
14
+
15
+ @interface MAURSensorFusionDetector ()
16
+ @property (nonatomic, strong) CMMotionManager *motion;
17
+ @property (nonatomic, strong) NSOperationQueue *queue;
18
+ @property (nonatomic, assign) BOOL started;
19
+ @property (nonatomic, assign) NSTimeInterval lastCrashAt;
20
+ @property (nonatomic, assign) NSTimeInterval lastPhoneUsageAt;
21
+ @property (nonatomic, assign) NSTimeInterval jitterAboveSince;
22
+ @end
23
+
24
+ @implementation MAURSensorFusionDetector
25
+
26
+ - (instancetype)init {
27
+ if ((self = [super init])) {
28
+ _motion = [[CMMotionManager alloc] init];
29
+ _motion.deviceMotionUpdateInterval = 1.0 / 50.0; // 50 Hz
30
+ _queue = [[NSOperationQueue alloc] init];
31
+ _queue.name = @"MAURSensorFusionQueue";
32
+ _queue.maxConcurrentOperationCount = 1;
33
+ _enabled = NO;
34
+ _crashImpactG = 3.0;
35
+ _crashCooldownMs = 10000;
36
+ _phoneUsageWindowMs = 4000;
37
+ _phoneUsageCooldownMs = 60000;
38
+ _started = NO;
39
+ _lastCrashAt = 0;
40
+ _lastPhoneUsageAt = 0;
41
+ _jitterAboveSince = 0;
42
+ }
43
+ return self;
44
+ }
45
+
46
+ - (BOOL)isAvailable {
47
+ return self.motion.isDeviceMotionAvailable;
48
+ }
49
+
50
+ - (void)start {
51
+ @synchronized (self) {
52
+ if (self.started || !self.enabled) return;
53
+ if (![self.motion isDeviceMotionAvailable]) return;
54
+ __weak typeof(self) weakSelf = self;
55
+ [self.motion startDeviceMotionUpdatesToQueue:self.queue
56
+ withHandler:^(CMDeviceMotion * _Nullable motion, NSError * _Nullable error) {
57
+ if (!motion || error) return;
58
+ [weakSelf processMotion:motion];
59
+ }];
60
+ self.started = YES;
61
+ }
62
+ }
63
+
64
+ - (void)stop {
65
+ @synchronized (self) {
66
+ if (!self.started) return;
67
+ [self.motion stopDeviceMotionUpdates];
68
+ self.started = NO;
69
+ self.jitterAboveSince = 0;
70
+ }
71
+ }
72
+
73
+ - (void)processMotion:(CMDeviceMotion *)motion {
74
+ if (!self.enabled) return;
75
+ NSTimeInterval nowMs = [[NSDate date] timeIntervalSince1970] * 1000.0;
76
+
77
+ // userAcceleration is in g (gravity removed); convert magnitude to g and to m/s².
78
+ double ax = motion.userAcceleration.x;
79
+ double ay = motion.userAcceleration.y;
80
+ double az = motion.userAcceleration.z;
81
+ double accelMagG = sqrt(ax*ax + ay*ay + az*az); // g
82
+ double accelMagMs = accelMagG * 9.80665; // m/s²
83
+
84
+ double gx = motion.rotationRate.x;
85
+ double gy = motion.rotationRate.y;
86
+ double gz = motion.rotationRate.z;
87
+ double gyroMag = sqrt(gx*gx + gy*gy + gz*gz); // rad/s
88
+
89
+ BOOL tripActiveNow = self.tripActive;
90
+ MAURLocation *loc = self.lastLocation;
91
+ id<MAURSensorFusionListener> l = self.listener;
92
+
93
+ // Crash detection
94
+ if (tripActiveNow && self.crashImpactG > 0 && accelMagG >= self.crashImpactG
95
+ && (nowMs - self.lastCrashAt) >= self.crashCooldownMs) {
96
+ self.lastCrashAt = nowMs;
97
+ if ([l respondsToSelector:@selector(onSensorCrashWithImpactG:location:)]) {
98
+ [l onSensorCrashWithImpactG:accelMagG location:loc];
99
+ }
100
+ }
101
+
102
+ // phoneUsageWhileDriving
103
+ if (!tripActiveNow) { self.jitterAboveSince = 0; return; }
104
+ BOOL screenOn = [self isScreenOnApprox];
105
+ if (!screenOn) { self.jitterAboveSince = 0; return; }
106
+
107
+ BOOL above = (accelMagMs >= kJitterAccelMps2) || (gyroMag >= kJitterGyroRadS);
108
+ if (above) {
109
+ if (self.jitterAboveSince == 0) self.jitterAboveSince = nowMs;
110
+ if ((nowMs - self.jitterAboveSince) >= self.phoneUsageWindowMs
111
+ && (nowMs - self.lastPhoneUsageAt) >= self.phoneUsageCooldownMs) {
112
+ self.lastPhoneUsageAt = nowMs;
113
+ self.jitterAboveSince = 0;
114
+ if ([l respondsToSelector:@selector(onPhoneUsageWhileDriving:)]) {
115
+ [l onPhoneUsageWhileDriving:loc];
116
+ }
117
+ }
118
+ } else {
119
+ self.jitterAboveSince = 0;
120
+ }
121
+ }
122
+
123
+ - (BOOL)isScreenOnApprox {
124
+ // Heuristic: app is foreground active => screen is on. Background sampling does
125
+ // not constitute phone usage while driving (passenger may have screen off too).
126
+ __block UIApplicationState state = UIApplicationStateBackground;
127
+ if ([NSThread isMainThread]) {
128
+ state = [UIApplication sharedApplication].applicationState;
129
+ } else {
130
+ dispatch_sync(dispatch_get_main_queue(), ^{
131
+ state = [UIApplication sharedApplication].applicationState;
132
+ });
133
+ }
134
+ return state == UIApplicationStateActive;
135
+ }
136
+
137
+ @end
@@ -0,0 +1,29 @@
1
+ //
2
+ // MAURSessionLocationContract.h
3
+ // BackgroundGeolocation
4
+ //
5
+ // Session table for current recording route. Independent of sync; cleared on startSession/clearSession.
6
+ //
7
+
8
+ #ifndef MAURSessionLocationContract_h
9
+ #define MAURSessionLocationContract_h
10
+
11
+ #define LSC_TABLE_NAME "location_session"
12
+ #define LSC_COLUMN_NAME_ID "id"
13
+ #define LSC_COLUMN_NAME_TIME "time"
14
+ #define LSC_COLUMN_NAME_ACCURACY "accuracy"
15
+ #define LSC_COLUMN_NAME_SPEED "speed"
16
+ #define LSC_COLUMN_NAME_BEARING "bearing"
17
+ #define LSC_COLUMN_NAME_ALTITUDE "altitude"
18
+ #define LSC_COLUMN_NAME_LATITUDE "latitude"
19
+ #define LSC_COLUMN_NAME_LONGITUDE "longitude"
20
+ #define LSC_COLUMN_NAME_PROVIDER "provider"
21
+ #define LSC_COLUMN_NAME_LOCATION_PROVIDER "service_provider"
22
+ #define LSC_COLUMN_NAME_STATUS "valid"
23
+ #define LSC_COLUMN_NAME_RECORDED_AT "recorded_at"
24
+
25
+ @interface MAURSessionLocationContract : NSObject
26
+ + (NSString*) createTableSQL;
27
+ @end
28
+
29
+ #endif /* MAURSessionLocationContract_h */
@@ -0,0 +1,31 @@
1
+ //
2
+ // MAURSessionLocationContract.m
3
+ // BackgroundGeolocation
4
+ //
5
+
6
+ #import <Foundation/Foundation.h>
7
+ #import "MAURSQLiteHelper.h"
8
+ #import "MAURSessionLocationContract.h"
9
+
10
+ @implementation MAURSessionLocationContract
11
+
12
+ + (NSString*) createTableSQL
13
+ {
14
+ NSArray *columns = @[
15
+ @{ @"name": @LSC_COLUMN_NAME_ID, @"type": [SQLPrimaryKeyAutoIncColumnType sqlColumnWithType: kInteger]},
16
+ @{ @"name": @LSC_COLUMN_NAME_TIME, @"type": [SQLColumnType sqlColumnWithType: kReal]},
17
+ @{ @"name": @LSC_COLUMN_NAME_ACCURACY, @"type": [SQLColumnType sqlColumnWithType: kReal]},
18
+ @{ @"name": @LSC_COLUMN_NAME_SPEED, @"type": [SQLColumnType sqlColumnWithType: kReal]},
19
+ @{ @"name": @LSC_COLUMN_NAME_BEARING, @"type": [SQLColumnType sqlColumnWithType: kReal]},
20
+ @{ @"name": @LSC_COLUMN_NAME_ALTITUDE, @"type": [SQLColumnType sqlColumnWithType: kReal]},
21
+ @{ @"name": @LSC_COLUMN_NAME_LATITUDE, @"type": [SQLColumnType sqlColumnWithType: kReal]},
22
+ @{ @"name": @LSC_COLUMN_NAME_LONGITUDE, @"type": [SQLColumnType sqlColumnWithType: kReal]},
23
+ @{ @"name": @LSC_COLUMN_NAME_PROVIDER, @"type": [SQLColumnType sqlColumnWithType: kText]},
24
+ @{ @"name": @LSC_COLUMN_NAME_LOCATION_PROVIDER, @"type": [SQLColumnType sqlColumnWithType: kText]},
25
+ @{ @"name": @LSC_COLUMN_NAME_STATUS, @"type": [SQLColumnType sqlColumnWithType: kInteger]},
26
+ @{ @"name": @LSC_COLUMN_NAME_RECORDED_AT, @"type": [SQLColumnType sqlColumnWithType: kInteger]}
27
+ ];
28
+ return [MAURSQLiteHelper createTableSqlStatement:@LSC_TABLE_NAME columns:columns];
29
+ }
30
+
31
+ @end
@@ -0,0 +1,25 @@
1
+ //
2
+ // MAURSessionLocationDAO.h
3
+ // BackgroundGeolocation
4
+ //
5
+ // Session storage for current route. Cleared on startSession/clearSession; not cleared by sync.
6
+ //
7
+
8
+ #ifndef MAURSessionLocationDAO_h
9
+ #define MAURSessionLocationDAO_h
10
+
11
+ #import <Foundation/Foundation.h>
12
+ #import "MAURLocation.h"
13
+
14
+ @interface MAURSessionLocationDAO : NSObject
15
+ + (instancetype) sharedInstance;
16
+ - (id) init NS_UNAVAILABLE;
17
+ - (void) startSession;
18
+ - (void) clearSession;
19
+ - (BOOL) isSessionActive;
20
+ - (void) persistSessionLocation:(MAURLocation*)location;
21
+ - (NSArray<MAURLocation*>*) getSessionLocations;
22
+ - (NSInteger) getSessionLocationsCount;
23
+ @end
24
+
25
+ #endif /* MAURSessionLocationDAO_h */
@@ -0,0 +1,153 @@
1
+ //
2
+ // MAURSessionLocationDAO.m
3
+ // BackgroundGeolocation
4
+ //
5
+
6
+ #import <Foundation/Foundation.h>
7
+ #import "MAURGeolocationOpenHelper.h"
8
+ #import "MAURSessionLocationContract.h"
9
+ #import "MAURSessionLocationDAO.h"
10
+ #import "MAURLocation.h"
11
+ #import "FMDB.h"
12
+
13
+ static NSString *const kSessionActiveKey = @"bgloc_session_active";
14
+
15
+ @interface MAURSessionLocationDAO ()
16
+ - (instancetype)initPrivate;
17
+ @end
18
+
19
+ @implementation MAURSessionLocationDAO {
20
+ FMDatabaseQueue* queue;
21
+ MAURGeolocationOpenHelper *helper;
22
+ }
23
+
24
+ + (instancetype) sharedInstance
25
+ {
26
+ static MAURSessionLocationDAO *instance = nil;
27
+ static dispatch_once_t onceToken;
28
+ dispatch_once(&onceToken, ^{
29
+ instance = [[self alloc] initPrivate];
30
+ });
31
+ return instance;
32
+ }
33
+
34
+ - (instancetype) initPrivate
35
+ {
36
+ if (self = [super init]) {
37
+ helper = [[MAURGeolocationOpenHelper alloc] init];
38
+ queue = [helper getWritableDatabase];
39
+ }
40
+ return self;
41
+ }
42
+
43
+ - (void) startSession
44
+ {
45
+ [queue inDatabase:^(FMDatabase *database) {
46
+ NSString *sql = [NSString stringWithFormat:@"DELETE FROM %@", @LSC_TABLE_NAME];
47
+ [database executeUpdate:sql];
48
+ }];
49
+ [[NSUserDefaults standardUserDefaults] setBool:YES forKey:kSessionActiveKey];
50
+ [[NSUserDefaults standardUserDefaults] synchronize];
51
+ }
52
+
53
+ - (void) clearSession
54
+ {
55
+ [queue inDatabase:^(FMDatabase *database) {
56
+ NSString *sql = [NSString stringWithFormat:@"DELETE FROM %@", @LSC_TABLE_NAME];
57
+ [database executeUpdate:sql];
58
+ }];
59
+ [[NSUserDefaults standardUserDefaults] setBool:NO forKey:kSessionActiveKey];
60
+ [[NSUserDefaults standardUserDefaults] synchronize];
61
+ }
62
+
63
+ - (BOOL) isSessionActive
64
+ {
65
+ return [[NSUserDefaults standardUserDefaults] boolForKey:kSessionActiveKey];
66
+ }
67
+
68
+ - (void) persistSessionLocation:(MAURLocation*)location
69
+ {
70
+ if (![self isSessionActive]) return;
71
+ if (!location || !location.time) return;
72
+ NSTimeInterval timestamp = [[NSDate date] timeIntervalSince1970];
73
+ NSNumber *recordedAt = [NSNumber numberWithDouble:timestamp];
74
+
75
+ NSString *sql = @"INSERT INTO " @LSC_TABLE_NAME @" ("
76
+ @LSC_COLUMN_NAME_TIME @"," @LSC_COLUMN_NAME_ACCURACY @"," @LSC_COLUMN_NAME_SPEED @","
77
+ @LSC_COLUMN_NAME_BEARING @"," @LSC_COLUMN_NAME_ALTITUDE @"," @LSC_COLUMN_NAME_LATITUDE @","
78
+ @LSC_COLUMN_NAME_LONGITUDE @"," @LSC_COLUMN_NAME_PROVIDER @"," @LSC_COLUMN_NAME_LOCATION_PROVIDER @","
79
+ @LSC_COLUMN_NAME_STATUS @"," @LSC_COLUMN_NAME_RECORDED_AT
80
+ @") VALUES (?,?,?,?,?,?,?,?,?,?,?)";
81
+
82
+ [queue inDatabase:^(FMDatabase *database) {
83
+ [database executeUpdate:sql,
84
+ [NSNumber numberWithDouble:[location.time timeIntervalSince1970]],
85
+ location.accuracy ?: @0,
86
+ location.speed ?: @0,
87
+ location.heading ?: @0,
88
+ location.altitude ?: @0,
89
+ location.latitude ?: @0,
90
+ location.longitude ?: @0,
91
+ location.provider ?: [NSNull null],
92
+ location.locationProvider ? [NSString stringWithFormat:@"%@", location.locationProvider] : [NSNull null],
93
+ @(1),
94
+ recordedAt
95
+ ];
96
+ }];
97
+ }
98
+
99
+ - (NSArray<MAURLocation*>*) getSessionLocations
100
+ {
101
+ __block NSMutableArray* locations = [[NSMutableArray alloc] init];
102
+ NSString *sql = @"SELECT " @LSC_COLUMN_NAME_ID @"," @LSC_COLUMN_NAME_TIME @"," @LSC_COLUMN_NAME_ACCURACY @","
103
+ @LSC_COLUMN_NAME_SPEED @"," @LSC_COLUMN_NAME_BEARING @"," @LSC_COLUMN_NAME_ALTITUDE @","
104
+ @LSC_COLUMN_NAME_LATITUDE @"," @LSC_COLUMN_NAME_LONGITUDE @"," @LSC_COLUMN_NAME_PROVIDER @","
105
+ @LSC_COLUMN_NAME_LOCATION_PROVIDER @"," @LSC_COLUMN_NAME_STATUS @"," @LSC_COLUMN_NAME_RECORDED_AT
106
+ @" FROM " @LSC_TABLE_NAME @" ORDER BY " @LSC_COLUMN_NAME_RECORDED_AT;
107
+
108
+ [queue inDatabase:^(FMDatabase *database) {
109
+ FMResultSet *rs = [database executeQuery:sql];
110
+ while ([rs next]) {
111
+ MAURLocation *loc = [self convertToLocation:rs];
112
+ [locations addObject:loc];
113
+ }
114
+ [rs close];
115
+ }];
116
+ return locations;
117
+ }
118
+
119
+ - (NSInteger) getSessionLocationsCount
120
+ {
121
+ __block NSInteger count = 0;
122
+ NSString *sql = [NSString stringWithFormat:@"SELECT COUNT(*) FROM %@", @LSC_TABLE_NAME];
123
+ [queue inDatabase:^(FMDatabase *database) {
124
+ FMResultSet *rs = [database executeQuery:sql];
125
+ if ([rs next]) {
126
+ count = [rs intForColumnIndex:0];
127
+ }
128
+ [rs close];
129
+ }];
130
+ return count;
131
+ }
132
+
133
+ - (MAURLocation*) convertToLocation:(FMResultSet*)rs
134
+ {
135
+ MAURLocation *location = [[MAURLocation alloc] init];
136
+ location.locationId = [NSNumber numberWithLongLong:[rs longLongIntForColumnIndex:0]];
137
+ NSTimeInterval timestamp = [rs doubleForColumnIndex:1];
138
+ location.time = [NSDate dateWithTimeIntervalSince1970:timestamp];
139
+ location.accuracy = [NSNumber numberWithDouble:[rs doubleForColumnIndex:2]];
140
+ location.speed = [NSNumber numberWithDouble:[rs doubleForColumnIndex:3]];
141
+ location.heading = [NSNumber numberWithDouble:[rs doubleForColumnIndex:4]];
142
+ location.altitude = [NSNumber numberWithDouble:[rs doubleForColumnIndex:5]];
143
+ location.latitude = [NSNumber numberWithDouble:[rs doubleForColumnIndex:6]];
144
+ location.longitude = [NSNumber numberWithDouble:[rs doubleForColumnIndex:7]];
145
+ location.provider = [rs stringForColumnIndex:8];
146
+ location.locationProvider = [NSNumber numberWithInt:[rs intForColumnIndex:9]];
147
+ location.isValid = [rs intForColumnIndex:10] == 1 ? YES : NO;
148
+ NSTimeInterval recordedAt = [rs longForColumnIndex:11];
149
+ location.recordedAt = [NSDate dateWithTimeIntervalSince1970:recordedAt];
150
+ return location;
151
+ }
152
+
153
+ @end
@@ -0,0 +1,31 @@
1
+ //
2
+ // MAURUrlTemplateResolver.h
3
+ // BackgroundGeolocation
4
+ //
5
+ // Resolves placeholders like {lat}, {lon}, {timestamp_iso}, {device_id}, ...
6
+ // in a URL template using a single MAURLocation and an optional queryParams dictionary.
7
+ //
8
+ // Placeholders not found in the location/queryParams are left as-is so partial templates
9
+ // (e.g. only static keys for batch mode) keep working.
10
+ //
11
+
12
+ #ifndef MAURUrlTemplateResolver_h
13
+ #define MAURUrlTemplateResolver_h
14
+
15
+ #import <Foundation/Foundation.h>
16
+
17
+ @class MAURLocation;
18
+
19
+ @interface MAURUrlTemplateResolver : NSObject
20
+
21
+ /**
22
+ * Resolve placeholders in `template` using values from `location` and `queryParams`.
23
+ * Either may be nil. Returns the resolved URL (or the original template if no placeholders).
24
+ */
25
+ + (NSString *)resolve:(NSString *)urlTemplate
26
+ location:(MAURLocation *)location
27
+ queryParams:(NSDictionary *)queryParams;
28
+
29
+ @end
30
+
31
+ #endif /* MAURUrlTemplateResolver_h */
@@ -0,0 +1,107 @@
1
+ //
2
+ // MAURUrlTemplateResolver.m
3
+ // BackgroundGeolocation
4
+ //
5
+
6
+ #import "MAURUrlTemplateResolver.h"
7
+ #import "MAURLocation.h"
8
+
9
+ @implementation MAURUrlTemplateResolver
10
+
11
+ + (NSString *)resolve:(NSString *)urlTemplate
12
+ location:(MAURLocation *)location
13
+ queryParams:(NSDictionary *)queryParams
14
+ {
15
+ if (urlTemplate == nil || urlTemplate.length == 0) return urlTemplate;
16
+
17
+ NSDictionary *ctx = [self buildContext:location queryParams:queryParams];
18
+
19
+ NSError *error = nil;
20
+ NSRegularExpression *regex = [NSRegularExpression
21
+ regularExpressionWithPattern:@"\\{([a-zA-Z0-9_]+)\\}"
22
+ options:0
23
+ error:&error];
24
+ if (regex == nil) return urlTemplate;
25
+
26
+ NSMutableString *result = [NSMutableString stringWithString:urlTemplate];
27
+ NSArray *matches = [regex matchesInString:urlTemplate
28
+ options:0
29
+ range:NSMakeRange(0, urlTemplate.length)];
30
+
31
+ // Iterate in reverse so ranges don't shift.
32
+ for (NSTextCheckingResult *match in [matches reverseObjectEnumerator]) {
33
+ NSString *key = [urlTemplate substringWithRange:[match rangeAtIndex:1]];
34
+ NSString *value = ctx[key];
35
+ if (value != nil) {
36
+ NSString *encoded = [self urlEncode:value];
37
+ [result replaceCharactersInRange:[match range] withString:encoded];
38
+ }
39
+ // else: leave the placeholder as-is.
40
+ }
41
+ return result;
42
+ }
43
+
44
+ + (NSDictionary *)buildContext:(MAURLocation *)loc queryParams:(NSDictionary *)queryParams
45
+ {
46
+ NSMutableDictionary *ctx = [NSMutableDictionary dictionary];
47
+
48
+ // queryParams first; location-derived values can override below.
49
+ if (queryParams != nil) {
50
+ for (id key in queryParams) {
51
+ id value = queryParams[key];
52
+ if (value != nil && value != [NSNull null]) {
53
+ ctx[key] = [NSString stringWithFormat:@"%@", value];
54
+ }
55
+ }
56
+ }
57
+
58
+ if (loc != nil) {
59
+ if (loc.latitude != nil) {
60
+ ctx[@"latitude"] = [loc.latitude stringValue];
61
+ ctx[@"lat"] = [loc.latitude stringValue];
62
+ }
63
+ if (loc.longitude != nil) {
64
+ ctx[@"longitude"] = [loc.longitude stringValue];
65
+ ctx[@"lon"] = [loc.longitude stringValue];
66
+ }
67
+ if (loc.time != nil) {
68
+ long long ms = (long long)([loc.time timeIntervalSince1970] * 1000.0);
69
+ ctx[@"time"] = [NSString stringWithFormat:@"%lld", ms];
70
+ ctx[@"timestamp"] = [NSString stringWithFormat:@"%lld", ms];
71
+ ctx[@"timestamp_iso"] = [self isoUtc:loc.time];
72
+ }
73
+ if (loc.speed != nil) ctx[@"speed"] = [loc.speed stringValue];
74
+ if (loc.altitude != nil) ctx[@"altitude"] = [loc.altitude stringValue];
75
+ if (loc.heading != nil) ctx[@"bearing"] = [loc.heading stringValue];
76
+ if (loc.accuracy != nil) ctx[@"accuracy"] = [loc.accuracy stringValue];
77
+ if (loc.provider != nil) ctx[@"provider"] = loc.provider;
78
+ }
79
+
80
+ return ctx;
81
+ }
82
+
83
+ + (NSString *)urlEncode:(NSString *)s
84
+ {
85
+ if (s == nil) return @"";
86
+ NSCharacterSet *allowed = [NSCharacterSet URLQueryAllowedCharacterSet];
87
+ NSString *enc = [s stringByAddingPercentEncodingWithAllowedCharacters:allowed];
88
+ // URLQueryAllowedCharacterSet leaves "+" and "&" unescaped which is unsafe in values.
89
+ enc = [enc stringByReplacingOccurrencesOfString:@"&" withString:@"%26"];
90
+ enc = [enc stringByReplacingOccurrencesOfString:@"+" withString:@"%2B"];
91
+ return enc != nil ? enc : s;
92
+ }
93
+
94
+ + (NSString *)isoUtc:(NSDate *)date
95
+ {
96
+ static NSDateFormatter *fmt = nil;
97
+ static dispatch_once_t onceToken;
98
+ dispatch_once(&onceToken, ^{
99
+ fmt = [[NSDateFormatter alloc] init];
100
+ fmt.dateFormat = @"yyyy-MM-dd'T'HH:mm:ss'Z'";
101
+ fmt.timeZone = [NSTimeZone timeZoneWithAbbreviation:@"UTC"];
102
+ fmt.locale = [NSLocale localeWithLocaleIdentifier:@"en_US_POSIX"];
103
+ });
104
+ return [fmt stringFromDate:date];
105
+ }
106
+
107
+ @end
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@josuelmm/cordova-background-geolocation",
3
- "version": "3.1.1",
3
+ "version": "4.2.0",
4
4
  "description": "Cordova Background Geolocation (fork actualizado)",
5
5
  "main": "./www/BackgroundGeolocation.js",
6
6
  "types": "./www/BackgroundGeolocation.d.ts",
@@ -64,6 +64,41 @@
64
64
  "cordova": ">=8.0.0",
65
65
  "cordova-android": ">=8.0.0",
66
66
  "cordova-ios": ">=6.0.0"
67
+ },
68
+ "3.3.0": {
69
+ "cordova": ">=10.0.0",
70
+ "cordova-android": ">=12.0.0",
71
+ "cordova-ios": ">=6.0.0"
72
+ },
73
+ "3.4.0": {
74
+ "cordova": ">=10.0.0",
75
+ "cordova-android": ">=12.0.0",
76
+ "cordova-ios": ">=6.2.0"
77
+ },
78
+ "3.5.0": {
79
+ "cordova": ">=10.0.0",
80
+ "cordova-android": ">=12.0.0",
81
+ "cordova-ios": ">=6.2.0"
82
+ },
83
+ "3.6.0": {
84
+ "cordova": ">=10.0.0",
85
+ "cordova-android": ">=12.0.0",
86
+ "cordova-ios": ">=6.2.0"
87
+ },
88
+ "4.0.0": {
89
+ "cordova": ">=10.0.0",
90
+ "cordova-android": ">=12.0.0",
91
+ "cordova-ios": ">=6.2.0"
92
+ },
93
+ "4.1.0": {
94
+ "cordova": ">=10.0.0",
95
+ "cordova-android": ">=12.0.0",
96
+ "cordova-ios": ">=6.2.0"
97
+ },
98
+ "4.2.0": {
99
+ "cordova": ">=10.0.0",
100
+ "cordova-android": ">=12.0.0",
101
+ "cordova-ios": ">=6.2.0"
67
102
  }
68
103
  }
69
104
  },