expo-beacon 0.6.12 → 0.6.13
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.
|
@@ -288,9 +288,10 @@ class BeaconForegroundService : Service(), BeaconConsumer {
|
|
|
288
288
|
missCounters.remove(region.uniqueId)
|
|
289
289
|
}
|
|
290
290
|
if (wasEntered) {
|
|
291
|
-
cancelTimeout(region.uniqueId)
|
|
292
291
|
sendBeaconBroadcast(region, "exit", -1.0)
|
|
293
292
|
showEnterExitNotification(region, "exit")
|
|
293
|
+
// OS-level exit safety net — start the timeout clock.
|
|
294
|
+
scheduleTimeoutIfConfigured(region)
|
|
294
295
|
}
|
|
295
296
|
}
|
|
296
297
|
|
|
@@ -329,13 +330,15 @@ class BeaconForegroundService : Service(), BeaconConsumer {
|
|
|
329
330
|
enteredRegions.add(region.uniqueId)
|
|
330
331
|
sendBeaconBroadcast(region, "enter", beacon.distance, beacon.rssi)
|
|
331
332
|
showEnterExitNotification(region, "enter")
|
|
332
|
-
|
|
333
|
+
// Beacon returned — cancel any running timeout timer.
|
|
334
|
+
cancelTimeout(region.uniqueId)
|
|
333
335
|
}
|
|
334
336
|
HysteresisAction.EXIT -> {
|
|
335
|
-
cancelTimeout(region.uniqueId)
|
|
336
337
|
enteredRegions.remove(region.uniqueId)
|
|
337
338
|
sendBeaconBroadcast(region, "exit", beacon.distance, beacon.rssi)
|
|
338
339
|
showEnterExitNotification(region, "exit")
|
|
340
|
+
// Beacon left — start the timeout clock.
|
|
341
|
+
scheduleTimeoutIfConfigured(region)
|
|
339
342
|
}
|
|
340
343
|
HysteresisAction.NONE -> {}
|
|
341
344
|
}
|
|
@@ -348,18 +351,14 @@ class BeaconForegroundService : Service(), BeaconConsumer {
|
|
|
348
351
|
missCounters[region.uniqueId] = count
|
|
349
352
|
|
|
350
353
|
if (enteredRegions.contains(region.uniqueId) && count >= EXIT_MISS_THRESHOLD) {
|
|
351
|
-
// Do NOT cancel the timeout here. A miss-based exit is triggered by BLE
|
|
352
|
-
// scan gaps (unreliable signal disappearance), not a confirmed physical
|
|
353
|
-
// departure. Cancelling the timeout here would prevent it from ever firing
|
|
354
|
-
// when the configured timeout (e.g. 25 s) exceeds the miss window (~21 s).
|
|
355
|
-
// The timeout runnable fires unconditionally; distance-based exits still
|
|
356
|
-
// call cancelTimeout() reliably when the beacon moves out of range.
|
|
357
354
|
enteredRegions.remove(region.uniqueId)
|
|
358
355
|
missCounters[region.uniqueId] = 0
|
|
359
356
|
enterCounters[region.uniqueId] = 0
|
|
360
357
|
exitCounters[region.uniqueId] = 0
|
|
361
358
|
sendBeaconBroadcast(region, "exit", -1.0)
|
|
362
359
|
showEnterExitNotification(region, "exit")
|
|
360
|
+
// Beacon disappeared — start the timeout clock.
|
|
361
|
+
scheduleTimeoutIfConfigured(region)
|
|
363
362
|
}
|
|
364
363
|
}
|
|
365
364
|
}
|
|
@@ -459,16 +458,10 @@ class BeaconForegroundService : Service(), BeaconConsumer {
|
|
|
459
458
|
|
|
460
459
|
private fun scheduleTimeoutIfConfigured(region: Region) {
|
|
461
460
|
val seconds = beaconTimeouts[region.uniqueId] ?: return
|
|
462
|
-
//
|
|
463
|
-
|
|
464
|
-
// must not restart the clock — doing so would defer the timeout indefinitely.
|
|
465
|
-
if (timeoutRunnables.containsKey(region.uniqueId)) return
|
|
461
|
+
// Cancel any existing timer so each exit resets the clock.
|
|
462
|
+
cancelTimeout(region.uniqueId)
|
|
466
463
|
val runnable = Runnable {
|
|
467
464
|
timeoutRunnables.remove(region.uniqueId)
|
|
468
|
-
// Fire unconditionally. A miss-based exit may have cleared enteredRegions before
|
|
469
|
-
// the timer elapsed (BLE gaps can cause false exits at ~21 s), but the beacon
|
|
470
|
-
// may still be physically present. Distance-based exits call cancelTimeout() so
|
|
471
|
-
// this runnable is never queued when the beacon has genuinely moved away.
|
|
472
465
|
sendBeaconBroadcast(region, "timeout", -1.0)
|
|
473
466
|
}
|
|
474
467
|
timeoutRunnables[region.uniqueId] = runnable
|
|
@@ -848,10 +848,10 @@ public class ExpoBeaconModule: Module {
|
|
|
848
848
|
"rssi": beaconRssi
|
|
849
849
|
])
|
|
850
850
|
postBeaconNotification(identifier: identifier, eventType: "enter")
|
|
851
|
-
|
|
851
|
+
// Beacon returned — cancel any running timeout timer.
|
|
852
|
+
cancelEddystoneTimeout(identifier: identifier)
|
|
852
853
|
case .exit:
|
|
853
854
|
smoothedDistances.removeValue(forKey: identifier)
|
|
854
|
-
cancelEddystoneTimeout(identifier: identifier)
|
|
855
855
|
sendLoggedEvent("onEddystoneExit", [
|
|
856
856
|
"identifier": identifier,
|
|
857
857
|
"namespace": ns,
|
|
@@ -861,6 +861,8 @@ public class ExpoBeaconModule: Module {
|
|
|
861
861
|
"rssi": beaconRssi
|
|
862
862
|
])
|
|
863
863
|
postBeaconNotification(identifier: identifier, eventType: "exit")
|
|
864
|
+
// Beacon left — start the timeout clock.
|
|
865
|
+
scheduleEddystoneTimeout(identifier: identifier, namespace: ns, instance: inst)
|
|
864
866
|
case .none:
|
|
865
867
|
break
|
|
866
868
|
}
|
|
@@ -1080,7 +1082,6 @@ public class ExpoBeaconModule: Module {
|
|
|
1080
1082
|
eddystoneExitCounters[identifier] = 0
|
|
1081
1083
|
eddystoneLatestSeen.removeValue(forKey: identifier)
|
|
1082
1084
|
smoothedDistances.removeValue(forKey: identifier)
|
|
1083
|
-
// Do NOT cancel the timeout here — same reason as iBeacon miss-based exit.
|
|
1084
1085
|
|
|
1085
1086
|
let ns = paired["namespace"] as? String ?? ""
|
|
1086
1087
|
let inst = paired["instance"] as? String ?? ""
|
|
@@ -1093,17 +1094,17 @@ public class ExpoBeaconModule: Module {
|
|
|
1093
1094
|
]
|
|
1094
1095
|
sendLoggedEvent("onEddystoneExit", params)
|
|
1095
1096
|
postBeaconNotification(identifier: identifier, eventType: "exit")
|
|
1097
|
+
// Beacon disappeared — start the timeout clock.
|
|
1098
|
+
scheduleEddystoneTimeout(identifier: identifier, namespace: ns, instance: inst)
|
|
1096
1099
|
}
|
|
1097
1100
|
}
|
|
1098
1101
|
}
|
|
1099
1102
|
|
|
1100
1103
|
// MARK: - Timeout timer helpers
|
|
1101
1104
|
|
|
1102
|
-
private func scheduleBeaconTimeout(identifier: String, beacon: CLBeacon) {
|
|
1103
|
-
//
|
|
1104
|
-
|
|
1105
|
-
// must not restart the clock — doing so would defer the timeout indefinitely.
|
|
1106
|
-
guard beaconTimeoutTimers[identifier] == nil else { return }
|
|
1105
|
+
private func scheduleBeaconTimeout(identifier: String, beacon: CLBeacon? = nil, region: CLBeaconRegion? = nil) {
|
|
1106
|
+
// Cancel any existing timer so each exit resets the clock.
|
|
1107
|
+
cancelBeaconTimeout(identifier: identifier)
|
|
1107
1108
|
|
|
1108
1109
|
let paired = loadPairedBeaconsRaw().first { ($0["identifier"] as? String) == identifier }
|
|
1109
1110
|
guard let seconds = paired?["timeoutSeconds"] as? Int, seconds > 0 else { return }
|
|
@@ -1111,11 +1112,7 @@ public class ExpoBeaconModule: Module {
|
|
|
1111
1112
|
let work = DispatchWorkItem { [weak self] in
|
|
1112
1113
|
guard let self = self else { return }
|
|
1113
1114
|
self.beaconTimeoutTimers.removeValue(forKey: identifier)
|
|
1114
|
-
|
|
1115
|
-
// the timer elapsed (ranging gaps can cause false exits), but the beacon may still
|
|
1116
|
-
// be physically present. Distance-based exits call cancelBeaconTimeout() so this
|
|
1117
|
-
// work item is cancelled before it runs on genuine out-of-range departures.
|
|
1118
|
-
self.sendLoggedEvent("onBeaconTimeout", self.makeBeaconEventParams(identifier: identifier, beacon: beacon))
|
|
1115
|
+
self.sendLoggedEvent("onBeaconTimeout", self.makeBeaconEventParams(identifier: identifier, beacon: beacon, region: region))
|
|
1119
1116
|
}
|
|
1120
1117
|
beaconTimeoutTimers[identifier] = work
|
|
1121
1118
|
DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(seconds), execute: work)
|
|
@@ -1126,8 +1123,8 @@ public class ExpoBeaconModule: Module {
|
|
|
1126
1123
|
}
|
|
1127
1124
|
|
|
1128
1125
|
private func scheduleEddystoneTimeout(identifier: String, namespace: String, instance: String) {
|
|
1129
|
-
//
|
|
1130
|
-
|
|
1126
|
+
// Cancel any existing timer so each exit resets the clock.
|
|
1127
|
+
cancelEddystoneTimeout(identifier: identifier)
|
|
1131
1128
|
|
|
1132
1129
|
let paired = loadPairedEddystonesRaw().first { ($0["identifier"] as? String) == identifier }
|
|
1133
1130
|
guard let seconds = paired?["timeoutSeconds"] as? Int, seconds > 0 else { return }
|
|
@@ -1135,7 +1132,6 @@ public class ExpoBeaconModule: Module {
|
|
|
1135
1132
|
let work = DispatchWorkItem { [weak self] in
|
|
1136
1133
|
guard let self = self else { return }
|
|
1137
1134
|
self.eddystoneTimeoutTimers.removeValue(forKey: identifier)
|
|
1138
|
-
// Fire unconditionally — same reason as iBeacon timeout.
|
|
1139
1135
|
self.sendLoggedEvent("onEddystoneTimeout", [
|
|
1140
1136
|
"identifier": identifier,
|
|
1141
1137
|
"namespace": namespace,
|
|
@@ -1362,12 +1358,14 @@ public class ExpoBeaconModule: Module {
|
|
|
1362
1358
|
case .enter:
|
|
1363
1359
|
sendLoggedEvent("onBeaconEnter", makeBeaconEventParams(identifier: identifier, beacon: beacon, event: "enter"))
|
|
1364
1360
|
postBeaconNotification(identifier: identifier, eventType: "enter")
|
|
1365
|
-
|
|
1361
|
+
// Beacon returned — cancel any running timeout timer.
|
|
1362
|
+
cancelBeaconTimeout(identifier: identifier)
|
|
1366
1363
|
case .exit:
|
|
1367
1364
|
smoothedDistances.removeValue(forKey: identifier)
|
|
1368
|
-
cancelBeaconTimeout(identifier: identifier)
|
|
1369
1365
|
sendLoggedEvent("onBeaconExit", makeBeaconEventParams(identifier: identifier, beacon: beacon, event: "exit"))
|
|
1370
1366
|
postBeaconNotification(identifier: identifier, eventType: "exit")
|
|
1367
|
+
// Beacon left — start the timeout clock.
|
|
1368
|
+
scheduleBeaconTimeout(identifier: identifier, beacon: beacon)
|
|
1371
1369
|
case .none:
|
|
1372
1370
|
break
|
|
1373
1371
|
}
|
|
@@ -1389,16 +1387,13 @@ public class ExpoBeaconModule: Module {
|
|
|
1389
1387
|
enterCounters[identifier] = 0
|
|
1390
1388
|
exitCounters[identifier] = 0
|
|
1391
1389
|
smoothedDistances.removeValue(forKey: identifier)
|
|
1392
|
-
// Do NOT cancel the timeout here. A miss-based exit is triggered by
|
|
1393
|
-
// ranging gaps (e.g. accuracy == -1), not a confirmed physical departure.
|
|
1394
|
-
// Cancelling here prevents the timeout from firing when it is longer than
|
|
1395
|
-
// the miss window. Distance-based exits call cancelBeaconTimeout() so the
|
|
1396
|
-
// timer is still cancelled on genuine out-of-range events.
|
|
1397
1390
|
|
|
1398
1391
|
// Look up region info for the exit event payload
|
|
1399
1392
|
let region = monitoredRegions.first { $0.identifier == identifier }
|
|
1400
1393
|
sendLoggedEvent("onBeaconExit", makeBeaconEventParams(identifier: identifier, region: region, event: "exit"))
|
|
1401
1394
|
postBeaconNotification(identifier: identifier, eventType: "exit")
|
|
1395
|
+
// Beacon disappeared — start the timeout clock.
|
|
1396
|
+
scheduleBeaconTimeout(identifier: identifier, region: region)
|
|
1402
1397
|
}
|
|
1403
1398
|
}
|
|
1404
1399
|
return
|
|
@@ -1441,9 +1436,10 @@ public class ExpoBeaconModule: Module {
|
|
|
1441
1436
|
missCounters.removeValue(forKey: identifier)
|
|
1442
1437
|
smoothedDistances.removeValue(forKey: identifier)
|
|
1443
1438
|
if wasEntered {
|
|
1444
|
-
cancelBeaconTimeout(identifier: identifier)
|
|
1445
1439
|
sendLoggedEvent("onBeaconExit", makeBeaconEventParams(identifier: identifier, region: beaconRegion, event: "exit"))
|
|
1446
1440
|
postBeaconNotification(identifier: identifier, eventType: "exit")
|
|
1441
|
+
// OS-level exit safety net — start the timeout clock.
|
|
1442
|
+
scheduleBeaconTimeout(identifier: identifier, region: beaconRegion)
|
|
1447
1443
|
}
|
|
1448
1444
|
}
|
|
1449
1445
|
|