@wayq/beekon-rn 0.0.3 → 0.0.6

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 (84) hide show
  1. package/BeekonRn.podspec +5 -3
  2. package/CHANGELOG.md +103 -0
  3. package/README.md +326 -52
  4. package/android/build.gradle +9 -4
  5. package/android/src/main/java/in/wayq/beekonrn/BeekonRnModule.kt +411 -59
  6. package/ios/BeekonRn.mm +103 -24
  7. package/ios/BeekonRn.swift +465 -61
  8. package/ios/Frameworks/BeekonKit.xcframework/Info.plist +5 -5
  9. package/ios/Frameworks/BeekonKit.xcframework/ios-arm64/BeekonKit.framework/BeekonKit +0 -0
  10. package/ios/Frameworks/BeekonKit.xcframework/ios-arm64/BeekonKit.framework/Info.plist +0 -0
  11. package/ios/Frameworks/BeekonKit.xcframework/ios-arm64/BeekonKit.framework/Modules/BeekonKit.swiftmodule/arm64-apple-ios.abi.json +11424 -1279
  12. package/ios/Frameworks/BeekonKit.xcframework/ios-arm64/BeekonKit.framework/Modules/BeekonKit.swiftmodule/arm64-apple-ios.swiftdoc +0 -0
  13. package/ios/Frameworks/BeekonKit.xcframework/ios-arm64/BeekonKit.framework/Modules/BeekonKit.swiftmodule/arm64-apple-ios.swiftinterface +262 -36
  14. package/ios/Frameworks/BeekonKit.xcframework/ios-arm64_x86_64-simulator/BeekonKit.framework/BeekonKit +0 -0
  15. package/ios/Frameworks/BeekonKit.xcframework/ios-arm64_x86_64-simulator/BeekonKit.framework/Info.plist +0 -0
  16. package/ios/Frameworks/BeekonKit.xcframework/ios-arm64_x86_64-simulator/BeekonKit.framework/Modules/BeekonKit.swiftmodule/arm64-apple-ios-simulator.abi.json +11424 -1279
  17. package/ios/Frameworks/BeekonKit.xcframework/ios-arm64_x86_64-simulator/BeekonKit.framework/Modules/BeekonKit.swiftmodule/arm64-apple-ios-simulator.swiftdoc +0 -0
  18. package/ios/Frameworks/BeekonKit.xcframework/ios-arm64_x86_64-simulator/BeekonKit.framework/Modules/BeekonKit.swiftmodule/arm64-apple-ios-simulator.swiftinterface +262 -36
  19. package/ios/Frameworks/BeekonKit.xcframework/ios-arm64_x86_64-simulator/BeekonKit.framework/Modules/BeekonKit.swiftmodule/x86_64-apple-ios-simulator.abi.json +11424 -1279
  20. package/ios/Frameworks/BeekonKit.xcframework/ios-arm64_x86_64-simulator/BeekonKit.framework/Modules/BeekonKit.swiftmodule/x86_64-apple-ios-simulator.swiftdoc +0 -0
  21. package/ios/Frameworks/BeekonKit.xcframework/ios-arm64_x86_64-simulator/BeekonKit.framework/Modules/BeekonKit.swiftmodule/x86_64-apple-ios-simulator.swiftinterface +262 -36
  22. package/ios/Frameworks/BeekonKit.xcframework/ios-arm64_x86_64-simulator/BeekonKit.framework/_CodeSignature/CodeResources +2 -80
  23. package/lib/module/NativeBeekonRn.js +35 -7
  24. package/lib/module/NativeBeekonRn.js.map +1 -1
  25. package/lib/module/beekon.js +246 -45
  26. package/lib/module/beekon.js.map +1 -1
  27. package/lib/module/index.js.map +1 -1
  28. package/lib/module/internal/mappers.js +166 -25
  29. package/lib/module/internal/mappers.js.map +1 -1
  30. package/lib/module/types/auth.js +4 -0
  31. package/lib/module/types/auth.js.map +1 -0
  32. package/lib/module/types/config.js +2 -0
  33. package/lib/module/types/enums.js +2 -0
  34. package/lib/module/types/enums.js.map +1 -0
  35. package/lib/module/types/error.js +20 -4
  36. package/lib/module/types/error.js.map +1 -1
  37. package/lib/module/types/geofence.js +2 -0
  38. package/lib/module/types/geofence.js.map +1 -0
  39. package/lib/module/types/location.js +2 -0
  40. package/lib/module/types/sync.js +2 -0
  41. package/lib/module/types/sync.js.map +1 -0
  42. package/lib/typescript/src/NativeBeekonRn.d.ts +150 -20
  43. package/lib/typescript/src/NativeBeekonRn.d.ts.map +1 -1
  44. package/lib/typescript/src/beekon.d.ts +110 -33
  45. package/lib/typescript/src/beekon.d.ts.map +1 -1
  46. package/lib/typescript/src/index.d.ts +6 -2
  47. package/lib/typescript/src/index.d.ts.map +1 -1
  48. package/lib/typescript/src/internal/mappers.d.ts +16 -6
  49. package/lib/typescript/src/internal/mappers.d.ts.map +1 -1
  50. package/lib/typescript/src/types/auth.d.ts +99 -0
  51. package/lib/typescript/src/types/auth.d.ts.map +1 -0
  52. package/lib/typescript/src/types/config.d.ts +66 -20
  53. package/lib/typescript/src/types/config.d.ts.map +1 -1
  54. package/lib/typescript/src/types/enums.d.ts +62 -0
  55. package/lib/typescript/src/types/enums.d.ts.map +1 -0
  56. package/lib/typescript/src/types/error.d.ts +21 -5
  57. package/lib/typescript/src/types/error.d.ts.map +1 -1
  58. package/lib/typescript/src/types/geofence.d.ts +36 -0
  59. package/lib/typescript/src/types/geofence.d.ts.map +1 -0
  60. package/lib/typescript/src/types/location.d.ts +22 -8
  61. package/lib/typescript/src/types/location.d.ts.map +1 -1
  62. package/lib/typescript/src/types/state.d.ts +13 -4
  63. package/lib/typescript/src/types/state.d.ts.map +1 -1
  64. package/lib/typescript/src/types/sync.d.ts +27 -0
  65. package/lib/typescript/src/types/sync.d.ts.map +1 -0
  66. package/package.json +8 -5
  67. package/scripts/fetch-beekonkit.sh +5 -5
  68. package/src/NativeBeekonRn.ts +165 -20
  69. package/src/beekon.ts +278 -48
  70. package/src/index.tsx +24 -2
  71. package/src/internal/mappers.ts +242 -27
  72. package/src/types/auth.ts +101 -0
  73. package/src/types/config.ts +68 -20
  74. package/src/types/enums.ts +80 -0
  75. package/src/types/error.ts +23 -5
  76. package/src/types/geofence.ts +37 -0
  77. package/src/types/location.ts +28 -8
  78. package/src/types/state.ts +13 -3
  79. package/src/types/sync.ts +23 -0
  80. package/ios/Frameworks/BeekonKit.xcframework/_CodeSignature/CodeDirectory +0 -0
  81. package/ios/Frameworks/BeekonKit.xcframework/_CodeSignature/CodeRequirements +0 -0
  82. package/ios/Frameworks/BeekonKit.xcframework/_CodeSignature/CodeResources +0 -296
  83. package/ios/Frameworks/BeekonKit.xcframework/_CodeSignature/CodeSignature +0 -0
  84. package/ios/Frameworks/BeekonKit.xcframework/ios-arm64/BeekonKit.framework/_CodeSignature/CodeResources +0 -146
package/BeekonRn.podspec CHANGED
@@ -10,10 +10,12 @@ Pod::Spec.new do |s|
10
10
  s.license = package["license"]
11
11
  s.authors = package["author"]
12
12
 
13
- # BeekonKit targets iOS 17.0 (uses CLLocationUpdate.liveUpdates). The
14
- # consuming app must also target iOS 17.0 or higher.
13
+ # iOS 17.0 floor reflects this wrapper's React Native baseline (RN 0.85), not
14
+ # a BeekonKit constraint BeekonKit 0.0.6 itself supports iOS 13+ (with
15
+ # 13–16 fallback paths). Kept at 17.0 to avoid any consumer regression; do not
16
+ # lower below React Native's own minimum.
15
17
  s.platforms = { :ios => "17.0" }
16
- s.source = { :git => "https://github.com/wayqteam/beekon.git", :tag => "v#{s.version}" }
18
+ s.source = { :git => "https://github.com/wayqteam/beekon-rn.git", :tag => "v#{s.version}" }
17
19
 
18
20
  s.source_files = "ios/**/*.{h,m,mm,swift,cpp}"
19
21
  # Headers are only for the auto-generated `BeekonRn-Swift.h` interop — keep
package/CHANGELOG.md ADDED
@@ -0,0 +1,103 @@
1
+ # Changelog
2
+
3
+ All notable changes to `@wayq/beekon-rn` are documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+ Beekon is pre-1.0 — releases may contain breaking changes.
8
+
9
+ ## [0.0.6]
10
+
11
+ Built against the native **0.0.6** API; requires native ≥ 0.0.6 at runtime.
12
+
13
+ ### Added
14
+
15
+ - **Native token refresh** — `SyncConfig.auth` (`AuthConfig`) lets the SDK attach
16
+ and natively refresh the upload access token, proactively before expiry and
17
+ reactively on a `401`/`403`, and retry the upload — all natively, so it works in
18
+ the background and on a cold launch with no host involvement. Rotated credentials
19
+ surface on the new `Beekon.onAuthTokens()` subscription (`AuthTokens`). Adds the
20
+ `AuthConfig`, `AuthResponseMapping`, `AuthTokens` types and the `AuthStrategy` /
21
+ `AuthBodyFormat` enums. Omitting `auth` keeps the legacy static-`headers`
22
+ behaviour. The refresh recipe is persisted in plaintext to survive cold launch —
23
+ do not put static client secrets in `refreshHeaders`/`refreshPayload`.
24
+ - **`getCurrentLocation({ timeoutMs, accuracy })`** — a one-shot current location,
25
+ independent of any tracking session (resolves `null` on timeout; never starts or
26
+ stops tracking; the fix is not persisted and does not appear on `onLocation`).
27
+ Re-introduces thrown `BeekonError` kinds `'permissionDenied'`,
28
+ `'locationServicesDisabled'`, and `'locationUnavailable'` on a precondition
29
+ failure (0.0.5 had removed the `PERMISSION_DENIED` / `LOCATION_SERVICES_DISABLED`
30
+ thrown errors; they now surface again, but only from `getCurrentLocation()` —
31
+ tracking-lifecycle problems still report through `onState` as `Stopped(reason)`).
32
+ - **`NotificationConfig.smallIcon`** — a custom Android status-bar icon (drawable /
33
+ mipmap resource name) for the foreground-service tracking notification.
34
+
35
+ ### Changed
36
+
37
+ - `androidNotification` (type `AndroidNotificationConfig`) renamed to `notification`
38
+ (type `NotificationConfig`) — **breaking**. The config remains Android-only; iOS
39
+ ignores it.
40
+ - `deleteLocations(before)` cutoff is now strictly **before** `before` (exclusive
41
+ `<`), aligning both natives and all wrappers; the prior wording said "at or
42
+ before".
43
+ - `resumeIfNeeded()` now calls the guarded native `Beekon.resumeIfNeeded()` on
44
+ Android (previously `Beekon.start()`): it re-adopts a previously-active,
45
+ non-user-stopped session only, mirroring iOS.
46
+ - Native pins bumped to `0.0.6` (Maven `io.github.wayqteam:beekon`, iOS
47
+ `BeekonKit.xcframework`).
48
+
49
+ ## [0.0.5]
50
+
51
+ Full rebuild against the native **0.0.5** API — a breaking, no-backward-compat
52
+ release. The TypeScript surface is a faithful 1:1 mirror of the native `Beekon`
53
+ (Android) / `Beekon.shared` (iOS).
54
+
55
+ ### Added
56
+
57
+ - **Server sync**: `SyncConfig` on `BeekonConfig`, plus `sync()`, `setExtras()`,
58
+ `pendingUploadCount()`, and the `onSyncStatus` subscription
59
+ (`SyncIdle`/`SyncPending`/`SyncFailed`).
60
+ - **Geofencing**: `addGeofences()` / `removeGeofences()` / `listGeofences()` and
61
+ the `onGeofenceEvent` subscription (`BeekonGeofence`, `GeofenceEvent`,
62
+ `Transition`).
63
+ - **Richer config**: `accuracyMode`, `whenStationary`, `stationaryRadiusMeters`,
64
+ `detectActivity`.
65
+ - **Richer `Location`**: `id`, `quality`, `trigger`, `motion`, `activity`,
66
+ `isMock`.
67
+ - `deleteLocations(before)`, `resumeIfNeeded()` (wrapper-only cold-launch resume,
68
+ not part of the native spec), and `StopReason 'locationUnavailable'`.
69
+
70
+ ### Changed
71
+
72
+ - `intervalSeconds` → `minTimeBetweenLocationsSeconds`; `distanceMeters` →
73
+ `minDistanceBetweenLocationsMeters`.
74
+ - `getLocations(from: Date, to: Date)` replaces the prior history call.
75
+ - Notification config is now `androidNotification` (Android-only; iOS ignores it).
76
+ - Native pins bumped to `0.0.5` (Maven `io.github.wayqteam:beekon`, SwiftPM
77
+ `beekon-ios-binary`).
78
+
79
+ ### Removed
80
+
81
+ - `PERMISSION_DENIED` / `LOCATION_SERVICES_DISABLED` thrown errors — permission
82
+ and service problems now surface only on `onState` as `Stopped(reason)`.
83
+
84
+ ## [0.0.3]
85
+
86
+ First synchronized release across all four registries (Maven Central,
87
+ `wayqteam/beekon-ios-binary` xcframework, pub.dev, npm). `Location`
88
+ accuracy/speed/bearing/altitude are nullable to faithfully represent providers
89
+ that don't deliver them.
90
+
91
+ ## [0.0.1]
92
+
93
+ Initial release.
94
+
95
+ ### Added
96
+
97
+ - React Native (New Architecture / TurboModule) binding for the native Beekon
98
+ location SDKs (Android `io.github.wayqteam:beekon`, iOS `BeekonKit`
99
+ xcframework).
100
+ - Public API mirroring the native surface: `Beekon.configure / start / stop /
101
+ getLocations`, plus `onState` / `onLocation` subscriptions.
102
+ - `BeekonConfig`, `BeekonState` (`idle → tracking → stopped(reason)`),
103
+ `Location`, and typed `BeekonError` kinds.
package/README.md CHANGED
@@ -1,43 +1,70 @@
1
- # @wayq/beekon-rn
1
+ <p align="center">
2
+ <img src="assets/icon.png" alt="Beekon" width="140" />
3
+ </p>
2
4
 
3
- React Native binding for the [Beekon](https://github.com/wayqteam/beekon) location SDK.
5
+ <h1 align="center">@wayq/beekon-rn</h1>
4
6
 
5
- A thin pass-through over the native Android (`io.github.wayqteam:beekon`) and iOS (`BeekonKit`) libraries. The public TypeScript surface mirrors the Kotlin / Swift APIs in shape: `configure / start / stop`, callback-backed `state` and `locations` streams, and a `history(from, to)` query.
7
+ <p align="center">
8
+ Background location tracking for React Native.<br/>
9
+ Native location stack on each platform — tracking survives backgrounding and termination.
10
+ </p>
6
11
 
7
- Built on the React Native New Architecture (TurboModules + Codegen). The binding is a thin delegation layer — all logic, persistence, and OS integration lives natively. **Writes never cross into JavaScript**: in background the JS engine isn't guaranteed to be alive, so the native libraries own the persistence path end-to-end.
12
+ <p align="center">
13
+ <a href="https://www.npmjs.com/package/@wayq/beekon-rn"><img alt="npm" src="https://img.shields.io/npm/v/@wayq/beekon-rn?label=npm&logo=npm&logoColor=white&color=CB3837&style=flat-square"></a>
14
+ <img alt="React Native · New Architecture" src="https://img.shields.io/badge/React%20Native-New%20Arch-61DAFB?logo=react&logoColor=white&style=flat-square">
15
+ <img alt="Platforms · Android | iOS" src="https://img.shields.io/badge/platforms-Android%20%7C%20iOS-61DAFB?style=flat-square">
16
+ </p>
17
+
18
+ <p align="center">
19
+ <a href="https://docs.getbeekon.com"><strong>Docs</strong></a> ·
20
+ <a href="https://console.getbeekon.com"><strong>Console</strong></a> ·
21
+ <a href="https://getbeekon.com"><strong>getbeekon.com</strong></a> ·
22
+ <a href="https://github.com/wayqteam/beekon"><strong>Umbrella</strong></a>
23
+ </p>
24
+
25
+ ---
26
+
27
+ Capture GPS in the foreground and background, keep a queryable history on the device, define geofences, and optionally sync to your own server. The JavaScript API is fully typed.
28
+
29
+ ## Features
30
+
31
+ - Foreground and background tracking that survives app termination
32
+ - Battery-aware capture: time and distance filtering, accuracy presets, and automatic pausing while the device is stationary
33
+ - On-device history you can query at any time
34
+ - Geofencing with enter and exit events
35
+ - Optional batched server sync with automatic retry
36
+ - Activity detection (walking, running, cycling, automotive)
37
+ - Built for the React Native New Architecture
8
38
 
9
39
  ## Requirements
10
40
 
11
- | Area | Floor |
41
+ | | Minimum |
12
42
  |---|---|
13
- | React Native | 0.76+ (target 0.85) |
43
+ | React Native | 0.76 (New Architecture) |
14
44
  | iOS | 17.0 |
15
- | Android | API 26 |
16
- | New Architecture | required (Old Arch is unsupported) |
45
+ | Android | 8.0 (API level 26) |
17
46
 
18
- ## Install
47
+ ## Installation
19
48
 
20
49
  ```sh
21
50
  npm install @wayq/beekon-rn
22
- # or
23
- yarn add @wayq/beekon-rn
24
51
  ```
25
52
 
26
- iOS:
53
+ **iOS** — install pods:
27
54
 
28
55
  ```sh
29
56
  cd ios && pod install
30
57
  ```
31
58
 
32
- The package bundles `BeekonKit.xcframework` directly no manual SwiftPM setup needed.
59
+ **Android** no additional steps; the module links automatically.
33
60
 
34
- Android: Maven Central is auto-included by the autolinker. The native AAR (`io.github.wayqteam:beekon`) is pulled transitively.
61
+ ## Permissions and setup
35
62
 
36
- ## Permissions
63
+ Beekon does not request permissions on your behalf. Declare them in your app and request them at runtime (for example with `PermissionsAndroid` or `react-native-permissions`).
37
64
 
38
- The library does NOT request permissions itself — your app must. Use a library like [`react-native-permissions`](https://github.com/zoontek/react-native-permissions) or platform APIs.
65
+ ### Android
39
66
 
40
- **Android** (`AndroidManifest.xml`):
67
+ In `android/app/src/main/AndroidManifest.xml`:
41
68
 
42
69
  ```xml
43
70
  <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
@@ -45,72 +72,319 @@ The library does NOT request permissions itself — your app must. Use a library
45
72
  <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
46
73
  <uses-permission android:name="android.permission.FOREGROUND_SERVICE_LOCATION" />
47
74
  <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
75
+ <!-- Only if you enable detectActivity -->
76
+ <uses-permission android:name="android.permission.ACTIVITY_RECOGNITION" />
48
77
  ```
49
78
 
50
- **iOS** (`Info.plist`):
79
+ While tracking, Android shows a persistent notification (required by the system). Set its title and text with the `notification` option.
80
+
81
+ ### iOS
82
+
83
+ In `Info.plist`:
51
84
 
52
85
  ```xml
53
86
  <key>NSLocationWhenInUseUsageDescription</key>
54
- <string>...</string>
87
+ <string>Explain why your app uses location.</string>
55
88
  <key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
56
- <string>...</string>
89
+ <string>Explain why your app uses location in the background.</string>
57
90
  <key>UIBackgroundModes</key>
58
- <array><string>location</string></array>
91
+ <array>
92
+ <string>location</string>
93
+ <string>fetch</string>
94
+ </array>
95
+ <key>BGTaskSchedulerPermittedIdentifiers</key>
96
+ <array>
97
+ <string>in.wayq.beekon.sync</string>
98
+ </array>
99
+ <!-- Only if you enable detectActivity -->
100
+ <key>NSMotionUsageDescription</key>
101
+ <string>Explain why your app detects activity.</string>
102
+ ```
103
+
104
+ Register Beekon's background task once during launch (required for background sync and to resume tracking after the app is terminated). In `AppDelegate.swift`:
105
+
106
+ ```swift
107
+ import BeekonRn
108
+
109
+ func application(
110
+ _ application: UIApplication,
111
+ didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil
112
+ ) -> Bool {
113
+ BeekonRnImpl.registerBackgroundTasks()
114
+ // ...your existing setup...
115
+ return true
116
+ }
59
117
  ```
60
118
 
61
119
  ## Usage
62
120
 
121
+ ### Start tracking
122
+
63
123
  ```ts
64
- import { Beekon, type BeekonState, type Location } from '@wayq/beekon-rn';
124
+ import { Beekon } from '@wayq/beekon-rn';
65
125
 
66
- // Subscribe returns an unsubscribe function. State replays the latest value
67
- // to new subscribers; locations is broadcast (no replay).
68
- const offState = Beekon.onState((s: BeekonState) => console.log('state', s));
69
- const offLoc = Beekon.onLocation((l: Location) => console.log('location', l));
126
+ const offState = Beekon.onState((state) => {
127
+ console.log('state:', state.kind); // 'idle' | 'tracking' | 'stopped'
128
+ });
70
129
 
71
- // Configure. Defaults: 30 s / 100 m. Pass 0 to disable a gate.
130
+ const offLocation = Beekon.onLocation((location) => {
131
+ console.log(location.latitude, location.longitude, location.timestamp);
132
+ });
133
+
134
+ // Configure is optional. Request OS permissions before starting.
72
135
  await Beekon.configure({
73
- intervalSeconds: 10,
74
- distanceMeters: 30,
75
- androidNotification: {
76
- title: 'Tracking',
77
- text: 'Recording your route',
78
- smallIconResName: 'ic_notification', // drawable in your app
79
- },
136
+ minTimeBetweenLocationsSeconds: 30,
137
+ minDistanceBetweenLocationsMeters: 100,
80
138
  });
81
139
 
82
140
  await Beekon.start();
83
- // ... later
141
+
142
+ // Later:
84
143
  await Beekon.stop();
144
+ offState();
145
+ offLocation();
146
+ ```
85
147
 
86
- // Read past fixes.
87
- const fixes = await Beekon.history(new Date(Date.now() - 3600_000), new Date());
148
+ `start()` and `stop()` never throw. Observe `onState` to learn whether tracking began and why it stopped:
88
149
 
89
- // Cleanup.
90
- offState();
91
- offLoc();
150
+ ```ts
151
+ Beekon.onState((state) => {
152
+ if (state.kind === 'stopped') {
153
+ // 'user' | 'permissionDenied' | 'locationServicesDisabled'
154
+ // | 'locationUnavailable' | 'system'
155
+ console.log('stopped:', state.reason);
156
+ }
157
+ });
158
+ ```
159
+
160
+ ### Read history
161
+
162
+ Recorded fixes are stored on the device and remain available even after the app is closed.
163
+
164
+ ```ts
165
+ const since = new Date(Date.now() - 24 * 60 * 60 * 1000);
166
+ const fixes = await Beekon.getLocations(since, new Date());
167
+
168
+ await Beekon.deleteLocations(); // delete everything
169
+ await Beekon.deleteLocations(since); // delete up to a cutoff
92
170
  ```
93
171
 
172
+ ### Geofences
173
+
174
+ ```ts
175
+ await Beekon.addGeofences([
176
+ { id: 'office', latitude: 37.331, longitude: -122.031, radiusMeters: 150 },
177
+ ]);
178
+
179
+ const offGeofence = Beekon.onGeofenceEvent((event) => {
180
+ console.log(event.geofenceId, event.type); // 'enter' | 'exit'
181
+ });
182
+
183
+ await Beekon.listGeofences();
184
+ await Beekon.removeGeofences(['office']);
185
+ ```
186
+
187
+ ### Server sync
188
+
189
+ Add a `sync` config to upload recorded fixes and geofence events to your backend. Uploads are batched, retried automatically, and removed from the device once your server accepts them. Without a `sync` config, all data stays on the device.
190
+
191
+ ```ts
192
+ await Beekon.configure({
193
+ sync: {
194
+ url: 'https://api.example.com/locations',
195
+ headers: { Authorization: 'Bearer <token>' },
196
+ // intervalSeconds defaults to 300, batchSize to 100
197
+ },
198
+ });
199
+
200
+ Beekon.setExtras({ userId: 'abc123' }); // attached to every upload
201
+
202
+ Beekon.onSyncStatus((status) => {
203
+ console.log('sync:', status.kind); // 'idle' | 'pending' | 'failed'
204
+ });
205
+ ```
206
+
207
+ #### Token refresh
208
+
209
+ Set `sync.auth` (an `AuthConfig`) to let the SDK attach and **natively** refresh the upload access token — proactively before it expires and reactively on a `401`/`403`, then retry the upload. This runs in the background and survives a cold launch with no host involvement. Without `auth`, a `401`/`403` pauses sync and surfaces `SyncFailure 'auth'`; re-seed by calling `configure()` again to resume.
210
+
211
+ The supplied tokens are a **seed**: the SDK owns and rotates the live token set in secure storage (Keychain / encrypted prefs) after first persist. Subscribe with `onAuthTokens` to mirror rotations into your own session store.
212
+
213
+ ```ts
214
+ await Beekon.configure({
215
+ sync: {
216
+ url: 'https://api.example.com/locations',
217
+ auth: {
218
+ accessToken: '<initial access token>',
219
+ refreshToken: '<initial refresh token>',
220
+ expiresAt: new Date(Date.now() + 3600_000),
221
+ refreshUrl: 'https://auth.example.com/oauth/token',
222
+ refreshPayload: {
223
+ grant_type: 'refresh_token',
224
+ refresh_token: '{refreshToken}', // substituted with the current token
225
+ },
226
+ // strategy defaults to 'bearer', refreshBodyFormat to 'form',
227
+ // skewMarginSeconds to 60. responseMapping defaults to common-name detection.
228
+ },
229
+ },
230
+ });
231
+
232
+ const offAuth = Beekon.onAuthTokens((tokens) => {
233
+ // Persist the rotated set into your own session store.
234
+ console.log('rotated:', tokens.accessToken, tokens.expiresAt, tokens.epoch);
235
+ });
236
+ ```
237
+
238
+ The refresh recipe is persisted in plaintext to survive a cold launch — **do not** put static client secrets in `refreshHeaders` or `refreshPayload`. Requires the native SDK ≥ 0.0.6; with an older native binary the recipe is ignored and only static `sync.headers` apply.
239
+
240
+ ## Configuration
241
+
242
+ All options are optional; defaults are shown.
243
+
244
+ | Option | Default | Description |
245
+ |---|---|---|
246
+ | `minTimeBetweenLocationsSeconds` | `30` | Minimum seconds between recorded fixes. `0` disables the time filter. |
247
+ | `minDistanceBetweenLocationsMeters` | `100` | Minimum metres between recorded fixes. `0` disables the distance filter. |
248
+ | `accuracyMode` | `'balanced'` | `'high'` \| `'balanced'` \| `'low'`. Trades accuracy against battery. |
249
+ | `whenStationary` | `'pause'` | `'keepTracking'` \| `'pause'` \| `'pauseWithCheckIns'`. |
250
+ | `stationaryRadiusMeters` | `5` | Distance the device must move to count as moving again. |
251
+ | `detectActivity` | `false` | Detect physical activity. Requires the motion permission. |
252
+ | `sync` | – | Server upload settings: `{ url, headers?, intervalSeconds?, batchSize?, auth? }`. See [Token refresh](#token-refresh). |
253
+ | `notification` | – | Android-only tracking notification: `{ title?, text?, smallIcon? }`. `smallIcon` is a drawable/mipmap resource name. |
254
+
255
+ A fix is recorded only when **both** the time and distance filters are satisfied.
256
+
94
257
  ## API
95
258
 
96
259
  | Method | Description |
97
260
  |---|---|
98
- | `Beekon.configure(config)` | Set sampling thresholds. Optional. |
99
- | `Beekon.start()` | Begin tracking. State → `tracking`. |
100
- | `Beekon.stop()` | Stop tracking. Idempotent. State → `stopped` (`reason: 'user'`). |
101
- | `Beekon.history(from, to)` | Read persisted fixes in range. |
102
- | `Beekon.onState(cb)` | Subscribe to state. Returns unsubscribe fn. |
103
- | `Beekon.onLocation(cb)` | Subscribe to gated fixes. Returns unsubscribe fn. |
261
+ | `configure(config)` | Apply settings. Optional; may be called while tracking. |
262
+ | `start()` | Begin tracking. |
263
+ | `stop()` | Stop tracking. |
264
+ | `resumeIfNeeded()` | Resume a session that was active before the app closed. |
265
+ | `getCurrentLocation(options?)` | One-shot current fix, independent of tracking. Resolves `null` on timeout. `options`: `{ timeoutMs?, accuracy? }`. |
266
+ | `getLocations(from, to)` | Recorded fixes within a date range. |
267
+ | `deleteLocations(before?)` | Delete fixes (all if `before` is omitted); returns the count removed. |
268
+ | `pendingUploadCount()` | Number of fixes not yet uploaded. |
269
+ | `sync()` | Request an immediate upload. |
270
+ | `setExtras(extras)` | Custom fields included with every upload. |
271
+ | `addGeofences(geofences)` | Register circular regions. |
272
+ | `removeGeofences(ids)` | Unregister geofences by id. |
273
+ | `listGeofences()` | Currently registered geofences. |
274
+
275
+ Subscriptions — each returns an unsubscribe function:
276
+
277
+ | Subscription | Delivers |
278
+ |---|---|
279
+ | `onState(cb)` | Tracking state changes. The current state is delivered immediately. |
280
+ | `onLocation(cb)` | Each newly recorded fix. |
281
+ | `onGeofenceEvent(cb)` | Geofence enter and exit events. |
282
+ | `onSyncStatus(cb)` | Upload status. The current status is delivered immediately. |
283
+ | `onAuthTokens(cb)` | Token rotations from native refresh (see [Token refresh](#token-refresh)). The latest rotation is delivered immediately. |
104
284
 
105
- State is a discriminated union on `kind`: `'idle' | 'tracking' | 'stopped'`. The `'stopped'` variant carries a `reason: 'user' | 'permissionDenied' | 'locationServicesDisabled' | 'system'` (`'system'` is Android-only).
285
+ `getLocations`, `deleteLocations`, `pendingUploadCount`, and `addGeofences` reject with a `BeekonError` on failure check `error.kind` (`'storage'` or `'invalidGeofence'`). `getCurrentLocation` rejects with `error.kind` `'permissionDenied'`, `'locationServicesDisabled'`, or `'locationUnavailable'` on a precondition failure. Other methods resolve normally; tracking-lifecycle problems are reported through `onState` rather than thrown.
106
286
 
107
- Errors are thrown as `BeekonError` instances with `kind`:
108
- `'permissionDenied'`, `'locationServicesDisabled'`, `'storageFailure'`.
287
+ ## Types
109
288
 
110
- ## Storage and retention
289
+ ```ts
290
+ type Location = {
291
+ id: string;
292
+ latitude: number;
293
+ longitude: number;
294
+ timestamp: Date;
295
+ accuracy: number | null; // metres
296
+ speed: number | null; // m/s
297
+ bearing: number | null; // degrees from true north
298
+ altitude: number | null; // metres
299
+ quality: 'ok' | 'lowAccuracy' | 'implausibleSpeed';
300
+ trigger: 'interval' | 'motion' | 'checkIn' | 'geofence' | 'manual';
301
+ motion: 'moving' | 'stationary' | 'unknown';
302
+ activity:
303
+ | 'stationary' | 'walking' | 'running'
304
+ | 'cycling' | 'automotive' | 'unknown' | null;
305
+ isMock: boolean;
306
+ };
307
+
308
+ type BeekonState =
309
+ | { kind: 'idle' }
310
+ | { kind: 'tracking' }
311
+ | {
312
+ kind: 'stopped';
313
+ reason:
314
+ | 'user' | 'permissionDenied' | 'locationServicesDisabled'
315
+ | 'locationUnavailable' | 'system';
316
+ };
317
+
318
+ type SyncStatus =
319
+ | { kind: 'idle' }
320
+ | { kind: 'pending' }
321
+ | { kind: 'failed'; reason: 'auth' | 'rejected' };
322
+
323
+ type BeekonGeofence = {
324
+ id: string;
325
+ latitude: number;
326
+ longitude: number;
327
+ radiusMeters: number;
328
+ notifyOnEntry?: boolean; // default true
329
+ notifyOnExit?: boolean; // default true
330
+ };
331
+
332
+ type GeofenceEvent = {
333
+ id: string;
334
+ geofenceId: string;
335
+ type: 'enter' | 'exit';
336
+ timestamp: Date;
337
+ };
338
+
339
+ // Set on `SyncConfig.auth` to enable native token refresh. The token fields are
340
+ // a seed — the SDK rotates them after first persist.
341
+ type AuthConfig = {
342
+ accessToken?: string;
343
+ refreshToken?: string;
344
+ expiresAt?: Date;
345
+ strategy?: 'bearer' | 'raw'; // default 'bearer'
346
+ refreshUrl?: string; // omit to disable refresh
347
+ refreshPayload?: Record<string, string>; // '{refreshToken}' substituted
348
+ refreshHeaders?: Record<string, string>; // '{accessToken}' substituted
349
+ refreshBodyFormat?: 'form' | 'json'; // default 'form'
350
+ responseMapping?: {
351
+ accessToken?: string;
352
+ refreshToken?: string;
353
+ expiresIn?: string;
354
+ expiresAt?: string;
355
+ };
356
+ skewMarginSeconds?: number; // default 60
357
+ seedEpoch?: number;
358
+ };
359
+
360
+ // Delivered by `onAuthTokens` after each native rotation. Sensitive — in-process
361
+ // only, never logged.
362
+ type AuthTokens = {
363
+ accessToken: string;
364
+ refreshToken: string | null;
365
+ expiresAt: Date | null;
366
+ epoch: number; // monotonic generation, bumped on each refresh
367
+ };
368
+ ```
369
+
370
+ Optional numeric fields on `Location` are `null` when the device did not report them — never `0`.
371
+
372
+ ## Notes
111
373
 
112
- The native SDKs persist every gated fix locally (Room on Android, GRDB on iOS) JS is a passive reader. Retention: **TTL 7 days OR most recent 100 K rows**, whichever is smaller; auto-pruned on each write batch.
374
+ - **Retention.** Fixes are kept on the device for up to 7 days, or the most recent 100,000 fixes, whichever comes first.
375
+ - **Background relaunch.** Call `resumeIfNeeded()` early in your app to restore tracking after the system terminates it. On Android, also resume from your `Application.onCreate` (see the example app).
376
+ - **Android background limits.** Some manufacturers aggressively stop background services; if tracking halts unexpectedly, see [dontkillmyapp.com](https://dontkillmyapp.com).
377
+
378
+ ## Example
379
+
380
+ A complete, runnable example is in the [`example/`](./example) directory.
113
381
 
114
382
  ## License
115
383
 
116
- Proprietary — see [LICENSE.txt](LICENSE.txt). Evaluation use only without a separate written agreement with wayqteam.
384
+ Proprietary — evaluation use only without a separate written agreement with wayqteam. See [LICENSE.txt](./LICENSE.txt).
385
+
386
+ ---
387
+
388
+ <p align="center">
389
+ © WayQ Technologies Pvt Ltd
390
+ </p>
@@ -1,7 +1,8 @@
1
1
  buildscript {
2
2
  ext.BeekonRn = [
3
3
  kotlinVersion: "2.1.20",
4
- minSdkVersion: 24,
4
+ // minSdk 26 is required by the native Beekon AAR (io.github.wayqteam:beekon).
5
+ minSdkVersion: 26,
5
6
  compileSdkVersion: 36,
6
7
  targetSdkVersion: 36
7
8
  ]
@@ -57,8 +58,12 @@ android {
57
58
  }
58
59
 
59
60
  compileOptions {
60
- sourceCompatibility JavaVersion.VERSION_1_8
61
- targetCompatibility JavaVersion.VERSION_1_8
61
+ sourceCompatibility JavaVersion.VERSION_17
62
+ targetCompatibility JavaVersion.VERSION_17
63
+ }
64
+
65
+ kotlinOptions {
66
+ jvmTarget = "17"
62
67
  }
63
68
  }
64
69
 
@@ -72,7 +77,7 @@ dependencies {
72
77
  // Native Beekon SDK — published from beekon-android/ via Maven Central.
73
78
  // Pinned exact in v0.x to avoid surprise breakage; loosen to a range when
74
79
  // the SDK reaches v1 stability.
75
- implementation "io.github.wayqteam:beekon:0.0.3"
80
+ implementation "io.github.wayqteam:beekon:0.0.6"
76
81
  // Kotlin coroutines — required for collecting Beekon's StateFlow/SharedFlow.
77
82
  // Beekon already depends on coroutines transitively, but declaring it here
78
83
  // makes the dependency intent explicit.