@wayq/beekon-rn 0.0.5 → 0.0.7
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/BeekonRn.podspec +4 -2
- package/CHANGELOG.md +127 -0
- package/README.md +303 -81
- package/android/build.gradle +3 -2
- package/android/src/main/java/in/wayq/beekonrn/BeekonRnModule.kt +164 -7
- package/ios/BeekonRn.mm +23 -0
- package/ios/BeekonRn.swift +199 -10
- package/ios/Frameworks/BeekonKit.xcframework/Info.plist +5 -5
- package/ios/Frameworks/BeekonKit.xcframework/ios-arm64/BeekonKit.framework/BeekonKit +0 -0
- package/ios/Frameworks/BeekonKit.xcframework/ios-arm64/BeekonKit.framework/Info.plist +0 -0
- package/ios/Frameworks/BeekonKit.xcframework/ios-arm64/BeekonKit.framework/Modules/BeekonKit.swiftmodule/arm64-apple-ios.abi.json +7784 -2697
- package/ios/Frameworks/BeekonKit.xcframework/ios-arm64/BeekonKit.framework/Modules/BeekonKit.swiftmodule/arm64-apple-ios.swiftdoc +0 -0
- package/ios/Frameworks/BeekonKit.xcframework/ios-arm64/BeekonKit.framework/Modules/BeekonKit.swiftmodule/arm64-apple-ios.swiftinterface +111 -3
- package/ios/Frameworks/BeekonKit.xcframework/ios-arm64_x86_64-simulator/BeekonKit.framework/BeekonKit +0 -0
- package/ios/Frameworks/BeekonKit.xcframework/ios-arm64_x86_64-simulator/BeekonKit.framework/Info.plist +0 -0
- package/ios/Frameworks/BeekonKit.xcframework/ios-arm64_x86_64-simulator/BeekonKit.framework/Modules/BeekonKit.swiftmodule/arm64-apple-ios-simulator.abi.json +7784 -2697
- package/ios/Frameworks/BeekonKit.xcframework/ios-arm64_x86_64-simulator/BeekonKit.framework/Modules/BeekonKit.swiftmodule/arm64-apple-ios-simulator.swiftdoc +0 -0
- package/ios/Frameworks/BeekonKit.xcframework/ios-arm64_x86_64-simulator/BeekonKit.framework/Modules/BeekonKit.swiftmodule/arm64-apple-ios-simulator.swiftinterface +111 -3
- package/ios/Frameworks/BeekonKit.xcframework/ios-arm64_x86_64-simulator/BeekonKit.framework/Modules/BeekonKit.swiftmodule/x86_64-apple-ios-simulator.abi.json +7784 -2697
- package/ios/Frameworks/BeekonKit.xcframework/ios-arm64_x86_64-simulator/BeekonKit.framework/Modules/BeekonKit.swiftmodule/x86_64-apple-ios-simulator.swiftdoc +0 -0
- package/ios/Frameworks/BeekonKit.xcframework/ios-arm64_x86_64-simulator/BeekonKit.framework/Modules/BeekonKit.swiftmodule/x86_64-apple-ios-simulator.swiftinterface +111 -3
- package/ios/Frameworks/BeekonKit.xcframework/ios-arm64_x86_64-simulator/BeekonKit.framework/_CodeSignature/CodeResources +1 -1
- package/lib/module/NativeBeekonRn.js +20 -0
- package/lib/module/NativeBeekonRn.js.map +1 -1
- package/lib/module/beekon.js +90 -7
- package/lib/module/beekon.js.map +1 -1
- package/lib/module/index.js.map +1 -1
- package/lib/module/internal/mappers.js +98 -4
- package/lib/module/internal/mappers.js.map +1 -1
- package/lib/module/types/auth.js +4 -0
- package/lib/module/types/auth.js.map +1 -0
- package/lib/module/types/error.js +13 -3
- package/lib/module/types/error.js.map +1 -1
- package/lib/module/types/license.js +2 -0
- package/lib/module/types/license.js.map +1 -0
- package/lib/typescript/src/NativeBeekonRn.d.ts +84 -0
- package/lib/typescript/src/NativeBeekonRn.d.ts.map +1 -1
- package/lib/typescript/src/beekon.d.ts +45 -1
- package/lib/typescript/src/beekon.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +3 -1
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/internal/mappers.d.ts +12 -1
- package/lib/typescript/src/internal/mappers.d.ts.map +1 -1
- package/lib/typescript/src/types/auth.d.ts +99 -0
- package/lib/typescript/src/types/auth.d.ts.map +1 -0
- package/lib/typescript/src/types/config.d.ts +29 -0
- package/lib/typescript/src/types/config.d.ts.map +1 -1
- package/lib/typescript/src/types/enums.d.ts +14 -0
- package/lib/typescript/src/types/enums.d.ts.map +1 -1
- package/lib/typescript/src/types/error.d.ts +14 -4
- package/lib/typescript/src/types/error.d.ts.map +1 -1
- package/lib/typescript/src/types/license.d.ts +50 -0
- package/lib/typescript/src/types/license.d.ts.map +1 -0
- package/package.json +10 -2
- package/scripts/fetch-beekonkit.sh +4 -4
- package/src/NativeBeekonRn.ts +93 -0
- package/src/beekon.ts +104 -6
- package/src/index.tsx +4 -0
- package/src/internal/mappers.ts +109 -1
- package/src/types/auth.ts +101 -0
- package/src/types/config.ts +29 -0
- package/src/types/enums.ts +16 -0
- package/src/types/error.ts +19 -4
- package/src/types/license.ts +47 -0
package/BeekonRn.podspec
CHANGED
|
@@ -10,8 +10,10 @@ Pod::Spec.new do |s|
|
|
|
10
10
|
s.license = package["license"]
|
|
11
11
|
s.authors = package["author"]
|
|
12
12
|
|
|
13
|
-
#
|
|
14
|
-
#
|
|
13
|
+
# iOS 17.0 floor reflects this wrapper's React Native baseline (RN 0.85), not
|
|
14
|
+
# a BeekonKit constraint — BeekonKit 0.0.7 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
18
|
s.source = { :git => "https://github.com/wayqteam/beekon-rn.git", :tag => "v#{s.version}" }
|
|
17
19
|
|
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
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.7]
|
|
10
|
+
|
|
11
|
+
Built against the native **0.0.7** API; requires native ≥ 0.0.7 at runtime.
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
|
|
15
|
+
- **License surface** (`license-format-v1`) — a pure pass-through to the native
|
|
16
|
+
verifier; the binding performs no validation of its own.
|
|
17
|
+
- `BeekonConfig.licenseKey` — supply a license token (a `license-format-v1`
|
|
18
|
+
JWS). The highest-priority channel, overriding the Android manifest
|
|
19
|
+
(`in.wayq.beekon.license`) / iOS `Info.plist` (`BeekonLicenseKey`) value;
|
|
20
|
+
`undefined`/blank means unset. Safe to commit to source control (app-id- and
|
|
21
|
+
product-bound).
|
|
22
|
+
- `Beekon.licenseStatus()` (one-shot) and `Beekon.onLicenseStatus(cb)`
|
|
23
|
+
(replay-1 subscription) expose the current platform's status: the
|
|
24
|
+
discriminated `LicenseStatus` union (`notDetermined` / `licensed` /
|
|
25
|
+
`evaluation` / `expired` / `updateEntitlementLapsed` / `invalid`) plus
|
|
26
|
+
`LicenseInvalidReason`. Purely observational — no status ever blocks,
|
|
27
|
+
degrades, or delays the SDK; Android and iOS may legally diverge for the
|
|
28
|
+
same app.
|
|
29
|
+
- The binding identifies itself to the verifier as the `rn` product.
|
|
30
|
+
- Native pins bumped to `0.0.7` (Maven `io.github.wayqteam:beekon`, `BeekonKit`
|
|
31
|
+
xcframework).
|
|
32
|
+
|
|
33
|
+
## [0.0.6]
|
|
34
|
+
|
|
35
|
+
Built against the native **0.0.6** API; requires native ≥ 0.0.6 at runtime.
|
|
36
|
+
|
|
37
|
+
### Added
|
|
38
|
+
|
|
39
|
+
- **Native token refresh** — `SyncConfig.auth` (`AuthConfig`) lets the SDK attach
|
|
40
|
+
and natively refresh the upload access token, proactively before expiry and
|
|
41
|
+
reactively on a `401`/`403`, and retry the upload — all natively, so it works in
|
|
42
|
+
the background and on a cold launch with no host involvement. Rotated credentials
|
|
43
|
+
surface on the new `Beekon.onAuthTokens()` subscription (`AuthTokens`). Adds the
|
|
44
|
+
`AuthConfig`, `AuthResponseMapping`, `AuthTokens` types and the `AuthStrategy` /
|
|
45
|
+
`AuthBodyFormat` enums. Omitting `auth` keeps the legacy static-`headers`
|
|
46
|
+
behaviour. The refresh recipe is persisted in plaintext to survive cold launch —
|
|
47
|
+
do not put static client secrets in `refreshHeaders`/`refreshPayload`.
|
|
48
|
+
- **`getCurrentLocation({ timeoutMs, accuracy })`** — a one-shot current location,
|
|
49
|
+
independent of any tracking session (resolves `null` on timeout; never starts or
|
|
50
|
+
stops tracking; the fix is not persisted and does not appear on `onLocation`).
|
|
51
|
+
Re-introduces thrown `BeekonError` kinds `'permissionDenied'`,
|
|
52
|
+
`'locationServicesDisabled'`, and `'locationUnavailable'` on a precondition
|
|
53
|
+
failure (0.0.5 had removed the `PERMISSION_DENIED` / `LOCATION_SERVICES_DISABLED`
|
|
54
|
+
thrown errors; they now surface again, but only from `getCurrentLocation()` —
|
|
55
|
+
tracking-lifecycle problems still report through `onState` as `Stopped(reason)`).
|
|
56
|
+
- **`NotificationConfig.smallIcon`** — a custom Android status-bar icon (drawable /
|
|
57
|
+
mipmap resource name) for the foreground-service tracking notification.
|
|
58
|
+
|
|
59
|
+
### Changed
|
|
60
|
+
|
|
61
|
+
- `androidNotification` (type `AndroidNotificationConfig`) renamed to `notification`
|
|
62
|
+
(type `NotificationConfig`) — **breaking**. The config remains Android-only; iOS
|
|
63
|
+
ignores it.
|
|
64
|
+
- `deleteLocations(before)` cutoff is now strictly **before** `before` (exclusive
|
|
65
|
+
`<`), aligning both natives and all wrappers; the prior wording said "at or
|
|
66
|
+
before".
|
|
67
|
+
- `resumeIfNeeded()` now calls the guarded native `Beekon.resumeIfNeeded()` on
|
|
68
|
+
Android (previously `Beekon.start()`): it re-adopts a previously-active,
|
|
69
|
+
non-user-stopped session only, mirroring iOS.
|
|
70
|
+
- Native pins bumped to `0.0.6` (Maven `io.github.wayqteam:beekon`, iOS
|
|
71
|
+
`BeekonKit.xcframework`).
|
|
72
|
+
|
|
73
|
+
## [0.0.5]
|
|
74
|
+
|
|
75
|
+
Full rebuild against the native **0.0.5** API — a breaking, no-backward-compat
|
|
76
|
+
release. The TypeScript surface is a faithful 1:1 mirror of the native `Beekon`
|
|
77
|
+
(Android) / `Beekon.shared` (iOS).
|
|
78
|
+
|
|
79
|
+
### Added
|
|
80
|
+
|
|
81
|
+
- **Server sync**: `SyncConfig` on `BeekonConfig`, plus `sync()`, `setExtras()`,
|
|
82
|
+
`pendingUploadCount()`, and the `onSyncStatus` subscription
|
|
83
|
+
(`SyncIdle`/`SyncPending`/`SyncFailed`).
|
|
84
|
+
- **Geofencing**: `addGeofences()` / `removeGeofences()` / `listGeofences()` and
|
|
85
|
+
the `onGeofenceEvent` subscription (`BeekonGeofence`, `GeofenceEvent`,
|
|
86
|
+
`Transition`).
|
|
87
|
+
- **Richer config**: `accuracyMode`, `whenStationary`, `stationaryRadiusMeters`,
|
|
88
|
+
`detectActivity`.
|
|
89
|
+
- **Richer `Location`**: `id`, `quality`, `trigger`, `motion`, `activity`,
|
|
90
|
+
`isMock`.
|
|
91
|
+
- `deleteLocations(before)`, `resumeIfNeeded()` (wrapper-only cold-launch resume,
|
|
92
|
+
not part of the native spec), and `StopReason 'locationUnavailable'`.
|
|
93
|
+
|
|
94
|
+
### Changed
|
|
95
|
+
|
|
96
|
+
- `intervalSeconds` → `minTimeBetweenLocationsSeconds`; `distanceMeters` →
|
|
97
|
+
`minDistanceBetweenLocationsMeters`.
|
|
98
|
+
- `getLocations(from: Date, to: Date)` replaces the prior history call.
|
|
99
|
+
- Notification config is now `androidNotification` (Android-only; iOS ignores it).
|
|
100
|
+
- Native pins bumped to `0.0.5` (Maven `io.github.wayqteam:beekon`, SwiftPM
|
|
101
|
+
`beekon-ios-binary`).
|
|
102
|
+
|
|
103
|
+
### Removed
|
|
104
|
+
|
|
105
|
+
- `PERMISSION_DENIED` / `LOCATION_SERVICES_DISABLED` thrown errors — permission
|
|
106
|
+
and service problems now surface only on `onState` as `Stopped(reason)`.
|
|
107
|
+
|
|
108
|
+
## [0.0.3]
|
|
109
|
+
|
|
110
|
+
First synchronized release across all four registries (Maven Central,
|
|
111
|
+
`wayqteam/beekon-ios-binary` xcframework, pub.dev, npm). `Location`
|
|
112
|
+
accuracy/speed/bearing/altitude are nullable to faithfully represent providers
|
|
113
|
+
that don't deliver them.
|
|
114
|
+
|
|
115
|
+
## [0.0.1]
|
|
116
|
+
|
|
117
|
+
Initial release.
|
|
118
|
+
|
|
119
|
+
### Added
|
|
120
|
+
|
|
121
|
+
- React Native (New Architecture / TurboModule) binding for the native Beekon
|
|
122
|
+
location SDKs (Android `io.github.wayqteam:beekon`, iOS `BeekonKit`
|
|
123
|
+
xcframework).
|
|
124
|
+
- Public API mirroring the native surface: `Beekon.configure / start / stop /
|
|
125
|
+
getLocations`, plus `onState` / `onLocation` subscriptions.
|
|
126
|
+
- `BeekonConfig`, `BeekonState` (`idle → tracking → stopped(reason)`),
|
|
127
|
+
`Location`, and typed `BeekonError` kinds.
|
package/README.md
CHANGED
|
@@ -1,43 +1,70 @@
|
|
|
1
|
-
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="assets/icon.png" alt="Beekon" width="140" />
|
|
3
|
+
</p>
|
|
2
4
|
|
|
3
|
-
|
|
5
|
+
<h1 align="center">@wayq/beekon-rn</h1>
|
|
4
6
|
|
|
5
|
-
|
|
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
|
-
|
|
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>
|
|
8
17
|
|
|
9
|
-
|
|
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
|
|
10
38
|
|
|
11
39
|
## Requirements
|
|
12
40
|
|
|
13
|
-
|
|
|
41
|
+
| | Minimum |
|
|
14
42
|
|---|---|
|
|
15
|
-
| React Native | 0.76
|
|
43
|
+
| React Native | 0.76 (New Architecture) |
|
|
16
44
|
| iOS | 17.0 |
|
|
17
|
-
| Android |
|
|
18
|
-
| New Architecture | required (Old Arch is unsupported) |
|
|
45
|
+
| Android | 8.0 (API level 26) |
|
|
19
46
|
|
|
20
|
-
##
|
|
47
|
+
## Installation
|
|
21
48
|
|
|
22
49
|
```sh
|
|
23
50
|
npm install @wayq/beekon-rn
|
|
24
|
-
# or
|
|
25
|
-
yarn add @wayq/beekon-rn
|
|
26
51
|
```
|
|
27
52
|
|
|
28
|
-
iOS —
|
|
53
|
+
**iOS** — install pods:
|
|
29
54
|
|
|
30
55
|
```sh
|
|
31
56
|
cd ios && pod install
|
|
32
57
|
```
|
|
33
58
|
|
|
34
|
-
Android —
|
|
59
|
+
**Android** — no additional steps; the module links automatically.
|
|
60
|
+
|
|
61
|
+
## Permissions and setup
|
|
35
62
|
|
|
36
|
-
|
|
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
|
-
|
|
65
|
+
### Android
|
|
39
66
|
|
|
40
|
-
|
|
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,20 +72,21 @@ The library does NOT request permissions — your app must (e.g. via [`react-nat
|
|
|
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" />
|
|
48
|
-
<!-- Only
|
|
75
|
+
<!-- Only if you enable detectActivity -->
|
|
49
76
|
<uses-permission android:name="android.permission.ACTIVITY_RECOGNITION" />
|
|
50
77
|
```
|
|
51
78
|
|
|
52
|
-
|
|
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`:
|
|
53
84
|
|
|
54
85
|
```xml
|
|
55
86
|
<key>NSLocationWhenInUseUsageDescription</key>
|
|
56
|
-
<string
|
|
87
|
+
<string>Explain why your app uses location.</string>
|
|
57
88
|
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
|
|
58
|
-
<string
|
|
59
|
-
<!-- Only when BeekonConfig.detectActivity is enabled -->
|
|
60
|
-
<key>NSMotionUsageDescription</key>
|
|
61
|
-
<string>…</string>
|
|
89
|
+
<string>Explain why your app uses location in the background.</string>
|
|
62
90
|
<key>UIBackgroundModes</key>
|
|
63
91
|
<array>
|
|
64
92
|
<string>location</string>
|
|
@@ -68,101 +96,295 @@ The library does NOT request permissions — your app must (e.g. via [`react-nat
|
|
|
68
96
|
<array>
|
|
69
97
|
<string>in.wayq.beekon.sync</string>
|
|
70
98
|
</array>
|
|
99
|
+
<!-- Only if you enable detectActivity -->
|
|
100
|
+
<key>NSMotionUsageDescription</key>
|
|
101
|
+
<string>Explain why your app detects activity.</string>
|
|
71
102
|
```
|
|
72
103
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
For background sync scheduling and cold-launch resume, register Beekon's background task **synchronously during app launch** (it must run before `didFinishLaunchingWithOptions` returns). In your `AppDelegate.swift`:
|
|
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`:
|
|
76
105
|
|
|
77
106
|
```swift
|
|
78
107
|
import BeekonRn
|
|
79
108
|
|
|
80
|
-
func application(
|
|
81
|
-
|
|
109
|
+
func application(
|
|
110
|
+
_ application: UIApplication,
|
|
111
|
+
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil
|
|
112
|
+
) -> Bool {
|
|
82
113
|
BeekonRnImpl.registerBackgroundTasks()
|
|
83
|
-
//
|
|
114
|
+
// ...your existing setup...
|
|
84
115
|
return true
|
|
85
116
|
}
|
|
86
117
|
```
|
|
87
118
|
|
|
88
|
-
|
|
119
|
+
## Usage
|
|
89
120
|
|
|
90
|
-
|
|
121
|
+
### Start tracking
|
|
91
122
|
|
|
92
|
-
|
|
123
|
+
```ts
|
|
124
|
+
import { Beekon } from '@wayq/beekon-rn';
|
|
93
125
|
|
|
94
|
-
|
|
126
|
+
const offState = Beekon.onState((state) => {
|
|
127
|
+
console.log('state:', state.kind); // 'idle' | 'tracking' | 'stopped'
|
|
128
|
+
});
|
|
129
|
+
|
|
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.
|
|
135
|
+
await Beekon.configure({
|
|
136
|
+
minTimeBetweenLocationsSeconds: 30,
|
|
137
|
+
minDistanceBetweenLocationsMeters: 100,
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
await Beekon.start();
|
|
141
|
+
|
|
142
|
+
// Later:
|
|
143
|
+
await Beekon.stop();
|
|
144
|
+
offState();
|
|
145
|
+
offLocation();
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
`start()` and `stop()` never throw. Observe `onState` to learn whether tracking began and why it stopped:
|
|
149
|
+
|
|
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
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
### Geofences
|
|
95
173
|
|
|
96
174
|
```ts
|
|
97
|
-
|
|
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
|
+
```
|
|
98
186
|
|
|
99
|
-
|
|
100
|
-
// replay the latest value to new subscribers; `onLocation` / `onGeofenceEvent`
|
|
101
|
-
// are broadcast (no replay).
|
|
102
|
-
const offState = Beekon.onState((s: BeekonState) => console.log('state', s));
|
|
103
|
-
const offLoc = Beekon.onLocation((l: Location) => console.log('location', l));
|
|
187
|
+
### Server sync
|
|
104
188
|
|
|
105
|
-
|
|
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
|
|
106
192
|
await Beekon.configure({
|
|
107
|
-
minTimeBetweenLocationsSeconds: 30, // default 30
|
|
108
|
-
minDistanceBetweenLocationsMeters: 100, // default 100
|
|
109
|
-
accuracyMode: 'balanced', // 'high' | 'balanced' | 'low'
|
|
110
|
-
whenStationary: 'pause', // 'keepTracking' | 'pause' | 'pauseWithCheckIns'
|
|
111
|
-
detectActivity: false,
|
|
112
|
-
// Optional server upload:
|
|
113
193
|
sync: {
|
|
114
|
-
url: 'https://example.com/
|
|
115
|
-
headers: { Authorization: 'Bearer
|
|
194
|
+
url: 'https://api.example.com/locations',
|
|
195
|
+
headers: { Authorization: 'Bearer <token>' },
|
|
196
|
+
// intervalSeconds defaults to 300, batchSize to 100
|
|
116
197
|
},
|
|
117
|
-
// Android-only foreground-service notification:
|
|
118
|
-
notification: { title: 'Tracking', text: 'Recording your route' },
|
|
119
198
|
});
|
|
120
199
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
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
|
+
```
|
|
124
206
|
|
|
125
|
-
|
|
126
|
-
const fixes = await Beekon.getLocations(new Date(Date.now() - 3600_000), new Date());
|
|
207
|
+
#### Token refresh
|
|
127
208
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
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
|
+
});
|
|
131
236
|
```
|
|
132
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
|
+
|
|
133
257
|
## API
|
|
134
258
|
|
|
135
259
|
| Method | Description |
|
|
136
260
|
|---|---|
|
|
137
|
-
| `configure(config)` |
|
|
138
|
-
| `start()` | Begin tracking.
|
|
139
|
-
| `stop()` | Stop tracking.
|
|
140
|
-
| `resumeIfNeeded()` | Resume a session active before the app
|
|
141
|
-
| `
|
|
142
|
-
| `
|
|
143
|
-
| `
|
|
144
|
-
| `
|
|
145
|
-
| `
|
|
146
|
-
| `
|
|
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. |
|
|
147
272
|
| `removeGeofences(ids)` | Unregister geofences by id. |
|
|
148
|
-
| `listGeofences()` |
|
|
149
|
-
| `onState(cb)` | Subscribe to state. Replay-1. Returns unsubscribe fn. |
|
|
150
|
-
| `onLocation(cb)` | Subscribe to gated fixes. No replay. |
|
|
151
|
-
| `onGeofenceEvent(cb)` | Subscribe to geofence enter/exit. No replay. |
|
|
152
|
-
| `onSyncStatus(cb)` | Subscribe to upload health. Replay-1. |
|
|
273
|
+
| `listGeofences()` | Currently registered geofences. |
|
|
153
274
|
|
|
154
|
-
|
|
275
|
+
Subscriptions — each returns an unsubscribe function:
|
|
155
276
|
|
|
156
|
-
|
|
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. |
|
|
157
284
|
|
|
158
|
-
`
|
|
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.
|
|
159
286
|
|
|
160
|
-
|
|
287
|
+
## Types
|
|
161
288
|
|
|
162
|
-
|
|
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
|
|
163
373
|
|
|
164
|
-
|
|
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.
|
|
165
381
|
|
|
166
382
|
## License
|
|
167
383
|
|
|
168
|
-
Proprietary —
|
|
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>
|
package/android/build.gradle
CHANGED
|
@@ -76,8 +76,9 @@ dependencies {
|
|
|
76
76
|
implementation "com.facebook.react:react-android"
|
|
77
77
|
// Native Beekon SDK — published from beekon-android/ via Maven Central.
|
|
78
78
|
// Pinned exact in v0.x to avoid surprise breakage; loosen to a range when
|
|
79
|
-
// the SDK reaches v1 stability.
|
|
80
|
-
|
|
79
|
+
// the SDK reaches v1 stability. 0.0.7 is the release that ships the license
|
|
80
|
+
// API (setWrapperInfo / BeekonConfig.licenseKey / Beekon.licenseStatus).
|
|
81
|
+
implementation "io.github.wayqteam:beekon:0.0.7"
|
|
81
82
|
// Kotlin coroutines — required for collecting Beekon's StateFlow/SharedFlow.
|
|
82
83
|
// Beekon already depends on coroutines transitively, but declaring it here
|
|
83
84
|
// makes the dependency intent explicit.
|