@josuelmm/cordova-background-geolocation 3.2.0 → 4.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/.npmignore +4 -0
  2. package/CHANGELOG.md +290 -0
  3. package/CLAUDE.md +56 -0
  4. package/HISTORY.md +125 -0
  5. package/README.md +189 -4
  6. package/android/CDVBackgroundGeolocation/src/main/java/com/marianhello/bgloc/cordova/ConfigMapper.java +90 -0
  7. package/android/CDVBackgroundGeolocation/src/main/java/com/tenforwardconsulting/bgloc/cordova/BackgroundGeolocationPlugin.java +310 -1
  8. package/android/common/src/main/java/com/marianhello/bgloc/BackgroundGeolocationFacade.java +127 -0
  9. package/android/common/src/main/java/com/marianhello/bgloc/BootCompletedReceiver.java +27 -11
  10. package/android/common/src/main/java/com/marianhello/bgloc/Config.java +268 -0
  11. package/android/common/src/main/java/com/marianhello/bgloc/HttpPostService.java +86 -26
  12. package/android/common/src/main/java/com/marianhello/bgloc/PluginDelegate.java +26 -0
  13. package/android/common/src/main/java/com/marianhello/bgloc/PostLocationTask.java +42 -5
  14. package/android/common/src/main/java/com/marianhello/bgloc/driving/DrivingEventsDetector.java +265 -0
  15. package/android/common/src/main/java/com/marianhello/bgloc/http/UrlTemplateResolver.java +115 -0
  16. package/android/common/src/main/java/com/marianhello/bgloc/oem/BatteryOemHelper.java +214 -0
  17. package/android/common/src/main/java/com/marianhello/bgloc/provider/ActivityRecognitionLocationProvider.java +13 -9
  18. package/android/common/src/main/java/com/marianhello/bgloc/provider/DistanceFilterLocationProvider.java +29 -40
  19. package/android/common/src/main/java/com/marianhello/bgloc/provider/RawLocationProvider.java +14 -34
  20. package/android/common/src/main/java/com/marianhello/bgloc/sensor/SensorFusionDetector.java +199 -0
  21. package/android/common/src/main/java/com/marianhello/bgloc/service/LocationServiceImpl.java +305 -6
  22. package/android/common/src/main/java/com/marianhello/bgloc/service/LocationServiceProxy.java +14 -2
  23. package/android/common/src/main/java/com/marianhello/bgloc/sync/SyncAdapter.java +50 -3
  24. package/android/dependencies.gradle +0 -3
  25. package/angular/background-geolocation-events.ts +21 -0
  26. package/angular/background-geolocation.service.ts +63 -0
  27. package/angular/dist/background-geolocation-events.d.ts +18 -1
  28. package/angular/dist/background-geolocation.service.d.ts +36 -0
  29. package/angular/dist/esm2022/background-geolocation-events.mjs +22 -1
  30. package/angular/dist/esm2022/background-geolocation.service.mjs +35 -1
  31. package/angular/dist/fesm2022/josuelmm-cordova-background-geolocation.mjs +55 -0
  32. package/angular/dist/fesm2022/josuelmm-cordova-background-geolocation.mjs.map +1 -1
  33. package/ios/CDVBackgroundGeolocation/CDVBackgroundGeolocation.m +312 -1
  34. package/ios/common/BackgroundGeolocation/MAURBackgroundGeolocationFacade.h +22 -0
  35. package/ios/common/BackgroundGeolocation/MAURBackgroundGeolocationFacade.m +400 -15
  36. package/ios/common/BackgroundGeolocation/MAURBackgroundSync.h +12 -0
  37. package/ios/common/BackgroundGeolocation/MAURBackgroundSync.m +83 -5
  38. package/ios/common/BackgroundGeolocation/MAURConfig.h +15 -0
  39. package/ios/common/BackgroundGeolocation/MAURConfig.m +100 -3
  40. package/ios/common/BackgroundGeolocation/MAURDistanceFilterLocationProvider.m +29 -2
  41. package/ios/common/BackgroundGeolocation/MAURPostLocationTask.h +4 -0
  42. package/ios/common/BackgroundGeolocation/MAURPostLocationTask.m +97 -44
  43. package/ios/common/BackgroundGeolocation/MAURSensorFusionDetector.h +41 -0
  44. package/ios/common/BackgroundGeolocation/MAURSensorFusionDetector.m +137 -0
  45. package/ios/common/BackgroundGeolocation/MAURUrlTemplateResolver.h +31 -0
  46. package/ios/common/BackgroundGeolocation/MAURUrlTemplateResolver.m +107 -0
  47. package/package.json +41 -1
  48. package/plugin.xml +19 -8
  49. package/www/BackgroundGeolocation.d.ts +517 -3
  50. package/www/BackgroundGeolocation.js +54 -1
  51. package/RELEASE.MD +0 -16
@@ -6,7 +6,6 @@ import android.content.BroadcastReceiver;
6
6
  import android.content.Context;
7
7
  import android.content.Intent;
8
8
  import android.content.IntentFilter;
9
- import android.location.Criteria;
10
9
  import android.location.Location;
11
10
  import android.location.LocationListener;
12
11
  import android.location.LocationManager;
@@ -55,8 +54,6 @@ public class DistanceFilterLocationProvider extends AbstractLocationProvider imp
55
54
  private PendingIntent singleUpdatePI;
56
55
  private Integer scaledDistanceFilter;
57
56
 
58
- private Criteria criteria;
59
-
60
57
  private LocationManager locationManager;
61
58
  private AlarmManager alarmManager;
62
59
 
@@ -110,12 +107,20 @@ public class DistanceFilterLocationProvider extends AbstractLocationProvider imp
110
107
  singleUpdatePI = PendingIntent.getBroadcast(mContext, 9003, singleLocationUpdateIntent, cancelCurrentFlag);
111
108
  registerReceiver(singleUpdateReceiver, new IntentFilter(SINGLE_LOCATION_UPDATE_ACTION));
112
109
 
113
- // Location criteria
114
- criteria = new Criteria();
115
- criteria.setAltitudeRequired(false);
116
- criteria.setBearingRequired(false);
117
- criteria.setSpeedRequired(true);
118
- criteria.setCostAllowed(true);
110
+ // v3.4 Phase 3: Criteria API removed (deprecated since Android 12 / API 31).
111
+ // Provider selection is now explicit (GPS-first / Network-fallback) via pickProvider().
112
+ }
113
+
114
+ /** v3.4 Phase 3: replaces getBestProvider(criteria, true). */
115
+ private String pickProvider() {
116
+ if (locationManager == null) return null;
117
+ if (locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)) {
118
+ return LocationManager.GPS_PROVIDER;
119
+ }
120
+ if (locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER)) {
121
+ return LocationManager.NETWORK_PROVIDER;
122
+ }
123
+ return null;
119
124
  }
120
125
 
121
126
  @Override
@@ -209,9 +214,6 @@ public class DistanceFilterLocationProvider extends AbstractLocationProvider imp
209
214
 
210
215
  try {
211
216
  locationManager.removeUpdates(this);
212
- criteria.setAccuracy(Criteria.ACCURACY_FINE);
213
- criteria.setHorizontalAccuracy(translateDesiredAccuracy(mConfig.getDesiredAccuracy()));
214
- criteria.setPowerRequirement(Criteria.POWER_HIGH);
215
217
 
216
218
  if (isMoving) {
217
219
  // setPace can be called while moving, after distanceFilter has been recalculated. We don't want to re-acquire velocity in this case.
@@ -228,18 +230,15 @@ public class DistanceFilterLocationProvider extends AbstractLocationProvider imp
228
230
  // Turn on each provider aggressively for a short period of time
229
231
  List<String> matchingProviders = locationManager.getAllProviders();
230
232
  for (String provider: matchingProviders) {
231
- if (provider != LocationManager.PASSIVE_PROVIDER) {
233
+ if (!LocationManager.PASSIVE_PROVIDER.equals(provider)) {
232
234
  logger.info("Requesting location updates from provider {}", provider);
233
235
  locationManager.requestLocationUpdates(provider, 0, 0, this);
234
236
  }
235
237
  }
236
238
  } else {
237
- String provider = locationManager.getBestProvider(criteria, true);
238
- if (Build.VERSION.SDK_INT > 30 && locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)) {
239
- provider = LocationManager.GPS_PROVIDER;
240
- }
239
+ String provider = pickProvider();
241
240
  if (provider == null) {
242
- logger.warn("No best provider available");
241
+ logger.warn("No location provider available (GPS and Network disabled)");
243
242
  return;
244
243
  }
245
244
  logger.info("Requesting location updates from provider {}", provider);
@@ -251,23 +250,8 @@ public class DistanceFilterLocationProvider extends AbstractLocationProvider imp
251
250
  }
252
251
  }
253
252
 
254
- /**
255
- * Translates a number representing desired accuracy of Geolocation system from set [0, 10, 100, 1000].
256
- * 0: most aggressive, most accurate, worst battery drain
257
- * 1000: least aggressive, least accurate, best for battery.
258
- */
259
- private int translateDesiredAccuracy(Integer accuracy) {
260
- if (accuracy == null) {
261
- return Criteria.ACCURACY_MEDIUM;
262
- }
263
- if (accuracy >= 1000) {
264
- return Criteria.ACCURACY_LOW;
265
- }
266
- if (accuracy >= 100) {
267
- return Criteria.ACCURACY_MEDIUM;
268
- }
269
- return Criteria.ACCURACY_HIGH;
270
- }
253
+ // v3.4 Phase 3: translateDesiredAccuracy(...) returning Criteria.ACCURACY_* removed.
254
+ // Provider selection no longer depends on Criteria; pickProvider() chooses GPS-first.
271
255
 
272
256
  /**
273
257
  * Returns the most accurate and timely previously detected location.
@@ -533,14 +517,19 @@ public class DistanceFilterLocationProvider extends AbstractLocationProvider imp
533
517
  logger.info("Stationary location monitor fired");
534
518
  playDebugTone(Tone.DIALTONE);
535
519
 
536
- criteria.setAccuracy(Criteria.ACCURACY_FINE);
537
- criteria.setHorizontalAccuracy(Criteria.ACCURACY_HIGH);
538
- criteria.setPowerRequirement(Criteria.POWER_HIGH);
539
-
520
+ // v3.4 Phase 3: Criteria-based requestSingleUpdate removed (deprecated since API 31).
521
+ // Use the provider-string overload, which is still supported and stable.
522
+ String provider = pickProvider();
523
+ if (provider == null) {
524
+ logger.warn("Stationary monitor: no provider available");
525
+ return;
526
+ }
540
527
  try {
541
- locationManager.requestSingleUpdate(criteria, singleUpdatePI);
528
+ locationManager.requestSingleUpdate(provider, singleUpdatePI);
542
529
  } catch (SecurityException e) {
543
530
  logger.error("Security exception: {}", e.getMessage());
531
+ } catch (IllegalArgumentException e) {
532
+ logger.warn("requestSingleUpdate failed: {}", e.getMessage());
544
533
  }
545
534
  }
546
535
  }
@@ -1,14 +1,12 @@
1
1
  package com.marianhello.bgloc.provider;
2
2
 
3
3
  import android.content.Context;
4
- import android.location.Criteria;
5
4
  import android.location.Location;
6
5
  import android.location.LocationListener;
7
6
  import android.location.LocationManager;
8
7
  import android.os.Bundle;
9
8
 
10
9
  import com.marianhello.bgloc.Config;
11
- import com.marianhello.logging.LoggerManager;
12
10
 
13
11
  public class RawLocationProvider extends AbstractLocationProvider implements LocationListener {
14
12
  private LocationManager locationManager;
@@ -39,21 +37,10 @@ public class RawLocationProvider extends AbstractLocationProvider implements Loc
39
37
  logger.warn("RawLocationProvider started without config");
40
38
  return;
41
39
  }
42
- String provider = LocationManager.GPS_PROVIDER;
43
- boolean gpsEnabled = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
44
- if (!gpsEnabled) {
45
- Criteria criteria = new Criteria();
46
- criteria.setAltitudeRequired(false);
47
- criteria.setBearingRequired(false);
48
- criteria.setSpeedRequired(true);
49
- criteria.setCostAllowed(true);
50
- criteria.setAccuracy(Criteria.ACCURACY_FINE);
51
- criteria.setHorizontalAccuracy(translateDesiredAccuracy(mConfig.getDesiredAccuracy()));
52
- criteria.setPowerRequirement(Criteria.POWER_HIGH);
53
- provider = locationManager.getBestProvider(criteria, true);
54
- }
40
+ // v3.4 Phase 3: explicit GPS/Network selection. Drops the deprecated Criteria + getBestProvider.
41
+ String provider = pickProvider();
55
42
  if (provider == null) {
56
- logger.warn("No location provider available (GPS disabled and getBestProvider returned null)");
43
+ logger.warn("No location provider available (GPS and Network disabled)");
57
44
  return;
58
45
  }
59
46
  try {
@@ -66,6 +53,17 @@ public class RawLocationProvider extends AbstractLocationProvider implements Loc
66
53
  }
67
54
  }
68
55
 
56
+ /** GPS first, fall back to Network. */
57
+ private String pickProvider() {
58
+ if (locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)) {
59
+ return LocationManager.GPS_PROVIDER;
60
+ }
61
+ if (locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER)) {
62
+ return LocationManager.NETWORK_PROVIDER;
63
+ }
64
+ return null;
65
+ }
66
+
69
67
  @Override
70
68
  public void onStop() {
71
69
  if (!isStarted) {
@@ -118,24 +116,6 @@ public class RawLocationProvider extends AbstractLocationProvider implements Loc
118
116
  logger.debug("Provider {} was disabled", provider);
119
117
  }
120
118
 
121
- /**
122
- * Translates a number representing desired accuracy of Geolocation system from set [0, 10, 100, 1000].
123
- * 0: most aggressive, most accurate, worst battery drain
124
- * 1000: least aggressive, least accurate, best for battery.
125
- */
126
- private int translateDesiredAccuracy(Integer accuracy) {
127
- if (accuracy == null) {
128
- return Criteria.ACCURACY_MEDIUM;
129
- }
130
- if (accuracy >= 1000) {
131
- return Criteria.ACCURACY_LOW;
132
- }
133
- if (accuracy >= 100) {
134
- return Criteria.ACCURACY_MEDIUM;
135
- }
136
- return Criteria.ACCURACY_HIGH;
137
- }
138
-
139
119
  @Override
140
120
  public void onDestroy() {
141
121
  logger.debug("Destroying RawLocationProvider");
@@ -0,0 +1,199 @@
1
+ package com.marianhello.bgloc.sensor;
2
+
3
+ import android.content.Context;
4
+ import android.hardware.Sensor;
5
+ import android.hardware.SensorEvent;
6
+ import android.hardware.SensorEventListener;
7
+ import android.hardware.SensorManager;
8
+ import android.os.Handler;
9
+ import android.os.Looper;
10
+ import android.os.PowerManager;
11
+
12
+ import com.marianhello.bgloc.data.BackgroundLocation;
13
+
14
+ /**
15
+ * v4.2 Phase 8 — Real sensor fusion detector.
16
+ *
17
+ * Samples linear acceleration (TYPE_LINEAR_ACCELERATION, gravity removed) and
18
+ * gyroscope (TYPE_GYROSCOPE) at SENSOR_DELAY_GAME (~50 Hz). Used to refine
19
+ * possibleCrash and to detect phoneUsageWhileDriving. Pure-Android, no JNI.
20
+ *
21
+ * Crash detection: |a| (m/s²) above {@code crashImpactG} g during a tripActive
22
+ * window emits {@link Listener#onPossibleCrash}. Combined with the GPS-derived
23
+ * heuristic in {@link com.marianhello.bgloc.driving.DrivingEventsDetector},
24
+ * this gives a far higher-confidence signal at low speeds (parking-lot impact)
25
+ * where GPS alone misses.
26
+ *
27
+ * phoneUsageWhileDriving: while {@code tripActive} is true, if the screen turns
28
+ * on and the user produces touch-shaped jitter on the device for a sustained
29
+ * window, fires {@link Listener#onPhoneUsageWhileDriving}. Conservative — designed
30
+ * to avoid false-positives from passenger usage by tying to {@code tripActive}.
31
+ */
32
+ public class SensorFusionDetector implements SensorEventListener {
33
+
34
+ public interface Listener {
35
+ /** Triggered when |a| exceeds crashImpactG while tripActive. */
36
+ void onSensorCrash(BackgroundLocation lastLocation, double impactG);
37
+ /** Screen on + sustained device interaction during tripActive. */
38
+ void onPhoneUsageWhileDriving(BackgroundLocation lastLocation);
39
+ }
40
+
41
+ public static class Config {
42
+ public boolean enabled = false;
43
+ /** Crash threshold in g. 1g = 9.81 m/s². Default 3g. */
44
+ public double crashImpactG = 3.0;
45
+ /** Cooldown between repeated crash detections. */
46
+ public long crashCooldownMs = 10_000;
47
+ /** Min sustained gyro+accel jitter window for phoneUsage. */
48
+ public long phoneUsageWindowMs = 4_000;
49
+ /** Cooldown between repeated phone-usage events. */
50
+ public long phoneUsageCooldownMs = 60_000;
51
+ }
52
+
53
+ private static final float G = 9.80665f;
54
+
55
+ private final Context appContext;
56
+ private final Listener listener;
57
+ private final SensorManager sensorManager;
58
+ private final Sensor linearAccel;
59
+ private final Sensor gyroscope;
60
+ private final PowerManager powerManager;
61
+ private final Handler handler;
62
+
63
+ private Config cfg = new Config();
64
+ private boolean started = false;
65
+ private boolean tripActive = false;
66
+ private BackgroundLocation lastLocation;
67
+
68
+ private long lastCrashAt = 0L;
69
+ private long lastPhoneUsageAt = 0L;
70
+
71
+ // phoneUsage state
72
+ private long jitterAboveSince = 0L;
73
+ private static final double JITTER_GYRO_RAD_S = 0.7; // ~40 deg/s
74
+ private static final double JITTER_ACCEL_MPS2 = 0.5; // small accel, hand movement
75
+
76
+ public SensorFusionDetector(Context context, Listener listener) {
77
+ this.appContext = context.getApplicationContext();
78
+ this.listener = listener;
79
+ this.sensorManager = (SensorManager) appContext.getSystemService(Context.SENSOR_SERVICE);
80
+ this.linearAccel = sensorManager != null ? sensorManager.getDefaultSensor(Sensor.TYPE_LINEAR_ACCELERATION) : null;
81
+ this.gyroscope = sensorManager != null ? sensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE) : null;
82
+ this.powerManager = (PowerManager) appContext.getSystemService(Context.POWER_SERVICE);
83
+ this.handler = new Handler(Looper.getMainLooper());
84
+ }
85
+
86
+ public synchronized void setConfig(Config c) {
87
+ if (c != null) this.cfg = c;
88
+ }
89
+
90
+ public synchronized boolean isAvailable() {
91
+ return sensorManager != null && linearAccel != null;
92
+ }
93
+
94
+ /** Start sampling sensors. Idempotent. */
95
+ public synchronized void start() {
96
+ if (started || !cfg.enabled || sensorManager == null) return;
97
+ if (linearAccel != null) {
98
+ sensorManager.registerListener(this, linearAccel, SensorManager.SENSOR_DELAY_GAME, handler);
99
+ }
100
+ if (gyroscope != null) {
101
+ sensorManager.registerListener(this, gyroscope, SensorManager.SENSOR_DELAY_GAME, handler);
102
+ }
103
+ started = true;
104
+ }
105
+
106
+ /** Stop sampling. Idempotent. */
107
+ public synchronized void stop() {
108
+ if (!started) return;
109
+ if (sensorManager != null) sensorManager.unregisterListener(this);
110
+ started = false;
111
+ jitterAboveSince = 0L;
112
+ }
113
+
114
+ /** Called by detector host whenever the GPS layer marks tripActive on/off. */
115
+ public synchronized void setTripActive(boolean active) {
116
+ this.tripActive = active;
117
+ if (!active) jitterAboveSince = 0L;
118
+ }
119
+
120
+ /** Last known location for event payload. Updated by host. */
121
+ public synchronized void setLastLocation(BackgroundLocation loc) {
122
+ this.lastLocation = loc;
123
+ }
124
+
125
+ @Override public void onAccuracyChanged(Sensor sensor, int accuracy) { /* ignore */ }
126
+
127
+ @Override
128
+ public void onSensorChanged(SensorEvent event) {
129
+ Listener l;
130
+ Config c;
131
+ boolean tripActiveNow;
132
+ BackgroundLocation loc;
133
+ synchronized (this) {
134
+ l = this.listener;
135
+ c = this.cfg;
136
+ tripActiveNow = this.tripActive;
137
+ loc = this.lastLocation;
138
+ }
139
+ if (l == null || c == null || !c.enabled) return;
140
+
141
+ long now = System.currentTimeMillis();
142
+
143
+ if (event.sensor.getType() == Sensor.TYPE_LINEAR_ACCELERATION) {
144
+ float ax = event.values[0], ay = event.values[1], az = event.values[2];
145
+ double mag = Math.sqrt(ax * ax + ay * ay + az * az);
146
+ double gMag = mag / G;
147
+
148
+ // Crash: high impact during a trip.
149
+ if (tripActiveNow && c.crashImpactG > 0 && gMag >= c.crashImpactG
150
+ && (now - lastCrashAt) >= c.crashCooldownMs) {
151
+ lastCrashAt = now;
152
+ l.onSensorCrash(loc, gMag);
153
+ }
154
+
155
+ // Phone usage signal: small accel jitter during trip + screen on.
156
+ evaluatePhoneUsage(now, mag, /*gyroMag*/ -1, c, l, tripActiveNow, loc);
157
+ } else if (event.sensor.getType() == Sensor.TYPE_GYROSCOPE) {
158
+ float gx = event.values[0], gy = event.values[1], gz = event.values[2];
159
+ double gyroMag = Math.sqrt(gx * gx + gy * gy + gz * gz);
160
+ evaluatePhoneUsage(now, /*accelMag*/ -1, gyroMag, c, l, tripActiveNow, loc);
161
+ }
162
+ }
163
+
164
+ private void evaluatePhoneUsage(long now,
165
+ double accelMag,
166
+ double gyroMag,
167
+ Config c,
168
+ Listener l,
169
+ boolean tripActiveNow,
170
+ BackgroundLocation loc) {
171
+ if (!tripActiveNow) { jitterAboveSince = 0L; return; }
172
+ if (powerManager == null || !isScreenOn()) { jitterAboveSince = 0L; return; }
173
+
174
+ boolean above = (accelMag >= 0 && accelMag >= JITTER_ACCEL_MPS2)
175
+ || (gyroMag >= 0 && gyroMag >= JITTER_GYRO_RAD_S);
176
+ if (above) {
177
+ if (jitterAboveSince == 0L) jitterAboveSince = now;
178
+ if ((now - jitterAboveSince) >= c.phoneUsageWindowMs
179
+ && (now - lastPhoneUsageAt) >= c.phoneUsageCooldownMs) {
180
+ lastPhoneUsageAt = now;
181
+ jitterAboveSince = 0L;
182
+ l.onPhoneUsageWhileDriving(loc);
183
+ }
184
+ } else {
185
+ jitterAboveSince = 0L;
186
+ }
187
+ }
188
+
189
+ @SuppressWarnings("deprecation")
190
+ private boolean isScreenOn() {
191
+ if (powerManager == null) return false;
192
+ try {
193
+ // isInteractive added API 20; we target >= 21 elsewhere, but guard anyway.
194
+ return powerManager.isInteractive();
195
+ } catch (Throwable t) {
196
+ return powerManager.isScreenOn();
197
+ }
198
+ }
199
+ }