@josuelmm/cordova-background-geolocation 3.1.1 → 4.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/.npmignore +4 -0
  2. package/CHANGELOG.md +313 -0
  3. package/CLAUDE.md +56 -0
  4. package/HISTORY.md +124 -0
  5. package/README.md +198 -6
  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 +362 -1
  8. package/android/common/src/main/java/com/marianhello/bgloc/BackgroundGeolocationFacade.java +153 -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 +48 -5
  14. package/android/common/src/main/java/com/marianhello/bgloc/data/SessionLocationDAO.java +18 -0
  15. package/android/common/src/main/java/com/marianhello/bgloc/data/sqlite/SQLiteOpenHelper.java +8 -1
  16. package/android/common/src/main/java/com/marianhello/bgloc/data/sqlite/SQLiteSessionContract.java +74 -0
  17. package/android/common/src/main/java/com/marianhello/bgloc/data/sqlite/SQLiteSessionLocationDAO.java +169 -0
  18. package/android/common/src/main/java/com/marianhello/bgloc/driving/DrivingEventsDetector.java +265 -0
  19. package/android/common/src/main/java/com/marianhello/bgloc/http/UrlTemplateResolver.java +115 -0
  20. package/android/common/src/main/java/com/marianhello/bgloc/oem/BatteryOemHelper.java +214 -0
  21. package/android/common/src/main/java/com/marianhello/bgloc/provider/ActivityRecognitionLocationProvider.java +13 -9
  22. package/android/common/src/main/java/com/marianhello/bgloc/provider/DistanceFilterLocationProvider.java +29 -40
  23. package/android/common/src/main/java/com/marianhello/bgloc/provider/RawLocationProvider.java +14 -34
  24. package/android/common/src/main/java/com/marianhello/bgloc/sensor/SensorFusionDetector.java +199 -0
  25. package/android/common/src/main/java/com/marianhello/bgloc/service/LocationServiceImpl.java +310 -7
  26. package/android/common/src/main/java/com/marianhello/bgloc/service/LocationServiceProxy.java +14 -2
  27. package/android/common/src/main/java/com/marianhello/bgloc/sync/SyncAdapter.java +50 -3
  28. package/android/dependencies.gradle +0 -3
  29. package/angular/background-geolocation-events.ts +21 -0
  30. package/angular/background-geolocation.service.ts +91 -0
  31. package/angular/dist/background-geolocation-events.d.ts +18 -1
  32. package/angular/dist/background-geolocation.service.d.ts +40 -0
  33. package/angular/dist/esm2022/background-geolocation-events.mjs +22 -1
  34. package/angular/dist/esm2022/background-geolocation.service.mjs +47 -1
  35. package/angular/dist/fesm2022/josuelmm-cordova-background-geolocation.mjs +67 -0
  36. package/angular/dist/fesm2022/josuelmm-cordova-background-geolocation.mjs.map +1 -1
  37. package/ios/CDVBackgroundGeolocation/CDVBackgroundGeolocation.h +4 -0
  38. package/ios/CDVBackgroundGeolocation/CDVBackgroundGeolocation.m +352 -1
  39. package/ios/common/BackgroundGeolocation/MAURBackgroundGeolocationFacade.h +26 -0
  40. package/ios/common/BackgroundGeolocation/MAURBackgroundGeolocationFacade.m +421 -15
  41. package/ios/common/BackgroundGeolocation/MAURBackgroundSync.h +12 -0
  42. package/ios/common/BackgroundGeolocation/MAURBackgroundSync.m +83 -5
  43. package/ios/common/BackgroundGeolocation/MAURConfig.h +15 -0
  44. package/ios/common/BackgroundGeolocation/MAURConfig.m +100 -3
  45. package/ios/common/BackgroundGeolocation/MAURDistanceFilterLocationProvider.m +29 -2
  46. package/ios/common/BackgroundGeolocation/MAURGeolocationOpenHelper.m +12 -3
  47. package/ios/common/BackgroundGeolocation/MAURPostLocationTask.h +4 -0
  48. package/ios/common/BackgroundGeolocation/MAURPostLocationTask.m +102 -44
  49. package/ios/common/BackgroundGeolocation/MAURSensorFusionDetector.h +41 -0
  50. package/ios/common/BackgroundGeolocation/MAURSensorFusionDetector.m +137 -0
  51. package/ios/common/BackgroundGeolocation/MAURSessionLocationContract.h +29 -0
  52. package/ios/common/BackgroundGeolocation/MAURSessionLocationContract.m +31 -0
  53. package/ios/common/BackgroundGeolocation/MAURSessionLocationDAO.h +25 -0
  54. package/ios/common/BackgroundGeolocation/MAURSessionLocationDAO.m +153 -0
  55. package/ios/common/BackgroundGeolocation/MAURUrlTemplateResolver.h +31 -0
  56. package/ios/common/BackgroundGeolocation/MAURUrlTemplateResolver.m +107 -0
  57. package/package.json +36 -1
  58. package/plugin.xml +26 -8
  59. package/www/BackgroundGeolocation.d.ts +559 -3
  60. package/www/BackgroundGeolocation.js +78 -1
  61. package/RELEASE.MD +0 -16
package/README.md CHANGED
@@ -193,15 +193,19 @@ Set your preferred provider, accuracy, intervals, and optional server URLs. All
193
193
  | `syncUrl` | Server URL where **pending** locations are sent in batch (when count reaches `syncThreshold` or on `forceSync()`) |
194
194
  | `syncThreshold` | Number of pending locations before automatic batch sync (default 100) |
195
195
  | `sync` | When `true` (default), automatic sync and `forceSync()` run. When `false`, sync is disabled (locations are still stored). |
196
- | `httpHeaders` | Headers for every POST (e.g. `{ 'Content-Type': 'application/json', 'Authorization': 'Bearer TOKEN' }`) |
197
- | `postTemplate` | Object or array of properties to send (use `@latitude`, `@longitude`, etc.). See [Custom post template](docs/api.md#custom-post-template). |
196
+ | `httpHeaders` / `headers` | Headers for every request (e.g. `{ 'Content-Type': 'application/json', 'Authorization': 'Bearer TOKEN' }`). `headers` is the alias added in 3.3.0; both are supported. |
197
+ | `httpMethod` | **Since 3.3.0.** HTTP method for `url`. `POST` (default) `| GET | PUT | PATCH`. Use `GET` together with URL templating to deliver positions through the query string. |
198
+ | `syncHttpMethod` | **Since 3.3.0.** HTTP method for `syncUrl`. Same values as `httpMethod`. |
199
+ | `httpMode` / `syncMode` | **Since 3.3.0.** `batch` (default) or `single` (one HTTP request per location). `single` is required when the corresponding method is `GET`. |
200
+ | `queryParams` | **Since 3.3.0.** Static placeholder values for URL/body templating (e.g. `{ device_id: 'USER_123' }`). See [HTTP transport](docs/api.md#http-transport-since-330). |
201
+ | `postTemplate` / `bodyTemplate` | Object or array of properties to send. Supports `@latitude`, `@longitude`, ... and the new `{latitude}`, `{lon}`, `{timestamp_iso}`, ... placeholders on string values. `bodyTemplate` is the alias added in 3.3.0. |
198
202
  | `maxLocations` | Max locations kept in DB (default 10000). Should be > `syncThreshold`. |
199
203
 
200
204
  **Android-only:** `notificationSyncTitle`, `notificationSyncText`, `notificationSyncCompletedText`, `notificationSyncFailedText` — texts shown in the notification while syncing (defaults in English; set for localization). `startForeground`, `notificationsEnabled`, `startOnBoot`, `stopOnTerminate`, `enableWatchdog`.
201
205
 
202
- **iOS-only:** `activityType`, `pauseLocationUpdates`, `saveBatteryOnBackground`.
206
+ **iOS-only:** `activityType`, `pauseLocationUpdates`, `saveBatteryOnBackground`, `showsBackgroundLocationIndicator` (since 3.4.0; iOS 11+, shows the blue background indicator).
203
207
 
204
- Example:
208
+ Example (default POST + JSON):
205
209
 
206
210
  ```js
207
211
  BackgroundGeolocation.configure({
@@ -215,6 +219,8 @@ BackgroundGeolocation.configure({
215
219
  interval: 10000,
216
220
  fastestInterval: 5000,
217
221
  activitiesInterval: 10000,
222
+ startOnBoot: true,
223
+ stopOnTerminate: false,
218
224
  url: 'https://yourserver.com/location',
219
225
  syncUrl: 'https://yourserver.com/location',
220
226
  syncThreshold: 5,
@@ -231,6 +237,21 @@ BackgroundGeolocation.configure({
231
237
  });
232
238
  ```
233
239
 
240
+ Example (since 3.3.0, GET with URL templating — works against Traccar OsmAnd, GPSWox, custom REST, etc.):
241
+
242
+ ```js
243
+ BackgroundGeolocation.configure({
244
+ url: 'https://your-backend/track?uid={uid}&lat={latitude}&lon={longitude}&t={timestamp_iso}&spd={speed}',
245
+ httpMethod: 'GET',
246
+ httpMode: 'single',
247
+ queryParams: { uid: 'USER_DEVICE_123' },
248
+ startOnBoot: true,
249
+ stopOnTerminate: false
250
+ });
251
+ ```
252
+
253
+ > **Android runtime permissions.** With `startOnBoot: true`, the app must request `ACCESS_FINE_LOCATION` first, then `POST_NOTIFICATIONS` (Android 13+), and finally `ACCESS_BACKGROUND_LOCATION` (Android 10+, the user must pick **Allow all the time**). Without the background permission the plugin will skip the start on boot. iOS does **not** allow auto-start on device boot (Apple restriction). See [Auto-start on Android boot](docs/api.md#auto-start-on-android-boot-since-330) for the full flow.
254
+
234
255
  ### 2. Start tracking
235
256
 
236
257
  ```js
@@ -316,11 +337,15 @@ More on sync (headers, retries, postTemplate): [HTTP posting](docs/http_posting.
316
337
  | `deleteLocation(id, success, fail)` | Delete one location by id. |
317
338
  | `deleteAllLocations(success, fail)` | Delete all stored locations. |
318
339
  | `getCurrentLocation(success, fail, options)` | One-shot location (e.g. timeout, maximumAge). |
319
- | `getPluginVersion(success, fail)` | Plugin version string (e.g. "3.1.1"). |
340
+ | `getPluginVersion(success, fail)` | Plugin version string (e.g. "3.2.0"). |
320
341
  | `checkStatus(success, fail)` | Service status (isRunning, authorization, etc.). |
321
342
  | `showAppSettings()` / `openSettings()` | Open app settings. |
322
343
  | `showLocationSettings()` | Open system location settings. |
323
344
  | `getLogEntries(limit, fromId, minLevel, success, fail)` | Debug log entries. |
345
+ | `startSession(success, fail)` | Start session: clear session table and store all new locations until `clearSession()`. |
346
+ | `getSessionLocations(success, fail)` | All locations in current session (restore route when reopening without internet). |
347
+ | `clearSession(success, fail)` | Clear session table (call when route finished and sync OK). |
348
+ | `getSessionLocationsCount(success, fail)` | Number of locations in current session. |
324
349
 
325
350
  All methods return a **Promise** if you omit the `success` / `fail` callbacks.
326
351
 
@@ -343,6 +368,90 @@ Subscribe with `BackgroundGeolocation.on(eventName, callback)`. Unsubscribe with
343
368
 
344
369
  Full event payloads and options: [Events](docs/events.md). Full API (all options, all methods): [API](docs/api.md).
345
370
 
371
+ ### New in 4.2.0
372
+
373
+ - **Real sensor fusion (Phase 8).** Optional accelerometer + gyroscope pipeline (`drivingEvents.sensorFusion: true`). On Android uses `Sensor.TYPE_LINEAR_ACCELERATION` + `Sensor.TYPE_GYROSCOPE`; on iOS uses `CMMotionManager.startDeviceMotionUpdatesToQueue` at 50 Hz.
374
+ - **Sensor-driven `possibleCrash`.** When the pipeline detects `|a| ≥ crashImpactG` (default 3 g) during an active trip, emits `possibleCrash` with `source: "sensor"`. GPS-derived `possibleCrash` keeps emitting with `source: "gps"`. Detects parking-lot impacts that GPS alone misses.
375
+ - **New event `phoneUsageWhileDriving`.** Sustained device interaction (gyro/accel jitter + screen on) during an active trip.
376
+ - New thresholds in `drivingEvents`: `crashImpactG`, `sensorCrashCooldownMs`, `phoneUsageWindowMs`, `phoneUsageCooldownMs`.
377
+ - Hot-reload: changes to `drivingEvents.sensorFusion` via `configure()` (re)start the pipeline without a full service restart.
378
+ - Battery: pipeline only samples while a trip is active. Off by default — opt in with `sensorFusion: true`.
379
+
380
+ ```javascript
381
+ BackgroundGeolocation.configure({
382
+ drivingEvents: {
383
+ enabled: true,
384
+ sensorFusion: true, // v4.2 — opt in
385
+ crashImpactG: 3.0, // |a| in g for sensor-driven possibleCrash (default 3.0)
386
+ phoneUsageWindowMs: 4000,
387
+ phoneUsageCooldownMs: 60000
388
+ }
389
+ });
390
+
391
+ BackgroundGeolocation.on('possibleCrash', (d) => {
392
+ // d.source === 'gps' (GPS heuristic) or 'sensor' (accelerometer impact, higher confidence)
393
+ });
394
+ BackgroundGeolocation.on('phoneUsageWhileDriving', (location) => {
395
+ // Sustained device interaction during an active trip with the screen on.
396
+ });
397
+ ```
398
+
399
+ Full reference (all thresholds, when to enable, iOS screen-on caveat): [docs/api.md → Driver insights](docs/api.md#driver-insights-since-400).
400
+
401
+ ### New in 4.1.0
402
+
403
+ - **GPS-derived driving events.** Four new events: `hardBrake`, `rapidAcceleration`, `sharpTurn`, `possibleCrash`. All derived from GPS speed and bearing — **no accelerometer/gyroscope required**. Configure thresholds via the extended `drivingEvents` config (`hardBrakeMps2`, `rapidAccelMps2`, `sharpTurnDegPerSec`, `crashImpactKmh`, `crashWindowMs`). Each event has a 4 s cooldown.
404
+ - `possibleCrash` is heuristic. **Always confirm with the user** before notifying contacts; false positives are expected (sudden tunnel exit, GPS glitches).
405
+ - TypeScript: 4 new typed `on()` overloads with `{ location, value }` payloads.
406
+ - v4.2 adds real sensor fusion on top of these events.
407
+
408
+ ### New in 4.0.0
409
+
410
+ - **Driver insights (Phase 6).** New GPS-only state machine emits `tripStart`, `tripEnd`, `moving`, `stopped`, `speeding`, `providerChange`, `sos` events end-to-end (Android + iOS). Configure thresholds via the new `drivingEvents` option.
411
+ - New method **`triggerSOS(payload?)`** — fires a single `sos` event with the latest known location plus your payload. The host app handles the actual SOS workflow (notify contacts, push, alarm UI).
412
+ - The Angular service exposes `triggerSOS()`.
413
+ - ✅ Driving events (`hardBrake`, `rapidAcceleration`, `sharpTurn`, `possibleCrash`) ya están disponibles en v4.1.0 — derivados de GPS (sin sensores adicionales). Real sensor fusion (acelerómetro/giroscopio) se planea para v4.2.
414
+
415
+ ### New in 3.6.0
416
+
417
+ - **Battery / OEM helpers (Phase 5).** Five new methods to guide the user through the steps required for reliable background tracking on aggressive OEMs:
418
+ - `isIgnoringBatteryOptimizations()` — Android Doze whitelist state.
419
+ - `requestIgnoreBatteryOptimizations()` — opens the system dialog.
420
+ - `openBatterySettings()` — opens the per-app battery screen.
421
+ - `openAutoStartSettings()` — opens the OEM-specific auto-start screen on Xiaomi MIUI, Huawei EMUI, Oppo ColorOS, Vivo FunTouch, OnePlus, Asus (Samsung falls back to app-info).
422
+ - `getManufacturerHelp()` — returns OEM-specific guidance text the app can render as a help screen.
423
+ - The Angular service exposes all 5 methods.
424
+ - Combine with `getDiagnostics()` to build a "Tracking is being killed by your phone — fix it here" flow.
425
+
426
+ ### New in 3.5.0
427
+
428
+ - **`getDiagnostics()` (Phase 4).** New extended diagnostics method that helps you debug "tracking is not running" in production. Returns permissions (`fineLocationGranted`, `coarseLocationGranted`, `backgroundLocationGranted`, `notificationPermissionGranted`, `activityRecognitionGranted`), battery optimisation state (`batteryOptimizationIgnored`), OEM manufacturer (`manufacturer`), last-fix timestamp, pending sync count, declared `foregroundServiceType` (Android), and `preciseLocationEnabled`, `backgroundRefreshStatus`, `lowPowerModeEnabled`, `motionPermissionStatus` (iOS). Use it to surface actionable hints to users like "Allow all the time" or "Disable battery optimisation". See [getDiagnostics](docs/api.md#getdiagnosticssuccess-fail).
429
+ - **`mockLocationPolicy: 'allow' | 'flag' | 'drop'`** — policy for mocked locations. Detection (`isFromMockProvider` Android, `simulated` iOS) was already present; this option controls what to do with those samples. Recommended for anti-fraud: `'flag'` (keep them but tagged) or `'drop'` (discard).
430
+ - **`heartbeatInterval`** option plus events `heartbeat`, `syncStart`, `syncProgress`, `syncSuccess`, `syncError` are all wired end-to-end with native emission on Android and iOS. Subscribe via `BackgroundGeolocation.on('heartbeat', cb)` etc.
431
+ - The Angular service exposes `getDiagnostics()` directly.
432
+
433
+ ### New in 3.4.0
434
+
435
+ - **Location API modernization (Phase 3).** The Android Activity provider now uses `LocationRequest.Builder` + `Priority.PRIORITY_*` (replaces the deprecated `LocationRequest.create()` / `setPriority` / `setInterval` / `LocationRequest.PRIORITY_*` family from `play-services-location 21.0.0+`). The Distance Filter and Raw providers no longer use the deprecated `Criteria` API; provider selection is now an explicit GPS-first / Network-fallback. iOS migrates `[NSURLConnection sendSynchronousRequest:]` (deprecated since iOS 9) to `NSURLSession + dispatch_semaphore`, and adds the iOS 14+ `locationManagerDidChangeAuthorization:` callback alongside the legacy one.
436
+ - **`showsBackgroundLocationIndicator`** (iOS 11+). Set to `true` to display the blue status indicator while the app uses location in the background.
437
+ - **Bug fixes:** iOS `activitiesInterval` is now correctly read from the dictionary (the previous `isNull` check was inverted, dropping user input). Android `Content-Length` is computed in UTF-8 byte length instead of `String.length()`, so multi-byte characters (`ñ`, `é`, emoji) no longer mismatch the body size.
438
+ - **Default `GOOGLE_PLAY_SERVICES_VERSION`** raised to `21.0.1`. Apps that override it via `--variable` keep their override.
439
+
440
+ ### New in 3.3.0
441
+
442
+ - **Auto-start Android (Phase 1).** The plugin's boot receiver now also handles `QUICKBOOT_POWERON` (HTC, MIUI / Xiaomi), `com.htc.intent.action.QUICKBOOT_POWERON`, and `MY_PACKAGE_REPLACED` (the service relaunches after a Play Store update). Added `ACCESS_BACKGROUND_LOCATION` to the manifest with runtime validation on Android 10+. `ForegroundServiceStartNotAllowedException` (Android 12+) is caught with clear logging — WorkManager is **not** used as a fallback for tracking. The `foregroundServiceType` is now `"location"` only (dropped `dataSync`); the runtime type is read dynamically from the merged manifest. `<uses-library org.apache.http.legacy>` and `useLibrary 'org.apache.http.legacy'` are removed (the plugin uses `HttpURLConnection`). `jcenter()` → `mavenCentral()`.
443
+ - **Backend-agnostic HTTP transport (Phase 2).** New `httpMethod`, `syncHttpMethod`, `httpMode`, `syncMode`, `queryParams`, `headers` (alias of `httpHeaders`), `bodyTemplate` (alias of `postTemplate`). URL templating with `{latitude}`, `{longitude}`, `{lat}`, `{lon}`, `{time}`, `{timestamp}`, `{timestamp_iso}`, `{speed}`, `{altitude}`, `{bearing}`, `{accuracy}`, `{provider}` and any keys from `queryParams`. The plugin no longer hardcodes `POST` on Android (`HttpPostService`) or iOS (`MAURPostLocationTask`, `MAURBackgroundSync`). Compatibility is fully preserved — apps that only set `url` + `httpHeaders` + `postTemplate` keep working unchanged. See [HTTP transport](docs/api.md#http-transport-since-330) for backend examples (REST, GET single, form-urlencoded, Firebase, n8n).
444
+ - **Engines:** `cordova-android >= 12.0.0` is now the minimum.
445
+
446
+ ### New in 3.2.0
447
+
448
+ - **Session API for route/recording** — Store all locations for the *current route* in the plugin, independent of sync. When the user reopens the app without internet, you can restore the full track from the plugin (no need for `localStorage` for points).
449
+ - **`startSession()`** — Call when the user starts a route. Clears the session table; from then on every location is also saved in the session table (and not removed when synced).
450
+ - **`getSessionLocations()`** — Returns all session locations (same format as `Location`: latitude, longitude, time, speed, altitude, bearing, accuracy). Use to rebuild the track after reopening without internet.
451
+ - **`clearSession()`** — Call when the route is finished and sync succeeded. Clears the session table.
452
+ - **`getSessionLocationsCount()`** — Returns how many points are in the session (e.g. to show "X points" in the UI).
453
+ - Typical flow: **Start route** → `startSession()` then `start()`. **Reopen without internet** → `getSessionLocations()` and redraw the route. **Finish route and sync OK** → `clearSession()`.
454
+
346
455
  ### New in 3.1.1
347
456
 
348
457
  - **Browser / `ng serve` builds** — The plugin can now be bundled by webpack without "Can't resolve 'cordova/exec'" or "Can't resolve 'cordova/channel'". The package ships stub modules and a `browser` field so `ng serve` and browser builds succeed; on device/emulator the stubs delegate to the real Cordova API. See [docs/angular.md](docs/angular.md#build-ng-serve--browser).
@@ -422,6 +531,89 @@ No extra wrapper (e.g. Awesome Cordova Plugins) is required.
422
531
 
423
532
  ## Compatibility
424
533
 
534
+ | Plugin version | Cordova CLI | Cordova Android | Cordova iOS |
535
+ |----------------|-------------|-----------------|-------------|
536
+ | 1.x | ≥ 8.0.0 | ≥ 8.0.0 | ≥ 6.0.0 |
537
+ | 2.x | ≥ 10.0.0 | ≥ 10.0.0 | ≥ 6.0.0 |
538
+ | 3.0.x – 3.2.x | ≥ 10.0.0 | ≥ 10.0.0 | ≥ 6.0.0 |
539
+ | 3.3.x – 4.1.x | ≥ 10.0.0 | ≥ 12.0.0 | ≥ 6.2.0 |
540
+
541
+ > 3.3.0+: `cordova-android >= 12` is required for `targetSdk 34+` and the modernised foreground-service handling. The new iOS APIs (`showsBackgroundLocationIndicator` iOS 11+, `locationManagerDidChangeAuthorization:` iOS 14+) are gated by runtime `@available` checks, so older iOS versions still link and run.
542
+
543
+ ---
544
+
545
+ ## Documentation and changelog
546
+
547
+ This README is the main entry point. For more detail, edge cases and examples use the docs below (and the [online documentation](https://josuelmm.github.io/cordova-background-geolocation/)).
548
+
549
+ | Doc | What you'll find |
550
+ |-----|------------------|
551
+ | **[API reference](docs/api.md)** | Every `configure` option, every method (`configure`, `start`, `stop`, `getPendingSyncCount`, `forceSync`, `clearSync`, `startSession`, `getSessionLocations`, `clearSession`, `getSessionLocationsCount`, `getConfig`, `getLocations`, etc.), TypeScript types. Also covers the **HTTP transport (3.3.0)** and **auto-start on Android boot (3.3.0)**. |
552
+ | **[HTTP transport (3.3.0)](docs/http-transport.md)** | `httpMethod`, `httpMode`, URL templating, `bodyTemplate`, `queryParams`, examples for REST, GET single, form-urlencoded, Firebase, n8n, Traccar. |
553
+ | **[Auto-start on boot (3.3.0)](docs/auto-start.md)** | Android receiver actions, `ACCESS_BACKGROUND_LOCATION` runtime flow, OEM caveats, iOS limitation. |
554
+ | **[Traccar integration example](docs/traccar.md)** | Optional backend: how to deliver positions to Traccar (or Firebase, n8n, custom REST) via the HTTP transport. The plugin is backend-agnostic. |
555
+ | **[Location modernization (3.4.0)](docs/location-modernization.md)** | What was modernised in Phase 3 (LocationRequest.Builder, NSURLSession, iOS 14+ auth callback) and what is still legacy. |
556
+ | **[Roadmap](docs/ROADMAP.md)** | Phases 1–6, what's released, what's next. |
557
+ | **[HTTP posting](docs/http_posting.md)** | `url` vs `syncUrl`, Content-Type (JSON = one POST with array; form-urlencoded = one POST per location), headers, retries, `postTemplate`, sync behaviour. |
558
+ | **[Events](docs/events.md)** | All events (`location`, `error`, `stationary`, `activity`, `http_authorization`, etc.) and payloads. |
559
+ | **[Angular / Ionic](docs/angular.md)** | Injectable service, module, lazy-loaded modules and token "must be defined", `ng serve` / browser build. |
560
+ | **[Example](docs/example.md)** | Full example with events and sync. |
561
+ | **[CHANGELOG](CHANGELOG.md)** | Version history. |
562
+
563
+ This project is based on [@mauron85/cordova-plugin-background-geolocation](https://github.com/mauron85/cordova-plugin-background-geolocation) and the original by [christocracy](https://github.com/christocracy). Maintained at [josuelmm/cordova-background-geolocation](https://github.com/josuelmm/cordova-background-geolocation). Issues and PRs welcome.
564
+
565
+ ---
566
+
567
+ ## Licence
568
+
569
+ [Apache License](http://www.apache.org/licenses/LICENSE-2.0)
570
+
571
+ Copyright (c) 2013 Christopher Scott, Transistor Software
572
+
573
+ Permission is hereby granted, free of charge, to any person obtaining a copy
574
+ of this software and associated documentation files (the "Software"), to deal
575
+ in the Software without restriction, including without limitation the rights
576
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
577
+ copies of the Software, and to permit persons to whom the Software is
578
+ furnished to do so, subject to the following conditions:
579
+
580
+ The above copyright notice and this permission notice shall be included in
581
+ all copies or substantial portions of the Software.
582
+
583
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
584
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
585
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
586
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
587
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
588
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
589
+ THE SOFTWARE.
590
+ return this.bg.on('location', (loc: BackgroundGeolocationResponse) => console.log(loc));
591
+ // subscription.unsubscribe() when done
592
+ }
593
+ }
594
+ ```
595
+
596
+ **You must import `BackgroundGeolocationModule`** in your `AppModule` (or feature module) so the service is provided and AOT builds work. Then inject `BackgroundGeolocationService` as in the example above. See [docs/angular.md](docs/angular.md) for the full snippet.
597
+
598
+ **`ng serve` / browser:** From 3.1.1 the plugin includes browser stubs so `ng serve` and web builds complete without "Can't resolve 'cordova/exec'" — see [docs/angular.md](docs/angular.md#build-ng-serve--browser).
599
+
600
+ **Lazy-loaded pages:** If you see **NG0202** or *"dependency at index N is invalid"* when opening a page that injects this service, use an app-defined token and inject by that token (the plugin token can be undefined in the lazy chunk). See [docs/angular.md](docs/angular.md) (Lazy-loaded modules).
601
+
602
+ **Migrating from @awesome-cordova-plugins/background-geolocation:** there you inject a class named `BackgroundGeolocation`. In this package, `BackgroundGeolocation` is the **global plugin object**, not an injectable class. Use `BackgroundGeolocationService` instead (same API). See [docs/angular.md](docs/angular.md) for details.
603
+
604
+ ### Summary
605
+
606
+ | Use case | What to do |
607
+ |-----------------------|------------|
608
+ | **Without Angular** | Use global `BackgroundGeolocation` after `deviceready`. Types: main package or Awesome-style aliases (see [TypeScript imports](#typescript-imports) above). |
609
+ | **With Angular** | Import from `@josuelmm/cordova-background-geolocation/angular`: add `BackgroundGeolocationModule` to your module `imports`, then inject `BackgroundGeolocationService`. Do **not** inject the global `BackgroundGeolocation`. |
610
+
611
+ No extra wrapper (e.g. Awesome Cordova Plugins) is required.
612
+
613
+ ---
614
+
615
+ ## Compatibility
616
+
425
617
  | Plugin version | Cordova CLI | Cordova Android | Cordova iOS |
426
618
  |----------------|-------------|-----------------|-------------|
427
619
  | 1.x | ≥ 8.0.0 | ≥ 8.0.0 | ≥ 6.0.0 |
@@ -435,7 +627,7 @@ This README is the main entry point. For more detail, edge cases and examples us
435
627
 
436
628
  | Doc | What you’ll find |
437
629
  |-----|------------------|
438
- | **[API reference](docs/api.md)** | Every `configure` option, every method (`configure`, `start`, `stop`, `getPendingSyncCount`, `forceSync`, `clearSync`, `getConfig`, `getLocations`, etc.), TypeScript types. |
630
+ | **[API reference](docs/api.md)** | Every `configure` option, every method (`configure`, `start`, `stop`, `getPendingSyncCount`, `forceSync`, `clearSync`, `startSession`, `getSessionLocations`, `clearSession`, `getSessionLocationsCount`, `getConfig`, `getLocations`, etc.), TypeScript types. |
439
631
  | **[HTTP posting](docs/http_posting.md)** | `url` vs `syncUrl`, Content-Type (JSON = one POST with array; form-urlencoded = one POST per location), headers, retries, `postTemplate`, sync behaviour. |
440
632
  | **[Events](docs/events.md)** | All events (`location`, `error`, `stationary`, `activity`, `http_authorization`, etc.) and payloads. |
441
633
  | **[Angular / Ionic](docs/angular.md)** | Injectable service, module, lazy-loaded modules and token “must be defined”, `ng serve` / browser build. |
@@ -99,12 +99,17 @@ public class ConfigMapper {
99
99
  if (jObject.has("sync")) {
100
100
  config.setSyncEnabled(jObject.getBoolean("sync"));
101
101
  }
102
+ // headers (alias of httpHeaders)
102
103
  if (jObject.has("httpHeaders")) {
103
104
  config.setHttpHeaders(jObject.getJSONObject("httpHeaders"));
104
105
  }
106
+ if (jObject.has("headers")) {
107
+ config.setHttpHeaders(jObject.getJSONObject("headers"));
108
+ }
105
109
  if (jObject.has("maxLocations")) {
106
110
  config.setMaxLocations(jObject.getInt("maxLocations"));
107
111
  }
112
+ // bodyTemplate (alias of postTemplate)
108
113
  if (jObject.has("postTemplate")) {
109
114
  if (jObject.isNull("postTemplate")) {
110
115
  config.setTemplate(LocationTemplateFactory.getDefault());
@@ -113,6 +118,60 @@ public class ConfigMapper {
113
118
  config.setTemplate(LocationTemplateFactory.fromJSON(postTemplate));
114
119
  }
115
120
  }
121
+ if (jObject.has("bodyTemplate")) {
122
+ if (jObject.isNull("bodyTemplate")) {
123
+ config.setTemplate(LocationTemplateFactory.getDefault());
124
+ } else {
125
+ Object bodyTemplate = jObject.get("bodyTemplate");
126
+ config.setTemplate(LocationTemplateFactory.fromJSON(bodyTemplate));
127
+ }
128
+ }
129
+ // v3.3 Phase 2: HTTP transport
130
+ if (jObject.has("httpMethod") && !jObject.isNull("httpMethod")) {
131
+ config.setHttpMethod(jObject.getString("httpMethod"));
132
+ }
133
+ if (jObject.has("syncHttpMethod") && !jObject.isNull("syncHttpMethod")) {
134
+ config.setSyncHttpMethod(jObject.getString("syncHttpMethod"));
135
+ }
136
+ if (jObject.has("httpMode") && !jObject.isNull("httpMode")) {
137
+ config.setHttpMode(jObject.getString("httpMode"));
138
+ }
139
+ if (jObject.has("syncMode") && !jObject.isNull("syncMode")) {
140
+ config.setSyncMode(jObject.getString("syncMode"));
141
+ }
142
+ if (jObject.has("queryParams") && !jObject.isNull("queryParams")) {
143
+ config.setQueryParams(jObject.getJSONObject("queryParams"));
144
+ }
145
+ if (jObject.has("heartbeatInterval") && !jObject.isNull("heartbeatInterval")) {
146
+ config.setHeartbeatInterval(jObject.getInt("heartbeatInterval"));
147
+ }
148
+ if (jObject.has("mockLocationPolicy") && !jObject.isNull("mockLocationPolicy")) {
149
+ config.setMockLocationPolicy(jObject.getString("mockLocationPolicy"));
150
+ }
151
+ // v4.0 Phase 6: drivingEvents
152
+ if (jObject.has("drivingEvents") && !jObject.isNull("drivingEvents")) {
153
+ JSONObject de = jObject.getJSONObject("drivingEvents");
154
+ Config.DrivingEventsOptions opts = new Config.DrivingEventsOptions();
155
+ if (de.has("enabled")) opts.enabled = de.getBoolean("enabled");
156
+ if (de.has("speedLimit")) opts.speedLimitKmh = de.getDouble("speedLimit");
157
+ if (de.has("minMovingSpeed")) opts.minMovingSpeedMps = de.getDouble("minMovingSpeed");
158
+ if (de.has("stoppedDuration")) opts.stoppedDurationMs = de.getLong("stoppedDuration");
159
+ if (de.has("minTripSpeed")) opts.minTripSpeedMps = de.getDouble("minTripSpeed");
160
+ if (de.has("minTripDuration")) opts.minTripDurationMs = de.getLong("minTripDuration");
161
+ // v4.1
162
+ if (de.has("hardBrakeMps2")) opts.hardBrakeMps2 = de.getDouble("hardBrakeMps2");
163
+ if (de.has("rapidAccelMps2")) opts.rapidAccelMps2 = de.getDouble("rapidAccelMps2");
164
+ if (de.has("sharpTurnDegPerSec")) opts.sharpTurnDegPerSec = de.getDouble("sharpTurnDegPerSec");
165
+ if (de.has("crashImpactKmh")) opts.crashImpactKmh = de.getDouble("crashImpactKmh");
166
+ if (de.has("crashWindowMs")) opts.crashWindowMs = de.getLong("crashWindowMs");
167
+ // v4.2 sensor fusion
168
+ if (de.has("sensorFusion")) opts.sensorFusion = de.getBoolean("sensorFusion");
169
+ if (de.has("crashImpactG")) opts.crashImpactG = de.getDouble("crashImpactG");
170
+ if (de.has("sensorCrashCooldownMs"))opts.sensorCrashCooldownMs = de.getLong("sensorCrashCooldownMs");
171
+ if (de.has("phoneUsageWindowMs")) opts.phoneUsageWindowMs = de.getLong("phoneUsageWindowMs");
172
+ if (de.has("phoneUsageCooldownMs")) opts.phoneUsageCooldownMs = de.getLong("phoneUsageCooldownMs");
173
+ config.setDrivingEvents(opts);
174
+ }
116
175
  if (jObject.has("enableWatchdog")) {
117
176
  config.setEnableWatchdog(jObject.getBoolean("enableWatchdog"));
118
177
  }
@@ -175,6 +234,37 @@ public class ConfigMapper {
175
234
 
176
235
  json.put("postTemplate", template);
177
236
 
237
+ json.put("httpMethod", config.getHttpMethod());
238
+ json.put("syncHttpMethod", config.getSyncHttpMethod());
239
+ json.put("httpMode", config.getHttpMode());
240
+ json.put("syncMode", config.getSyncMode());
241
+ json.put("queryParams", config.getQueryParams() != null ? new JSONObject(config.getQueryParams()) : JSONObject.NULL);
242
+ json.put("heartbeatInterval", config.getHeartbeatInterval());
243
+ json.put("mockLocationPolicy", config.getMockLocationPolicy());
244
+
245
+ Config.DrivingEventsOptions de = config.getDrivingEvents();
246
+ if (de != null) {
247
+ JSONObject deJson = new JSONObject();
248
+ deJson.put("enabled", de.enabled);
249
+ deJson.put("speedLimit", de.speedLimitKmh);
250
+ deJson.put("minMovingSpeed", de.minMovingSpeedMps);
251
+ deJson.put("stoppedDuration", de.stoppedDurationMs);
252
+ deJson.put("minTripSpeed", de.minTripSpeedMps);
253
+ deJson.put("minTripDuration", de.minTripDurationMs);
254
+ deJson.put("hardBrakeMps2", de.hardBrakeMps2);
255
+ deJson.put("rapidAccelMps2", de.rapidAccelMps2);
256
+ deJson.put("sharpTurnDegPerSec", de.sharpTurnDegPerSec);
257
+ deJson.put("crashImpactKmh", de.crashImpactKmh);
258
+ deJson.put("crashWindowMs", de.crashWindowMs);
259
+ // v4.2 sensor fusion
260
+ deJson.put("sensorFusion", de.sensorFusion);
261
+ deJson.put("crashImpactG", de.crashImpactG);
262
+ deJson.put("sensorCrashCooldownMs", de.sensorCrashCooldownMs);
263
+ deJson.put("phoneUsageWindowMs", de.phoneUsageWindowMs);
264
+ deJson.put("phoneUsageCooldownMs", de.phoneUsageCooldownMs);
265
+ json.put("drivingEvents", deJson);
266
+ }
267
+
178
268
  return json;
179
269
  }
180
270
  }