@josuelmm/cordova-background-geolocation 4.2.3 → 4.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (103) hide show
  1. package/.npmignore +11 -0
  2. package/CHANGELOG.md +261 -0
  3. package/README.md +306 -115
  4. package/android/CDVBackgroundGeolocation/src/main/java/com/marianhello/bgloc/cordova/ConfigMapper.java +34 -0
  5. package/android/CDVBackgroundGeolocation/src/main/java/com/tenforwardconsulting/bgloc/cordova/BackgroundGeolocationPlugin.java +61 -1
  6. package/android/common/src/main/AndroidManifest.xml +1 -1
  7. package/android/common/src/main/java/com/marianhello/bgloc/BootCompletedReceiver.java +20 -3
  8. package/android/common/src/main/java/com/marianhello/bgloc/Config.java +87 -1
  9. package/android/common/src/main/java/com/marianhello/bgloc/data/BackgroundLocation.java +94 -0
  10. package/android/common/src/main/java/com/marianhello/bgloc/data/ConfigJsonMapper.java +211 -0
  11. package/android/common/src/main/java/com/marianhello/bgloc/data/LocationTemplateFactory.java +6 -0
  12. package/android/common/src/main/java/com/marianhello/bgloc/data/sqlite/SQLiteConfigurationContract.java +5 -1
  13. package/android/common/src/main/java/com/marianhello/bgloc/data/sqlite/SQLiteConfigurationDAO.java +32 -1
  14. package/android/common/src/main/java/com/marianhello/bgloc/data/sqlite/SQLiteLocationContract.java +12 -2
  15. package/android/common/src/main/java/com/marianhello/bgloc/data/sqlite/SQLiteLocationDAO.java +33 -2
  16. package/android/common/src/main/java/com/marianhello/bgloc/data/sqlite/SQLiteOpenHelper.java +15 -1
  17. package/android/common/src/main/java/com/marianhello/bgloc/provider/AbstractLocationProvider.java +48 -1
  18. package/android/common/src/main/java/com/marianhello/bgloc/provider/ActivityRecognitionLocationProvider.java +105 -6
  19. package/android/common/src/main/java/com/marianhello/bgloc/provider/DistanceFilterLocationProvider.java +336 -250
  20. package/android/common/src/main/java/com/marianhello/bgloc/provider/RawLocationProvider.java +69 -19
  21. package/android/common/src/main/java/com/marianhello/bgloc/service/LocationServiceImpl.java +246 -21
  22. package/android/common/src/main/java/com/marianhello/bgloc/service/LocationServiceProxy.java +5 -2
  23. package/android/common/src/main/java/com/marianhello/bgloc/sync/BatchManager.java +46 -13
  24. package/ios/CDVBackgroundGeolocation/CDVBackgroundGeolocation.m +23 -1
  25. package/ios/common/BackgroundGeolocation/MAURActivityLocationProvider.m +208 -70
  26. package/ios/common/BackgroundGeolocation/MAURBackgroundGeolocationFacade.m +132 -5
  27. package/ios/common/BackgroundGeolocation/MAURBackgroundSync.m +20 -0
  28. package/ios/common/BackgroundGeolocation/MAURConfig.h +7 -0
  29. package/ios/common/BackgroundGeolocation/MAURConfig.m +37 -2
  30. package/ios/common/BackgroundGeolocation/MAURConfigurationContract.h +3 -0
  31. package/ios/common/BackgroundGeolocation/MAURConfigurationContract.m +3 -1
  32. package/ios/common/BackgroundGeolocation/MAURDistanceFilterLocationProvider.m +10 -1
  33. package/ios/common/BackgroundGeolocation/MAURGeolocationOpenHelper.m +15 -1
  34. package/ios/common/BackgroundGeolocation/MAURLocation.h +12 -0
  35. package/ios/common/BackgroundGeolocation/MAURLocation.m +33 -4
  36. package/ios/common/BackgroundGeolocation/MAURLocationContract.h +4 -0
  37. package/ios/common/BackgroundGeolocation/MAURLocationContract.m +5 -1
  38. package/ios/common/BackgroundGeolocation/MAURLocationManager.m +19 -1
  39. package/ios/common/BackgroundGeolocation/MAURPostLocationTask.h +9 -0
  40. package/ios/common/BackgroundGeolocation/MAURPostLocationTask.m +59 -1
  41. package/ios/common/BackgroundGeolocation/MAURRawLocationProvider.m +10 -1
  42. package/ios/common/BackgroundGeolocation/MAURSQLiteConfigurationDAO.m +54 -4
  43. package/ios/common/BackgroundGeolocation/MAURSQLiteLocationDAO.h +12 -0
  44. package/ios/common/BackgroundGeolocation/MAURSQLiteLocationDAO.m +125 -5
  45. package/package.json +31 -1
  46. package/plugin.xml +3 -10
  47. package/www/BackgroundGeolocation.d.ts +143 -3
  48. package/www/BackgroundGeolocation.js +11 -4
  49. package/CLAUDE.md +0 -56
  50. package/HISTORY.md +0 -871
  51. package/android/CDVBackgroundGeolocation/src/test/java/com/marianhello/ConfigMapperTest.java +0 -220
  52. package/android/common/src/androidTest/java/com/marianhello/bgloc/BackgroundGeolocationFacadeTest.java +0 -45
  53. package/android/common/src/androidTest/java/com/marianhello/bgloc/BatchManagerTest.java +0 -570
  54. package/android/common/src/androidTest/java/com/marianhello/bgloc/ConfigTest.java +0 -76
  55. package/android/common/src/androidTest/java/com/marianhello/bgloc/ContentProviderLocationDAOTest.java +0 -437
  56. package/android/common/src/androidTest/java/com/marianhello/bgloc/DBLogReaderTest.java +0 -95
  57. package/android/common/src/androidTest/java/com/marianhello/bgloc/LocationContentProviderTest.java +0 -159
  58. package/android/common/src/androidTest/java/com/marianhello/bgloc/LocationServiceProxyTest.java +0 -161
  59. package/android/common/src/androidTest/java/com/marianhello/bgloc/LocationServiceTest.java +0 -247
  60. package/android/common/src/androidTest/java/com/marianhello/bgloc/SQLiteConfigurationDAOTest.java +0 -200
  61. package/android/common/src/androidTest/java/com/marianhello/bgloc/SQLiteLocationDAOTest.java +0 -457
  62. package/android/common/src/androidTest/java/com/marianhello/bgloc/SQLiteLocationDAOThreadTest.java +0 -96
  63. package/android/common/src/androidTest/java/com/marianhello/bgloc/SQLiteOpenHelperTest.java +0 -225
  64. package/android/common/src/androidTest/java/com/marianhello/bgloc/TestPluginDelegate.java +0 -46
  65. package/android/common/src/androidTest/java/com/marianhello/bgloc/TestResourceResolver.java +0 -14
  66. package/android/common/src/androidTest/java/com/marianhello/bgloc/provider/MockLocationProvider.java +0 -50
  67. package/android/common/src/androidTest/java/com/marianhello/bgloc/provider/TestLocationProviderFactory.java +0 -17
  68. package/android/common/src/androidTest/java/com/marianhello/bgloc/sqlite/SQLiteOpenHelper10.java +0 -92
  69. package/android/common/src/androidTest/java/com/marianhello/bgloc/test/LocationProviderTestCase.java +0 -107
  70. package/android/common/src/androidTest/java/com/marianhello/bgloc/test/TestConstants.java +0 -5
  71. package/android/common/src/test/java/com/marianhello/backgroundgeolocation/ArrayListLocationTemplateTest.java +0 -82
  72. package/android/common/src/test/java/com/marianhello/backgroundgeolocation/BackgroundLocationTest.java +0 -128
  73. package/android/common/src/test/java/com/marianhello/backgroundgeolocation/ConfigTest.java +0 -191
  74. package/android/common/src/test/java/com/marianhello/backgroundgeolocation/DBLogReaderTest.java +0 -37
  75. package/android/common/src/test/java/com/marianhello/backgroundgeolocation/HashMapLocationTemplateTest.java +0 -216
  76. package/android/common/src/test/java/com/marianhello/backgroundgeolocation/HttpPostServiceTest.java +0 -223
  77. package/android/common/src/test/java/com/marianhello/backgroundgeolocation/LocationTemplateFactoryTest.java +0 -50
  78. package/android/common/src/test/java/com/marianhello/backgroundgeolocation/PostLocationTaskTest.java +0 -180
  79. package/android/common/src/test/java/com/marianhello/backgroundgeolocation/TestHelper.java +0 -16
  80. package/ios/common/BackgroundGeolocation/SOMotionDetector/CHANGELOG.md +0 -2
  81. package/ios/common/BackgroundGeolocation/SOMotionDetector/LICENSE +0 -21
  82. package/ios/common/BackgroundGeolocation/SOMotionDetector/README.md +0 -135
  83. package/ios/common/BackgroundGeolocation/SOMotionDetector/SOLocationManager.h +0 -80
  84. package/ios/common/BackgroundGeolocation/SOMotionDetector/SOLocationManager.m +0 -147
  85. package/ios/common/BackgroundGeolocation/SOMotionDetector/SOMotionActivity.h +0 -30
  86. package/ios/common/BackgroundGeolocation/SOMotionDetector/SOMotionActivity.m +0 -42
  87. package/ios/common/BackgroundGeolocation/SOMotionDetector/SOMotionDetector.h +0 -99
  88. package/ios/common/BackgroundGeolocation/SOMotionDetector/SOMotionDetector.m +0 -327
  89. package/ios/common/BackgroundGeolocation/SOMotionDetector/SOStepDetector.h +0 -44
  90. package/ios/common/BackgroundGeolocation/SOMotionDetector/SOStepDetector.m +0 -94
  91. package/ios/common/BackgroundGeolocationTests/Info.plist +0 -24
  92. package/ios/common/BackgroundGeolocationTests/MAURBackgroundLocationTest.m +0 -185
  93. package/ios/common/BackgroundGeolocationTests/MAURConfigTest.m +0 -161
  94. package/ios/common/BackgroundGeolocationTests/MAURGeolocationOpenHelperTest.m +0 -102
  95. package/ios/common/BackgroundGeolocationTests/MAURLocationTest.m +0 -216
  96. package/ios/common/BackgroundGeolocationTests/MAURLocationUploaderTest.m +0 -55
  97. package/ios/common/BackgroundGeolocationTests/MAURLogReaderTest.m +0 -43
  98. package/ios/common/BackgroundGeolocationTests/MAURSQLiteConfigurationDAOTest.m +0 -102
  99. package/ios/common/BackgroundGeolocationTests/MAURSQLiteHelperTest.m +0 -41
  100. package/ios/common/BackgroundGeolocationTests/MAURSQLiteLocationDAOTests.m +0 -240
  101. package/ios/common/BackgroundGeolocationTests/MAURSQLiteLocationDAOThreadTest.m +0 -84
  102. package/ios/common/BackgroundGeolocationTests/MAURSQLiteOpenHelperTest.m +0 -144
  103. package/ios/common/scripts/xcode-refactor.js +0 -184
package/.npmignore CHANGED
@@ -8,6 +8,12 @@
8
8
  /REVIEW_*.md
9
9
  /RELEASE.MD
10
10
  /NPM_PUBLISH.md
11
+ /CLAUDE.md
12
+ # exclude dev tooling, CI, tarballs, workspace
13
+ /scripts
14
+ /.github
15
+ /josuelmm-cordova-background-geolocation-*.tgz
16
+ /cordova-background-geolocation-plugin.code-workspace
11
17
  !/plugin.xml
12
18
  !/package.json
13
19
  !/www
@@ -26,11 +32,16 @@
26
32
  !/android/common
27
33
  /android/common/*
28
34
  !/android/common/src
35
+ /android/common/src/test
36
+ /android/common/src/androidTest
29
37
  !/android/CDVBackgroundGeolocation
30
38
  /android/CDVBackgroundGeolocation/*
31
39
  !/android/CDVBackgroundGeolocation/src
40
+ /android/CDVBackgroundGeolocation/src/test
32
41
  !/ios
33
42
  /ios/*
34
43
  !/ios/common
44
+ /ios/common/BackgroundGeolocationTests
45
+ /ios/common/scripts
35
46
  !/ios/CDVBackgroundGeolocation
36
47
  !/LICENSE
package/CHANGELOG.md CHANGED
@@ -1,5 +1,266 @@
1
1
  # Changelog
2
2
 
3
+ ## [4.5.2](https://github.com/josuelmm/cordova-background-geolocation/tree/4.5.2) (2026-05-10)
4
+
5
+ ### Added — Provider Hardening
6
+ - **`activityConfidenceThreshold` (0-100, default 50)**: las transiciones STILL/ACTIVE por debajo de este umbral se ignoran. Antes, ACTIVITY_PROVIDER reaccionaba a cualquier ruido de baja confianza → bursts espurios de start/stop GPS. iOS normaliza `CMMotionActivityConfidence` (Low/Medium/High → 20/40/80) para que el umbral signifique lo mismo en ambas plataformas.
7
+ - **`maxAcceptedAccuracy` (m, opcional)**: filtro global que descarta fixes con accuracy peor a este valor antes de persistir/POST/emitir al JS. Aplica a los 3 providers.
8
+
9
+ ### Fixed (BLOQUEANTES — ACTIVITY_PROVIDER)
10
+ - **Android `ACTIVITY_PROVIDER` ignoraba `distanceFilter`**: el `LocationRequest.Builder` no llamaba a `setMinUpdateDistanceMeters`. Resultado: con `interval` bajo se bombardeaba al consumidor sin throttling por distancia.
11
+ - **Android sin verificación de Google Play Services**: si GPS estaba ausente/desactualizado, `FusedLocationProviderClient` + `ActivityRecognitionClient` fallaban silenciosamente. Ahora `onCreate` emite `SERVICE_ERROR` y queda inerte.
12
+ - **Android `ACTIVITY_RECOGNITION` denegado en Android 10+**: `requestActivityUpdates` retornaba sin emitir nada y STILL/ACTIVE nunca cambiaba → tracking continuo accidental. Ahora `attachRecorder` emite `PERMISSION_DENIED_ERROR` una vez y desiste.
13
+ - **Android `onConfigure` siempre stop+start**: incluso si la config nueva era idéntica, se dropeaba el callback y se reanudaba. Ahora compara `desiredAccuracy/interval/fastestInterval/distanceFilter/activitiesInterval/stopOnStillActivity` y solo reinicia si cambió algo relevante.
14
+ - **iOS `onLocationsChanged` durante NotMoving emitía DOBLE**: invocaba `onStationaryChanged` + un `onLocationChanged` por cada CLLocation. Resultado: rows "moving" fantasma durante ventana STILL. Ahora retorna tras stationary.
15
+ - **iOS confidence cruda (0/1/2 enum) comparada con threshold 0-100**: cualquier umbral > 2 ignoraba TODO. Normalizada a 0-100 en el provider.
16
+ - **iOS ACTIVITY/RAW/DISTANCE `onDestroy` no soltaba `delegate`**: `MAURLocationManager` y `SOMotionDetector` son singletons compartidos; un swap de provider dejaba el destruido recibiendo callbacks. `delegate = nil` en `onDestroy` + `dealloc`.
17
+
18
+ ### Fixed — Provider Errors
19
+ - **Android DISTANCE_FILTER + RAW `onProviderDisabled`**: era no-op silencioso. Ahora emite `SERVICE_ERROR` cuando no queda ningún provider disponible (el JS puede repromptear al usuario).
20
+ - **iOS `MAURLocationManager` no implementaba el callback iOS 14+ `locationManagerDidChangeAuthorization:`**: solo el legacy deprecado. RAW + ACTIVITY (que usan la singleton) no veían cambios de "Always → While Using" sin reinicio del proceso. Añadido, con guard `@available(iOS 14, *)` en el legacy para evitar doble notificación.
21
+ - **Android RAW solo usaba GPS-o-Network (excluyente)** ignorando `desiredAccuracy`. Ahora `pickProviders` mapea: HIGH (<10 m) → GPS only, BALANCED → GPS+Network, LOW (≥1000 m) → Network only. Suscripción simultánea cuando ambos son útiles.
22
+ - **`AbstractLocationProvider` warning si `stopOnStillActivity: false` con ACTIVITY_PROVIDER**: el provider depende del state machine STILL/ACTIVE; con eso desactivado tracking continuo accidental. Warning en logcat.
23
+
24
+ ### Internal
25
+ - `AbstractLocationProvider` ahora expone `handlePermissionDenied(msg)` y `handleServiceError(msg)` para que los providers emitan `PluginException` sin tocar `mDelegate` directamente.
26
+
27
+ ### Fixed (post-revisión 4.5.2, cuarta iteración — hardening)
28
+ - **`BootCompletedReceiver` ahora valida el `intent.getAction()`**: el receiver está declarado `exported="true"` para recibir `BOOT_COMPLETED` del sistema; sin validar la action, una app maliciosa podría enviarle un intent explícito y disparar el auto-start del servicio. Ahora solo acepta `BOOT_COMPLETED`, `MY_PACKAGE_REPLACED`, y los quickboot OEM (HTC/Samsung); lo demás se ignora con un log warn.
29
+
30
+ ### Fixed (post-revisión 4.5.2, tercera iteración)
31
+ - **iOS `ACTIVITY_PROVIDER` no arrancaba GPS si la app abría con el usuario quieto**: `onStart` subscribía a `CMMotionActivityManager` pero NO llamaba `startTracking`. Si CoreMotion disparaba STILL antes del primer fix, `handleActivityUpdate` (cuya regla es "ACTIVE → start") nunca arrancaba el location manager, así que no llegaba ningún fix y el `onStationaryChanged` inicial nunca se emitía. Restaurado el patrón del SOMotionDetector legacy: `startTracking` se invoca inmediatamente al `onStart`. Si CoreMotion confirma STILL después, el primer fix dispara `onStationaryChanged` + `stopTracking` automáticamente — el consumo de batería sigue acotado.
32
+
33
+ ### Fixed (post-revisión 4.5.2, segunda iteración)
34
+ - **iOS `MAURActivityLocationProvider` UNKNOWN seguía mutando `lastMotionType`**: en la corrección anterior el `return` por UNKNOWN ocurría DESPUÉS de actualizar `lastMotionType`. Si la secuencia era STILL → UNKNOWN, el próximo fix ya no se trataba como stationary porque `lastMotionType` había pasado a `Unknown`. Ahora el `return` es lo primero — UNKNOWN no toca estado ni emite (silenciado además para no spamear consecutivos).
35
+ - **Android `singleUpdatePI` con `FLAG_IMMUTABLE` en API 31+**: `LocationManager.requestSingleUpdate(provider, PendingIntent)` rellena la `Location` en los extras del intent en delivery; con `FLAG_IMMUTABLE` el OS bloquea esa población y el receiver nunca veía el fix. Solo el `singleUpdatePI` se cambió a `FLAG_MUTABLE`; los demás siguen `IMMUTABLE` (alarms y monitor reciben PI sin payload).
36
+ - **`AbstractLocationProvider.hasMockLocationsEnabled` NPE**: `Settings.Secure.getString(...)` puede devolver `null` (clave ausente en el provider de Settings del dispositivo); el `.equals("1")` crasheaba. Invertido a `"1".equals(value)` — null-safe.
37
+ - **DISTANCE_FILTER legacy ahora suscribe GPS+Network simultáneo en modo moving normal**: antes elegía uno (GPS-o-Network excluyente). En Androids baratos/vehiculares donde GPS está "enabled" pero tarda en dar fix, esto perdía la oportunidad de un fix rápido por Network. Burst mode (acquisition) ya lo hacía; ahora moving también.
38
+
39
+ ### Fixed (post-revisión 4.5.2)
40
+ - **iOS `MAURActivityLocationProvider` trataba UNKNOWN como NotMoving**: el branch `activity.stationary || activity.unknown` colapsaba ambos casos a `MAURMotionTypeNotMoving`, lo que paraba GPS al recibir un fix de baja confianza ("unknown" puede llegar con confianza alta). Ahora UNKNOWN se procesa como un tipo aparte: emite `onActivityChanged` pero **no toca el estado de tracking** y **no actualiza `lastMotionType`** (así un STILL/ACTIVE posterior produce la transición correcta). Contradecía el propio comentario "UNKNOWN keeps previous behavior (don't pause on uncertainty)".
41
+ - **README desactualizado**: la tabla de `locationProvider` decía que `DISTANCE_FILTER_PROVIDER` era "Pure Android LocationManager" y "Works without Google Play Services". Actualizada a "hybrid (v4.5.2+)": usa FLP cuando hay Play Services, fallback a LocationManager si no.
42
+ - **README caveats v4.5.2 añadidos**:
43
+ - `maxAcceptedAccuracy` está apagado por defecto (`null`); recomendaciones por escenario.
44
+ - `ACTIVITY_PROVIDER` sin `ACTIVITY_RECOGNITION` degrada a tracking continuo (caveat).
45
+
46
+ ### Refactor — Provider internals (sin cambio de API JS, sin pérdida de cobertura)
47
+ - **iOS `ACTIVITY_PROVIDER` migrado a `CMMotionActivityManager` directo** (CoreMotion). Se eliminó la dependencia `SOMotionDetector` (sources + plugin.xml entries). Threading propio (NSOperationQueue) y manejo de permiso "Motion & Fitness" denegado (iOS 11+: `CMMotionActivityManager.authorizationStatus`) con emisión de error.
48
+ - **Android `DISTANCE_FILTER_PROVIDER` ahora es híbrido**: detecta Google Play Services en `onCreate` y elige backend en runtime.
49
+ - **Play Services disponible** → `FusedLocationProviderClient` + `LocationCallback` (mezcla GPS+Network, mejor batería, `setMinUpdateDistanceMeters`/Priority).
50
+ - **Play Services ausente** (Huawei/HMS, AOSP, ChinaROMs) → fallback a `android.location.LocationManager` + `LocationListener` (preservando el comportamiento previo a v4.5.2 — el plugin funciona en cualquier Android).
51
+ - **Eliminado el path de `addProximityAlert`** (decisión de producto: sin geozonas) en ambas rutas; la salida del estado stationary se detecta exclusivamente por polling (FLP `setMaxUpdates(1)` o `LocationManager.requestSingleUpdate`).
52
+ - `DISTANCE_FILTER_PROVIDER` emite `SERVICE_ERROR` cuando ni GPS ni Network están habilitados al `setPace`.
53
+
54
+ ### Matriz Play Services (Android)
55
+ | Provider | Play Services | Comportamiento |
56
+ |----------|---------------|----------------|
57
+ | `RAW_PROVIDER` | No requiere | OS LocationManager directo (GPS+Network simultáneo según `desiredAccuracy`). |
58
+ | `DISTANCE_FILTER_PROVIDER` | Opcional | FLP si está disponible, LocationManager si no. Auto. |
59
+ | `ACTIVITY_PROVIDER` | Requerido | Sin fallback (depende de `ActivityRecognitionClient`). En dispositivos sin Play Services usar `DISTANCE_FILTER` o `RAW`. |
60
+
61
+ ## [4.5.1](https://github.com/josuelmm/cordova-background-geolocation/tree/4.5.1) (2026-05-09)
62
+
63
+ ### Fixed (BLOQUEANTES)
64
+ - **Android no compilaba**: faltaba `import android.os.Build;` en `BackgroundGeolocationPlugin.java` después de los handlers v4.5.0 que usaban `Build.VERSION.SDK_INT`.
65
+ - **Android UPDATE en `maxLocations` mezclaba events/battery viejos**: cuando la cola SQLite alcanzaba `maxLocations`, el row más viejo se reciclaba con UPDATE pero NO actualizaba `events_json`, `battery_level`, `is_charging`. Resultado: un `possibleCrash`/`hardBrake`/batería vieja podía quedarse pegada a una location nueva. `SQLiteLocationDAO` extendido con esas 3 columnas (con `null` cuando la nueva no las tiene, para limpiar valores viejos).
66
+ - **iOS UPDATE en `persistLocation:limitRows:`**: mismo bug que Android. `MAURSQLiteLocationDAO.m` ahora setea `events_json`, `battery_level`, `is_charging` (con `[NSNull null]` cuando faltan).
67
+
68
+ ### Added — Optimización de batería
69
+
70
+ - **`wakeLockMode: 'none' | 'posting' | 'always'`** (default `'posting'`). Antes el servicio mantenía un `PARTIAL_WAKE_LOCK` permanente todo el tiempo de tracking → drenaba batería sin necesidad. Ahora:
71
+ - `'posting'` (default): solo 30 s al recibir cada fix (suficiente para SQLite + POST).
72
+ - `'none'`: nunca. Mejor batería; usa solo con `httpMode: 'batch'`.
73
+ - `'always'`: comportamiento legacy. Solo para fleet/emergency apps.
74
+ - **Watchdog moving-only**: `enableWatchdog` ya no reinicia el provider cuando estamos en estacionario. Antes despertaba el GPS cada 60 s aunque el plugin estuviera intencionalmente quieto. Ahora solo reinicia si `tripActive` (driver insights) está marcado.
75
+ - **Stationary params configurables**: `stationaryTimeout` (default 300_000 ms), `stationaryPollInterval` (default 180_000 ms), `stationaryPollFast` (default 60_000 ms). Ya no son constantes hard-coded en `DistanceFilterLocationProvider`.
76
+
77
+ ### Fixed (otros)
78
+ - `android/common/src/main/AndroidManifest.xml` interno: `<uses-permission android:name="android.hardware.location" />` → `<uses-feature ... required="false" />` (paridad con `plugin.xml` raíz).
79
+ - `README.md` y `www/BackgroundGeolocation.d.ts`: removido caveat obsoleto que decía "events no sobrevive sync queue" (sí sobrevive desde 4.5.0).
80
+ - `.npmignore`: limpieza para no publicar `CLAUDE.md`, tests internos, scripts, etc.
81
+
82
+ ### Fixed (post-auditoría 4 — flujos end-to-end + casos edge)
83
+ - **iOS `MAURLocationMapper._location` declarado a nivel de archivo**: race real entre real-time post + background sync (queues concurrentes). El segundo `+map:` pisaba la referencia del primer mapper → backend recibía fixes con campos mezclados. Migrado a ivar de instancia.
84
+ - **iOS pending events se perdían si `locationTransform` retornaba `nil`**: la facade drenaba `pendingDrivingEventsBuffer` al `location` ANTES de `[postLocationTask add:]`; cuando el transform descartaba el fix, esos eventos no llegaban nunca al backend. Movidos al `MAURPostLocationTask.add:` DESPUÉS del transform (vía property weak `pendingDrivingEventsBuffer` + block `attachBatterySnapshot`).
85
+ - **iOS re-attach con `==` sobrescribía events** del transform en lugar de hacer merge. Paridad con Android: ahora hace `addObjectsFromArray:` cuando la nueva instancia tiene su propio array de events.
86
+ - **Android `wakeLockMode` no hot-reload**: al cambiar `'always' → 'posting'/'none'` el lock permanente se mantenía hasta `stop()`; inverso tampoco lo adquiría. Añadida lógica en `configure()` que compara prev/new y llama `release()` / `acquire()` según corresponda.
87
+ - **`d.ts` `headlessTask`** no marcaba iOS como no soportado. Añadido `Platform: Android` + nota explicando que en iOS es no-op.
88
+ - **README**: bloque corrupto de líneas 638-712 (duplicado de Angular + License) eliminado. Tabla `Compatibility` extendida con `4.2.x – 4.5.x`.
89
+ - **docs/api.md**: tabla `configure` extendida con `drivingEvents`, `includeBattery`, `wakeLockMode`, `stationaryTimeout`, `stationaryPollInterval`, `stationaryPollFast`. Documentados los 3 helpers de permisos runtime.
90
+
91
+ ### Fixed (post-auditoría 3 — payload default y serialización)
92
+ - **Payload default no incluía `events` / `battery` / `isCharging`.** `Config.getTemplate()` siempre cae a `LocationTemplateFactory.getDefault()` cuando no hay `postTemplate` custom, y el default omitía los placeholders nuevos. README/CHANGELOG decían "el payload default los incluye" pero **el backend recibía sin esos campos**. Añadidos `@events`, `@battery`, `@isCharging` al default (Android `LocationTemplateFactory.getDefault`, iOS `MAURConfig.getDefaultTemplate`).
93
+ - **iOS default template tenía bug**: `@"provider": @"provider"` enviaba la string literal `"provider"` en lugar del valor real. Corregido a `@"@provider"`.
94
+ - **iOS mapper (`MAURLocation.mapValue`) devolvía el placeholder literal** (`"@events"`, `"@battery"`) cuando la location no tenía valor para esa clave. Ahora retorna `NSNull` para keys que empiezan con `@`; preserva el comportamiento legacy para strings estáticos (ej. `deviceId` literal en postTemplate).
95
+ - **Android `BatchManager.writeValue`** no manejaba `JSONArray` / `JSONObject` — cuando una location de la cola de sync salía con `events` poblado, el array se serializaba como string escapado `"[{\"type\":\"hardBrake\"}]"` en lugar de JSON real. Añadido manejo de tipos JSON + helper `resolveTemplateValue` que devuelve `JSONObject.NULL` para placeholders `@…` sin valor.
96
+ - **`onStationary` Android e iOS** no enriquecían el fix con events/battery — el backend recibía updates stationary sin esos campos aunque las features estuvieran habilitadas. Añadido `flushPendingDrivingEvents` + `attachBatterySnapshot` en ambos.
97
+ - `.npmignore`: añadido `/ios/common/scripts` para no publicar `xcode-refactor.js`.
98
+
99
+ ### Fixed (post-auditoría 2)
100
+ - **Android `toContentValues` / `SQLiteLocationDAO.getContentValues` ahora limpian con `putNull`** las columnas `events_json`, `battery_level`, `is_charging` cuando la nueva location no las trae. Sin esto, el flujo `ContentProviderLocationDAO.persistLocation(location, maxRows)` que recicla el row más viejo via UPDATE dejaba pegados los valores del row anterior (ej. un `possibleCrash` viejo aparecía adherido a una location nueva).
101
+ - **`registerReceiver` override**: guard por SDK. `RECEIVER_NOT_EXPORTED` y el 5-arg overload solo se usan en API ≥ 33 / ≥ 26. En APIs viejas se cae al 2-arg estándar.
102
+ - **`checkSelfPermission`** migrado a `ContextCompat.checkSelfPermission` en `BootCompletedReceiver`, `LocationServiceImpl`, `LocationServiceProxy`. Funciona seguro en API < 23 (permisos auto-granted al install).
103
+ - **iOS recovery de `SyncPending` stale**: nuevo `restoreStaleSyncLocationsOlderThan:` que se invoca al inicio de cada `MAURPostLocationTask.sync`. Si la app/proceso murió entre `getLocationsForSync` y el callback success/failure, las locations quedaban atascadas en `SyncPending` y nunca se reintentaban. Ahora se restauran a `PostPending` automáticamente si tienen más de 15 min.
104
+ - **iOS `locationTransform` que retorna nueva instancia**: ahora `MAURPostLocationTask.add` copia `drivingEvents` / `batteryLevel` / `isCharging` de la location original a la transformada si el transform no las propagó. Paridad con el fix Android.
105
+
106
+ ### Fixed (post-auditoría — BLOQUEANTE iOS sync)
107
+ - **iOS `getLocationsForSync` borraba TODA la tabla antes del upload.** Bug heredado: `UPDATE location SET valid = Deleted` sin `WHERE`. Si la red caía a mitad del POST, todas las ubicaciones se perdían silenciosamente. Ahora usa transición de estados:
108
+ - `getLocationsForSync` → `PostPending → SyncPending` (in-flight, no se re-incluye en otros sync windows).
109
+ - Success → `deleteSyncedLocationsBefore:cutoff` opera sobre `SyncPending` (no `PostPending` que estarían esperando POST real-time).
110
+ - Failure (network/HTTP) → nuevo `restoreFailedSyncLocations`: `SyncPending → PostPending` para reintento. Sin esto un solo fallo dropeaba todas.
111
+
112
+ ### Fixed (post-auditoría)
113
+ - **`LocationServiceImpl.onLocation`** — ahora `attachBatterySnapshot()` y `flushPendingDrivingEvents()` se ejecutan DESPUÉS de `transformLocation()`. Antes, si el usuario configuraba un `LocationTransform` que retornaba una nueva instancia, los `events` y la batería se perdían (se anexaban a la location original que el plugin descartaba). Adicionalmente, los eventos del detector que se anexaron a la location RAW se copian a la transformada via `addDrivingEvent`.
114
+ - **Watchdog smart**: la lógica `mDrivingTripActive` solo aplica cuando `drivingEvents.enabled == true`. Si el usuario tiene `enableWatchdog: true` SIN `drivingEvents`, el watchdog mantiene comportamiento legacy (reinicia el provider tras 60s sin fix). Sin esto, watchdog no se activaría nunca con drivingEvents desactivado.
115
+ - **`Config(Parcel)`** — `in.readBundle(Config.class.getClassLoader())` para que `LocationTemplate`/HashMaps subclase se deserialicen correctamente cuando el Parcel cruza el proceso `:sync`.
116
+ - **iOS race en `deletePendingSyncLocations` tras success** — nuevo método `deleteSyncedLocationsBefore:` que solo borra rows con `recorded_at <= cutoff`, donde `cutoff` se captura ANTES del upload. Las locations persistidas DURANTE el upload (window de race entre POST y delete) se preservan correctamente.
117
+
118
+ ## [4.5.0](https://github.com/josuelmm/cordova-background-geolocation/tree/4.5.0) (2026-05-09)
119
+
120
+ ### Added — Paridad de persistencia + helpers de permisos
121
+
122
+ #### Persistencia events / battery / charging en cola de sync
123
+ - **Android (DB v22)**: nuevas columnas `events_json TEXT`, `battery_level INTEGER`, `is_charging INTEGER` en tabla `location`. Migración v21→v22 automática. `BackgroundLocation.toContentValues` / `fromCursor` actualizados; campos ya NO transient — sobreviven Parcel y SQLite.
124
+ - **iOS (DB v6)**: mismas 3 columnas en `location`. `MAURSQLiteLocationDAO` `persistLocation` / `convertToLocation` actualizados.
125
+ - **Resultado**: si el POST en real-time falla y la location entra a la cola de sync, los `events`, `battery`, `isCharging` ahora viajan con ella cuando se sincroniza después. Antes se perdían.
126
+
127
+ #### Persistencia config completa iOS (paridad con Android)
128
+ - **DB v7** añade columna `config_json TEXT` en `configuration`. `MAURSQLiteConfigurationDAO.persistConfiguration` ahora serializa todas las keys post-3.2.0 (`httpMethod`, `syncHttpMethod`, `httpMode`, `syncMode`, `queryParams`, `heartbeatInterval`, `mockLocationPolicy`, `drivingEvents`, `includeBattery`) a JSON. `retrieveConfiguration` rehidrata desde el blob.
129
+
130
+ #### Helpers de permisos runtime (Android)
131
+ Tres nuevos métodos JS para que la app pueda controlar el flujo de permisos modernos:
132
+ - `requestBackgroundLocationPermission()` — pide `ACCESS_BACKGROUND_LOCATION` (Android 10+).
133
+ - `requestActivityRecognitionPermission()` — pide `ACTIVITY_RECOGNITION` (Android 10+).
134
+ - `requestNotificationPermission()` — pide `POST_NOTIFICATIONS` (Android 13+).
135
+
136
+ Resuelven con `{ granted: boolean, denied?: string[], notRequired?: boolean }`.
137
+ En iOS y Android < versión mínima resuelven inmediatamente con `notRequired: true`.
138
+
139
+ #### iOS sync cleanup tras success (bug fix)
140
+ - `MAURBackgroundSync` ahora llama `deletePendingSyncLocations` tras un POST batch exitoso (2xx). Antes la cola SQLite no se vaciaba, provocando re-uploads de los mismos rows en cada ciclo.
141
+
142
+ ### Migraciones
143
+ - Android DB v21 → v22 (location columns).
144
+ - iOS DB v5 → v7 (location + configuration columns). v5→v6 añade location columns; v6→v7 añade config_json.
145
+
146
+ ## [4.4.1](https://github.com/josuelmm/cordova-background-geolocation/tree/4.4.1) (2026-05-09)
147
+
148
+ ### Fixed — stability patch
149
+
150
+ #### Crítico
151
+ - **Android: persistencia de configuración**. Tras un reboot + `startOnBoot`, las opciones añadidas desde 3.3.0 (`httpMethod`, `syncHttpMethod`, `httpMode`, `syncMode`, `queryParams`, `heartbeatInterval`, `mockLocationPolicy`, `drivingEvents`, `includeBattery`) volvían a default porque `SQLiteConfigurationDAO` solo persistía las columnas de 3.2.0. Solución: nueva columna `config_json TEXT` (DB v20→v21) que serializa todo el `Config`. Las columnas viejas se mantienen pobladas para retrocompat.
152
+ - **Android: `Config.merge()` ignoraba `includeBattery`**. `configure({includeBattery: false})` se descartaba en el merge interno y el plugin seguía estampando batería en cada location. Arreglado.
153
+
154
+ #### Real
155
+ - **Android: `attachBatterySnapshot` rompía con el override interno de `registerReceiver`**. El servicio sobrescribe `registerReceiver` para forzar `RECEIVER_NOT_EXPORTED` + handler — incompatible con la lectura sticky-only que necesita batería. Ahora la batería se lee con `getApplicationContext().registerReceiver(null, filter)` para bypassear el override.
156
+ - **iOS `MAURPostLocationTask.m:258`**: `*outError == nil` sin guard de `outError == NULL`. Crash defensivo si futuro caller pasa NULL. Añadido `if (outError == NULL || *outError == nil)`.
157
+ - **`pendingDrivingEvents` (Android+iOS)**: ahora capped a 20 entradas (oldest evicted) y al drenar descarta las que tengan `time` > 60s para no anexar eventos cuyo contexto ya no es relevante.
158
+
159
+ #### Menor
160
+ - `plugin.xml`: `<uses-permission android:name="android.hardware.location" />` → `<uses-feature android:name="android.hardware.location" android:required="false" />`. `android.hardware.location` es feature, no permiso.
161
+ - `www/BackgroundGeolocation.js`: removido comentario huérfano "1. new method isLocationEnabled" (no existe método).
162
+
163
+ #### Diseño
164
+ - Para evitar dependencia circular `common → cordova`, la serialización JSON del Config vive en una nueva clase `com.marianhello.bgloc.data.ConfigJsonMapper` (en `common/`). Tanto el SQLite DAO (common) como el `ConfigMapper` Cordova (cordova) pueden reusarla. **Registrada en `plugin.xml` como `<source-file>`** para que llegue al APK.
165
+ - `template` (postTemplate) se mantiene en su columna SQLite dedicada porque tiene serialización propia (`LocationTemplateFactory`). El DAO la rehidrata después de leer el JSON para no perderla.
166
+ - Strings con `Config.NullString` sentinel (cuando el usuario hace `configure({notificationTitle: null})`) sobreviven el round-trip via JSONObject.NULL.
167
+
168
+ #### Decisiones conscientes (no cambiadas en este patch)
169
+ - Eventos/batería NO sobreviven a la cola de sync. Documentado en 4.3.0/4.4.0. Cambiar el schema de `locations` rompería migraciones de apps existentes.
170
+ - Permisos runtime modernos (`ACCESS_BACKGROUND_LOCATION`, `ACTIVITY_RECOGNITION`) NO se piden automáticamente por el plugin — convención: la app controla el flujo. Posibles helpers `requestBackgroundLocationPermission()` / `requestActivityRecognitionPermission()` quedan para una v4.5.
171
+ - **iOS `MAURSQLiteConfigurationDAO`** tiene la misma limitación que tenía Android antes: solo persiste columnas heredadas de 3.2.0. Impacto práctico bajo en iOS porque Apple no permite auto-start al boot — la app siempre llama `configure()` al arrancar. Si más adelante se quiere paridad, se replicará el approach `config_json` de Android.
172
+
173
+ ## [4.4.0](https://github.com/josuelmm/cordova-background-geolocation/tree/4.4.0) (2026-05-09)
174
+
175
+ ### Added — Battery snapshot in every location payload
176
+
177
+ Cada `location` enviada al backend incluye automáticamente:
178
+ - `battery: number` (0-100, porcentaje)
179
+ - `isCharging: boolean`
180
+
181
+ Ejemplo:
182
+
183
+ ```json
184
+ {
185
+ "latitude": 40.4168,
186
+ "longitude": -3.7038,
187
+ "time": 1730000000000,
188
+ "speed": 8.2,
189
+ "battery": 78,
190
+ "isCharging": false
191
+ }
192
+ ```
193
+
194
+ #### Configuración
195
+ - Default: **ON**.
196
+ - Opt-out: `configure({ includeBattery: false })`.
197
+
198
+ #### Templates custom
199
+ Si usas `postTemplate` / `bodyTemplate`, añade los placeholders `'@battery'` / `'@isCharging'`:
200
+
201
+ ```js
202
+ bodyTemplate: {
203
+ deviceId: 'ABC',
204
+ lat: '@latitude',
205
+ lon: '@longitude',
206
+ bat: '@battery',
207
+ charging: '@isCharging'
208
+ }
209
+ ```
210
+
211
+ #### Detalles técnicos
212
+ - Android: lectura instantánea via `Intent.ACTION_BATTERY_CHANGED` sticky broadcast (sin permisos extra). Stamp en `LocationServiceImpl.onLocation`.
213
+ - iOS: lectura via `UIDevice.batteryLevel` y `UIDevice.batteryState` (activa `batteryMonitoringEnabled` automáticamente). Stamp en `MAURBackgroundGeolocationFacade.onLocationChanged`.
214
+ - Reemplaza la dependencia de `cordova-plugin-battery-status` para apps que solo querían enviar la batería al backend con cada fix.
215
+
216
+ ## [4.3.0](https://github.com/josuelmm/cordova-background-geolocation/tree/4.3.0) (2026-05-09)
217
+
218
+ ### Added — Driving events anexados al payload de location
219
+
220
+ Cuando un driving event se dispara, ahora se anexa al location del momento como atributo `events: [...]` y viaja al backend en el mismo POST. La emisión por JS (`on('hardBrake', ...)`) sigue funcionando exactamente igual — esto solo añade el evento al payload.
221
+
222
+ ```json
223
+ {
224
+ "latitude": 40.4168,
225
+ "longitude": -3.7038,
226
+ "time": 1730000000000,
227
+ "speed": 8.2,
228
+ "events": [
229
+ { "type": "hardBrake", "value": -4.1, "time": 1730000000000 }
230
+ ]
231
+ }
232
+ ```
233
+
234
+ Tipos posibles: `moving`, `stopped`, `tripStart`, `tripEnd`, `speeding`, `providerChange`, `hardBrake`, `rapidAcceleration`, `sharpTurn`, `possibleCrash`, `phoneUsageWhileDriving`. Cada uno con su payload adicional (`value`, `distance`, `source`, etc.).
235
+
236
+ #### Detalles técnicos
237
+ - Android: nuevo campo `transient JSONArray drivingEvents` en `BackgroundLocation`. Helpers `addDrivingEvent`/`getDrivingEvents`/`clearDrivingEvents`. `toJSONObject()` lo incluye si está populado.
238
+ - iOS: nueva property `NSMutableArray *drivingEvents` en `MAURLocation`. `toDictionary` la incluye. `copyWithZone:` la copia.
239
+ - Buffer de "pending events" para eventos que disparan sin un fix simultáneo (sensor crash, phone usage, providerChange) — se drenan al próximo `onLocation`.
240
+ - Eventos GPS-derived (hardBrake, sharpTurn, etc.) se anexan en el mismo callback del detector, antes del broadcast a JS.
241
+
242
+ #### Caveat
243
+ Si el POST en real-time falla y la location entra a la cola de sync (SQLite/Core Data), el array `events` NO sobrevive — los eventos siguen emitiéndose por JS para que la app pueda postearlos por separado. La cola de sync solo guarda el formato GPS estándar (no se cambió el schema para preservar migraciones existentes).
244
+
245
+ Si usas `postTemplate`/`bodyTemplate` custom, los `events` no se incluyen automáticamente — el template solo serializa los keys que declara. Para incluirlos, añade `events: '@events'` (o el nombre que prefieras) a tu template:
246
+
247
+ ```js
248
+ postTemplate: {
249
+ lat: '@latitude',
250
+ lon: '@longitude',
251
+ t: '@time',
252
+ events: '@events' // ← v4.3
253
+ }
254
+ ```
255
+
256
+ ## [4.2.4](https://github.com/josuelmm/cordova-background-geolocation/tree/4.2.4) (2026-05-09)
257
+
258
+ ### Fixed (CRÍTICO)
259
+ - **Foreground service no arrancaba en Android 14+** cuando la reflexión sobre `ServiceInfo.foregroundServiceType` fallaba o el manifest merged no incluía el atributo. Síntomas: sin notificación, sin tracking en background, sin envío de ubicaciones al minimizar/cerrar la app.
260
+ - `LocationServiceImpl.startForeground()`: si `getManifestForegroundServiceType()` devuelve `0`, ahora hace fallback a `0x00000008` (`FOREGROUND_SERVICE_TYPE_LOCATION`) con un `logger.warn` visible — antes retornaba silenciosamente y dejaba el servicio en background sin promover.
261
+ - `try/catch` defensivo alrededor de `super.startForeground(...)` con retry sin tipo si la primera llamada falla.
262
+ - El tipo se aplica desde API 30+ (antes solo desde 34); Android 12-13 (API 31-33) lo aceptan opcionalmente y mejora el comportamiento bajo Doze/restricciones.
263
+
3
264
  ## [4.2.3](https://github.com/josuelmm/cordova-background-geolocation/tree/4.2.3) (2026-05-09)
4
265
 
5
266
  ### Fixed