@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,265 @@
1
+ package com.marianhello.bgloc.driving;
2
+
3
+ import com.marianhello.bgloc.data.BackgroundLocation;
4
+
5
+ /**
6
+ * v4.0 Phase 6 — Driver insights state machine (GPS-only).
7
+ *
8
+ * Pure-Java helper, no Android imports. Hosted by {@code LocationServiceImpl}, which
9
+ * feeds it every received location and surfaces emitted events via the plugin
10
+ * {@code MSG_ON_*} broadcast pipeline.
11
+ *
12
+ * Heuristics:
13
+ * moving = speed > minMovingSpeed
14
+ * stopped = !moving for stoppedDuration ms
15
+ * tripStart = stopped → moving with speed >= minTripSpeed sustained for minTripDuration ms
16
+ * tripEnd = moving → stopped (after a tripStart)
17
+ * speeding = first crossing above speedLimit (km/h); rearms on drop below
18
+ *
19
+ * Sensor-fusion events (hardBrake / sharpTurn / possibleCrash) are intentionally NOT
20
+ * implemented in this class. They require linear acceleration + gyroscope sampling and
21
+ * are planned for v4.1 in a separate {@code SensorFusionDetector}.
22
+ */
23
+ public class DrivingEventsDetector {
24
+
25
+ public interface Listener {
26
+ void onMoving(BackgroundLocation location);
27
+ void onStopped(BackgroundLocation location);
28
+ void onTripStart(BackgroundLocation location);
29
+ /** distance in meters, durationMs in milliseconds. */
30
+ void onTripEnd(BackgroundLocation location, double distance, long durationMs);
31
+ void onSpeeding(BackgroundLocation location, double speedKmh, double limitKmh);
32
+ void onProviderChange(String provider);
33
+ // v4.1 GPS-derived driving events
34
+ /** GPS-derived deceleration (m/s²). Negative number, more negative = harder brake. */
35
+ void onHardBrake(BackgroundLocation location, double decelMps2);
36
+ /** GPS-derived acceleration (m/s²). */
37
+ void onRapidAcceleration(BackgroundLocation location, double accelMps2);
38
+ /** Bearing change rate (deg/s). */
39
+ void onSharpTurn(BackgroundLocation location, double degPerSec);
40
+ /** Velocity drop in km/h within {@code crashWindowMs} while tripActive. */
41
+ void onPossibleCrash(BackgroundLocation location, double velocityDropKmh);
42
+ }
43
+
44
+ public static class Config {
45
+ public boolean enabled = false;
46
+ public double speedLimitKmh = 0; // 0 disables speeding
47
+ public double minMovingSpeedMps = 1.0; // ~3.6 km/h
48
+ public long stoppedDurationMs = 60_000;
49
+ public double minTripSpeedMps = 3.0; // ~10.8 km/h
50
+ public long minTripDurationMs = 30_000;
51
+ // v4.1 GPS-derived driving events. 0 disables each one.
52
+ public double hardBrakeMps2 = 3.5; // -m/s² threshold (positive value)
53
+ public double rapidAccelMps2 = 3.5; // m/s² threshold
54
+ public double sharpTurnDegPerSec = 30; // deg/sec, requires speed > 5 m/s
55
+ public double crashImpactKmh = 25; // velocity drop within crashWindow
56
+ public long crashWindowMs = 2_000; // window to evaluate the velocity drop
57
+ }
58
+
59
+ private final Listener listener;
60
+ private Config cfg = new Config();
61
+
62
+ // State
63
+ private boolean isMoving = false;
64
+ private boolean tripActive = false;
65
+ private long tripStartedAt = 0;
66
+ private double tripDistanceMeters = 0;
67
+ private double tripStartLat, tripStartLon;
68
+ private boolean hasTripStartCoord = false;
69
+
70
+ private long aboveTripSpeedSince = 0; // first sample with speed >= minTripSpeed
71
+ private long belowMovingSinceMs = 0; // first sample with speed < minMovingSpeed
72
+
73
+ private boolean wasSpeeding = false;
74
+ private String lastProvider;
75
+
76
+ private double prevLat, prevLon;
77
+ private boolean hasPrev = false;
78
+
79
+ // v4.1 GPS-derived deltas
80
+ private double prevSpeedMps = 0.0;
81
+ private long prevSpeedAt = 0L;
82
+ private double prevBearingDeg = 0.0;
83
+ private boolean hasPrevBearing = false;
84
+ private long prevBearingAt = 0L;
85
+ /** Cooldown so we don't refire the same event on every fix in a sustained brake. */
86
+ private long lastHardBrakeAt = 0L, lastRapidAccelAt = 0L, lastSharpTurnAt = 0L, lastCrashAt = 0L;
87
+ private static final long DRIVING_EVENT_COOLDOWN_MS = 4_000L;
88
+
89
+ public DrivingEventsDetector(Listener listener) {
90
+ this.listener = listener;
91
+ }
92
+
93
+ public synchronized void setConfig(Config c) {
94
+ if (c != null) this.cfg = c;
95
+ }
96
+
97
+ /** Reset internal state. Called when service stops. */
98
+ public synchronized void reset() {
99
+ prevSpeedMps = 0.0;
100
+ prevSpeedAt = 0L;
101
+ prevBearingDeg = 0.0;
102
+ hasPrevBearing = false;
103
+ prevBearingAt = 0L;
104
+ lastHardBrakeAt = lastRapidAccelAt = lastSharpTurnAt = lastCrashAt = 0L;
105
+ isMoving = false;
106
+ tripActive = false;
107
+ tripStartedAt = 0;
108
+ tripDistanceMeters = 0;
109
+ hasTripStartCoord = false;
110
+ aboveTripSpeedSince = 0;
111
+ belowMovingSinceMs = 0;
112
+ wasSpeeding = false;
113
+ lastProvider = null;
114
+ hasPrev = false;
115
+ }
116
+
117
+ public synchronized void onLocation(BackgroundLocation loc) {
118
+ if (!cfg.enabled || loc == null) return;
119
+ long now = System.currentTimeMillis();
120
+ double speed = loc.hasSpeed() ? loc.getSpeed() : 0.0;
121
+
122
+ // Provider change
123
+ String provider = loc.getProvider();
124
+ if (provider != null && !provider.equals(lastProvider)) {
125
+ lastProvider = provider;
126
+ if (listener != null) listener.onProviderChange(provider);
127
+ }
128
+
129
+ // Distance accumulator (very simple: planar haversine approximation via
130
+ // Location.distanceBetween is not used here to keep this class platform-free;
131
+ // consumer can swap to BackgroundLocation.distanceTo in onTripEnd if desired).
132
+ double curLat = loc.getLatitude();
133
+ double curLon = loc.getLongitude();
134
+ if (hasPrev && tripActive) {
135
+ tripDistanceMeters += haversineMeters(prevLat, prevLon, curLat, curLon);
136
+ }
137
+ prevLat = curLat;
138
+ prevLon = curLon;
139
+ hasPrev = true;
140
+
141
+ // Moving / stopped state
142
+ boolean nowMoving = speed >= cfg.minMovingSpeedMps;
143
+ if (nowMoving) {
144
+ belowMovingSinceMs = 0;
145
+ if (!isMoving) {
146
+ isMoving = true;
147
+ if (listener != null) listener.onMoving(loc);
148
+ }
149
+ // Trip start arming
150
+ if (!tripActive) {
151
+ if (speed >= cfg.minTripSpeedMps) {
152
+ if (aboveTripSpeedSince == 0) aboveTripSpeedSince = now;
153
+ if (now - aboveTripSpeedSince >= cfg.minTripDurationMs) {
154
+ tripActive = true;
155
+ tripStartedAt = now;
156
+ tripDistanceMeters = 0;
157
+ tripStartLat = curLat;
158
+ tripStartLon = curLon;
159
+ hasTripStartCoord = true;
160
+ if (listener != null) listener.onTripStart(loc);
161
+ }
162
+ } else {
163
+ aboveTripSpeedSince = 0;
164
+ }
165
+ }
166
+ } else {
167
+ aboveTripSpeedSince = 0;
168
+ if (belowMovingSinceMs == 0) belowMovingSinceMs = now;
169
+ if (isMoving && (now - belowMovingSinceMs) >= cfg.stoppedDurationMs) {
170
+ isMoving = false;
171
+ if (listener != null) listener.onStopped(loc);
172
+ if (tripActive) {
173
+ long durMs = now - tripStartedAt;
174
+ double dist = tripDistanceMeters;
175
+ tripActive = false;
176
+ if (listener != null) listener.onTripEnd(loc, dist, durMs);
177
+ }
178
+ }
179
+ }
180
+
181
+ // Speeding (km/h)
182
+ if (cfg.speedLimitKmh > 0) {
183
+ double kmh = speed * 3.6;
184
+ if (kmh > cfg.speedLimitKmh) {
185
+ if (!wasSpeeding) {
186
+ wasSpeeding = true;
187
+ if (listener != null) listener.onSpeeding(loc, kmh, cfg.speedLimitKmh);
188
+ }
189
+ } else {
190
+ // Rearm: emit again on next crossing.
191
+ wasSpeeding = false;
192
+ }
193
+ }
194
+
195
+ // v4.1 GPS-derived driving events (only meaningful during an active trip)
196
+ if (tripActive && prevSpeedAt > 0) {
197
+ long dtMs = now - prevSpeedAt;
198
+ if (dtMs > 0 && dtMs <= 5_000) {
199
+ double dt = dtMs / 1000.0;
200
+ double dv = speed - prevSpeedMps; // m/s
201
+ double accel = dv / dt; // m/s²
202
+
203
+ if (cfg.hardBrakeMps2 > 0
204
+ && accel <= -cfg.hardBrakeMps2
205
+ && (now - lastHardBrakeAt) >= DRIVING_EVENT_COOLDOWN_MS) {
206
+ lastHardBrakeAt = now;
207
+ if (listener != null) listener.onHardBrake(loc, accel);
208
+ }
209
+ if (cfg.rapidAccelMps2 > 0
210
+ && accel >= cfg.rapidAccelMps2
211
+ && (now - lastRapidAccelAt) >= DRIVING_EVENT_COOLDOWN_MS) {
212
+ lastRapidAccelAt = now;
213
+ if (listener != null) listener.onRapidAcceleration(loc, accel);
214
+ }
215
+
216
+ // Possible crash: sustained velocity drop greater than crashImpactKmh in <= crashWindow.
217
+ if (cfg.crashImpactKmh > 0 && dtMs <= cfg.crashWindowMs) {
218
+ double dropKmh = (prevSpeedMps - speed) * 3.6; // positive when slowing down
219
+ if (dropKmh >= cfg.crashImpactKmh
220
+ && speed < 1.5 // ended near stop
221
+ && prevSpeedMps * 3.6 >= cfg.crashImpactKmh
222
+ && (now - lastCrashAt) >= DRIVING_EVENT_COOLDOWN_MS) {
223
+ lastCrashAt = now;
224
+ if (listener != null) listener.onPossibleCrash(loc, dropKmh);
225
+ }
226
+ }
227
+ }
228
+ }
229
+
230
+ // Sharp turn (bearing change rate) — requires meaningful speed to avoid GPS jitter.
231
+ if (cfg.sharpTurnDegPerSec > 0 && loc.hasBearing() && speed >= 5.0 && hasPrevBearing) {
232
+ long dtMs = now - prevBearingAt;
233
+ if (dtMs > 0 && dtMs <= 5_000) {
234
+ double bearing = loc.getBearing();
235
+ double diff = Math.abs(bearing - prevBearingDeg);
236
+ if (diff > 180) diff = 360 - diff;
237
+ double rate = diff * 1000.0 / dtMs;
238
+ if (rate >= cfg.sharpTurnDegPerSec
239
+ && (now - lastSharpTurnAt) >= DRIVING_EVENT_COOLDOWN_MS) {
240
+ lastSharpTurnAt = now;
241
+ if (listener != null) listener.onSharpTurn(loc, rate);
242
+ }
243
+ }
244
+ prevBearingDeg = loc.getBearing();
245
+ prevBearingAt = now;
246
+ } else if (loc.hasBearing()) {
247
+ prevBearingDeg = loc.getBearing();
248
+ prevBearingAt = now;
249
+ hasPrevBearing = true;
250
+ }
251
+
252
+ prevSpeedMps = speed;
253
+ prevSpeedAt = now;
254
+ }
255
+
256
+ private static double haversineMeters(double lat1, double lon1, double lat2, double lon2) {
257
+ double R = 6371000.0;
258
+ double dLat = Math.toRadians(lat2 - lat1);
259
+ double dLon = Math.toRadians(lon2 - lon1);
260
+ double a = Math.sin(dLat/2) * Math.sin(dLat/2)
261
+ + Math.cos(Math.toRadians(lat1)) * Math.cos(Math.toRadians(lat2))
262
+ * Math.sin(dLon/2) * Math.sin(dLon/2);
263
+ return 2 * R * Math.asin(Math.sqrt(a));
264
+ }
265
+ }
@@ -0,0 +1,15 @@
1
+ package com.marianhello.bgloc.headless;
2
+
3
+ import android.content.Context;
4
+
5
+ public abstract class AbstractTaskRunner implements TaskRunner {
6
+ protected Context mContext;
7
+
8
+ public AbstractTaskRunner() {}
9
+
10
+ public abstract void runTask(Task task);
11
+
12
+ public void setContext(Context context) {
13
+ mContext = context;
14
+ }
15
+ }
@@ -0,0 +1,48 @@
1
+ package com.marianhello.bgloc.headless;
2
+
3
+ import android.os.Bundle;
4
+
5
+ import com.marianhello.bgloc.data.BackgroundActivity;
6
+
7
+ import org.json.JSONException;
8
+
9
+ public abstract class ActivityTask extends Task {
10
+ private BackgroundActivity mActivity;
11
+
12
+ public ActivityTask(BackgroundActivity activity) {
13
+ mActivity = activity;
14
+ }
15
+
16
+ @Override
17
+ public String getName() {
18
+ return "activity";
19
+ }
20
+
21
+ @Override
22
+ public Bundle getBundle() {
23
+ Bundle bundle = new Bundle();
24
+ Bundle params = new Bundle();
25
+
26
+ params.putInt("confidence", mActivity.getConfidence());
27
+ params.putString("type", BackgroundActivity.getActivityString(mActivity.getType()));
28
+
29
+ bundle.putString("name", getName());
30
+ bundle.putBundle("params", params);
31
+ return bundle;
32
+ }
33
+
34
+ @Override
35
+ public String toString() {
36
+ if (mActivity == null) {
37
+ return null;
38
+ }
39
+
40
+ try {
41
+ return mActivity.toJSONObject().toString();
42
+ } catch (JSONException e) {
43
+ onError("Error processing params: " + e.getMessage());
44
+ }
45
+
46
+ return null;
47
+ }
48
+ }
@@ -0,0 +1,10 @@
1
+ package com.marianhello.bgloc.headless;
2
+
3
+ /**
4
+ * Interface for passing code that will be executed after the JS has finished
5
+ */
6
+
7
+ public interface JsCallback {
8
+ void onResult(String value);
9
+ void onError(String errorMessage);
10
+ }
@@ -0,0 +1,60 @@
1
+ package com.marianhello.bgloc.headless;
2
+
3
+ import android.os.Bundle;
4
+
5
+ import com.marianhello.bgloc.data.BackgroundLocation;
6
+
7
+ import org.json.JSONException;
8
+
9
+ public abstract class LocationTask extends Task {
10
+ protected BackgroundLocation mLocation;
11
+
12
+ public LocationTask(BackgroundLocation location) {
13
+ mLocation = location;
14
+ }
15
+
16
+ @Override
17
+ public String getName() {
18
+ return "location";
19
+ }
20
+
21
+ @Override
22
+ public Bundle getBundle() {
23
+ Bundle bundle = new Bundle();
24
+ Bundle params = new Bundle();
25
+
26
+ params.putString("provider", mLocation.getProvider());
27
+ params.putInt("locationProvider", mLocation.getLocationProvider());
28
+ params.putLong("time", mLocation.getTime());
29
+ params.putDouble("latitude", mLocation.getLatitude());
30
+ params.putDouble("longitude", mLocation.getLongitude());
31
+ if (mLocation.hasAccuracy()) params.putFloat("accuracy", mLocation.getAccuracy());
32
+ if (mLocation.hasVerticalAccuracy()) params.putFloat("accuracy", mLocation.getVerticalAccuracy());
33
+ if (mLocation.hasSpeed()) params.putFloat("speed", mLocation.getSpeed());
34
+ if (mLocation.hasAltitude()) params.putDouble("altitude", mLocation.getAltitude());
35
+ if (mLocation.hasBearing()) params.putFloat("bearing", mLocation.getBearing());
36
+ if (mLocation.hasRadius()) params.putFloat("radius", mLocation.getRadius());
37
+ if (mLocation.hasIsFromMockProvider()) params.putBoolean("isFromMockProvider", mLocation.isFromMockProvider());
38
+ if (mLocation.hasMockLocationsEnabled()) params.putBoolean("mockLocationsEnabled", mLocation.areMockLocationsEnabled());
39
+
40
+ bundle.putString("name", getName());
41
+ bundle.putBundle("params", params);
42
+
43
+ return bundle;
44
+ }
45
+
46
+ @Override
47
+ public String toString() {
48
+ if (mLocation == null) {
49
+ return null;
50
+ }
51
+
52
+ try {
53
+ return mLocation.toJSONObject().toString();
54
+ } catch (JSONException e) {
55
+ onError("Error processing params: " + e.getMessage());
56
+ }
57
+
58
+ return null;
59
+ }
60
+ }
@@ -0,0 +1,25 @@
1
+ package com.marianhello.bgloc.headless;
2
+
3
+ import android.os.Bundle;
4
+
5
+ import com.marianhello.bgloc.data.BackgroundLocation;
6
+
7
+ import org.json.JSONException;
8
+
9
+ public abstract class StationaryTask extends LocationTask {
10
+ public StationaryTask(BackgroundLocation location) {
11
+ super(location);
12
+ }
13
+
14
+ @Override
15
+ public String getName() {
16
+ return "stationary";
17
+ }
18
+
19
+ @Override
20
+ public Bundle getBundle() {
21
+ Bundle bundle = super.getBundle();
22
+
23
+ return bundle;
24
+ }
25
+ }
@@ -0,0 +1,8 @@
1
+ package com.marianhello.bgloc.headless;
2
+
3
+ import android.os.Bundle;
4
+
5
+ public abstract class Task implements JsCallback {
6
+ public abstract String getName();
7
+ public abstract Bundle getBundle();
8
+ }
@@ -0,0 +1,5 @@
1
+ package com.marianhello.bgloc.headless;
2
+
3
+ public interface TaskRunner {
4
+ void runTask(Task task);
5
+ }
@@ -0,0 +1,8 @@
1
+ package com.marianhello.bgloc.headless;
2
+
3
+ public class TaskRunnerFactory {
4
+
5
+ public TaskRunner getTaskRunner(String className) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
6
+ return (TaskRunner) Class.forName(className).newInstance();
7
+ }
8
+ }
@@ -0,0 +1,115 @@
1
+ package com.marianhello.bgloc.http;
2
+
3
+ import com.marianhello.bgloc.data.BackgroundLocation;
4
+
5
+ import java.text.SimpleDateFormat;
6
+ import java.util.Date;
7
+ import java.util.HashMap;
8
+ import java.util.Locale;
9
+ import java.util.Map;
10
+ import java.util.TimeZone;
11
+ import java.util.regex.Matcher;
12
+ import java.util.regex.Pattern;
13
+
14
+ /**
15
+ * Resolves placeholders like {lat}, {lon}, {timestamp_iso}, {device_id}, ...
16
+ * in a URL template using a single BackgroundLocation and an optional queryParams map.
17
+ *
18
+ * Placeholders not found in the location/queryParams are left as-is so that
19
+ * partial templates (e.g. only static keys for batch mode) keep working.
20
+ *
21
+ * Usage:
22
+ * String url = UrlTemplateResolver.resolve(template, location, queryParams);
23
+ * // location may be null for batch mode (only queryParams keys are resolved).
24
+ */
25
+ public final class UrlTemplateResolver {
26
+
27
+ private static final Pattern PLACEHOLDER = Pattern.compile("\\{([a-zA-Z0-9_]+)\\}");
28
+
29
+ private UrlTemplateResolver() { /* no instances */ }
30
+
31
+ public static String resolve(String template, BackgroundLocation location, Map<String, ?> queryParams) {
32
+ if (template == null || template.isEmpty()) return template;
33
+ Map<String, String> ctx = buildContext(location, queryParams);
34
+ Matcher m = PLACEHOLDER.matcher(template);
35
+ StringBuffer sb = new StringBuffer(template.length());
36
+ while (m.find()) {
37
+ String key = m.group(1);
38
+ String value = ctx.get(key);
39
+ if (value != null) {
40
+ m.appendReplacement(sb, Matcher.quoteReplacement(urlEncode(value)));
41
+ } else {
42
+ // Leave placeholder as-is if no value available.
43
+ m.appendReplacement(sb, Matcher.quoteReplacement(m.group(0)));
44
+ }
45
+ }
46
+ m.appendTail(sb);
47
+ return sb.toString();
48
+ }
49
+
50
+ public static boolean hasPlaceholders(String template) {
51
+ if (template == null) return false;
52
+ return PLACEHOLDER.matcher(template).find();
53
+ }
54
+
55
+ private static Map<String, String> buildContext(BackgroundLocation loc, Map<String, ?> queryParams) {
56
+ Map<String, String> ctx = new HashMap<String, String>();
57
+
58
+ // queryParams first so location-derived values can override if user wants
59
+ if (queryParams != null) {
60
+ for (Map.Entry<String, ?> e : queryParams.entrySet()) {
61
+ if (e.getValue() != null) {
62
+ ctx.put(e.getKey(), String.valueOf(e.getValue()));
63
+ }
64
+ }
65
+ }
66
+
67
+ if (loc != null) {
68
+ ctx.put("latitude", String.valueOf(loc.getLatitude()));
69
+ ctx.put("longitude", String.valueOf(loc.getLongitude()));
70
+ ctx.put("lat", String.valueOf(loc.getLatitude()));
71
+ ctx.put("lon", String.valueOf(loc.getLongitude()));
72
+
73
+ long timeMs = loc.getTime();
74
+ ctx.put("time", String.valueOf(timeMs));
75
+ ctx.put("timestamp", String.valueOf(timeMs));
76
+ ctx.put("timestamp_iso", isoUtc(timeMs));
77
+
78
+ if (loc.hasSpeed()) ctx.put("speed", String.valueOf(loc.getSpeed()));
79
+ if (loc.hasAltitude()) ctx.put("altitude", String.valueOf(loc.getAltitude()));
80
+ if (loc.hasBearing()) ctx.put("bearing", String.valueOf(loc.getBearing()));
81
+ if (loc.hasAccuracy()) ctx.put("accuracy", String.valueOf(loc.getAccuracy()));
82
+
83
+ if (loc.getProvider() != null) ctx.put("provider", loc.getProvider());
84
+ // is_moving derived from speed when available (>0.5 m/s ~ walking pace).
85
+ if (loc.hasSpeed()) {
86
+ ctx.put("is_moving", loc.getSpeed() > 0.5f ? "true" : "false");
87
+ }
88
+ // {activity} is not produced by BackgroundLocation by default; the user can supply
89
+ // a value via queryParams (already populated above).
90
+ }
91
+
92
+ return ctx;
93
+ }
94
+
95
+ /**
96
+ * URL-encode a placeholder value for use in URL paths and query strings.
97
+ * Spaces become %20, not + (which is form-encoding).
98
+ */
99
+ private static String urlEncode(String s) {
100
+ if (s == null) return "";
101
+ try {
102
+ String enc = java.net.URLEncoder.encode(s, "UTF-8");
103
+ // URLEncoder uses application/x-www-form-urlencoded; convert "+" back to "%20" for URL safety.
104
+ return enc.replace("+", "%20");
105
+ } catch (Exception e) {
106
+ return s;
107
+ }
108
+ }
109
+
110
+ private static String isoUtc(long ms) {
111
+ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US);
112
+ sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
113
+ return sdf.format(new Date(ms));
114
+ }
115
+ }