@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.
- package/.npmignore +11 -0
- package/CHANGELOG.md +261 -0
- package/README.md +306 -115
- package/android/CDVBackgroundGeolocation/src/main/java/com/marianhello/bgloc/cordova/ConfigMapper.java +34 -0
- package/android/CDVBackgroundGeolocation/src/main/java/com/tenforwardconsulting/bgloc/cordova/BackgroundGeolocationPlugin.java +61 -1
- package/android/common/src/main/AndroidManifest.xml +1 -1
- package/android/common/src/main/java/com/marianhello/bgloc/BootCompletedReceiver.java +20 -3
- package/android/common/src/main/java/com/marianhello/bgloc/Config.java +87 -1
- package/android/common/src/main/java/com/marianhello/bgloc/data/BackgroundLocation.java +94 -0
- package/android/common/src/main/java/com/marianhello/bgloc/data/ConfigJsonMapper.java +211 -0
- package/android/common/src/main/java/com/marianhello/bgloc/data/LocationTemplateFactory.java +6 -0
- package/android/common/src/main/java/com/marianhello/bgloc/data/sqlite/SQLiteConfigurationContract.java +5 -1
- package/android/common/src/main/java/com/marianhello/bgloc/data/sqlite/SQLiteConfigurationDAO.java +32 -1
- package/android/common/src/main/java/com/marianhello/bgloc/data/sqlite/SQLiteLocationContract.java +12 -2
- package/android/common/src/main/java/com/marianhello/bgloc/data/sqlite/SQLiteLocationDAO.java +33 -2
- package/android/common/src/main/java/com/marianhello/bgloc/data/sqlite/SQLiteOpenHelper.java +15 -1
- package/android/common/src/main/java/com/marianhello/bgloc/provider/AbstractLocationProvider.java +48 -1
- package/android/common/src/main/java/com/marianhello/bgloc/provider/ActivityRecognitionLocationProvider.java +105 -6
- package/android/common/src/main/java/com/marianhello/bgloc/provider/DistanceFilterLocationProvider.java +336 -250
- package/android/common/src/main/java/com/marianhello/bgloc/provider/RawLocationProvider.java +69 -19
- package/android/common/src/main/java/com/marianhello/bgloc/service/LocationServiceImpl.java +246 -21
- package/android/common/src/main/java/com/marianhello/bgloc/service/LocationServiceProxy.java +5 -2
- package/android/common/src/main/java/com/marianhello/bgloc/sync/BatchManager.java +46 -13
- package/ios/CDVBackgroundGeolocation/CDVBackgroundGeolocation.m +23 -1
- package/ios/common/BackgroundGeolocation/MAURActivityLocationProvider.m +208 -70
- package/ios/common/BackgroundGeolocation/MAURBackgroundGeolocationFacade.m +132 -5
- package/ios/common/BackgroundGeolocation/MAURBackgroundSync.m +20 -0
- package/ios/common/BackgroundGeolocation/MAURConfig.h +7 -0
- package/ios/common/BackgroundGeolocation/MAURConfig.m +37 -2
- package/ios/common/BackgroundGeolocation/MAURConfigurationContract.h +3 -0
- package/ios/common/BackgroundGeolocation/MAURConfigurationContract.m +3 -1
- package/ios/common/BackgroundGeolocation/MAURDistanceFilterLocationProvider.m +10 -1
- package/ios/common/BackgroundGeolocation/MAURGeolocationOpenHelper.m +15 -1
- package/ios/common/BackgroundGeolocation/MAURLocation.h +12 -0
- package/ios/common/BackgroundGeolocation/MAURLocation.m +33 -4
- package/ios/common/BackgroundGeolocation/MAURLocationContract.h +4 -0
- package/ios/common/BackgroundGeolocation/MAURLocationContract.m +5 -1
- package/ios/common/BackgroundGeolocation/MAURLocationManager.m +19 -1
- package/ios/common/BackgroundGeolocation/MAURPostLocationTask.h +9 -0
- package/ios/common/BackgroundGeolocation/MAURPostLocationTask.m +59 -1
- package/ios/common/BackgroundGeolocation/MAURRawLocationProvider.m +10 -1
- package/ios/common/BackgroundGeolocation/MAURSQLiteConfigurationDAO.m +54 -4
- package/ios/common/BackgroundGeolocation/MAURSQLiteLocationDAO.h +12 -0
- package/ios/common/BackgroundGeolocation/MAURSQLiteLocationDAO.m +125 -5
- package/package.json +31 -1
- package/plugin.xml +3 -10
- package/www/BackgroundGeolocation.d.ts +143 -3
- package/www/BackgroundGeolocation.js +11 -4
- package/CLAUDE.md +0 -56
- package/HISTORY.md +0 -871
- package/android/CDVBackgroundGeolocation/src/test/java/com/marianhello/ConfigMapperTest.java +0 -220
- package/android/common/src/androidTest/java/com/marianhello/bgloc/BackgroundGeolocationFacadeTest.java +0 -45
- package/android/common/src/androidTest/java/com/marianhello/bgloc/BatchManagerTest.java +0 -570
- package/android/common/src/androidTest/java/com/marianhello/bgloc/ConfigTest.java +0 -76
- package/android/common/src/androidTest/java/com/marianhello/bgloc/ContentProviderLocationDAOTest.java +0 -437
- package/android/common/src/androidTest/java/com/marianhello/bgloc/DBLogReaderTest.java +0 -95
- package/android/common/src/androidTest/java/com/marianhello/bgloc/LocationContentProviderTest.java +0 -159
- package/android/common/src/androidTest/java/com/marianhello/bgloc/LocationServiceProxyTest.java +0 -161
- package/android/common/src/androidTest/java/com/marianhello/bgloc/LocationServiceTest.java +0 -247
- package/android/common/src/androidTest/java/com/marianhello/bgloc/SQLiteConfigurationDAOTest.java +0 -200
- package/android/common/src/androidTest/java/com/marianhello/bgloc/SQLiteLocationDAOTest.java +0 -457
- package/android/common/src/androidTest/java/com/marianhello/bgloc/SQLiteLocationDAOThreadTest.java +0 -96
- package/android/common/src/androidTest/java/com/marianhello/bgloc/SQLiteOpenHelperTest.java +0 -225
- package/android/common/src/androidTest/java/com/marianhello/bgloc/TestPluginDelegate.java +0 -46
- package/android/common/src/androidTest/java/com/marianhello/bgloc/TestResourceResolver.java +0 -14
- package/android/common/src/androidTest/java/com/marianhello/bgloc/provider/MockLocationProvider.java +0 -50
- package/android/common/src/androidTest/java/com/marianhello/bgloc/provider/TestLocationProviderFactory.java +0 -17
- package/android/common/src/androidTest/java/com/marianhello/bgloc/sqlite/SQLiteOpenHelper10.java +0 -92
- package/android/common/src/androidTest/java/com/marianhello/bgloc/test/LocationProviderTestCase.java +0 -107
- package/android/common/src/androidTest/java/com/marianhello/bgloc/test/TestConstants.java +0 -5
- package/android/common/src/test/java/com/marianhello/backgroundgeolocation/ArrayListLocationTemplateTest.java +0 -82
- package/android/common/src/test/java/com/marianhello/backgroundgeolocation/BackgroundLocationTest.java +0 -128
- package/android/common/src/test/java/com/marianhello/backgroundgeolocation/ConfigTest.java +0 -191
- package/android/common/src/test/java/com/marianhello/backgroundgeolocation/DBLogReaderTest.java +0 -37
- package/android/common/src/test/java/com/marianhello/backgroundgeolocation/HashMapLocationTemplateTest.java +0 -216
- package/android/common/src/test/java/com/marianhello/backgroundgeolocation/HttpPostServiceTest.java +0 -223
- package/android/common/src/test/java/com/marianhello/backgroundgeolocation/LocationTemplateFactoryTest.java +0 -50
- package/android/common/src/test/java/com/marianhello/backgroundgeolocation/PostLocationTaskTest.java +0 -180
- package/android/common/src/test/java/com/marianhello/backgroundgeolocation/TestHelper.java +0 -16
- package/ios/common/BackgroundGeolocation/SOMotionDetector/CHANGELOG.md +0 -2
- package/ios/common/BackgroundGeolocation/SOMotionDetector/LICENSE +0 -21
- package/ios/common/BackgroundGeolocation/SOMotionDetector/README.md +0 -135
- package/ios/common/BackgroundGeolocation/SOMotionDetector/SOLocationManager.h +0 -80
- package/ios/common/BackgroundGeolocation/SOMotionDetector/SOLocationManager.m +0 -147
- package/ios/common/BackgroundGeolocation/SOMotionDetector/SOMotionActivity.h +0 -30
- package/ios/common/BackgroundGeolocation/SOMotionDetector/SOMotionActivity.m +0 -42
- package/ios/common/BackgroundGeolocation/SOMotionDetector/SOMotionDetector.h +0 -99
- package/ios/common/BackgroundGeolocation/SOMotionDetector/SOMotionDetector.m +0 -327
- package/ios/common/BackgroundGeolocation/SOMotionDetector/SOStepDetector.h +0 -44
- package/ios/common/BackgroundGeolocation/SOMotionDetector/SOStepDetector.m +0 -94
- package/ios/common/BackgroundGeolocationTests/Info.plist +0 -24
- package/ios/common/BackgroundGeolocationTests/MAURBackgroundLocationTest.m +0 -185
- package/ios/common/BackgroundGeolocationTests/MAURConfigTest.m +0 -161
- package/ios/common/BackgroundGeolocationTests/MAURGeolocationOpenHelperTest.m +0 -102
- package/ios/common/BackgroundGeolocationTests/MAURLocationTest.m +0 -216
- package/ios/common/BackgroundGeolocationTests/MAURLocationUploaderTest.m +0 -55
- package/ios/common/BackgroundGeolocationTests/MAURLogReaderTest.m +0 -43
- package/ios/common/BackgroundGeolocationTests/MAURSQLiteConfigurationDAOTest.m +0 -102
- package/ios/common/BackgroundGeolocationTests/MAURSQLiteHelperTest.m +0 -41
- package/ios/common/BackgroundGeolocationTests/MAURSQLiteLocationDAOTests.m +0 -240
- package/ios/common/BackgroundGeolocationTests/MAURSQLiteLocationDAOThreadTest.m +0 -84
- package/ios/common/BackgroundGeolocationTests/MAURSQLiteOpenHelperTest.m +0 -144
- package/ios/common/scripts/xcode-refactor.js +0 -184
|
@@ -188,12 +188,22 @@ public class BatchManager {
|
|
|
188
188
|
}
|
|
189
189
|
|
|
190
190
|
private void writeValue(Object value) throws IOException {
|
|
191
|
-
if (value
|
|
191
|
+
if (value == null || value == JSONObject.NULL) {
|
|
192
|
+
writer.nullValue();
|
|
193
|
+
} else if (value instanceof String ) {
|
|
192
194
|
writer.value((String) value);
|
|
193
195
|
} else if (value instanceof Map) {
|
|
194
196
|
writeMap((Map) value);
|
|
195
197
|
} else if (value instanceof List) {
|
|
196
198
|
writeList((List) value);
|
|
199
|
+
// v4.5.1 — handle JSONArray / JSONObject so that placeholders that resolve to
|
|
200
|
+
// structured data (@events → JSONArray of events) are written as real JSON arrays /
|
|
201
|
+
// objects, not as escaped strings. Without this, a fix coming out of the sync queue
|
|
202
|
+
// serialised "events" as "[{\"type\":\"hardBrake\"}]" literal.
|
|
203
|
+
} else if (value instanceof org.json.JSONArray) {
|
|
204
|
+
writeJsonArray((org.json.JSONArray) value);
|
|
205
|
+
} else if (value instanceof org.json.JSONObject) {
|
|
206
|
+
writeJsonObject((org.json.JSONObject) value);
|
|
197
207
|
} else if (Integer.class.isInstance(value)) {
|
|
198
208
|
writer.value((Integer) value);
|
|
199
209
|
} else if (Double.class.isInstance(value)) {
|
|
@@ -204,13 +214,44 @@ public class BatchManager {
|
|
|
204
214
|
writer.value((Long) value);
|
|
205
215
|
} else if (Boolean.class.isInstance(value)) {
|
|
206
216
|
writer.value((Boolean) value);
|
|
207
|
-
} else if (value == JSONObject.NULL) {
|
|
208
|
-
writer.nullValue();
|
|
209
217
|
} else {
|
|
210
218
|
writer.value(String.valueOf(value));
|
|
211
219
|
}
|
|
212
220
|
}
|
|
213
221
|
|
|
222
|
+
private void writeJsonArray(org.json.JSONArray arr) throws IOException {
|
|
223
|
+
writer.beginArray();
|
|
224
|
+
for (int i = 0; i < arr.length(); i++) {
|
|
225
|
+
Object v = arr.opt(i);
|
|
226
|
+
writeValue(v == null ? JSONObject.NULL : v);
|
|
227
|
+
}
|
|
228
|
+
writer.endArray();
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
private void writeJsonObject(org.json.JSONObject obj) throws IOException {
|
|
232
|
+
writer.beginObject();
|
|
233
|
+
Iterator<String> keys = obj.keys();
|
|
234
|
+
while (keys.hasNext()) {
|
|
235
|
+
String k = keys.next();
|
|
236
|
+
writer.name(k);
|
|
237
|
+
writeValue(obj.opt(k));
|
|
238
|
+
}
|
|
239
|
+
writer.endObject();
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/** v4.5.1 — resolve a template string. If it is a placeholder ("@foo") and the location
|
|
243
|
+
* has no value, return JSONObject.NULL so the writer emits `null` instead of the literal
|
|
244
|
+
* "@foo" string. */
|
|
245
|
+
private Object resolveTemplateValue(Object value) {
|
|
246
|
+
if (value instanceof String) {
|
|
247
|
+
String s = (String) value;
|
|
248
|
+
Object resolved = location.getValueForKey(s);
|
|
249
|
+
if (resolved != null) return resolved;
|
|
250
|
+
if (s.startsWith("@")) return JSONObject.NULL;
|
|
251
|
+
}
|
|
252
|
+
return value;
|
|
253
|
+
}
|
|
254
|
+
|
|
214
255
|
public void writeMap(Map values) throws IOException {
|
|
215
256
|
writer.beginObject();
|
|
216
257
|
Iterator<?> it = values.entrySet().iterator();
|
|
@@ -218,12 +259,8 @@ public class BatchManager {
|
|
|
218
259
|
Map.Entry<String, Object> pair = (Map.Entry) it.next();
|
|
219
260
|
String key = pair.getKey();
|
|
220
261
|
Object value = pair.getValue();
|
|
221
|
-
Object locationValue = null;
|
|
222
|
-
if (value instanceof String) {
|
|
223
|
-
locationValue = location.getValueForKey((String)value);
|
|
224
|
-
}
|
|
225
262
|
writer.name(key);
|
|
226
|
-
writeValue(
|
|
263
|
+
writeValue(resolveTemplateValue(value));
|
|
227
264
|
}
|
|
228
265
|
writer.endObject();
|
|
229
266
|
}
|
|
@@ -233,11 +270,7 @@ public class BatchManager {
|
|
|
233
270
|
Iterator<?> it = values.iterator();
|
|
234
271
|
while (it.hasNext()) {
|
|
235
272
|
Object value = it.next();
|
|
236
|
-
|
|
237
|
-
if (value instanceof String) {
|
|
238
|
-
locationValue = location.getValueForKey((String) value);
|
|
239
|
-
}
|
|
240
|
-
writeValue(locationValue != null ? locationValue : value);
|
|
273
|
+
writeValue(resolveTemplateValue(value));
|
|
241
274
|
}
|
|
242
275
|
writer.endArray();
|
|
243
276
|
}
|
|
@@ -135,6 +135,28 @@ static NSString * const TAG = @"CDVBackgroundGeolocation";
|
|
|
135
135
|
callbackId:command.callbackId];
|
|
136
136
|
}
|
|
137
137
|
|
|
138
|
+
// v4.5: runtime permission helpers — paridad de API con Android. iOS no expone gates
|
|
139
|
+
// separados para background location / activity recognition / notifications, así que
|
|
140
|
+
// resolvemos siempre con notRequired:YES.
|
|
141
|
+
- (void) requestBackgroundLocationPermission:(CDVInvokedUrlCommand *)command
|
|
142
|
+
{
|
|
143
|
+
[self sendNotRequiredPermissionResult:command];
|
|
144
|
+
}
|
|
145
|
+
- (void) requestActivityRecognitionPermission:(CDVInvokedUrlCommand *)command
|
|
146
|
+
{
|
|
147
|
+
[self sendNotRequiredPermissionResult:command];
|
|
148
|
+
}
|
|
149
|
+
- (void) requestNotificationPermission:(CDVInvokedUrlCommand *)command
|
|
150
|
+
{
|
|
151
|
+
[self sendNotRequiredPermissionResult:command];
|
|
152
|
+
}
|
|
153
|
+
- (void) sendNotRequiredPermissionResult:(CDVInvokedUrlCommand *)command
|
|
154
|
+
{
|
|
155
|
+
NSDictionary *r = @{ @"granted": @YES, @"notRequired": @YES };
|
|
156
|
+
[self.commandDelegate sendPluginResult:[CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:r]
|
|
157
|
+
callbackId:command.callbackId];
|
|
158
|
+
}
|
|
159
|
+
|
|
138
160
|
// v4.1 GPS-derived sensor-like events
|
|
139
161
|
- (void) sendDrivingEventN:(NSString *)name note:(NSNotification *)note
|
|
140
162
|
{
|
|
@@ -513,7 +535,7 @@ static NSString * const TAG = @"CDVBackgroundGeolocation";
|
|
|
513
535
|
- (void) getPluginVersion:(CDVInvokedUrlCommand*)command
|
|
514
536
|
{
|
|
515
537
|
NSLog(@"%@ #%@", TAG, @"getPluginVersion");
|
|
516
|
-
NSString *version = @"4.2
|
|
538
|
+
NSString *version = @"4.5.2"; // keep in sync with plugin.xml and Android PLUGIN_VERSION
|
|
517
539
|
CDVPluginResult *result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:version];
|
|
518
540
|
[self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
|
|
519
541
|
}
|
|
@@ -5,43 +5,59 @@
|
|
|
5
5
|
// Created by Marian Hello on 14/09/2016.
|
|
6
6
|
// Copyright © 2016 mauron85. All rights reserved.
|
|
7
7
|
//
|
|
8
|
+
// v4.5.2 — refactored to use CoreMotion's CMMotionActivityManager directly.
|
|
9
|
+
// The SOMotionDetector dependency (sources + plugin.xml entries) was removed
|
|
10
|
+
// in this version.
|
|
11
|
+
//
|
|
8
12
|
|
|
9
13
|
#import <Foundation/Foundation.h>
|
|
14
|
+
#import <CoreMotion/CoreMotion.h>
|
|
10
15
|
#import "MAURActivityLocationProvider.h"
|
|
11
16
|
#import "MAURActivity.h"
|
|
12
|
-
#import "SOMotionDetector.h"
|
|
13
17
|
#import "MAURLocationManager.h"
|
|
14
18
|
#import "MAURLogging.h"
|
|
15
19
|
|
|
16
|
-
#define SYSTEM_VERSION_EQUAL_TO(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedSame)
|
|
17
|
-
#define SYSTEM_VERSION_GREATER_THAN(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedDescending)
|
|
18
|
-
#define SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedAscending)
|
|
19
|
-
#define SYSTEM_VERSION_LESS_THAN(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedAscending)
|
|
20
|
-
#define SYSTEM_VERSION_LESS_THAN_OR_EQUAL_TO(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedDescending)
|
|
21
|
-
|
|
22
20
|
static NSString * const TAG = @"ActivityLocationProvider";
|
|
23
21
|
static NSString * const Domain = @"com.marianhello";
|
|
24
22
|
|
|
25
|
-
|
|
26
|
-
|
|
23
|
+
// Local motion-type enum (replaces the legacy SOMotionType used previously).
|
|
24
|
+
typedef NS_ENUM(NSUInteger, MAURMotionType) {
|
|
25
|
+
MAURMotionTypeUnknown = 0,
|
|
26
|
+
MAURMotionTypeNotMoving,
|
|
27
|
+
MAURMotionTypeWalking,
|
|
28
|
+
MAURMotionTypeRunning,
|
|
29
|
+
MAURMotionTypeAutomotive,
|
|
30
|
+
MAURMotionTypeCycling
|
|
31
|
+
};
|
|
27
32
|
|
|
28
33
|
@implementation MAURActivityLocationProvider {
|
|
29
34
|
BOOL isStarted;
|
|
30
35
|
BOOL isTracking;
|
|
31
|
-
|
|
36
|
+
BOOL motionAvailable;
|
|
37
|
+
BOOL motionPermissionErrorEmitted;
|
|
38
|
+
MAURMotionType lastMotionType;
|
|
32
39
|
|
|
33
40
|
MAURLocationManager *locationManager;
|
|
41
|
+
CMMotionActivityManager *activityManager;
|
|
42
|
+
NSOperationQueue *activityQueue;
|
|
43
|
+
|
|
44
|
+
// v4.5.2: cache of active config so motion callbacks can read
|
|
45
|
+
// activityConfidenceThreshold without re-fetching.
|
|
46
|
+
MAURConfig *currentConfig;
|
|
34
47
|
}
|
|
35
48
|
|
|
36
49
|
- (instancetype) init
|
|
37
50
|
{
|
|
38
51
|
self = [super init];
|
|
39
|
-
|
|
52
|
+
|
|
40
53
|
if (self) {
|
|
41
54
|
isStarted = NO;
|
|
42
55
|
isTracking = NO;
|
|
56
|
+
motionAvailable = NO;
|
|
57
|
+
motionPermissionErrorEmitted = NO;
|
|
58
|
+
lastMotionType = MAURMotionTypeUnknown;
|
|
43
59
|
}
|
|
44
|
-
|
|
60
|
+
|
|
45
61
|
return self;
|
|
46
62
|
}
|
|
47
63
|
|
|
@@ -49,52 +65,197 @@ static NSString * const Domain = @"com.marianhello";
|
|
|
49
65
|
locationManager = [MAURLocationManager sharedInstance];
|
|
50
66
|
locationManager.delegate = self;
|
|
51
67
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
68
|
+
// v4.5.2 — CoreMotion direct. Without CMMotionActivityManager support the
|
|
69
|
+
// provider cannot drive STILL/ACTIVE transitions; emit a clear error so the
|
|
70
|
+
// host app knows to fall back to DISTANCE_FILTER or RAW.
|
|
71
|
+
motionAvailable = [CMMotionActivityManager isActivityAvailable];
|
|
72
|
+
if (!motionAvailable) {
|
|
73
|
+
DDLogError(@"%@ CMMotionActivityManager unavailable on this device; ACTIVITY_PROVIDER will be inert.", TAG);
|
|
74
|
+
NSError *err = [NSError errorWithDomain:Domain
|
|
75
|
+
code:MAURBGServiceError
|
|
76
|
+
userInfo:@{ NSLocalizedDescriptionKey: @"CMMotionActivityManager unavailable on this device." }];
|
|
77
|
+
if (self.delegate && [self.delegate respondsToSelector:@selector(onError:)]) {
|
|
78
|
+
[self.delegate onError:err];
|
|
79
|
+
}
|
|
80
|
+
return;
|
|
56
81
|
}
|
|
82
|
+
|
|
83
|
+
activityManager = [[CMMotionActivityManager alloc] init];
|
|
84
|
+
activityQueue = [[NSOperationQueue alloc] init];
|
|
85
|
+
activityQueue.name = @"MAURActivityRecognitionQueue";
|
|
86
|
+
activityQueue.maxConcurrentOperationCount = 1;
|
|
57
87
|
}
|
|
58
88
|
|
|
59
89
|
- (BOOL) onConfigure:(MAURConfig*)config error:(NSError * __autoreleasing *)outError
|
|
60
90
|
{
|
|
61
91
|
DDLogVerbose(@"%@ configure", TAG);
|
|
62
|
-
|
|
92
|
+
|
|
93
|
+
currentConfig = config;
|
|
94
|
+
|
|
63
95
|
locationManager.pausesLocationUpdatesAutomatically = [config pauseLocationUpdates];
|
|
64
96
|
locationManager.activityType = [config decodeActivityType];
|
|
65
97
|
locationManager.distanceFilter = config.distanceFilter.integerValue; // meters
|
|
66
98
|
locationManager.desiredAccuracy = [config decodeDesiredAccuracy];
|
|
67
|
-
|
|
68
|
-
|
|
99
|
+
|
|
69
100
|
return YES;
|
|
70
101
|
}
|
|
71
102
|
|
|
72
103
|
- (BOOL) onStart:(NSError * __autoreleasing *)outError
|
|
73
104
|
{
|
|
74
105
|
DDLogInfo(@"%@ will start", TAG);
|
|
75
|
-
|
|
76
|
-
if (
|
|
77
|
-
|
|
106
|
+
|
|
107
|
+
if (isStarted) {
|
|
108
|
+
return YES;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (!motionAvailable || activityManager == nil) {
|
|
112
|
+
// No motion service: degrade gracefully — keep emitting raw location
|
|
113
|
+
// changes via the manager, but the STILL/ACTIVE state machine is off.
|
|
78
114
|
[self startTracking];
|
|
79
115
|
isStarted = YES;
|
|
116
|
+
return YES;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// iOS 11+: surface motion-permission denial up-front so the host app can
|
|
120
|
+
// re-prompt. Older iOS versions deliver permission errors via the handler.
|
|
121
|
+
if (@available(iOS 11.0, *)) {
|
|
122
|
+
CMAuthorizationStatus authStatus = [CMMotionActivityManager authorizationStatus];
|
|
123
|
+
if (authStatus == CMAuthorizationStatusDenied || authStatus == CMAuthorizationStatusRestricted) {
|
|
124
|
+
if (!motionPermissionErrorEmitted) {
|
|
125
|
+
NSError *err = [NSError errorWithDomain:Domain
|
|
126
|
+
code:MAURBGServiceError
|
|
127
|
+
userInfo:@{ NSLocalizedDescriptionKey: @"Motion & Fitness permission denied; ACTIVITY_PROVIDER cannot detect STILL/ACTIVE." }];
|
|
128
|
+
if (self.delegate && [self.delegate respondsToSelector:@selector(onError:)]) {
|
|
129
|
+
[self.delegate onError:err];
|
|
130
|
+
}
|
|
131
|
+
motionPermissionErrorEmitted = YES;
|
|
132
|
+
}
|
|
133
|
+
// Still start raw tracking so locations flow.
|
|
134
|
+
[self startTracking];
|
|
135
|
+
isStarted = YES;
|
|
136
|
+
return YES;
|
|
137
|
+
}
|
|
80
138
|
}
|
|
81
|
-
|
|
139
|
+
|
|
140
|
+
__weak typeof(self) weakSelf = self;
|
|
141
|
+
[activityManager startActivityUpdatesToQueue:activityQueue
|
|
142
|
+
withHandler:^(CMMotionActivity * _Nullable activity) {
|
|
143
|
+
typeof(self) strongSelf = weakSelf;
|
|
144
|
+
if (strongSelf == nil || activity == nil) return;
|
|
145
|
+
[strongSelf handleActivityUpdate:activity];
|
|
146
|
+
}];
|
|
147
|
+
|
|
148
|
+
// v4.5.2 — start tracking immediately. Without this, if the user opens the
|
|
149
|
+
// app while already still, CoreMotion fires STILL first and `handleActivityUpdate`
|
|
150
|
+
// never calls startTracking (its rule is "ACTIVE → start"), so no fix is ever
|
|
151
|
+
// produced and the initial stationary is never emitted. Mirrors the legacy
|
|
152
|
+
// SOMotionDetector behavior. If CoreMotion subsequently confirms STILL, the
|
|
153
|
+
// first incoming fix will trigger `onStationaryChanged` + `stopTracking`, so
|
|
154
|
+
// battery cost stays bounded.
|
|
155
|
+
[self startTracking];
|
|
156
|
+
|
|
157
|
+
isStarted = YES;
|
|
82
158
|
return YES;
|
|
83
159
|
}
|
|
84
160
|
|
|
85
161
|
- (BOOL) onStop:(NSError * __autoreleasing *)outError
|
|
86
162
|
{
|
|
87
163
|
DDLogInfo(@"%@ will stop", TAG);
|
|
88
|
-
|
|
89
|
-
if (isStarted) {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
164
|
+
|
|
165
|
+
if (!isStarted) {
|
|
166
|
+
return YES;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
if (activityManager != nil) {
|
|
170
|
+
[activityManager stopActivityUpdates];
|
|
93
171
|
}
|
|
94
|
-
|
|
172
|
+
[self stopTracking];
|
|
173
|
+
isStarted = NO;
|
|
174
|
+
|
|
95
175
|
return YES;
|
|
96
176
|
}
|
|
97
177
|
|
|
178
|
+
#pragma mark - CMMotionActivity → MAURActivity bridge
|
|
179
|
+
|
|
180
|
+
- (void) handleActivityUpdate:(CMMotionActivity *)activity
|
|
181
|
+
{
|
|
182
|
+
// CMMotionActivityConfidence: Low=0, Medium=1, High=2 → normalize to 0-100
|
|
183
|
+
// so activityConfidenceThreshold means the same thing as on Android.
|
|
184
|
+
int confidence;
|
|
185
|
+
switch (activity.confidence) {
|
|
186
|
+
case CMMotionActivityConfidenceLow: confidence = 20; break;
|
|
187
|
+
case CMMotionActivityConfidenceMedium: confidence = 40; break;
|
|
188
|
+
case CMMotionActivityConfidenceHigh: confidence = 80; break;
|
|
189
|
+
default: confidence = 0; break;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
NSNumber *thresholdN = currentConfig.activityConfidenceThreshold;
|
|
193
|
+
if (thresholdN != nil && confidence < thresholdN.intValue) {
|
|
194
|
+
DDLogDebug(@"%@ ignoring low-confidence activity confidence=%d threshold=%@", TAG, confidence, thresholdN);
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
MAURMotionType motionType;
|
|
199
|
+
NSString *typeStr;
|
|
200
|
+
if (activity.automotive) {
|
|
201
|
+
motionType = MAURMotionTypeAutomotive;
|
|
202
|
+
typeStr = @"IN_VEHICLE";
|
|
203
|
+
} else if (activity.cycling) {
|
|
204
|
+
motionType = MAURMotionTypeCycling;
|
|
205
|
+
typeStr = @"ON_BICYCLE";
|
|
206
|
+
} else if (activity.running) {
|
|
207
|
+
motionType = MAURMotionTypeRunning;
|
|
208
|
+
typeStr = @"RUNNING";
|
|
209
|
+
} else if (activity.walking) {
|
|
210
|
+
motionType = MAURMotionTypeWalking;
|
|
211
|
+
typeStr = @"WALKING";
|
|
212
|
+
} else if (activity.stationary) {
|
|
213
|
+
motionType = MAURMotionTypeNotMoving;
|
|
214
|
+
typeStr = @"STILL";
|
|
215
|
+
} else {
|
|
216
|
+
// CoreMotion fired "unknown" or no specific motion flag set. Do NOT
|
|
217
|
+
// collapse this to NotMoving — under uncertainty we keep the current
|
|
218
|
+
// tracking state (legacy behavior was to stop on UNKNOWN, which paused
|
|
219
|
+
// GPS unexpectedly during low-confidence motion gaps).
|
|
220
|
+
motionType = MAURMotionTypeUnknown;
|
|
221
|
+
typeStr = @"UNKNOWN";
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// Hop to main queue: location manager + delegate are main-thread-affine.
|
|
225
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
226
|
+
// UNKNOWN must not perturb the state machine: no emit, no lastMotionType
|
|
227
|
+
// mutation, no tracking change. Otherwise a sequence STILL → UNKNOWN
|
|
228
|
+
// would lose the STILL state and the next fix would be delivered as a
|
|
229
|
+
// regular location instead of stationary.
|
|
230
|
+
if (motionType == MAURMotionTypeUnknown) {
|
|
231
|
+
DDLogDebug(@"%@ ignoring UNKNOWN activity (state preserved, confidence=%d)", TAG, confidence);
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
BOOL changed = (motionType != self->lastMotionType);
|
|
236
|
+
self->lastMotionType = motionType;
|
|
237
|
+
|
|
238
|
+
if (changed) {
|
|
239
|
+
DDLogDebug(@"%@ activityTypeChanged: %@ confidence=%d", TAG, typeStr, confidence);
|
|
240
|
+
MAURActivity *act = [[MAURActivity alloc] init];
|
|
241
|
+
act.type = typeStr;
|
|
242
|
+
act.confidence = [NSNumber numberWithInt:confidence];
|
|
243
|
+
if (super.delegate && [super.delegate respondsToSelector:@selector(onActivityChanged:)]) {
|
|
244
|
+
[super.delegate onActivityChanged:act];
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// Tracking control:
|
|
249
|
+
// - ACTIVE motion (walking, running, automotive, cycling) → ensure tracking is on.
|
|
250
|
+
// - STILL → leave tracking running; it will be stopped after the next fix (legacy).
|
|
251
|
+
if (motionType != MAURMotionTypeNotMoving) {
|
|
252
|
+
[self startTracking];
|
|
253
|
+
}
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
#pragma mark - Location plumbing
|
|
258
|
+
|
|
98
259
|
- (void) startTracking
|
|
99
260
|
{
|
|
100
261
|
if (isTracking) {
|
|
@@ -105,7 +266,9 @@ static NSString * const Domain = @"com.marianhello";
|
|
|
105
266
|
if ([locationManager start:&error]) {
|
|
106
267
|
isTracking = YES;
|
|
107
268
|
} else {
|
|
108
|
-
[self.delegate onError:
|
|
269
|
+
if (self.delegate && [self.delegate respondsToSelector:@selector(onError:)]) {
|
|
270
|
+
[self.delegate onError:error];
|
|
271
|
+
}
|
|
109
272
|
}
|
|
110
273
|
}
|
|
111
274
|
|
|
@@ -129,9 +292,13 @@ static NSString * const Domain = @"com.marianhello";
|
|
|
129
292
|
|
|
130
293
|
- (void) onLocationsChanged:(NSArray*)locations
|
|
131
294
|
{
|
|
132
|
-
|
|
295
|
+
// v4.5.2: while NotMoving we only emit the stationary fix; the previous
|
|
296
|
+
// code fell through and also delivered each location as onLocationChanged,
|
|
297
|
+
// which produced phantom "moving" rows during a STILL window.
|
|
298
|
+
if (lastMotionType == MAURMotionTypeNotMoving) {
|
|
133
299
|
[self stopTracking];
|
|
134
300
|
[self.delegate onStationaryChanged:[MAURLocation fromCLLocation:[locations lastObject]]];
|
|
301
|
+
return;
|
|
135
302
|
}
|
|
136
303
|
|
|
137
304
|
for (CLLocation *location in locations) {
|
|
@@ -140,45 +307,6 @@ static NSString * const Domain = @"com.marianhello";
|
|
|
140
307
|
}
|
|
141
308
|
}
|
|
142
309
|
|
|
143
|
-
- (void)motionDetector:(SOMotionDetector *)motionDetector activityTypeChanged:(SOMotionActivity *)motionActivity;
|
|
144
|
-
{
|
|
145
|
-
int confidence = motionActivity.confidence;
|
|
146
|
-
SOMotionType motionType = motionActivity.motionType;
|
|
147
|
-
lastMotionType = motionType;
|
|
148
|
-
|
|
149
|
-
if (motionType != MotionTypeNotMoving) {
|
|
150
|
-
[self startTracking];
|
|
151
|
-
} else {
|
|
152
|
-
// we delay tracking stop after location is found
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
NSString *type;
|
|
156
|
-
switch (motionType) {
|
|
157
|
-
case MotionTypeNotMoving:
|
|
158
|
-
type = @"STILL";
|
|
159
|
-
break;
|
|
160
|
-
case MotionTypeWalking:
|
|
161
|
-
type = @"WALKING";
|
|
162
|
-
break;
|
|
163
|
-
case MotionTypeRunning:
|
|
164
|
-
type = @"RUNNING";
|
|
165
|
-
break;
|
|
166
|
-
case MotionTypeAutomotive:
|
|
167
|
-
type = @"IN_VEHICLE";
|
|
168
|
-
break;
|
|
169
|
-
case MotionTypeUnknown:
|
|
170
|
-
type = @"UNKNOWN";
|
|
171
|
-
break;
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
DDLogDebug(@"%@ activityTypeChanged: %@", TAG, type);
|
|
175
|
-
MAURActivity *activity = [[MAURActivity alloc] init];
|
|
176
|
-
activity.type = type;
|
|
177
|
-
activity.confidence = [NSNumber numberWithInt:confidence];
|
|
178
|
-
|
|
179
|
-
[super.delegate onActivityChanged:activity];
|
|
180
|
-
}
|
|
181
|
-
|
|
182
310
|
- (void) onError:(NSError*)error
|
|
183
311
|
{
|
|
184
312
|
[self.delegate onError:error];
|
|
@@ -197,6 +325,16 @@ static NSString * const Domain = @"com.marianhello";
|
|
|
197
325
|
- (void) onDestroy {
|
|
198
326
|
DDLogInfo(@"Destroying %@ ", TAG);
|
|
199
327
|
[self onStop:nil];
|
|
328
|
+
|
|
329
|
+
// v4.5.2: MAURLocationManager is a singleton shared with the other providers
|
|
330
|
+
// (RAW, DISTANCE). Release the delegate slot so a subsequent provider swap
|
|
331
|
+
// does not leave this destroyed instance as the active delegate.
|
|
332
|
+
if (locationManager != nil && locationManager.delegate == self) {
|
|
333
|
+
locationManager.delegate = nil;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
activityManager = nil;
|
|
337
|
+
activityQueue = nil;
|
|
200
338
|
}
|
|
201
339
|
|
|
202
340
|
@end
|