expo-beacon 0.2.0 → 0.3.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/README.md +35 -20
- package/android/src/main/java/expo/modules/beacon/BeaconForegroundService.kt +101 -28
- package/android/src/main/java/expo/modules/beacon/ExpoBeaconModule.kt +53 -8
- package/build/ExpoBeacon.types.d.ts +0 -10
- package/build/ExpoBeacon.types.d.ts.map +1 -1
- package/build/ExpoBeacon.types.js.map +1 -1
- package/build/ExpoBeaconModule.d.ts.map +1 -1
- package/build/ExpoBeaconModule.js.map +1 -1
- package/build/ExpoBeaconModule.web.d.ts +1 -1
- package/build/ExpoBeaconModule.web.d.ts.map +1 -1
- package/build/ExpoBeaconModule.web.js +1 -1
- package/build/ExpoBeaconModule.web.js.map +1 -1
- package/build/index.d.ts +1 -1
- package/build/index.d.ts.map +1 -1
- package/build/index.js.map +1 -1
- package/ios/ExpoBeacon.podspec +1 -1
- package/ios/ExpoBeaconModule.swift +364 -75
- package/package.json +1 -1
- package/src/ExpoBeacon.types.ts +0 -11
- package/src/ExpoBeaconModule.ts +11 -2
- package/src/ExpoBeaconModule.web.ts +4 -2
- package/src/index.ts +0 -1
package/README.md
CHANGED
|
@@ -11,7 +11,7 @@ An Expo module for scanning, pairing, and monitoring iBeacons in React Native ap
|
|
|
11
11
|
| Platform | Native implementation |
|
|
12
12
|
| -------- | --------------------------------------------------------------------------------------------------------- |
|
|
13
13
|
| Android | [AltBeacon](https://altbeacon.github.io/android-beacon-library/) (`org.altbeacon:android-beacon-library`) |
|
|
14
|
-
| iOS | CoreLocation
|
|
14
|
+
| iOS | CoreLocation (UUID-targeted scans & monitoring) + CoreBluetooth (wildcard scanning) |
|
|
15
15
|
| Web | Not supported (throws on all calls) |
|
|
16
16
|
|
|
17
17
|
---
|
|
@@ -47,6 +47,8 @@ In Xcode under **Signing & Capabilities**, enable:
|
|
|
47
47
|
- **Background Modes → Uses Bluetooth LE accessories**
|
|
48
48
|
|
|
49
49
|
> iOS limits apps to **20 simultaneously monitored regions**.
|
|
50
|
+
>
|
|
51
|
+
> **Wildcard scanning**: When you call `scanForBeaconsAsync()` with an empty or omitted `uuids` array, iOS uses CoreBluetooth raw BLE scanning to discover all nearby iBeacons. This works **in the foreground only** — it is an Apple platform limitation. UUID-targeted scans and background monitoring continue to use CoreLocation.
|
|
50
52
|
|
|
51
53
|
### Android
|
|
52
54
|
|
|
@@ -111,7 +113,8 @@ export default function App() {
|
|
|
111
113
|
}, []);
|
|
112
114
|
|
|
113
115
|
async function scan() {
|
|
114
|
-
|
|
116
|
+
// Wildcard scan — discovers all nearby iBeacons (no UUID filter)
|
|
117
|
+
const results = await ExpoBeacon.scanForBeaconsAsync([], 5000);
|
|
115
118
|
setBeacons(results);
|
|
116
119
|
}
|
|
117
120
|
|
|
@@ -158,23 +161,41 @@ if (!granted) {
|
|
|
158
161
|
|
|
159
162
|
---
|
|
160
163
|
|
|
161
|
-
### `scanForBeaconsAsync(scanDurationMs?)`
|
|
164
|
+
### `scanForBeaconsAsync(uuids?, scanDurationMs?)`
|
|
162
165
|
|
|
163
166
|
```ts
|
|
164
|
-
scanForBeaconsAsync(scanDurationMs?: number): Promise<BeaconScanResult[]>
|
|
167
|
+
scanForBeaconsAsync(uuids?: string[], scanDurationMs?: number): Promise<BeaconScanResult[]>
|
|
165
168
|
```
|
|
166
169
|
|
|
167
170
|
Starts a **one-shot BLE scan**, waits for `scanDurationMs` milliseconds, then resolves with all beacons discovered during that window.
|
|
168
171
|
|
|
169
|
-
| Parameter
|
|
170
|
-
|
|
|
171
|
-
| `
|
|
172
|
+
| Parameter | Type | Default | Description |
|
|
173
|
+
| ---------------- | ---------- | ------- | ---------------------------------------- |
|
|
174
|
+
| `uuids` | `string[]` | `[]` | Proximity UUIDs to filter by. Pass `[]` or omit for a **wildcard scan** that discovers all nearby iBeacons. |
|
|
175
|
+
| `scanDurationMs` | `number` | `5000` | How long to scan in milliseconds (1–60 000 recommended) |
|
|
172
176
|
|
|
173
177
|
Returns an array of [`BeaconScanResult`](#beaconscanresult) objects. Rejects with `SCAN_IN_PROGRESS` if another scan is already running.
|
|
174
178
|
|
|
179
|
+
**Wildcard vs. targeted scans**
|
|
180
|
+
|
|
181
|
+
| | Wildcard (`[]` / omitted) | Targeted (`['UUID-1', …]`) |
|
|
182
|
+
|---|---|---|
|
|
183
|
+
| **Android** | Discovers all iBeacons via AltBeacon | Filters results to matching UUIDs |
|
|
184
|
+
| **iOS** | CoreBluetooth raw BLE scan (**foreground only**) | CoreLocation ranging (works in background) |
|
|
185
|
+
|
|
186
|
+
> **iOS limitation**: Wildcard scanning uses CoreBluetooth, which cannot scan in the background. If your app is backgrounded during a wildcard scan, no new beacons will be discovered. Use UUID-targeted scans or `startMonitoring()` for background beacon detection.
|
|
187
|
+
|
|
175
188
|
```ts
|
|
176
|
-
|
|
177
|
-
|
|
189
|
+
// Wildcard scan — discover all nearby iBeacons
|
|
190
|
+
const all = await ExpoBeacon.scanForBeaconsAsync([], 5000);
|
|
191
|
+
|
|
192
|
+
// Targeted scan — only beacons matching these UUIDs
|
|
193
|
+
const filtered = await ExpoBeacon.scanForBeaconsAsync(
|
|
194
|
+
['E2C56DB5-DFFB-48D2-B060-D0F5A71096E0'],
|
|
195
|
+
8000,
|
|
196
|
+
);
|
|
197
|
+
|
|
198
|
+
filtered.forEach((b) =>
|
|
178
199
|
console.log(`${b.uuid} major=${b.major} minor=${b.minor} dist=${b.distance.toFixed(1)}m rssi=${b.rssi}dBm`)
|
|
179
200
|
);
|
|
180
201
|
```
|
|
@@ -191,6 +212,8 @@ Begins a **continuous BLE scan** that fires an [`onBeaconFound`](#onbeaconfound)
|
|
|
191
212
|
|
|
192
213
|
Unlike `scanForBeaconsAsync`, this never resolves — it streams results in real time.
|
|
193
214
|
|
|
215
|
+
> **iOS note**: Due to CoreLocation API constraints, `startContinuousScan()` on iOS only ranges beacons that have been previously paired with `pairBeacon()`. On Android, all nearby BLE beacons are reported regardless of pairing status.
|
|
216
|
+
|
|
194
217
|
```ts
|
|
195
218
|
const sub = ExpoBeacon.addListener("onBeaconFound", (beacon) => {
|
|
196
219
|
console.log("Live:", beacon.uuid, beacon.distance);
|
|
@@ -420,15 +443,7 @@ ExpoBeacon.addListener("onBeaconExit", (e) => {
|
|
|
420
443
|
|
|
421
444
|
### `onBeaconRanging`
|
|
422
445
|
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
**Payload**: [`BeaconRangingEvent`](#beaconrangingevent)
|
|
426
|
-
|
|
427
|
-
```ts
|
|
428
|
-
ExpoBeacon.addListener("onBeaconRanging", (e) => {
|
|
429
|
-
console.log(`Ranging ${e.identifier}: ${e.distance.toFixed(2)} m rssi=${e.rssi}`);
|
|
430
|
-
});
|
|
431
|
-
```
|
|
446
|
+
> **Not currently emitted.** This event is declared in the TypeScript types ([`BeaconRangingEvent`](#beaconrangingevent)) but is not fired by either the iOS or Android native implementation. Use [`onBeaconDistance`](#onbeacondistance) for periodic distance updates during monitoring.
|
|
432
447
|
|
|
433
448
|
---
|
|
434
449
|
|
|
@@ -507,7 +522,7 @@ type BeaconRegionEvent = {
|
|
|
507
522
|
|
|
508
523
|
### `BeaconRangingEvent`
|
|
509
524
|
|
|
510
|
-
Payload for `onBeaconRanging
|
|
525
|
+
Payload type for the `onBeaconRanging` event. **Declared for future use — this event is not currently emitted by either platform.** Use [`BeaconDistanceEvent`](#beacondistanceevent) and [`onBeaconDistance`](#onbeacondistance) for real-time distance updates.
|
|
511
526
|
|
|
512
527
|
```ts
|
|
513
528
|
type BeaconRangingEvent = {
|
|
@@ -648,7 +663,7 @@ Both notifications share the channel id `expo_beacon_channel`. The channel is re
|
|
|
648
663
|
|
|
649
664
|
## Contributing
|
|
650
665
|
|
|
651
|
-
Contributions are welcome!
|
|
666
|
+
Contributions are welcome! Open an issue or pull request on [GitHub](https://github.com/martinmikesCCS/expo-beacon).
|
|
652
667
|
|
|
653
668
|
## License
|
|
654
669
|
|
|
@@ -18,7 +18,12 @@ private const val PREFS_NAME = "expo.beacon.paired"
|
|
|
18
18
|
private const val PREFS_KEY = "paired_beacons"
|
|
19
19
|
private const val CHANNEL_ID = "expo_beacon_channel"
|
|
20
20
|
private const val FOREGROUND_NOTIF_ID = 1001
|
|
21
|
-
private const val
|
|
21
|
+
private const val ENTER_EXIT_NOTIF_BASE_ID = 2000
|
|
22
|
+
|
|
23
|
+
/// Number of consecutive ranging misses before emitting a distance-based exit event.
|
|
24
|
+
private const val EXIT_MISS_THRESHOLD = 3
|
|
25
|
+
/// Number of consecutive readings required to confirm a distance-based enter or exit transition.
|
|
26
|
+
private const val HYSTERESIS_COUNT = 3
|
|
22
27
|
|
|
23
28
|
class BeaconForegroundService : Service(), BeaconConsumer {
|
|
24
29
|
|
|
@@ -30,6 +35,16 @@ class BeaconForegroundService : Service(), BeaconConsumer {
|
|
|
30
35
|
private val rangingRegions = java.util.concurrent.CopyOnWriteArraySet<Region>()
|
|
31
36
|
private val enteredRegions = java.util.concurrent.CopyOnWriteArraySet<String>()
|
|
32
37
|
|
|
38
|
+
// Hysteresis counters (synchronized on distanceLock)
|
|
39
|
+
private val distanceLock = Any()
|
|
40
|
+
private val enterCounters = java.util.concurrent.ConcurrentHashMap<String, Int>()
|
|
41
|
+
private val exitCounters = java.util.concurrent.ConcurrentHashMap<String, Int>()
|
|
42
|
+
private val missCounters = java.util.concurrent.ConcurrentHashMap<String, Int>()
|
|
43
|
+
|
|
44
|
+
// Notification ID counter for unique per-beacon notifications
|
|
45
|
+
private var notifIdCounter = 0
|
|
46
|
+
private val notifIdMap = java.util.concurrent.ConcurrentHashMap<String, Int>()
|
|
47
|
+
|
|
33
48
|
// Distance logging
|
|
34
49
|
private val distanceLogRegions = java.util.concurrent.CopyOnWriteArraySet<Region>()
|
|
35
50
|
|
|
@@ -153,26 +168,62 @@ class BeaconForegroundService : Service(), BeaconConsumer {
|
|
|
153
168
|
Log.d("BeaconMonitor", "[${region.uniqueId}] distance: ${"%.2f".format(closest.distance)} m rssi=${closest.rssi} txPower=${closest.txPower}")
|
|
154
169
|
sendBeaconBroadcast(region, "distance", closest.distance)
|
|
155
170
|
|
|
171
|
+
// Reset miss counter — we got a valid reading
|
|
172
|
+
missCounters[region.uniqueId] = 0
|
|
173
|
+
|
|
156
174
|
val maxDist = maxDistance
|
|
157
175
|
if (maxDist != null) {
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
176
|
+
synchronized(distanceLock) {
|
|
177
|
+
if (closest.distance <= maxDist) {
|
|
178
|
+
// Reading is inside threshold
|
|
179
|
+
exitCounters[region.uniqueId] = 0
|
|
180
|
+
val count = (enterCounters[region.uniqueId] ?: 0) + 1
|
|
181
|
+
enterCounters[region.uniqueId] = count
|
|
182
|
+
|
|
183
|
+
if (!enteredRegions.contains(region.uniqueId) && count >= HYSTERESIS_COUNT) {
|
|
184
|
+
enteredRegions.add(region.uniqueId)
|
|
185
|
+
enterCounters[region.uniqueId] = 0
|
|
186
|
+
rangingRegions.remove(region)
|
|
187
|
+
sendBeaconBroadcast(region, "enter", closest.distance)
|
|
188
|
+
showEnterExitNotification(region, "enter")
|
|
189
|
+
}
|
|
190
|
+
} else {
|
|
191
|
+
// Reading is outside threshold
|
|
192
|
+
enterCounters[region.uniqueId] = 0
|
|
193
|
+
val count = (exitCounters[region.uniqueId] ?: 0) + 1
|
|
194
|
+
exitCounters[region.uniqueId] = count
|
|
195
|
+
|
|
196
|
+
if (enteredRegions.contains(region.uniqueId) && count >= HYSTERESIS_COUNT) {
|
|
197
|
+
enteredRegions.remove(region.uniqueId)
|
|
198
|
+
exitCounters[region.uniqueId] = 0
|
|
199
|
+
rangingRegions.add(region)
|
|
200
|
+
sendBeaconBroadcast(region, "exit", closest.distance)
|
|
201
|
+
showEnterExitNotification(region, "exit")
|
|
202
|
+
}
|
|
203
|
+
}
|
|
172
204
|
}
|
|
173
205
|
}
|
|
174
206
|
} else {
|
|
175
207
|
Log.d("BeaconMonitor", "[${region.uniqueId}] no beacons in range")
|
|
208
|
+
|
|
209
|
+
// Beacon may have disappeared — track consecutive misses
|
|
210
|
+
val maxDist = maxDistance
|
|
211
|
+
if (maxDist != null) {
|
|
212
|
+
val count = (missCounters[region.uniqueId] ?: 0) + 1
|
|
213
|
+
missCounters[region.uniqueId] = count
|
|
214
|
+
|
|
215
|
+
if (enteredRegions.contains(region.uniqueId) && count >= EXIT_MISS_THRESHOLD) {
|
|
216
|
+
synchronized(distanceLock) {
|
|
217
|
+
if (enteredRegions.remove(region.uniqueId)) {
|
|
218
|
+
missCounters[region.uniqueId] = 0
|
|
219
|
+
enterCounters[region.uniqueId] = 0
|
|
220
|
+
exitCounters[region.uniqueId] = 0
|
|
221
|
+
sendBeaconBroadcast(region, "exit", -1.0)
|
|
222
|
+
showEnterExitNotification(region, "exit")
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
176
227
|
}
|
|
177
228
|
}
|
|
178
229
|
|
|
@@ -190,8 +241,15 @@ class BeaconForegroundService : Service(), BeaconConsumer {
|
|
|
190
241
|
}
|
|
191
242
|
|
|
192
243
|
override fun didExitRegion(region: Region) {
|
|
193
|
-
// Remove from confirmation tracking (ranging stays active for distance logging)
|
|
194
244
|
rangingRegions.remove(region)
|
|
245
|
+
|
|
246
|
+
if (maxDistance != null) {
|
|
247
|
+
// When maxDistance is set, distance ranging handles exit events.
|
|
248
|
+
// Just clean up tracked state so distance-driven exit can fire.
|
|
249
|
+
enteredRegions.remove(region.uniqueId)
|
|
250
|
+
return
|
|
251
|
+
}
|
|
252
|
+
|
|
195
253
|
enteredRegions.remove(region.uniqueId)
|
|
196
254
|
sendBeaconBroadcast(region, "exit", -1.0)
|
|
197
255
|
showEnterExitNotification(region, "exit")
|
|
@@ -209,12 +267,19 @@ class BeaconForegroundService : Service(), BeaconConsumer {
|
|
|
209
267
|
.filter { it.distance >= 0 }
|
|
210
268
|
.minByOrNull { it.distance } ?: return@RangeNotifier
|
|
211
269
|
|
|
212
|
-
|
|
213
|
-
enteredRegions.
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
270
|
+
synchronized(distanceLock) {
|
|
271
|
+
if (beacon.distance <= maxDist && !enteredRegions.contains(region.uniqueId)) {
|
|
272
|
+
val count = (enterCounters[region.uniqueId] ?: 0) + 1
|
|
273
|
+
enterCounters[region.uniqueId] = count
|
|
274
|
+
|
|
275
|
+
if (count >= HYSTERESIS_COUNT) {
|
|
276
|
+
enteredRegions.add(region.uniqueId)
|
|
277
|
+
enterCounters[region.uniqueId] = 0
|
|
278
|
+
rangingRegions.remove(region)
|
|
279
|
+
sendBeaconBroadcast(region, "enter", beacon.distance)
|
|
280
|
+
showEnterExitNotification(region, "enter")
|
|
281
|
+
}
|
|
282
|
+
}
|
|
218
283
|
}
|
|
219
284
|
}
|
|
220
285
|
|
|
@@ -266,7 +331,10 @@ class BeaconForegroundService : Service(), BeaconConsumer {
|
|
|
266
331
|
.build()
|
|
267
332
|
|
|
268
333
|
try {
|
|
269
|
-
|
|
334
|
+
val notifId = notifIdMap.getOrPut(region.uniqueId) {
|
|
335
|
+
ENTER_EXIT_NOTIF_BASE_ID + notifIdCounter++
|
|
336
|
+
}
|
|
337
|
+
NotificationManagerCompat.from(this).notify(notifId, notification)
|
|
270
338
|
} catch (_: SecurityException) {
|
|
271
339
|
// POST_NOTIFICATIONS not granted — silently skip notification
|
|
272
340
|
}
|
|
@@ -288,12 +356,13 @@ class BeaconForegroundService : Service(), BeaconConsumer {
|
|
|
288
356
|
}
|
|
289
357
|
|
|
290
358
|
val notifMgr = getSystemService(NotificationManager::class.java)
|
|
291
|
-
//
|
|
292
|
-
notifMgr?.
|
|
293
|
-
|
|
294
|
-
|
|
359
|
+
// Only create channel if it doesn't exist yet — preserves user notification preferences
|
|
360
|
+
if (notifMgr?.getNotificationChannel(CHANNEL_ID) == null) {
|
|
361
|
+
val channel = NotificationChannel(CHANNEL_ID, channelName, importance).apply {
|
|
362
|
+
description = channelDesc
|
|
363
|
+
}
|
|
364
|
+
notifMgr?.createNotificationChannel(channel)
|
|
295
365
|
}
|
|
296
|
-
notifMgr?.createNotificationChannel(channel)
|
|
297
366
|
}
|
|
298
367
|
}
|
|
299
368
|
|
|
@@ -339,6 +408,10 @@ class BeaconForegroundService : Service(), BeaconConsumer {
|
|
|
339
408
|
}
|
|
340
409
|
distanceLogRegions.clear()
|
|
341
410
|
enteredRegions.clear()
|
|
411
|
+
enterCounters.clear()
|
|
412
|
+
exitCounters.clear()
|
|
413
|
+
missCounters.clear()
|
|
414
|
+
notifIdMap.clear()
|
|
342
415
|
monitoredRegions.forEach {
|
|
343
416
|
try { beaconManager.stopMonitoringBeaconsInRegion(it) } catch (_: RemoteException) {}
|
|
344
417
|
}
|
|
@@ -52,6 +52,7 @@ class ExpoBeaconModule : Module(), BeaconConsumer {
|
|
|
52
52
|
private var scanPromise: Promise? = null
|
|
53
53
|
private var scanJob: Job? = null
|
|
54
54
|
private val scanResults = mutableListOf<Beacon>()
|
|
55
|
+
private var scanUuidFilter: Set<String> = emptySet()
|
|
55
56
|
private var isBoundForScan = false
|
|
56
57
|
|
|
57
58
|
// Continuous scan state
|
|
@@ -61,14 +62,15 @@ class ExpoBeaconModule : Module(), BeaconConsumer {
|
|
|
61
62
|
override fun definition() = ModuleDefinition {
|
|
62
63
|
Name("ExpoBeacon")
|
|
63
64
|
|
|
64
|
-
Events("onBeaconEnter", "onBeaconExit", "
|
|
65
|
+
Events("onBeaconEnter", "onBeaconExit", "onBeaconDistance", "onBeaconFound")
|
|
65
66
|
|
|
66
|
-
AsyncFunction("scanForBeaconsAsync") { scanDurationMs: Int, promise: Promise ->
|
|
67
|
+
AsyncFunction("scanForBeaconsAsync") { uuids: List<String>?, scanDurationMs: Int, promise: Promise ->
|
|
67
68
|
if (scanPromise != null) {
|
|
68
69
|
promise.reject("SCAN_IN_PROGRESS", "A scan is already running", null)
|
|
69
70
|
return@AsyncFunction
|
|
70
71
|
}
|
|
71
72
|
scanResults.clear()
|
|
73
|
+
scanUuidFilter = uuids?.map { it.lowercase() }?.toSet() ?: emptySet()
|
|
72
74
|
scanPromise = promise
|
|
73
75
|
|
|
74
76
|
beaconManager.addRangeNotifier(scanRangeNotifier)
|
|
@@ -113,6 +115,19 @@ class ExpoBeaconModule : Module(), BeaconConsumer {
|
|
|
113
115
|
}
|
|
114
116
|
|
|
115
117
|
Function("pairBeacon") { identifier: String, uuid: String, major: Int, minor: Int ->
|
|
118
|
+
// Validate UUID format
|
|
119
|
+
try {
|
|
120
|
+
java.util.UUID.fromString(uuid)
|
|
121
|
+
} catch (_: IllegalArgumentException) {
|
|
122
|
+
throw expo.modules.kotlin.exception.CodedException("INVALID_UUID", "Invalid UUID format: $uuid", null)
|
|
123
|
+
}
|
|
124
|
+
if (major !in 0..65535) {
|
|
125
|
+
throw expo.modules.kotlin.exception.CodedException("INVALID_MAJOR", "Major must be 0\u201365535, got $major", null)
|
|
126
|
+
}
|
|
127
|
+
if (minor !in 0..65535) {
|
|
128
|
+
throw expo.modules.kotlin.exception.CodedException("INVALID_MINOR", "Minor must be 0\u201365535, got $minor", null)
|
|
129
|
+
}
|
|
130
|
+
|
|
116
131
|
val beacons = loadPairedBeaconsJson()
|
|
117
132
|
// Remove duplicate if exists
|
|
118
133
|
val filtered = (0 until beacons.length())
|
|
@@ -176,6 +191,15 @@ class ExpoBeaconModule : Module(), BeaconConsumer {
|
|
|
176
191
|
if (maxDistance != null) putFloat("max_distance", maxDistance.toFloat())
|
|
177
192
|
else remove("max_distance")
|
|
178
193
|
}.apply()
|
|
194
|
+
// Verify we have the permissions needed for background monitoring
|
|
195
|
+
val hasLocation = ContextCompat.checkSelfPermission(ctx, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED
|
|
196
|
+
val hasBgLocation = Build.VERSION.SDK_INT < Build.VERSION_CODES.Q ||
|
|
197
|
+
ContextCompat.checkSelfPermission(ctx, Manifest.permission.ACCESS_BACKGROUND_LOCATION) == PackageManager.PERMISSION_GRANTED
|
|
198
|
+
if (!hasLocation || !hasBgLocation) {
|
|
199
|
+
promise.reject("PERMISSION_DENIED", "Location permissions required for background monitoring. Call requestPermissionsAsync() first.", null)
|
|
200
|
+
return@AsyncFunction
|
|
201
|
+
}
|
|
202
|
+
|
|
179
203
|
registerEventReceiver()
|
|
180
204
|
BeaconForegroundService.start(ctx)
|
|
181
205
|
promise.resolve(null)
|
|
@@ -195,7 +219,8 @@ class ExpoBeaconModule : Module(), BeaconConsumer {
|
|
|
195
219
|
}
|
|
196
220
|
|
|
197
221
|
AsyncFunction("requestPermissionsAsync") { promise: Promise ->
|
|
198
|
-
|
|
222
|
+
// Step 1: request foreground permissions
|
|
223
|
+
val foreground = buildList {
|
|
199
224
|
add(Manifest.permission.ACCESS_FINE_LOCATION)
|
|
200
225
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
|
201
226
|
add(Manifest.permission.BLUETOOTH_SCAN)
|
|
@@ -212,7 +237,7 @@ class ExpoBeaconModule : Module(), BeaconConsumer {
|
|
|
212
237
|
promise.resolve(false)
|
|
213
238
|
return@AsyncFunction
|
|
214
239
|
}
|
|
215
|
-
val allGranted =
|
|
240
|
+
val allGranted = foreground.all {
|
|
216
241
|
ContextCompat.checkSelfPermission(context, it) == PackageManager.PERMISSION_GRANTED
|
|
217
242
|
}
|
|
218
243
|
promise.resolve(allGranted)
|
|
@@ -220,11 +245,24 @@ class ExpoBeaconModule : Module(), BeaconConsumer {
|
|
|
220
245
|
}
|
|
221
246
|
|
|
222
247
|
permissionsManager.askForPermissions({ results ->
|
|
223
|
-
val
|
|
248
|
+
val fgGranted = foreground.all { perm ->
|
|
224
249
|
results[perm]?.status == PermissionsStatus.GRANTED
|
|
225
250
|
}
|
|
226
|
-
|
|
227
|
-
|
|
251
|
+
if (!fgGranted) {
|
|
252
|
+
promise.resolve(false)
|
|
253
|
+
return@askForPermissions
|
|
254
|
+
}
|
|
255
|
+
// Step 2: request background location (Android 10+)
|
|
256
|
+
// Must be requested separately after foreground location is granted
|
|
257
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
|
258
|
+
permissionsManager.askForPermissions({ bgResults ->
|
|
259
|
+
val bgGranted = bgResults[Manifest.permission.ACCESS_BACKGROUND_LOCATION]?.status == PermissionsStatus.GRANTED
|
|
260
|
+
promise.resolve(bgGranted)
|
|
261
|
+
}, Manifest.permission.ACCESS_BACKGROUND_LOCATION)
|
|
262
|
+
} else {
|
|
263
|
+
promise.resolve(true)
|
|
264
|
+
}
|
|
265
|
+
}, *foreground.toTypedArray())
|
|
228
266
|
}
|
|
229
267
|
|
|
230
268
|
OnDestroy {
|
|
@@ -266,7 +304,13 @@ class ExpoBeaconModule : Module(), BeaconConsumer {
|
|
|
266
304
|
}
|
|
267
305
|
|
|
268
306
|
private val scanRangeNotifier = RangeNotifier { beacons, _ ->
|
|
269
|
-
|
|
307
|
+
if (scanUuidFilter.isEmpty()) {
|
|
308
|
+
scanResults.addAll(beacons)
|
|
309
|
+
} else {
|
|
310
|
+
scanResults.addAll(beacons.filter { beacon ->
|
|
311
|
+
scanUuidFilter.contains(beacon.id1.toString().lowercase())
|
|
312
|
+
})
|
|
313
|
+
}
|
|
270
314
|
}
|
|
271
315
|
|
|
272
316
|
private val continuousScanRangeNotifier = RangeNotifier { beacons, _ ->
|
|
@@ -309,6 +353,7 @@ class ExpoBeaconModule : Module(), BeaconConsumer {
|
|
|
309
353
|
}
|
|
310
354
|
scanPromise?.resolve(results)
|
|
311
355
|
scanPromise = null
|
|
356
|
+
scanUuidFilter = emptySet()
|
|
312
357
|
}
|
|
313
358
|
|
|
314
359
|
// --- Notification config helpers ---
|
|
@@ -24,15 +24,6 @@ export type BeaconRegionEvent = {
|
|
|
24
24
|
/** Measured distance in metres at the time of the event (–1 if unavailable). */
|
|
25
25
|
distance: number;
|
|
26
26
|
};
|
|
27
|
-
/** Payload for ranging events (beacon proximity update). */
|
|
28
|
-
export type BeaconRangingEvent = {
|
|
29
|
-
identifier: string;
|
|
30
|
-
uuid: string;
|
|
31
|
-
major: number;
|
|
32
|
-
minor: number;
|
|
33
|
-
rssi: number;
|
|
34
|
-
distance: number;
|
|
35
|
-
};
|
|
36
27
|
/** Payload for periodic distance update events during monitoring. */
|
|
37
28
|
export type BeaconDistanceEvent = {
|
|
38
29
|
identifier: string;
|
|
@@ -103,7 +94,6 @@ export type MonitoringOptions = {
|
|
|
103
94
|
export type ExpoBeaconModuleEvents = {
|
|
104
95
|
onBeaconEnter: (params: BeaconRegionEvent) => void;
|
|
105
96
|
onBeaconExit: (params: BeaconRegionEvent) => void;
|
|
106
|
-
onBeaconRanging: (params: BeaconRangingEvent) => void;
|
|
107
97
|
onBeaconDistance: (params: BeaconDistanceEvent) => void;
|
|
108
98
|
/** Fired continuously during a live scan as each beacon is detected. */
|
|
109
99
|
onBeaconFound: (params: BeaconScanResult) => void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ExpoBeacon.types.d.ts","sourceRoot":"","sources":["../src/ExpoBeacon.types.ts"],"names":[],"mappings":"AAAA,2CAA2C;AAC3C,MAAM,MAAM,gBAAgB,GAAG;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,+DAA+D;AAC/D,MAAM,MAAM,YAAY,GAAG;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,4CAA4C;AAC5C,MAAM,MAAM,iBAAiB,GAAG;IAC9B,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,OAAO,GAAG,MAAM,CAAC;IACxB,gFAAgF;IAChF,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,
|
|
1
|
+
{"version":3,"file":"ExpoBeacon.types.d.ts","sourceRoot":"","sources":["../src/ExpoBeacon.types.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ExpoBeacon.types.js","sourceRoot":"","sources":["../src/ExpoBeacon.types.ts"],"names":[],"mappings":"","sourcesContent":["/** Raw beacon discovered during a scan. */\r\nexport type BeaconScanResult = {\r\n uuid: string; // iBeacon proximity UUID (uppercase, formatted)\r\n major: number; // iBeacon major value (0–65535)\r\n minor: number; // iBeacon minor value (0–65535)\r\n rssi: number; // Signal strength in dBm (negative number)\r\n distance: number; // Estimated distance in meters\r\n txPower: number; // Calibrated TX power\r\n};\r\n\r\n/** A beacon that has been paired/registered for monitoring. */\r\nexport type PairedBeacon = {\r\n identifier: string; // User-defined label (e.g. \"lobby-door\")\r\n uuid: string;\r\n major: number;\r\n minor: number;\r\n};\r\n\r\n/** Payload for enter/exit region events. */\r\nexport type BeaconRegionEvent = {\r\n identifier: string; // Matches PairedBeacon.identifier\r\n uuid: string;\r\n major: number;\r\n minor: number;\r\n event: \"enter\" | \"exit\";\r\n /** Measured distance in metres at the time of the event (–1 if unavailable). */\r\n distance: number;\r\n};\r\n\r\n/** Payload for
|
|
1
|
+
{"version":3,"file":"ExpoBeacon.types.js","sourceRoot":"","sources":["../src/ExpoBeacon.types.ts"],"names":[],"mappings":"","sourcesContent":["/** Raw beacon discovered during a scan. */\r\nexport type BeaconScanResult = {\r\n uuid: string; // iBeacon proximity UUID (uppercase, formatted)\r\n major: number; // iBeacon major value (0–65535)\r\n minor: number; // iBeacon minor value (0–65535)\r\n rssi: number; // Signal strength in dBm (negative number)\r\n distance: number; // Estimated distance in meters\r\n txPower: number; // Calibrated TX power\r\n};\r\n\r\n/** A beacon that has been paired/registered for monitoring. */\r\nexport type PairedBeacon = {\r\n identifier: string; // User-defined label (e.g. \"lobby-door\")\r\n uuid: string;\r\n major: number;\r\n minor: number;\r\n};\r\n\r\n/** Payload for enter/exit region events. */\r\nexport type BeaconRegionEvent = {\r\n identifier: string; // Matches PairedBeacon.identifier\r\n uuid: string;\r\n major: number;\r\n minor: number;\r\n event: \"enter\" | \"exit\";\r\n /** Measured distance in metres at the time of the event (–1 if unavailable). */\r\n distance: number;\r\n};\r\n\r\n/** Payload for periodic distance update events during monitoring. */\r\nexport type BeaconDistanceEvent = {\r\n identifier: string;\r\n uuid: string;\r\n major: number;\r\n minor: number;\r\n distance: number;\r\n};\r\n\r\n/** Configuration for beacon enter/exit event notifications. */\r\nexport type BeaconNotificationConfig = {\r\n /** Whether to show enter/exit notifications. Default: true. */\r\n enabled?: boolean;\r\n /** Notification title on beacon enter. Default: \"Beacon Entered\". */\r\n enterTitle?: string;\r\n /** Notification title on beacon exit. Default: \"Beacon Exited\". */\r\n exitTitle?: string;\r\n /**\r\n * Notification body template. Supports {identifier} and {event} placeholders.\r\n * Default: \"{identifier} region {event}ed\".\r\n */\r\n body?: string;\r\n /** Play a sound with the notification (iOS only). Default: true. */\r\n sound?: boolean;\r\n /** Android drawable resource name for the notification icon (e.g. \"ic_notification\"). */\r\n icon?: string;\r\n};\r\n\r\n/** Configuration for the Android foreground service notification (persistent status bar entry). */\r\nexport type ForegroundServiceConfig = {\r\n /** Title of the persistent notification. Default: \"Beacon Monitoring Active\". */\r\n title?: string;\r\n /** Body text of the persistent notification. Default: \"Monitoring for iBeacons in the background\". */\r\n text?: string;\r\n /** Android drawable resource name for the notification icon. */\r\n icon?: string;\r\n};\r\n\r\n/** Configuration for the Android notification channel. */\r\nexport type NotificationChannelConfig = {\r\n /** Channel display name shown in system settings. Default: \"Beacon Monitoring\". */\r\n name?: string;\r\n /** Channel description shown in system settings. Default: \"Used for background iBeacon region monitoring\". */\r\n description?: string;\r\n /**\r\n * Channel importance level. Default: 'low'.\r\n * Note: Android may ignore decreases in importance after first channel creation until the app is reinstalled.\r\n */\r\n importance?: \"low\" | \"default\" | \"high\";\r\n};\r\n\r\n/** Combined notification configuration for all notification types. */\r\nexport type NotificationConfig = {\r\n /** Settings for beacon enter/exit event notifications. */\r\n beaconEvents?: BeaconNotificationConfig;\r\n /** Settings for the persistent foreground service notification (Android only). */\r\n foregroundService?: ForegroundServiceConfig;\r\n /** Settings for the Android notification channel (Android only). */\r\n channel?: NotificationChannelConfig;\r\n};\r\n\r\n/** Options accepted by startMonitoring(). */\r\nexport type MonitoringOptions = {\r\n /**\r\n * Maximum distance in metres for distance-based enter events.\r\n * Exit events are always emitted when the region is lost.\r\n */\r\n maxDistance?: number;\r\n /** Notification configuration overrides to apply for this monitoring session. */\r\n notifications?: NotificationConfig;\r\n};\r\n\r\n/** Module event map. */\r\nexport type ExpoBeaconModuleEvents = {\r\n onBeaconEnter: (params: BeaconRegionEvent) => void;\r\n onBeaconExit: (params: BeaconRegionEvent) => void;\r\n onBeaconDistance: (params: BeaconDistanceEvent) => void;\r\n /** Fired continuously during a live scan as each beacon is detected. */\r\n onBeaconFound: (params: BeaconScanResult) => void;\r\n};\r\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ExpoBeaconModule.d.ts","sourceRoot":"","sources":["../src/ExpoBeaconModule.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ExpoBeaconModule.d.ts","sourceRoot":"","sources":["../src/ExpoBeaconModule.ts"],"names":[],"mappings":"AA0FA,eAAe,MAAM,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ExpoBeaconModule.js","sourceRoot":"","sources":["../src/ExpoBeaconModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,mBAAmB,EAAE,MAAM,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"ExpoBeaconModule.js","sourceRoot":"","sources":["../src/ExpoBeaconModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,mBAAmB,EAAE,MAAM,MAAM,CAAC;AAgFzD,IAAI,CAAC;IACH,qDAAqD;IACrD,IAAI,MAAM,GAAG,mBAAmB,CAAmB,YAAY,CAAC,CAAC;AACnE,CAAC;AAAC,MAAM,CAAC;IACP,MAAM,IAAI,KAAK,CACb,oFAAoF;QAClF,8EAA8E,CACjF,CAAC;AACJ,CAAC;AAED,eAAe,MAAM,CAAC","sourcesContent":["import { NativeModule, requireNativeModule } from \"expo\";\r\n\r\nimport {\r\n ExpoBeaconModuleEvents,\r\n BeaconScanResult,\r\n PairedBeacon,\r\n NotificationConfig,\r\n MonitoringOptions,\r\n} from \"./ExpoBeacon.types\";\r\n\r\ndeclare class ExpoBeaconModule extends NativeModule<ExpoBeaconModuleEvents> {\r\n /**\r\n * Start a one-shot iBeacon scan. Resolves with discovered beacons after scanDuration ms.\r\n *\r\n * Pass one or more UUIDs to scan for specific beacons (uses CoreLocation on iOS).\r\n * Pass an empty array or omit to perform a wildcard scan that discovers all nearby\r\n * iBeacons (uses CoreBluetooth on iOS — foreground only).\r\n *\r\n * @param uuids Proximity UUIDs to filter by. Empty/omitted = wildcard scan.\r\n * @param scanDuration Duration in ms (default 5000)\r\n */\r\n scanForBeaconsAsync(\r\n uuids?: string[],\r\n scanDuration?: number,\r\n ): Promise<BeaconScanResult[]>;\r\n\r\n /**\r\n * Register a beacon for persistent region monitoring.\r\n */\r\n pairBeacon(\r\n identifier: string,\r\n uuid: string,\r\n major: number,\r\n minor: number,\r\n ): void;\r\n\r\n /**\r\n * Remove a previously paired beacon.\r\n */\r\n unpairBeacon(identifier: string): void;\r\n\r\n /**\r\n * Return all currently paired beacons.\r\n */\r\n getPairedBeacons(): PairedBeacon[];\r\n\r\n /**\r\n * Set persistent notification configuration. Settings are saved and applied to all\r\n * subsequent monitoring sessions until explicitly changed.\r\n */\r\n setNotificationConfig(config: NotificationConfig): void;\r\n\r\n /**\r\n * Start background region monitoring for all paired beacons.\r\n * On Android starts a foreground service.\r\n * On iOS starts CLLocationManager region monitoring.\r\n *\r\n * Accepts a plain number (backward-compatible maxDistance shorthand) or a\r\n * MonitoringOptions object with maxDistance and/or notification overrides.\r\n */\r\n startMonitoring(options?: MonitoringOptions | number): Promise<void>;\r\n\r\n /**\r\n * Stop background region monitoring.\r\n */\r\n stopMonitoring(): Promise<void>;\r\n\r\n /**\r\n * Start a continuous BLE scan. Fires `onBeaconFound` events as beacons are detected.\r\n * Call stopContinuousScan() to end the scan.\r\n */\r\n startContinuousScan(): void;\r\n\r\n /** Stop the continuous scan started by startContinuousScan(). */\r\n stopContinuousScan(): void;\r\n\r\n /** Request Bluetooth + Location permissions. Returns true if granted. */\r\n requestPermissionsAsync(): Promise<boolean>;\r\n}\r\n\r\ntry {\r\n // eslint-disable-next-line import/no-mutable-exports\r\n var module = requireNativeModule<ExpoBeaconModule>(\"ExpoBeacon\");\r\n} catch {\r\n throw new Error(\r\n \"expo-beacon: native module not found. Make sure you are using a development build \" +\r\n \"(not Expo Go) and have run `npx expo prebuild` followed by a native rebuild.\",\r\n );\r\n}\r\n\r\nexport default module;\r\n"]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { ExpoBeaconModuleEvents, BeaconScanResult, PairedBeacon } from "./ExpoBeacon.types";
|
|
2
2
|
declare const stub: {
|
|
3
|
-
scanForBeaconsAsync: (_scanDuration?: number) => Promise<BeaconScanResult[]>;
|
|
3
|
+
scanForBeaconsAsync: (_uuids: string[], _scanDuration?: number) => Promise<BeaconScanResult[]>;
|
|
4
4
|
pairBeacon: (_identifier: string, _uuid: string, _major: number, _minor: number) => void;
|
|
5
5
|
unpairBeacon: (_identifier: string) => void;
|
|
6
6
|
getPairedBeacons: () => PairedBeacon[];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ExpoBeaconModule.web.d.ts","sourceRoot":"","sources":["../src/ExpoBeaconModule.web.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,sBAAsB,EACtB,gBAAgB,EAChB,YAAY,EACb,MAAM,oBAAoB,CAAC;AAM5B,QAAA,MAAM,IAAI;
|
|
1
|
+
{"version":3,"file":"ExpoBeaconModule.web.d.ts","sourceRoot":"","sources":["../src/ExpoBeaconModule.web.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,sBAAsB,EACtB,gBAAgB,EAChB,YAAY,EACb,MAAM,oBAAoB,CAAC;AAM5B,QAAA,MAAM,IAAI;kCAEE,MAAM,EAAE,kBACA,MAAM,KACrB,OAAO,CAAC,gBAAgB,EAAE,CAAC;8BAEf,MAAM,SACZ,MAAM,UACL,MAAM,UACN,MAAM,KACb,IAAI;gCACqB,MAAM,KAAG,IAAI;4BACnB,YAAY,EAAE;2BACf,OAAO,CAAC,IAAI,CAAC;0BACd,OAAO,CAAC,IAAI,CAAC;mCACJ,OAAO,CAAC,OAAO,CAAC;8BACnB,MAAM,sBAAsB,aAAa,GAAG;;;qCAGrC,MAAM,sBAAsB;CAC9D,CAAC;AAEF,eAAe,IAAI,CAAC"}
|
|
@@ -2,7 +2,7 @@ const notSupported = () => {
|
|
|
2
2
|
throw new Error("expo-beacon is not supported on web.");
|
|
3
3
|
};
|
|
4
4
|
const stub = {
|
|
5
|
-
scanForBeaconsAsync: (_scanDuration) => notSupported(),
|
|
5
|
+
scanForBeaconsAsync: (_uuids, _scanDuration) => notSupported(),
|
|
6
6
|
pairBeacon: (_identifier, _uuid, _major, _minor) => notSupported(),
|
|
7
7
|
unpairBeacon: (_identifier) => notSupported(),
|
|
8
8
|
getPairedBeacons: () => notSupported(),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ExpoBeaconModule.web.js","sourceRoot":"","sources":["../src/ExpoBeaconModule.web.ts"],"names":[],"mappings":"AAMA,MAAM,YAAY,GAAG,GAAU,EAAE;IAC/B,MAAM,IAAI,KAAK,CAAC,sCAAsC,CAAC,CAAC;AAC1D,CAAC,CAAC;AAEF,MAAM,IAAI,GAAG;IACX,mBAAmB,EAAE,
|
|
1
|
+
{"version":3,"file":"ExpoBeaconModule.web.js","sourceRoot":"","sources":["../src/ExpoBeaconModule.web.ts"],"names":[],"mappings":"AAMA,MAAM,YAAY,GAAG,GAAU,EAAE;IAC/B,MAAM,IAAI,KAAK,CAAC,sCAAsC,CAAC,CAAC;AAC1D,CAAC,CAAC;AAEF,MAAM,IAAI,GAAG;IACX,mBAAmB,EAAE,CACnB,MAAgB,EAChB,aAAsB,EACO,EAAE,CAAC,YAAY,EAAE;IAChD,UAAU,EAAE,CACV,WAAmB,EACnB,KAAa,EACb,MAAc,EACd,MAAc,EACR,EAAE,CAAC,YAAY,EAAE;IACzB,YAAY,EAAE,CAAC,WAAmB,EAAQ,EAAE,CAAC,YAAY,EAAE;IAC3D,gBAAgB,EAAE,GAAmB,EAAE,CAAC,YAAY,EAAE;IACtD,eAAe,EAAE,GAAkB,EAAE,CAAC,YAAY,EAAE;IACpD,cAAc,EAAE,GAAkB,EAAE,CAAC,YAAY,EAAE;IACnD,uBAAuB,EAAE,GAAqB,EAAE,CAAC,YAAY,EAAE;IAC/D,WAAW,EAAE,CAAC,UAAwC,EAAE,SAAc,EAAE,EAAE,CAAC,CAAC;QAC1E,MAAM,EAAE,GAAG,EAAE,GAAE,CAAC;KACjB,CAAC;IACF,kBAAkB,EAAE,CAAC,UAAwC,EAAE,EAAE,GAAE,CAAC;CACrE,CAAC;AAEF,eAAe,IAAI,CAAC","sourcesContent":["import type {\r\n ExpoBeaconModuleEvents,\r\n BeaconScanResult,\r\n PairedBeacon,\r\n} from \"./ExpoBeacon.types\";\r\n\r\nconst notSupported = (): never => {\r\n throw new Error(\"expo-beacon is not supported on web.\");\r\n};\r\n\r\nconst stub = {\r\n scanForBeaconsAsync: (\r\n _uuids: string[],\r\n _scanDuration?: number,\r\n ): Promise<BeaconScanResult[]> => notSupported(),\r\n pairBeacon: (\r\n _identifier: string,\r\n _uuid: string,\r\n _major: number,\r\n _minor: number,\r\n ): void => notSupported(),\r\n unpairBeacon: (_identifier: string): void => notSupported(),\r\n getPairedBeacons: (): PairedBeacon[] => notSupported(),\r\n startMonitoring: (): Promise<void> => notSupported(),\r\n stopMonitoring: (): Promise<void> => notSupported(),\r\n requestPermissionsAsync: (): Promise<boolean> => notSupported(),\r\n addListener: (_eventName: keyof ExpoBeaconModuleEvents, _listener: any) => ({\r\n remove: () => {},\r\n }),\r\n removeAllListeners: (_eventName: keyof ExpoBeaconModuleEvents) => {},\r\n};\r\n\r\nexport default stub;\r\n"]}
|
package/build/index.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
export { default } from "./ExpoBeaconModule";
|
|
2
|
-
export type { BeaconScanResult, PairedBeacon, BeaconRegionEvent,
|
|
2
|
+
export type { BeaconScanResult, PairedBeacon, BeaconRegionEvent, BeaconDistanceEvent, ExpoBeaconModuleEvents, NotificationConfig, MonitoringOptions, BeaconNotificationConfig, ForegroundServiceConfig, NotificationChannelConfig, } from "./ExpoBeacon.types";
|
|
3
3
|
//# sourceMappingURL=index.d.ts.map
|
package/build/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAG7C,YAAY,EACV,gBAAgB,EAChB,YAAY,EACZ,iBAAiB,EACjB,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAG7C,YAAY,EACV,gBAAgB,EAChB,YAAY,EACZ,iBAAiB,EACjB,mBAAmB,EACnB,sBAAsB,EACtB,kBAAkB,EAClB,iBAAiB,EACjB,wBAAwB,EACxB,uBAAuB,EACvB,yBAAyB,GAC1B,MAAM,oBAAoB,CAAC"}
|
package/build/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,iCAAiC;AACjC,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC","sourcesContent":["// Native module (default export)\r\nexport { default } from \"./ExpoBeaconModule\";\r\n\r\n// All public types\r\nexport type {\r\n BeaconScanResult,\r\n PairedBeacon,\r\n BeaconRegionEvent,\r\n
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,iCAAiC;AACjC,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC","sourcesContent":["// Native module (default export)\r\nexport { default } from \"./ExpoBeaconModule\";\r\n\r\n// All public types\r\nexport type {\r\n BeaconScanResult,\r\n PairedBeacon,\r\n BeaconRegionEvent,\r\n BeaconDistanceEvent,\r\n ExpoBeaconModuleEvents,\r\n NotificationConfig,\r\n MonitoringOptions,\r\n BeaconNotificationConfig,\r\n ForegroundServiceConfig,\r\n NotificationChannelConfig,\r\n} from \"./ExpoBeacon.types\";\r\n"]}
|
package/ios/ExpoBeacon.podspec
CHANGED
|
@@ -19,7 +19,7 @@ Pod::Spec.new do |s|
|
|
|
19
19
|
|
|
20
20
|
s.dependency 'ExpoModulesCore'
|
|
21
21
|
|
|
22
|
-
# Required system frameworks for iBeacon monitoring
|
|
22
|
+
# Required system frameworks for iBeacon monitoring + wildcard BLE scanning
|
|
23
23
|
s.frameworks = 'CoreLocation', 'CoreBluetooth', 'UserNotifications'
|
|
24
24
|
|
|
25
25
|
# Swift/Objective-C compatibility
|