fix(track-location): try 7 (+ up rnbl to v5)
This commit is contained in:
parent
b61aff7078
commit
f4f7708e71
12 changed files with 293 additions and 188 deletions
|
|
@ -16,5 +16,5 @@ ASC_API_KEY_PATH=
|
|||
PROVIDER_ID=
|
||||
|
||||
# Background Geolocation License Keys
|
||||
BACKGROUND_GEOLOCATION_LICENSE=your_license_key_here
|
||||
BACKGROUND_GEOLOCATION_HMS_LICENSE=your_hms_license_key_here
|
||||
BACKGROUND_GEOLOCATION_LICENSE_ANDROID=your_license_key_here
|
||||
BACKGROUND_GEOLOCATION_LICENSE_IOS=your_license_key_here
|
||||
|
|
@ -37,8 +37,7 @@
|
|||
<application android:name=".MainApplication" android:label="@string/app_name" android:icon="@mipmap/ic_launcher" android:allowBackup="true" android:theme="@style/AppTheme" android:supportsRtl="true" android:networkSecurityConfig="@xml/network_security_config" android:fullBackupContent="@xml/secure_store_backup_rules" android:dataExtractionRules="@xml/secure_store_data_extraction_rules">
|
||||
<meta-data android:name="com.google.firebase.messaging.default_notification_color" android:resource="@color/notification_icon_color" tools:replace="android:resource"/>
|
||||
<meta-data android:name="com.google.firebase.messaging.default_notification_icon" android:resource="@drawable/notification_icon"/>
|
||||
<meta-data android:name="com.transistorsoft.locationmanager.hms.license" android:value="ba479a3c61fbe471c826a39d1ee8b1a088df6d2249fad51f6ab5f24346f6bf87"/>
|
||||
<meta-data android:name="com.transistorsoft.locationmanager.license" android:value="90f34e070bebc8e433a9b604e1728dfa17a62ab484602e3cb7ef7ece784a23eb"/>
|
||||
<meta-data android:name="com.transistorsoft.locationmanager.license" android:value="eyJhbGciOiJFZERTQSIsImtpZCI6ImVkMjU1MTktbWFpbi12MSJ9.eyJvcyI6ImFuZHJvaWQiLCJhcHBfaWQiOiJjb20uYWxlcnRlc2Vjb3VycyIsIm9yZGVyX251bWJlciI6ODY0MCwicmVuZXdhbF91cmwiOiJodHRwczovL3Nob3AudHJhbnNpc3RvcnNvZnQuY29tL2NhcnQvMTY1MDc4NjE1MDU6MT9ub3RlPTYxOTMiLCJjdXN0b21lcl9pZCI6NTc0MiwicHJvZHVjdCI6InJlYWN0LW5hdGl2ZS1iYWNrZ3JvdW5kLWdlb2xvY2F0aW9uIiwia2V5X3ZlcnNpb24iOjEsImFsbG93ZWRfc3VmZml4ZXMiOlsiLmRldiIsIi5kZXZlbG9wbWVudCIsIi5zdGFnaW5nIiwiLnN0YWdlIiwiLnFhIiwiLnVhdCIsIi50ZXN0IiwiLmRlYnVnIl0sIm1heF9idWlsZF9zdGFtcCI6MjAyNzAyMjIsImdyYWNlX2J1aWxkcyI6MCwiZW50aXRsZW1lbnRzIjpbImNvcmUiXSwiaWF0IjoxNzY5MTI2Mjg5fQ.sCJRdXJ78r2B0BLfht-AnYXSoF5pcTxAfEkCcX6BN7FiYc8GT9RUMlqDdwacD6UlI0v6WLhQck6lPkRgCq4MDQ"/>
|
||||
<meta-data android:name="expo.modules.notifications.default_notification_color" android:resource="@color/notification_icon_color"/>
|
||||
<meta-data android:name="expo.modules.notifications.default_notification_icon" android:resource="@drawable/notification_icon"/>
|
||||
<meta-data android:name="expo.modules.updates.CODE_SIGNING_CERTIFICATE" android:value="-----BEGIN CERTIFICATE-----
MIICzTCCAbWgAwIBAgIJR0KfFDoMJrYZMA0GCSqGSIb3DQEBCwUAMA8xDTALBgNV
BAMTBHRlc3QwIBcNMjQwODE4MTU0OTExWhgPMjEyNDA4MTgxNTQ5MTFaMA8xDTAL
BgNVBAMTBHRlc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCk46qR
a0Do2fpBDtif18a/WQNWHm/xseHsh97bZdt8ooV4PQooK6VZUbADUhhJXqqomapa
yFMJX7sZzfBUF7/xMrWDrgS0R4FLbXijAolhpXoqMkBCx3toKUCbU4ljA+Lz/BX1
AEqVWqAweNzNDi4bvd1PG1/sQuuEtoZuSVfTPRAjF8vVkWyn8nfkorTtMYaw2QFu
ugs1wp7YieD4C8CIK5gMX7f8bxx3l7BR50bf+9MHJFI+eTjmoFoJFEVWbCcrOrky
FLM0+NrMI2fZYunrN6jcKc/NKEaDKb1VDO9yrLcFQOtXJJIXz94/lS6kHDjzEgUV
zx3uaDbAdSQsyZxxAgMBAAGjKjAoMA4GA1UdDwEB/wQEAwIHgDAWBgNVHSUBAf8E
DDAKBggrBgEFBQcDAzANBgkqhkiG9w0BAQsFAAOCAQEASIjMvS3N9NEPeakUmYXg
MEyjaX+N/62Pbcp4taU4G9vDB/fyDqMMef8+CWBpo/noXqzt4K6k1id7UwdZhRks
xdBTSf1x5yzDB24mbqNAvPa2q8G6KIoNZuvLUDz35366FxR+vTHQmp2d4Yz92kIL
EEmFr8eMHf60tfHG+em97p+evmXDyBjF3CwOvtuzog1wCF/AsJ1d0gbPPMKdAHKC
LZHsiXJ5i/oFuYzWkDDJkO9bb6HaQplt/46iC0CyM6SsT6H8kkDVQbfQCH1JAXsL
Knk10FbAMKJ7GWbAdsdcbNZlDMrzPprw8N/fpGc7RHdHBwKcFm44mNtrMrzEd4eX
pQ==
-----END CERTIFICATE-----
"/>
|
||||
|
|
|
|||
|
|
@ -30,14 +30,7 @@ apply plugin: "com.facebook.react.rootproject"
|
|||
|
||||
allprojects {
|
||||
repositories {
|
||||
// @generated begin react-native-background-fetch-maven - expo prebuild (DO NOT MODIFY) sync-b86324ce2eb77b03cc8b69ba206ef8275cd006ff
|
||||
maven { url "${project(":react-native-background-fetch").projectDir}/libs" }
|
||||
// @generated begin react-native-background-geolocation-maven - expo prebuild (DO NOT MODIFY) sync-4b2bae87fd8579c445d8885f6bdc8542d9d0bbca
|
||||
maven { url "${project(":react-native-background-geolocation").projectDir}/libs" }
|
||||
maven { url 'https://developer.huawei.com/repo/' }
|
||||
// @generated end react-native-background-geolocation-maven
|
||||
// @generated end react-native-background-fetch-maven
|
||||
maven {
|
||||
maven {
|
||||
// All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
|
||||
url(new File(['node', '--print', "require.resolve('react-native/package.json')"].execute(null, rootDir).text.trim(), '../android'))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -140,15 +140,9 @@ let config = {
|
|||
],
|
||||
},
|
||||
UIBackgroundModes: ["location", "fetch", "processing"],
|
||||
TSLocationManagerLicense: process.env.BACKGROUND_GEOLOCATION_LICENSE_IOS,
|
||||
},
|
||||
plugins: [
|
||||
[
|
||||
"react-native-background-geolocation",
|
||||
{
|
||||
license: process.env.BACKGROUND_GEOLOCATION_LICENSE,
|
||||
hmsLicense: process.env.BACKGROUND_GEOLOCATION_HMS_LICENSE,
|
||||
},
|
||||
],
|
||||
[
|
||||
"expo-gradle-ext-vars",
|
||||
{
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@ The app uses environment variables for configuration and sensitive information.
|
|||
Copy `.env.default` to `.env.local` (which is git-ignored) and fill in the required values:
|
||||
|
||||
- `BACKGROUND_GEOLOCATION_LICENSE`: License key for react-native-background-geolocation
|
||||
- `BACKGROUND_GEOLOCATION_HMS_LICENSE`: HMS license key for react-native-background-geolocation
|
||||
|
||||
### Production Environment
|
||||
|
||||
|
|
|
|||
|
|
@ -15,14 +15,14 @@ Applies to the BackgroundGeolocation integration:
|
|||
## Current implementation notes
|
||||
|
||||
- Movement-driven recording only:
|
||||
- IDLE uses `distanceFilter: 200` (aim: no updates while not moving).
|
||||
- ACTIVE uses `distanceFilter: 25`.
|
||||
- IDLE uses `geolocation.distanceFilter: 200` (aim: no updates while not moving).
|
||||
- ACTIVE uses `geolocation.distanceFilter: 25`.
|
||||
- JS may request a persisted fix when entering ACTIVE (see [`applyProfile()`](src/location/trackLocation.js:351)).
|
||||
- Upload strategy is intentionally simple:
|
||||
- Keep only the latest persisted geopoint: `maxRecordsToPersist: 1`.
|
||||
- Keep only the latest persisted geopoint: `persistence.maxRecordsToPersist: 1`.
|
||||
- No batching / thresholds: `batchSync: false`, `autoSyncThreshold: 0`.
|
||||
- When authenticated, each persisted location should upload immediately via native HTTP (works while JS is suspended).
|
||||
- Pre-auth: tracking may persist locally but `url` is empty so nothing is uploaded until auth is ready.
|
||||
- Pre-auth: tracking may persist locally but `http.url` is empty so nothing is uploaded until auth is ready.
|
||||
|
||||
## Basic preconditions
|
||||
|
||||
|
|
@ -69,18 +69,40 @@ Applies to the BackgroundGeolocation integration:
|
|||
### Android
|
||||
|
||||
1. Ensure tracking enabled and authenticated.
|
||||
2. Force-stop the app task.
|
||||
2. Swipe the app away from recents / kill the task.
|
||||
3. Move ~250m in IDLE.
|
||||
- Expect: native service still records + uploads.
|
||||
4. Move ~30m in ACTIVE.
|
||||
- Expect: native service still records + uploads.
|
||||
|
||||
> Note: This does **not** include Android Settings → **Force stop**.
|
||||
> Force-stop prevents background services and receivers from running; no SDK can reliably track after that.
|
||||
|
||||
### iOS
|
||||
|
||||
1. Swipe-kill the app.
|
||||
2. Move significantly (expect iOS to relaunch app on stationary-geofence exit).
|
||||
- Expect: tracking resumes and uploads after movement.
|
||||
|
||||
## Test matrix (quick)
|
||||
|
||||
| Platform | App state | Profile | Move | Expected signals |
|
||||
|---|---|---|---:|---|
|
||||
| Android | foreground | IDLE | ~250m | [`onLocation`](src/location/trackLocation.js:693) (sample=false), then [`onHttp`](src/location/trackLocation.js:733) |
|
||||
| Android | background | IDLE | ~250m | same as above |
|
||||
| Android | swipe-away | IDLE | ~250m | native persists + uploads; verify server + `onHttp` when app relaunches |
|
||||
| Android | foreground | ACTIVE | ~30m | location + upload continues |
|
||||
| iOS | background | IDLE | ~250m | movement-driven update; no periodic uploads while stationary |
|
||||
| iOS | swipe-killed | IDLE | significant | OS relaunch on movement; upload after relaunch |
|
||||
|
||||
## What to look for in logs
|
||||
|
||||
- App lifecycle tagging: [`updateTrackingContextExtras()`](src/location/trackLocation.js:63) should update `tracking_ctx.app_state` on AppState changes.
|
||||
- No time-based uploads: heartbeat is disabled (`heartbeatInterval: 0`), so no `Heartbeat` logs from [`onHeartbeat`](src/location/trackLocation.js:762).
|
||||
- Movement-only uploads:
|
||||
- IDLE distance threshold: `distanceFilter: 200` in [`TRACKING_PROFILES`](src/location/backgroundGeolocationConfig.js:148).
|
||||
- ACTIVE distance threshold: `distanceFilter: 25` in [`TRACKING_PROFILES`](src/location/backgroundGeolocationConfig.js:148).
|
||||
|
||||
## Debugging tips
|
||||
|
||||
- Observe logs in app:
|
||||
|
|
|
|||
|
|
@ -146,6 +146,8 @@
|
|||
<string>[Alerte-Secours] Cette application utilise les notifications pour pouvoir vous prévenir lorsque quelqu'un a besoin d'aide à proximité</string>
|
||||
<key>NSUserNotificationUsageDescription</key>
|
||||
<string>Alerte-Secours utilise les notifications pour vous informer immédiatement lorsqu'une personne à proximité nécessite une assistance urgente. Ces alertes sont essentielles pour une réponse rapide aux situations d'urgence.</string>
|
||||
<key>TSLocationManagerLicense</key>
|
||||
<string>eyJhbGciOiJFZERTQSIsImtpZCI6ImVkMjU1MTktbWFpbi12MSJ9.eyJvcyI6ImlvcyIsImFwcF9pZCI6ImNvbS5hbGVydGVzZWNvdXJzLmFsZXJ0ZXNlY291cnMiLCJvcmRlcl9udW1iZXIiOjg2NDAsInJlbmV3YWxfdXJsIjoiaHR0cHM6Ly9zaG9wLnRyYW5zaXN0b3Jzb2Z0LmNvbS9jYXJ0LzE2NTA3ODYxNTA1OjE_bm90ZT02MTkzIiwiY3VzdG9tZXJfaWQiOjU3NDIsInByb2R1Y3QiOiJyZWFjdC1uYXRpdmUtYmFja2dyb3VuZC1nZW9sb2NhdGlvbiIsImtleV92ZXJzaW9uIjoxLCJhbGxvd2VkX3N1ZmZpeGVzIjpbIi5kZXYiLCIuZGV2ZWxvcG1lbnQiLCIuc3RhZ2luZyIsIi5zdGFnZSIsIi5xYSIsIi51YXQiLCIudGVzdCIsIi5kZWJ1ZyJdLCJtYXhfYnVpbGRfc3RhbXAiOjIwMjcwMjIyLCJncmFjZV9idWlsZHMiOjAsImVudGl0bGVtZW50cyI6WyJjb3JlIl0sImlhdCI6MTc2OTEyNjI4OX0.bOJmTwxkRlRVAMHuNeXUVlSFTQyai3wal2TEz6jlOvEHFadbP6WG0E_KUMatinSRgKEIxs0L_SmSn6G5eWAYDQ</string>
|
||||
<key>UIBackgroundModes</key>
|
||||
<array>
|
||||
<string>fetch</string>
|
||||
|
|
|
|||
|
|
@ -178,7 +178,7 @@
|
|||
"react-native-animatable": "^1.3.3",
|
||||
"react-native-app-link": "^1.0.1",
|
||||
"react-native-background-fetch": "^4.2.8",
|
||||
"react-native-background-geolocation": "^4.19.0",
|
||||
"react-native-background-geolocation": "5.0.1",
|
||||
"react-native-battery-optimization-check": "^1.0.8",
|
||||
"react-native-contact-pick": "^0.1.2",
|
||||
"react-native-country-picker-modal": "^2.0.0",
|
||||
|
|
|
|||
|
|
@ -19,53 +19,66 @@ import env from "~/env";
|
|||
// - ACTIVE (open alert): first location should reach server within seconds, then continuous distance-based updates.
|
||||
//
|
||||
// Notes:
|
||||
// - We avoid `reset: true` in production because it can unintentionally wipe persisted / configured state.
|
||||
// In dev, `reset: true` is useful to avoid config drift while iterating.
|
||||
// - `maxRecordsToPersist` must be > 1 to support offline catch-up.
|
||||
// - We keep config deterministic across launches to avoid stale persisted settings creating
|
||||
// unexpected periodic wakeups/uploads.
|
||||
// - `maxRecordsToPersist: 1` matches product requirement (only latest geopoint).
|
||||
export const BASE_GEOLOCATION_CONFIG = {
|
||||
// Android Headless Mode
|
||||
// We do not require JS execution while terminated. Native tracking + native HTTP upload
|
||||
// are sufficient for our needs (stopOnTerminate:false).
|
||||
enableHeadless: false,
|
||||
reset: true,
|
||||
|
||||
// Default to low-power (idle) profile; will be overridden when needed.
|
||||
desiredAccuracy: BackgroundGeolocation.DESIRED_ACCURACY_LOW,
|
||||
// Logger config
|
||||
logger: {
|
||||
// debug: true,
|
||||
// Logging can become large and also adds overhead; keep verbose logs to dev/staging.
|
||||
logLevel:
|
||||
__DEV__ || env.IS_STAGING
|
||||
? BackgroundGeolocation.LogLevel.Verbose
|
||||
: BackgroundGeolocation.LogLevel.Error,
|
||||
},
|
||||
|
||||
// Geolocation config
|
||||
geolocation: {
|
||||
// Default profile is IDLE.
|
||||
//
|
||||
// Important: `DesiredAccuracy.Low` (wifi/cell) can yield very large errors (km-level).
|
||||
// Those poor fixes can create false motion/geofence transitions on some Android devices,
|
||||
// resulting in periodic uploads while the user is stationary.
|
||||
//
|
||||
// We default to a GPS-capable accuracy but rely on motion + distance thresholds to
|
||||
// protect battery.
|
||||
desiredAccuracy: BackgroundGeolocation.DesiredAccuracy.High,
|
||||
|
||||
// Default to the IDLE profile behaviour: we still want distance-based updates
|
||||
// even with no open alert (see TRACKING_PROFILES.idle).
|
||||
distanceFilter: 200,
|
||||
|
||||
// Activity-recognition stop-detection.
|
||||
// NOTE: Transistorsoft defaults `stopTimeout` to 5 minutes (see
|
||||
// [`node_modules/react-native-background-geolocation/src/declarations/interfaces/Config.d.ts:79`](node_modules/react-native-background-geolocation/src/declarations/interfaces/Config.d.ts:79)).
|
||||
// Stop-detection.
|
||||
// NOTE: historically we set this at top-level. In v5 the knob is under `geolocation`.
|
||||
stopTimeout: 5,
|
||||
|
||||
// debug: true,
|
||||
// Logging can become large and also adds overhead; keep verbose logs to dev/staging.
|
||||
logLevel:
|
||||
__DEV__ || env.IS_STAGING
|
||||
? BackgroundGeolocation.LOG_LEVEL_VERBOSE
|
||||
: BackgroundGeolocation.LOG_LEVEL_ERROR,
|
||||
|
||||
// Permission request strategy
|
||||
locationAuthorizationRequest: "Always",
|
||||
},
|
||||
|
||||
// Application / lifecycle config
|
||||
app: {
|
||||
// Android Headless Mode
|
||||
// We do not require JS execution while terminated. Native tracking + native HTTP upload
|
||||
// are sufficient for our needs (stopOnTerminate:false).
|
||||
enableHeadless: false,
|
||||
|
||||
// Lifecycle
|
||||
stopOnTerminate: false,
|
||||
startOnBoot: true,
|
||||
|
||||
// Background scheduling
|
||||
// Disable heartbeats by default to avoid periodic background wakeups while stationary.
|
||||
// ACTIVE profile will explicitly enable a fast heartbeat when needed.
|
||||
// Disable heartbeats to avoid periodic background wakeups while stationary.
|
||||
heartbeatInterval: 0,
|
||||
|
||||
// Android foreground service
|
||||
foregroundService: true,
|
||||
// Android foreground service notification
|
||||
notification: {
|
||||
title: "Alerte Secours",
|
||||
text: "Suivi de localisation actif",
|
||||
channelName: "Location tracking",
|
||||
priority: BackgroundGeolocation.NOTIFICATION_PRIORITY_HIGH,
|
||||
priority: BackgroundGeolocation.NotificationPriority.High,
|
||||
},
|
||||
|
||||
// Android 10+ rationale dialog
|
||||
|
|
@ -77,84 +90,95 @@ export const BASE_GEOLOCATION_CONFIG = {
|
|||
positiveAction: "Autoriser",
|
||||
negativeAction: "Désactiver",
|
||||
},
|
||||
},
|
||||
|
||||
// HTTP configuration
|
||||
// HTTP config
|
||||
// IMPORTANT: Default to uploads disabled until we have an auth token.
|
||||
// Authenticated mode will set `url` + `Authorization` header and enable `autoSync`.
|
||||
// Authenticated mode will set `http.url` + `Authorization` header and enable `autoSync`.
|
||||
http: {
|
||||
url: "",
|
||||
method: "POST",
|
||||
httpRootProperty: "location",
|
||||
method: BackgroundGeolocation.HttpMethod.POST,
|
||||
rootProperty: "location",
|
||||
// Keep uploads simple: 1 location record -> 1 HTTP request.
|
||||
// (We intentionally keep only the latest record; batching provides no benefit.)
|
||||
autoSync: false,
|
||||
// Ensure no persisted config can keep batching/threshold behavior.
|
||||
batchSync: false,
|
||||
autoSyncThreshold: 0,
|
||||
},
|
||||
|
||||
// Persistence
|
||||
// Product requirement: keep only the latest geopoint. This reduces on-device storage
|
||||
// and avoids building up a queue.
|
||||
// NOTE: This means we intentionally do not support offline catch-up of multiple points.
|
||||
// Persistence config
|
||||
persistence: {
|
||||
// Product requirement: keep only the latest geopoint.
|
||||
maxRecordsToPersist: 1,
|
||||
maxDaysToPersist: 1,
|
||||
|
||||
// IMPORTANT: Keep config deterministic across upgrades.
|
||||
// `reset: false` causes the SDK to ignore changes in `ready(config)` after first install.
|
||||
// This can leave old values (eg heartbeatInterval) lingering and creating periodic uploads.
|
||||
reset: true,
|
||||
|
||||
// Behavior tweaks
|
||||
disableProviderChangeRecord: true,
|
||||
},
|
||||
};
|
||||
|
||||
// Options we want to be stable across launches even when the plugin loads a persisted config.
|
||||
// NOTE: We intentionally do *not* include HTTP auth headers here.
|
||||
export const BASE_GEOLOCATION_INVARIANTS = {
|
||||
app: {
|
||||
enableHeadless: false,
|
||||
stopOnTerminate: false,
|
||||
startOnBoot: true,
|
||||
foregroundService: true,
|
||||
disableProviderChangeRecord: true,
|
||||
// Never allow background heartbeats by default (prevents time-based wakeups/uploads).
|
||||
heartbeatInterval: 0,
|
||||
// Filter extreme GPS teleports that can create false uploads while stationary.
|
||||
// Units: meters/second. 100 m/s ~= 360 km/h.
|
||||
speedJumpFilter: 100,
|
||||
method: "POST",
|
||||
httpRootProperty: "location",
|
||||
},
|
||||
http: {
|
||||
method: BackgroundGeolocation.HttpMethod.POST,
|
||||
rootProperty: "location",
|
||||
autoSync: false,
|
||||
batchSync: false,
|
||||
autoSyncThreshold: 0,
|
||||
},
|
||||
persistence: {
|
||||
maxRecordsToPersist: 1,
|
||||
maxDaysToPersist: 1,
|
||||
disableProviderChangeRecord: true,
|
||||
},
|
||||
// NOTE: `speedJumpFilter` was a legacy Config knob; it is not part of v5 shared types.
|
||||
// If we still want jump filtering, we'll need to implement a server-side filter or
|
||||
// re-introduce a supported SDK filter (eg `geolocation.filter`) when available.
|
||||
};
|
||||
|
||||
export const TRACKING_PROFILES = {
|
||||
idle: {
|
||||
desiredAccuracy: BackgroundGeolocation.DESIRED_ACCURACY_LOW,
|
||||
geolocation: {
|
||||
// Same rationale as BASE: prefer GPS-capable accuracy to avoid km-level coarse fixes
|
||||
// that can trigger false motion/geofence transitions on Android.
|
||||
desiredAccuracy: BackgroundGeolocation.DesiredAccuracy.High,
|
||||
// Defensive: keep the distanceFilter conservative to avoid battery drain.
|
||||
distanceFilter: 200,
|
||||
|
||||
},
|
||||
app: {
|
||||
// Never use heartbeat-driven updates; only movement-driven.
|
||||
heartbeatInterval: 0,
|
||||
|
||||
// Keep the plugin's speed-based distanceFilter scaling enabled (default).
|
||||
// This yields fewer updates as speed increases (highway speeds) and helps battery.
|
||||
// We intentionally do NOT set `disableElasticity: true`.
|
||||
|
||||
},
|
||||
activity: {
|
||||
// Android-only: reduce false-positive motion triggers due to screen-on/unlock.
|
||||
// We keep Motion API enabled (battery-optimized) but add a large delay so brief
|
||||
// activity-jitter cannot repeatedly toggle moving/stationary while the user is idle.
|
||||
// (This is ignored on iOS.)
|
||||
motionTriggerDelay: 30000,
|
||||
motionTriggerDelay: 300000,
|
||||
},
|
||||
},
|
||||
active: {
|
||||
desiredAccuracy: BackgroundGeolocation.DESIRED_ACCURACY_HIGH,
|
||||
geolocation: {
|
||||
desiredAccuracy: BackgroundGeolocation.DesiredAccuracy.High,
|
||||
// ACTIVE target: frequent updates while moving.
|
||||
distanceFilter: 25,
|
||||
|
||||
},
|
||||
app: {
|
||||
// Never use heartbeat-driven updates; only movement-driven.
|
||||
heartbeatInterval: 0,
|
||||
|
||||
},
|
||||
activity: {
|
||||
// Android-only: do not delay motion triggers while ACTIVE.
|
||||
motionTriggerDelay: 0,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -44,9 +44,9 @@ export async function getCurrentLocation() {
|
|||
|
||||
const isAuthorized =
|
||||
authorizationStatus ===
|
||||
BackgroundGeolocation.AUTHORIZATION_STATUS_ALWAYS ||
|
||||
BackgroundGeolocation.AuthorizationStatus?.Always ||
|
||||
authorizationStatus ===
|
||||
BackgroundGeolocation.AUTHORIZATION_STATUS_WHEN_IN_USE;
|
||||
BackgroundGeolocation.AuthorizationStatus?.WhenInUse;
|
||||
|
||||
if (!isAuthorized) {
|
||||
// If unable to get permissions, provide a link to settings
|
||||
|
|
@ -67,7 +67,7 @@ export async function getCurrentLocation() {
|
|||
timeout: 30,
|
||||
persist: false,
|
||||
maximumAge: 5000,
|
||||
desiredAccuracy: BackgroundGeolocation.DESIRED_ACCURACY_HIGH,
|
||||
desiredAccuracy: BackgroundGeolocation.DesiredAccuracy.High,
|
||||
samples: 1,
|
||||
});
|
||||
const coords = camelCaseKeys(location.coords);
|
||||
|
|
|
|||
|
|
@ -64,6 +64,7 @@ export default function trackLocation() {
|
|||
try {
|
||||
const { userId } = getSessionState();
|
||||
await BackgroundGeolocation.setConfig({
|
||||
persistence: {
|
||||
extras: {
|
||||
tracking_ctx: {
|
||||
reason,
|
||||
|
|
@ -74,6 +75,7 @@ export default function trackLocation() {
|
|||
at: new Date().toISOString(),
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
} catch (e) {
|
||||
// Non-fatal: extras are only for observability/debugging.
|
||||
|
|
@ -249,7 +251,17 @@ export default function trackLocation() {
|
|||
const AUTH_FIX_COOLDOWN_MS = 15 * 60 * 1000;
|
||||
let lastAuthFixAt = 0;
|
||||
|
||||
// Avoid periodic UI-only getCurrentPosition while app is backgrounded, since
|
||||
// this is a common source of "updates while stationary" (it can also influence
|
||||
// motion state / generate provider churn on some Android devices).
|
||||
const shouldAllowUiFixes = () => appState === "active";
|
||||
|
||||
const scheduleAuthFreshFix = () => {
|
||||
// Do not perform UI refresh fixes while backgrounded.
|
||||
if (!shouldAllowUiFixes()) {
|
||||
return authFixInFlight;
|
||||
}
|
||||
|
||||
// Avoid generating persisted + auto-synced locations as a side-effect of frequent
|
||||
// auth refreshes (eg app resume / screen unlock).
|
||||
if (Date.now() - lastAuthFixAt < AUTH_FIX_COOLDOWN_MS) {
|
||||
|
|
@ -365,9 +377,9 @@ export default function trackLocation() {
|
|||
|
||||
locationLogger.info("Applying tracking profile", {
|
||||
profileName,
|
||||
desiredAccuracy: profile.desiredAccuracy,
|
||||
distanceFilter: profile.distanceFilter,
|
||||
heartbeatInterval: profile.heartbeatInterval,
|
||||
desiredAccuracy: profile?.geolocation?.desiredAccuracy,
|
||||
distanceFilter: profile?.geolocation?.distanceFilter,
|
||||
heartbeatInterval: profile?.app?.heartbeatInterval,
|
||||
});
|
||||
|
||||
try {
|
||||
|
|
@ -485,9 +497,11 @@ export default function trackLocation() {
|
|||
|
||||
try {
|
||||
await BackgroundGeolocation.setConfig({
|
||||
http: {
|
||||
url: "",
|
||||
autoSync: false,
|
||||
headers: {},
|
||||
},
|
||||
});
|
||||
didDisableUploadsForAnonymous = true;
|
||||
didSyncAfterAuth = false;
|
||||
|
|
@ -547,7 +561,9 @@ export default function trackLocation() {
|
|||
// unsub();
|
||||
locationLogger.debug("Updating background geolocation config");
|
||||
await BackgroundGeolocation.setConfig({
|
||||
url: env.GEOLOC_SYNC_URL, // Update the sync URL for when it's changed for staging
|
||||
http: {
|
||||
// Update the sync URL for when it's changed for staging
|
||||
url: env.GEOLOC_SYNC_URL,
|
||||
// IMPORTANT: enable native uploading when authenticated.
|
||||
// This ensures uploads continue even if JS is suspended in background.
|
||||
autoSync: true,
|
||||
|
|
@ -556,6 +572,7 @@ export default function trackLocation() {
|
|||
headers: {
|
||||
Authorization: `Bearer ${userToken}`,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
authReady = true;
|
||||
|
|
@ -756,11 +773,50 @@ export default function trackLocation() {
|
|||
});
|
||||
},
|
||||
onMotionChange: (event) => {
|
||||
// Diagnostic snapshot to understand periodic motion-change loops (eg Android ~5min).
|
||||
// Keep it cheap: avoid heavy calls unless motion-change fires.
|
||||
// NOTE: This is safe to run in background because it does not request a new location.
|
||||
locationLogger.info("Motion change", {
|
||||
isMoving: event?.isMoving,
|
||||
location: event?.location?.coords,
|
||||
});
|
||||
|
||||
// Async snapshot of BGGeo internal state/config at the time of motion-change.
|
||||
// This helps correlate native behavior with our current profile + config.
|
||||
(async () => {
|
||||
try {
|
||||
const state = await BackgroundGeolocation.getState();
|
||||
|
||||
locationLogger.info("Motion change diagnostic", {
|
||||
isMoving: event?.isMoving,
|
||||
appState: appState,
|
||||
profile: currentProfile,
|
||||
authReady,
|
||||
// Time correlation
|
||||
at: new Date().toISOString(),
|
||||
// Core BGGeo runtime state
|
||||
enabled: state?.enabled,
|
||||
trackingMode: state?.trackingMode,
|
||||
isMovingState: state?.isMoving,
|
||||
schedulerEnabled: state?.schedulerEnabled,
|
||||
// Critical config knobs related to periodic updates
|
||||
distanceFilter: state?.geolocation?.distanceFilter,
|
||||
heartbeatInterval: state?.app?.heartbeatInterval,
|
||||
motionTriggerDelay: state?.activity?.motionTriggerDelay,
|
||||
disableMotionActivityUpdates:
|
||||
state?.activity?.disableMotionActivityUpdates,
|
||||
stopTimeout: state?.geolocation?.stopTimeout,
|
||||
// Location quality signal
|
||||
accuracy: event?.location?.coords?.accuracy,
|
||||
speed: event?.location?.coords?.speed,
|
||||
});
|
||||
} catch (e) {
|
||||
locationLogger.warn("Motion change diagnostic failed", {
|
||||
error: e?.message,
|
||||
});
|
||||
}
|
||||
})();
|
||||
|
||||
// Moving-edge strategy: when we enter moving state, force one persisted high-quality
|
||||
// point + sync so the server gets a quick update.
|
||||
//
|
||||
|
|
|
|||
42
yarn.lock
42
yarn.lock
|
|
@ -3237,6 +3237,13 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@babel/runtime@npm:^7.26.0":
|
||||
version: 7.28.6
|
||||
resolution: "@babel/runtime@npm:7.28.6"
|
||||
checksum: 10/fbcd439cb74d4a681958eb064c509829e3f46d8a4bfaaf441baa81bb6733d1e680bccc676c813883d7741bcaada1d0d04b15aa320ef280b5734e2192b50decf9
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@babel/template@npm:^7.0.0, @babel/template@npm:^7.22.5":
|
||||
version: 7.22.5
|
||||
resolution: "@babel/template@npm:7.22.5"
|
||||
|
|
@ -6110,6 +6117,13 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@transistorsoft/background-geolocation-types@npm:^5.0.1":
|
||||
version: 5.0.1
|
||||
resolution: "@transistorsoft/background-geolocation-types@npm:5.0.1"
|
||||
checksum: 10/14e8a1e64f653f59c8ff22ecb576fc79b632278acdd1bb70a84d938d943f98bd51c620827a0036c4e486e2eaac255101686c40487904b1c866e7e261867faac0
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@turf/along@npm:^7.1.0":
|
||||
version: 7.1.0
|
||||
resolution: "@turf/along@npm:7.1.0"
|
||||
|
|
@ -7127,7 +7141,7 @@ __metadata:
|
|||
react-native-animatable: "npm:^1.3.3"
|
||||
react-native-app-link: "npm:^1.0.1"
|
||||
react-native-background-fetch: "npm:^4.2.8"
|
||||
react-native-background-geolocation: "npm:^4.19.0"
|
||||
react-native-background-geolocation: "npm:^5.0.1"
|
||||
react-native-battery-optimization-check: "npm:^1.0.8"
|
||||
react-native-clean-project: "npm:^4.0.3"
|
||||
react-native-contact-pick: "npm:^0.1.2"
|
||||
|
|
@ -16713,19 +16727,14 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"react-native-background-fetch@npm:~4.2.6":
|
||||
version: 4.2.7
|
||||
resolution: "react-native-background-fetch@npm:4.2.7"
|
||||
checksum: 10/f0b3d008379dbd992cc1d9708ba1dae86848226a3f4644a8e5955d805841e3122ad56be28bea79af3fa6a03fa5e4f4b8ebf593eacee4dc5ef1ec8cd2dfd5f379
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"react-native-background-geolocation@npm:^4.19.0":
|
||||
version: 4.19.0
|
||||
resolution: "react-native-background-geolocation@npm:4.19.0"
|
||||
"react-native-background-geolocation@npm:^5.0.1":
|
||||
version: 5.0.1
|
||||
resolution: "react-native-background-geolocation@npm:5.0.1"
|
||||
dependencies:
|
||||
react-native-background-fetch: "npm:~4.2.6"
|
||||
checksum: 10/0ef8a52737ff77b2f04b4c6b1f806abc576b85570c4eba0274e9d2ea0b8393ccd96c7c24c1c5b067f4111b317a958b6d3760dc2a63b733ebecd6af63306a7191
|
||||
"@babel/runtime": "npm:^7.26.0"
|
||||
"@transistorsoft/background-geolocation-types": "npm:^5.0.1"
|
||||
tslib: "npm:^2.6.3"
|
||||
checksum: 10/7d994a2403228669140ffed55019041d0b2aab08b9de3dd4cbbfb26ea6378488a3c4d5fc0779f6138ddacb796df21363cafab996c0d83a86cd213975c1abc56c
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
|
@ -19092,6 +19101,13 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"tslib@npm:^2.6.3":
|
||||
version: 2.8.1
|
||||
resolution: "tslib@npm:2.8.1"
|
||||
checksum: 10/3e2e043d5c2316461cb54e5c7fe02c30ef6dccb3384717ca22ae5c6b5bc95232a6241df19c622d9c73b809bea33b187f6dbc73030963e29950c2141bc32a79f7
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"tween-functions@npm:^1.0.1":
|
||||
version: 1.2.0
|
||||
resolution: "tween-functions@npm:1.2.0"
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue