Compare commits

...

4 commits

8 changed files with 110 additions and 16 deletions

View file

@ -2,6 +2,26 @@
All notable changes to this project will be documented in this file. See [commit-and-tag-version](https://github.com/absolute-version/commit-and-tag-version) for commit guidelines. All notable changes to this project will be documented in this file. See [commit-and-tag-version](https://github.com/absolute-version/commit-and-tag-version) for commit guidelines.
## [1.16.8](https://github.com/alerte-secours/as-app/compare/v1.16.7...v1.16.8) (2026-02-05)
### Bug Fixes
* **up:** react-native-background-geolocation (+fix mountain android) ([a1d4f0e](https://github.com/alerte-secours/as-app/commit/a1d4f0e0596f1e00bae608ba191ef700d4c4bd04))
## [1.16.7](https://github.com/alerte-secours/as-app/compare/v1.16.6...v1.16.7) (2026-01-27)
### Bug Fixes
* **ios:** deps after up rnbl ([1980822](https://github.com/alerte-secours/as-app/commit/1980822919deda76bc6e186ab3e7addd7ed32618))
* **track-location:** try 5 ([69753bc](https://github.com/alerte-secours/as-app/commit/69753bc7e18ebd27715d2c0f6472c3c9ea14c0e5))
* **track-location:** try 6 ([b61aff7](https://github.com/alerte-secours/as-app/commit/b61aff70787e82e05500cbf9c3fda1d7882a040b))
* **track-location:** try 7 (+ up rnbl to v5) ([f4f7708](https://github.com/alerte-secours/as-app/commit/f4f7708e71227301b9cd5624669da8a092a3a379))
* **track-location:** try 8 ([33eb0cf](https://github.com/alerte-secours/as-app/commit/33eb0cfa13eb75308fc549f4289c6eaa15a3ac5c))
* **track-location:** try 8 ([7ba78c7](https://github.com/alerte-secours/as-app/commit/7ba78c7334462d6de6e647d236d39c415fa9cb2b))
* **track-location:** try 9 ([a18baf9](https://github.com/alerte-secours/as-app/commit/a18baf9ae6bf955f4be609d014c9f5cab8dc6268))
## [1.16.6](https://github.com/alerte-secours/as-app/compare/v1.16.5...v1.16.6) (2026-01-22) ## [1.16.6](https://github.com/alerte-secours/as-app/compare/v1.16.5...v1.16.6) (2026-01-22)

View file

@ -83,8 +83,8 @@ android {
applicationId 'com.alertesecours' applicationId 'com.alertesecours'
minSdkVersion rootProject.ext.minSdkVersion minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 237 versionCode 239
versionName "1.16.6" versionName "1.16.8"
multiDexEnabled true multiDexEnabled true
testBuildType System.getProperty('testBuildType', 'debug') testBuildType System.getProperty('testBuildType', 'debug')
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner' testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'

View file

@ -161,3 +161,21 @@ Applies to the BackgroundGeolocation integration:
- `onLocation` events - `onLocation` events
- `onHttp` events - `onHttp` events
- pending queue (`BackgroundGeolocation.getCount()` in logs) - pending queue (`BackgroundGeolocation.getCount()` in logs)
## Android-specific note (stationary-geofence EXIT loop)
Some Android devices can repeatedly trigger the SDK's internal **stationary geofence EXIT** using a very poor "trigger" fix (eg `hAcc=500m`).
This can cause false motion transitions and periodic persisted uploads even when the phone is not moving.
Mitigation applied:
- Android IDLE disables `geolocation.stopOnStationary` (we do **not** rely on stationary-geofence mode in IDLE on Android).
- See [`BASE_GEOLOCATION_CONFIG.geolocation.stopOnStationary`](src/location/backgroundGeolocationConfig.js:1) and [`TRACKING_PROFILES.idle.geolocation.stopOnStationary`](src/location/backgroundGeolocationConfig.js:1).
- Android IDLE uses `geolocation.useSignificantChangesOnly: true` to rely on OS-level significant movement events.
- See [`TRACKING_PROFILES.idle.geolocation.useSignificantChangesOnly`](src/location/backgroundGeolocationConfig.js:1).
Diagnostics:
- `onGeofence` events are logged (identifier/action/accuracy + current BGGeo state) to confirm whether the SDK is emitting stationary geofence events.
- See [`setBackgroundGeolocationEventHandlers({ onGeofence })`](src/location/trackLocation.js:1).

View file

@ -25,7 +25,7 @@
<key>CFBundlePackageType</key> <key>CFBundlePackageType</key>
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string> <string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>
<string>1.16.6</string> <string>1.16.8</string>
<key>CFBundleSignature</key> <key>CFBundleSignature</key>
<string>????</string> <string>????</string>
<key>CFBundleURLTypes</key> <key>CFBundleURLTypes</key>
@ -48,7 +48,7 @@
</dict> </dict>
</array> </array>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>237</string> <string>239</string>
<key>ITSAppUsesNonExemptEncryption</key> <key>ITSAppUsesNonExemptEncryption</key>
<false/> <false/>
<key>LSApplicationQueriesSchemes</key> <key>LSApplicationQueriesSchemes</key>

View file

@ -1,6 +1,6 @@
{ {
"name": "alerte-secours", "name": "alerte-secours",
"version": "1.16.6", "version": "1.16.8",
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {
"start": "expo start --dev-client --private-key-path ./keys/private-key.pem", "start": "expo start --dev-client --private-key-path ./keys/private-key.pem",
@ -9,6 +9,7 @@
"android": "scripts/android-run.sh", "android": "scripts/android-run.sh",
"android:staging": "dotenv --override -e .env.staging -- bash -lc 'EMU=$(adb devices | awk \"/^emulator-/{print $1}\" | head -n1); USB=$(adb devices -l | awk \"/ device usb:/{print $1}\" | head -n1); if [ -n \"$EMU\" ] && [ -z \"$USB\" ]; then EXPO_ANDROID_GRADLE_ARGS=\"-PreactNativeArchitectures=x86_64\" exec expo run:android --variant emulatorX86_64Debug; elif [ -n \"$USB\" ] && [ -z \"$EMU\" ]; then EXPO_ANDROID_GRADLE_ARGS=\"-PreactNativeArchitectures=arm64-v8a\" exec expo run:android --variant deviceArm64Debug; else echo \"Connect exactly one target (emulator or USB). Or run: EXPO_ANDROID_GRADLE_ARGS=\\\"-PreactNativeArchitectures=x86_64\\\" expo run:android --variant emulatorX86_64Debug | EXPO_ANDROID_GRADLE_ARGS=\\\"-PreactNativeArchitectures=arm64-v8a\\\" expo run:android --variant deviceArm64Debug\"; exit 1; fi'", "android:staging": "dotenv --override -e .env.staging -- bash -lc 'EMU=$(adb devices | awk \"/^emulator-/{print $1}\" | head -n1); USB=$(adb devices -l | awk \"/ device usb:/{print $1}\" | head -n1); if [ -n \"$EMU\" ] && [ -z \"$USB\" ]; then EXPO_ANDROID_GRADLE_ARGS=\"-PreactNativeArchitectures=x86_64\" exec expo run:android --variant emulatorX86_64Debug; elif [ -n \"$USB\" ] && [ -z \"$EMU\" ]; then EXPO_ANDROID_GRADLE_ARGS=\"-PreactNativeArchitectures=arm64-v8a\" exec expo run:android --variant deviceArm64Debug; else echo \"Connect exactly one target (emulator or USB). Or run: EXPO_ANDROID_GRADLE_ARGS=\\\"-PreactNativeArchitectures=x86_64\\\" expo run:android --variant emulatorX86_64Debug | EXPO_ANDROID_GRADLE_ARGS=\\\"-PreactNativeArchitectures=arm64-v8a\\\" expo run:android --variant deviceArm64Debug\"; exit 1; fi'",
"android:prod": "dotenv --override -e .env.prod -- expo run:android", "android:prod": "dotenv --override -e .env.prod -- expo run:android",
"android:refresh-deps": "cd android && ./gradlew --refresh-dependencies",
"bundle:android": "dotenv --override -e .env.prod -- bundle-android", "bundle:android": "dotenv --override -e .env.prod -- bundle-android",
"bundle:android:fastdev": "SENTRY_ALLOW_FAILURE=true SENTRY_AUTH_TOKEN='' yarn bundle:android", "bundle:android:fastdev": "SENTRY_ALLOW_FAILURE=true SENTRY_AUTH_TOKEN='' yarn bundle:android",
"bundle:android:upload:internal": "./scripts/upload-android-internal.sh", "bundle:android:upload:internal": "./scripts/upload-android-internal.sh",
@ -53,8 +54,8 @@
"screenshot:android": "scripts/screenshot-android.sh" "screenshot:android": "scripts/screenshot-android.sh"
}, },
"customExpoVersioning": { "customExpoVersioning": {
"versionCode": 237, "versionCode": 239,
"buildNumber": 237 "buildNumber": 239
}, },
"commit-and-tag-version": { "commit-and-tag-version": {
"scripts": { "scripts": {
@ -177,7 +178,7 @@
"react-native": "0.79.6", "react-native": "0.79.6",
"react-native-animatable": "^1.3.3", "react-native-animatable": "^1.3.3",
"react-native-app-link": "^1.0.1", "react-native-app-link": "^1.0.1",
"react-native-background-geolocation": "5.0.1", "react-native-background-geolocation": "5.0.3",
"react-native-battery-optimization-check": "^1.0.8", "react-native-battery-optimization-check": "^1.0.8",
"react-native-contact-pick": "^0.1.2", "react-native-contact-pick": "^0.1.2",
"react-native-country-picker-modal": "^2.0.0", "react-native-country-picker-modal": "^2.0.0",
@ -281,4 +282,4 @@
} }
}, },
"packageManager": "yarn@4.5.3" "packageManager": "yarn@4.5.3"
} }

View file

@ -1,7 +1,9 @@
import { Platform } from "react-native";
import BackgroundGeolocation from "react-native-background-geolocation"; import BackgroundGeolocation from "react-native-background-geolocation";
import env from "~/env"; import env from "~/env";
const LOCATION_ACCURACY_GATE_M = 100; const LOCATION_ACCURACY_GATE_M = 100;
const IS_ANDROID = Platform.OS === "android";
// Native filter to reduce GPS drift and suppress stationary jitter. // Native filter to reduce GPS drift and suppress stationary jitter.
// This is the primary mechanism to prevent unwanted persisted/uploaded points while the device // This is the primary mechanism to prevent unwanted persisted/uploaded points while the device
@ -62,6 +64,12 @@ export const BASE_GEOLOCATION_CONFIG = {
// Default to the IDLE profile behaviour. // Default to the IDLE profile behaviour.
distanceFilter: 200, distanceFilter: 200,
// Ensure deterministic behavior when switching profiles.
// If we enable significant-changes mode in Android IDLE, we *must* explicitly
// restore it to `false` in ACTIVE (and other modes) to avoid the SDK keeping a
// previously-set `true` value.
useSignificantChangesOnly: false,
// Prevent dynamic distanceFilter shrink. // Prevent dynamic distanceFilter shrink.
// Elasticity can lower the effective threshold (eg while stationary), resulting in // Elasticity can lower the effective threshold (eg while stationary), resulting in
// unexpected frequent updates. // unexpected frequent updates.
@ -73,7 +81,14 @@ export const BASE_GEOLOCATION_CONFIG = {
// True-stationary strategy: once stop-detection decides we're stationary, stop active // True-stationary strategy: once stop-detection decides we're stationary, stop active
// tracking and rely on the stationary geofence to detect significant movement. // tracking and rely on the stationary geofence to detect significant movement.
// This is intended to eliminate periodic stationary updates on Android. // This is intended to eliminate periodic stationary updates on Android.
stopOnStationary: true, //
// HOWEVER: On some Android devices, the stationary-geofence EXIT can be triggered by
// extremely poor "trigger" fixes (eg hAcc=500m), causing false motion transitions and
// periodic persisted uploads while the phone is actually stationary.
//
// Mitigation (Android IDLE): do NOT rely on stationary-geofence mode; instead rely on
// distanceFilter + native filtering.
stopOnStationary: IS_ANDROID ? false : true,
stationaryRadius: 200, stationaryRadius: 200,
// Prevent identical/noise locations from being persisted. // Prevent identical/noise locations from being persisted.
@ -178,7 +193,14 @@ export const TRACKING_PROFILES = {
// IMPORTANT: ACTIVE sets stopOnStationary:false. // IMPORTANT: ACTIVE sets stopOnStationary:false.
// Ensure we restore it when transitioning back to IDLE, otherwise the SDK may // Ensure we restore it when transitioning back to IDLE, otherwise the SDK may
// continue recording while stationary. // continue recording while stationary.
stopOnStationary: true, //
// Android mitigation: disable stopOnStationary to avoid stationary-geofence EXIT loops.
stopOnStationary: IS_ANDROID ? false : true,
// Android IDLE: rely on OS-level significant movement only.
// This avoids periodic wakeups/records due to poor fused-location fixes while the phone
// is stationary (screen-off / locked scenarios).
useSignificantChangesOnly: IS_ANDROID,
// QA helper: allow easier validation in dev/staging while keeping production at 200m. // QA helper: allow easier validation in dev/staging while keeping production at 200m.
stationaryRadius: 200, stationaryRadius: 200,
@ -198,6 +220,9 @@ export const TRACKING_PROFILES = {
// ACTIVE target: frequent updates while moving. // ACTIVE target: frequent updates while moving.
distanceFilter: 25, distanceFilter: 25,
// ACTIVE must not use significant-changes-only (we want continuous distance-based updates).
useSignificantChangesOnly: false,
// While ACTIVE, do not stop updates simply because the device appears stationary. // While ACTIVE, do not stop updates simply because the device appears stationary.
// Motion-detection + distanceFilter should govern updates. // Motion-detection + distanceFilter should govern updates.
stopOnStationary: false, stopOnStationary: false,

View file

@ -1130,6 +1130,36 @@ export default function trackLocation() {
} }
} }
}, },
onGeofence: async (event) => {
// Diagnostic only: geofences are still used internally by the SDK (eg stationary geofence)
// even when we don't manage any app-defined geofences.
try {
const state = await BackgroundGeolocation.getState();
locationLogger.info("Geofence event", {
identifier: event?.identifier,
action: event?.action,
accuracy: event?.location?.coords?.accuracy,
latitude: event?.location?.coords?.latitude,
longitude: event?.location?.coords?.longitude,
enabled: state?.enabled,
isMoving: state?.isMoving,
trackingMode: state?.trackingMode,
profile: currentProfile,
appState,
});
} catch (e) {
locationLogger.info("Geofence event", {
identifier: event?.identifier,
action: event?.action,
accuracy: event?.location?.coords?.accuracy,
latitude: event?.location?.coords?.latitude,
longitude: event?.location?.coords?.longitude,
profile: currentProfile,
appState,
error: e?.message,
});
}
},
onLocationError: (error) => { onLocationError: (error) => {
locationLogger.warn("Location error", { locationLogger.warn("Location error", {
error: error?.message, error: error?.message,

View file

@ -7140,7 +7140,7 @@ __metadata:
react-native: "npm:0.79.6" react-native: "npm:0.79.6"
react-native-animatable: "npm:^1.3.3" react-native-animatable: "npm:^1.3.3"
react-native-app-link: "npm:^1.0.1" react-native-app-link: "npm:^1.0.1"
react-native-background-geolocation: "npm:5.0.1" react-native-background-geolocation: "npm:5.0.3"
react-native-battery-optimization-check: "npm:^1.0.8" react-native-battery-optimization-check: "npm:^1.0.8"
react-native-clean-project: "npm:^4.0.3" react-native-clean-project: "npm:^4.0.3"
react-native-contact-pick: "npm:^0.1.2" react-native-contact-pick: "npm:^0.1.2"
@ -16719,14 +16719,14 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"react-native-background-geolocation@npm:5.0.1": "react-native-background-geolocation@npm:5.0.3":
version: 5.0.1 version: 5.0.3
resolution: "react-native-background-geolocation@npm:5.0.1" resolution: "react-native-background-geolocation@npm:5.0.3"
dependencies: dependencies:
"@babel/runtime": "npm:^7.26.0" "@babel/runtime": "npm:^7.26.0"
"@transistorsoft/background-geolocation-types": "npm:^5.0.1" "@transistorsoft/background-geolocation-types": "npm:^5.0.1"
tslib: "npm:^2.6.3" tslib: "npm:^2.6.3"
checksum: 10/7d994a2403228669140ffed55019041d0b2aab08b9de3dd4cbbfb26ea6378488a3c4d5fc0779f6138ddacb796df21363cafab996c0d83a86cd213975c1abc56c checksum: 10/8811c0daf6a5fc760ac4192d5ae283bb2983477c09731132bacfa431be4f849cc6eda52bc83a3705f88cd6459abed0930e0e76511e1294fcdc1053d74bfb023c
languageName: node languageName: node
linkType: hard linkType: hard