expo-background-tracking 1.0.0 → 1.0.1
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.
- package/FEATURES.md +110 -0
- package/README.md +61 -63
- package/package.json +1 -1
package/FEATURES.md
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# Features
|
|
2
|
+
|
|
3
|
+
## Core Capabilities
|
|
4
|
+
|
|
5
|
+
### 🎯 Background Geolocation
|
|
6
|
+
- **Continuous tracking** in foreground and background (dev builds)
|
|
7
|
+
- **Configurable accuracy** levels (DESIRED_ACCURACY_HIGH, etc.)
|
|
8
|
+
- **Distance filtering** to reduce excessive location updates
|
|
9
|
+
- **Native GPS** integration with fallback to software-based tracking
|
|
10
|
+
- **Adaptive accuracy** based on device motion
|
|
11
|
+
|
|
12
|
+
### 🎪 Geofencing (Unlimited)
|
|
13
|
+
- **Circle geofences** with customizable radius
|
|
14
|
+
- **Polygon geofences** for complex zone detection
|
|
15
|
+
- **Entry/Exit/Dwell events** with configurable delays
|
|
16
|
+
- **Software & native** evaluation modes
|
|
17
|
+
- **Loitering detection** to identify stationary periods
|
|
18
|
+
|
|
19
|
+
### 📍 Motion Detection
|
|
20
|
+
- **Accelerometer-based** motion sensing
|
|
21
|
+
- **Battery-efficient** detection algorithm
|
|
22
|
+
- **Activity classification** (walking, running, vehicle, stationary)
|
|
23
|
+
- **Automatic pace control** for power optimization
|
|
24
|
+
|
|
25
|
+
### 💾 Data Persistence
|
|
26
|
+
- **SQLite storage** for offline resilience
|
|
27
|
+
- **Automatic batching** for efficient queries
|
|
28
|
+
- **Configurable retention** (max days, max records)
|
|
29
|
+
- **Full queue visibility** and manual control
|
|
30
|
+
|
|
31
|
+
### 🌐 HTTP Sync & Server Integration
|
|
32
|
+
- **Native fetch-based** transmission (no Firebase)
|
|
33
|
+
- **Offline queue** with automatic retry on reconnection
|
|
34
|
+
- **Batch transmission** to reduce network overhead
|
|
35
|
+
- **Custom headers & parameters** for auth/context
|
|
36
|
+
- **Template-based payloads** for flexible formatting
|
|
37
|
+
- **JWT authentication** with automatic token refresh
|
|
38
|
+
- **HTTP event tracking** for monitoring uploads
|
|
39
|
+
|
|
40
|
+
### ⏱️ Scheduling
|
|
41
|
+
- **Weekly schedules** (e.g., "1-5 09:00-17:00" = Mon-Fri 9am-5pm)
|
|
42
|
+
- **Automatic start/stop** based on schedule
|
|
43
|
+
- **Smart scheduling** to save power during off-hours
|
|
44
|
+
|
|
45
|
+
### 🔋 Battery & Power Management
|
|
46
|
+
- **Battery level reporting** in location data
|
|
47
|
+
- **Charging state detection**
|
|
48
|
+
- **Power save mode** awareness
|
|
49
|
+
- **Motion-based sleep** to reduce sampling when stationary
|
|
50
|
+
- **Efficient foreground service** (Android)
|
|
51
|
+
|
|
52
|
+
### 📊 Metrics & Diagnostics
|
|
53
|
+
- **Odometer tracking** (distance traveled)
|
|
54
|
+
- **Sensor data collection** (GPS, accelerometer, compass)
|
|
55
|
+
- **Comprehensive logging** with debug output
|
|
56
|
+
- **Provider state monitoring** (GPS, network quality)
|
|
57
|
+
- **Heartbeat events** to confirm app is still tracking
|
|
58
|
+
|
|
59
|
+
### 🔐 Security & Authentication
|
|
60
|
+
- **Bearer token support** in HTTP headers
|
|
61
|
+
- **JWT-based authentication** with refresh strategy
|
|
62
|
+
- **Custom headers** for API key injection
|
|
63
|
+
- **Secure HTTPS** for all transmissions
|
|
64
|
+
|
|
65
|
+
## Platform Support
|
|
66
|
+
|
|
67
|
+
### ✅ Supported Platforms
|
|
68
|
+
- **iOS** (13+) with UIBackgroundModes configuration
|
|
69
|
+
- **Android** (5+) with FOREGROUND_SERVICE permissions
|
|
70
|
+
|
|
71
|
+
### 📱 Expo Compatibility
|
|
72
|
+
- **Expo Go**: Full foreground tracking, software geofencing
|
|
73
|
+
- **Dev builds**: Full background tracking with native geofencing
|
|
74
|
+
- **Production**: Full background tracking after EAS build
|
|
75
|
+
|
|
76
|
+
## Performance Characteristics
|
|
77
|
+
|
|
78
|
+
- **Minimal battery drain** with optimized algorithms
|
|
79
|
+
- **Low memory footprint** (~5-10 MB)
|
|
80
|
+
- **Efficient database** queries with indexing
|
|
81
|
+
- **Batched network requests** to reduce data usage
|
|
82
|
+
- **Configurable trade-offs** between accuracy and power consumption
|
|
83
|
+
|
|
84
|
+
## Integration Options
|
|
85
|
+
|
|
86
|
+
- **Seamless integration** with Expo ecosystem
|
|
87
|
+
- **Cross-platform** code (same logic iOS/Android)
|
|
88
|
+
- **Headless task support** for background processing
|
|
89
|
+
- **Full TypeScript** support with type definitions
|
|
90
|
+
- **Event-driven architecture** for reactive applications
|
|
91
|
+
|
|
92
|
+
## Data Collection
|
|
93
|
+
|
|
94
|
+
Each location includes:
|
|
95
|
+
- **Coordinates** (latitude, longitude, accuracy)
|
|
96
|
+
- **Movement** (speed, heading, altitude)
|
|
97
|
+
- **Activity** (type, confidence level)
|
|
98
|
+
- **Battery** (level, charging state)
|
|
99
|
+
- **Device** (ID, model, OS)
|
|
100
|
+
- **Timestamp** (ISO 8601)
|
|
101
|
+
- **Odometer** (distance traveled)
|
|
102
|
+
- **Custom extras** (mission, user, etc.)
|
|
103
|
+
|
|
104
|
+
## No External Dependencies
|
|
105
|
+
|
|
106
|
+
- ✅ No Firebase required
|
|
107
|
+
- ✅ No Google Play Services (uses native APIs)
|
|
108
|
+
- ✅ No paid tracking SDKs
|
|
109
|
+
- ✅ Full control over your data
|
|
110
|
+
- ✅ Custom server backend compatible
|
package/README.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# expo-background-tracking
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
GPS tracking & geofencing for **Expo** (Android + iOS), 100% Expo, **no Firebase**, no custom native code, **Expo Go compatible**.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Battery-efficient motion detection (accelerometer + GPS speed), SQLite persistence, HTTP sync with offline queue, unlimited geofencing (circles + polygons), odometer, weekly scheduling.
|
|
6
6
|
|
|
7
7
|
## Installation
|
|
8
8
|
|
|
@@ -11,18 +11,18 @@ npm install expo-background-tracking
|
|
|
11
11
|
npx expo install expo-location expo-task-manager expo-sqlite expo-sensors expo-battery expo-network expo-device expo-constants
|
|
12
12
|
```
|
|
13
13
|
|
|
14
|
-
##
|
|
14
|
+
## Expo Go Compatibility
|
|
15
15
|
|
|
16
16
|
| | Expo Go | Dev build / production |
|
|
17
17
|
|---|---|---|
|
|
18
|
-
| Tracking app
|
|
19
|
-
| Tracking app
|
|
20
|
-
| Geofencing | ✅ (
|
|
21
|
-
| SQLite, HTTP sync,
|
|
18
|
+
| Tracking app in foreground | ✅ | ✅ |
|
|
19
|
+
| Tracking app in background | ❌* | ✅ |
|
|
20
|
+
| Geofencing | ✅ (software, foreground) | ✅ (native + software) |
|
|
21
|
+
| SQLite, HTTP sync, odometer, scheduler, motion-detection | ✅ | ✅ |
|
|
22
22
|
|
|
23
|
-
\*
|
|
23
|
+
\* Official Expo limitation: background location requires a development build ([Expo docs](https://docs.expo.dev/versions/latest/sdk/location/)). The library automatically detects the environment — **same code everywhere**, zero crashes in Expo Go.
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
For full background (dev build), add to `app.json`:
|
|
26
26
|
|
|
27
27
|
```json
|
|
28
28
|
{
|
|
@@ -42,7 +42,7 @@ Pour le background complet (dev build), ajoutez dans `app.json` :
|
|
|
42
42
|
```ts
|
|
43
43
|
import BackgroundGeolocation from 'expo-background-tracking';
|
|
44
44
|
|
|
45
|
-
// 1.
|
|
45
|
+
// 1. Listen to events
|
|
46
46
|
const sub = BackgroundGeolocation.onLocation((location) => {
|
|
47
47
|
console.log('[location]', location.coords);
|
|
48
48
|
});
|
|
@@ -55,25 +55,25 @@ BackgroundGeolocation.onGeofence(({ identifier, action }) => {
|
|
|
55
55
|
console.log('[geofence]', identifier, action);
|
|
56
56
|
});
|
|
57
57
|
|
|
58
|
-
// 2.
|
|
58
|
+
// 2. Configure (ready, once only)
|
|
59
59
|
const state = await BackgroundGeolocation.ready({
|
|
60
60
|
desiredAccuracy: BackgroundGeolocation.DESIRED_ACCURACY_HIGH,
|
|
61
61
|
distanceFilter: 10,
|
|
62
62
|
stopTimeout: 5,
|
|
63
|
-
url: 'https://your-server.com/locations', // HTTP sync — fetch
|
|
63
|
+
url: 'https://your-server.com/locations', // HTTP sync — native fetch, no Firebase
|
|
64
64
|
autoSync: true,
|
|
65
65
|
batchSync: false,
|
|
66
66
|
headers: { 'X-API-KEY': 'xyz' },
|
|
67
67
|
debug: true,
|
|
68
68
|
});
|
|
69
69
|
|
|
70
|
-
// 3.
|
|
70
|
+
// 3. Start
|
|
71
71
|
if (!state.enabled) {
|
|
72
72
|
await BackgroundGeolocation.start();
|
|
73
73
|
}
|
|
74
74
|
```
|
|
75
75
|
|
|
76
|
-
### Geofencing (
|
|
76
|
+
### Geofencing (unlimited, circles + polygons)
|
|
77
77
|
|
|
78
78
|
```ts
|
|
79
79
|
await BackgroundGeolocation.addGeofence({
|
|
@@ -87,70 +87,70 @@ await BackgroundGeolocation.addGeofence({
|
|
|
87
87
|
loiteringDelay: 60000,
|
|
88
88
|
});
|
|
89
89
|
|
|
90
|
-
//
|
|
90
|
+
// Polygon (software-evaluated)
|
|
91
91
|
await BackgroundGeolocation.addGeofence({
|
|
92
92
|
identifier: 'ZONE',
|
|
93
93
|
vertices: [[48.85, 2.34], [48.86, 2.34], [48.86, 2.36], [48.85, 2.36]],
|
|
94
94
|
});
|
|
95
95
|
|
|
96
|
-
await BackgroundGeolocation.startGeofences(); //
|
|
96
|
+
await BackgroundGeolocation.startGeofences(); // geofences-only mode
|
|
97
97
|
```
|
|
98
98
|
|
|
99
|
-
##
|
|
99
|
+
## Sending HTTP locations to your server
|
|
100
100
|
|
|
101
|
-
|
|
101
|
+
The library includes a complete HTTP service (based on `fetch`, **no Firebase**): each location is persisted in SQLite then sent to your `url`. On failure (offline, server error), locations remain queued and are automatically resent on reconnection.
|
|
102
102
|
|
|
103
|
-
###
|
|
103
|
+
### Basic configuration
|
|
104
104
|
|
|
105
105
|
```ts
|
|
106
106
|
await BackgroundGeolocation.ready({
|
|
107
|
-
url: 'https://
|
|
108
|
-
method: 'POST',
|
|
109
|
-
headers: {
|
|
107
|
+
url: 'https://your-server.com/locations', // destination for locations
|
|
108
|
+
method: 'POST', // 'POST' (default) | 'PUT' | 'OPTIONS'
|
|
109
|
+
headers: { // custom HTTP headers
|
|
110
110
|
'X-API-KEY': 'abc123',
|
|
111
111
|
'Authorization': 'Bearer ...',
|
|
112
112
|
},
|
|
113
|
-
params: {
|
|
113
|
+
params: { // fields added at JSON root level
|
|
114
114
|
device_id: 'phone-01',
|
|
115
115
|
company_id: 7,
|
|
116
116
|
},
|
|
117
|
-
extras: {
|
|
117
|
+
extras: { // attached to EVERY location sent
|
|
118
118
|
user_id: 42,
|
|
119
|
-
mission: '
|
|
119
|
+
mission: 'delivery',
|
|
120
120
|
},
|
|
121
|
-
autoSync: true,
|
|
122
|
-
httpTimeout: 60000,
|
|
121
|
+
autoSync: true, // automatically send each location
|
|
122
|
+
httpTimeout: 60000, // request timeout in ms
|
|
123
123
|
});
|
|
124
124
|
```
|
|
125
125
|
|
|
126
|
-
###
|
|
126
|
+
### Control transmission count and frequency
|
|
127
127
|
|
|
128
|
-
| Option |
|
|
128
|
+
| Option | Effect |
|
|
129
129
|
|---|---|
|
|
130
|
-
| `autoSync: true` |
|
|
131
|
-
| `autoSyncThreshold: 10` |
|
|
132
|
-
| `batchSync: true` |
|
|
133
|
-
| `maxBatchSize: 50` | Maximum **50
|
|
134
|
-
| `autoSync: false` + `sync()` |
|
|
130
|
+
| `autoSync: true` | Automatically send each new location |
|
|
131
|
+
| `autoSyncThreshold: 10` | Only send when **10 locations** accumulate in queue |
|
|
132
|
+
| `batchSync: true` | Send locations **in batches** (1 HTTP request = N locations) |
|
|
133
|
+
| `maxBatchSize: 50` | Maximum **50 locations per request** (batches chain together) |
|
|
134
|
+
| `autoSync: false` + `sync()` | **100% manual** sending, when you decide |
|
|
135
135
|
|
|
136
136
|
```ts
|
|
137
|
-
//
|
|
137
|
+
// Example: save battery/network — 1 request per 25 points, in batches
|
|
138
138
|
await BackgroundGeolocation.ready({
|
|
139
|
-
url: 'https://
|
|
139
|
+
url: 'https://your-server.com/locations',
|
|
140
140
|
autoSync: true,
|
|
141
141
|
autoSyncThreshold: 25,
|
|
142
142
|
batchSync: true,
|
|
143
143
|
maxBatchSize: 100,
|
|
144
144
|
});
|
|
145
145
|
|
|
146
|
-
//
|
|
146
|
+
// Manual send (e.g., on button or at mission end)
|
|
147
147
|
const uploaded = await BackgroundGeolocation.sync();
|
|
148
|
-
console.log(`${uploaded.length}
|
|
148
|
+
console.log(`${uploaded.length} locations sent`);
|
|
149
149
|
```
|
|
150
150
|
|
|
151
|
-
###
|
|
151
|
+
### Payload format received by your server
|
|
152
152
|
|
|
153
|
-
|
|
153
|
+
By default (`httpRootProperty: 'location'`):
|
|
154
154
|
|
|
155
155
|
```json
|
|
156
156
|
{
|
|
@@ -165,28 +165,28 @@ Par défaut (`httpRootProperty: 'location'`) :
|
|
|
165
165
|
},
|
|
166
166
|
"activity": { "activity": "walking", "confidence": 75 },
|
|
167
167
|
"battery": { "level": 0.82, "is_charging": false },
|
|
168
|
-
"extras": { "user_id": 42, "mission": "
|
|
168
|
+
"extras": { "user_id": 42, "mission": "delivery" }
|
|
169
169
|
},
|
|
170
170
|
"device_id": "phone-01",
|
|
171
171
|
"company_id": 7
|
|
172
172
|
}
|
|
173
173
|
```
|
|
174
174
|
|
|
175
|
-
|
|
175
|
+
With `batchSync: true`, `"location"` becomes an **array** of locations.
|
|
176
176
|
|
|
177
|
-
###
|
|
177
|
+
### Customize format
|
|
178
178
|
|
|
179
179
|
```ts
|
|
180
180
|
await BackgroundGeolocation.ready({
|
|
181
181
|
url: '…',
|
|
182
|
-
httpRootProperty: 'data', // { "data": {...} } —
|
|
182
|
+
httpRootProperty: 'data', // { "data": {...} } — or '.' for no wrapper
|
|
183
183
|
locationTemplate: '{"lat":<%= latitude %>,"lng":<%= longitude %>,"ts":<%= timestamp %>,"batt":<%= battery.level %>}',
|
|
184
184
|
});
|
|
185
185
|
```
|
|
186
186
|
|
|
187
|
-
|
|
187
|
+
Template variables: `latitude`, `longitude`, `accuracy`, `speed`, `heading`, `altitude`, `timestamp`, `uuid`, `odometer`, `is_moving`, `activity.type`, `activity.confidence`, `battery.level`, `battery.is_charging`.
|
|
188
188
|
|
|
189
|
-
###
|
|
189
|
+
### Track transmissions
|
|
190
190
|
|
|
191
191
|
```ts
|
|
192
192
|
BackgroundGeolocation.onHttp((response) => {
|
|
@@ -194,27 +194,27 @@ BackgroundGeolocation.onHttp((response) => {
|
|
|
194
194
|
});
|
|
195
195
|
```
|
|
196
196
|
|
|
197
|
-
###
|
|
197
|
+
### Persistence & manual sync
|
|
198
198
|
|
|
199
199
|
```ts
|
|
200
|
-
const count = await BackgroundGeolocation.getCount(); //
|
|
201
|
-
const locations = await BackgroundGeolocation.getLocations(); //
|
|
202
|
-
const uploaded = await BackgroundGeolocation.sync(); //
|
|
203
|
-
await BackgroundGeolocation.destroyLocations(); //
|
|
200
|
+
const count = await BackgroundGeolocation.getCount(); // queued locations
|
|
201
|
+
const locations = await BackgroundGeolocation.getLocations(); // read queue
|
|
202
|
+
const uploaded = await BackgroundGeolocation.sync(); // send now
|
|
203
|
+
await BackgroundGeolocation.destroyLocations(); // empty queue
|
|
204
204
|
```
|
|
205
205
|
|
|
206
|
-
|
|
206
|
+
Retention options: `maxDaysToPersist` (default 1 day), `maxRecordsToPersist`, `persistMode` (`ALL` | `LOCATION` | `GEOFENCE` | `NONE`).
|
|
207
207
|
|
|
208
|
-
###
|
|
208
|
+
### Scheduling
|
|
209
209
|
|
|
210
210
|
```ts
|
|
211
211
|
await BackgroundGeolocation.ready({
|
|
212
|
-
schedule: ['1-5 09:00-17:00', '6 10:00-14:00'], //
|
|
212
|
+
schedule: ['1-5 09:00-17:00', '6 10:00-14:00'], // Mon-Fri 9am-5pm, Sat 10am-2pm
|
|
213
213
|
});
|
|
214
214
|
await BackgroundGeolocation.startSchedule();
|
|
215
215
|
```
|
|
216
216
|
|
|
217
|
-
###
|
|
217
|
+
### Odometer, sensors, state
|
|
218
218
|
|
|
219
219
|
```ts
|
|
220
220
|
const km = (await BackgroundGeolocation.getOdometer()) / 1000;
|
|
@@ -224,7 +224,7 @@ const providerState = await BackgroundGeolocation.getProviderState();
|
|
|
224
224
|
const powerSave = await BackgroundGeolocation.isPowerSaveMode();
|
|
225
225
|
```
|
|
226
226
|
|
|
227
|
-
###
|
|
227
|
+
### JWT auth with automatic refresh (on 401)
|
|
228
228
|
|
|
229
229
|
```ts
|
|
230
230
|
await BackgroundGeolocation.ready({
|
|
@@ -239,15 +239,13 @@ await BackgroundGeolocation.ready({
|
|
|
239
239
|
BackgroundGeolocation.onAuthorization((event) => console.log(event));
|
|
240
240
|
```
|
|
241
241
|
|
|
242
|
-
## API
|
|
242
|
+
## Complete API
|
|
243
243
|
|
|
244
|
-
|
|
244
|
+
Events: `onLocation`, `onMotionChange`, `onActivityChange`, `onGeofence`, `onGeofencesChange`, `onHeartbeat`, `onHttp`, `onProviderChange`, `onConnectivityChange`, `onPowerSaveChange`, `onEnabledChange`, `onSchedule`, `onAuthorization`.
|
|
245
245
|
|
|
246
|
-
|
|
246
|
+
Methods: `ready`, `start`, `stop`, `startGeofences`, `startSchedule`, `stopSchedule`, `changePace`, `getCurrentPosition`, `watchPosition`, `stopWatchPosition`, `getState`, `setConfig`, `reset`, `getLocations`, `getCount`, `insertLocation`, `destroyLocations`, `destroyLocation`, `sync`, `getOdometer`, `setOdometer`, `resetOdometer`, `addGeofence`, `addGeofences`, `removeGeofence`, `removeGeofences`, `getGeofences`, `getGeofence`, `geofenceExists`, `getSensors`, `getDeviceInfo`, `isPowerSaveMode`, `getProviderState`, `requestPermission`, `requestTemporaryFullAccuracy`, `registerHeadlessTask`, `startBackgroundTask`, `stopBackgroundTask`, `getLog`, `destroyLog`, `emailLog`, `removeListeners`.
|
|
247
247
|
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
## App de test
|
|
248
|
+
## Test app
|
|
251
249
|
|
|
252
250
|
```bash
|
|
253
251
|
cd testapp
|
|
@@ -255,6 +253,6 @@ npm install
|
|
|
255
253
|
npx expo start --clear
|
|
256
254
|
```
|
|
257
255
|
|
|
258
|
-
##
|
|
256
|
+
## License
|
|
259
257
|
|
|
260
258
|
MIT
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "expo-background-tracking",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"description": "Background geolocation & geofencing for Expo — battery-conscious motion-detection, SQLite persistence, HTTP sync. Expo Go compatible (foreground), full background in dev builds. No Firebase.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"expo",
|