@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
|
@@ -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
|
+
"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
|
},
|