expo-beacon 0.8.0 → 0.8.2
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 +9 -0
- package/android/src/main/java/expo/modules/beacon/BeaconApiForwarder.kt +0 -3
- package/android/src/main/java/expo/modules/beacon/BeaconForegroundService.kt +193 -6
- package/android/src/main/java/expo/modules/beacon/BootReceiver.kt +15 -0
- package/android/src/main/java/expo/modules/beacon/ExpoBeaconModule.kt +40 -27
- package/build/ExpoBeaconModule.d.ts +35 -2
- package/build/ExpoBeaconModule.d.ts.map +1 -1
- package/build/ExpoBeaconModule.js.map +1 -1
- package/build/ExpoBeaconModule.web.d.ts +2 -0
- package/build/ExpoBeaconModule.web.d.ts.map +1 -1
- package/build/ExpoBeaconModule.web.js +2 -0
- package/build/ExpoBeaconModule.web.js.map +1 -1
- package/ios/BeaconApiForwarder.swift +0 -5
- package/ios/BeaconCarPlaySceneDelegate.swift +78 -0
- package/ios/BluetoothDelegate.swift +43 -0
- package/ios/CarPlayMonitor.swift +94 -3
- package/ios/ExpoBeacon.podspec +5 -2
- package/ios/ExpoBeaconConstants.swift +41 -0
- package/ios/ExpoBeaconModule+CarPlay.swift +49 -0
- package/ios/ExpoBeaconModule+Eddystone.swift +365 -0
- package/ios/ExpoBeaconModule+EventLogging.swift +98 -0
- package/ios/ExpoBeaconModule+Monitoring.swift +383 -0
- package/ios/ExpoBeaconModule+Notifications.swift +59 -0
- package/ios/ExpoBeaconModule+Permissions.swift +55 -0
- package/ios/ExpoBeaconModule+Scanning.swift +54 -0
- package/ios/ExpoBeaconModule+Storage.swift +97 -0
- package/ios/ExpoBeaconModule+Timers.swift +100 -0
- package/ios/ExpoBeaconModule.swift +122 -1364
- package/ios/LocationDelegate.swift +48 -0
- package/package.json +1 -1
- package/plugin/build/index.d.ts +5 -1
- package/plugin/build/index.d.ts.map +1 -1
- package/plugin/build/index.js +3 -2
- package/plugin/build/withBeaconIOS.d.ts +19 -1
- package/plugin/build/withBeaconIOS.d.ts.map +1 -1
- package/plugin/build/withBeaconIOS.js +41 -1
- package/src/ExpoBeaconModule.ts +37 -2
- package/src/ExpoBeaconModule.web.ts +2 -0
package/README.md
CHANGED
|
@@ -729,11 +729,20 @@ disconnectSub.remove();
|
|
|
729
729
|
- Connect/disconnect events flow through the same SQLite event log and remote API forwarder as beacon events.
|
|
730
730
|
- When the config plugin is installed, the auto-generated `BeaconGeoPlugin` also calls `BackgroundGeolocation.start()` on connect and `.stop()` on disconnect — no extra wiring required.
|
|
731
731
|
|
|
732
|
+
**Background detection**
|
|
733
|
+
|
|
734
|
+
CarPlay observation is **persistent** — the enabled flag is stored in native preferences and the observer is automatically re-attached after app kill or device reboot. `startMonitoring()` also enables CarPlay observation by default; calling `startCarPlayMonitoring()` explicitly is only required if you want CarPlay events without beacon monitoring.
|
|
735
|
+
|
|
736
|
+
- **Android:** the foreground service hosts the `CarConnection` observer. As long as the service runs (which it does whenever beacon monitoring or CarPlay monitoring is enabled, and is restarted on boot by `BootReceiver`), CarPlay events are captured even after the app process is killed. **Guaranteed background detection.**
|
|
737
|
+
- **iOS:** the observer auto-restarts in the module's `OnCreate`, including background-launches triggered by beacon region monitoring. **iOS cannot wake a terminated app on CarPlay alone** — for guaranteed wake-from-suspension, also call `startMonitoring()` with at least one paired beacon (e.g. a beacon left in the vehicle). Region-wake events trigger a CarPlay state resync to reconcile any route changes that happened while the app was suspended.
|
|
738
|
+
|
|
732
739
|
**Notes**
|
|
733
740
|
|
|
734
741
|
- `startCarPlayMonitoring()` is idempotent. Calling it twice does not register a duplicate observer.
|
|
742
|
+
- `stopCarPlayMonitoring()` clears the persisted flag, so the observer will not auto-restart on next launch.
|
|
735
743
|
- The iOS detector does not require the CarPlay entitlement because it only reads the active audio route; you do not need to ship a CarPlay app.
|
|
736
744
|
- On iOS, if the JS bundle is suspended in the background, the JS event delivery is deferred until the app resumes, but the native lifecycle delegate (used by the geolocation plugin) fires immediately on connect.
|
|
745
|
+
- On Android, when CarPlay monitoring is enabled without beacon monitoring, the foreground service shows a generic "Connected device monitoring active" notification.
|
|
737
746
|
|
|
738
747
|
---
|
|
739
748
|
|
|
@@ -28,9 +28,6 @@ internal class BeaconApiForwarder(private val context: Context) {
|
|
|
28
28
|
context.getSharedPreferences(API_PREFS, Context.MODE_PRIVATE)
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
-
val isConfigured: Boolean
|
|
32
|
-
get() = prefs.getString(API_URL_KEY, null)?.isNotEmpty() == true
|
|
33
|
-
|
|
34
31
|
fun configure(url: String, apiKey: String?, id: String? = null) {
|
|
35
32
|
prefs.edit().apply {
|
|
36
33
|
putString(API_URL_KEY, url)
|
|
@@ -84,6 +84,12 @@ class BeaconForegroundService : Service(), BeaconConsumer {
|
|
|
84
84
|
// Seconds of silence after last valid sighting before a disappearance-based exit fires.
|
|
85
85
|
@Volatile private var exitTimeoutMs: Long = (DEFAULT_EXIT_TIMEOUT_SECONDS * 1000.0).toLong()
|
|
86
86
|
|
|
87
|
+
// CarPlay / Android Auto observer hosted by the foreground service so that
|
|
88
|
+
// CarConnection events are captured for as long as the service runs —
|
|
89
|
+
// independent of the JS bridge / module lifecycle. Survives app suspension
|
|
90
|
+
// and (via BootReceiver) device reboot.
|
|
91
|
+
@Volatile private var carPlayMonitor: CarPlayMonitor? = null
|
|
92
|
+
|
|
87
93
|
override fun onCreate() {
|
|
88
94
|
super.onCreate()
|
|
89
95
|
activeService = this
|
|
@@ -106,6 +112,14 @@ class BeaconForegroundService : Service(), BeaconConsumer {
|
|
|
106
112
|
// process and inherits the elevated priority. Calling enable/disable on the
|
|
107
113
|
// shared singleton causes crashes when the ExpoBeaconModule has an active
|
|
108
114
|
// scan bound to the same BeaconManager.
|
|
115
|
+
|
|
116
|
+
// Restore CarPlay observer if the user previously enabled CarPlay
|
|
117
|
+
// monitoring. Reading the flag here means cold-started services
|
|
118
|
+
// (e.g. via BootReceiver after reboot) automatically re-attach the
|
|
119
|
+
// observer with no JS interaction required.
|
|
120
|
+
if (isCarPlayEnabled(this)) {
|
|
121
|
+
startCarPlayObserverInternal()
|
|
122
|
+
}
|
|
109
123
|
}
|
|
110
124
|
|
|
111
125
|
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
|
@@ -134,11 +148,32 @@ class BeaconForegroundService : Service(), BeaconConsumer {
|
|
|
134
148
|
stopSelf()
|
|
135
149
|
return START_NOT_STICKY
|
|
136
150
|
}
|
|
151
|
+
// Handle CarPlay enable/disable actions. These can arrive after the
|
|
152
|
+
// service is already running for beacon monitoring, OR be the reason
|
|
153
|
+
// the service was started in the first place (CarPlay-only mode).
|
|
154
|
+
when (intent?.action) {
|
|
155
|
+
ACTION_ENABLE_CARPLAY -> {
|
|
156
|
+
setCarPlayEnabled(this, true)
|
|
157
|
+
startCarPlayObserverInternal()
|
|
158
|
+
}
|
|
159
|
+
ACTION_DISABLE_CARPLAY -> {
|
|
160
|
+
setCarPlayEnabled(this, false)
|
|
161
|
+
stopCarPlayObserverInternal()
|
|
162
|
+
// If the service is only alive for CarPlay (no beacon monitoring),
|
|
163
|
+
// shut it down so we don't keep an unnecessary foreground notification.
|
|
164
|
+
if (!isMonitoringActive(this)) {
|
|
165
|
+
stopSelf()
|
|
166
|
+
return START_NOT_STICKY
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
137
170
|
if (serviceConnected) {
|
|
138
171
|
// Already bound from a prior onStartCommand — reload regions directly
|
|
139
172
|
// so that re-starting monitoring from JS always takes effect.
|
|
140
173
|
loadAndMonitorRegions()
|
|
141
|
-
} else {
|
|
174
|
+
} else if (isMonitoringActive(this)) {
|
|
175
|
+
// Only bind to AltBeacon when beacon monitoring is active.
|
|
176
|
+
// CarPlay-only mode keeps the service alive without scanning.
|
|
142
177
|
beaconManager.bind(this)
|
|
143
178
|
}
|
|
144
179
|
return START_STICKY
|
|
@@ -765,6 +800,64 @@ class BeaconForegroundService : Service(), BeaconConsumer {
|
|
|
765
800
|
}
|
|
766
801
|
}
|
|
767
802
|
|
|
803
|
+
// MARK: - CarPlay observer (service-hosted)
|
|
804
|
+
|
|
805
|
+
/**
|
|
806
|
+
* Lazily instantiate and start the CarPlay observer. Idempotent —
|
|
807
|
+
* `CarPlayMonitor.start` itself is safe to call multiple times.
|
|
808
|
+
* Must be called from any thread; the monitor hops to main internally.
|
|
809
|
+
*/
|
|
810
|
+
private fun startCarPlayObserverInternal() {
|
|
811
|
+
val monitor = carPlayMonitor ?: try {
|
|
812
|
+
CarPlayMonitor(applicationContext).also { carPlayMonitor = it }
|
|
813
|
+
} catch (e: Throwable) {
|
|
814
|
+
Log.w(TAG, "Failed to create CarPlayMonitor", e)
|
|
815
|
+
return
|
|
816
|
+
}
|
|
817
|
+
try {
|
|
818
|
+
monitor.start { eventName, payload ->
|
|
819
|
+
emitCarPlayEvent(eventName, payload)
|
|
820
|
+
}
|
|
821
|
+
} catch (e: Throwable) {
|
|
822
|
+
Log.w(TAG, "Failed to start CarPlayMonitor", e)
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
private fun stopCarPlayObserverInternal() {
|
|
827
|
+
carPlayMonitor?.stop()
|
|
828
|
+
// Keep the instance — it's idempotent, and recreating costs nothing
|
|
829
|
+
// beyond the `CarConnection` LiveData wrapper. Setting null here is
|
|
830
|
+
// also safe but unnecessary.
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
/**
|
|
834
|
+
* Fan out a CarPlay event to all sinks: SQLite log, remote API forwarder,
|
|
835
|
+
* native plugin registry, and (best-effort) the live JS bridge. Runs from
|
|
836
|
+
* the main thread (CarPlayMonitor's emit hop).
|
|
837
|
+
*/
|
|
838
|
+
private fun emitCarPlayEvent(eventName: String, payload: Map<String, Any?>) {
|
|
839
|
+
// SQLite log (only if event logging is enabled).
|
|
840
|
+
try {
|
|
841
|
+
if (BeaconEventLogger.isLoggingEnabled(this)) {
|
|
842
|
+
val identifier = payload["identifier"] as? String
|
|
843
|
+
getOrCreateEventLogger().logEvent(eventName, identifier, payload)
|
|
844
|
+
}
|
|
845
|
+
} catch (e: Throwable) {
|
|
846
|
+
Log.w(TAG, "CarPlay log write failed", e)
|
|
847
|
+
}
|
|
848
|
+
// Remote API forwarder (no-op if unconfigured).
|
|
849
|
+
try { apiForwarder?.forwardEvent(payload) } catch (_: Throwable) {}
|
|
850
|
+
// Native plugin registry (BeaconGeoPlugin etc.).
|
|
851
|
+
when (eventName) {
|
|
852
|
+
"onCarPlayConnected" -> BeaconPluginRegistry.dispatchCarPlayConnected(
|
|
853
|
+
payload["transport"] as? String ?: "unknown"
|
|
854
|
+
)
|
|
855
|
+
"onCarPlayDisconnected" -> BeaconPluginRegistry.dispatchCarPlayDisconnected()
|
|
856
|
+
}
|
|
857
|
+
// Best-effort delivery to the live JS bridge if a module instance is bound.
|
|
858
|
+
try { boundModule?.get()?.forwardCarPlayEventFromService(eventName, payload) } catch (_: Throwable) {}
|
|
859
|
+
}
|
|
860
|
+
|
|
768
861
|
private fun createNotificationChannel() {
|
|
769
862
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
|
770
863
|
val config = readNotificationConfig()
|
|
@@ -814,13 +907,20 @@ class BeaconForegroundService : Service(), BeaconConsumer {
|
|
|
814
907
|
const val DISTANCE_JUMP_FACTOR = 5.0
|
|
815
908
|
|
|
816
909
|
private const val PREF_IS_MONITORING = "expo.beacon.is_monitoring"
|
|
910
|
+
private const val PREF_CARPLAY_ENABLED = "expo.beacon.carplay_enabled"
|
|
817
911
|
private const val EXTRA_RETRY_COUNT = "retryCount"
|
|
912
|
+
/** Intent action: enable CarPlay/Android Auto observation in the foreground service. */
|
|
913
|
+
const val ACTION_ENABLE_CARPLAY = "expo.modules.beacon.ENABLE_CARPLAY"
|
|
914
|
+
/** Intent action: disable CarPlay/Android Auto observation. Stops the service if no other reason to run. */
|
|
915
|
+
const val ACTION_DISABLE_CARPLAY = "expo.modules.beacon.DISABLE_CARPLAY"
|
|
818
916
|
private const val MAX_STARTFOREGROUND_RETRIES = 3
|
|
819
917
|
private const val RETRY_DELAY_MS = 10_000L
|
|
820
918
|
private const val RETRY_SERVICE_REQUEST_CODE = 0x42454143 // "BEAC"
|
|
821
919
|
/** Minimum milliseconds between consecutive loadAndMonitorRegions() calls. */
|
|
822
920
|
private const val LOAD_REGIONS_DEBOUNCE_MS = 500L
|
|
823
921
|
@Volatile private var activeService: BeaconForegroundService? = null
|
|
922
|
+
/** Weak reference to the live ExpoBeaconModule for best-effort JS bridge fan-out. */
|
|
923
|
+
@Volatile private var boundModule: java.lang.ref.WeakReference<ExpoBeaconModule>? = null
|
|
824
924
|
|
|
825
925
|
fun start(context: Context) {
|
|
826
926
|
context.getSharedPreferences(PREF_IS_MONITORING, Context.MODE_PRIVATE)
|
|
@@ -837,6 +937,12 @@ class BeaconForegroundService : Service(), BeaconConsumer {
|
|
|
837
937
|
fun stop(context: Context) {
|
|
838
938
|
context.getSharedPreferences(PREF_IS_MONITORING, Context.MODE_PRIVATE)
|
|
839
939
|
.edit().putBoolean("active", false).apply()
|
|
940
|
+
// Keep the service alive if it's still needed for CarPlay observation;
|
|
941
|
+
// otherwise the user would silently lose CarPlay events when calling
|
|
942
|
+
// stopMonitoring() while CarPlay monitoring was independently enabled.
|
|
943
|
+
if (isCarPlayEnabled(context)) {
|
|
944
|
+
return
|
|
945
|
+
}
|
|
840
946
|
context.stopService(Intent(context, BeaconForegroundService::class.java))
|
|
841
947
|
}
|
|
842
948
|
|
|
@@ -845,6 +951,70 @@ class BeaconForegroundService : Service(), BeaconConsumer {
|
|
|
845
951
|
.getBoolean("active", false)
|
|
846
952
|
}
|
|
847
953
|
|
|
954
|
+
// MARK: - CarPlay public API
|
|
955
|
+
|
|
956
|
+
/**
|
|
957
|
+
* Persist whether the user wants CarPlay observation. Cold service starts
|
|
958
|
+
* (e.g. via [BootReceiver]) read this flag in [onCreate] and re-attach
|
|
959
|
+
* the observer automatically.
|
|
960
|
+
*/
|
|
961
|
+
internal fun setCarPlayEnabled(context: Context, enabled: Boolean) {
|
|
962
|
+
context.getSharedPreferences(PREF_CARPLAY_ENABLED, Context.MODE_PRIVATE)
|
|
963
|
+
.edit().putBoolean("enabled", enabled).apply()
|
|
964
|
+
}
|
|
965
|
+
|
|
966
|
+
fun isCarPlayEnabled(context: Context): Boolean {
|
|
967
|
+
return context.getSharedPreferences(PREF_CARPLAY_ENABLED, Context.MODE_PRIVATE)
|
|
968
|
+
.getBoolean("enabled", false)
|
|
969
|
+
}
|
|
970
|
+
|
|
971
|
+
/**
|
|
972
|
+
* Enable CarPlay observation. Starts the foreground service if it's not
|
|
973
|
+
* already running so the observer survives app suspension and process death.
|
|
974
|
+
* Idempotent and safe to call from any context.
|
|
975
|
+
*/
|
|
976
|
+
fun enableCarPlay(context: Context) {
|
|
977
|
+
setCarPlayEnabled(context, true)
|
|
978
|
+
ensureNotificationChannel(context)
|
|
979
|
+
val intent = Intent(context, BeaconForegroundService::class.java)
|
|
980
|
+
.setAction(ACTION_ENABLE_CARPLAY)
|
|
981
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
|
982
|
+
context.startForegroundService(intent)
|
|
983
|
+
} else {
|
|
984
|
+
context.startService(intent)
|
|
985
|
+
}
|
|
986
|
+
}
|
|
987
|
+
|
|
988
|
+
/**
|
|
989
|
+
* Disable CarPlay observation. The foreground service stops itself
|
|
990
|
+
* if no other monitoring reason remains.
|
|
991
|
+
*/
|
|
992
|
+
fun disableCarPlay(context: Context) {
|
|
993
|
+
setCarPlayEnabled(context, false)
|
|
994
|
+
val intent = Intent(context, BeaconForegroundService::class.java)
|
|
995
|
+
.setAction(ACTION_DISABLE_CARPLAY)
|
|
996
|
+
// Best-effort: if the service isn't running, sending the intent will
|
|
997
|
+
// start it just to stop it. Skip startForegroundService unless beacon
|
|
998
|
+
// monitoring is also active (otherwise the start might fail with
|
|
999
|
+
// ForegroundServiceStartNotAllowedException on some Android versions).
|
|
1000
|
+
if (activeService != null || isMonitoringActive(context)) {
|
|
1001
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
|
1002
|
+
context.startForegroundService(intent)
|
|
1003
|
+
} else {
|
|
1004
|
+
context.startService(intent)
|
|
1005
|
+
}
|
|
1006
|
+
}
|
|
1007
|
+
}
|
|
1008
|
+
|
|
1009
|
+
/**
|
|
1010
|
+
* Bind a live module instance for best-effort JS-bridge delivery of
|
|
1011
|
+
* service-emitted events. The reference is weak; pass `null` from the
|
|
1012
|
+
* module's OnDestroy to clear it.
|
|
1013
|
+
*/
|
|
1014
|
+
fun bindModule(module: ExpoBeaconModule?) {
|
|
1015
|
+
boundModule = module?.let { java.lang.ref.WeakReference(it) }
|
|
1016
|
+
}
|
|
1017
|
+
|
|
848
1018
|
fun getMonitoringRuntimeSnapshot(): Map<String, MonitoringRuntimeState> {
|
|
849
1019
|
return activeService?.snapshotMonitoringRuntimeState() ?: emptyMap()
|
|
850
1020
|
}
|
|
@@ -891,10 +1061,16 @@ class BeaconForegroundService : Service(), BeaconConsumer {
|
|
|
891
1061
|
val config = try { org.json.JSONObject(json ?: "") } catch (_: Exception) { org.json.JSONObject() }
|
|
892
1062
|
val fgConfig = config.optJSONObject("foregroundService")
|
|
893
1063
|
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
1064
|
+
// If beacon monitoring is not active and the service is alive only
|
|
1065
|
+
// for CarPlay observation, fall back to a generic notification so
|
|
1066
|
+
// users don't see "Monitoring for iBeacons" when no beacons are being
|
|
1067
|
+
// monitored.
|
|
1068
|
+
val carPlayOnly = !isMonitoringActive(context) && isCarPlayEnabled(context)
|
|
1069
|
+
val defaultTitle = if (carPlayOnly) "Connected device monitoring active" else "Beacon Monitoring Active"
|
|
1070
|
+
val defaultText = if (carPlayOnly) "Monitoring connected vehicle (CarPlay/Android Auto)" else "Monitoring for iBeacons in the background"
|
|
1071
|
+
|
|
1072
|
+
val title = fgConfig?.optString("title")?.takeIf { it.isNotEmpty() } ?: defaultTitle
|
|
1073
|
+
val text = fgConfig?.optString("text")?.takeIf { it.isNotEmpty() } ?: defaultText
|
|
898
1074
|
val iconName = fgConfig?.optString("icon")?.takeIf { it.isNotEmpty() }
|
|
899
1075
|
val iconResId = iconName?.let { name ->
|
|
900
1076
|
try { context.resources.getIdentifier(name, "drawable", context.packageName).takeIf { it != 0 } }
|
|
@@ -921,6 +1097,13 @@ class BeaconForegroundService : Service(), BeaconConsumer {
|
|
|
921
1097
|
if (activeService === this) {
|
|
922
1098
|
activeService = null
|
|
923
1099
|
}
|
|
1100
|
+
// Stop CarPlay observer first so the LiveData observer doesn't leak
|
|
1101
|
+
// past the service lifecycle. Safe even if it was never started.
|
|
1102
|
+
try {
|
|
1103
|
+
carPlayMonitor?.stop()
|
|
1104
|
+
} catch (_: Throwable) {}
|
|
1105
|
+
carPlayMonitor = null
|
|
1106
|
+
val wasBound = serviceConnected
|
|
924
1107
|
serviceConnected = false
|
|
925
1108
|
timeoutHandler.removeCallbacksAndMessages(null)
|
|
926
1109
|
timeoutRunnables.clear()
|
|
@@ -944,7 +1127,11 @@ class BeaconForegroundService : Service(), BeaconConsumer {
|
|
|
944
1127
|
monitoredRegions.forEach {
|
|
945
1128
|
try { beaconManager.stopMonitoringBeaconsInRegion(it) } catch (_: RemoteException) {}
|
|
946
1129
|
}
|
|
947
|
-
|
|
1130
|
+
// Only unbind if we successfully bound — CarPlay-only service
|
|
1131
|
+
// instances skip beaconManager.bind() in onStartCommand.
|
|
1132
|
+
if (wasBound) {
|
|
1133
|
+
try { beaconManager.unbind(this) } catch (_: Throwable) {}
|
|
1134
|
+
}
|
|
948
1135
|
super.onDestroy()
|
|
949
1136
|
}
|
|
950
1137
|
|
|
@@ -29,11 +29,16 @@ class BootReceiver : BroadcastReceiver() {
|
|
|
29
29
|
logMemoryKillDiagnostics(context)
|
|
30
30
|
if (BeaconForegroundService.isMonitoringActive(context)) {
|
|
31
31
|
tryStartService(context)
|
|
32
|
+
} else if (BeaconForegroundService.isCarPlayEnabled(context)) {
|
|
33
|
+
// CarPlay-only mode: re-attach the observer so it survives reboot.
|
|
34
|
+
tryEnableCarPlay(context)
|
|
32
35
|
}
|
|
33
36
|
}
|
|
34
37
|
ACTION_RETRY_MONITORING -> {
|
|
35
38
|
if (BeaconForegroundService.isMonitoringActive(context)) {
|
|
36
39
|
tryStartService(context)
|
|
40
|
+
} else if (BeaconForegroundService.isCarPlayEnabled(context)) {
|
|
41
|
+
tryEnableCarPlay(context)
|
|
37
42
|
}
|
|
38
43
|
}
|
|
39
44
|
}
|
|
@@ -54,6 +59,16 @@ class BootReceiver : BroadcastReceiver() {
|
|
|
54
59
|
}
|
|
55
60
|
}
|
|
56
61
|
|
|
62
|
+
private fun tryEnableCarPlay(context: Context) {
|
|
63
|
+
try {
|
|
64
|
+
BeaconForegroundService.enableCarPlay(context)
|
|
65
|
+
Log.d(TAG, "BootReceiver: CarPlay-only service started successfully")
|
|
66
|
+
} catch (e: Exception) {
|
|
67
|
+
Log.e(TAG, "BootReceiver: Failed to start CarPlay-only service — retrying in ${RETRY_DELAY_MS}ms", e)
|
|
68
|
+
scheduleRetry(context)
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
57
72
|
private fun scheduleRetry(context: Context) {
|
|
58
73
|
val alarmManager = context.getSystemService(AlarmManager::class.java) ?: return
|
|
59
74
|
val retryIntent = Intent(context, BootReceiver::class.java).apply {
|
|
@@ -82,9 +82,6 @@ class ExpoBeaconModule : Module(), BeaconConsumer {
|
|
|
82
82
|
private var eventLogger: BeaconEventLogger? = null
|
|
83
83
|
@Volatile private var loggingEnabled = false
|
|
84
84
|
|
|
85
|
-
// CarPlay / Android Auto monitor (lazy because it requires reactContext)
|
|
86
|
-
private var carPlayMonitor: CarPlayMonitor? = null
|
|
87
|
-
|
|
88
85
|
override fun definition() = ModuleDefinition {
|
|
89
86
|
Name("ExpoBeacon")
|
|
90
87
|
|
|
@@ -379,6 +376,10 @@ class ExpoBeaconModule : Module(), BeaconConsumer {
|
|
|
379
376
|
sendEvent("onBeaconError", mapOf("identifier" to "", "code" to "SERVICE_START_FAILED", "message" to "Failed to start monitoring service: ${e.message}"))
|
|
380
377
|
return@AsyncFunction
|
|
381
378
|
}
|
|
379
|
+
// Auto-enable CarPlay observation alongside beacon monitoring so
|
|
380
|
+
// the service captures CarPlay/Android Auto events for the same
|
|
381
|
+
// lifetime. Users can opt out at any time via stopCarPlayMonitoring().
|
|
382
|
+
try { BeaconForegroundService.enableCarPlay(ctx) } catch (_: Throwable) {}
|
|
382
383
|
promise.resolve(null)
|
|
383
384
|
}
|
|
384
385
|
|
|
@@ -462,6 +463,11 @@ class ExpoBeaconModule : Module(), BeaconConsumer {
|
|
|
462
463
|
loggingEnabled = false
|
|
463
464
|
}
|
|
464
465
|
|
|
466
|
+
Function("isEventLoggingEnabled") {
|
|
467
|
+
val ctx = appContext.reactContext ?: return@Function false
|
|
468
|
+
BeaconEventLogger.isLoggingEnabled(ctx)
|
|
469
|
+
}
|
|
470
|
+
|
|
465
471
|
Function("getEventLogs") { options: Map<String, Any?>? ->
|
|
466
472
|
val logger = getOrCreateEventLogger() ?: return@Function emptyList<Map<String, Any?>>()
|
|
467
473
|
val limit = (options?.get("limit") as? Number)?.toInt() ?: 1000
|
|
@@ -574,10 +580,7 @@ class ExpoBeaconModule : Module(), BeaconConsumer {
|
|
|
574
580
|
return@AsyncFunction
|
|
575
581
|
}
|
|
576
582
|
try {
|
|
577
|
-
|
|
578
|
-
monitor.start { eventName, payload ->
|
|
579
|
-
emitCarPlayEvent(eventName, payload)
|
|
580
|
-
}
|
|
583
|
+
BeaconForegroundService.enableCarPlay(ctx)
|
|
581
584
|
promise.resolve(null)
|
|
582
585
|
} catch (e: Throwable) {
|
|
583
586
|
promise.reject("CARPLAY_START_FAILED", "Failed to start CarPlay monitoring: ${e.message}", e)
|
|
@@ -585,15 +588,35 @@ class ExpoBeaconModule : Module(), BeaconConsumer {
|
|
|
585
588
|
}
|
|
586
589
|
|
|
587
590
|
AsyncFunction("stopCarPlayMonitoring") { promise: Promise ->
|
|
588
|
-
|
|
589
|
-
|
|
591
|
+
val ctx = appContext.reactContext
|
|
592
|
+
if (ctx == null) {
|
|
593
|
+
promise.reject("NO_CONTEXT", "React context is not available", null)
|
|
594
|
+
return@AsyncFunction
|
|
595
|
+
}
|
|
596
|
+
try {
|
|
597
|
+
BeaconForegroundService.disableCarPlay(ctx)
|
|
598
|
+
promise.resolve(null)
|
|
599
|
+
} catch (e: Throwable) {
|
|
600
|
+
promise.reject("CARPLAY_STOP_FAILED", "Failed to stop CarPlay monitoring: ${e.message}", e)
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
Function("isCarPlayMonitoringEnabled") {
|
|
605
|
+
val ctx = appContext.reactContext ?: return@Function false
|
|
606
|
+
BeaconForegroundService.isCarPlayEnabled(ctx)
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
OnCreate {
|
|
610
|
+
// Register this module instance for best-effort JS-bridge fan-out
|
|
611
|
+
// of CarPlay events emitted by the foreground service. The service
|
|
612
|
+
// holds a weak reference; cleared in OnDestroy.
|
|
613
|
+
BeaconForegroundService.bindModule(this@ExpoBeaconModule)
|
|
590
614
|
}
|
|
591
615
|
|
|
592
616
|
OnDestroy {
|
|
593
617
|
with(this@ExpoBeaconModule) {
|
|
618
|
+
BeaconForegroundService.bindModule(null)
|
|
594
619
|
unregisterEventReceiver()
|
|
595
|
-
carPlayMonitor?.stop()
|
|
596
|
-
carPlayMonitor = null
|
|
597
620
|
loggingEnabled = false
|
|
598
621
|
eventLogger?.close()
|
|
599
622
|
eventLogger = null
|
|
@@ -1038,22 +1061,12 @@ class ExpoBeaconModule : Module(), BeaconConsumer {
|
|
|
1038
1061
|
}
|
|
1039
1062
|
|
|
1040
1063
|
/**
|
|
1041
|
-
*
|
|
1042
|
-
*
|
|
1064
|
+
* Called by [BeaconForegroundService] (via weak reference) when it emits a
|
|
1065
|
+
* CarPlay event from its own observer. Best-effort delivery to the JS bridge
|
|
1066
|
+
* — the service has already handled SQLite logging, API forwarding, and
|
|
1067
|
+
* native plugin dispatch, so this method only needs to relay to JS.
|
|
1043
1068
|
*/
|
|
1044
|
-
|
|
1045
|
-
sendEvent(eventName, payload)
|
|
1046
|
-
logBeaconEvent(eventName, payload)
|
|
1047
|
-
appContext.reactContext?.let { ctx ->
|
|
1048
|
-
try {
|
|
1049
|
-
BeaconApiForwarder(ctx).forwardEvent(payload)
|
|
1050
|
-
} catch (_: Throwable) { /* best-effort */ }
|
|
1051
|
-
}
|
|
1052
|
-
when (eventName) {
|
|
1053
|
-
"onCarPlayConnected" -> BeaconPluginRegistry.dispatchCarPlayConnected(
|
|
1054
|
-
payload["transport"] as? String ?: "unknown"
|
|
1055
|
-
)
|
|
1056
|
-
"onCarPlayDisconnected" -> BeaconPluginRegistry.dispatchCarPlayDisconnected()
|
|
1057
|
-
}
|
|
1069
|
+
fun forwardCarPlayEventFromService(eventName: String, payload: Map<String, Any?>) {
|
|
1070
|
+
try { sendEvent(eventName, payload) } catch (_: Throwable) { /* JS bridge may be torn down */ }
|
|
1058
1071
|
}
|
|
1059
1072
|
}
|
|
@@ -93,6 +93,11 @@ declare class ExpoBeaconModule extends NativeModule<ExpoBeaconModuleEvents> {
|
|
|
93
93
|
enableEventLogging(): void;
|
|
94
94
|
/** Disable event logging. Previously logged events are retained. */
|
|
95
95
|
disableEventLogging(): void;
|
|
96
|
+
/**
|
|
97
|
+
* Returns whether SQLite event logging is currently enabled.
|
|
98
|
+
* Reads the persisted flag, so this stays accurate across app cold-starts.
|
|
99
|
+
*/
|
|
100
|
+
isEventLoggingEnabled(): boolean;
|
|
96
101
|
/**
|
|
97
102
|
* Retrieve logged beacon events from the SQLite database.
|
|
98
103
|
* @param options Optional filters (limit, eventType, sinceTimestamp).
|
|
@@ -143,7 +148,22 @@ declare class ExpoBeaconModule extends NativeModule<ExpoBeaconModuleEvents> {
|
|
|
143
148
|
* endpoint, and dispatched to native lifecycle plugins (used by the auto-
|
|
144
149
|
* generated geolocation plugin to start/stop background-geolocation).
|
|
145
150
|
*
|
|
146
|
-
*
|
|
151
|
+
* **Persistent.** The enabled state is stored in native preferences and
|
|
152
|
+
* survives app kill / device reboot. Call `stopCarPlayMonitoring()` to
|
|
153
|
+
* disable. `startMonitoring()` also enables CarPlay observation
|
|
154
|
+
* automatically — calling this method explicitly is only required if you
|
|
155
|
+
* want CarPlay events without beacon monitoring.
|
|
156
|
+
*
|
|
157
|
+
* **Background behavior:**
|
|
158
|
+
* - **Android**: the foreground service hosts the observer and continues to
|
|
159
|
+
* receive `CarConnection` events even after the app process is killed and
|
|
160
|
+
* restarted via `BootReceiver`. Guaranteed background detection.
|
|
161
|
+
* - **iOS**: the observer auto-restarts in `OnCreate` whenever the module
|
|
162
|
+
* is recreated, including background-launches triggered by beacon region
|
|
163
|
+
* monitoring. **iOS cannot wake a terminated app on CarPlay alone** — for
|
|
164
|
+
* guaranteed wake-from-suspension, also call `startMonitoring()` with at
|
|
165
|
+
* least one paired beacon. Region wake events trigger a CarPlay state
|
|
166
|
+
* resync to reconcile any route changes that occurred during suspension.
|
|
147
167
|
*
|
|
148
168
|
* - iOS: observes `AVAudioSession.routeChangeNotification` for `.carAudio` ports.
|
|
149
169
|
* No CarPlay entitlement required.
|
|
@@ -152,8 +172,21 @@ declare class ExpoBeaconModule extends NativeModule<ExpoBeaconModuleEvents> {
|
|
|
152
172
|
* - Web: no-op (resolves immediately).
|
|
153
173
|
*/
|
|
154
174
|
startCarPlayMonitoring(): Promise<void>;
|
|
155
|
-
/**
|
|
175
|
+
/**
|
|
176
|
+
* Stop CarPlay / Android Auto connection monitoring and clear the persisted
|
|
177
|
+
* "enabled" flag so the observer is not auto-restarted on next launch / boot.
|
|
178
|
+
*
|
|
179
|
+
* On Android, if no beacon monitoring is active, the foreground service
|
|
180
|
+
* stops itself.
|
|
181
|
+
*/
|
|
156
182
|
stopCarPlayMonitoring(): Promise<void>;
|
|
183
|
+
/**
|
|
184
|
+
* Returns whether CarPlay / Android Auto observation is currently enabled.
|
|
185
|
+
* Reads the persisted flag, so the value survives app cold-starts and
|
|
186
|
+
* reflects the native source of truth (foreground service on Android,
|
|
187
|
+
* UserDefaults suite on iOS).
|
|
188
|
+
*/
|
|
189
|
+
isCarPlayMonitoringEnabled(): boolean;
|
|
157
190
|
}
|
|
158
191
|
declare const _default: ExpoBeaconModule;
|
|
159
192
|
export default _default;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ExpoBeaconModule.d.ts","sourceRoot":"","sources":["../src/ExpoBeaconModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAuB,MAAM,MAAM,CAAC;AAEzD,OAAO,EACL,sBAAsB,EACtB,gBAAgB,EAChB,mBAAmB,EACnB,YAAY,EACZ,eAAe,EACf,kBAAkB,EAClB,iBAAiB,EACjB,gBAAgB,EAChB,oBAAoB,EACpB,oBAAoB,EACpB,aAAa,EACd,MAAM,oBAAoB,CAAC;AAE5B,OAAO,OAAO,gBAAiB,SAAQ,YAAY,CAAC,sBAAsB,CAAC;IACzE;;;;;;;;;;;OAWG;IACH,mBAAmB,CACjB,KAAK,CAAC,EAAE,MAAM,EAAE,EAChB,YAAY,CAAC,EAAE,MAAM,GACpB,OAAO,CAAC,gBAAgB,EAAE,CAAC;IAE9B;;;;;OAKG;IACH,sBAAsB,CACpB,YAAY,CAAC,EAAE,MAAM,GACpB,OAAO,CAAC,mBAAmB,EAAE,CAAC;IAEjC;;OAEG;IACH,UAAU,CACR,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,EACb,IAAI,CAAC,EAAE,MAAM,EACb,cAAc,CAAC,EAAE,MAAM,GACtB,IAAI;IAEP;;OAEG;IACH,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI;IAEtC;;OAEG;IACH,gBAAgB,IAAI,YAAY,EAAE;IAElC;;OAEG;IACH,aAAa,CACX,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,EAChB,IAAI,CAAC,EAAE,MAAM,EACb,cAAc,CAAC,EAAE,MAAM,GACtB,IAAI;IAEP;;OAEG;IACH,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI;IAEzC;;OAEG;IACH,mBAAmB,IAAI,eAAe,EAAE;IAExC;;;OAGG;IACH,qBAAqB,CAAC,MAAM,EAAE,kBAAkB,GAAG,IAAI;IAEvD;;;;;;;OAOG;IACH,eAAe,CAAC,OAAO,CAAC,EAAE,iBAAiB,GAAG,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAEpE;;OAEG;IACH,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC;IAE/B;;;OAGG;IACH,mBAAmB,IAAI,IAAI;IAE3B,iEAAiE;IACjE,kBAAkB,IAAI,IAAI;IAE1B;;;OAGG;IACH,UAAU,IAAI,IAAI;IAElB,yEAAyE;IACzE,uBAAuB,IAAI,OAAO,CAAC,OAAO,CAAC;IAE3C;;;OAGG;IACH,2BAA2B,IAAI,OAAO;IAEtC;;;;;OAKG;IACH,mCAAmC,IAAI,OAAO,CAAC,OAAO,CAAC;IAEvD,4FAA4F;IAC5F,kBAAkB,IAAI,IAAI;IAE1B,oEAAoE;IACpE,mBAAmB,IAAI,IAAI;IAE3B;;;OAGG;IACH,YAAY,CAAC,OAAO,CAAC,EAAE,oBAAoB,GAAG,aAAa,EAAE;IAE7D,kDAAkD;IAClD,cAAc,IAAI,IAAI;IAEtB,mEAAmE;IACnE,gBAAgB,IAAI,IAAI;IAExB;;;;;;;;OAQG;IACH,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,MAAM,GAAG,IAAI;IAE/D;;;OAGG;IACH,mBAAmB,IAAI,gBAAgB;IAEvC;;;OAGG;IACH,uBAAuB,CAAC,UAAU,EAAE,MAAM,GAAG,oBAAoB,GAAG,IAAI;IAExE;;OAEG;IACH,wBAAwB,IAAI,oBAAoB,EAAE;IAElD;;;OAGG;IACH,cAAc,IAAI;QAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE;IAElF
|
|
1
|
+
{"version":3,"file":"ExpoBeaconModule.d.ts","sourceRoot":"","sources":["../src/ExpoBeaconModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAuB,MAAM,MAAM,CAAC;AAEzD,OAAO,EACL,sBAAsB,EACtB,gBAAgB,EAChB,mBAAmB,EACnB,YAAY,EACZ,eAAe,EACf,kBAAkB,EAClB,iBAAiB,EACjB,gBAAgB,EAChB,oBAAoB,EACpB,oBAAoB,EACpB,aAAa,EACd,MAAM,oBAAoB,CAAC;AAE5B,OAAO,OAAO,gBAAiB,SAAQ,YAAY,CAAC,sBAAsB,CAAC;IACzE;;;;;;;;;;;OAWG;IACH,mBAAmB,CACjB,KAAK,CAAC,EAAE,MAAM,EAAE,EAChB,YAAY,CAAC,EAAE,MAAM,GACpB,OAAO,CAAC,gBAAgB,EAAE,CAAC;IAE9B;;;;;OAKG;IACH,sBAAsB,CACpB,YAAY,CAAC,EAAE,MAAM,GACpB,OAAO,CAAC,mBAAmB,EAAE,CAAC;IAEjC;;OAEG;IACH,UAAU,CACR,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,EACb,IAAI,CAAC,EAAE,MAAM,EACb,cAAc,CAAC,EAAE,MAAM,GACtB,IAAI;IAEP;;OAEG;IACH,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI;IAEtC;;OAEG;IACH,gBAAgB,IAAI,YAAY,EAAE;IAElC;;OAEG;IACH,aAAa,CACX,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,EAChB,IAAI,CAAC,EAAE,MAAM,EACb,cAAc,CAAC,EAAE,MAAM,GACtB,IAAI;IAEP;;OAEG;IACH,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI;IAEzC;;OAEG;IACH,mBAAmB,IAAI,eAAe,EAAE;IAExC;;;OAGG;IACH,qBAAqB,CAAC,MAAM,EAAE,kBAAkB,GAAG,IAAI;IAEvD;;;;;;;OAOG;IACH,eAAe,CAAC,OAAO,CAAC,EAAE,iBAAiB,GAAG,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAEpE;;OAEG;IACH,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC;IAE/B;;;OAGG;IACH,mBAAmB,IAAI,IAAI;IAE3B,iEAAiE;IACjE,kBAAkB,IAAI,IAAI;IAE1B;;;OAGG;IACH,UAAU,IAAI,IAAI;IAElB,yEAAyE;IACzE,uBAAuB,IAAI,OAAO,CAAC,OAAO,CAAC;IAE3C;;;OAGG;IACH,2BAA2B,IAAI,OAAO;IAEtC;;;;;OAKG;IACH,mCAAmC,IAAI,OAAO,CAAC,OAAO,CAAC;IAEvD,4FAA4F;IAC5F,kBAAkB,IAAI,IAAI;IAE1B,oEAAoE;IACpE,mBAAmB,IAAI,IAAI;IAE3B;;;OAGG;IACH,qBAAqB,IAAI,OAAO;IAEhC;;;OAGG;IACH,YAAY,CAAC,OAAO,CAAC,EAAE,oBAAoB,GAAG,aAAa,EAAE;IAE7D,kDAAkD;IAClD,cAAc,IAAI,IAAI;IAEtB,mEAAmE;IACnE,gBAAgB,IAAI,IAAI;IAExB;;;;;;;;OAQG;IACH,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,MAAM,GAAG,IAAI;IAE/D;;;OAGG;IACH,mBAAmB,IAAI,gBAAgB;IAEvC;;;OAGG;IACH,uBAAuB,CAAC,UAAU,EAAE,MAAM,GAAG,oBAAoB,GAAG,IAAI;IAExE;;OAEG;IACH,wBAAwB,IAAI,oBAAoB,EAAE;IAElD;;;OAGG;IACH,cAAc,IAAI;QAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE;IAElF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA8BG;IACH,sBAAsB,IAAI,OAAO,CAAC,IAAI,CAAC;IAEvC;;;;;;OAMG;IACH,qBAAqB,IAAI,OAAO,CAAC,IAAI,CAAC;IAEtC;;;;;OAKG;IACH,0BAA0B,IAAI,OAAO;CACtC;;AAED,wBAAmE"}
|
|
@@ -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;AAyPzD,eAAe,mBAAmB,CAAmB,YAAY,CAAC,CAAC","sourcesContent":["import { NativeModule, requireNativeModule } from \"expo\";\r\n\r\nimport {\r\n ExpoBeaconModuleEvents,\r\n BeaconScanResult,\r\n EddystoneScanResult,\r\n PairedBeacon,\r\n PairedEddystone,\r\n NotificationConfig,\r\n MonitoringOptions,\r\n MonitoringConfig,\r\n MonitoredDeviceState,\r\n EventLogQueryOptions,\r\n EventLogEntry,\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 * On iOS, at least one UUID is required — Apple strips iBeacon data from BLE\r\n * advertisements, making wildcard discovery impossible. When you pass an empty\r\n * array, the module automatically uses UUIDs from paired beacons.\r\n * On Android, pass an empty array to discover all nearby iBeacons.\r\n *\r\n * @param uuids Proximity UUIDs to filter by. Empty/omitted = use paired UUIDs (iOS) or wildcard (Android).\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 * Start a one-shot Eddystone beacon scan using BLE.\r\n * Discovers Eddystone-UID and Eddystone-URL frames.\r\n *\r\n * @param scanDuration Duration in ms (default 5000)\r\n */\r\n scanForEddystonesAsync(\r\n scanDuration?: number,\r\n ): Promise<EddystoneScanResult[]>;\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 name?: string,\r\n timeoutSeconds?: 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 * Register an Eddystone-UID beacon for persistent monitoring.\r\n */\r\n pairEddystone(\r\n identifier: string,\r\n namespace: string,\r\n instance: string,\r\n name?: string,\r\n timeoutSeconds?: number,\r\n ): void;\r\n\r\n /**\r\n * Remove a previously paired Eddystone beacon.\r\n */\r\n unpairEddystone(identifier: string): void;\r\n\r\n /**\r\n * Return all currently paired Eddystone beacons.\r\n */\r\n getPairedEddystones(): PairedEddystone[];\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 /**\r\n * Cancel any in-progress one-shot scan (iBeacon or Eddystone).\r\n * The pending promise will be rejected with code \"SCAN_CANCELLED\".\r\n */\r\n cancelScan(): void;\r\n\r\n /** Request Bluetooth + Location permissions. Returns true if granted. */\r\n requestPermissionsAsync(): Promise<boolean>;\r\n\r\n /**\r\n * Check whether the app is exempt from Android battery optimizations.\r\n * Always returns true on iOS and web (no equivalent concept).\r\n */\r\n isBatteryOptimizationExempt(): boolean;\r\n\r\n /**\r\n * Request exemption from Android battery optimizations.\r\n * Opens the system dialog asking the user to whitelist this app.\r\n * Returns true if the dialog was shown (or already exempt), false on failure.\r\n * Always resolves true on iOS and web.\r\n */\r\n requestBatteryOptimizationExemption(): Promise<boolean>;\r\n\r\n /** Enable SQLite event logging. All beacon events will be persisted to a local database. */\r\n enableEventLogging(): void;\r\n\r\n /** Disable event logging. Previously logged events are retained. */\r\n disableEventLogging(): void;\r\n\r\n /**\r\n * Returns whether SQLite event logging is currently enabled.\r\n * Reads the persisted flag, so this stays accurate across app cold-starts.\r\n */\r\n isEventLoggingEnabled(): boolean;\r\n\r\n /**\r\n * Retrieve logged beacon events from the SQLite database.\r\n * @param options Optional filters (limit, eventType, sinceTimestamp).\r\n */\r\n getEventLogs(options?: EventLogQueryOptions): EventLogEntry[];\r\n\r\n /** Delete all logged events from the database. */\r\n clearEventLogs(): void;\r\n\r\n /** Delete the entire event log database. Also disables logging. */\r\n destroyEventLogs(): void;\r\n\r\n /**\r\n * Configure a remote API endpoint for native event forwarding.\r\n * Once set, beacon events are POSTed directly from native code,\r\n * ensuring delivery even when the JS bridge is not active (app backgrounded).\r\n *\r\n * @param url The API endpoint URL to POST events to.\r\n * @param apiKey Optional API key sent as X-CSFR-Token header.\r\n * @param id Optional identifier appended to every forwarded event payload.\r\n */\r\n setApiEndpoint(url: string, apiKey?: string, id?: string): void;\r\n\r\n /**\r\n * Return the current monitoring configuration and active state.\r\n * Option fields are undefined if not explicitly set.\r\n */\r\n getMonitoringConfig(): MonitoringConfig;\r\n\r\n /**\r\n * Return the current state snapshot for a paired monitored device.\r\n * Returns null when no paired device matches the identifier.\r\n */\r\n getMonitoredDeviceState(identifier: string): MonitoredDeviceState | null;\r\n\r\n /**\r\n * Return the current state snapshot for all paired monitored devices.\r\n */\r\n getMonitoredDeviceStates(): MonitoredDeviceState[];\r\n\r\n /**\r\n * Return the current API forwarding configuration.\r\n * Each field is `null` if not set.\r\n */\r\n getApiEndpoint(): { url: string | null; apiKey: string | null; id: string | null };\r\n\r\n /**\r\n * Start observing CarPlay (iOS) / Android Auto (Android) connection state.\r\n *\r\n * Emits `onCarPlayConnected` and `onCarPlayDisconnected` JS events. Events are\r\n * also written to the SQLite event log and forwarded to the configured API\r\n * endpoint, and dispatched to native lifecycle plugins (used by the auto-\r\n * generated geolocation plugin to start/stop background-geolocation).\r\n *\r\n * **Persistent.** The enabled state is stored in native preferences and\r\n * survives app kill / device reboot. Call `stopCarPlayMonitoring()` to\r\n * disable. `startMonitoring()` also enables CarPlay observation\r\n * automatically — calling this method explicitly is only required if you\r\n * want CarPlay events without beacon monitoring.\r\n *\r\n * **Background behavior:**\r\n * - **Android**: the foreground service hosts the observer and continues to\r\n * receive `CarConnection` events even after the app process is killed and\r\n * restarted via `BootReceiver`. Guaranteed background detection.\r\n * - **iOS**: the observer auto-restarts in `OnCreate` whenever the module\r\n * is recreated, including background-launches triggered by beacon region\r\n * monitoring. **iOS cannot wake a terminated app on CarPlay alone** — for\r\n * guaranteed wake-from-suspension, also call `startMonitoring()` with at\r\n * least one paired beacon. Region wake events trigger a CarPlay state\r\n * resync to reconcile any route changes that occurred during suspension.\r\n *\r\n * - iOS: observes `AVAudioSession.routeChangeNotification` for `.carAudio` ports.\r\n * No CarPlay entitlement required.\r\n * - Android: observes `androidx.car.app.connection.CarConnection` LiveData.\r\n * No Android Auto certification required.\r\n * - Web: no-op (resolves immediately).\r\n */\r\n startCarPlayMonitoring(): Promise<void>;\r\n\r\n /**\r\n * Stop CarPlay / Android Auto connection monitoring and clear the persisted\r\n * \"enabled\" flag so the observer is not auto-restarted on next launch / boot.\r\n *\r\n * On Android, if no beacon monitoring is active, the foreground service\r\n * stops itself.\r\n */\r\n stopCarPlayMonitoring(): Promise<void>;\r\n\r\n /**\r\n * Returns whether CarPlay / Android Auto observation is currently enabled.\r\n * Reads the persisted flag, so the value survives app cold-starts and\r\n * reflects the native source of truth (foreground service on Android,\r\n * UserDefaults suite on iOS).\r\n */\r\n isCarPlayMonitoringEnabled(): boolean;\r\n}\r\n\r\nexport default requireNativeModule<ExpoBeaconModule>(\"ExpoBeacon\");\r\n"]}
|
|
@@ -17,6 +17,7 @@ declare const stub: {
|
|
|
17
17
|
requestPermissionsAsync: () => Promise<boolean>;
|
|
18
18
|
enableEventLogging: () => void;
|
|
19
19
|
disableEventLogging: () => void;
|
|
20
|
+
isEventLoggingEnabled: () => boolean;
|
|
20
21
|
getEventLogs: (_options?: EventLogQueryOptions) => EventLogEntry[];
|
|
21
22
|
clearEventLogs: () => void;
|
|
22
23
|
destroyEventLogs: () => void;
|
|
@@ -32,6 +33,7 @@ declare const stub: {
|
|
|
32
33
|
requestBatteryOptimizationExemption: () => Promise<boolean>;
|
|
33
34
|
startCarPlayMonitoring: () => Promise<void>;
|
|
34
35
|
stopCarPlayMonitoring: () => Promise<void>;
|
|
36
|
+
isCarPlayMonitoringEnabled: () => boolean;
|
|
35
37
|
addListener: (_eventName: keyof ExpoBeaconModuleEvents, _listener: any) => {
|
|
36
38
|
remove: () => void;
|
|
37
39
|
};
|
|
@@ -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,mBAAmB,EACnB,YAAY,EACZ,eAAe,EACf,oBAAoB,EACpB,oBAAoB,EACpB,aAAa,EACd,MAAM,oBAAoB,CAAC;AAM5B,QAAA,MAAM,IAAI;kCAEE,MAAM,EAAE,kBACA,MAAM,KACrB,OAAO,CAAC,gBAAgB,EAAE,CAAC;6CAEZ,MAAM,KACrB,OAAO,CAAC,mBAAmB,EAAE,CAAC;8BAElB,MAAM,SACZ,MAAM,UACL,MAAM,UACN,MAAM,KACb,IAAI;gCACqB,MAAM,KAAG,IAAI;4BACnB,YAAY,EAAE;iCAErB,MAAM,cACP,MAAM,aACP,MAAM,KAChB,IAAI;mCACwB,MAAM,KAAG,IAAI;+BACnB,eAAe,EAAE;2BACrB,OAAO,CAAC,IAAI,CAAC;0BACd,OAAO,CAAC,IAAI,CAAC;+BACR,IAAI;8BACL,IAAI;sBACZ,IAAI;qCACa,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAG,IAAI;mCAClC,OAAO,CAAC,OAAO,CAAC;8BACrB,IAAI;+BACH,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,mBAAmB,EACnB,YAAY,EACZ,eAAe,EACf,oBAAoB,EACpB,oBAAoB,EACpB,aAAa,EACd,MAAM,oBAAoB,CAAC;AAM5B,QAAA,MAAM,IAAI;kCAEE,MAAM,EAAE,kBACA,MAAM,KACrB,OAAO,CAAC,gBAAgB,EAAE,CAAC;6CAEZ,MAAM,KACrB,OAAO,CAAC,mBAAmB,EAAE,CAAC;8BAElB,MAAM,SACZ,MAAM,UACL,MAAM,UACN,MAAM,KACb,IAAI;gCACqB,MAAM,KAAG,IAAI;4BACnB,YAAY,EAAE;iCAErB,MAAM,cACP,MAAM,aACP,MAAM,KAChB,IAAI;mCACwB,MAAM,KAAG,IAAI;+BACnB,eAAe,EAAE;2BACrB,OAAO,CAAC,IAAI,CAAC;0BACd,OAAO,CAAC,IAAI,CAAC;+BACR,IAAI;8BACL,IAAI;sBACZ,IAAI;qCACa,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAG,IAAI;mCAClC,OAAO,CAAC,OAAO,CAAC;8BACrB,IAAI;+BACH,IAAI;iCACF,OAAO;8BACR,oBAAoB,KAAG,aAAa,EAAE;0BAC5C,IAAI;4BACF,IAAI;;2CAEa,MAAM,KAAG,oBAAoB,GAAG,IAAI;oCAC7C,oBAAoB,EAAE;0BAChC;QAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE;uCACnD,OAAO;+CACC,OAAO,CAAC,OAAO,CAAC;kCAC7B,OAAO,CAAC,IAAI,CAAC;iCACd,OAAO,CAAC,IAAI,CAAC;sCACR,OAAO;8BACb,MAAM,sBAAsB,aAAa,GAAG;;;qCAGrC,MAAM,sBAAsB;CAC9D,CAAC;AAEF,eAAe,IAAI,CAAC"}
|
|
@@ -19,6 +19,7 @@ const stub = {
|
|
|
19
19
|
requestPermissionsAsync: () => notSupported(),
|
|
20
20
|
enableEventLogging: () => notSupported(),
|
|
21
21
|
disableEventLogging: () => notSupported(),
|
|
22
|
+
isEventLoggingEnabled: () => false,
|
|
22
23
|
getEventLogs: (_options) => notSupported(),
|
|
23
24
|
clearEventLogs: () => notSupported(),
|
|
24
25
|
destroyEventLogs: () => notSupported(),
|
|
@@ -30,6 +31,7 @@ const stub = {
|
|
|
30
31
|
requestBatteryOptimizationExemption: () => Promise.resolve(true),
|
|
31
32
|
startCarPlayMonitoring: () => Promise.resolve(),
|
|
32
33
|
stopCarPlayMonitoring: () => Promise.resolve(),
|
|
34
|
+
isCarPlayMonitoringEnabled: () => false,
|
|
33
35
|
addListener: (_eventName, _listener) => ({
|
|
34
36
|
remove: () => { },
|
|
35
37
|
}),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ExpoBeaconModule.web.js","sourceRoot":"","sources":["../src/ExpoBeaconModule.web.ts"],"names":[],"mappings":"AAWA,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,sBAAsB,EAAE,CACtB,aAAsB,EACU,EAAE,CAAC,YAAY,EAAE;IACnD,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,aAAa,EAAE,CACb,WAAmB,EACnB,UAAkB,EAClB,SAAiB,EACX,EAAE,CAAC,YAAY,EAAE;IACzB,eAAe,EAAE,CAAC,WAAmB,EAAQ,EAAE,CAAC,YAAY,EAAE;IAC9D,mBAAmB,EAAE,GAAsB,EAAE,CAAC,YAAY,EAAE;IAC5D,eAAe,EAAE,GAAkB,EAAE,CAAC,YAAY,EAAE;IACpD,cAAc,EAAE,GAAkB,EAAE,CAAC,YAAY,EAAE;IACnD,mBAAmB,EAAE,GAAS,EAAE,CAAC,YAAY,EAAE;IAC/C,kBAAkB,EAAE,GAAS,EAAE,CAAC,YAAY,EAAE;IAC9C,UAAU,EAAE,GAAS,EAAE,CAAC,YAAY,EAAE;IACtC,qBAAqB,EAAE,CAAC,OAAgC,EAAQ,EAAE,CAAC,YAAY,EAAE;IACjF,uBAAuB,EAAE,GAAqB,EAAE,CAAC,YAAY,EAAE;IAC/D,kBAAkB,EAAE,GAAS,EAAE,CAAC,YAAY,EAAE;IAC9C,mBAAmB,EAAE,GAAS,EAAE,CAAC,YAAY,EAAE;IAC/C,YAAY,EAAE,CAAC,QAA+B,EAAmB,EAAE,CAAC,YAAY,EAAE;IAClF,cAAc,EAAE,GAAS,EAAE,CAAC,YAAY,EAAE;IAC1C,gBAAgB,EAAE,GAAS,EAAE,CAAC,YAAY,EAAE;IAC5C,mBAAmB,EAAE,GAAG,EAAE,CAAC,YAAY,EAAE;IACzC,uBAAuB,EAAE,CAAC,WAAmB,EAA+B,EAAE,CAAC,YAAY,EAAE;IAC7F,wBAAwB,EAAE,GAA2B,EAAE,CAAC,YAAY,EAAE;IACtE,cAAc,EAAE,GAAqE,EAAE,CAAC,YAAY,EAAE;IACtG,2BAA2B,EAAE,GAAY,EAAE,CAAC,IAAI;IAChD,mCAAmC,EAAE,GAAqB,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC;IAClF,sBAAsB,EAAE,GAAkB,EAAE,CAAC,OAAO,CAAC,OAAO,EAAE;IAC9D,qBAAqB,EAAE,GAAkB,EAAE,CAAC,OAAO,CAAC,OAAO,EAAE;IAC7D,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 EddystoneScanResult,\r\n PairedBeacon,\r\n PairedEddystone,\r\n MonitoredDeviceState,\r\n EventLogQueryOptions,\r\n EventLogEntry,\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 scanForEddystonesAsync: (\r\n _scanDuration?: number,\r\n ): Promise<EddystoneScanResult[]> => 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 pairEddystone: (\r\n _identifier: string,\r\n _namespace: string,\r\n _instance: string,\r\n ): void => notSupported(),\r\n unpairEddystone: (_identifier: string): void => notSupported(),\r\n getPairedEddystones: (): PairedEddystone[] => notSupported(),\r\n startMonitoring: (): Promise<void> => notSupported(),\r\n stopMonitoring: (): Promise<void> => notSupported(),\r\n startContinuousScan: (): void => notSupported(),\r\n stopContinuousScan: (): void => notSupported(),\r\n cancelScan: (): void => notSupported(),\r\n setNotificationConfig: (_config: Record<string, unknown>): void => notSupported(),\r\n requestPermissionsAsync: (): Promise<boolean> => notSupported(),\r\n enableEventLogging: (): void => notSupported(),\r\n disableEventLogging: (): void => notSupported(),\r\n getEventLogs: (_options?: EventLogQueryOptions): EventLogEntry[] => notSupported(),\r\n clearEventLogs: (): void => notSupported(),\r\n destroyEventLogs: (): void => notSupported(),\r\n getMonitoringConfig: () => notSupported(),\r\n getMonitoredDeviceState: (_identifier: string): MonitoredDeviceState | null => notSupported(),\r\n getMonitoredDeviceStates: (): MonitoredDeviceState[] => notSupported(),\r\n getApiEndpoint: (): { url: string | null; apiKey: string | null; id: string | null } => notSupported(),\r\n isBatteryOptimizationExempt: (): boolean => true,\r\n requestBatteryOptimizationExemption: (): Promise<boolean> => Promise.resolve(true),\r\n startCarPlayMonitoring: (): Promise<void> => Promise.resolve(),\r\n stopCarPlayMonitoring: (): Promise<void> => Promise.resolve(),\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"]}
|
|
1
|
+
{"version":3,"file":"ExpoBeaconModule.web.js","sourceRoot":"","sources":["../src/ExpoBeaconModule.web.ts"],"names":[],"mappings":"AAWA,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,sBAAsB,EAAE,CACtB,aAAsB,EACU,EAAE,CAAC,YAAY,EAAE;IACnD,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,aAAa,EAAE,CACb,WAAmB,EACnB,UAAkB,EAClB,SAAiB,EACX,EAAE,CAAC,YAAY,EAAE;IACzB,eAAe,EAAE,CAAC,WAAmB,EAAQ,EAAE,CAAC,YAAY,EAAE;IAC9D,mBAAmB,EAAE,GAAsB,EAAE,CAAC,YAAY,EAAE;IAC5D,eAAe,EAAE,GAAkB,EAAE,CAAC,YAAY,EAAE;IACpD,cAAc,EAAE,GAAkB,EAAE,CAAC,YAAY,EAAE;IACnD,mBAAmB,EAAE,GAAS,EAAE,CAAC,YAAY,EAAE;IAC/C,kBAAkB,EAAE,GAAS,EAAE,CAAC,YAAY,EAAE;IAC9C,UAAU,EAAE,GAAS,EAAE,CAAC,YAAY,EAAE;IACtC,qBAAqB,EAAE,CAAC,OAAgC,EAAQ,EAAE,CAAC,YAAY,EAAE;IACjF,uBAAuB,EAAE,GAAqB,EAAE,CAAC,YAAY,EAAE;IAC/D,kBAAkB,EAAE,GAAS,EAAE,CAAC,YAAY,EAAE;IAC9C,mBAAmB,EAAE,GAAS,EAAE,CAAC,YAAY,EAAE;IAC/C,qBAAqB,EAAE,GAAY,EAAE,CAAC,KAAK;IAC3C,YAAY,EAAE,CAAC,QAA+B,EAAmB,EAAE,CAAC,YAAY,EAAE;IAClF,cAAc,EAAE,GAAS,EAAE,CAAC,YAAY,EAAE;IAC1C,gBAAgB,EAAE,GAAS,EAAE,CAAC,YAAY,EAAE;IAC5C,mBAAmB,EAAE,GAAG,EAAE,CAAC,YAAY,EAAE;IACzC,uBAAuB,EAAE,CAAC,WAAmB,EAA+B,EAAE,CAAC,YAAY,EAAE;IAC7F,wBAAwB,EAAE,GAA2B,EAAE,CAAC,YAAY,EAAE;IACtE,cAAc,EAAE,GAAqE,EAAE,CAAC,YAAY,EAAE;IACtG,2BAA2B,EAAE,GAAY,EAAE,CAAC,IAAI;IAChD,mCAAmC,EAAE,GAAqB,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC;IAClF,sBAAsB,EAAE,GAAkB,EAAE,CAAC,OAAO,CAAC,OAAO,EAAE;IAC9D,qBAAqB,EAAE,GAAkB,EAAE,CAAC,OAAO,CAAC,OAAO,EAAE;IAC7D,0BAA0B,EAAE,GAAY,EAAE,CAAC,KAAK;IAChD,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 EddystoneScanResult,\r\n PairedBeacon,\r\n PairedEddystone,\r\n MonitoredDeviceState,\r\n EventLogQueryOptions,\r\n EventLogEntry,\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 scanForEddystonesAsync: (\r\n _scanDuration?: number,\r\n ): Promise<EddystoneScanResult[]> => 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 pairEddystone: (\r\n _identifier: string,\r\n _namespace: string,\r\n _instance: string,\r\n ): void => notSupported(),\r\n unpairEddystone: (_identifier: string): void => notSupported(),\r\n getPairedEddystones: (): PairedEddystone[] => notSupported(),\r\n startMonitoring: (): Promise<void> => notSupported(),\r\n stopMonitoring: (): Promise<void> => notSupported(),\r\n startContinuousScan: (): void => notSupported(),\r\n stopContinuousScan: (): void => notSupported(),\r\n cancelScan: (): void => notSupported(),\r\n setNotificationConfig: (_config: Record<string, unknown>): void => notSupported(),\r\n requestPermissionsAsync: (): Promise<boolean> => notSupported(),\r\n enableEventLogging: (): void => notSupported(),\r\n disableEventLogging: (): void => notSupported(),\r\n isEventLoggingEnabled: (): boolean => false,\r\n getEventLogs: (_options?: EventLogQueryOptions): EventLogEntry[] => notSupported(),\r\n clearEventLogs: (): void => notSupported(),\r\n destroyEventLogs: (): void => notSupported(),\r\n getMonitoringConfig: () => notSupported(),\r\n getMonitoredDeviceState: (_identifier: string): MonitoredDeviceState | null => notSupported(),\r\n getMonitoredDeviceStates: (): MonitoredDeviceState[] => notSupported(),\r\n getApiEndpoint: (): { url: string | null; apiKey: string | null; id: string | null } => notSupported(),\r\n isBatteryOptimizationExempt: (): boolean => true,\r\n requestBatteryOptimizationExemption: (): Promise<boolean> => Promise.resolve(true),\r\n startCarPlayMonitoring: (): Promise<void> => Promise.resolve(),\r\n stopCarPlayMonitoring: (): Promise<void> => Promise.resolve(),\r\n isCarPlayMonitoringEnabled: (): boolean => false,\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"]}
|