@@ -60,50 +60,49 @@ class CentralManager(private val context: ReactContext) {
60
60
var characteristicWriteUUID: UUID ? = null
61
61
var characteristicIndicationUUID: UUID ? = null
62
62
63
+ private val scanSettings = ScanSettings
64
+ .Builder ()
65
+ .setScanMode(ScanSettings .SCAN_MODE_BALANCED )
66
+ .setReportDelay(0 )
67
+ .build()
68
+
63
69
fun setService (
64
70
serviceUUID : UUID ,
65
71
writeCharacteristicUUID : UUID ,
66
72
indicationCharacteristicUUID : UUID ,
67
73
) {
68
74
this .serviceUUID = serviceUUID
69
- this . characteristicWriteUUID = writeCharacteristicUUID
70
- this . characteristicIndicationUUID = indicationCharacteristicUUID
75
+ characteristicWriteUUID = writeCharacteristicUUID
76
+ characteristicIndicationUUID = indicationCharacteristicUUID
71
77
}
72
78
73
79
@SuppressLint(" MissingPermission" )
74
80
fun shutdownCentral () {
75
81
try {
76
- this .stopScan()
77
- this .connectedGatt?.disconnect( )
78
- this .connectedGatt?.close()
79
- } catch (e : CentralManagerException . NotScanning ) {
80
- // Not Scanning
81
- } catch (e : Exception ) {
82
- // Error we don't care about
82
+ characteristicIndication?. let {
83
+ unsubscribeFromCharacteristic(it )
84
+ }
85
+ stopScan()
86
+ connectedGatt?.disconnect()
87
+ connectedGatt?.close()
88
+ } catch (_ : Exception ) {
83
89
} finally {
84
- this . serviceUUID = null
85
- this . characteristicWriteUUID = null
86
- this . characteristicIndicationUUID = null
87
- this . connectedGatt = null
88
- this . discoveredPeripherals.clear()
90
+ serviceUUID = null
91
+ characteristicWriteUUID = null
92
+ characteristicIndicationUUID = null
93
+ connectedGatt = null
94
+ discoveredPeripherals.clear()
89
95
}
90
96
}
91
97
92
98
@RequiresPermission(value = " android.permission.BLUETOOTH_SCAN" )
93
99
fun scan () {
94
- val serviceUUID = this .serviceUUID
95
- ? : throw CentralManagerException .NoService ()
96
-
97
- val settings = ScanSettings
98
- .Builder ()
99
- .setScanMode(ScanSettings .SCAN_MODE_BALANCED )
100
- .setReportDelay(0 )
101
- .build()
100
+ val serviceUUID = serviceUUID ? : throw CentralManagerException .NoService ()
102
101
103
102
val filter = ScanFilter .Builder ().setServiceUuid(ParcelUuid (serviceUUID)).build()
104
103
val filters = listOf (filter)
105
104
106
- bleScanner.startScan(filters, settings , scanCallback)
105
+ bleScanner.startScan(filters, scanSettings , scanCallback)
107
106
}
108
107
109
108
@RequiresPermission(value = " android.permission.BLUETOOTH_SCAN" )
@@ -123,6 +122,10 @@ class CentralManager(private val context: ReactContext) {
123
122
@RequiresPermission(value = " android.permission.BLUETOOTH_CONNECT" )
124
123
fun write (message : ByteArray ) {
125
124
Log .d(Constants .TAG , " [CENTRAL]: Sending message of ${message.size} bytes." )
125
+ val chunkSize =
126
+ min(connectedMtu - Constants .NUMBER_OF_BYTES_FOR_DATA_HEADER , message.count())
127
+ Log .d(Constants .TAG , " [CENTRAL]: Using a chunk size of $chunkSize . Sending ${message.size / chunkSize + 1 } messages." )
128
+
126
129
if (isSending) throw CentralManagerException .AlreadySending ()
127
130
val characteristic =
128
131
characteristicWrite ? : throw CentralManagerException .NoCharacteristicFound ()
@@ -138,29 +141,31 @@ class CentralManager(private val context: ReactContext) {
138
141
139
142
Thread {
140
143
isSending = true
141
- val chunkSize =
142
- min(connectedMtu - Constants .NUMBER_OF_BYTES_FOR_DATA_HEADER , message.count())
144
+
143
145
for (chunkIndexStart in 0 .. message.count() step chunkSize) {
144
146
val chunkIndexEnd = min(chunkIndexStart + chunkSize, message.count()) - 1
145
147
val chunkedMessage = message.sliceArray(IntRange (chunkIndexStart, chunkIndexEnd))
148
+ if (chunkedMessage.isEmpty()) {
149
+ continue
150
+ }
146
151
characteristic.value = chunkedMessage
147
152
while (! isPeripheralReady) {
148
153
Thread .sleep(20 )
149
154
}
150
155
Log .d(
151
156
Constants .TAG ,
152
- " [CENTRAL]: Sending chunked message of ${chunkedMessage.size} bytes."
157
+ " [CENTRAL]: Sending chunked message of ${chunkedMessage.size} bytes." ,
153
158
)
154
159
val didSend = connectedPeripheral.writeCharacteristic(characteristic)
155
160
if (didSend) {
156
161
Log .d(
157
162
Constants .TAG ,
158
- " [CENTRAL]: Send the message"
163
+ " [CENTRAL]: Sent the message" ,
159
164
)
160
165
} else {
161
166
Log .d(
162
167
Constants .TAG ,
163
- " [CENTRAL]: Did not send the message"
168
+ " [CENTRAL]: Did not sent the message" ,
164
169
)
165
170
}
166
171
isPeripheralReady = false
@@ -176,17 +181,11 @@ class CentralManager(private val context: ReactContext) {
176
181
}
177
182
178
183
@SuppressLint(" MissingPermission" )
179
- private fun subscribeToIndication (
180
- characteristic : BluetoothGattCharacteristic ,
181
- gatt : BluetoothGatt
182
- ) {
184
+ private fun subscribeToIndications (characteristic : BluetoothGattCharacteristic , gatt : BluetoothGatt ) {
183
185
val cccdUuid = UUID .fromString(Constants .CCC_DESCRIPTOR_UUID )
184
186
characteristic.getDescriptor(cccdUuid)?.let { cccDescriptor ->
185
187
if (! gatt.setCharacteristicNotification(characteristic, true )) {
186
- Log .e(
187
- Constants .TAG ,
188
- " [CENTRAL]: Could not set notifications for characteristic ${characteristic.uuid} "
189
- )
188
+ Log .e(Constants .TAG , " [CENTRAL]: setNotification(true) failed for ${characteristic.uuid} " )
190
189
return
191
190
}
192
191
cccDescriptor.value = BluetoothGattDescriptor .ENABLE_INDICATION_VALUE
@@ -201,10 +200,7 @@ class CentralManager(private val context: ReactContext) {
201
200
val cccdUuid = UUID .fromString(Constants .CCC_DESCRIPTOR_UUID )
202
201
characteristic.getDescriptor(cccdUuid)?.let { cccDescriptor ->
203
202
if (! gatt.setCharacteristicNotification(characteristic, false )) {
204
- Log .e(
205
- Constants .TAG ,
206
- " [CENTRAL]: Could not unsubscribe from characteristic ${characteristic.uuid} "
207
- )
203
+ Log .e(Constants .TAG , " [CENTRAL]: setNotification(false) failed for ${characteristic.uuid} " )
208
204
return
209
205
}
210
206
cccDescriptor.value = BluetoothGattDescriptor .DISABLE_NOTIFICATION_VALUE
@@ -220,17 +216,16 @@ class CentralManager(private val context: ReactContext) {
220
216
override fun onConnectionStateChange (gatt : BluetoothGatt , status : Int , newState : Int ) {
221
217
Log .d(
222
218
Constants .TAG ,
223
- " [CENTRAL]: Connection state has been changed to $newState with status $status "
219
+ " [CENTRAL]: Connection state has been changed to $newState with status $status " ,
224
220
)
225
221
226
222
if (status == BluetoothGatt .GATT_SUCCESS ) {
227
223
val params = Arguments .createMap().apply {
228
224
putString(" identifier" , gatt.device.address)
229
225
}
230
226
if (newState == BluetoothProfile .STATE_CONNECTED ) {
231
- gatt.requestMtu( 512 )
227
+ gatt.discoverServices( )
232
228
stopScan()
233
- sendEvent(BleDidcommEvent .OnConnectedPeripheral , params)
234
229
} else if (newState == BluetoothProfile .STATE_DISCONNECTED ) {
235
230
sendEvent(BleDidcommEvent .OnDisconnectedPeripheral , params)
236
231
gatt.close()
@@ -266,20 +261,11 @@ class CentralManager(private val context: ReactContext) {
266
261
return
267
262
}
268
263
269
- gatt.setCharacteristicNotification(characteristicIndication, true )
270
- val descriptor =
271
- characteristicIndication?.getDescriptor(UUID .fromString(Constants .CCC_DESCRIPTOR_UUID ))
272
- ? : run {
273
- Log .d(
274
- Constants .TAG ,
275
- " [CENTRAL]: Indication Descriptor not found. Make sure CCC is set on the descriptor. Task of the peripheral"
276
- )
277
- gatt.disconnect()
278
- return
279
- }
280
-
281
- descriptor.value = BluetoothGattDescriptor .ENABLE_INDICATION_VALUE
282
- gatt.writeDescriptor(descriptor)
264
+ characteristicIndication?.let {
265
+ subscribeToIndications(it, gatt)
266
+ } ? : run {
267
+ Log .e(Constants .TAG , " [CENTRAL]: characteristic not found $characteristicIndicationUUID " )
268
+ }
283
269
}
284
270
285
271
// Triggered when the client is ready to send the next message
@@ -301,7 +287,7 @@ class CentralManager(private val context: ReactContext) {
301
287
) {
302
288
Log .d(
303
289
Constants .TAG ,
304
- " [CENTRAL]: Received an indication of ${characteristic.value.size} bytes."
290
+ " [CENTRAL]: Received an indication of ${characteristic.value.size} bytes." ,
305
291
)
306
292
307
293
if (characteristic.uuid == characteristicIndicationUUID) {
@@ -320,13 +306,19 @@ class CentralManager(private val context: ReactContext) {
320
306
}
321
307
}
322
308
309
+ @SuppressLint(" MissingPermission" )
323
310
override fun onDescriptorWrite (
324
311
gatt : BluetoothGatt ,
325
312
descriptor : BluetoothGattDescriptor ,
326
- status : Int
313
+ status : Int ,
327
314
) {
328
315
super .onDescriptorWrite(gatt, descriptor, status)
329
316
Log .d(Constants .TAG , " [CENTRAL]: Descriptor write. Connection is ready" )
317
+ gatt.requestMtu(512 )
318
+ val params = Arguments .createMap().apply {
319
+ putString(" identifier" , gatt.device.address)
320
+ }
321
+ sendEvent(BleDidcommEvent .OnConnectedPeripheral , params)
330
322
}
331
323
332
324
// Triggered when the MTU has been changed.
@@ -339,7 +331,6 @@ class CentralManager(private val context: ReactContext) {
339
331
return
340
332
}
341
333
connectedMtu = mtu
342
- gatt.discoverServices()
343
334
}
344
335
}
345
336
@@ -348,7 +339,6 @@ class CentralManager(private val context: ReactContext) {
348
339
override fun onScanResult (callbackType : Int , result : ScanResult ) {
349
340
val name = result.scanRecord?.deviceName ? : result.device.name ? : result.device.address
350
341
Log .d(Constants .TAG , " [CENTRAL]: Found item $name " )
351
- super .onScanResult(callbackType, result)
352
342
353
343
discoveredPeripherals.add(result.device)
354
344
val params = Arguments .createMap().apply {
@@ -358,40 +348,3 @@ class CentralManager(private val context: ReactContext) {
358
348
}
359
349
}
360
350
}
361
-
362
-
363
-
364
-
365
-
366
-
367
-
368
-
369
-
370
-
371
-
372
-
373
-
374
-
375
-
376
-
377
-
378
-
379
-
380
-
381
-
382
-
383
-
384
-
385
-
386
-
387
-
388
-
389
-
390
-
391
-
392
-
393
-
394
-
395
-
396
-
397
-
0 commit comments