expo-beacon 0.7.2 → 0.7.3
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/android/src/main/java/expo/modules/beacon/BeaconEventPlugin.kt +17 -0
- package/android/src/main/java/expo/modules/beacon/BeaconForegroundService.kt +15 -0
- package/android/src/main/java/expo/modules/beacon/BeaconPluginRegistry.kt +61 -0
- package/ios/BeaconLifecycleDelegate.swift +54 -0
- package/ios/ExpoBeaconModule.swift +44 -0
- package/package.json +1 -1
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
package expo.modules.beacon
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Implement this interface in your app to react to beacon enter/exit events at the native level.
|
|
5
|
+
*
|
|
6
|
+
* Register your implementation via [BeaconPluginRegistry.register] from MainApplication.onCreate().
|
|
7
|
+
* Unregister via [BeaconPluginRegistry.unregister] if the plugin has a scoped lifetime.
|
|
8
|
+
*/
|
|
9
|
+
interface BeaconEventPlugin {
|
|
10
|
+
// iBeacon
|
|
11
|
+
fun onBeaconEnter(identifier: String, uuid: String, major: Int, minor: Int, distance: Double)
|
|
12
|
+
fun onBeaconExit(identifier: String, uuid: String, major: Int, minor: Int, distance: Double)
|
|
13
|
+
|
|
14
|
+
// Eddystone
|
|
15
|
+
fun onEddystoneEnter(identifier: String, namespace: String, instance: String, distance: Double)
|
|
16
|
+
fun onEddystoneExit(identifier: String, namespace: String, instance: String, distance: Double)
|
|
17
|
+
}
|
|
@@ -614,6 +614,21 @@ class BeaconForegroundService : Service(), BeaconConsumer {
|
|
|
614
614
|
// Forward all produced events to remote API
|
|
615
615
|
apiForwarder?.forwardEvent(params)
|
|
616
616
|
|
|
617
|
+
// Dispatch enter/exit to registered plugins (e.g. to start/stop BGLocation)
|
|
618
|
+
if (eventType == "enter" || eventType == "exit") {
|
|
619
|
+
val identifier = region.uniqueId
|
|
620
|
+
val uuid = if (!isEddystone) id1Str else ""
|
|
621
|
+
val major = if (!isEddystone) region.id2?.toInt() ?: 0 else 0
|
|
622
|
+
val minor = if (!isEddystone) region.id3?.toInt() ?: 0 else 0
|
|
623
|
+
val namespace = if (isEddystone) id1Str.removePrefix("0x") else ""
|
|
624
|
+
val instance = if (isEddystone) region.id2?.toString()?.removePrefix("0x") ?: "" else ""
|
|
625
|
+
if (eventType == "enter") {
|
|
626
|
+
BeaconPluginRegistry.dispatchEnter(isEddystone, identifier, uuid, major, minor, namespace, instance, distance)
|
|
627
|
+
} else {
|
|
628
|
+
BeaconPluginRegistry.dispatchExit(isEddystone, identifier, uuid, major, minor, namespace, instance, distance)
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
|
|
617
632
|
val intent = Intent(ACTION_BEACON_EVENT).apply {
|
|
618
633
|
putExtra("identifier", region.uniqueId)
|
|
619
634
|
putExtra("event", eventType)
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
package expo.modules.beacon
|
|
2
|
+
|
|
3
|
+
import java.util.concurrent.CopyOnWriteArrayList
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Thread-safe singleton registry for [BeaconEventPlugin] implementations.
|
|
7
|
+
*
|
|
8
|
+
* Register your plugin once from MainApplication.onCreate():
|
|
9
|
+
* BeaconPluginRegistry.register(MyBeaconPlugin(this))
|
|
10
|
+
*
|
|
11
|
+
* If no plugins are registered, all dispatch calls are no-ops.
|
|
12
|
+
*/
|
|
13
|
+
object BeaconPluginRegistry {
|
|
14
|
+
private val plugins = CopyOnWriteArrayList<BeaconEventPlugin>()
|
|
15
|
+
|
|
16
|
+
fun register(plugin: BeaconEventPlugin) {
|
|
17
|
+
plugins.add(plugin)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
fun unregister(plugin: BeaconEventPlugin) {
|
|
21
|
+
plugins.remove(plugin)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
internal fun dispatchEnter(
|
|
25
|
+
isEddystone: Boolean,
|
|
26
|
+
identifier: String,
|
|
27
|
+
uuid: String,
|
|
28
|
+
major: Int,
|
|
29
|
+
minor: Int,
|
|
30
|
+
namespace: String,
|
|
31
|
+
instance: String,
|
|
32
|
+
distance: Double,
|
|
33
|
+
) {
|
|
34
|
+
plugins.forEach { plugin ->
|
|
35
|
+
if (isEddystone) {
|
|
36
|
+
plugin.onEddystoneEnter(identifier, namespace, instance, distance)
|
|
37
|
+
} else {
|
|
38
|
+
plugin.onBeaconEnter(identifier, uuid, major, minor, distance)
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
internal fun dispatchExit(
|
|
44
|
+
isEddystone: Boolean,
|
|
45
|
+
identifier: String,
|
|
46
|
+
uuid: String,
|
|
47
|
+
major: Int,
|
|
48
|
+
minor: Int,
|
|
49
|
+
namespace: String,
|
|
50
|
+
instance: String,
|
|
51
|
+
distance: Double,
|
|
52
|
+
) {
|
|
53
|
+
plugins.forEach { plugin ->
|
|
54
|
+
if (isEddystone) {
|
|
55
|
+
plugin.onEddystoneExit(identifier, namespace, instance, distance)
|
|
56
|
+
} else {
|
|
57
|
+
plugin.onBeaconExit(identifier, uuid, major, minor, distance)
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/// Implement this protocol in your app to react to beacon enter/exit events at the native level.
|
|
2
|
+
///
|
|
3
|
+
/// Register your implementation once, before the Expo module is created:
|
|
4
|
+
/// BeaconLifecycleRegistry.register(MyPlugin())
|
|
5
|
+
///
|
|
6
|
+
/// The registry holds a strong reference — register early (e.g. AppDelegate.didFinishLaunching
|
|
7
|
+
/// before super, or in a +load / initialize method).
|
|
8
|
+
public protocol BeaconLifecycleDelegate: AnyObject {
|
|
9
|
+
// MARK: iBeacon
|
|
10
|
+
func beaconDidEnter(identifier: String, uuid: String, major: Int, minor: Int, distance: Double)
|
|
11
|
+
func beaconDidExit(identifier: String, uuid: String, major: Int, minor: Int, distance: Double)
|
|
12
|
+
|
|
13
|
+
// MARK: Eddystone
|
|
14
|
+
func eddystoneDidEnter(identifier: String, namespace: String, instance: String, distance: Double)
|
|
15
|
+
func eddystoneDidExit(identifier: String, namespace: String, instance: String, distance: Double)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/// Thread-safe registry for [BeaconLifecycleDelegate] plugins.
|
|
19
|
+
/// Mirrors the Android BeaconPluginRegistry pattern so both platforms use the same app-side wiring.
|
|
20
|
+
public final class BeaconLifecycleRegistry {
|
|
21
|
+
public static let shared = BeaconLifecycleRegistry()
|
|
22
|
+
private init() {}
|
|
23
|
+
|
|
24
|
+
private let lock = NSLock()
|
|
25
|
+
private var plugins: [any BeaconLifecycleDelegate] = []
|
|
26
|
+
|
|
27
|
+
public static func register(_ plugin: any BeaconLifecycleDelegate) {
|
|
28
|
+
shared.lock.lock(); defer { shared.lock.unlock() }
|
|
29
|
+
shared.plugins.append(plugin)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
public static func unregister(_ plugin: any BeaconLifecycleDelegate) {
|
|
33
|
+
shared.lock.lock(); defer { shared.lock.unlock() }
|
|
34
|
+
shared.plugins.removeAll { $0 === plugin }
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
internal func dispatchEnter(identifier: String, uuid: String, major: Int, minor: Int, distance: Double) {
|
|
38
|
+
snapshot().forEach { $0.beaconDidEnter(identifier: identifier, uuid: uuid, major: major, minor: minor, distance: distance) }
|
|
39
|
+
}
|
|
40
|
+
internal func dispatchExit(identifier: String, uuid: String, major: Int, minor: Int, distance: Double) {
|
|
41
|
+
snapshot().forEach { $0.beaconDidExit(identifier: identifier, uuid: uuid, major: major, minor: minor, distance: distance) }
|
|
42
|
+
}
|
|
43
|
+
internal func dispatchEddystoneEnter(identifier: String, namespace: String, instance: String, distance: Double) {
|
|
44
|
+
snapshot().forEach { $0.eddystoneDidEnter(identifier: identifier, namespace: namespace, instance: instance, distance: distance) }
|
|
45
|
+
}
|
|
46
|
+
internal func dispatchEddystoneExit(identifier: String, namespace: String, instance: String, distance: Double) {
|
|
47
|
+
snapshot().forEach { $0.eddystoneDidExit(identifier: identifier, namespace: namespace, instance: instance, distance: distance) }
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
private func snapshot() -> [any BeaconLifecycleDelegate] {
|
|
51
|
+
lock.lock(); defer { lock.unlock() }
|
|
52
|
+
return plugins
|
|
53
|
+
}
|
|
54
|
+
}
|
|
@@ -123,6 +123,8 @@ public class ExpoBeaconModule: Module {
|
|
|
123
123
|
private var eventLogger: BeaconEventLogger?
|
|
124
124
|
private var loggingEnabled = false
|
|
125
125
|
|
|
126
|
+
|
|
127
|
+
|
|
126
128
|
// Native API forwarder (fire-and-forget HTTP)
|
|
127
129
|
private lazy var apiForwarder = BeaconApiForwarder(defaults: defaults)
|
|
128
130
|
|
|
@@ -1038,9 +1040,51 @@ public class ExpoBeaconModule: Module {
|
|
|
1038
1040
|
}
|
|
1039
1041
|
// Forward all produced events to remote API
|
|
1040
1042
|
apiForwarder.forwardEvent(params)
|
|
1043
|
+
// Dispatch enter/exit to registered plugins (e.g. to start/stop BGLocation)
|
|
1044
|
+
dispatchToLifecycleRegistry(eventName: eventName, params: params)
|
|
1041
1045
|
sendEvent(eventName, params)
|
|
1042
1046
|
}
|
|
1043
1047
|
|
|
1048
|
+
private func dispatchToLifecycleRegistry(eventName: String, params: [String: Any]) {
|
|
1049
|
+
let r = BeaconLifecycleRegistry.shared
|
|
1050
|
+
let identifier = params["identifier"] as? String ?? ""
|
|
1051
|
+
let distance = params["distance"] as? Double ?? -1.0
|
|
1052
|
+
switch eventName {
|
|
1053
|
+
case "onBeaconEnter":
|
|
1054
|
+
r.dispatchEnter(
|
|
1055
|
+
identifier: identifier,
|
|
1056
|
+
uuid: params["uuid"] as? String ?? "",
|
|
1057
|
+
major: params["major"] as? Int ?? 0,
|
|
1058
|
+
minor: params["minor"] as? Int ?? 0,
|
|
1059
|
+
distance: distance
|
|
1060
|
+
)
|
|
1061
|
+
case "onBeaconExit":
|
|
1062
|
+
r.dispatchExit(
|
|
1063
|
+
identifier: identifier,
|
|
1064
|
+
uuid: params["uuid"] as? String ?? "",
|
|
1065
|
+
major: params["major"] as? Int ?? 0,
|
|
1066
|
+
minor: params["minor"] as? Int ?? 0,
|
|
1067
|
+
distance: distance
|
|
1068
|
+
)
|
|
1069
|
+
case "onEddystoneEnter":
|
|
1070
|
+
r.dispatchEddystoneEnter(
|
|
1071
|
+
identifier: identifier,
|
|
1072
|
+
namespace: params["namespace"] as? String ?? "",
|
|
1073
|
+
instance: params["instance"] as? String ?? "",
|
|
1074
|
+
distance: distance
|
|
1075
|
+
)
|
|
1076
|
+
case "onEddystoneExit":
|
|
1077
|
+
r.dispatchEddystoneExit(
|
|
1078
|
+
identifier: identifier,
|
|
1079
|
+
namespace: params["namespace"] as? String ?? "",
|
|
1080
|
+
instance: params["instance"] as? String ?? "",
|
|
1081
|
+
distance: distance
|
|
1082
|
+
)
|
|
1083
|
+
default:
|
|
1084
|
+
break
|
|
1085
|
+
}
|
|
1086
|
+
}
|
|
1087
|
+
|
|
1044
1088
|
private func getOrCreateEventLogger() -> BeaconEventLogger {
|
|
1045
1089
|
if let logger = eventLogger {
|
|
1046
1090
|
return logger
|