Skip to content

Commit 52df1d8

Browse files
committed
Add Central callbacks
1 parent f638ca3 commit 52df1d8

File tree

1 file changed

+374
-0
lines changed

1 file changed

+374
-0
lines changed
Lines changed: 374 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,374 @@
1+
//
2+
// AndroidCentralCallback.swift
3+
// AndroidBluetooth
4+
//
5+
// Created by Alsey Coleman Miller on 7/13/25.
6+
//
7+
8+
#if canImport(FoundationEssentials)
9+
import FoundationEssentials
10+
#elseif canImport(Foundation)
11+
import Foundation
12+
#endif
13+
import JavaKit
14+
import JavaUtil
15+
import Bluetooth
16+
import GATT
17+
18+
extension AndroidCentral {
19+
20+
@JavaClass("org.pureswift.bluetooth.le.ScanCallback")
21+
internal class LowEnergyScanCallback: AndroidBluetooth.ScanCallback {
22+
23+
weak var central: AndroidCentral?
24+
25+
/*
26+
public override func onScanResult(
27+
callbackType: Android.Bluetooth.LE.ScanCallbackType,
28+
result: Android.Bluetooth.LE.ScanResult
29+
) {
30+
31+
central?.log?("\(type(of: self)) \(#function) name: \(result.device.getName() ?? "") address: \(result.device.address)")
32+
33+
let scanData = ScanData(result)
34+
35+
Task {
36+
await central?.storage.update { state in
37+
state.scan.continuation?.yield(scanData)
38+
state.scan.peripherals[scanData.peripheral] = InternalState.Scan.Device(
39+
scanData: scanData,
40+
scanResult: result
41+
)
42+
}
43+
}
44+
}
45+
46+
public override func onBatchScanResults(results: [Android.Bluetooth.LE.ScanResult]) {
47+
48+
central?.log?("\(type(of: self)): \(#function)")
49+
50+
for result in results {
51+
52+
let scanData = ScanData(result)
53+
54+
Task {
55+
await central?.storage.update { state in
56+
state.scan.continuation?.yield(scanData)
57+
state.scan.peripherals[scanData.peripheral] = InternalState.Scan.Device(
58+
scanData: scanData,
59+
scanResult: result
60+
)
61+
}
62+
}
63+
}
64+
}
65+
66+
public override func onScanFailed(error: AndroidBluetoothLowEnergyScanCallback.Error) {
67+
68+
central?.log?("\(type(of: self)): \(#function)")
69+
70+
Task {
71+
await central?.storage.update { state in
72+
state.scan.continuation?.finish(throwing: error)
73+
}
74+
}
75+
}*/
76+
}
77+
78+
@JavaClass("org.pureswift.bluetooth.BluetoothGattCallback")
79+
internal class GattCallback: AndroidBluetooth.BluetoothGattCallback {
80+
81+
private weak var central: AndroidCentral?
82+
83+
/*
84+
convenience init(central: AndroidCentral) {
85+
self.init(javaObject: nil)
86+
bindNewJavaObject()
87+
88+
self.central = central
89+
}
90+
91+
public required init(javaObject: jobject?) {
92+
super.init(javaObject: javaObject)
93+
}
94+
95+
public override func onConnectionStateChange(
96+
gatt: Android.Bluetooth.Gatt,
97+
status: Android.Bluetooth.Gatt.Status,
98+
newState: Android.Bluetooth.Device.State
99+
) {
100+
let log = central?.log
101+
log?("\(type(of: self)): \(#function)")
102+
log?("Status: \(status) - newState: \(newState)")
103+
104+
let peripheral = Peripheral(gatt)
105+
106+
Task {
107+
await central?.storage.update { state in
108+
switch (status, newState) {
109+
case (.success, .connected):
110+
log?("\(peripheral) Connected")
111+
// if we are expecting a new connection
112+
if state.cache[peripheral]?.continuation.connect != nil {
113+
state.cache[peripheral]?.continuation.connect?.resume()
114+
state.cache[peripheral]?.continuation.connect = nil
115+
}
116+
case (.success, .disconnected):
117+
log?("\(peripheral) Disconnected")
118+
state.cache[peripheral] = nil
119+
default:
120+
log?("\(peripheral) Status Error")
121+
state.cache[peripheral]?.continuation.connect?.resume(throwing: status) // throw `status` error
122+
state.cache[peripheral]?.continuation.connect = nil
123+
}
124+
}
125+
}
126+
}
127+
128+
public override func onServicesDiscovered(
129+
gatt: Android.Bluetooth.Gatt,
130+
status: Android.Bluetooth.Gatt.Status
131+
) {
132+
let log = central?.log
133+
let peripheral = Peripheral(gatt)
134+
log?("\(type(of: self)): \(#function) Status: \(status)")
135+
136+
Task {
137+
await central?.storage.update { state in
138+
// success discovering
139+
switch status {
140+
case .success:
141+
guard let services = state.cache[peripheral]?.update(gatt.services) else {
142+
assertionFailure()
143+
return
144+
}
145+
state.cache[peripheral]?.continuation.discoverServices?.resume(returning: services)
146+
default:
147+
state.cache[peripheral]?.continuation.discoverServices?.resume(throwing: status)
148+
}
149+
state.cache[peripheral]?.continuation.discoverServices = nil
150+
}
151+
}
152+
}
153+
154+
public override func onCharacteristicChanged(
155+
gatt: Android.Bluetooth.Gatt,
156+
characteristic: Android.Bluetooth.GattCharacteristic
157+
) {
158+
let log = central?.log
159+
log?("\(type(of: self)): \(#function)")
160+
161+
let peripheral = Peripheral(gatt)
162+
163+
Task {
164+
await central?.storage.update { state in
165+
166+
guard let uuid = characteristic.getUUID().toString() else {
167+
assertionFailure()
168+
return
169+
}
170+
171+
guard let cache = state.cache[peripheral] else {
172+
assertionFailure("Invalid cache for \(uuid)")
173+
return
174+
}
175+
176+
let id = cache.identifier(for: characteristic)
177+
178+
let data = characteristic.getValue()
179+
.map { Data(unsafeBitCast($0, to: [UInt8].self)) } ?? Data()
180+
181+
guard let characteristicCache = cache.characteristics.values[id] else {
182+
assertionFailure("Invalid identifier for \(uuid)")
183+
return
184+
}
185+
186+
guard let notification = characteristicCache.notification else {
187+
assertionFailure("Unexpected notification for \(uuid)")
188+
return
189+
}
190+
191+
notification.yield(data)
192+
}
193+
}
194+
}
195+
196+
public override func onCharacteristicRead(
197+
gatt: Android.Bluetooth.Gatt,
198+
characteristic: Android.Bluetooth.GattCharacteristic,
199+
status: Android.Bluetooth.Gatt.Status
200+
) {
201+
let log = central?.log
202+
let peripheral = Peripheral(gatt)
203+
log?("\(type(of: self)): \(#function) \(peripheral) Status: \(status)")
204+
205+
Task {
206+
await central?.storage.update { state in
207+
208+
switch status {
209+
case .success:
210+
let data = characteristic.getValue()
211+
.map { Data(unsafeBitCast($0, to: [UInt8].self)) } ?? Data()
212+
state.cache[peripheral]?.continuation.readCharacteristic?.resume(returning: data)
213+
default:
214+
state.cache[peripheral]?.continuation.readCharacteristic?.resume(throwing: status)
215+
}
216+
state.cache[peripheral]?.continuation.readCharacteristic = nil
217+
}
218+
}
219+
}
220+
221+
public override func onCharacteristicWrite(
222+
gatt: Android.Bluetooth.Gatt,
223+
characteristic: Android.Bluetooth.GattCharacteristic,
224+
status: Android.Bluetooth.Gatt.Status
225+
) {
226+
central?.log?("\(type(of: self)): \(#function)")
227+
228+
let peripheral = Peripheral(gatt)
229+
230+
Task {
231+
await central?.storage.update { state in
232+
switch status {
233+
case .success:
234+
state.cache[peripheral]?.continuation.writeCharacteristic?.resume()
235+
default:
236+
state.cache[peripheral]?.continuation.writeCharacteristic?.resume(throwing: status)
237+
}
238+
state.cache[peripheral]?.continuation.writeCharacteristic = nil
239+
}
240+
}
241+
}
242+
243+
public override func onDescriptorRead(
244+
gatt: Android.Bluetooth.Gatt,
245+
descriptor: Android.Bluetooth.GattDescriptor,
246+
status: Android.Bluetooth.Gatt.Status
247+
) {
248+
let peripheral = Peripheral(gatt)
249+
250+
guard let uuid = descriptor.getUUID().toString() else {
251+
assertionFailure()
252+
return
253+
}
254+
255+
central?.log?(" \(type(of: self)): \(#function) \(uuid)")
256+
257+
Task {
258+
await central?.storage.update { state in
259+
260+
switch status {
261+
case .success:
262+
let data = descriptor.getValue()
263+
.map { Data(unsafeBitCast($0, to: [UInt8].self)) } ?? Data()
264+
state.cache[peripheral]?.continuation.readDescriptor?.resume(returning: data)
265+
default:
266+
state.cache[peripheral]?.continuation.readDescriptor?.resume(throwing: status)
267+
}
268+
state.cache[peripheral]?.continuation.readDescriptor = nil
269+
}
270+
}
271+
}
272+
273+
public override func onDescriptorWrite(
274+
gatt: Android.Bluetooth.Gatt,
275+
descriptor: Android.Bluetooth.GattDescriptor,
276+
status: AndroidBluetoothGatt.Status
277+
) {
278+
279+
let peripheral = Peripheral(gatt)
280+
281+
guard let uuid = descriptor.getUUID().toString() else {
282+
assertionFailure()
283+
return
284+
}
285+
286+
central?.log?(" \(type(of: self)): \(#function) \(uuid)")
287+
288+
Task {
289+
await central?.storage.update { state in
290+
switch status {
291+
case .success:
292+
state.cache[peripheral]?.continuation.writeDescriptor?.resume()
293+
default:
294+
state.cache[peripheral]?.continuation.writeDescriptor?.resume(throwing: status)
295+
}
296+
state.cache[peripheral]?.continuation.writeDescriptor = nil
297+
}
298+
}
299+
}
300+
301+
public override func onMtuChanged(
302+
gatt: Android.Bluetooth.Gatt,
303+
mtu: Int,
304+
status: Android.Bluetooth.Gatt.Status
305+
) {
306+
central?.log?("\(type(of: self)): \(#function) Peripheral \(Peripheral(gatt)) MTU \(mtu) Status \(status)")
307+
308+
let peripheral = Peripheral(gatt)
309+
310+
guard let central = self.central else {
311+
assertionFailure()
312+
return
313+
}
314+
315+
let oldMTU = central.options.maximumTransmissionUnit
316+
317+
Task {
318+
319+
await central.storage.update { state in
320+
321+
// get new MTU value
322+
guard let newMTU = MaximumTransmissionUnit(rawValue: UInt16(mtu)) else {
323+
assertionFailure("Invalid MTU \(mtu)")
324+
return
325+
}
326+
327+
assert(newMTU <= oldMTU, "Invalid MTU: \(newMTU) > \(oldMTU)")
328+
329+
// cache new MTU value
330+
state.cache[peripheral]?.maximumTransmissionUnit = newMTU
331+
332+
// pending MTU exchange
333+
state.cache[peripheral]?.continuation.exchangeMTU?.resume(returning: newMTU)
334+
state.cache[peripheral]?.continuation.exchangeMTU = nil
335+
return
336+
}
337+
}
338+
}
339+
340+
public override func onPhyRead(gatt: Android.Bluetooth.Gatt, txPhy: Android.Bluetooth.Gatt.TxPhy, rxPhy: Android.Bluetooth.Gatt.RxPhy, status: AndroidBluetoothGatt.Status) {
341+
342+
central?.log?("\(type(of: self)): \(#function)")
343+
}
344+
345+
public override func onPhyUpdate(gatt: Android.Bluetooth.Gatt, txPhy: Android.Bluetooth.Gatt.TxPhy, rxPhy: Android.Bluetooth.Gatt.RxPhy, status: AndroidBluetoothGatt.Status) {
346+
347+
central?.log?("\(type(of: self)): \(#function)")
348+
}
349+
350+
public override func onReadRemoteRssi(gatt: Android.Bluetooth.Gatt, rssi: Int, status: Android.Bluetooth.Gatt.Status) {
351+
352+
central?.log?("\(type(of: self)): \(#function) \(rssi) \(status)")
353+
354+
let peripheral = Peripheral(gatt)
355+
356+
Task {
357+
await central?.storage.update { state in
358+
switch status {
359+
case .success:
360+
state.cache[peripheral]?.continuation.readRemoteRSSI?.resume(returning: rssi)
361+
default:
362+
state.cache[peripheral]?.continuation.readRemoteRSSI?.resume(throwing: status)
363+
}
364+
state.cache[peripheral]?.continuation.readRemoteRSSI = nil
365+
}
366+
}
367+
}
368+
369+
public override func onReliableWriteCompleted(gatt: Android.Bluetooth.Gatt, status: AndroidBluetoothGatt.Status) {
370+
371+
central?.log?("\(type(of: self)): \(#function)")
372+
}*/
373+
}
374+
}

0 commit comments

Comments
 (0)