munim-bluetooth 0.3.24 → 0.3.26

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.
Files changed (28) hide show
  1. package/android/src/main/AndroidManifest.xml +10 -0
  2. package/android/src/main/java/com/munimbluetooth/HybridMunimBluetooth.kt +87 -0
  3. package/android/src/main/java/com/munimbluetooth/MunimBluetoothBackgroundService.kt +344 -0
  4. package/ios/HybridMunimBluetooth.swift +265 -22
  5. package/lib/commonjs/index.js +21 -0
  6. package/lib/commonjs/index.js.map +1 -1
  7. package/lib/module/index.js +19 -0
  8. package/lib/module/index.js.map +1 -1
  9. package/lib/typescript/src/index.d.ts +15 -2
  10. package/lib/typescript/src/index.d.ts.map +1 -1
  11. package/lib/typescript/src/specs/munim-bluetooth.nitro.d.ts +25 -3
  12. package/lib/typescript/src/specs/munim-bluetooth.nitro.d.ts.map +1 -1
  13. package/nitrogen/generated/android/c++/JBackgroundSessionOptions.hpp +107 -0
  14. package/nitrogen/generated/android/c++/JHybridMunimBluetoothSpec.cpp +12 -0
  15. package/nitrogen/generated/android/c++/JHybridMunimBluetoothSpec.hpp +2 -0
  16. package/nitrogen/generated/android/kotlin/com/margelo/nitro/munimbluetooth/BackgroundSessionOptions.kt +59 -0
  17. package/nitrogen/generated/android/kotlin/com/margelo/nitro/munimbluetooth/HybridMunimBluetoothSpec.kt +28 -20
  18. package/nitrogen/generated/ios/MunimBluetooth-Swift-Cxx-Umbrella.hpp +3 -0
  19. package/nitrogen/generated/ios/c++/HybridMunimBluetoothSpecSwift.hpp +15 -0
  20. package/nitrogen/generated/ios/swift/BackgroundSessionOptions.swift +154 -0
  21. package/nitrogen/generated/ios/swift/HybridMunimBluetoothSpec.swift +2 -0
  22. package/nitrogen/generated/ios/swift/HybridMunimBluetoothSpec_cxx.swift +43 -21
  23. package/nitrogen/generated/shared/c++/BackgroundSessionOptions.hpp +115 -0
  24. package/nitrogen/generated/shared/c++/HybridMunimBluetoothSpec.cpp +2 -0
  25. package/nitrogen/generated/shared/c++/HybridMunimBluetoothSpec.hpp +5 -0
  26. package/package.json +1 -1
  27. package/src/index.ts +23 -0
  28. package/src/specs/munim-bluetooth.nitro.ts +28 -3
@@ -13,8 +13,18 @@
13
13
  <!-- Location permissions required for BLE scanning on Android 6.0+ -->
14
14
  <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
15
15
  <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
16
+ <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
17
+ <uses-permission android:name="android.permission.FOREGROUND_SERVICE_LOCATION" />
16
18
 
17
19
  <!-- Feature declarations -->
18
20
  <uses-feature android:name="android.hardware.bluetooth" android:required="false" />
19
21
  <uses-feature android:name="android.hardware.bluetooth_le" android:required="true" />
22
+
23
+ <application>
24
+ <service
25
+ android:name=".MunimBluetoothBackgroundService"
26
+ android:enabled="true"
27
+ android:exported="false"
28
+ android:foregroundServiceType="location" />
29
+ </application>
20
30
  </manifest>
@@ -22,6 +22,7 @@ import android.bluetooth.le.ScanRecord
22
22
  import android.bluetooth.le.ScanResult
23
23
  import android.bluetooth.le.ScanSettings
24
24
  import android.content.Context
25
+ import android.content.Intent
25
26
  import android.os.Build
26
27
  import android.os.ParcelUuid
27
28
  import android.util.Log
@@ -33,6 +34,7 @@ import com.margelo.nitro.NitroModules
33
34
  import com.margelo.nitro.core.Promise
34
35
  import com.margelo.nitro.munimbluetooth.AdvertisingDataTypes
35
36
  import com.margelo.nitro.munimbluetooth.AdvertisingOptions
37
+ import com.margelo.nitro.munimbluetooth.BackgroundSessionOptions
36
38
  import com.margelo.nitro.munimbluetooth.CharacteristicValue
37
39
  import com.margelo.nitro.munimbluetooth.GATTCharacteristic
38
40
  import com.margelo.nitro.munimbluetooth.GATTService
@@ -61,6 +63,7 @@ class HybridMunimBluetooth : HybridMunimBluetoothSpec() {
61
63
  private var currentServiceUUIDs: Array<String> = emptyArray()
62
64
  private var currentLocalName: String? = null
63
65
  private var currentManufacturerData: String? = null
66
+ private var previousAdapterName: String? = null
64
67
  private var bluetoothManager: BluetoothManager? = null
65
68
  private var bluetoothAdapter: BluetoothAdapter? = null
66
69
 
@@ -111,6 +114,17 @@ class HybridMunimBluetooth : HybridMunimBluetoothSpec() {
111
114
  options.manufacturerData
112
115
  )
113
116
 
117
+ if (!currentLocalName.isNullOrBlank() && previousAdapterName == null) {
118
+ previousAdapterName = adapter.name
119
+ }
120
+ if (!currentLocalName.isNullOrBlank()) {
121
+ try {
122
+ adapter.name = currentLocalName
123
+ } catch (error: SecurityException) {
124
+ Log.w(TAG, "Unable to apply custom localName to Bluetooth adapter", error)
125
+ }
126
+ }
127
+
114
128
  if (!gattServerReady) {
115
129
  setServicesFromOptions(options.serviceUUIDs)
116
130
  }
@@ -143,6 +157,7 @@ class HybridMunimBluetooth : HybridMunimBluetoothSpec() {
143
157
  currentServiceUUIDs = emptyArray()
144
158
  currentLocalName = null
145
159
  currentManufacturerData = null
160
+ restoreAdapterName()
146
161
  }
147
162
 
148
163
  override fun setServices(services: Array<GATTService>) {
@@ -432,6 +447,67 @@ class HybridMunimBluetooth : HybridMunimBluetoothSpec() {
432
447
  return promise
433
448
  }
434
449
 
450
+ override fun startBackgroundSession(options: BackgroundSessionOptions) {
451
+ val context = NitroModules.applicationContext ?: run {
452
+ Log.w(TAG, "Unable to start background BLE session: application context unavailable")
453
+ return
454
+ }
455
+
456
+ val intent = Intent(context, MunimBluetoothBackgroundService::class.java).apply {
457
+ action = MunimBluetoothBackgroundService.ACTION_START
458
+ putExtra(
459
+ MunimBluetoothBackgroundService.EXTRA_SERVICE_UUIDS,
460
+ options.serviceUUIDs
461
+ )
462
+ putExtra(
463
+ MunimBluetoothBackgroundService.EXTRA_LOCAL_NAME,
464
+ options.localName
465
+ )
466
+ putExtra(
467
+ MunimBluetoothBackgroundService.EXTRA_ALLOW_DUPLICATES,
468
+ options.allowDuplicates ?: false
469
+ )
470
+ putExtra(
471
+ MunimBluetoothBackgroundService.EXTRA_SCAN_MODE,
472
+ options.scanMode?.name ?: ScanMode.LOWPOWER.name
473
+ )
474
+ putExtra(
475
+ MunimBluetoothBackgroundService.EXTRA_NOTIFICATION_CHANNEL_ID,
476
+ options.androidNotificationChannelId
477
+ ?: MunimBluetoothBackgroundService.DEFAULT_CHANNEL_ID
478
+ )
479
+ putExtra(
480
+ MunimBluetoothBackgroundService.EXTRA_NOTIFICATION_CHANNEL_NAME,
481
+ options.androidNotificationChannelName
482
+ ?: MunimBluetoothBackgroundService.DEFAULT_CHANNEL_NAME
483
+ )
484
+ putExtra(
485
+ MunimBluetoothBackgroundService.EXTRA_NOTIFICATION_TITLE,
486
+ options.androidNotificationTitle
487
+ ?: MunimBluetoothBackgroundService.DEFAULT_NOTIFICATION_TITLE
488
+ )
489
+ putExtra(
490
+ MunimBluetoothBackgroundService.EXTRA_NOTIFICATION_TEXT,
491
+ options.androidNotificationText
492
+ ?: MunimBluetoothBackgroundService.DEFAULT_NOTIFICATION_TEXT
493
+ )
494
+ }
495
+
496
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
497
+ context.startForegroundService(intent)
498
+ } else {
499
+ context.startService(intent)
500
+ }
501
+ }
502
+
503
+ override fun stopBackgroundSession() {
504
+ val context = NitroModules.applicationContext ?: return
505
+ val intent = Intent(context, MunimBluetoothBackgroundService::class.java).apply {
506
+ action = MunimBluetoothBackgroundService.ACTION_STOP
507
+ }
508
+ context.startService(intent)
509
+ }
510
+
435
511
  override fun addListener(eventName: String) {
436
512
  // Nitro uses JS-side listener registration. No native bookkeeping required here.
437
513
  }
@@ -938,6 +1014,17 @@ class HybridMunimBluetooth : HybridMunimBluetoothSpec() {
938
1014
  gattServerReady = true
939
1015
  }
940
1016
 
1017
+ private fun restoreAdapterName() {
1018
+ val adapter = bluetoothAdapter ?: return
1019
+ val originalName = previousAdapterName ?: return
1020
+ try {
1021
+ adapter.name = originalName
1022
+ } catch (error: SecurityException) {
1023
+ Log.w(TAG, "Unable to restore Bluetooth adapter name", error)
1024
+ }
1025
+ previousAdapterName = null
1026
+ }
1027
+
941
1028
  companion object {
942
1029
  private const val TAG = "HybridMunimBluetooth"
943
1030
  private val CLIENT_CHARACTERISTIC_CONFIG_UUID =
@@ -0,0 +1,344 @@
1
+ package com.munimbluetooth
2
+
3
+ import android.app.Notification
4
+ import android.app.NotificationChannel
5
+ import android.app.NotificationManager
6
+ import android.app.Service
7
+ import android.bluetooth.BluetoothAdapter
8
+ import android.bluetooth.BluetoothManager
9
+ import android.bluetooth.le.AdvertiseCallback
10
+ import android.bluetooth.le.AdvertiseData
11
+ import android.bluetooth.le.AdvertiseSettings
12
+ import android.bluetooth.le.BluetoothLeAdvertiser
13
+ import android.bluetooth.le.BluetoothLeScanner
14
+ import android.bluetooth.le.ScanCallback
15
+ import android.bluetooth.le.ScanFilter
16
+ import android.bluetooth.le.ScanResult
17
+ import android.bluetooth.le.ScanSettings
18
+ import android.content.Context
19
+ import android.content.Intent
20
+ import android.os.Build
21
+ import android.os.IBinder
22
+ import android.os.ParcelUuid
23
+ import android.util.Log
24
+ import com.margelo.nitro.munimbluetooth.ScanMode
25
+ import java.util.Locale
26
+
27
+ class MunimBluetoothBackgroundService : Service() {
28
+ private var bluetoothManager: BluetoothManager? = null
29
+ private var bluetoothAdapter: BluetoothAdapter? = null
30
+ private var advertiser: BluetoothLeAdvertiser? = null
31
+ private var advertiseCallback: AdvertiseCallback? = null
32
+ private var scanner: BluetoothLeScanner? = null
33
+ private var scanCallback: ScanCallback? = null
34
+ private var previousAdapterName: String? = null
35
+
36
+ private val discoveredDeviceIds = linkedSetOf<String>()
37
+ private var notificationChannelId = DEFAULT_CHANNEL_ID
38
+ private var notificationChannelName = DEFAULT_CHANNEL_NAME
39
+ private var notificationTitle = DEFAULT_NOTIFICATION_TITLE
40
+ private var notificationText = DEFAULT_NOTIFICATION_TEXT
41
+
42
+ override fun onBind(intent: Intent?): IBinder? = null
43
+
44
+ override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
45
+ when (intent?.action) {
46
+ ACTION_STOP -> {
47
+ stopSelf()
48
+ return START_NOT_STICKY
49
+ }
50
+
51
+ ACTION_START -> {
52
+ notificationChannelId =
53
+ intent.getStringExtra(EXTRA_NOTIFICATION_CHANNEL_ID)
54
+ ?: DEFAULT_CHANNEL_ID
55
+ notificationChannelName =
56
+ intent.getStringExtra(EXTRA_NOTIFICATION_CHANNEL_NAME)
57
+ ?: DEFAULT_CHANNEL_NAME
58
+ notificationTitle =
59
+ intent.getStringExtra(EXTRA_NOTIFICATION_TITLE)
60
+ ?: DEFAULT_NOTIFICATION_TITLE
61
+ notificationText =
62
+ intent.getStringExtra(EXTRA_NOTIFICATION_TEXT)
63
+ ?: DEFAULT_NOTIFICATION_TEXT
64
+
65
+ startForeground(
66
+ NOTIFICATION_ID,
67
+ buildNotification(neighborCount = discoveredDeviceIds.size)
68
+ )
69
+
70
+ val serviceUUIDs =
71
+ intent.getStringArrayExtra(EXTRA_SERVICE_UUIDS) ?: emptyArray()
72
+ val localName = intent.getStringExtra(EXTRA_LOCAL_NAME)
73
+ val allowDuplicates =
74
+ intent.getBooleanExtra(EXTRA_ALLOW_DUPLICATES, false)
75
+ val scanMode = parseScanMode(
76
+ intent.getStringExtra(EXTRA_SCAN_MODE) ?: ScanMode.LOWPOWER.name
77
+ )
78
+
79
+ startBleSession(
80
+ serviceUUIDs = serviceUUIDs,
81
+ localName = localName,
82
+ allowDuplicates = allowDuplicates,
83
+ scanMode = scanMode
84
+ )
85
+ return START_STICKY
86
+ }
87
+ }
88
+
89
+ return START_NOT_STICKY
90
+ }
91
+
92
+ override fun onDestroy() {
93
+ stopBleSession()
94
+ super.onDestroy()
95
+ }
96
+
97
+ private fun startBleSession(
98
+ serviceUUIDs: Array<String>,
99
+ localName: String?,
100
+ allowDuplicates: Boolean,
101
+ scanMode: ScanMode
102
+ ) {
103
+ bluetoothManager =
104
+ applicationContext.getSystemService(Context.BLUETOOTH_SERVICE) as? BluetoothManager
105
+ bluetoothAdapter = bluetoothManager?.adapter
106
+
107
+ val adapter = bluetoothAdapter
108
+ if (adapter == null || !adapter.isEnabled) {
109
+ Log.w(TAG, "Unable to start background BLE session: Bluetooth unavailable")
110
+ stopSelf()
111
+ return
112
+ }
113
+
114
+ if (!localName.isNullOrBlank() && previousAdapterName == null) {
115
+ previousAdapterName = adapter.name
116
+ try {
117
+ adapter.name = localName
118
+ } catch (error: SecurityException) {
119
+ Log.w(TAG, "Unable to set adapter name for background advertising", error)
120
+ }
121
+ }
122
+
123
+ startAdvertising(adapter, serviceUUIDs, !localName.isNullOrBlank())
124
+ startScan(adapter, serviceUUIDs, allowDuplicates, scanMode)
125
+ }
126
+
127
+ private fun stopBleSession() {
128
+ scanCallback?.let { callback ->
129
+ try {
130
+ scanner?.stopScan(callback)
131
+ } catch (error: SecurityException) {
132
+ Log.w(TAG, "Unable to stop background scan cleanly", error)
133
+ }
134
+ }
135
+ scanCallback = null
136
+ scanner = null
137
+
138
+ advertiseCallback?.let { callback ->
139
+ try {
140
+ advertiser?.stopAdvertising(callback)
141
+ } catch (error: SecurityException) {
142
+ Log.w(TAG, "Unable to stop background advertising cleanly", error)
143
+ }
144
+ }
145
+ advertiseCallback = null
146
+ advertiser = null
147
+
148
+ val adapter = bluetoothAdapter
149
+ val previousName = previousAdapterName
150
+ if (adapter != null && previousName != null) {
151
+ try {
152
+ adapter.name = previousName
153
+ } catch (error: SecurityException) {
154
+ Log.w(TAG, "Unable to restore adapter name", error)
155
+ }
156
+ }
157
+ previousAdapterName = null
158
+ discoveredDeviceIds.clear()
159
+ }
160
+
161
+ private fun startAdvertising(
162
+ adapter: BluetoothAdapter,
163
+ serviceUUIDs: Array<String>,
164
+ includeDeviceName: Boolean
165
+ ) {
166
+ val activeAdvertiser = adapter.bluetoothLeAdvertiser ?: run {
167
+ Log.w(TAG, "Bluetooth advertiser unavailable for background session")
168
+ return
169
+ }
170
+ advertiser = activeAdvertiser
171
+
172
+ val data = AdvertiseData.Builder()
173
+ .setIncludeDeviceName(includeDeviceName)
174
+
175
+ serviceUUIDs.forEach { uuid ->
176
+ runCatching { ParcelUuid.fromString(uuid) }.getOrNull()?.let(data::addServiceUuid)
177
+ }
178
+
179
+ val settings = AdvertiseSettings.Builder()
180
+ .setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_LOW_POWER)
181
+ .setConnectable(true)
182
+ .setTimeout(0)
183
+ .setTxPowerLevel(AdvertiseSettings.ADVERTISE_TX_POWER_MEDIUM)
184
+ .build()
185
+
186
+ advertiseCallback = object : AdvertiseCallback() {
187
+ override fun onStartSuccess(settingsInEffect: AdvertiseSettings) {
188
+ Log.i(TAG, "Background advertising started")
189
+ }
190
+
191
+ override fun onStartFailure(errorCode: Int) {
192
+ Log.e(TAG, "Background advertising failed: $errorCode")
193
+ }
194
+ }
195
+
196
+ activeAdvertiser.startAdvertising(settings, data.build(), advertiseCallback)
197
+ }
198
+
199
+ private fun startScan(
200
+ adapter: BluetoothAdapter,
201
+ serviceUUIDs: Array<String>,
202
+ allowDuplicates: Boolean,
203
+ scanMode: ScanMode
204
+ ) {
205
+ val activeScanner = adapter.bluetoothLeScanner ?: run {
206
+ Log.w(TAG, "Bluetooth scanner unavailable for background session")
207
+ return
208
+ }
209
+ scanner = activeScanner
210
+
211
+ val filters = serviceUUIDs.mapNotNull { uuid ->
212
+ runCatching {
213
+ ScanFilter.Builder()
214
+ .setServiceUuid(ParcelUuid.fromString(uuid))
215
+ .build()
216
+ }.getOrNull()
217
+ }
218
+
219
+ val androidScanMode = when (scanMode) {
220
+ ScanMode.LOWLATENCY -> ScanSettings.SCAN_MODE_LOW_LATENCY
221
+ ScanMode.BALANCED -> ScanSettings.SCAN_MODE_BALANCED
222
+ ScanMode.LOWPOWER -> ScanSettings.SCAN_MODE_LOW_POWER
223
+ }
224
+
225
+ val settingsBuilder = ScanSettings.Builder().setScanMode(androidScanMode)
226
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
227
+ settingsBuilder.setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES)
228
+ settingsBuilder.setMatchMode(ScanSettings.MATCH_MODE_STICKY)
229
+ }
230
+
231
+ scanCallback = object : ScanCallback() {
232
+ override fun onScanResult(callbackType: Int, result: ScanResult) {
233
+ handleScanResult(result, allowDuplicates)
234
+ }
235
+
236
+ override fun onBatchScanResults(results: MutableList<ScanResult>) {
237
+ results.forEach { handleScanResult(it, allowDuplicates) }
238
+ }
239
+
240
+ override fun onScanFailed(errorCode: Int) {
241
+ Log.e(TAG, "Background scan failed: $errorCode")
242
+ }
243
+ }
244
+
245
+ activeScanner.startScan(filters, settingsBuilder.build(), scanCallback)
246
+ }
247
+
248
+ private fun handleScanResult(result: ScanResult, allowDuplicates: Boolean) {
249
+ val deviceId = result.device.address ?: return
250
+ if (!allowDuplicates && !discoveredDeviceIds.add(deviceId)) {
251
+ return
252
+ }
253
+ if (allowDuplicates) {
254
+ discoveredDeviceIds.add(deviceId)
255
+ }
256
+
257
+ updateForegroundNotification()
258
+ }
259
+
260
+ private fun updateForegroundNotification() {
261
+ val manager = getSystemService(Context.NOTIFICATION_SERVICE) as? NotificationManager
262
+ manager?.notify(
263
+ NOTIFICATION_ID,
264
+ buildNotification(neighborCount = discoveredDeviceIds.size)
265
+ )
266
+ }
267
+
268
+ private fun buildNotification(neighborCount: Int): Notification {
269
+ ensureNotificationChannel()
270
+
271
+ val text = if (neighborCount <= 0) {
272
+ notificationText
273
+ } else {
274
+ String.format(
275
+ Locale.US,
276
+ "%s (%d nearby)",
277
+ notificationText,
278
+ neighborCount
279
+ )
280
+ }
281
+
282
+ val builder = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
283
+ Notification.Builder(this, notificationChannelId)
284
+ } else {
285
+ Notification.Builder(this)
286
+ }
287
+
288
+ return builder
289
+ .setContentTitle(notificationTitle)
290
+ .setContentText(text)
291
+ .setSmallIcon(android.R.drawable.stat_sys_data_bluetooth)
292
+ .setOngoing(true)
293
+ .setOnlyAlertOnce(true)
294
+ .setForegroundServiceBehaviorCompat()
295
+ .build()
296
+ }
297
+
298
+ private fun Notification.Builder.setForegroundServiceBehaviorCompat(): Notification.Builder {
299
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
300
+ setForegroundServiceBehavior(Notification.FOREGROUND_SERVICE_IMMEDIATE)
301
+ }
302
+ return this
303
+ }
304
+
305
+ private fun ensureNotificationChannel() {
306
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
307
+ return
308
+ }
309
+
310
+ val manager = getSystemService(Context.NOTIFICATION_SERVICE) as? NotificationManager
311
+ val channel = NotificationChannel(
312
+ notificationChannelId,
313
+ notificationChannelName,
314
+ NotificationManager.IMPORTANCE_LOW
315
+ )
316
+ manager?.createNotificationChannel(channel)
317
+ }
318
+
319
+ private fun parseScanMode(rawValue: String): ScanMode {
320
+ return runCatching { ScanMode.valueOf(rawValue) }.getOrElse { ScanMode.LOWPOWER }
321
+ }
322
+
323
+ companion object {
324
+ const val ACTION_START = "com.munimbluetooth.action.START_BACKGROUND_SESSION"
325
+ const val ACTION_STOP = "com.munimbluetooth.action.STOP_BACKGROUND_SESSION"
326
+
327
+ const val EXTRA_SERVICE_UUIDS = "serviceUUIDs"
328
+ const val EXTRA_LOCAL_NAME = "localName"
329
+ const val EXTRA_ALLOW_DUPLICATES = "allowDuplicates"
330
+ const val EXTRA_SCAN_MODE = "scanMode"
331
+ const val EXTRA_NOTIFICATION_CHANNEL_ID = "notificationChannelId"
332
+ const val EXTRA_NOTIFICATION_CHANNEL_NAME = "notificationChannelName"
333
+ const val EXTRA_NOTIFICATION_TITLE = "notificationTitle"
334
+ const val EXTRA_NOTIFICATION_TEXT = "notificationText"
335
+
336
+ const val DEFAULT_CHANNEL_ID = "munim-bluetooth-background"
337
+ const val DEFAULT_CHANNEL_NAME = "Bluetooth background session"
338
+ const val DEFAULT_NOTIFICATION_TITLE = "Bluetooth nearby mode"
339
+ const val DEFAULT_NOTIFICATION_TEXT = "Scanning for nearby Bluetooth devices"
340
+
341
+ private const val NOTIFICATION_ID = 48231
342
+ private const val TAG = "MunimBluetoothBgSvc"
343
+ }
344
+ }