expo-beacon 0.7.1 → 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/AndroidManifest.xml +7 -3
- package/android/src/main/java/expo/modules/beacon/BeaconEventPlugin.kt +17 -0
- package/android/src/main/java/expo/modules/beacon/BeaconForegroundService.kt +67 -4
- package/android/src/main/java/expo/modules/beacon/BeaconPluginRegistry.kt +61 -0
- package/android/src/main/java/expo/modules/beacon/BootReceiver.kt +75 -3
- package/ios/BeaconLifecycleDelegate.swift +54 -0
- package/ios/ExpoBeaconModule.swift +44 -0
- package/package.json +1 -1
|
@@ -48,13 +48,17 @@
|
|
|
48
48
|
android:foregroundServiceType="connectedDevice"
|
|
49
49
|
android:exported="false" />
|
|
50
50
|
|
|
51
|
-
<!-- Restart monitoring after boot
|
|
51
|
+
<!-- Restart monitoring after boot.
|
|
52
|
+
android:exported="true" is required for the system to deliver BOOT_COMPLETED.
|
|
53
|
+
No android:permission — the incorrect RECEIVE_BOOT_COMPLETED attribute was
|
|
54
|
+
restricting incoming broadcasts instead of restricting who can receive them. -->
|
|
52
55
|
<receiver
|
|
53
56
|
android:name="expo.modules.beacon.BootReceiver"
|
|
54
|
-
android:exported="true"
|
|
55
|
-
android:permission="android.permission.RECEIVE_BOOT_COMPLETED">
|
|
57
|
+
android:exported="true">
|
|
56
58
|
<intent-filter>
|
|
57
59
|
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
|
60
|
+
<!-- Self-scheduled retry when BT isn't ready at boot -->
|
|
61
|
+
<action android:name="expo.modules.beacon.ACTION_RETRY_MONITORING" />
|
|
58
62
|
</intent-filter>
|
|
59
63
|
</receiver>
|
|
60
64
|
</application>
|
|
@@ -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
|
+
}
|
|
@@ -112,10 +112,17 @@ class BeaconForegroundService : Service(), BeaconConsumer {
|
|
|
112
112
|
startForeground(FOREGROUND_NOTIF_ID, buildForegroundNotification())
|
|
113
113
|
}
|
|
114
114
|
} catch (e: Exception) {
|
|
115
|
-
// SecurityException on Android 14+ if BT permissions
|
|
116
|
-
// or
|
|
117
|
-
|
|
118
|
-
|
|
115
|
+
// SecurityException on Android 14+ if BT runtime permissions weren't yet granted,
|
|
116
|
+
// or ForegroundServiceStartNotAllowedException on Android 12+ / Android 17 beta
|
|
117
|
+
// if the service start window was missed (e.g. BT not yet initialized at boot).
|
|
118
|
+
val retryCount = intent?.getIntExtra(EXTRA_RETRY_COUNT, 0) ?: 0
|
|
119
|
+
Log.e(TAG, "startForeground failed (retry=$retryCount) — stopping service", e)
|
|
120
|
+
sendErrorBroadcast(null, "SERVICE_START_FAILED", "startForeground failed (retry=$retryCount): ${e.message}")
|
|
121
|
+
// Schedule a retry so monitoring can recover without user interaction, capped to
|
|
122
|
+
// avoid infinite crash loops. Only retry if monitoring should still be active.
|
|
123
|
+
if (retryCount < MAX_STARTFOREGROUND_RETRIES && isMonitoringActive(this)) {
|
|
124
|
+
scheduleServiceRetry(retryCount + 1)
|
|
125
|
+
}
|
|
119
126
|
stopSelf()
|
|
120
127
|
return START_NOT_STICKY
|
|
121
128
|
}
|
|
@@ -129,6 +136,43 @@ class BeaconForegroundService : Service(), BeaconConsumer {
|
|
|
129
136
|
return START_STICKY
|
|
130
137
|
}
|
|
131
138
|
|
|
139
|
+
/**
|
|
140
|
+
* Schedules a one-shot alarm to restart this service via startForegroundService().
|
|
141
|
+
* Used when startForeground() fails transiently (e.g. BT not yet ready at boot).
|
|
142
|
+
*/
|
|
143
|
+
private fun scheduleServiceRetry(retryCount: Int) {
|
|
144
|
+
val alarmManager = getSystemService(AlarmManager::class.java) ?: return
|
|
145
|
+
val retryIntent = Intent(this, BeaconForegroundService::class.java)
|
|
146
|
+
.putExtra(EXTRA_RETRY_COUNT, retryCount)
|
|
147
|
+
val pendingIntent = PendingIntent.getService(
|
|
148
|
+
this,
|
|
149
|
+
RETRY_SERVICE_REQUEST_CODE,
|
|
150
|
+
retryIntent,
|
|
151
|
+
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
|
|
152
|
+
)
|
|
153
|
+
alarmManager.setExactAndAllowWhileIdle(
|
|
154
|
+
AlarmManager.ELAPSED_REALTIME_WAKEUP,
|
|
155
|
+
SystemClock.elapsedRealtime() + RETRY_DELAY_MS,
|
|
156
|
+
pendingIntent
|
|
157
|
+
)
|
|
158
|
+
Log.w(TAG, "startForeground retry $retryCount scheduled in ${RETRY_DELAY_MS}ms")
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Called by the system on Android 14+ (API 34) if a foreground service with a time-limited
|
|
163
|
+
* type exceeds its allowed duration. connectedDevice has no documented time limit as of
|
|
164
|
+
* Android 17, but this override ensures we handle a future limit gracefully rather than
|
|
165
|
+
* receiving an ANR.
|
|
166
|
+
*/
|
|
167
|
+
@Suppress("NewApi")
|
|
168
|
+
override fun onTimeout(startId: Int, fgsType: Int) {
|
|
169
|
+
Log.w(TAG, "BeaconForegroundService.onTimeout(startId=$startId, fgsType=$fgsType) — scheduling restart")
|
|
170
|
+
if (isMonitoringActive(this)) {
|
|
171
|
+
scheduleServiceRetry(0)
|
|
172
|
+
}
|
|
173
|
+
stopSelf()
|
|
174
|
+
}
|
|
175
|
+
|
|
132
176
|
override fun onBeaconServiceConnect() {
|
|
133
177
|
serviceConnected = true
|
|
134
178
|
// Read max distance, exit distance, and min RSSI from options prefs
|
|
@@ -570,6 +614,21 @@ class BeaconForegroundService : Service(), BeaconConsumer {
|
|
|
570
614
|
// Forward all produced events to remote API
|
|
571
615
|
apiForwarder?.forwardEvent(params)
|
|
572
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
|
+
|
|
573
632
|
val intent = Intent(ACTION_BEACON_EVENT).apply {
|
|
574
633
|
putExtra("identifier", region.uniqueId)
|
|
575
634
|
putExtra("event", eventType)
|
|
@@ -736,6 +795,10 @@ class BeaconForegroundService : Service(), BeaconConsumer {
|
|
|
736
795
|
const val DISTANCE_JUMP_FACTOR = 5.0
|
|
737
796
|
|
|
738
797
|
private const val PREF_IS_MONITORING = "expo.beacon.is_monitoring"
|
|
798
|
+
private const val EXTRA_RETRY_COUNT = "retryCount"
|
|
799
|
+
private const val MAX_STARTFOREGROUND_RETRIES = 3
|
|
800
|
+
private const val RETRY_DELAY_MS = 10_000L
|
|
801
|
+
private const val RETRY_SERVICE_REQUEST_CODE = 0x42454143 // "BEAC"
|
|
739
802
|
@Volatile private var activeService: BeaconForegroundService? = null
|
|
740
803
|
|
|
741
804
|
fun start(context: Context) {
|
|
@@ -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
|
+
}
|
|
@@ -1,17 +1,89 @@
|
|
|
1
1
|
package expo.modules.beacon
|
|
2
2
|
|
|
3
|
+
import android.app.ActivityManager
|
|
4
|
+
import android.app.AlarmManager
|
|
5
|
+
import android.app.PendingIntent
|
|
3
6
|
import android.content.BroadcastReceiver
|
|
4
7
|
import android.content.Context
|
|
5
8
|
import android.content.Intent
|
|
9
|
+
import android.os.Build
|
|
10
|
+
import android.os.SystemClock
|
|
11
|
+
import android.util.Log
|
|
12
|
+
|
|
13
|
+
private const val ACTION_RETRY_MONITORING = "expo.modules.beacon.ACTION_RETRY_MONITORING"
|
|
14
|
+
private const val RETRY_DELAY_MS = 10_000L
|
|
15
|
+
private const val RETRY_REQUEST_CODE = 0x424F4F54 // "BOOT"
|
|
6
16
|
|
|
7
17
|
/**
|
|
8
18
|
* Restarts beacon monitoring after device reboot if it was active before shutdown.
|
|
19
|
+
*
|
|
20
|
+
* Also handles ACTION_RETRY_MONITORING — a self-scheduled alarm used to retry the
|
|
21
|
+
* service start if Bluetooth is not yet fully initialized when BOOT_COMPLETED fires
|
|
22
|
+
* (observed on Android 17 beta where startForegroundService() throws SecurityException
|
|
23
|
+
* or ForegroundServiceStartNotAllowedException shortly after boot).
|
|
9
24
|
*/
|
|
10
25
|
class BootReceiver : BroadcastReceiver() {
|
|
11
26
|
override fun onReceive(context: Context, intent: Intent) {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
27
|
+
when (intent.action) {
|
|
28
|
+
Intent.ACTION_BOOT_COMPLETED -> {
|
|
29
|
+
logMemoryKillDiagnostics(context)
|
|
30
|
+
if (BeaconForegroundService.isMonitoringActive(context)) {
|
|
31
|
+
tryStartService(context)
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
ACTION_RETRY_MONITORING -> {
|
|
35
|
+
if (BeaconForegroundService.isMonitoringActive(context)) {
|
|
36
|
+
tryStartService(context)
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
private fun tryStartService(context: Context) {
|
|
43
|
+
try {
|
|
44
|
+
BeaconForegroundService.start(context)
|
|
45
|
+
Log.d(TAG, "BootReceiver: BeaconForegroundService started successfully")
|
|
46
|
+
} catch (e: SecurityException) {
|
|
47
|
+
// ForegroundServiceStartNotAllowedException extends SecurityException.
|
|
48
|
+
// Can occur on Android 17 beta if Bluetooth is not yet fully initialized at boot.
|
|
49
|
+
Log.e(TAG, "BootReceiver: Failed to start service (SecurityException) — retrying in ${RETRY_DELAY_MS}ms", e)
|
|
50
|
+
scheduleRetry(context)
|
|
51
|
+
} catch (e: Exception) {
|
|
52
|
+
Log.e(TAG, "BootReceiver: Failed to start service — retrying in ${RETRY_DELAY_MS}ms", e)
|
|
53
|
+
scheduleRetry(context)
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
private fun scheduleRetry(context: Context) {
|
|
58
|
+
val alarmManager = context.getSystemService(AlarmManager::class.java) ?: return
|
|
59
|
+
val retryIntent = Intent(context, BootReceiver::class.java).apply {
|
|
60
|
+
action = ACTION_RETRY_MONITORING
|
|
61
|
+
}
|
|
62
|
+
val pendingIntent = PendingIntent.getBroadcast(
|
|
63
|
+
context,
|
|
64
|
+
RETRY_REQUEST_CODE,
|
|
65
|
+
retryIntent,
|
|
66
|
+
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
|
|
67
|
+
)
|
|
68
|
+
alarmManager.setExactAndAllowWhileIdle(
|
|
69
|
+
AlarmManager.ELAPSED_REALTIME_WAKEUP,
|
|
70
|
+
SystemClock.elapsedRealtime() + RETRY_DELAY_MS,
|
|
71
|
+
pendingIntent
|
|
72
|
+
)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Logs if the previous process was killed by Android 17's app memory limits
|
|
77
|
+
* (ApplicationExitInfo.description contains "MemoryLimiter") so the cause is
|
|
78
|
+
* visible in Logcat when the service restarts after being killed.
|
|
79
|
+
*/
|
|
80
|
+
private fun logMemoryKillDiagnostics(context: Context) {
|
|
81
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
|
82
|
+
val am = context.getSystemService(ActivityManager::class.java)
|
|
83
|
+
am?.getHistoricalProcessExitReasons(null, 0, 5)?.forEach { info ->
|
|
84
|
+
if (info.description?.contains("MemoryLimiter") == true) {
|
|
85
|
+
Log.w(TAG, "BootReceiver: previous process killed by Android 17 memory limits: ${info.description}")
|
|
86
|
+
}
|
|
15
87
|
}
|
|
16
88
|
}
|
|
17
89
|
}
|
|
@@ -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
|