expo-beacon 0.6.6 → 0.6.8
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.
|
@@ -76,29 +76,31 @@ class BeaconForegroundService : Service(), BeaconConsumer {
|
|
|
76
76
|
// Increase AltBeacon's region exit period so didExitRegion doesn't fire
|
|
77
77
|
// prematurely during brief BLE scan gaps.
|
|
78
78
|
BeaconManager.setRegionExitPeriod(REGION_EXIT_PERIOD_MS)
|
|
79
|
-
//
|
|
80
|
-
//
|
|
81
|
-
//
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
buildForegroundNotification(), FOREGROUND_NOTIF_ID
|
|
86
|
-
)
|
|
87
|
-
} catch (e: IllegalStateException) {
|
|
88
|
-
// Already bound by another consumer (e.g. a scan in progress) — non-fatal.
|
|
89
|
-
Log.w(TAG, "enableForegroundServiceScanning skipped (already bound)", e)
|
|
90
|
-
}
|
|
79
|
+
// NOTE: We intentionally do NOT call enableForegroundServiceScanning().
|
|
80
|
+
// Our BeaconForegroundService is already a foreground service (startForeground
|
|
81
|
+
// in onStartCommand). AltBeacon's internal BeaconService runs in the same
|
|
82
|
+
// process and inherits the elevated priority. Calling enable/disable on the
|
|
83
|
+
// shared singleton causes crashes when the ExpoBeaconModule has an active
|
|
84
|
+
// scan bound to the same BeaconManager.
|
|
91
85
|
}
|
|
92
86
|
|
|
93
87
|
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
88
|
+
try {
|
|
89
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
|
90
|
+
startForeground(
|
|
91
|
+
FOREGROUND_NOTIF_ID,
|
|
92
|
+
buildForegroundNotification(),
|
|
93
|
+
ServiceInfo.FOREGROUND_SERVICE_TYPE_CONNECTED_DEVICE
|
|
94
|
+
)
|
|
95
|
+
} else {
|
|
96
|
+
startForeground(FOREGROUND_NOTIF_ID, buildForegroundNotification())
|
|
97
|
+
}
|
|
98
|
+
} catch (e: Exception) {
|
|
99
|
+
// SecurityException on Android 14+ if BT permissions missing,
|
|
100
|
+
// or other platform-specific issues. Stop gracefully instead of crashing.
|
|
101
|
+
Log.e(TAG, "startForeground failed — stopping service", e)
|
|
102
|
+
stopSelf()
|
|
103
|
+
return START_NOT_STICKY
|
|
102
104
|
}
|
|
103
105
|
if (serviceConnected) {
|
|
104
106
|
// Already bound from a prior onStartCommand — reload regions directly
|
|
@@ -157,8 +159,11 @@ class BeaconForegroundService : Service(), BeaconConsumer {
|
|
|
157
159
|
try { beaconManager.stopRangingBeaconsInRegion(it) } catch (_: RemoteException) {}
|
|
158
160
|
}
|
|
159
161
|
distanceLogRegions.clear()
|
|
162
|
+
monitoredRegions.forEach {
|
|
163
|
+
try { beaconManager.stopMonitoringBeaconsInRegion(it) } catch (_: RemoteException) {}
|
|
164
|
+
}
|
|
165
|
+
monitoredRegions.clear()
|
|
160
166
|
monitoredRegionIds.clear()
|
|
161
|
-
enteredRegions.clear()
|
|
162
167
|
lastSeenAtMs.clear()
|
|
163
168
|
timeoutHandler.removeCallbacksAndMessages(null)
|
|
164
169
|
timeoutRunnables.clear()
|
|
@@ -167,10 +172,12 @@ class BeaconForegroundService : Service(), BeaconConsumer {
|
|
|
167
172
|
exitCounters.clear()
|
|
168
173
|
missCounters.clear()
|
|
169
174
|
}
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
175
|
+
// NOTE: enteredRegions is intentionally NOT cleared here.
|
|
176
|
+
// Clearing it on every reload (e.g. START_STICKY restart or repeated
|
|
177
|
+
// startMonitoring calls) would reset the "already entered" state and
|
|
178
|
+
// cause the hysteresis to fire another ENTER event for beacons that
|
|
179
|
+
// are still nearby. Stale entries are pruned below after new regions
|
|
180
|
+
// are determined.
|
|
174
181
|
|
|
175
182
|
// iBeacon regions
|
|
176
183
|
for (i in 0 until beacons.length()) {
|
|
@@ -230,8 +237,12 @@ class BeaconForegroundService : Service(), BeaconConsumer {
|
|
|
230
237
|
|
|
231
238
|
// If no regions to monitor, stop the service to avoid idling
|
|
232
239
|
if (monitoredRegions.isEmpty()) {
|
|
240
|
+
enteredRegions.clear()
|
|
233
241
|
Log.d(TAG, "No paired beacons — stopping idle foreground service")
|
|
234
242
|
stopSelf()
|
|
243
|
+
} else {
|
|
244
|
+
// Prune enteredRegions for regions that are no longer monitored
|
|
245
|
+
enteredRegions.retainAll(monitoredRegionIds)
|
|
235
246
|
}
|
|
236
247
|
}
|
|
237
248
|
|
|
@@ -581,6 +592,7 @@ class BeaconForegroundService : Service(), BeaconConsumer {
|
|
|
581
592
|
fun start(context: Context) {
|
|
582
593
|
context.getSharedPreferences(PREF_IS_MONITORING, Context.MODE_PRIVATE)
|
|
583
594
|
.edit().putBoolean("active", true).apply()
|
|
595
|
+
ensureNotificationChannel(context)
|
|
584
596
|
val intent = Intent(context, BeaconForegroundService::class.java)
|
|
585
597
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
|
586
598
|
context.startForegroundService(intent)
|
|
@@ -691,7 +703,6 @@ class BeaconForegroundService : Service(), BeaconConsumer {
|
|
|
691
703
|
monitoredRegions.forEach {
|
|
692
704
|
try { beaconManager.stopMonitoringBeaconsInRegion(it) } catch (_: RemoteException) {}
|
|
693
705
|
}
|
|
694
|
-
try { beaconManager.disableForegroundServiceScanning() } catch (_: Exception) {}
|
|
695
706
|
beaconManager.unbind(this)
|
|
696
707
|
super.onDestroy()
|
|
697
708
|
}
|
|
@@ -327,24 +327,25 @@ class ExpoBeaconModule : Module(), BeaconConsumer {
|
|
|
327
327
|
promise.reject("PERMISSION_DENIED", "Location permissions required for background monitoring. Call requestPermissionsAsync() first.", null)
|
|
328
328
|
return@AsyncFunction
|
|
329
329
|
}
|
|
330
|
-
|
|
331
|
-
//
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
beaconManager.enableForegroundServiceScanning(
|
|
339
|
-
BeaconForegroundService.buildForegroundNotification(ctx), FOREGROUND_NOTIF_ID
|
|
340
|
-
)
|
|
341
|
-
} catch (_: IllegalStateException) {
|
|
342
|
-
// Already bound — service onCreate() will enable it instead.
|
|
330
|
+
// Android 12+ requires BLUETOOTH_SCAN for BLE operations;
|
|
331
|
+
// Android 14+ additionally requires it for connectedDevice foreground services.
|
|
332
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
|
333
|
+
val hasBtScan = ContextCompat.checkSelfPermission(ctx, Manifest.permission.BLUETOOTH_SCAN) == PackageManager.PERMISSION_GRANTED
|
|
334
|
+
val hasBtConnect = ContextCompat.checkSelfPermission(ctx, Manifest.permission.BLUETOOTH_CONNECT) == PackageManager.PERMISSION_GRANTED
|
|
335
|
+
if (!hasBtScan || !hasBtConnect) {
|
|
336
|
+
promise.reject("PERMISSION_DENIED", "Bluetooth permissions required for beacon monitoring. Call requestPermissionsAsync() first.", null)
|
|
337
|
+
return@AsyncFunction
|
|
343
338
|
}
|
|
344
339
|
}
|
|
345
340
|
|
|
346
341
|
registerEventReceiver()
|
|
347
|
-
|
|
342
|
+
try {
|
|
343
|
+
BeaconForegroundService.start(ctx)
|
|
344
|
+
} catch (e: Exception) {
|
|
345
|
+
unregisterEventReceiver()
|
|
346
|
+
promise.reject("SERVICE_START_FAILED", "Failed to start monitoring service: ${e.message}", e)
|
|
347
|
+
return@AsyncFunction
|
|
348
|
+
}
|
|
348
349
|
promise.resolve(null)
|
|
349
350
|
}
|
|
350
351
|
|
|
@@ -360,7 +361,6 @@ class ExpoBeaconModule : Module(), BeaconConsumer {
|
|
|
360
361
|
return@AsyncFunction
|
|
361
362
|
}
|
|
362
363
|
BeaconForegroundService.stop(ctx)
|
|
363
|
-
try { beaconManager.disableForegroundServiceScanning() } catch (_: Exception) {}
|
|
364
364
|
unregisterEventReceiver()
|
|
365
365
|
promise.resolve(null)
|
|
366
366
|
}
|