@josuelmm/capacitor-background-geolocation 1.0.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 (207) hide show
  1. package/JosuelmmCapacitorBackgroundGeolocation.podspec +34 -0
  2. package/LICENSE +17 -0
  3. package/NOTICE.md +32 -0
  4. package/Package.swift +45 -0
  5. package/README.md +402 -0
  6. package/android/build.gradle +79 -0
  7. package/android/proguard-rules.pro +1 -0
  8. package/android/src/main/AndroidManifest.xml +83 -0
  9. package/android/src/main/java/com/evgenii/jsevaluator/HandlerWrapper.java +18 -0
  10. package/android/src/main/java/com/evgenii/jsevaluator/JavaScriptInterface.java +22 -0
  11. package/android/src/main/java/com/evgenii/jsevaluator/JsEvaluator.java +133 -0
  12. package/android/src/main/java/com/evgenii/jsevaluator/JsFunctionCallFormatter.java +37 -0
  13. package/android/src/main/java/com/evgenii/jsevaluator/WebViewWrapper.java +71 -0
  14. package/android/src/main/java/com/evgenii/jsevaluator/interfaces/CallJavaResultInterface.java +8 -0
  15. package/android/src/main/java/com/evgenii/jsevaluator/interfaces/HandlerWrapperInterface.java +5 -0
  16. package/android/src/main/java/com/evgenii/jsevaluator/interfaces/JsCallback.java +10 -0
  17. package/android/src/main/java/com/evgenii/jsevaluator/interfaces/JsEvaluatorInterface.java +18 -0
  18. package/android/src/main/java/com/evgenii/jsevaluator/interfaces/WebViewWrapperInterface.java +14 -0
  19. package/android/src/main/java/com/josuelmm/capacitor/backgroundgeolocation/BackgroundGeolocationPlugin.java +898 -0
  20. package/android/src/main/java/com/josuelmm/capacitor/backgroundgeolocation/ConfigMapper.java +303 -0
  21. package/android/src/main/java/com/josuelmm/capacitor/backgroundgeolocation/HeadlessTaskRegistry.java +34 -0
  22. package/android/src/main/java/com/josuelmm/capacitor/backgroundgeolocation/JsEvaluatorTaskRunner.java +63 -0
  23. package/android/src/main/java/com/marianhello/bgloc/BackgroundGeolocationFacade.java +699 -0
  24. package/android/src/main/java/com/marianhello/bgloc/BootCompletedReceiver.java +103 -0
  25. package/android/src/main/java/com/marianhello/bgloc/Config.java +1155 -0
  26. package/android/src/main/java/com/marianhello/bgloc/ConnectivityListener.java +5 -0
  27. package/android/src/main/java/com/marianhello/bgloc/HttpPostService.java +362 -0
  28. package/android/src/main/java/com/marianhello/bgloc/LocationManager.java +138 -0
  29. package/android/src/main/java/com/marianhello/bgloc/PluginDelegate.java +45 -0
  30. package/android/src/main/java/com/marianhello/bgloc/PluginException.java +38 -0
  31. package/android/src/main/java/com/marianhello/bgloc/PostLocationTask.java +238 -0
  32. package/android/src/main/java/com/marianhello/bgloc/ResourceResolver.java +55 -0
  33. package/android/src/main/java/com/marianhello/bgloc/data/AbstractLocationTemplate.java +69 -0
  34. package/android/src/main/java/com/marianhello/bgloc/data/ArrayListLocationTemplate.java +88 -0
  35. package/android/src/main/java/com/marianhello/bgloc/data/BackgroundActivity.java +108 -0
  36. package/android/src/main/java/com/marianhello/bgloc/data/BackgroundLocation.java +1088 -0
  37. package/android/src/main/java/com/marianhello/bgloc/data/ConfigJsonMapper.java +211 -0
  38. package/android/src/main/java/com/marianhello/bgloc/data/ConfigurationDAO.java +13 -0
  39. package/android/src/main/java/com/marianhello/bgloc/data/DAOFactory.java +17 -0
  40. package/android/src/main/java/com/marianhello/bgloc/data/HashMapLocationTemplate.java +82 -0
  41. package/android/src/main/java/com/marianhello/bgloc/data/LocationDAO.java +27 -0
  42. package/android/src/main/java/com/marianhello/bgloc/data/LocationTemplate.java +12 -0
  43. package/android/src/main/java/com/marianhello/bgloc/data/LocationTemplateFactory.java +71 -0
  44. package/android/src/main/java/com/marianhello/bgloc/data/LocationTransform.java +19 -0
  45. package/android/src/main/java/com/marianhello/bgloc/data/SessionLocationDAO.java +18 -0
  46. package/android/src/main/java/com/marianhello/bgloc/data/provider/ContentProviderLocationDAO.java +406 -0
  47. package/android/src/main/java/com/marianhello/bgloc/data/provider/LocationContentProvider.java +321 -0
  48. package/android/src/main/java/com/marianhello/bgloc/data/sqlite/SQLiteConfigurationContract.java +94 -0
  49. package/android/src/main/java/com/marianhello/bgloc/data/sqlite/SQLiteConfigurationDAO.java +227 -0
  50. package/android/src/main/java/com/marianhello/bgloc/data/sqlite/SQLiteLocationContract.java +122 -0
  51. package/android/src/main/java/com/marianhello/bgloc/data/sqlite/SQLiteLocationDAO.java +550 -0
  52. package/android/src/main/java/com/marianhello/bgloc/data/sqlite/SQLiteOpenHelper.java +189 -0
  53. package/android/src/main/java/com/marianhello/bgloc/data/sqlite/SQLiteSessionContract.java +74 -0
  54. package/android/src/main/java/com/marianhello/bgloc/data/sqlite/SQLiteSessionLocationDAO.java +169 -0
  55. package/android/src/main/java/com/marianhello/bgloc/driving/DrivingEventsDetector.java +265 -0
  56. package/android/src/main/java/com/marianhello/bgloc/headless/AbstractTaskRunner.java +15 -0
  57. package/android/src/main/java/com/marianhello/bgloc/headless/ActivityTask.java +48 -0
  58. package/android/src/main/java/com/marianhello/bgloc/headless/JsCallback.java +10 -0
  59. package/android/src/main/java/com/marianhello/bgloc/headless/LocationTask.java +60 -0
  60. package/android/src/main/java/com/marianhello/bgloc/headless/StationaryTask.java +25 -0
  61. package/android/src/main/java/com/marianhello/bgloc/headless/Task.java +8 -0
  62. package/android/src/main/java/com/marianhello/bgloc/headless/TaskRunner.java +5 -0
  63. package/android/src/main/java/com/marianhello/bgloc/headless/TaskRunnerFactory.java +8 -0
  64. package/android/src/main/java/com/marianhello/bgloc/http/UrlTemplateResolver.java +115 -0
  65. package/android/src/main/java/com/marianhello/bgloc/oem/BatteryOemHelper.java +214 -0
  66. package/android/src/main/java/com/marianhello/bgloc/provider/AbstractLocationProvider.java +218 -0
  67. package/android/src/main/java/com/marianhello/bgloc/provider/ActivityRecognitionLocationProvider.java +385 -0
  68. package/android/src/main/java/com/marianhello/bgloc/provider/DistanceFilterLocationProvider.java +685 -0
  69. package/android/src/main/java/com/marianhello/bgloc/provider/LocationProvider.java +32 -0
  70. package/android/src/main/java/com/marianhello/bgloc/provider/LocationProviderFactory.java +47 -0
  71. package/android/src/main/java/com/marianhello/bgloc/provider/ProviderDelegate.java +12 -0
  72. package/android/src/main/java/com/marianhello/bgloc/provider/RawLocationProvider.java +175 -0
  73. package/android/src/main/java/com/marianhello/bgloc/sensor/SensorFusionDetector.java +199 -0
  74. package/android/src/main/java/com/marianhello/bgloc/service/LocationService.java +16 -0
  75. package/android/src/main/java/com/marianhello/bgloc/service/LocationServiceImpl.java +1531 -0
  76. package/android/src/main/java/com/marianhello/bgloc/service/LocationServiceInfo.java +6 -0
  77. package/android/src/main/java/com/marianhello/bgloc/service/LocationServiceInfoImpl.java +41 -0
  78. package/android/src/main/java/com/marianhello/bgloc/service/LocationServiceIntentBuilder.java +203 -0
  79. package/android/src/main/java/com/marianhello/bgloc/service/LocationServiceProxy.java +156 -0
  80. package/android/src/main/java/com/marianhello/bgloc/sync/AccountHelper.java +39 -0
  81. package/android/src/main/java/com/marianhello/bgloc/sync/Authenticator.java +68 -0
  82. package/android/src/main/java/com/marianhello/bgloc/sync/AuthenticatorService.java +28 -0
  83. package/android/src/main/java/com/marianhello/bgloc/sync/BatchManager.java +311 -0
  84. package/android/src/main/java/com/marianhello/bgloc/sync/NotificationHelper.java +148 -0
  85. package/android/src/main/java/com/marianhello/bgloc/sync/SyncAdapter.java +301 -0
  86. package/android/src/main/java/com/marianhello/bgloc/sync/SyncService.java +68 -0
  87. package/android/src/main/java/com/marianhello/logging/DBLogReader.java +208 -0
  88. package/android/src/main/java/com/marianhello/logging/LogEntry.java +99 -0
  89. package/android/src/main/java/com/marianhello/logging/LoggerManager.java +70 -0
  90. package/android/src/main/java/com/marianhello/logging/UncaughtExceptionLogger.java +36 -0
  91. package/android/src/main/java/com/marianhello/utils/CloneHelper.java +22 -0
  92. package/android/src/main/java/com/marianhello/utils/Convert.java +56 -0
  93. package/android/src/main/java/com/marianhello/utils/TextUtils.java +72 -0
  94. package/android/src/main/java/com/marianhello/utils/ToneGenerator.java +68 -0
  95. package/android/src/main/java/org/apache/commons/io/Charsets.java +153 -0
  96. package/android/src/main/java/org/apache/commons/io/input/ReversedLinesFileReader.java +344 -0
  97. package/android/src/main/java/org/chromium/content/browser/ThreadUtils.java +134 -0
  98. package/android/src/main/java/ru/andremoniy/sqlbuilder/SqlExpression.java +398 -0
  99. package/android/src/main/java/ru/andremoniy/sqlbuilder/SqlSelectStatement.java +671 -0
  100. package/android/src/main/java/ru/andremoniy/sqlbuilder/SqlStatement.java +29 -0
  101. package/android/src/main/java/ru/andremoniy/utils/TextUtils.java +61 -0
  102. package/android/src/main/res/mipmap-hdpi/ic_launcher.png +0 -0
  103. package/android/src/main/res/mipmap-mdpi/ic_launcher.png +0 -0
  104. package/android/src/main/res/mipmap-xhdpi/ic_launcher.png +0 -0
  105. package/android/src/main/res/mipmap-xxhdpi/ic_launcher.png +0 -0
  106. package/android/src/main/res/mipmap-xxxhdpi/ic_launcher.png +0 -0
  107. package/android/src/main/res/values/strings.xml +15 -0
  108. package/android/src/main/res/xml/authenticator.xml +7 -0
  109. package/android/src/main/res/xml/syncadapter.xml +9 -0
  110. package/dist/esm/definitions.d.ts +1052 -0
  111. package/dist/esm/definitions.js +142 -0
  112. package/dist/esm/definitions.js.map +1 -0
  113. package/dist/esm/index.d.ts +8 -0
  114. package/dist/esm/index.js +23 -0
  115. package/dist/esm/index.js.map +1 -0
  116. package/dist/esm/web.d.ts +92 -0
  117. package/dist/esm/web.js +242 -0
  118. package/dist/esm/web.js.map +1 -0
  119. package/dist/plugin.cjs.js +415 -0
  120. package/dist/plugin.cjs.js.map +1 -0
  121. package/dist/plugin.js +418 -0
  122. package/dist/plugin.js.map +1 -0
  123. package/ios/Sources/BackgroundGeolocationPlugin/BackgroundGeolocationPlugin-Bridging-Header.h +18 -0
  124. package/ios/Sources/BackgroundGeolocationPlugin/BackgroundGeolocationPlugin.m +52 -0
  125. package/ios/Sources/BackgroundGeolocationPlugin/BackgroundGeolocationPlugin.swift +750 -0
  126. package/ios/Tests/BackgroundGeolocationPluginTests/BackgroundGeolocationPluginTests.swift +12 -0
  127. package/ios/common/BackgroundGeolocation/CocoaLumberjack.h +1945 -0
  128. package/ios/common/BackgroundGeolocation/CocoaLumberjack.m +5255 -0
  129. package/ios/common/BackgroundGeolocation/FMDB.h +2357 -0
  130. package/ios/common/BackgroundGeolocation/FMDB.m +2672 -0
  131. package/ios/common/BackgroundGeolocation/FMDBLogger.h +42 -0
  132. package/ios/common/BackgroundGeolocation/FMDBLogger.m +264 -0
  133. package/ios/common/BackgroundGeolocation/INTULocationManager/INTUHeadingRequest.h +41 -0
  134. package/ios/common/BackgroundGeolocation/INTULocationManager/INTUHeadingRequest.m +68 -0
  135. package/ios/common/BackgroundGeolocation/INTULocationManager/INTULocationManager+Internal.h +33 -0
  136. package/ios/common/BackgroundGeolocation/INTULocationManager/INTULocationManager.h +178 -0
  137. package/ios/common/BackgroundGeolocation/INTULocationManager/INTULocationManager.m +1025 -0
  138. package/ios/common/BackgroundGeolocation/INTULocationManager/INTULocationRequest.h +103 -0
  139. package/ios/common/BackgroundGeolocation/INTULocationManager/INTULocationRequest.m +238 -0
  140. package/ios/common/BackgroundGeolocation/INTULocationManager/INTULocationRequestDefines.h +163 -0
  141. package/ios/common/BackgroundGeolocation/INTULocationManager/INTURequestIDGenerator.h +39 -0
  142. package/ios/common/BackgroundGeolocation/INTULocationManager/INTURequestIDGenerator.m +37 -0
  143. package/ios/common/BackgroundGeolocation/MAURAbstractLocationProvider.h +51 -0
  144. package/ios/common/BackgroundGeolocation/MAURAbstractLocationProvider.m +53 -0
  145. package/ios/common/BackgroundGeolocation/MAURActivity.h +23 -0
  146. package/ios/common/BackgroundGeolocation/MAURActivity.m +52 -0
  147. package/ios/common/BackgroundGeolocation/MAURActivityLocationProvider.h +18 -0
  148. package/ios/common/BackgroundGeolocation/MAURActivityLocationProvider.m +340 -0
  149. package/ios/common/BackgroundGeolocation/MAURBackgroundGeolocationFacade.h +88 -0
  150. package/ios/common/BackgroundGeolocation/MAURBackgroundGeolocationFacade.m +1193 -0
  151. package/ios/common/BackgroundGeolocation/MAURBackgroundSync.h +46 -0
  152. package/ios/common/BackgroundGeolocation/MAURBackgroundSync.m +283 -0
  153. package/ios/common/BackgroundGeolocation/MAURBackgroundTaskManager.h +25 -0
  154. package/ios/common/BackgroundGeolocation/MAURBackgroundTaskManager.m +105 -0
  155. package/ios/common/BackgroundGeolocation/MAURConfig.h +99 -0
  156. package/ios/common/BackgroundGeolocation/MAURConfig.m +636 -0
  157. package/ios/common/BackgroundGeolocation/MAURConfigurationContract.h +53 -0
  158. package/ios/common/BackgroundGeolocation/MAURConfigurationContract.m +54 -0
  159. package/ios/common/BackgroundGeolocation/MAURDistanceFilterLocationProvider.h +20 -0
  160. package/ios/common/BackgroundGeolocation/MAURDistanceFilterLocationProvider.m +550 -0
  161. package/ios/common/BackgroundGeolocation/MAURGeolocationOpenHelper.h +17 -0
  162. package/ios/common/BackgroundGeolocation/MAURGeolocationOpenHelper.m +124 -0
  163. package/ios/common/BackgroundGeolocation/MAURLocation.h +73 -0
  164. package/ios/common/BackgroundGeolocation/MAURLocation.m +392 -0
  165. package/ios/common/BackgroundGeolocation/MAURLocationContract.h +38 -0
  166. package/ios/common/BackgroundGeolocation/MAURLocationContract.m +39 -0
  167. package/ios/common/BackgroundGeolocation/MAURLocationManager.h +53 -0
  168. package/ios/common/BackgroundGeolocation/MAURLocationManager.m +305 -0
  169. package/ios/common/BackgroundGeolocation/MAURLogReader.h +26 -0
  170. package/ios/common/BackgroundGeolocation/MAURLogReader.m +122 -0
  171. package/ios/common/BackgroundGeolocation/MAURLogging.h +19 -0
  172. package/ios/common/BackgroundGeolocation/MAURPostLocationTask.h +53 -0
  173. package/ios/common/BackgroundGeolocation/MAURPostLocationTask.m +367 -0
  174. package/ios/common/BackgroundGeolocation/MAURProviderDelegate.h +52 -0
  175. package/ios/common/BackgroundGeolocation/MAURRawLocationProvider.h +18 -0
  176. package/ios/common/BackgroundGeolocation/MAURRawLocationProvider.m +138 -0
  177. package/ios/common/BackgroundGeolocation/MAURSQLiteConfigurationDAO.h +26 -0
  178. package/ios/common/BackgroundGeolocation/MAURSQLiteConfigurationDAO.m +335 -0
  179. package/ios/common/BackgroundGeolocation/MAURSQLiteHelper.h +57 -0
  180. package/ios/common/BackgroundGeolocation/MAURSQLiteHelper.m +93 -0
  181. package/ios/common/BackgroundGeolocation/MAURSQLiteLocationDAO.h +52 -0
  182. package/ios/common/BackgroundGeolocation/MAURSQLiteLocationDAO.m +520 -0
  183. package/ios/common/BackgroundGeolocation/MAURSQLiteOpenHelper.h +32 -0
  184. package/ios/common/BackgroundGeolocation/MAURSQLiteOpenHelper.m +276 -0
  185. package/ios/common/BackgroundGeolocation/MAURSensorFusionDetector.h +41 -0
  186. package/ios/common/BackgroundGeolocation/MAURSensorFusionDetector.m +137 -0
  187. package/ios/common/BackgroundGeolocation/MAURSessionLocationContract.h +29 -0
  188. package/ios/common/BackgroundGeolocation/MAURSessionLocationContract.m +31 -0
  189. package/ios/common/BackgroundGeolocation/MAURSessionLocationDAO.h +25 -0
  190. package/ios/common/BackgroundGeolocation/MAURSessionLocationDAO.m +153 -0
  191. package/ios/common/BackgroundGeolocation/MAURUncaughtExceptionLogger.h +20 -0
  192. package/ios/common/BackgroundGeolocation/MAURUncaughtExceptionLogger.m +62 -0
  193. package/ios/common/BackgroundGeolocation/MAURUrlTemplateResolver.h +31 -0
  194. package/ios/common/BackgroundGeolocation/MAURUrlTemplateResolver.m +107 -0
  195. package/ios/common/BackgroundGeolocation/Reachability.h +102 -0
  196. package/ios/common/BackgroundGeolocation/Reachability.m +475 -0
  197. package/ios/common/BackgroundGeolocation/SQLQueryBuilder/README.md +170 -0
  198. package/ios/common/BackgroundGeolocation/SQLQueryBuilder/ext/NSString+ZIMString.h +55 -0
  199. package/ios/common/BackgroundGeolocation/SQLQueryBuilder/ext/NSString+ZIMString.m +47 -0
  200. package/ios/common/BackgroundGeolocation/SQLQueryBuilder/sql/ZIMSqlDataManipulationCommand.h +27 -0
  201. package/ios/common/BackgroundGeolocation/SQLQueryBuilder/sql/ZIMSqlExpression.h +250 -0
  202. package/ios/common/BackgroundGeolocation/SQLQueryBuilder/sql/ZIMSqlExpression.m +259 -0
  203. package/ios/common/BackgroundGeolocation/SQLQueryBuilder/sql/ZIMSqlSelectStatement.h +360 -0
  204. package/ios/common/BackgroundGeolocation/SQLQueryBuilder/sql/ZIMSqlSelectStatement.m +427 -0
  205. package/ios/common/BackgroundGeolocation/SQLQueryBuilder/sql/ZIMSqlStatement.h +37 -0
  206. package/ios/common/BackgroundGeolocation/module.modulemap +16 -0
  207. package/package.json +82 -0
@@ -0,0 +1,1193 @@
1
+ //
2
+ // MAURBackgroundGeolocationFacade.m
3
+ //
4
+ // Created by Marian Hello on 04/06/16.
5
+ // Version 2.0.0
6
+ //
7
+ // According to apache license
8
+ //
9
+ // This is class is using code from christocracy cordova-plugin-background-geolocation plugin
10
+ // https://github.com/christocracy/cordova-plugin-background-geolocation
11
+ //
12
+
13
+ #import <UIKit/UIKit.h>
14
+ #import <CoreLocation/CoreLocation.h>
15
+ #import <AudioToolbox/AudioToolbox.h>
16
+ #import "MAURBackgroundGeolocationFacade.h"
17
+ #import "MAURPostLocationTask.h"
18
+ #import "MAURSQLiteConfigurationDAO.h"
19
+ #import "MAURSQLiteLocationDAO.h"
20
+ #import "MAURSessionLocationDAO.h"
21
+ #import "MAURBackgroundTaskManager.h"
22
+ #import "MAURLogging.h"
23
+ #import "FMDBLogger.h"
24
+ #import "MAURLogReader.h"
25
+ #import "MAURLocationManager.h"
26
+ #import "MAURActivityLocationProvider.h"
27
+ #import "MAURDistanceFilterLocationProvider.h"
28
+ #import "MAURRawLocationProvider.h"
29
+ #import "MAURUncaughtExceptionLogger.h"
30
+ #import "MAURPostLocationTask.h"
31
+ #import "INTULocationManager.h"
32
+ #import "MAURSensorFusionDetector.h"
33
+
34
+ // error messages
35
+ #define CONFIGURE_ERROR_MSG "Configuration error."
36
+ #define SERVICE_ERROR_MSG "Cannot start service error."
37
+ #define UNKNOWN_LOCATION_PROVIDER_MSG "Unknown location provider."
38
+
39
+ // Position errors
40
+ // https://developer.mozilla.org/en-US/docs/Web/API/PositionError
41
+ #define PERMISSION_DENIED 1
42
+ #define POSITION_UNAVAILABLE 2
43
+ #define TIMEOUT 3
44
+
45
+ static NSString * const BGGeolocationDomain = @"com.marianhello";
46
+ static NSString * const TAG = @"BgGeo";
47
+
48
+ FMDBLogger *sqliteLogger;
49
+
50
+ @interface MAURBackgroundGeolocationFacade () <MAURProviderDelegate, MAURPostLocationTaskDelegate, MAURSensorFusionListener>
51
+ @end
52
+
53
+ // v3.5 Phase 4: notification name for heartbeat events. CDVBackgroundGeolocation observes
54
+ // it to forward into the JS event "heartbeat" with the latest known location.
55
+ NSString * const MAURHeartbeatNotification = @"MAURHeartbeatNotification";
56
+ // v4.0 Phase 6: driver-insight notifications.
57
+ NSString * const MAURTripStartNotification = @"MAURTripStartNotification";
58
+ NSString * const MAURTripEndNotification = @"MAURTripEndNotification";
59
+ NSString * const MAURMovingNotification = @"MAURMovingNotification";
60
+ NSString * const MAURStoppedNotification = @"MAURStoppedNotification";
61
+ NSString * const MAURSpeedingNotification = @"MAURSpeedingNotification";
62
+ NSString * const MAURProviderChangeNotification = @"MAURProviderChangeNotification";
63
+ NSString * const MAURSOSNotification = @"MAURSOSNotification";
64
+ // v4.1
65
+ NSString * const MAURHardBrakeNotification = @"MAURHardBrakeNotification";
66
+ NSString * const MAURRapidAccelerationNotification = @"MAURRapidAccelerationNotification";
67
+ NSString * const MAURSharpTurnNotification = @"MAURSharpTurnNotification";
68
+ NSString * const MAURPossibleCrashNotification = @"MAURPossibleCrashNotification";
69
+ // v4.2
70
+ NSString * const MAURPhoneUsageWhileDrivingNotification = @"MAURPhoneUsageWhileDrivingNotification";
71
+
72
+ @implementation MAURBackgroundGeolocationFacade {
73
+ BOOL isStarted;
74
+ MAUROperationalMode operationMode;
75
+
76
+ UILocalNotification *localNotification;
77
+
78
+ // configurable options
79
+ MAURConfig *_config;
80
+
81
+ MAURLocation *stationaryLocation;
82
+ MAURLocation *lastReceivedLocation; // v3.5 Phase 4: heartbeat payload
83
+ NSTimer *heartbeatTimer; // v3.5 Phase 4
84
+ MAURAbstractLocationProvider<MAURLocationProvider> *locationProvider;
85
+ MAURPostLocationTask *postLocationTask;
86
+
87
+ // v4.0 Phase 6: driver-insights state
88
+ BOOL drIsMoving;
89
+ BOOL drTripActive;
90
+ NSTimeInterval drTripStartedAt;
91
+ double drTripDistanceMeters;
92
+ BOOL drHasPrev;
93
+ double drPrevLat, drPrevLon;
94
+ NSTimeInterval drAboveTripSpeedSince;
95
+ NSTimeInterval drBelowMovingSince;
96
+ BOOL drWasSpeeding;
97
+ NSString *drLastProvider;
98
+ // v4.1 GPS-derived sensor-like state
99
+ double drPrevSpeed;
100
+ NSTimeInterval drPrevSpeedAt;
101
+ double drPrevBearing;
102
+ BOOL drHasPrevBearing;
103
+ NSTimeInterval drPrevBearingAt;
104
+ NSTimeInterval drLastHardBrakeAt, drLastRapidAccelAt, drLastSharpTurnAt, drLastCrashAt;
105
+ // v4.2 sensor fusion
106
+ MAURSensorFusionDetector *sensorFusion;
107
+ // v4.3 — events buffered when no simultaneous fix is available; drained onto next location.
108
+ NSMutableArray *pendingDrivingEvents;
109
+ }
110
+
111
+
112
+ - (instancetype) init
113
+ {
114
+ self = [super init];
115
+
116
+ if (self == nil) {
117
+ return self;
118
+ }
119
+
120
+ [DDLog addLogger:[DDASLLogger sharedInstance] withLevel:DDLogLevelInfo];
121
+ [DDLog addLogger:[DDTTYLogger sharedInstance] withLevel:DDLogLevelDebug];
122
+
123
+ sqliteLogger = [[FMDBLogger alloc] initWithLogDirectory:[self loggerDirectory]];
124
+ sqliteLogger.saveThreshold = 1;
125
+ sqliteLogger.saveInterval = 0;
126
+ sqliteLogger.maxAge = 60 * 60 * 24 * 7; // 7 days
127
+ sqliteLogger.deleteInterval = 60 * 60 * 24; // 1 day
128
+ sqliteLogger.deleteOnEverySave = NO;
129
+
130
+ [DDLog addLogger:sqliteLogger withLevel:DDLogLevelDebug];
131
+
132
+ MAHUncaughtExceptionLogger *logger = mah_get_uncaught_exception_logger();
133
+ logger->setEnabled(YES);
134
+
135
+ postLocationTask = [[MAURPostLocationTask alloc] init];
136
+ postLocationTask.delegate = self;
137
+
138
+ localNotification = [[UILocalNotification alloc] init];
139
+ localNotification.timeZone = [NSTimeZone defaultTimeZone];
140
+
141
+ isStarted = NO;
142
+ pendingDrivingEvents = [[NSMutableArray alloc] init];
143
+
144
+ // v4.5.1 — wire pending events + battery snapshot into the post task so they run AFTER
145
+ // any locationTransform that may produce a new instance / return nil. The previous flow
146
+ // (flush BEFORE add:) lost buffered events whenever the transform returned nil.
147
+ postLocationTask.pendingDrivingEventsBuffer = pendingDrivingEvents;
148
+ __weak typeof(self) weakSelf = self;
149
+ postLocationTask.attachBatterySnapshot = ^(MAURLocation * _Nonnull loc) {
150
+ __strong typeof(self) strongSelf = weakSelf;
151
+ if (strongSelf == nil) return;
152
+ BOOL includeBat = (strongSelf->_config == nil
153
+ || strongSelf->_config.includeBattery == nil
154
+ || [strongSelf->_config.includeBattery boolValue]);
155
+ if (includeBat) [strongSelf attachBatterySnapshotTo:loc];
156
+ };
157
+
158
+ return self;
159
+ }
160
+
161
+ /**
162
+ * configure manager
163
+ * @param {Config} configuration
164
+ * @param {NSError} optional error
165
+ */
166
+ - (BOOL) configure:(MAURConfig*)config error:(NSError * __autoreleasing *)outError
167
+ {
168
+ __block NSError *error = nil;
169
+
170
+ MAURConfig *currentConfig = [self getConfig];
171
+ _config = [MAURConfig merge:currentConfig withConfig:config];
172
+
173
+ DDLogInfo(@"%@ #configure: %@", TAG, _config);
174
+
175
+ postLocationTask.config = _config;
176
+
177
+ MAURSQLiteConfigurationDAO* configDAO = [MAURSQLiteConfigurationDAO sharedInstance];
178
+ [configDAO persistConfiguration:_config];
179
+
180
+ // ios 8 requires permissions to send local-notifications
181
+ if ([_config isDebugging]) {
182
+ [self runOnMainThread:^{
183
+ UIApplication *app = [UIApplication sharedApplication];
184
+ if ([[UIApplication sharedApplication]respondsToSelector:@selector(currentUserNotificationSettings)]) {
185
+ UIUserNotificationType wantedTypes = UIUserNotificationTypeBadge|UIUserNotificationTypeSound|UIUserNotificationTypeAlert;
186
+ UIUserNotificationSettings *currentSettings = [app currentUserNotificationSettings];
187
+ if (!currentSettings || (currentSettings.types != wantedTypes)) {
188
+ if ([app respondsToSelector:@selector(registerUserNotificationSettings:)]) {
189
+ [app registerUserNotificationSettings:[UIUserNotificationSettings settingsForTypes:wantedTypes categories:nil]];
190
+ }
191
+ }
192
+ }
193
+ }];
194
+ }
195
+
196
+ if (isStarted) {
197
+ // Note: CLLocationManager must be created on a thread with an active run loop (main thread)
198
+ [self runOnMainThread:^{
199
+
200
+ // requesting new provider
201
+ if (![currentConfig.locationProvider isEqual:_config.locationProvider]) {
202
+ [locationProvider onDestroy]; // destroy current provider
203
+ locationProvider = [self getProvider:_config.locationProvider.intValue error:&error];
204
+ }
205
+
206
+ if (locationProvider == nil) {
207
+ return;
208
+ }
209
+
210
+ // trap configuration errors
211
+ if (![locationProvider onConfigure:_config error:&error]) {
212
+ return;
213
+ }
214
+
215
+ isStarted = [locationProvider onStart:&error];
216
+ locationProvider.delegate = self;
217
+ }];
218
+
219
+ // v4.1: hot-reload heartbeat scheduler if heartbeatInterval changed.
220
+ NSInteger prevHb = currentConfig.heartbeatInterval != nil ? [currentConfig.heartbeatInterval integerValue] : 0;
221
+ NSInteger newHb = _config.heartbeatInterval != nil ? [_config.heartbeatInterval integerValue] : 0;
222
+ if (prevHb != newHb) {
223
+ [self scheduleHeartbeat]; // cancels and reschedules; is a no-op if 0.
224
+ }
225
+ // Driver-insights detector reads `_config.drivingEvents` on every feed; no rebuild needed
226
+ // unless the dictionary identity changed in a way that toggles `enabled`. Always reset
227
+ // accumulators to apply the new thresholds cleanly from this point on.
228
+ if (![[currentConfig.drivingEvents description] isEqualToString:[_config.drivingEvents description]]) {
229
+ [self drivingDetectorReset];
230
+ // v4.2: re-evaluate sensor fusion as well (might have just been enabled/disabled).
231
+ [self configureSensorFusion];
232
+ if (isStarted) [sensorFusion start];
233
+ }
234
+ }
235
+
236
+ if (error != nil) {
237
+ if (outError != nil) {
238
+ NSDictionary *userInfo = @{
239
+ NSLocalizedDescriptionKey: NSLocalizedString(@CONFIGURE_ERROR_MSG, nil),
240
+ NSUnderlyingErrorKey : error
241
+ };
242
+ *outError = [NSError errorWithDomain:BGGeolocationDomain code:MAURBGConfigureError userInfo:userInfo];
243
+ }
244
+
245
+ return NO;
246
+ }
247
+
248
+ return YES;
249
+ }
250
+
251
+ /**
252
+ * Turn on background geolocation
253
+ * in case of failure it calls error callback from configure method
254
+ * may fire two callback when location services are disabled and when authorization failed
255
+ */
256
+ - (BOOL) start:(NSError * __autoreleasing *)outError
257
+ {
258
+ DDLogInfo(@"%@ #start: %d", TAG, isStarted);
259
+
260
+ if (isStarted) {
261
+ return NO;
262
+ }
263
+
264
+ __block NSError *error = nil;
265
+ MAURConfig *config = [self getConfig];
266
+
267
+ postLocationTask.config = config;
268
+ [postLocationTask start];
269
+
270
+ // Note: CLLocationManager must be created on a thread with an active run loop (main thread)
271
+ [self runOnMainThread:^{
272
+ locationProvider = [self getProvider:config.locationProvider.intValue error:&error];
273
+
274
+ if (locationProvider == nil) {
275
+ return;
276
+ }
277
+
278
+ // trap configuration errors
279
+ if (![locationProvider onConfigure:config error:&error]) {
280
+ return;
281
+ }
282
+
283
+ isStarted = [locationProvider onStart:&error];
284
+ locationProvider.delegate = self;
285
+ }];
286
+
287
+
288
+ if (!isStarted) {
289
+ if (outError != nil) {
290
+ *outError = error;
291
+ }
292
+
293
+ return NO;
294
+ }
295
+
296
+ // v3.5 Phase 4: schedule heartbeat once provider is up.
297
+ [self scheduleHeartbeat];
298
+ // v4.2 Phase 8: configure & start sensor fusion if requested.
299
+ [self configureSensorFusion];
300
+ [sensorFusion start];
301
+
302
+ return isStarted;
303
+ }
304
+
305
+ /**
306
+ * Turn off background geolocation
307
+ */
308
+ - (BOOL) stop:(NSError * __autoreleasing *)outError
309
+ {
310
+ DDLogInfo(@"%@ #stop", TAG);
311
+
312
+ if (!isStarted) {
313
+ return YES;
314
+ }
315
+
316
+ // v3.5 Phase 4: cancel heartbeat scheduler.
317
+ [self cancelHeartbeat];
318
+ // v4.0 Phase 6: reset driver-insights state machine.
319
+ [self drivingDetectorReset];
320
+ // v4.2 Phase 8: stop sensor fusion sampling.
321
+ sensorFusion.tripActive = NO;
322
+ [sensorFusion stop];
323
+
324
+ [postLocationTask stop];
325
+
326
+ [self runOnMainThread:^{
327
+ isStarted = ![locationProvider onStop:outError];
328
+ }];
329
+
330
+ return isStarted;
331
+ }
332
+
333
+ // v3.5 Phase 4: heartbeat scheduler.
334
+ - (void) scheduleHeartbeat
335
+ {
336
+ [self cancelHeartbeat];
337
+ if (_config == nil || _config.heartbeatInterval == nil) return;
338
+ NSInteger ms = [_config.heartbeatInterval integerValue];
339
+ if (ms <= 0) return;
340
+ NSTimeInterval seconds = ms / 1000.0;
341
+ DDLogDebug(@"%@ scheduling heartbeat every %.2fs", TAG, seconds);
342
+ dispatch_async(dispatch_get_main_queue(), ^{
343
+ heartbeatTimer = [NSTimer scheduledTimerWithTimeInterval:seconds
344
+ target:self
345
+ selector:@selector(onHeartbeatTick:)
346
+ userInfo:nil
347
+ repeats:YES];
348
+ });
349
+ }
350
+
351
+ - (void) cancelHeartbeat
352
+ {
353
+ if (heartbeatTimer != nil) {
354
+ [heartbeatTimer invalidate];
355
+ heartbeatTimer = nil;
356
+ }
357
+ }
358
+
359
+ - (void) onHeartbeatTick:(NSTimer *)timer
360
+ {
361
+ NSDictionary *userInfo = lastReceivedLocation != nil
362
+ ? @{ @"location": lastReceivedLocation }
363
+ : @{};
364
+ [[NSNotificationCenter defaultCenter] postNotificationName:MAURHeartbeatNotification
365
+ object:self
366
+ userInfo:userInfo];
367
+ }
368
+
369
+ #pragma mark - v4.0 Phase 6 driver-insights state machine
370
+
371
+ - (void) drivingDetectorReset
372
+ {
373
+ drIsMoving = NO;
374
+ drTripActive = NO;
375
+ drTripStartedAt = 0;
376
+ drTripDistanceMeters = 0;
377
+ drHasPrev = NO;
378
+ drAboveTripSpeedSince = 0;
379
+ drBelowMovingSince = 0;
380
+ drWasSpeeding = NO;
381
+ drLastProvider = nil;
382
+ drPrevSpeed = 0;
383
+ drPrevSpeedAt = 0;
384
+ drPrevBearing = 0;
385
+ drHasPrevBearing = NO;
386
+ drPrevBearingAt = 0;
387
+ drLastHardBrakeAt = drLastRapidAccelAt = drLastSharpTurnAt = drLastCrashAt = 0;
388
+ }
389
+
390
+ #pragma mark - v4.2 Phase 8 sensor fusion
391
+
392
+ - (void) configureSensorFusion
393
+ {
394
+ NSDictionary *de = _config.drivingEvents;
395
+ BOOL want = [de isKindOfClass:[NSDictionary class]]
396
+ && [de[@"enabled"] boolValue]
397
+ && [de[@"sensorFusion"] boolValue];
398
+ if (!want) {
399
+ [sensorFusion stop];
400
+ sensorFusion = nil;
401
+ return;
402
+ }
403
+ if (sensorFusion == nil) {
404
+ sensorFusion = [[MAURSensorFusionDetector alloc] init];
405
+ sensorFusion.listener = self;
406
+ }
407
+ sensorFusion.enabled = YES;
408
+ if (de[@"crashImpactG"]) sensorFusion.crashImpactG = [de[@"crashImpactG"] doubleValue];
409
+ if (de[@"sensorCrashCooldownMs"]) sensorFusion.crashCooldownMs = [de[@"sensorCrashCooldownMs"] doubleValue];
410
+ if (de[@"phoneUsageWindowMs"]) sensorFusion.phoneUsageWindowMs = [de[@"phoneUsageWindowMs"] doubleValue];
411
+ if (de[@"phoneUsageCooldownMs"]) sensorFusion.phoneUsageCooldownMs = [de[@"phoneUsageCooldownMs"] doubleValue];
412
+ // v4.2 hot-reload: re-inject current trip state + last location so a config change
413
+ // mid-trip starts the sensor pipeline in the correct mode.
414
+ sensorFusion.tripActive = drTripActive;
415
+ sensorFusion.lastLocation = lastReceivedLocation;
416
+ }
417
+
418
+ // MAURSensorFusionListener
419
+ - (void) onSensorCrashWithImpactG:(double)impactG location:(MAURLocation *)location
420
+ {
421
+ [self bufferPendingEvent:@"possibleCrash" extra:@{@"value": @(impactG), @"source": @"sensor"}];
422
+ NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
423
+ if (location != nil) userInfo[@"location"] = location;
424
+ userInfo[@"value"] = @(impactG);
425
+ userInfo[@"source"] = @"sensor";
426
+ [[NSNotificationCenter defaultCenter] postNotificationName:MAURPossibleCrashNotification
427
+ object:self
428
+ userInfo:userInfo];
429
+ }
430
+ - (void) onPhoneUsageWhileDriving:(MAURLocation *)location
431
+ {
432
+ [self bufferPendingEvent:@"phoneUsageWhileDriving" extra:nil];
433
+ NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
434
+ if (location != nil) userInfo[@"location"] = location;
435
+ [[NSNotificationCenter defaultCenter] postNotificationName:MAURPhoneUsageWhileDrivingNotification
436
+ object:self
437
+ userInfo:userInfo];
438
+ }
439
+
440
+ // v4.3 — driving event helpers
441
+ - (void) attachDrivingEvent:(NSString *)type to:(MAURLocation *)loc extra:(NSDictionary *)extra
442
+ {
443
+ if (loc == nil || type == nil) return;
444
+ NSMutableDictionary *ev = [NSMutableDictionary dictionary];
445
+ ev[@"type"] = type;
446
+ ev[@"time"] = @((long long)([[NSDate date] timeIntervalSince1970] * 1000.0));
447
+ if (extra != nil) [ev addEntriesFromDictionary:extra];
448
+ if (loc.drivingEvents == nil) loc.drivingEvents = [NSMutableArray array];
449
+ [loc.drivingEvents addObject:ev];
450
+ }
451
+
452
+ // v4.4.1 — pending driving events: cap + TTL.
453
+ static NSInteger const kPendingDrivingEventsMax = 20;
454
+ static NSTimeInterval const kPendingDrivingEventsTTLMs = 60000.0;
455
+
456
+ - (void) bufferPendingEvent:(NSString *)type extra:(NSDictionary *)extra
457
+ {
458
+ if (type == nil) return;
459
+ NSMutableDictionary *ev = [NSMutableDictionary dictionary];
460
+ ev[@"type"] = type;
461
+ ev[@"time"] = @((long long)([[NSDate date] timeIntervalSince1970] * 1000.0));
462
+ if (extra != nil) [ev addEntriesFromDictionary:extra];
463
+ @synchronized (pendingDrivingEvents) {
464
+ while ((NSInteger)[pendingDrivingEvents count] >= kPendingDrivingEventsMax) {
465
+ [pendingDrivingEvents removeObjectAtIndex:0];
466
+ }
467
+ [pendingDrivingEvents addObject:ev];
468
+ }
469
+ }
470
+
471
+ - (void) flushPendingDrivingEventsTo:(MAURLocation *)loc
472
+ {
473
+ if (loc == nil) return;
474
+ NSTimeInterval nowMs = [[NSDate date] timeIntervalSince1970] * 1000.0;
475
+ @synchronized (pendingDrivingEvents) {
476
+ if ([pendingDrivingEvents count] == 0) return;
477
+ if (loc.drivingEvents == nil) loc.drivingEvents = [NSMutableArray array];
478
+ for (NSDictionary *ev in pendingDrivingEvents) {
479
+ NSNumber *t = ev[@"time"];
480
+ NSTimeInterval evMs = t != nil ? [t doubleValue] : nowMs;
481
+ if (nowMs - evMs <= kPendingDrivingEventsTTLMs) {
482
+ [loc.drivingEvents addObject:ev];
483
+ }
484
+ }
485
+ [pendingDrivingEvents removeAllObjects];
486
+ }
487
+ }
488
+
489
+ // v4.4 — read device battery via UIDevice. Calling main thread; safe from any thread.
490
+ - (void) attachBatterySnapshotTo:(MAURLocation *)loc
491
+ {
492
+ if (loc == nil) return;
493
+ void (^read)(void) = ^{
494
+ UIDevice *device = [UIDevice currentDevice];
495
+ if (!device.batteryMonitoringEnabled) device.batteryMonitoringEnabled = YES;
496
+ float lvl = device.batteryLevel; // 0.0 - 1.0, or -1 if unknown
497
+ if (lvl >= 0) {
498
+ loc.batteryLevel = @((int) round(lvl * 100.0));
499
+ }
500
+ UIDeviceBatteryState state = device.batteryState;
501
+ BOOL charging = (state == UIDeviceBatteryStateCharging || state == UIDeviceBatteryStateFull);
502
+ loc.isCharging = @(charging);
503
+ };
504
+ if ([NSThread isMainThread]) {
505
+ read();
506
+ } else {
507
+ dispatch_sync(dispatch_get_main_queue(), read);
508
+ }
509
+ }
510
+
511
+ - (void) drivingDetectorFeed:(MAURLocation *)loc
512
+ {
513
+ if (loc == nil || _config == nil) return;
514
+ BOOL enabled = NO;
515
+ double speedLimit = 0;
516
+ double minMovingSpeed = 1.0;
517
+ NSTimeInterval stoppedDuration = 60.0;
518
+ double minTripSpeed = 3.0;
519
+ NSTimeInterval minTripDuration = 30.0;
520
+ NSDictionary *de = [_config valueForKey:@"drivingEvents"]; // see MAURConfig: provided as NSDictionary
521
+ if ([de isKindOfClass:[NSDictionary class]]) {
522
+ enabled = [[de objectForKey:@"enabled"] boolValue];
523
+ speedLimit = [[de objectForKey:@"speedLimit"] doubleValue];
524
+ if ([de objectForKey:@"minMovingSpeed"]) minMovingSpeed = [[de objectForKey:@"minMovingSpeed"] doubleValue];
525
+ if ([de objectForKey:@"stoppedDuration"]) stoppedDuration = [[de objectForKey:@"stoppedDuration"] doubleValue] / 1000.0;
526
+ if ([de objectForKey:@"minTripSpeed"]) minTripSpeed = [[de objectForKey:@"minTripSpeed"] doubleValue];
527
+ if ([de objectForKey:@"minTripDuration"]) minTripDuration = [[de objectForKey:@"minTripDuration"] doubleValue] / 1000.0;
528
+ }
529
+ if (!enabled) return;
530
+
531
+ NSTimeInterval now = [[NSDate date] timeIntervalSince1970];
532
+ double speed = loc.speed != nil ? [loc.speed doubleValue] : 0.0;
533
+ if (speed < 0) speed = 0;
534
+
535
+ // Provider change
536
+ NSString *provider = loc.provider;
537
+ if (provider != nil && ![provider isEqualToString:drLastProvider]) {
538
+ drLastProvider = provider;
539
+ [self attachDrivingEvent:@"providerChange" to:loc extra:@{@"provider": provider}];
540
+ [[NSNotificationCenter defaultCenter] postNotificationName:MAURProviderChangeNotification
541
+ object:self
542
+ userInfo:@{@"provider": provider}];
543
+ }
544
+
545
+ double curLat = [loc.latitude doubleValue];
546
+ double curLon = [loc.longitude doubleValue];
547
+ if (drHasPrev && drTripActive) {
548
+ drTripDistanceMeters += [self drHaversineFromLat:drPrevLat lon:drPrevLon toLat:curLat lon:curLon];
549
+ }
550
+ drPrevLat = curLat;
551
+ drPrevLon = curLon;
552
+ drHasPrev = YES;
553
+
554
+ BOOL nowMoving = speed >= minMovingSpeed;
555
+ if (nowMoving) {
556
+ drBelowMovingSince = 0;
557
+ if (!drIsMoving) {
558
+ drIsMoving = YES;
559
+ [self attachDrivingEvent:@"moving" to:loc extra:nil];
560
+ [[NSNotificationCenter defaultCenter] postNotificationName:MAURMovingNotification
561
+ object:self
562
+ userInfo:@{@"location": loc}];
563
+ }
564
+ if (!drTripActive) {
565
+ if (speed >= minTripSpeed) {
566
+ if (drAboveTripSpeedSince == 0) drAboveTripSpeedSince = now;
567
+ if (now - drAboveTripSpeedSince >= minTripDuration) {
568
+ drTripActive = YES;
569
+ drTripStartedAt = now;
570
+ drTripDistanceMeters = 0;
571
+ [self attachDrivingEvent:@"tripStart" to:loc extra:nil];
572
+ [[NSNotificationCenter defaultCenter] postNotificationName:MAURTripStartNotification
573
+ object:self
574
+ userInfo:@{@"location": loc}];
575
+ sensorFusion.tripActive = YES;
576
+ }
577
+ } else {
578
+ drAboveTripSpeedSince = 0;
579
+ }
580
+ }
581
+ } else {
582
+ drAboveTripSpeedSince = 0;
583
+ if (drBelowMovingSince == 0) drBelowMovingSince = now;
584
+ if (drIsMoving && (now - drBelowMovingSince) >= stoppedDuration) {
585
+ drIsMoving = NO;
586
+ [self attachDrivingEvent:@"stopped" to:loc extra:nil];
587
+ [[NSNotificationCenter defaultCenter] postNotificationName:MAURStoppedNotification
588
+ object:self
589
+ userInfo:@{@"location": loc}];
590
+ if (drTripActive) {
591
+ NSTimeInterval durMs = (now - drTripStartedAt) * 1000.0;
592
+ double dist = drTripDistanceMeters;
593
+ drTripActive = NO;
594
+ [self attachDrivingEvent:@"tripEnd" to:loc extra:@{@"distance": @(dist), @"durationMs": @((long long)durMs)}];
595
+ [[NSNotificationCenter defaultCenter] postNotificationName:MAURTripEndNotification
596
+ object:self
597
+ userInfo:@{
598
+ @"location": loc,
599
+ @"distance": @(dist),
600
+ @"durationMs": @((long long)durMs)
601
+ }];
602
+ sensorFusion.tripActive = NO;
603
+ }
604
+ }
605
+ }
606
+
607
+ if (speedLimit > 0) {
608
+ double kmh = speed * 3.6;
609
+ if (kmh > speedLimit) {
610
+ if (!drWasSpeeding) {
611
+ drWasSpeeding = YES;
612
+ [self attachDrivingEvent:@"speeding" to:loc extra:@{@"speedKmh": @(kmh), @"limitKmh": @(speedLimit)}];
613
+ [[NSNotificationCenter defaultCenter] postNotificationName:MAURSpeedingNotification
614
+ object:self
615
+ userInfo:@{
616
+ @"location": loc,
617
+ @"speedKmh": @(kmh),
618
+ @"limitKmh": @(speedLimit)
619
+ }];
620
+ }
621
+ } else {
622
+ drWasSpeeding = NO;
623
+ }
624
+ }
625
+
626
+ // v4.1 GPS-derived sensor-like events
627
+ double hardBrakeMps2 = 3.5, rapidAccelMps2 = 3.5, sharpTurnDegPerSec = 30, crashImpactKmh = 25;
628
+ NSTimeInterval crashWindow = 2.0;
629
+ if ([de isKindOfClass:[NSDictionary class]]) {
630
+ if ([de objectForKey:@"hardBrakeMps2"]) hardBrakeMps2 = [[de objectForKey:@"hardBrakeMps2"] doubleValue];
631
+ if ([de objectForKey:@"rapidAccelMps2"]) rapidAccelMps2 = [[de objectForKey:@"rapidAccelMps2"] doubleValue];
632
+ if ([de objectForKey:@"sharpTurnDegPerSec"]) sharpTurnDegPerSec = [[de objectForKey:@"sharpTurnDegPerSec"] doubleValue];
633
+ if ([de objectForKey:@"crashImpactKmh"]) crashImpactKmh = [[de objectForKey:@"crashImpactKmh"] doubleValue];
634
+ if ([de objectForKey:@"crashWindowMs"]) crashWindow = [[de objectForKey:@"crashWindowMs"] doubleValue] / 1000.0;
635
+ }
636
+ static const NSTimeInterval kCooldown = 4.0;
637
+
638
+ if (drTripActive && drPrevSpeedAt > 0) {
639
+ NSTimeInterval dt = now - drPrevSpeedAt;
640
+ if (dt > 0 && dt <= 5.0) {
641
+ double dv = speed - drPrevSpeed;
642
+ double accel = dv / dt;
643
+ if (hardBrakeMps2 > 0 && accel <= -hardBrakeMps2 && (now - drLastHardBrakeAt) >= kCooldown) {
644
+ drLastHardBrakeAt = now;
645
+ [self attachDrivingEvent:@"hardBrake" to:loc extra:@{@"value": @(accel)}];
646
+ [[NSNotificationCenter defaultCenter] postNotificationName:MAURHardBrakeNotification
647
+ object:self
648
+ userInfo:@{@"location": loc, @"value": @(accel)}];
649
+ }
650
+ if (rapidAccelMps2 > 0 && accel >= rapidAccelMps2 && (now - drLastRapidAccelAt) >= kCooldown) {
651
+ drLastRapidAccelAt = now;
652
+ [self attachDrivingEvent:@"rapidAcceleration" to:loc extra:@{@"value": @(accel)}];
653
+ [[NSNotificationCenter defaultCenter] postNotificationName:MAURRapidAccelerationNotification
654
+ object:self
655
+ userInfo:@{@"location": loc, @"value": @(accel)}];
656
+ }
657
+ if (crashImpactKmh > 0 && dt <= crashWindow) {
658
+ double dropKmh = (drPrevSpeed - speed) * 3.6;
659
+ if (dropKmh >= crashImpactKmh
660
+ && speed < 1.5
661
+ && drPrevSpeed * 3.6 >= crashImpactKmh
662
+ && (now - drLastCrashAt) >= kCooldown) {
663
+ drLastCrashAt = now;
664
+ [self attachDrivingEvent:@"possibleCrash" to:loc extra:@{@"value": @(dropKmh), @"source": @"gps"}];
665
+ [[NSNotificationCenter defaultCenter] postNotificationName:MAURPossibleCrashNotification
666
+ object:self
667
+ userInfo:@{@"location": loc, @"value": @(dropKmh), @"source": @"gps"}];
668
+ }
669
+ }
670
+ }
671
+ }
672
+
673
+ // Sharp turn (bearing rate)
674
+ if (sharpTurnDegPerSec > 0 && loc.heading != nil && speed >= 5.0 && drHasPrevBearing) {
675
+ NSTimeInterval dt = now - drPrevBearingAt;
676
+ if (dt > 0 && dt <= 5.0) {
677
+ double bearing = [loc.heading doubleValue];
678
+ double diff = fabs(bearing - drPrevBearing);
679
+ if (diff > 180) diff = 360 - diff;
680
+ double rate = diff / dt;
681
+ if (rate >= sharpTurnDegPerSec && (now - drLastSharpTurnAt) >= kCooldown) {
682
+ drLastSharpTurnAt = now;
683
+ [self attachDrivingEvent:@"sharpTurn" to:loc extra:@{@"value": @(rate)}];
684
+ [[NSNotificationCenter defaultCenter] postNotificationName:MAURSharpTurnNotification
685
+ object:self
686
+ userInfo:@{@"location": loc, @"value": @(rate)}];
687
+ }
688
+ }
689
+ drPrevBearing = [loc.heading doubleValue];
690
+ drPrevBearingAt = now;
691
+ } else if (loc.heading != nil) {
692
+ drPrevBearing = [loc.heading doubleValue];
693
+ drPrevBearingAt = now;
694
+ drHasPrevBearing = YES;
695
+ }
696
+
697
+ drPrevSpeed = speed;
698
+ drPrevSpeedAt = now;
699
+ }
700
+
701
+ - (double) drHaversineFromLat:(double)lat1 lon:(double)lon1 toLat:(double)lat2 lon:(double)lon2
702
+ {
703
+ const double R = 6371000.0;
704
+ double dLat = (lat2 - lat1) * M_PI / 180.0;
705
+ double dLon = (lon2 - lon1) * M_PI / 180.0;
706
+ double a = sin(dLat/2) * sin(dLat/2)
707
+ + cos(lat1 * M_PI / 180.0) * cos(lat2 * M_PI / 180.0)
708
+ * sin(dLon/2) * sin(dLon/2);
709
+ return 2 * R * asin(sqrt(a));
710
+ }
711
+
712
+ - (void) triggerSOS:(NSDictionary *)payload
713
+ {
714
+ NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
715
+ if (lastReceivedLocation != nil) userInfo[@"location"] = lastReceivedLocation;
716
+ userInfo[@"payload"] = payload != nil ? payload : @{};
717
+ [[NSNotificationCenter defaultCenter] postNotificationName:MAURSOSNotification
718
+ object:self
719
+ userInfo:userInfo];
720
+ }
721
+
722
+ /**
723
+ * toggle between foreground and background operation mode
724
+ */
725
+ - (void) switchMode:(MAUROperationalMode)mode
726
+ {
727
+ DDLogInfo(@"%@ #switchMode %lu", TAG, (unsigned long)mode);
728
+
729
+ operationMode = mode;
730
+
731
+ if (!isStarted) return;
732
+
733
+ if ([self getConfig].isDebugging) {
734
+ AudioServicesPlaySystemSound (operationMode == MAURForegroundMode ? paceChangeYesSound : paceChangeNoSound);
735
+ }
736
+
737
+ [self runOnMainThread:^{
738
+ [locationProvider onSwitchMode:mode];
739
+ }];
740
+ }
741
+
742
+ - (BOOL) locationServicesEnabled
743
+ {
744
+ if ([CLLocationManager respondsToSelector:@selector(locationServicesEnabled)]) { // iOS 4.x
745
+ return [CLLocationManager locationServicesEnabled];
746
+ }
747
+
748
+ return NO;
749
+ }
750
+
751
+ - (MAURLocationAuthorizationStatus) authorizationStatus
752
+ {
753
+ CLAuthorizationStatus authStatus = [CLLocationManager authorizationStatus];
754
+ switch (authStatus) {
755
+ case kCLAuthorizationStatusNotDetermined:
756
+ return MAURLocationAuthorizationNotDetermined;
757
+ case kCLAuthorizationStatusRestricted:
758
+ case kCLAuthorizationStatusDenied:
759
+ return MAURLocationAuthorizationDenied;
760
+ case kCLAuthorizationStatusAuthorizedAlways:
761
+ return MAURLocationAuthorizationAlways;
762
+ case kCLAuthorizationStatusAuthorizedWhenInUse:
763
+ return MAURLocationAuthorizationForeground;
764
+ }
765
+ }
766
+
767
+ - (BOOL) isStarted
768
+ {
769
+ return isStarted;
770
+ }
771
+
772
+ - (MAURAbstractLocationProvider<MAURLocationProvider>*) getProvider:(int)providerId error:(NSError * __autoreleasing *)outError
773
+ {
774
+ NSDictionary *errorDictionary;
775
+ MAURAbstractLocationProvider<MAURLocationProvider> *locationProvider = nil;
776
+ switch (providerId) {
777
+ case DISTANCE_FILTER_PROVIDER:
778
+ locationProvider = [[MAURDistanceFilterLocationProvider alloc] init];
779
+ break;
780
+ case ACTIVITY_PROVIDER:
781
+ locationProvider = [[MAURActivityLocationProvider alloc] init];
782
+ break;
783
+ case RAW_PROVIDER:
784
+ locationProvider = [[MAURRawLocationProvider alloc] init];
785
+ break;
786
+ default:
787
+ if (outError != nil) {
788
+ errorDictionary = @{
789
+ NSLocalizedDescriptionKey: NSLocalizedString(@UNKNOWN_LOCATION_PROVIDER_MSG, nil),
790
+ };
791
+ *outError = [NSError errorWithDomain:BGGeolocationDomain code:MAURBGConfigureError userInfo:errorDictionary];
792
+ }
793
+ return nil;
794
+ }
795
+ [locationProvider onCreate];
796
+ return locationProvider;
797
+ }
798
+
799
+ - (void) showAppSettings
800
+ {
801
+ [self runOnMainThread:^{
802
+ BOOL canGoToSettings = (UIApplicationOpenSettingsURLString != NULL);
803
+ if (canGoToSettings) {
804
+ NSURL *settingsURL = [NSURL URLWithString:UIApplicationOpenSettingsURLString];
805
+ [[UIApplication sharedApplication] openURL:settingsURL
806
+ options:@{}
807
+ completionHandler:nil];
808
+ }
809
+ }];
810
+ }
811
+
812
+ - (void) showLocationSettings
813
+ {
814
+ // NOOP - Since Apple started rejecting apps using non public url schemes
815
+ // https://github.com/mauron85/cordova-plugin-background-geolocation/issues/394
816
+ }
817
+
818
+ - (MAURLocation*) getStationaryLocation
819
+ {
820
+ return stationaryLocation;
821
+ }
822
+
823
+ - (NSArray<MAURLocation*>*) getLocations
824
+ {
825
+ MAURSQLiteLocationDAO* locationDAO = [MAURSQLiteLocationDAO sharedInstance];
826
+ return [locationDAO getAllLocations];
827
+ }
828
+
829
+ - (NSArray<MAURLocation*>*) getValidLocations
830
+ {
831
+ MAURSQLiteLocationDAO* locationDAO = [MAURSQLiteLocationDAO sharedInstance];
832
+ return [locationDAO getValidLocations];
833
+ }
834
+
835
+ - (NSArray<MAURLocation*>*) getValidLocationsAndDelete
836
+ {
837
+ MAURSQLiteLocationDAO* locationDAO = [MAURSQLiteLocationDAO sharedInstance];
838
+ return [locationDAO getLocationsForSync];
839
+ }
840
+
841
+ - (void) startSession
842
+ {
843
+ [[MAURSessionLocationDAO sharedInstance] startSession];
844
+ }
845
+
846
+ - (NSArray<MAURLocation*>*) getSessionLocations
847
+ {
848
+ return [[MAURSessionLocationDAO sharedInstance] getSessionLocations];
849
+ }
850
+
851
+ - (void) clearSession
852
+ {
853
+ [[MAURSessionLocationDAO sharedInstance] clearSession];
854
+ }
855
+
856
+ - (NSInteger) getSessionLocationsCount
857
+ {
858
+ return [[MAURSessionLocationDAO sharedInstance] getSessionLocationsCount];
859
+ }
860
+
861
+ - (BOOL) deleteLocation:(NSNumber*)locationId error:(NSError * __autoreleasing *)outError
862
+ {
863
+ MAURSQLiteLocationDAO* locationDAO = [MAURSQLiteLocationDAO sharedInstance];
864
+ return [locationDAO deleteLocation:locationId error:outError];
865
+ }
866
+
867
+ - (BOOL) deleteAllLocations:(NSError * __autoreleasing *)outError
868
+ {
869
+ MAURSQLiteLocationDAO* locationDAO = [MAURSQLiteLocationDAO sharedInstance];
870
+ return [locationDAO deleteAllLocations:outError];
871
+ }
872
+
873
+ - (MAURLocation*)getCurrentLocation:(int)timeout maximumAge:(long)maximumAge
874
+ enableHighAccuracy:(BOOL)enableHighAccuracy
875
+ error:(NSError * __autoreleasing *)outError
876
+ {
877
+ __block NSError *error = nil;
878
+ __block CLLocation *location = nil;
879
+
880
+ dispatch_semaphore_t sema = dispatch_semaphore_create(0);
881
+ [self runOnMainThread:^{
882
+ CLLocation *currentLocation = [MAURLocationManager sharedInstance].locationManager.location;
883
+ if (currentLocation != nil) {
884
+ long locationAge = ceil(fabs([currentLocation.timestamp timeIntervalSinceNow]) * 1000);
885
+ if (locationAge <= maximumAge) {
886
+ location = currentLocation;
887
+ dispatch_semaphore_signal(sema);
888
+ return;
889
+ }
890
+ }
891
+
892
+ INTULocationManager *locationManager = [INTULocationManager sharedInstance];
893
+ float timeoutInSeconds = ceil((float)timeout/1000);
894
+ [locationManager requestLocationWithDesiredAccuracy:enableHighAccuracy ? INTULocationAccuracyRoom : INTULocationAccuracyCity
895
+ timeout:timeoutInSeconds
896
+ delayUntilAuthorized:YES // This parameter is optional, defaults to NO if omitted
897
+ block:^(CLLocation *currentLocation, INTULocationAccuracy achievedAccuracy, INTULocationStatus status) {
898
+ if (status == INTULocationStatusSuccess) {
899
+ // Request succeeded, meaning achievedAccuracy is at least the requested accuracy, and
900
+ // currentLocation contains the device's current location.
901
+ location = currentLocation;
902
+ }
903
+ else if (status == INTULocationStatusTimedOut) {
904
+ // Wasn't able to locate the user with the requested accuracy within the timeout interval.
905
+ // However, currentLocation contains the best location available (if any) as of right now,
906
+ // and achievedAccuracy has info on the accuracy/recency of the location in currentLocation.
907
+ error = [NSError errorWithDomain:BGGeolocationDomain code:TIMEOUT userInfo:nil];
908
+ }
909
+ else {
910
+ // An error occurred, more info is available by looking at the specific status returned.
911
+ error = [NSError errorWithDomain:BGGeolocationDomain code:POSITION_UNAVAILABLE userInfo:nil];
912
+ }
913
+ dispatch_semaphore_signal(sema);
914
+ }];
915
+ }];
916
+ dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
917
+
918
+ if (location != nil) {
919
+ return [MAURLocation fromCLLocation:location];
920
+ }
921
+
922
+ if (outError != nil) {
923
+ *outError = error;
924
+ }
925
+
926
+ return nil;
927
+ }
928
+
929
+ - (MAURConfig*) getConfig
930
+ {
931
+ if (_config == nil) {
932
+ MAURSQLiteConfigurationDAO* configDAO = [MAURSQLiteConfigurationDAO sharedInstance];
933
+ _config = [configDAO retrieveConfiguration];
934
+ if (_config == nil) {
935
+ _config = [[MAURConfig alloc] initWithDefaults];
936
+ }
937
+ }
938
+
939
+ return _config;
940
+ }
941
+
942
+ - (NSArray*) getLogEntries:(NSInteger)limit
943
+ {
944
+ MAURLogReader *logReader = [[MAURLogReader alloc] initWithLogDirectory:[self loggerDirectory]];
945
+ return [logReader getEntries:limit fromLogEntryId:0 minLogLevel:DDLogFlagDebug];
946
+ }
947
+
948
+ - (NSArray*) getLogEntries:(NSInteger)limit fromLogEntryId:(NSInteger)entryId minLogLevelFromString:(NSString *)minLogLevel
949
+ {
950
+ MAURLogReader *logReader = [[MAURLogReader alloc] initWithLogDirectory:[self loggerDirectory]];
951
+ return [logReader getLogEntries:limit fromLogEntryId:entryId minLogLevelAsString:minLogLevel];
952
+ }
953
+
954
+ - (NSArray*) getLogEntries:(NSInteger)limit fromLogEntryId:(NSInteger)entryId minLogLevel:(DDLogFlag)minLogLevel
955
+ {
956
+ MAURLogReader *logReader = [[MAURLogReader alloc] initWithLogDirectory:[self loggerDirectory]];
957
+ NSArray *logs = [logReader getEntries:limit fromLogEntryId:entryId minLogLevel:minLogLevel];
958
+ return logs;
959
+ }
960
+
961
+ - (void) forceSync
962
+ {
963
+ if (![_config syncEnabled]) {
964
+ DDLogDebug(@"Sync disabled in config, skipping forceSync");
965
+ return;
966
+ }
967
+ [postLocationTask sync];
968
+ }
969
+
970
+ - (void) clearSync
971
+ {
972
+ MAURSQLiteLocationDAO *locationDAO = [MAURSQLiteLocationDAO sharedInstance];
973
+ NSError *error = nil;
974
+ if ([locationDAO deletePendingSyncLocations:&error]) {
975
+ DDLogDebug(@"Cleared pending sync locations");
976
+ } else {
977
+ DDLogError(@"clearSync failed: %@", error.localizedDescription);
978
+ }
979
+ }
980
+
981
+ - (NSInteger) getPendingSyncCount
982
+ {
983
+ MAURSQLiteLocationDAO *locationDAO = [MAURSQLiteLocationDAO sharedInstance];
984
+ NSNumber *count = [locationDAO getLocationsForSyncCount];
985
+ return count != nil ? [count integerValue] : 0;
986
+ }
987
+
988
+ - (void) notify:(NSString*)message
989
+ {
990
+ localNotification.fireDate = [NSDate date];
991
+ localNotification.alertBody = message;
992
+ [[UIApplication sharedApplication] scheduleLocalNotification:localNotification];
993
+ }
994
+
995
+ -(void) runOnMainThread:(dispatch_block_t)completionHandle {
996
+ BOOL alreadyOnMainThread = [NSThread isMainThread];
997
+ // this check avoids possible deadlock resulting from
998
+ // calling dispatch_sync() on the same queue as current one
999
+ if (alreadyOnMainThread) {
1000
+ // execute code in place
1001
+ completionHandle();
1002
+ } else {
1003
+ // dispatch to main queue
1004
+ dispatch_sync(dispatch_get_main_queue(), completionHandle);
1005
+ }
1006
+ }
1007
+
1008
+ - (NSString *)loggerDirectory
1009
+ {
1010
+ NSArray *paths = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES);
1011
+ NSString *basePath = ([paths count] > 0) ? [paths objectAtIndex:0] : NSTemporaryDirectory();
1012
+
1013
+ return [basePath stringByAppendingPathComponent:@"SQLiteLogger"];
1014
+ }
1015
+
1016
+ - (void) onStationaryChanged:(MAURLocation *)location
1017
+ {
1018
+ DDLogDebug(@"%@ #onStationaryChanged", TAG);
1019
+
1020
+ // v4.5.2: drop stationary fixes whose accuracy is worse than the configured
1021
+ // maxAcceptedAccuracy threshold. Mirrors the Android filter in
1022
+ // AbstractLocationProvider.handleStationary.
1023
+ NSNumber *maxAcc = [self getConfig].maxAcceptedAccuracy;
1024
+ if (maxAcc != nil && maxAcc.doubleValue > 0 && location.accuracy != nil
1025
+ && [location.accuracy doubleValue] > maxAcc.doubleValue) {
1026
+ DDLogDebug(@"%@ dropping stationary fix accuracy=%@ exceeds maxAcceptedAccuracy=%@", TAG, location.accuracy, maxAcc);
1027
+ return;
1028
+ }
1029
+
1030
+ stationaryLocation = location;
1031
+
1032
+ // v4.5.1 — enrichment moved into MAURPostLocationTask.add: so pending events / battery
1033
+ // land on the post-transform instance (and survive transforms that return new instances).
1034
+
1035
+ [postLocationTask add:location];
1036
+
1037
+ MAURConfig *config = [self getConfig];
1038
+ if ([config isDebugging]) {
1039
+ double distanceFilter = [MAURLocationManager sharedInstance].distanceFilter;
1040
+ [self notify:[NSString stringWithFormat:@"Stationary update: %s\nSPD: %0.0f | DF: %f | ACY: %0.0f | RAD: %0.0f",
1041
+ ((operationMode == MAURForegroundMode) ? "FG" : "BG"),
1042
+ [location.speed doubleValue],
1043
+ distanceFilter,
1044
+ [location.accuracy doubleValue],
1045
+ [location.radius doubleValue]
1046
+ ]];
1047
+
1048
+ AudioServicesPlaySystemSound (locationSyncSound);
1049
+ }
1050
+
1051
+ // Any javascript stationaryRegion event-listeners?
1052
+ if (self.delegate && [self.delegate respondsToSelector:@selector(onStationaryChanged:)]) {
1053
+ [self.delegate onStationaryChanged:location];
1054
+ }
1055
+ }
1056
+
1057
+ - (void) onLocationChanged:(MAURLocation *)location
1058
+ {
1059
+ DDLogDebug(@"%@ #onLocationChanged %@", TAG, location);
1060
+
1061
+ // v4.5.2: drop fixes whose accuracy is worse than maxAcceptedAccuracy.
1062
+ // Mirrors AbstractLocationProvider.handleLocation on Android.
1063
+ NSNumber *maxAcc = [self getConfig].maxAcceptedAccuracy;
1064
+ if (maxAcc != nil && maxAcc.doubleValue > 0 && location.accuracy != nil
1065
+ && [location.accuracy doubleValue] > maxAcc.doubleValue) {
1066
+ DDLogDebug(@"%@ dropping fix accuracy=%@ exceeds maxAcceptedAccuracy=%@", TAG, location.accuracy, maxAcc);
1067
+ return;
1068
+ }
1069
+
1070
+ stationaryLocation = nil;
1071
+ lastReceivedLocation = location; // v3.5 Phase 4: cached for heartbeat payload
1072
+
1073
+ // v4.0 Phase 6: feed driver-insights state machine. Listener may attach events to `location`.
1074
+ [self drivingDetectorFeed:location];
1075
+ // v4.2 Phase 8: keep sensor pipeline aware of the latest fix.
1076
+ sensorFusion.lastLocation = location;
1077
+ // v4.5.1 — pending events drain + battery snapshot moved into MAURPostLocationTask.add:
1078
+ // so they run AFTER any user-supplied locationTransform. The previous order lost them
1079
+ // whenever the transform returned nil.
1080
+
1081
+ [postLocationTask add:location];
1082
+
1083
+ MAURConfig *config = [self getConfig];
1084
+ if ([config isDebugging]) {
1085
+ double distanceFilter = [MAURLocationManager sharedInstance].distanceFilter;
1086
+ [self notify:[NSString stringWithFormat:@"Location update: %s\nSPD: %0.0f | DF: %f | ACY: %0.0f",
1087
+ ((operationMode == MAURForegroundMode) ? "FG" : "BG"),
1088
+ [location.speed doubleValue],
1089
+ distanceFilter,
1090
+ [location.accuracy doubleValue]
1091
+ ]];
1092
+
1093
+ AudioServicesPlaySystemSound (locationSyncSound);
1094
+ }
1095
+
1096
+ // Delegate to main module
1097
+ if (self.delegate && [self.delegate respondsToSelector:@selector(onLocationChanged:)]) {
1098
+ [self.delegate onLocationChanged:location];
1099
+ }
1100
+ }
1101
+
1102
+ - (void) onAuthorizationChanged:(MAURLocationAuthorizationStatus)authStatus
1103
+ {
1104
+ [self.delegate onAuthorizationChanged:authStatus];
1105
+ }
1106
+
1107
+ - (void) onError:(NSError*)error
1108
+ {
1109
+ [self.delegate onError:error];
1110
+ }
1111
+
1112
+ - (void) onLocationPause
1113
+ {
1114
+ [self.delegate onLocationPause];
1115
+ }
1116
+
1117
+ - (void) onLocationResume
1118
+ {
1119
+ [self.delegate onLocationResume];
1120
+ }
1121
+
1122
+ - (void) onActivityChanged:(MAURActivity *)activity
1123
+ {
1124
+ DDLogDebug(@"%@ #onActivityChanged %@", TAG, activity);
1125
+
1126
+ if ([self getConfig].isDebugging) {
1127
+ [self notify:[NSString stringWithFormat:@"%@ activity detected: %@ activity, confidence: %@", TAG, activity.type, activity.confidence]];
1128
+ }
1129
+
1130
+ [self.delegate onActivityChanged:activity];
1131
+ }
1132
+
1133
+ /**@
1134
+ * If you don't stopMonitoring when application terminates, the app will be awoken still when a
1135
+ * new location arrives, essentially monitoring the user's location even when they've killed the app.
1136
+ * Might be desirable in certain apps.
1137
+ */
1138
+ - (void) onAppTerminate
1139
+ {
1140
+ MAURConfig *config = [self getConfig];
1141
+ if ([config stopOnTerminate]) {
1142
+ DDLogInfo(@"%@ #onAppTerminate.", TAG);
1143
+ [self stop:nil];
1144
+ } else {
1145
+ [locationProvider onTerminate];
1146
+ }
1147
+ }
1148
+
1149
+ - (void) dealloc
1150
+ {
1151
+ DDLogDebug(@"%@ #dealloc", TAG);
1152
+ // currently noop
1153
+ }
1154
+
1155
+ #pragma mark - Location transform
1156
+
1157
+ + (void) setLocationTransform:(MAURLocationTransform _Nullable)transform
1158
+ {
1159
+ [MAURPostLocationTask setLocationTransform:transform];
1160
+ }
1161
+
1162
+ + (MAURLocationTransform _Nullable) locationTransform
1163
+ {
1164
+ return [MAURPostLocationTask locationTransform];
1165
+ }
1166
+
1167
+ #pragma mark - MAURPostLocationTaskDelegate
1168
+
1169
+ - (void) postLocationTaskRequestedAbortUpdates:(MAURPostLocationTask *)task
1170
+ {
1171
+ if (_delegate && [_delegate respondsToSelector:@selector(onAbortRequested)])
1172
+ {
1173
+ // We have a delegate, tell it that there's a request.
1174
+ // It will decide whether to stop or not.
1175
+ [_delegate onAbortRequested];
1176
+ }
1177
+ else
1178
+ {
1179
+ // No delegate, we may be running in the background.
1180
+ // Let's just stop.
1181
+ [self stop:nil];
1182
+ }
1183
+ }
1184
+
1185
+ - (void) postLocationTaskHttpAuthorizationUpdates:(MAURPostLocationTask *)task
1186
+ {
1187
+ if (_delegate && [_delegate respondsToSelector:@selector(onHttpAuthorization)])
1188
+ {
1189
+ [_delegate onHttpAuthorization];
1190
+ }
1191
+ }
1192
+
1193
+ @end