feat: 支持外围设备接口,优化中心设备接口 (#18)

* 临时提交

* 临时提交

* 临时提交

* fix: 调整接口

* fix: 修复问题

* fix: 调整 iOS 实现

* fix: 添加注释

* fix: 修改预览版本号

* fix: 修复已知问题

* fix: 优化接口

* fix: 解决 32 位 UUID 报错问题

* fix: 修复问题

* fix: 修复依赖项

* fix: 移除多余代码

* fix: 修复已知问题

* fix: 修复问题

* fix: 修改版本号

* fix: 修复问题

* fix: 发布正式版本
This commit is contained in:
Mr剑侠客
2023-10-10 05:01:25 -05:00
committed by GitHub
parent 073c2b9a2e
commit 79c50d638d
113 changed files with 9125 additions and 4560 deletions

View File

@ -1,3 +1,48 @@
## 3.0.0
* Add `PeripheralManager` api.
* Add `CentralManager#readRSSI` method.
* Add `CentralManager.instance` api.
* Add `PeripheralManager.instance` api.
* Move `CentralController` to `CentralManager`.
* Move `CentralState` to `BluetoothLowEnergyState`.
* Move `CentralDiscoveredEventArgs` to `DiscoveredEventArgs`.
* Move `Advertisement` class to `AdvertiseData` class.
* Move `setUp` method from `BluetoothLowEnergy` class to `BluetoothLowEnergyManger` class.
* Change the type of `manufacturerSpecificData` from `Map<int, Uint8List>` to `ManufacturerSpecificData`.
* [Fix the issue that `UUID.fromString()` throw FormatException with 32 bits UUID string.](https://github.com/yanshouwang/bluetooth_low_energy/issues/13)
* Fix known issues.
## 3.0.0-dev.6
* Add default `CCCD` to GATT characteristic for notify and indicate.
* Fix the issue that callbacks must run on ui thread.
* Change requested MTU from 512 to 517 when get the maximum write length of characteristic.
## 3.0.0-dev.5
* Fix the issue that the `BLUETOOTH_ADVERTISE` permission is not requested.
## 3.0.0-dev.4
* Move `Advertisement` class to `AdvertiseData` class.
* Fix known issues.
## 3.0.0-dev.3
* [Fix the issue that `UUID.fromString()` throw FormatException with 32 bits UUID string.](https://github.com/yanshouwang/bluetooth_low_energy/issues/13)
* Change the type of `manufacturerSpecificData` from `Map<int, Uint8List>` to `ManufacturerSpecificData`.
## 3.0.0-dev.2
* Move `setUp` method from `BluetoothLowEnergy` class to `BluetoothLowEnergyManger` class.
* Add `CentralManager.instance` api.
* Add `PeripheralManager.instance` api.
## 3.0.0-dev.1
* Implement new api.
## 2.2.1
* Fix the issue that `CentralController#getMaximumWriteLength` may throw.

View File

@ -9,6 +9,7 @@
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"
android:maxSdkVersion="30" />
<uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
</manifest>

View File

@ -6,33 +6,39 @@ import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding
/** BluetoothLowEnergyAndroid */
class BluetoothLowEnergyAndroid : FlutterPlugin, ActivityAware {
private lateinit var centralController: MyCentralController
private lateinit var centralManager: MyCentralManager
private lateinit var peripheralManager: MyPeripheralManager
override fun onAttachedToEngine(binding: FlutterPlugin.FlutterPluginBinding) {
val context = binding.applicationContext
val binaryMessenger = binding.binaryMessenger
centralController = MyCentralController(context, binaryMessenger)
MyCentralControllerHostApi.setUp(binaryMessenger, centralController)
centralManager = MyCentralManager(context, binaryMessenger)
peripheralManager = MyPeripheralManager(context, binaryMessenger)
MyCentralManagerHostApi.setUp(binaryMessenger, centralManager)
MyPeripheralManagerHostApi.setUp(binaryMessenger, peripheralManager)
}
override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) {
val binaryMessenger = binding.binaryMessenger
MyCentralControllerHostApi.setUp(binaryMessenger, null)
MyCentralManagerHostApi.setUp(binaryMessenger, null)
MyPeripheralManagerHostApi.setUp(binaryMessenger, null)
}
override fun onAttachedToActivity(binding: ActivityPluginBinding) {
centralController.onAttachedToActivity(binding)
}
override fun onDetachedFromActivityForConfigChanges() {
centralController.onDetachedFromActivity()
}
override fun onReattachedToActivityForConfigChanges(binding: ActivityPluginBinding) {
centralController.onAttachedToActivity(binding)
centralManager.onAttachedToActivity(binding)
peripheralManager.onAttachedToActivity(binding)
}
override fun onDetachedFromActivity() {
centralController.onDetachedFromActivity()
centralManager.onDetachedFromActivity()
peripheralManager.onDetachedFromActivity()
}
override fun onReattachedToActivityForConfigChanges(binding: ActivityPluginBinding) {
onAttachedToActivity(binding)
}
override fun onDetachedFromActivityForConfigChanges() {
onDetachedFromActivity()
}
}

View File

@ -0,0 +1,16 @@
package dev.yanshouwang.bluetooth_low_energy_android
import android.bluetooth.le.AdvertiseCallback
import android.bluetooth.le.AdvertiseSettings
class MyAdvertiseCallback(private val peripheralManager: MyPeripheralManager) : AdvertiseCallback() {
override fun onStartSuccess(settingsInEffect: AdvertiseSettings) {
super.onStartSuccess(settingsInEffect)
peripheralManager.onStartSuccess(settingsInEffect)
}
override fun onStartFailure(errorCode: Int) {
super.onStartFailure(errorCode)
peripheralManager.onStartFailure(errorCode)
}
}

View File

@ -0,0 +1,233 @@
package dev.yanshouwang.bluetooth_low_energy_android
import android.bluetooth.BluetoothAdapter
import android.bluetooth.BluetoothDevice
import android.bluetooth.BluetoothGattCharacteristic
import android.bluetooth.BluetoothGattDescriptor
import android.bluetooth.BluetoothGattService
import android.bluetooth.le.AdvertiseData
import android.bluetooth.le.ScanRecord
import android.bluetooth.le.ScanResult
import android.os.ParcelUuid
import android.util.SparseArray
import java.util.UUID
val Any.TAG get() = this::class.java.simpleName as String
val BluetoothAdapter.stateArgs: MyBluetoothLowEnergyStateArgs
get() = state.toBluetoothLowEnergyStateArgs()
fun Int.toBluetoothLowEnergyStateArgs(): MyBluetoothLowEnergyStateArgs {
return when (this) {
BluetoothAdapter.STATE_ON -> MyBluetoothLowEnergyStateArgs.POWEREDON
else -> MyBluetoothLowEnergyStateArgs.POWEREDOFF
}
}
fun BluetoothDevice.toPeripheralArgs(): MyPeripheralArgs {
val hashCodeArgs = hashCode().toLong()
val uuid = this.uuid.toString()
return MyPeripheralArgs(hashCodeArgs, uuid)
}
fun BluetoothDevice.toCentralArgs(): MyCentralArgs {
val hashCodeArgs = hashCode().toLong()
val uuid = this.uuid.toString()
return MyCentralArgs(hashCodeArgs, uuid)
}
val BluetoothDevice.uuid: UUID
get() {
val node = address.filter { char -> char != ':' }
// We don't know the timestamp of the bluetooth device, use nil UUID as prefix.
return UUID.fromString("00000000-0000-0000-0000-$node")
}
val ScanResult.advertiseDataArgs: MyAdvertiseDataArgs
get() {
val record = scanRecord
return if (record == null) {
val nameArgs = null
val serviceUUIDsArgs = emptyList<String?>()
val serviceDataArgs = emptyMap<String?, ByteArray>()
val manufacturerSpecificDataArgs = null
MyAdvertiseDataArgs(nameArgs, serviceUUIDsArgs, serviceDataArgs, manufacturerSpecificDataArgs)
} else {
val nameArgs = record.deviceName
val serviceUUIDsArgs = record.serviceUuids?.map { uuid -> uuid.toString() }
?: emptyList()
val pairs = record.serviceData.map { (uuid, value) ->
val key = uuid.toString()
return@map Pair(key, value)
}.toTypedArray()
val serviceDataArgs = mapOf<String?, ByteArray?>(*pairs)
val manufacturerSpecificDataArgs = record.manufacturerSpecificData.toManufacturerSpecificDataArgs()
MyAdvertiseDataArgs(nameArgs, serviceUUIDsArgs, serviceDataArgs, manufacturerSpecificDataArgs)
}
}
fun SparseArray<ByteArray>.toManufacturerSpecificDataArgs(): MyManufacturerSpecificDataArgs? {
var index = 0
val size = size()
val itemsArgs = mutableListOf<MyManufacturerSpecificDataArgs>()
while (index < size) {
val idArgs = keyAt(index).toLong()
val dataArgs = valueAt(index)
val itemArgs = MyManufacturerSpecificDataArgs(idArgs, dataArgs)
itemsArgs.add(itemArgs)
index++
}
return itemsArgs.lastOrNull()
}
val ScanRecord.rawValues: Map<Byte, ByteArray>
get() {
val rawValues = mutableMapOf<Byte, ByteArray>()
var index = 0
val size = bytes.size
while (index < size) {
val length = bytes[index++].toInt() and 0xff
if (length == 0) {
break
}
val end = index + length
val type = bytes[index++]
val value = bytes.slice(index until end).toByteArray()
rawValues[type] = value
index = end
}
return rawValues.toMap()
}
fun MyAdvertiseDataArgs.toAdvertiseData(adapter: BluetoothAdapter): AdvertiseData {
val advertiseDataBuilder = AdvertiseData.Builder()
if (nameArgs == null) {
advertiseDataBuilder.setIncludeDeviceName(false)
} else {
adapter.name = nameArgs
advertiseDataBuilder.setIncludeDeviceName(true)
}
for (serviceUuidArgs in serviceUUIDsArgs) {
val serviceUUID = ParcelUuid.fromString(serviceUuidArgs)
advertiseDataBuilder.addServiceUuid(serviceUUID)
}
for (entry in serviceDataArgs) {
val serviceDataUUID = ParcelUuid.fromString(entry.key as String)
val serviceData = entry.value as ByteArray
advertiseDataBuilder.addServiceData(serviceDataUUID, serviceData)
}
if (manufacturerSpecificDataArgs != null) {
val manufacturerId = manufacturerSpecificDataArgs.idArgs.toInt()
val manufacturerSpecificData = manufacturerSpecificDataArgs.dataArgs
advertiseDataBuilder.addManufacturerData(manufacturerId, manufacturerSpecificData)
}
return advertiseDataBuilder.build()
}
fun BluetoothGattService.toManufacturerSpecificDataArgs(characteristicsArgs: List<MyGattCharacteristicArgs>): MyGattServiceArgs {
val hashCodeArgs = hashCode().toLong()
val uuidArgs = this.uuid.toString()
return MyGattServiceArgs(hashCodeArgs, uuidArgs, characteristicsArgs)
}
fun BluetoothGattCharacteristic.toManufacturerSpecificDataArgs(descriptorsArgs: List<MyGattDescriptorArgs>): MyGattCharacteristicArgs {
val hashCodeArgs = hashCode().toLong()
val uuidArgs = this.uuid.toString()
return MyGattCharacteristicArgs(hashCodeArgs, uuidArgs, propertyNumbersArgs, descriptorsArgs)
}
val BluetoothGattCharacteristic.propertyNumbersArgs: List<Long>
get() {
val numbersArgs = mutableListOf<Long>()
if (properties and BluetoothGattCharacteristic.PROPERTY_READ != 0) {
val number = MyGattCharacteristicPropertyArgs.READ.raw.toLong()
numbersArgs.add(number)
}
if (properties and BluetoothGattCharacteristic.PROPERTY_WRITE != 0) {
val number = MyGattCharacteristicPropertyArgs.WRITE.raw.toLong()
numbersArgs.add(number)
}
if (properties and BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE != 0) {
val number = MyGattCharacteristicPropertyArgs.WRITEWITHOUTRESPONSE.raw.toLong()
numbersArgs.add(number)
}
if (properties and BluetoothGattCharacteristic.PROPERTY_NOTIFY != 0) {
val number = MyGattCharacteristicPropertyArgs.NOTIFY.raw.toLong()
numbersArgs.add(number)
}
if (properties and BluetoothGattCharacteristic.PROPERTY_INDICATE != 0) {
val number = MyGattCharacteristicPropertyArgs.INDICATE.raw.toLong()
numbersArgs.add(number)
}
return numbersArgs
}
fun BluetoothGattDescriptor.toManufacturerSpecificDataArgs(): MyGattDescriptorArgs {
val hashCodeArgs = hashCode().toLong()
val uuidArgs = this.uuid.toString()
return MyGattDescriptorArgs(hashCodeArgs, uuidArgs, null)
}
fun MyGattServiceArgs.toService(): BluetoothGattService {
val uuid = UUID.fromString(uuidArgs)
val serviceType = BluetoothGattService.SERVICE_TYPE_PRIMARY
return BluetoothGattService(uuid, serviceType)
}
fun MyGattCharacteristicArgs.toCharacteristic(): BluetoothGattCharacteristic {
val uuid = UUID.fromString(uuidArgs)
return BluetoothGattCharacteristic(uuid, properties, permissions)
}
val MyGattCharacteristicArgs.properties: Int
get() {
val propertiesArgs = propertyNumbersArgs.filterNotNull().map { args ->
val raw = args.toInt()
MyGattCharacteristicPropertyArgs.ofRaw(raw)
}
val read = propertiesArgs.contains(MyGattCharacteristicPropertyArgs.READ)
val write = propertiesArgs.contains(MyGattCharacteristicPropertyArgs.WRITE)
val writeWithoutResponse = propertiesArgs.contains(MyGattCharacteristicPropertyArgs.WRITEWITHOUTRESPONSE)
val notify = propertiesArgs.contains(MyGattCharacteristicPropertyArgs.NOTIFY)
val indicate = propertiesArgs.contains(MyGattCharacteristicPropertyArgs.INDICATE)
var properties = 0
if (read) properties = properties or BluetoothGattCharacteristic.PROPERTY_READ
if (write) properties = properties or BluetoothGattCharacteristic.PROPERTY_WRITE
if (writeWithoutResponse) properties = properties or BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE
if (notify) properties = properties or BluetoothGattCharacteristic.PROPERTY_NOTIFY
if (indicate) properties = properties or BluetoothGattCharacteristic.PROPERTY_INDICATE
return properties
}
val MyGattCharacteristicArgs.permissions: Int
get() {
val propertiesArgs = propertyNumbersArgs.filterNotNull().map { args ->
val raw = args.toInt()
MyGattCharacteristicPropertyArgs.ofRaw(raw)
}
val read = propertiesArgs.contains(MyGattCharacteristicPropertyArgs.READ)
val write = propertiesArgs.contains(MyGattCharacteristicPropertyArgs.WRITE)
val writeWithoutResponse = propertiesArgs.contains(MyGattCharacteristicPropertyArgs.WRITEWITHOUTRESPONSE)
var permissions = 0
if (read) permissions = permissions or BluetoothGattCharacteristic.PERMISSION_READ
if (write || writeWithoutResponse) permissions = permissions or BluetoothGattCharacteristic.PERMISSION_WRITE
return permissions
}
fun MyGattDescriptorArgs.toDescriptor(): BluetoothGattDescriptor {
val uuid = UUID.fromString(uuidArgs)
val permissions = BluetoothGattDescriptor.PERMISSION_READ or BluetoothGattDescriptor.PERMISSION_WRITE
return BluetoothGattDescriptor(uuid, permissions)
}
fun Long.toWriteTypeArgs(): MyGattCharacteristicWriteTypeArgs {
val raw = toInt()
return MyGattCharacteristicWriteTypeArgs.ofRaw(raw) ?: throw IllegalArgumentException()
}
fun MyGattCharacteristicWriteTypeArgs.toType(): Int {
return when (this) {
MyGattCharacteristicWriteTypeArgs.WITHRESPONSE -> BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT
MyGattCharacteristicWriteTypeArgs.WITHOUTRESPONSE -> BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE
}
}

View File

@ -7,32 +7,39 @@ import android.bluetooth.BluetoothGattDescriptor
import android.os.Build
import java.util.concurrent.Executor
class MyBluetoothGattCallback(private val myCentralController: MyCentralController, private val executor: Executor) : BluetoothGattCallback() {
class MyBluetoothGattCallback(private val centralManager: MyCentralManager, private val executor: Executor) : BluetoothGattCallback() {
override fun onConnectionStateChange(gatt: BluetoothGatt, status: Int, newState: Int) {
super.onConnectionStateChange(gatt, status, newState)
executor.execute {
myCentralController.onConnectionStateChange(gatt, status, newState)
centralManager.onConnectionStateChange(gatt, status, newState)
}
}
override fun onMtuChanged(gatt: BluetoothGatt, mtu: Int, status: Int) {
super.onMtuChanged(gatt, mtu, status)
executor.execute {
myCentralController.onMtuChanged(gatt, mtu, status)
centralManager.onMtuChanged(gatt, mtu, status)
}
}
override fun onReadRemoteRssi(gatt: BluetoothGatt, rssi: Int, status: Int) {
super.onReadRemoteRssi(gatt, rssi, status)
executor.execute {
centralManager.onReadRemoteRssi(gatt, rssi, status)
}
}
override fun onServicesDiscovered(gatt: BluetoothGatt, status: Int) {
super.onServicesDiscovered(gatt, status)
executor.execute {
myCentralController.onServicesDiscovered(gatt, status)
centralManager.onServicesDiscovered(gatt, status)
}
}
override fun onCharacteristicRead(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic, value: ByteArray, status: Int) {
super.onCharacteristicRead(gatt, characteristic, value, status)
executor.execute {
myCentralController.onCharacteristicRead(characteristic, status, value)
centralManager.onCharacteristicRead(characteristic, status, value)
}
}
@ -44,21 +51,21 @@ class MyBluetoothGattCallback(private val myCentralController: MyCentralControll
}
val value = characteristic.value
executor.execute {
myCentralController.onCharacteristicRead(characteristic, status, value)
centralManager.onCharacteristicRead(characteristic, status, value)
}
}
override fun onCharacteristicWrite(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic, status: Int) {
super.onCharacteristicWrite(gatt, characteristic, status)
executor.execute {
myCentralController.onCharacteristicWrite(characteristic, status)
centralManager.onCharacteristicWrite(characteristic, status)
}
}
override fun onCharacteristicChanged(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic, value: ByteArray) {
super.onCharacteristicChanged(gatt, characteristic, value)
executor.execute {
myCentralController.onCharacteristicChanged(characteristic, value)
centralManager.onCharacteristicChanged(characteristic, value)
}
}
@ -70,14 +77,14 @@ class MyBluetoothGattCallback(private val myCentralController: MyCentralControll
}
val value = characteristic.value
executor.execute {
myCentralController.onCharacteristicChanged(characteristic, value)
centralManager.onCharacteristicChanged(characteristic, value)
}
}
override fun onDescriptorRead(gatt: BluetoothGatt, descriptor: BluetoothGattDescriptor, status: Int, value: ByteArray) {
super.onDescriptorRead(gatt, descriptor, status, value)
executor.execute {
myCentralController.onDescriptorRead(descriptor, status, value)
centralManager.onDescriptorRead(descriptor, status, value)
}
}
@ -89,14 +96,14 @@ class MyBluetoothGattCallback(private val myCentralController: MyCentralControll
}
val value = descriptor.value
executor.execute {
myCentralController.onDescriptorRead(descriptor, status, value)
centralManager.onDescriptorRead(descriptor, status, value)
}
}
override fun onDescriptorWrite(gatt: BluetoothGatt, descriptor: BluetoothGattDescriptor, status: Int) {
super.onDescriptorWrite(gatt, descriptor, status)
executor.execute {
myCentralController.onDescriptorWrite(descriptor, status)
centralManager.onDescriptorWrite(descriptor, status)
}
}
}

View File

@ -0,0 +1,59 @@
package dev.yanshouwang.bluetooth_low_energy_android
import android.bluetooth.BluetoothDevice
import android.bluetooth.BluetoothGattCharacteristic
import android.bluetooth.BluetoothGattDescriptor
import android.bluetooth.BluetoothGattServerCallback
import android.bluetooth.BluetoothGattService
import java.util.concurrent.Executor
class MyBluetoothGattServerCallback(private val peripheralManager: MyPeripheralManager, private val executor: Executor) : BluetoothGattServerCallback() {
override fun onServiceAdded(status: Int, service: BluetoothGattService) {
super.onServiceAdded(status, service)
executor.execute {
peripheralManager.onServiceAdded(status, service)
}
}
override fun onMtuChanged(device: BluetoothDevice, mtu: Int) {
super.onMtuChanged(device, mtu)
executor.execute {
peripheralManager.onMtuChanged(device, mtu)
}
}
override fun onCharacteristicReadRequest(device: BluetoothDevice, requestId: Int, offset: Int, characteristic: BluetoothGattCharacteristic) {
super.onCharacteristicReadRequest(device, requestId, offset, characteristic)
executor.execute {
peripheralManager.onCharacteristicReadRequest(device, requestId, offset, characteristic)
}
}
override fun onCharacteristicWriteRequest(device: BluetoothDevice, requestId: Int, characteristic: BluetoothGattCharacteristic, preparedWrite: Boolean, responseNeeded: Boolean, offset: Int, value: ByteArray) {
super.onCharacteristicWriteRequest(device, requestId, characteristic, preparedWrite, responseNeeded, offset, value)
executor.execute {
peripheralManager.onCharacteristicWriteRequest(device, requestId, characteristic, preparedWrite, responseNeeded, offset, value)
}
}
override fun onNotificationSent(device: BluetoothDevice, status: Int) {
super.onNotificationSent(device, status)
executor.execute {
peripheralManager.onNotificationSent(device, status)
}
}
override fun onDescriptorReadRequest(device: BluetoothDevice, requestId: Int, offset: Int, descriptor: BluetoothGattDescriptor) {
super.onDescriptorReadRequest(device, requestId, offset, descriptor)
executor.execute {
peripheralManager.onDescriptorReadRequest(device, requestId, offset, descriptor)
}
}
override fun onDescriptorWriteRequest(device: BluetoothDevice, requestId: Int, descriptor: BluetoothGattDescriptor, preparedWrite: Boolean, responseNeeded: Boolean, offset: Int, value: ByteArray) {
super.onDescriptorWriteRequest(device, requestId, descriptor, preparedWrite, responseNeeded, offset, value)
executor.execute {
peripheralManager.onDescriptorWriteRequest(device, requestId, descriptor, preparedWrite, responseNeeded, offset, value)
}
}
}

View File

@ -0,0 +1,63 @@
package dev.yanshouwang.bluetooth_low_energy_android
import android.bluetooth.BluetoothAdapter
import android.bluetooth.BluetoothManager
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.content.pm.PackageManager
import androidx.annotation.CallSuper
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding
import java.util.UUID
import java.util.concurrent.Executor
abstract class MyBluetoothLowEnergyManager(private val context: Context) {
companion object {
const val DATA_TYPE_MANUFACTURER_SPECIFIC_DATA = 0xff.toByte()
const val REQUEST_CODE = 443
val HEART_RATE_MEASUREMENT_UUID: UUID = UUID.fromString("00002a37-0000-1000-8000-00805f9b34fb")
val CLIENT_CHARACTERISTIC_CONFIG_UUID: UUID = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb")
}
private lateinit var binding: ActivityPluginBinding
protected val unsupported = !context.packageManager.hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)
protected val executor = ContextCompat.getMainExecutor(context) as Executor
protected val manager get() = ContextCompat.getSystemService(context, BluetoothManager::class.java) as BluetoothManager
protected val adapter get() = manager.adapter as BluetoothAdapter
private val listener by lazy { MyRequestPermissionResultListener(this) }
private val receiver by lazy { MyBroadcastReceiver(this) }
fun onAttachedToActivity(binding: ActivityPluginBinding) {
binding.addRequestPermissionsResultListener(listener)
this.binding = binding
}
fun onDetachedFromActivity() {
binding.removeRequestPermissionsResultListener(listener)
}
protected fun authorize(permissions: Array<String>) {
val activity = binding.activity
ActivityCompat.requestPermissions(activity, permissions, REQUEST_CODE)
}
@CallSuper
protected open fun register() {
val filter = IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED)
context.registerReceiver(receiver, filter)
}
@CallSuper
protected open fun unregister() {
context.unregisterReceiver(receiver)
}
abstract fun onRequestPermissionsResult(requestCode: Int, results: IntArray): Boolean
abstract fun onReceive(intent: Intent)
}

View File

@ -4,8 +4,8 @@ import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
class MyBroadcastReceiver(private val myCentralController: MyCentralController) : BroadcastReceiver() {
class MyBroadcastReceiver(private val bluetoothLowEnergyManager: MyBluetoothLowEnergyManager) : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
myCentralController.onReceive(intent)
bluetoothLowEnergyManager.onReceive(intent)
}
}

View File

@ -1,745 +0,0 @@
package dev.yanshouwang.bluetooth_low_energy_android
import android.bluetooth.BluetoothAdapter
import android.bluetooth.BluetoothDevice
import android.bluetooth.BluetoothGatt
import android.bluetooth.BluetoothGattCharacteristic
import android.bluetooth.BluetoothGattDescriptor
import android.bluetooth.BluetoothGattService
import android.bluetooth.BluetoothManager
import android.bluetooth.BluetoothProfile
import android.bluetooth.BluetoothStatusCodes
import android.bluetooth.le.ScanFilter
import android.bluetooth.le.ScanResult
import android.bluetooth.le.ScanSettings
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.content.pm.PackageManager
import android.os.Build
import android.util.SparseArray
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding
import io.flutter.plugin.common.BinaryMessenger
import java.util.UUID
class MyCentralController(private val context: Context, binaryMessenger: BinaryMessenger) : MyCentralControllerHostApi {
companion object {
// const val DATA_TYPE_MANUFACTURER_SPECIFIC_DATA = 0xff.toByte()
private const val REQUEST_CODE = 443
// private val UUID_HEART_RATE_MEASUREMENT = UUID.fromString("00002a37-0000-1000-8000-00805f9b34fb")
private val UUID_CLIENT_CHARACTERISTIC_CONFIG = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb")
}
private lateinit var binding: ActivityPluginBinding
private val manager = ContextCompat.getSystemService(context, BluetoothManager::class.java) as BluetoothManager
private val adapter = manager.adapter
private val scanner = adapter.bluetoothLeScanner
private val executor = ContextCompat.getMainExecutor(context)
private val myApi = MyCentralControllerFlutterApi(binaryMessenger)
private val myRequestPermissionResultListener = MyRequestPermissionResultListener(this)
private val myBroadcastReceiver = MyBroadcastReceiver(this)
private val myScanCallback = MyScanCallback(this)
private val myBluetoothGattCallback = MyBluetoothGattCallback(this, executor)
private val cachedDevices = mutableMapOf<Int, BluetoothDevice>()
private val cachedGATTs = mutableMapOf<Int, BluetoothGatt>()
private val cachedServices = mutableMapOf<Int, Map<Int, BluetoothGattService>>()
private val cachedCharacteristics = mutableMapOf<Int, Map<Int, BluetoothGattCharacteristic>>()
private val cachedDescriptors = mutableMapOf<Int, Map<Int, BluetoothGattDescriptor>>()
private var registered = false
private var discovering = false
private var setUpCallback: ((Result<MyCentralControllerArgs>) -> Unit)? = null
private var startDiscoveryCallback: ((Result<Unit>) -> Unit)? = null
private val connectCallbacks = mutableMapOf<Int, (Result<Unit>) -> Unit>()
private val disconnectCallbacks = mutableMapOf<Int, (Result<Unit>) -> Unit>()
private val getMaximumWriteLengthCallbacks = mutableMapOf<Int, (Result<Long>) -> Unit>()
private val discoverGattCallbacks = mutableMapOf<Int, (Result<Unit>) -> Unit>()
private val readCharacteristicCallbacks = mutableMapOf<Int, (Result<ByteArray>) -> Unit>()
private val writeCharacteristicCallbacks = mutableMapOf<Int, (Result<Unit>) -> Unit>()
private val readDescriptorCallbacks = mutableMapOf<Int, (Result<ByteArray>) -> Unit>()
private val writeDescriptorCallbacks = mutableMapOf<Int, (Result<Unit>) -> Unit>()
override fun setUp(callback: (Result<MyCentralControllerArgs>) -> Unit) {
try {
val available = context.packageManager.hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)
if (!available) {
val stateNumber = MyCentralStateArgs.UNSUPPORTED.raw.toLong()
val args = MyCentralControllerArgs(stateNumber)
callback(Result.success(args))
}
val unfinishedCallback = setUpCallback
if (unfinishedCallback != null) {
throw IllegalStateException()
}
val permissions = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
arrayOf(android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION, android.Manifest.permission.BLUETOOTH_SCAN, android.Manifest.permission.BLUETOOTH_CONNECT)
} else {
arrayOf(android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION)
}
val activity = binding.activity
ActivityCompat.requestPermissions(activity, permissions, REQUEST_CODE)
setUpCallback = callback
} catch (e: Throwable) {
callback(Result.failure(e))
}
}
override fun tearDown() {
if (registered) {
unregister()
}
if (discovering) {
stopDiscovery()
}
for (gatt in cachedGATTs.values) {
gatt.disconnect()
}
cachedDevices.clear()
cachedGATTs.clear()
cachedServices.clear()
cachedCharacteristics.clear()
cachedDescriptors.clear()
}
private fun register() {
val filter = IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED)
context.registerReceiver(myBroadcastReceiver, filter)
registered = true
}
private fun unregister() {
context.unregisterReceiver(myBroadcastReceiver)
registered = false
}
override fun startDiscovery(callback: (Result<Unit>) -> Unit) {
try {
val unfinishedCallback = startDiscoveryCallback
if (unfinishedCallback != null) {
throw IllegalStateException()
}
val filters = emptyList<ScanFilter>()
val settings = ScanSettings.Builder().setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY).build()
scanner.startScan(filters, settings, myScanCallback)
executor.execute {
val finishedCallback = startDiscoveryCallback ?: return@execute
startDiscoveryCallback = null
finishedCallback(Result.success(Unit))
}
startDiscoveryCallback = callback
} catch (e: Throwable) {
callback(Result.failure(e))
}
}
override fun stopDiscovery() {
scanner.stopScan(myScanCallback)
discovering = false
}
override fun connect(myPeripheralKey: Long, callback: (Result<Unit>) -> Unit) {
try {
val deviceKey = myPeripheralKey.toInt()
val unfinishedCallback = connectCallbacks[deviceKey]
if (unfinishedCallback != null) {
throw IllegalStateException()
}
val device = cachedDevices[deviceKey] as BluetoothDevice
val autoConnect = false
cachedGATTs[deviceKey] = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
val transport = BluetoothDevice.TRANSPORT_LE
device.connectGatt(context, autoConnect, myBluetoothGattCallback, transport)
} else {
device.connectGatt(context, autoConnect, myBluetoothGattCallback)
}
connectCallbacks[deviceKey] = callback
} catch (e: Throwable) {
callback(Result.failure(e))
}
}
override fun disconnect(myPeripheralKey: Long, callback: (Result<Unit>) -> Unit) {
try {
val deviceKey = myPeripheralKey.toInt()
val unfinishedCallback = disconnectCallbacks[deviceKey]
if (unfinishedCallback != null) {
throw IllegalStateException()
}
val gatt = cachedGATTs[deviceKey] as BluetoothGatt
gatt.disconnect()
disconnectCallbacks[deviceKey] = callback
} catch (e: Throwable) {
callback(Result.failure(e))
}
}
override fun getMaximumWriteLength(myPeripheralKey: Long, callback: (Result<Long>) -> Unit) {
try {
val deviceKey = myPeripheralKey.toInt()
val unfinishedCallback = getMaximumWriteLengthCallbacks[deviceKey]
if (unfinishedCallback != null) {
throw IllegalStateException()
}
val gatt = cachedGATTs[deviceKey] as BluetoothGatt
val requesting = gatt.requestMtu(512)
if (!requesting) {
throw IllegalStateException()
}
getMaximumWriteLengthCallbacks[deviceKey] = callback
} catch (e: Throwable) {
callback(Result.failure(e))
}
}
override fun discoverGATT(myPeripheralKey: Long, callback: (Result<Unit>) -> Unit) {
try {
val deviceKey = myPeripheralKey.toInt()
val unfinishedCallback = discoverGattCallbacks[deviceKey]
if (unfinishedCallback != null) {
throw IllegalStateException()
}
val gatt = cachedGATTs[deviceKey] as BluetoothGatt
val discovering = gatt.discoverServices()
if (!discovering) {
throw IllegalStateException()
}
discoverGattCallbacks[deviceKey] = callback
} catch (e: Throwable) {
callback(Result.failure(e))
}
}
override fun getServices(myPeripheralKey: Long): List<MyGattServiceArgs> {
val deviceKey = myPeripheralKey.toInt()
val services = cachedServices[deviceKey] ?: throw IllegalStateException()
return services.values.map { service -> service.toMyArgs() }
}
override fun getCharacteristics(myServiceKey: Long): List<MyGattCharacteristicArgs> {
val serviceKey = myServiceKey.toInt()
val characteristics = cachedCharacteristics[serviceKey] ?: throw IllegalStateException()
return characteristics.values.map { characteristic -> characteristic.toMyArgs() }
}
override fun getDescriptors(myCharacteristicKey: Long): List<MyGattDescriptorArgs> {
val characteristicKey = myCharacteristicKey.toInt()
val descriptors = cachedDescriptors[characteristicKey] ?: throw IllegalStateException()
return descriptors.values.map { descriptor -> descriptor.toMyArgs() }
}
override fun readCharacteristic(myPeripheralKey: Long, myServiceKey: Long, myCharacteristicKey: Long, callback: (Result<ByteArray>) -> Unit) {
try {
val deviceKey = myPeripheralKey.toInt()
val gatt = cachedGATTs[deviceKey] as BluetoothGatt
val serviceKey = myServiceKey.toInt()
val characteristics = cachedCharacteristics[serviceKey]
?: throw IllegalArgumentException()
val characteristicKey = myCharacteristicKey.toInt()
val characteristic = characteristics[characteristicKey] as BluetoothGattCharacteristic
val unfinishedCallback = readCharacteristicCallbacks[characteristicKey]
if (unfinishedCallback != null) {
throw IllegalStateException()
}
val reading = gatt.readCharacteristic(characteristic)
if (!reading) {
throw IllegalStateException()
}
readCharacteristicCallbacks[characteristicKey] = callback
} catch (e: Throwable) {
callback(Result.failure(e))
}
}
override fun writeCharacteristic(myPeripheralKey: Long, myServiceKey: Long, myCharacteristicKey: Long, value: ByteArray, myTypeNumber: Long, callback: (Result<Unit>) -> Unit) {
try {
val deviceKey = myPeripheralKey.toInt()
val gatt = cachedGATTs[deviceKey] as BluetoothGatt
val serviceKey = myServiceKey.toInt()
val characteristics = cachedCharacteristics[serviceKey]
?: throw IllegalArgumentException()
val characteristicKey = myCharacteristicKey.toInt()
val characteristic = characteristics[characteristicKey] as BluetoothGattCharacteristic
val unfinishedCallback = writeCharacteristicCallbacks[characteristicKey]
if (unfinishedCallback != null) {
throw IllegalStateException()
}
val myTypeArgs = myTypeNumber.toMyGattCharacteristicTypeArgs()
val writeType = myTypeArgs.toType()
val writing = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
val writeCode = gatt.writeCharacteristic(characteristic, value, writeType)
writeCode == BluetoothStatusCodes.SUCCESS
} else {
// TODO: remove this when minSdkVersion >= 33
characteristic.value = value
characteristic.writeType = writeType
gatt.writeCharacteristic(characteristic)
}
if (!writing) {
throw IllegalStateException()
}
writeCharacteristicCallbacks[characteristicKey] = callback
} catch (e: Throwable) {
callback(Result.failure(e))
}
}
override fun notifyCharacteristic(myPeripheralKey: Long, myServiceKey: Long, myCharacteristicKey: Long, state: Boolean, callback: (Result<Unit>) -> Unit) {
try {
val deviceKey = myPeripheralKey.toInt()
val gatt = cachedGATTs[deviceKey] as BluetoothGatt
val serviceKey = myServiceKey.toInt()
val characteristics = cachedCharacteristics[serviceKey]
?: throw IllegalArgumentException()
val characteristicKey = myCharacteristicKey.toInt()
val characteristic = characteristics[characteristicKey] as BluetoothGattCharacteristic
val notifying = gatt.setCharacteristicNotification(characteristic, state)
if (!notifying) {
throw IllegalStateException()
}
// TODO: Seems the docs is not correct, this operation is necessary for all characteristics.
// https://developer.android.com/guide/topics/connectivity/bluetooth/transfer-ble-data#notification
// This is specific to Heart Rate Measurement.
// if (characteristic.uuid == UUID_HEART_RATE_MEASUREMENT) {
val descriptor = characteristic.getDescriptor(UUID_CLIENT_CHARACTERISTIC_CONFIG)
val descriptorKey = descriptor.hashCode()
val unfinishedCallback = writeDescriptorCallbacks[descriptorKey]
if (unfinishedCallback != null) {
throw IllegalStateException()
}
val value = if (state) BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE
else BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE
val writing = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
val writeCode = gatt.writeDescriptor(descriptor, value)
writeCode == BluetoothStatusCodes.SUCCESS
} else {
// TODO: remove this when minSdkVersion >= 33
descriptor.value = value
gatt.writeDescriptor(descriptor)
}
if (!writing) {
throw IllegalStateException()
}
writeDescriptorCallbacks[descriptorKey] = callback
// } else {
// callback(Result.success(Unit))
// }
} catch (e: Throwable) {
callback(Result.failure(e))
}
}
override fun readDescriptor(myPeripheralKey: Long, myCharacteristicKey: Long, myDescriptorKey: Long, callback: (Result<ByteArray>) -> Unit) {
try {
val deviceKey = myPeripheralKey.toInt()
val gatt = cachedGATTs[deviceKey] as BluetoothGatt
val characteristicKey = myCharacteristicKey.toInt()
val descriptors = cachedDescriptors[characteristicKey]
?: throw IllegalArgumentException()
val descriptorKey = myDescriptorKey.toInt()
val descriptor = descriptors[descriptorKey] as BluetoothGattDescriptor
val unfinishedCallback = readDescriptorCallbacks[descriptorKey]
if (unfinishedCallback != null) {
throw IllegalStateException()
}
val reading = gatt.readDescriptor(descriptor)
if (!reading) {
throw IllegalStateException()
}
readDescriptorCallbacks[descriptorKey] = callback
} catch (e: Throwable) {
callback(Result.failure(e))
}
}
override fun writeDescriptor(myPeripheralKey: Long, myCharacteristicKey: Long, myDescriptorKey: Long, value: ByteArray, callback: (Result<Unit>) -> Unit) {
try {
val deviceKey = myPeripheralKey.toInt()
val gatt = cachedGATTs[deviceKey] as BluetoothGatt
val characteristicKey = myCharacteristicKey.toInt()
val descriptors = cachedDescriptors[characteristicKey]
?: throw IllegalArgumentException()
val descriptorKey = myDescriptorKey.toInt()
val descriptor = descriptors[descriptorKey] as BluetoothGattDescriptor
val unfinishedCallback = writeDescriptorCallbacks[descriptorKey]
if (unfinishedCallback != null) {
throw IllegalStateException()
}
val writing = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
val writeCode = gatt.writeDescriptor(descriptor, value)
writeCode == BluetoothStatusCodes.SUCCESS
} else {
// TODO: remove this when minSdkVersion >= 33
descriptor.value = value
gatt.writeDescriptor(descriptor)
}
if (!writing) {
throw IllegalStateException()
}
writeDescriptorCallbacks[descriptorKey] = callback
} catch (e: Throwable) {
callback(Result.failure(e))
}
}
fun onAttachedToActivity(binding: ActivityPluginBinding) {
binding.addRequestPermissionsResultListener(myRequestPermissionResultListener)
this.binding = binding
}
fun onDetachedFromActivity() {
binding.removeRequestPermissionsResultListener(myRequestPermissionResultListener)
}
fun onRequestPermissionsResult(requestCode: Int, results: IntArray): Boolean {
if (requestCode != REQUEST_CODE) {
return false
}
val callback = setUpCallback ?: return false
val isGranted = results.all { r -> r == PackageManager.PERMISSION_GRANTED }
if (isGranted) {
register()
}
val myStateArgs = if (isGranted) adapter.myStateArgs
else MyCentralStateArgs.UNAUTHORIZED
val myStateNumber = myStateArgs.raw.toLong()
val myArgs = MyCentralControllerArgs(myStateNumber)
callback(Result.success(myArgs))
return true
}
fun onReceive(intent: Intent) {
val action = intent.action
if (action != BluetoothAdapter.ACTION_STATE_CHANGED) {
return
}
val state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.STATE_OFF)
val myStateArgs = state.toMyCentralStateArgs()
val myStateNumber = myStateArgs.raw.toLong()
myApi.onStateChanged(myStateNumber) {}
}
fun onScanFailed(errorCode: Int) {
val callback = startDiscoveryCallback ?: return
startDiscoveryCallback = null
val error = IllegalStateException("Start discovery failed with error code: $errorCode")
callback(Result.failure(error))
}
fun onScanResult(result: ScanResult) {
val device = result.device
val deviceKey = device.hashCode()
cachedDevices[deviceKey] = device
val myPeripheralArgs = device.toMyArgs()
val rssi = result.rssi.toLong()
val myAdvertisementArgs = result.myAdvertisementArgs
myApi.onDiscovered(myPeripheralArgs, rssi, myAdvertisementArgs) {}
}
fun onConnectionStateChange(gatt: BluetoothGatt, status: Int, newState: Int) {
val device = gatt.device
val deviceKey = device.hashCode()
val myPeripheralKey = deviceKey.toLong()
if (newState != BluetoothProfile.STATE_CONNECTED) {
gatt.close()
cachedGATTs.remove(deviceKey)
val error = IllegalStateException("GATT is disconnected with status: $status")
val getMaximumWriteLengthCallback = getMaximumWriteLengthCallbacks.remove(deviceKey)
if (getMaximumWriteLengthCallback != null) {
getMaximumWriteLengthCallback(Result.failure(error))
}
val discoverGattCallback = discoverGattCallbacks.remove(deviceKey)
if (discoverGattCallback != null) {
discoverGattCallback(Result.failure(error))
}
val services = cachedServices[deviceKey] ?: emptyMap()
for (service in services) {
val characteristics = cachedCharacteristics[service.key] ?: emptyMap()
for (characteristic in characteristics) {
val readCharacteristicCallback = readCharacteristicCallbacks.remove(characteristic.key)
val writeCharacteristicCallback = writeCharacteristicCallbacks.remove(characteristic.key)
if (readCharacteristicCallback != null) {
readCharacteristicCallback(Result.failure(error))
}
if (writeCharacteristicCallback != null) {
writeCharacteristicCallback(Result.failure(error))
}
val descriptors = cachedDescriptors[characteristic.key] ?: emptyMap()
for (descriptor in descriptors) {
val readDescriptorCallback = readDescriptorCallbacks.remove(descriptor.key)
val writeDescriptorCallback = writeDescriptorCallbacks.remove(descriptor.key)
if (readDescriptorCallback != null) {
readDescriptorCallback(Result.failure(error))
}
if (writeDescriptorCallback != null) {
writeDescriptorCallback(Result.failure(error))
}
}
}
}
}
val connectCallback = connectCallbacks.remove(deviceKey)
val disconnectCallback = disconnectCallbacks.remove(deviceKey)
if (connectCallback == null && disconnectCallback == null) {
// State changed.
val state = newState == BluetoothProfile.STATE_CONNECTED
myApi.onPeripheralStateChanged(myPeripheralKey, state) {}
} else {
if (connectCallback != null) {
if (status == BluetoothGatt.GATT_SUCCESS) {
// Connect succeed.
connectCallback(Result.success(Unit))
myApi.onPeripheralStateChanged(myPeripheralKey, true) {}
} else {
// Connect failed.
val error = IllegalStateException("Connect failed with status: $status")
connectCallback(Result.failure(error))
}
}
if (disconnectCallback != null) {
if (status == BluetoothGatt.GATT_SUCCESS) {
// Disconnect succeed.
disconnectCallback(Result.success(Unit))
myApi.onPeripheralStateChanged(myPeripheralKey, false) {}
} else {
// Disconnect failed.
val error = IllegalStateException("Connect failed with status: $status")
disconnectCallback(Result.failure(error))
}
}
}
}
fun onMtuChanged(gatt: BluetoothGatt, mtu: Int, status: Int) {
val device = gatt.device
val deviceKey = device.hashCode()
val callback = getMaximumWriteLengthCallbacks.remove(deviceKey) ?: return
if (status == BluetoothGatt.GATT_SUCCESS) {
val maximumWriteLength = (mtu - 3).toLong()
callback(Result.success(maximumWriteLength))
} else {
val error = IllegalStateException("Get maximum write length failed with status: $status")
callback(Result.failure(error))
}
}
fun onServicesDiscovered(gatt: BluetoothGatt, status: Int) {
val device = gatt.device
val deviceKey = device.hashCode()
val callback = discoverGattCallbacks.remove(deviceKey) ?: return
if (status == BluetoothGatt.GATT_SUCCESS) {
val cachedServices = mutableMapOf<Int, BluetoothGattService>()
for (service in gatt.services) {
val serviceKey = service.hashCode()
cachedServices[serviceKey] = service
val cachedCharacteristics = mutableMapOf<Int, BluetoothGattCharacteristic>()
for (characteristic in service.characteristics) {
val characteristicKey = characteristic.hashCode()
cachedCharacteristics[characteristicKey] = characteristic
val cachedDescriptors = mutableMapOf<Int, BluetoothGattDescriptor>()
for (descriptor in characteristic.descriptors) {
val descriptorKey = descriptor.hashCode()
cachedDescriptors[descriptorKey] = descriptor
}
this.cachedDescriptors[characteristicKey] = cachedDescriptors
}
this.cachedCharacteristics[serviceKey] = cachedCharacteristics
}
this.cachedServices[deviceKey] = cachedServices
callback(Result.success(Unit))
} else {
val error = IllegalStateException("Discover GATT failed with status: $status")
callback(Result.failure(error))
}
}
fun onCharacteristicRead(characteristic: BluetoothGattCharacteristic, status: Int, value: ByteArray) {
val characteristicKey = characteristic.hashCode()
val callback = readCharacteristicCallbacks.remove(characteristicKey) ?: return
if (status == BluetoothGatt.GATT_SUCCESS) {
callback(Result.success(value))
} else {
val error = IllegalStateException("Read characteristic failed with status: $status.")
callback(Result.failure(error))
}
}
fun onCharacteristicWrite(characteristic: BluetoothGattCharacteristic, status: Int) {
val characteristicKey = characteristic.hashCode()
val callback = writeCharacteristicCallbacks.remove(characteristicKey) ?: return
if (status == BluetoothGatt.GATT_SUCCESS) {
callback(Result.success(Unit))
} else {
val error = IllegalStateException("Write characteristic failed with status: $status.")
callback(Result.failure(error))
}
}
fun onCharacteristicChanged(characteristic: BluetoothGattCharacteristic, value: ByteArray) {
val characteristicKey = characteristic.hashCode()
val myCharacteristicKey = characteristicKey.toLong()
myApi.onCharacteristicValueChanged(myCharacteristicKey, value) {}
}
fun onDescriptorRead(descriptor: BluetoothGattDescriptor, status: Int, value: ByteArray) {
val descriptorKey = descriptor.hashCode()
val callback = readDescriptorCallbacks.remove(descriptorKey) ?: return
if (status == BluetoothGatt.GATT_SUCCESS) {
callback(Result.success(value))
} else {
val error = IllegalStateException("Read descriptor failed with status: $status.")
callback(Result.failure(error))
}
}
fun onDescriptorWrite(descriptor: BluetoothGattDescriptor, status: Int) {
val descriptorKey = descriptor.hashCode()
val callback = writeDescriptorCallbacks.remove(descriptorKey) ?: return
if (status == BluetoothGatt.GATT_SUCCESS) {
callback(Result.success(Unit))
} else {
val error = IllegalStateException("Write descriptor failed with status: $status.")
callback(Result.failure(error))
}
}
}
private val BluetoothAdapter.myStateArgs: MyCentralStateArgs
get() = state.toMyCentralStateArgs()
private fun Int.toMyCentralStateArgs(): MyCentralStateArgs {
return when (this) {
BluetoothAdapter.STATE_ON -> MyCentralStateArgs.POWEREDON
else -> MyCentralStateArgs.POWEREDOFF
}
}
private fun BluetoothDevice.toMyArgs(): MyPeripheralArgs {
val key = hashCode().toLong()
val uuid = this.uuid.toString()
return MyPeripheralArgs(key, uuid)
}
private val BluetoothDevice.uuid: UUID
get() {
val node = address.filter { char -> char != ':' }
// We don't know the timestamp of the bluetooth device, use nil UUID as prefix.
return UUID.fromString("00000000-0000-0000-0000-$node")
}
private val ScanResult.myAdvertisementArgs: MyAdvertisementArgs
get() {
val record = scanRecord
return if (record == null) {
val name = null
val manufacturerSpecificData = emptyMap<Long?, ByteArray?>()
val serviceUUIDs = emptyList<String?>()
val serviceData = emptyMap<String?, ByteArray>()
MyAdvertisementArgs(name, manufacturerSpecificData, serviceUUIDs, serviceData)
} else {
val name = record.deviceName
val manufacturerSpecificData = record.manufacturerSpecificData.toMyArgs()
val serviceUUIDs = record.serviceUuids?.map { uuid -> uuid.toString() }
?: emptyList()
val pairs = record.serviceData.entries.map { (uuid, value) ->
val key = uuid.toString()
return@map Pair(key, value)
}.toTypedArray()
val serviceData = mapOf<String?, ByteArray?>(*pairs)
MyAdvertisementArgs(name, manufacturerSpecificData, serviceUUIDs, serviceData)
}
}
private fun SparseArray<ByteArray>.toMyArgs(): Map<Long?, ByteArray?> {
var index = 0
val size = size()
val values = mutableMapOf<Long?, ByteArray>()
while (index < size) {
val key = keyAt(index).toLong()
val value = valueAt(index)
values[key] = value
index++
}
return values
}
//private val ScanRecord.rawValues: Map<Byte, ByteArray>
// get() {
// val rawValues = mutableMapOf<Byte, ByteArray>()
// var index = 0
// val size = bytes.size
// while (index < size) {
// val length = bytes[index++].toInt() and 0xff
// if (length == 0) {
// break
// }
// val end = index + length
// val type = bytes[index++]
// val value = bytes.slice(index until end).toByteArray()
// rawValues[type] = value
// index = end
// }
// return rawValues.toMap()
// }
private fun BluetoothGattService.toMyArgs(): MyGattServiceArgs {
val key = hashCode().toLong()
val uuid = this.uuid.toString()
return MyGattServiceArgs(key, uuid)
}
private fun BluetoothGattCharacteristic.toMyArgs(): MyGattCharacteristicArgs {
val key = hashCode().toLong()
val uuid = this.uuid.toString()
return MyGattCharacteristicArgs(key, uuid, myPropertyNumbers)
}
private val BluetoothGattCharacteristic.myPropertyNumbers: List<Long>
get() {
val numbers = mutableListOf<Long>()
if (properties and BluetoothGattCharacteristic.PROPERTY_READ != 0) {
val number = MyGattCharacteristicPropertyArgs.READ.raw.toLong()
numbers.add(number)
}
if (properties and BluetoothGattCharacteristic.PROPERTY_WRITE != 0) {
val number = MyGattCharacteristicPropertyArgs.WRITE.raw.toLong()
numbers.add(number)
}
if (properties and BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE != 0) {
val number = MyGattCharacteristicPropertyArgs.WRITEWITHOUTRESPONSE.raw.toLong()
numbers.add(number)
}
if (properties and BluetoothGattCharacteristic.PROPERTY_NOTIFY != 0) {
val number = MyGattCharacteristicPropertyArgs.NOTIFY.raw.toLong()
numbers.add(number)
}
if (properties and BluetoothGattCharacteristic.PROPERTY_INDICATE != 0) {
val number = MyGattCharacteristicPropertyArgs.INDICATE.raw.toLong()
numbers.add(number)
}
return numbers
}
private fun BluetoothGattDescriptor.toMyArgs(): MyGattDescriptorArgs {
val key = hashCode().toLong()
val uuid = this.uuid.toString()
return MyGattDescriptorArgs(key, uuid)
}
private fun Long.toMyGattCharacteristicTypeArgs(): MyGattCharacteristicWriteTypeArgs {
val raw = toInt()
return MyGattCharacteristicWriteTypeArgs.ofRaw(raw) ?: throw IllegalArgumentException()
}
private fun MyGattCharacteristicWriteTypeArgs.toType(): Int {
return when (this) {
MyGattCharacteristicWriteTypeArgs.WITHRESPONSE -> BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT
MyGattCharacteristicWriteTypeArgs.WITHOUTRESPONSE -> BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE
}
}

View File

@ -0,0 +1,627 @@
package dev.yanshouwang.bluetooth_low_energy_android
import android.bluetooth.BluetoothAdapter
import android.bluetooth.BluetoothDevice
import android.bluetooth.BluetoothGatt
import android.bluetooth.BluetoothGattCharacteristic
import android.bluetooth.BluetoothGattDescriptor
import android.bluetooth.BluetoothGattService
import android.bluetooth.BluetoothProfile
import android.bluetooth.BluetoothStatusCodes
import android.bluetooth.le.ScanFilter
import android.bluetooth.le.ScanResult
import android.bluetooth.le.ScanSettings
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.os.Build
import io.flutter.plugin.common.BinaryMessenger
class MyCentralManager(private val context: Context, binaryMessenger: BinaryMessenger) : MyBluetoothLowEnergyManager(context), MyCentralManagerHostApi {
private val scanner get() = adapter.bluetoothLeScanner
private val api = MyCentralManagerFlutterApi(binaryMessenger)
private val scanCallback = MyScanCallback(this)
private val bluetoothGattCallback = MyBluetoothGattCallback(this, executor)
private val devices = mutableMapOf<Long, BluetoothDevice>()
private val bluetoothGATTs = mutableMapOf<Long, BluetoothGatt>()
private val services = mutableMapOf<Long, BluetoothGattService>()
private val characteristics = mutableMapOf<Long, BluetoothGattCharacteristic>()
private val descriptors = mutableMapOf<Long, BluetoothGattDescriptor>()
private val peripheralsArgs = mutableMapOf<Int, MyPeripheralArgs>()
private val servicesArgsOfPeripherals = mutableMapOf<Long, List<MyGattServiceArgs>>()
private val servicesArgs = mutableMapOf<Int, MyGattServiceArgs>()
private val characteristicsArgs = mutableMapOf<Int, MyGattCharacteristicArgs>()
private val descriptorsArgs = mutableMapOf<Int, MyGattDescriptorArgs>()
private var registered = false
private var discovering = false
private var setUpCallback: ((Result<MyCentralManagerArgs>) -> Unit)? = null
private var startDiscoveryCallback: ((Result<Unit>) -> Unit)? = null
private val connectCallbacks = mutableMapOf<Long, (Result<Unit>) -> Unit>()
private val disconnectCallbacks = mutableMapOf<Long, (Result<Unit>) -> Unit>()
private val getMaximumWriteLengthCallbacks = mutableMapOf<Long, (Result<Long>) -> Unit>()
private val readRssiCallbacks = mutableMapOf<Long, (Result<Long>) -> Unit>()
private val discoverGattCallbacks = mutableMapOf<Long, (Result<List<MyGattServiceArgs>>) -> Unit>()
private val readCharacteristicCallbacks = mutableMapOf<Long, (Result<ByteArray>) -> Unit>()
private val writeCharacteristicCallbacks = mutableMapOf<Long, (Result<Unit>) -> Unit>()
private val readDescriptorCallbacks = mutableMapOf<Long, (Result<ByteArray>) -> Unit>()
private val writeDescriptorCallbacks = mutableMapOf<Long, (Result<Unit>) -> Unit>()
override fun setUp(callback: (Result<MyCentralManagerArgs>) -> Unit) {
try {
if (setUpCallback != null) {
throw IllegalStateException()
}
tearDown()
if (unsupported) {
val stateNumberArgs = MyBluetoothLowEnergyStateArgs.UNSUPPORTED.raw.toLong()
val args = MyCentralManagerArgs(stateNumberArgs)
callback(Result.success(args))
}
val permissions = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
arrayOf(android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION, android.Manifest.permission.BLUETOOTH_SCAN, android.Manifest.permission.BLUETOOTH_CONNECT)
} else {
arrayOf(android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION)
}
authorize(permissions)
setUpCallback = callback
} catch (e: Throwable) {
callback(Result.failure(e))
}
}
private fun tearDown() {
if (registered) {
unregister()
}
if (discovering) {
stopDiscovery()
}
for (gatt in bluetoothGATTs.values) {
gatt.disconnect()
}
devices.clear()
bluetoothGATTs.clear()
services.clear()
characteristics.clear()
descriptors.clear()
peripheralsArgs.clear()
servicesArgsOfPeripherals.clear()
servicesArgs.clear()
characteristicsArgs.clear()
descriptorsArgs.clear()
setUpCallback = null
startDiscoveryCallback = null
connectCallbacks.clear()
disconnectCallbacks.clear()
getMaximumWriteLengthCallbacks.clear()
readRssiCallbacks.clear()
discoverGattCallbacks.clear()
readCharacteristicCallbacks.clear()
writeCharacteristicCallbacks.clear()
readDescriptorCallbacks.clear()
writeDescriptorCallbacks.clear()
}
override fun register() {
super.register()
registered = true
}
override fun unregister() {
super.unregister()
registered = false
}
override fun startDiscovery(callback: (Result<Unit>) -> Unit) {
try {
if (startDiscoveryCallback != null) {
throw IllegalStateException()
}
val filters = emptyList<ScanFilter>()
val settings = ScanSettings.Builder().setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY).build()
scanner.startScan(filters, settings, scanCallback)
executor.execute { onScanSucceed() }
startDiscoveryCallback = callback
} catch (e: Throwable) {
callback(Result.failure(e))
}
}
override fun stopDiscovery() {
scanner.stopScan(scanCallback)
discovering = false
}
override fun connect(peripheralHashCodeArgs: Long, callback: (Result<Unit>) -> Unit) {
try {
val unfinishedCallback = connectCallbacks[peripheralHashCodeArgs]
if (unfinishedCallback != null) {
throw IllegalStateException()
}
val device = devices[peripheralHashCodeArgs] as BluetoothDevice
val autoConnect = false
// Add to bluetoothGATTs cache.
bluetoothGATTs[peripheralHashCodeArgs] = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
val transport = BluetoothDevice.TRANSPORT_LE
device.connectGatt(context, autoConnect, bluetoothGattCallback, transport)
} else {
device.connectGatt(context, autoConnect, bluetoothGattCallback)
}
connectCallbacks[peripheralHashCodeArgs] = callback
} catch (e: Throwable) {
callback(Result.failure(e))
}
}
override fun disconnect(peripheralHashCodeArgs: Long, callback: (Result<Unit>) -> Unit) {
try {
val unfinishedCallback = disconnectCallbacks[peripheralHashCodeArgs]
if (unfinishedCallback != null) {
throw IllegalStateException()
}
val gatt = bluetoothGATTs[peripheralHashCodeArgs] as BluetoothGatt
gatt.disconnect()
disconnectCallbacks[peripheralHashCodeArgs] = callback
} catch (e: Throwable) {
callback(Result.failure(e))
}
}
override fun getMaximumWriteLength(peripheralHashCodeArgs: Long, callback: (Result<Long>) -> Unit) {
try {
val unfinishedCallback = getMaximumWriteLengthCallbacks[peripheralHashCodeArgs]
if (unfinishedCallback != null) {
throw IllegalStateException()
}
val gatt = bluetoothGATTs[peripheralHashCodeArgs] as BluetoothGatt
val requesting = gatt.requestMtu(517)
if (!requesting) {
throw IllegalStateException()
}
getMaximumWriteLengthCallbacks[peripheralHashCodeArgs] = callback
} catch (e: Throwable) {
callback(Result.failure(e))
}
}
override fun readRSSI(peripheralHashCodeArgs: Long, callback: (Result<Long>) -> Unit) {
try {
val unfinishedCallback = readRssiCallbacks[peripheralHashCodeArgs]
if (unfinishedCallback != null) {
throw IllegalStateException()
}
val gatt = bluetoothGATTs[peripheralHashCodeArgs] as BluetoothGatt
val reading = gatt.readRemoteRssi()
if (!reading) {
throw IllegalStateException()
}
readRssiCallbacks[peripheralHashCodeArgs] = callback
} catch (e: Throwable) {
callback(Result.failure(e))
}
}
override fun discoverGATT(peripheralHashCodeArgs: Long, callback: (Result<List<MyGattServiceArgs>>) -> Unit) {
try {
val unfinishedCallback = discoverGattCallbacks[peripheralHashCodeArgs]
if (unfinishedCallback != null) {
throw IllegalStateException()
}
val gatt = bluetoothGATTs[peripheralHashCodeArgs] as BluetoothGatt
val discovering = gatt.discoverServices()
if (!discovering) {
throw IllegalStateException()
}
discoverGattCallbacks[peripheralHashCodeArgs] = callback
} catch (e: Throwable) {
callback(Result.failure(e))
}
}
override fun readCharacteristic(peripheralHashCodeArgs: Long, characteristicHashCodeArgs: Long, callback: (Result<ByteArray>) -> Unit) {
try {
val unfinishedCallback = readCharacteristicCallbacks[characteristicHashCodeArgs]
if (unfinishedCallback != null) {
throw IllegalStateException()
}
val gatt = bluetoothGATTs[peripheralHashCodeArgs] as BluetoothGatt
val characteristic = characteristics[characteristicHashCodeArgs] as BluetoothGattCharacteristic
val reading = gatt.readCharacteristic(characteristic)
if (!reading) {
throw IllegalStateException()
}
readCharacteristicCallbacks[characteristicHashCodeArgs] = callback
} catch (e: Throwable) {
callback(Result.failure(e))
}
}
override fun writeCharacteristic(peripheralHashCodeArgs: Long, characteristicHashCodeArgs: Long, valueArgs: ByteArray, typeNumberArgs: Long, callback: (Result<Unit>) -> Unit) {
try {
val unfinishedCallback = writeCharacteristicCallbacks[characteristicHashCodeArgs]
if (unfinishedCallback != null) {
throw IllegalStateException()
}
val gatt = bluetoothGATTs[peripheralHashCodeArgs] as BluetoothGatt
val characteristic = characteristics[characteristicHashCodeArgs] as BluetoothGattCharacteristic
val typeArgs = typeNumberArgs.toWriteTypeArgs()
val type = typeArgs.toType()
val writing = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
val code = gatt.writeCharacteristic(characteristic, valueArgs, type)
code == BluetoothStatusCodes.SUCCESS
} else {
// TODO: remove this when minSdkVersion >= 33
characteristic.value = valueArgs
characteristic.writeType = type
gatt.writeCharacteristic(characteristic)
}
if (!writing) {
throw IllegalStateException()
}
writeCharacteristicCallbacks[characteristicHashCodeArgs] = callback
} catch (e: Throwable) {
callback(Result.failure(e))
}
}
override fun notifyCharacteristic(peripheralHashCodeArgs: Long, characteristicHashCodeArgs: Long, stateArgs: Boolean, callback: (Result<Unit>) -> Unit) {
try {
val gatt = bluetoothGATTs[peripheralHashCodeArgs] as BluetoothGatt
val characteristic = characteristics[characteristicHashCodeArgs] as BluetoothGattCharacteristic
val notifying = gatt.setCharacteristicNotification(characteristic, stateArgs)
if (!notifying) {
throw IllegalStateException()
}
// TODO: Seems the docs is not correct, this operation is necessary for all characteristics.
// https://developer.android.com/guide/topics/connectivity/bluetooth/transfer-ble-data#notification
// This is specific to Heart Rate Measurement.
// if (characteristic.uuid == UUID_HEART_RATE_MEASUREMENT) {
val descriptor = characteristic.getDescriptor(CLIENT_CHARACTERISTIC_CONFIG_UUID)
val descriptorHashCode = descriptor.hashCode()
val descriptorArgs = descriptorsArgs[descriptorHashCode] as MyGattDescriptorArgs
val descriptorHashCodeArgs = descriptorArgs.hashCodeArgs
val unfinishedCallback = writeDescriptorCallbacks[descriptorHashCodeArgs]
if (unfinishedCallback != null) {
throw IllegalStateException()
}
val value = if (stateArgs) BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE
else BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE
val writing = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
val code = gatt.writeDescriptor(descriptor, value)
code == BluetoothStatusCodes.SUCCESS
} else {
// TODO: remove this when minSdkVersion >= 33
descriptor.value = value
gatt.writeDescriptor(descriptor)
}
if (!writing) {
throw IllegalStateException()
}
writeDescriptorCallbacks[descriptorHashCodeArgs] = callback
// } else {
// callback(Result.success(Unit))
// }
} catch (e: Throwable) {
callback(Result.failure(e))
}
}
override fun readDescriptor(peripheralHashCodeArgs: Long, descriptorHashCodeArgs: Long, callback: (Result<ByteArray>) -> Unit) {
try {
val unfinishedCallback = readDescriptorCallbacks[descriptorHashCodeArgs]
if (unfinishedCallback != null) {
throw IllegalStateException()
}
val gatt = bluetoothGATTs[peripheralHashCodeArgs] as BluetoothGatt
val descriptor = descriptors[descriptorHashCodeArgs] as BluetoothGattDescriptor
val reading = gatt.readDescriptor(descriptor)
if (!reading) {
throw IllegalStateException()
}
readDescriptorCallbacks[descriptorHashCodeArgs] = callback
} catch (e: Throwable) {
callback(Result.failure(e))
}
}
override fun writeDescriptor(peripheralHashCodeArgs: Long, descriptorHashCodeArgs: Long, valueArgs: ByteArray, callback: (Result<Unit>) -> Unit) {
try {
val unfinishedCallback = writeDescriptorCallbacks[descriptorHashCodeArgs]
if (unfinishedCallback != null) {
throw IllegalStateException()
}
val gatt = bluetoothGATTs[peripheralHashCodeArgs] as BluetoothGatt
val descriptor = descriptors[descriptorHashCodeArgs] as BluetoothGattDescriptor
val writing = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
val code = gatt.writeDescriptor(descriptor, valueArgs)
code == BluetoothStatusCodes.SUCCESS
} else {
// TODO: remove this when minSdkVersion >= 33
descriptor.value = valueArgs
gatt.writeDescriptor(descriptor)
}
if (!writing) {
throw IllegalStateException()
}
writeDescriptorCallbacks[descriptorHashCodeArgs] = callback
} catch (e: Throwable) {
callback(Result.failure(e))
}
}
override fun onRequestPermissionsResult(requestCode: Int, results: IntArray): Boolean {
if (requestCode != REQUEST_CODE) {
return false
}
val authorized = results.all { r -> r == PackageManager.PERMISSION_GRANTED }
val callback = setUpCallback ?: return false
setUpCallback = null
val stateArgs = if (authorized) adapter.stateArgs
else MyBluetoothLowEnergyStateArgs.UNAUTHORIZED
val stateNumberArgs = stateArgs.raw.toLong()
val args = MyCentralManagerArgs(stateNumberArgs)
callback(Result.success(args))
if (authorized) {
register()
}
return true
}
override fun onReceive(intent: Intent) {
val action = intent.action
if (action != BluetoothAdapter.ACTION_STATE_CHANGED) {
return
}
val state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.STATE_OFF)
val stateArgs = state.toBluetoothLowEnergyStateArgs()
val stateNumberArgs = stateArgs.raw.toLong()
api.onStateChanged(stateNumberArgs) {}
}
private fun onScanSucceed() {
discovering = true
val callback = startDiscoveryCallback ?: return
startDiscoveryCallback = null
callback(Result.success(Unit))
}
fun onScanFailed(errorCode: Int) {
val callback = startDiscoveryCallback ?: return
startDiscoveryCallback = null
val error = IllegalStateException("Start discovery failed with error code: $errorCode")
callback(Result.failure(error))
}
fun onScanResult(result: ScanResult) {
val device = result.device
val peripheralArgs = device.toPeripheralArgs()
val hashCode = device.hashCode()
val hashCodeArgs = peripheralArgs.hashCodeArgs
this.devices[hashCodeArgs] = device
this.peripheralsArgs[hashCode] = peripheralArgs
val rssiArgs = result.rssi.toLong()
val advertiseDataArgs = result.advertiseDataArgs
api.onDiscovered(peripheralArgs, rssiArgs, advertiseDataArgs) {}
}
fun onConnectionStateChange(gatt: BluetoothGatt, status: Int, newState: Int) {
val device = gatt.device
val deviceHashCode = device.hashCode()
val peripheralArgs = peripheralsArgs[deviceHashCode] as MyPeripheralArgs
val peripheralHashCodeArgs = peripheralArgs.hashCodeArgs
// Check callbacks
if (newState != BluetoothProfile.STATE_CONNECTED) {
gatt.close()
bluetoothGATTs.remove(peripheralHashCodeArgs)
val error = IllegalStateException("GATT is disconnected with status: $status")
val getMaximumWriteLengthCallback = getMaximumWriteLengthCallbacks.remove(peripheralHashCodeArgs)
if (getMaximumWriteLengthCallback != null) {
getMaximumWriteLengthCallback(Result.failure(error))
}
val readRssiCallback = readRssiCallbacks.remove(peripheralHashCodeArgs)
if (readRssiCallback != null) {
readRssiCallback(Result.failure(error))
}
val discoverGattCallback = discoverGattCallbacks.remove(peripheralHashCodeArgs)
if (discoverGattCallback != null) {
discoverGattCallback(Result.failure(error))
}
val servicesArgs = servicesArgsOfPeripherals[peripheralHashCodeArgs] ?: emptyList()
for (serviceArgs in servicesArgs) {
val characteristicsArgs = serviceArgs.characteristicsArgs.filterNotNull()
for (characteristicArgs in characteristicsArgs) {
val characteristicHashCodeArgs = characteristicArgs.hashCodeArgs
val readCharacteristicCallback = readCharacteristicCallbacks.remove(characteristicHashCodeArgs)
val writeCharacteristicCallback = writeCharacteristicCallbacks.remove(characteristicHashCodeArgs)
if (readCharacteristicCallback != null) {
readCharacteristicCallback(Result.failure(error))
}
if (writeCharacteristicCallback != null) {
writeCharacteristicCallback(Result.failure(error))
}
val descriptorsArgs = characteristicArgs.descriptorsArgs.filterNotNull()
for (descriptorArgs in descriptorsArgs) {
val descriptorHashCodeArgs = descriptorArgs.hashCodeArgs
val readDescriptorCallback = readDescriptorCallbacks.remove(descriptorHashCodeArgs)
val writeDescriptorCallback = writeDescriptorCallbacks.remove(descriptorHashCodeArgs)
if (readDescriptorCallback != null) {
readDescriptorCallback(Result.failure(error))
}
if (writeDescriptorCallback != null) {
writeDescriptorCallback(Result.failure(error))
}
}
}
}
}
// Check state
val connectCallback = connectCallbacks.remove(peripheralHashCodeArgs)
val disconnectCallback = disconnectCallbacks.remove(peripheralHashCodeArgs)
if (connectCallback == null && disconnectCallback == null) {
// State changed.
val stateArgs = newState == BluetoothProfile.STATE_CONNECTED
api.onPeripheralStateChanged(peripheralArgs, stateArgs) {}
} else {
if (connectCallback != null) {
if (status == BluetoothGatt.GATT_SUCCESS) {
// Connect succeed.
connectCallback(Result.success(Unit))
api.onPeripheralStateChanged(peripheralArgs, true) {}
} else {
// Connect failed.
val error = IllegalStateException("Connect failed with status: $status")
connectCallback(Result.failure(error))
}
}
if (disconnectCallback != null) {
if (status == BluetoothGatt.GATT_SUCCESS) {
// Disconnect succeed.
disconnectCallback(Result.success(Unit))
api.onPeripheralStateChanged(peripheralArgs, false) {}
} else {
// Disconnect failed.
val error = IllegalStateException("Connect failed with status: $status")
disconnectCallback(Result.failure(error))
}
}
}
}
fun onMtuChanged(gatt: BluetoothGatt, mtu: Int, status: Int) {
val device = gatt.device
val hashCode = device.hashCode()
val peripheralArgs = peripheralsArgs[hashCode] as MyPeripheralArgs
val peripheralHashCodeArgs = peripheralArgs.hashCodeArgs
val callback = getMaximumWriteLengthCallbacks.remove(peripheralHashCodeArgs) ?: return
if (status == BluetoothGatt.GATT_SUCCESS) {
val maximumWriteLengthArgs = (mtu - 3).toLong()
callback(Result.success(maximumWriteLengthArgs))
} else {
val error = IllegalStateException("Get maximum write length failed with status: $status")
callback(Result.failure(error))
}
}
fun onReadRemoteRssi(gatt: BluetoothGatt, rssi: Int, status: Int) {
val device = gatt.device
val hashCode = device.hashCode()
val peripheralArgs = peripheralsArgs[hashCode] as MyPeripheralArgs
val peripheralHashCodeArgs = peripheralArgs.hashCodeArgs
val callback = readRssiCallbacks.remove(peripheralHashCodeArgs) ?: return
if (status == BluetoothGatt.GATT_SUCCESS) {
val rssiArgs = rssi.toLong()
callback(Result.success(rssiArgs))
} else {
val error = IllegalStateException("Read rssi failed with status: $status")
callback(Result.failure(error))
}
}
fun onServicesDiscovered(gatt: BluetoothGatt, status: Int) {
val device = gatt.device
val deviceHashCode = device.hashCode()
val peripheralArgs = peripheralsArgs[deviceHashCode] as MyPeripheralArgs
val peripheralHashCodeArgs = peripheralArgs.hashCodeArgs
val callback = discoverGattCallbacks.remove(peripheralHashCodeArgs) ?: return
if (status == BluetoothGatt.GATT_SUCCESS) {
val services = gatt.services
val servicesArgs = mutableListOf<MyGattServiceArgs>()
for (service in services) {
val characteristics = service.characteristics
val characteristicsArgs = mutableListOf<MyGattCharacteristicArgs>()
for (characteristic in characteristics) {
val descriptors = characteristic.descriptors
val descriptorsArgs = mutableListOf<MyGattDescriptorArgs>()
for (descriptor in descriptors) {
val descriptorArgs = descriptor.toManufacturerSpecificDataArgs()
val descriptorHashCode = descriptor.hashCode()
val descriptorHashCodeArgs = descriptorArgs.hashCodeArgs
this.descriptors[descriptorHashCodeArgs] = descriptor
this.descriptorsArgs[descriptorHashCode] = descriptorArgs
descriptorsArgs.add(descriptorArgs)
}
val characteristicArgs = characteristic.toManufacturerSpecificDataArgs(descriptorsArgs)
val characteristicHashCode = characteristic.hashCode()
val characteristicHashCodeArgs = characteristicArgs.hashCodeArgs
this.characteristics[characteristicHashCodeArgs] = characteristic
this.characteristicsArgs[characteristicHashCode] = characteristicArgs
characteristicsArgs.add(characteristicArgs)
}
val serviceArgs = service.toManufacturerSpecificDataArgs(characteristicsArgs)
val serviceHashCode = service.hashCode()
val serviceHashCodeArgs = serviceArgs.hashCodeArgs
this.services[serviceHashCodeArgs] = service
this.servicesArgs[serviceHashCode] = serviceArgs
servicesArgs.add(serviceArgs)
}
servicesArgsOfPeripherals[peripheralHashCodeArgs] = servicesArgs
callback(Result.success(servicesArgs))
} else {
val error = IllegalStateException("Discover GATT failed with status: $status")
callback(Result.failure(error))
}
}
fun onCharacteristicRead(characteristic: BluetoothGattCharacteristic, status: Int, value: ByteArray) {
val hashCode = characteristic.hashCode()
val characteristicArgs = characteristicsArgs[hashCode] as MyGattCharacteristicArgs
val characteristicHashCodeArgs = characteristicArgs.hashCodeArgs
val callback = readCharacteristicCallbacks.remove(characteristicHashCodeArgs) ?: return
if (status == BluetoothGatt.GATT_SUCCESS) {
callback(Result.success(value))
} else {
val error = IllegalStateException("Read characteristic failed with status: $status.")
callback(Result.failure(error))
}
}
fun onCharacteristicWrite(characteristic: BluetoothGattCharacteristic, status: Int) {
val hashCode = characteristic.hashCode()
val characteristicArgs = characteristicsArgs[hashCode] as MyGattCharacteristicArgs
val characteristicHashCodeArgs = characteristicArgs.hashCodeArgs
val callback = writeCharacteristicCallbacks.remove(characteristicHashCodeArgs) ?: return
if (status == BluetoothGatt.GATT_SUCCESS) {
callback(Result.success(Unit))
} else {
val error = IllegalStateException("Write characteristic failed with status: $status.")
callback(Result.failure(error))
}
}
fun onCharacteristicChanged(characteristic: BluetoothGattCharacteristic, value: ByteArray) {
val hashCode = characteristic.hashCode()
val characteristicArgs = characteristicsArgs[hashCode] as MyGattCharacteristicArgs
api.onCharacteristicValueChanged(characteristicArgs, value) {}
}
fun onDescriptorRead(descriptor: BluetoothGattDescriptor, status: Int, value: ByteArray) {
val hashCode = descriptor.hashCode()
val descriptorArgs = descriptorsArgs[hashCode] as MyGattDescriptorArgs
val descriptorHashCodeArgs = descriptorArgs.hashCodeArgs
val callback = readDescriptorCallbacks.remove(descriptorHashCodeArgs) ?: return
if (status == BluetoothGatt.GATT_SUCCESS) {
callback(Result.success(value))
} else {
val error = IllegalStateException("Read descriptor failed with status: $status.")
callback(Result.failure(error))
}
}
fun onDescriptorWrite(descriptor: BluetoothGattDescriptor, status: Int) {
val hashCode = descriptor.hashCode()
val descriptorArgs = descriptorsArgs[hashCode] as MyGattDescriptorArgs
val descriptorHashCodeArgs = descriptorArgs.hashCodeArgs
val callback = writeDescriptorCallbacks.remove(descriptorHashCodeArgs) ?: return
if (status == BluetoothGatt.GATT_SUCCESS) {
callback(Result.success(Unit))
} else {
val error = IllegalStateException("Write descriptor failed with status: $status.")
callback(Result.failure(error))
}
}
}

View File

@ -0,0 +1,423 @@
package dev.yanshouwang.bluetooth_low_energy_android
import android.bluetooth.BluetoothAdapter
import android.bluetooth.BluetoothDevice
import android.bluetooth.BluetoothGatt
import android.bluetooth.BluetoothGattCharacteristic
import android.bluetooth.BluetoothGattDescriptor
import android.bluetooth.BluetoothGattService
import android.bluetooth.BluetoothStatusCodes
import android.bluetooth.le.AdvertiseSettings
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.os.Build
import android.util.Log
import io.flutter.plugin.common.BinaryMessenger
class MyPeripheralManager(private val context: Context, binaryMessenger: BinaryMessenger) : MyBluetoothLowEnergyManager(context), MyPeripheralManagerHostApi {
private val server by lazy { manager.openGattServer(context, bluetoothGattServerCallback) }
private val advertiser get() = adapter.bluetoothLeAdvertiser
private val api = MyPeripheralManagerFlutterApi(binaryMessenger)
private val bluetoothGattServerCallback = MyBluetoothGattServerCallback(this, executor)
private val advertiseCallback = MyAdvertiseCallback(this)
private val devices = mutableMapOf<Long, BluetoothDevice>()
private val services = mutableMapOf<Long, BluetoothGattService>()
private val characteristics = mutableMapOf<Long, BluetoothGattCharacteristic>()
private val descriptors = mutableMapOf<Long, BluetoothGattDescriptor>()
private val mtus = mutableMapOf<Long, Int>()
private val confirms = mutableMapOf<Long, Boolean>()
private val centralsArgs = mutableMapOf<Int, MyCentralArgs>()
private val servicesArgs = mutableMapOf<Int, MyGattServiceArgs>()
private val characteristicsArgs = mutableMapOf<Int, MyGattCharacteristicArgs>()
private val descriptorsArgs = mutableMapOf<Int, MyGattDescriptorArgs>()
private var registered = false
private var advertising = false
private var setUpCallback: ((Result<MyPeripheralManagerArgs>) -> Unit)? = null
private var addServiceCallback: ((Result<Unit>) -> Unit)? = null
private var startAdvertisingCallback: ((Result<Unit>) -> Unit)? = null
private val notifyCharacteristicValueChangedCallbacks = mutableMapOf<Long, (Result<Unit>) -> Unit>()
override fun setUp(callback: (Result<MyPeripheralManagerArgs>) -> Unit) {
try {
val unfinishedCallback = setUpCallback
if (unfinishedCallback != null) {
throw IllegalStateException()
}
tearDown()
if (unsupported) {
val stateNumberArgs = MyBluetoothLowEnergyStateArgs.UNSUPPORTED.raw.toLong()
val args = MyPeripheralManagerArgs(stateNumberArgs)
callback(Result.success(args))
}
val permissions = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
arrayOf(android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION, android.Manifest.permission.BLUETOOTH_ADVERTISE, android.Manifest.permission.BLUETOOTH_CONNECT)
} else {
arrayOf(android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION)
}
authorize(permissions)
setUpCallback = callback
} catch (e: Throwable) {
callback(Result.failure(e))
}
}
private fun tearDown() {
if (registered) {
unregister()
}
if (advertising) {
stopAdvertising()
}
devices.clear()
services.clear()
characteristics.clear()
descriptors.clear()
mtus.clear()
confirms.clear()
centralsArgs.clear()
servicesArgs.clear()
characteristicsArgs.clear()
descriptorsArgs.clear()
setUpCallback = null
addServiceCallback = null
startAdvertisingCallback = null
notifyCharacteristicValueChangedCallbacks.clear()
}
override fun register() {
super.register()
registered = true
}
override fun unregister() {
super.unregister()
registered = false
}
override fun addService(serviceArgs: MyGattServiceArgs, callback: (Result<Unit>) -> Unit) {
try {
val unfinishedCallback = addServiceCallback
if (unfinishedCallback != null) {
throw IllegalStateException()
}
val service = serviceArgs.toService()
val characteristicsArgs = serviceArgs.characteristicsArgs.filterNotNull()
for (characteristicArgs in characteristicsArgs) {
val characteristic = characteristicArgs.toCharacteristic()
val cccDescriptor = BluetoothGattDescriptor(CLIENT_CHARACTERISTIC_CONFIG_UUID, BluetoothGattDescriptor.PERMISSION_READ or BluetoothGattDescriptor.PERMISSION_WRITE)
val cccDescriptorAdded = characteristic.addDescriptor(cccDescriptor)
if (!cccDescriptorAdded) {
throw IllegalStateException()
}
val descriptorsArgs = characteristicArgs.descriptorsArgs.filterNotNull()
for (descriptorArgs in descriptorsArgs) {
val descriptor = descriptorArgs.toDescriptor()
if (descriptor.uuid == CLIENT_CHARACTERISTIC_CONFIG_UUID) {
// Already added.
continue
}
val descriptorAdded = characteristic.addDescriptor(descriptor)
if (!descriptorAdded) {
throw IllegalStateException()
}
val descriptorHashCodeArgs = descriptorArgs.hashCodeArgs
val descriptorHashCode = descriptor.hashCode()
this.descriptorsArgs[descriptorHashCode] = descriptorArgs
this.descriptors[descriptorHashCodeArgs] = descriptor
}
val characteristicAdded = service.addCharacteristic(characteristic)
if (!characteristicAdded) {
throw IllegalStateException()
}
val characteristicHashCodeArgs = characteristicArgs.hashCodeArgs
val characteristicHashCode = characteristic.hashCode()
this.characteristicsArgs[characteristicHashCode] = characteristicArgs
this.characteristics[characteristicHashCodeArgs] = characteristic
}
val adding = server.addService(service)
if (!adding) {
throw IllegalStateException()
}
val serviceHashCodeArgs = serviceArgs.hashCodeArgs
val serviceHashCode = service.hashCode()
this.servicesArgs[serviceHashCode] = serviceArgs
this.services[serviceHashCodeArgs] = service
addServiceCallback = callback
} catch (e: Throwable) {
freeService(serviceArgs)
callback(Result.failure(e))
}
}
override fun removeService(serviceHashCodeArgs: Long) {
val service = services[serviceHashCodeArgs] as BluetoothGattService
val serviceHashCode = service.hashCode()
val serviceArgs = servicesArgs[serviceHashCode] as MyGattServiceArgs
val removed = server.removeService(service)
if (!removed) {
throw IllegalStateException()
}
freeService(serviceArgs)
}
private fun freeService(serviceArgs: MyGattServiceArgs) {
val characteristicsArgs = serviceArgs.characteristicsArgs.filterNotNull()
for (characteristicArgs in characteristicsArgs) {
val descriptorsArgs = characteristicArgs.descriptorsArgs.filterNotNull()
for (descriptorArgs in descriptorsArgs) {
val descriptorHashCodeArgs = descriptorArgs.hashCodeArgs
val descriptor = this.descriptors.remove(descriptorHashCodeArgs) ?: continue
val descriptorHashCode = descriptor.hashCode()
this.descriptorsArgs.remove(descriptorHashCode)
}
val characteristicHashCodeArgs = characteristicArgs.hashCodeArgs
val characteristic = this.characteristics.remove(characteristicHashCodeArgs) ?: continue
this.confirms.remove(characteristicHashCodeArgs)
val characteristicHashCode = characteristic.hashCode()
this.characteristicsArgs.remove(characteristicHashCode)
}
val serviceHashCodeArgs = serviceArgs.hashCodeArgs
val service = services.remove(serviceHashCodeArgs) ?: return
val serviceHashCode = service.hashCode()
servicesArgs.remove(serviceHashCode)
}
override fun clearServices() {
server.clearServices()
val servicesArgs = this.servicesArgs.values
for (serviceArgs in servicesArgs) {
freeService(serviceArgs)
}
}
override fun startAdvertising(advertiseDataArgs: MyAdvertiseDataArgs, callback: (Result<Unit>) -> Unit) {
try {
if (startAdvertisingCallback != null) {
throw IllegalStateException()
}
val settings = AdvertiseSettings.Builder().setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_BALANCED).setConnectable(true).build()
val advertiseData = advertiseDataArgs.toAdvertiseData(adapter)
advertiser.startAdvertising(settings, advertiseData, advertiseCallback)
startAdvertisingCallback = callback
} catch (e: Throwable) {
callback(Result.failure(e))
}
}
override fun stopAdvertising() {
advertiser.stopAdvertising(advertiseCallback)
advertising = false
}
override fun getMaximumWriteLength(centralHashCodeArgs: Long): Long {
val mtu = mtus[centralHashCodeArgs] ?: 23
return (mtu - 3).toLong()
}
override fun sendReadCharacteristicReply(centralHashCodeArgs: Long, characteristicHashCodeArgs: Long, idArgs: Long, offsetArgs: Long, statusArgs: Boolean, valueArgs: ByteArray) {
val device = devices[centralHashCodeArgs] as BluetoothDevice
val requestId = idArgs.toInt()
val status = if (statusArgs) BluetoothGatt.GATT_SUCCESS
else BluetoothGatt.GATT_FAILURE
val offset = offsetArgs.toInt()
val sent = server.sendResponse(device, requestId, status, offset, valueArgs)
if (!sent) {
throw IllegalStateException("Send read characteristic reply failed.")
}
}
override fun sendWriteCharacteristicReply(centralHashCodeArgs: Long, characteristicHashCodeArgs: Long, idArgs: Long, offsetArgs: Long, statusArgs: Boolean) {
val device = devices[centralHashCodeArgs] as BluetoothDevice
val requestId = idArgs.toInt()
val status = if (statusArgs) BluetoothGatt.GATT_SUCCESS
else BluetoothGatt.GATT_FAILURE
val offset = offsetArgs.toInt()
val value = null
val sent = server.sendResponse(device, requestId, status, offset, value)
if (!sent) {
throw IllegalStateException("Send write characteristic reply failed.")
}
}
override fun notifyCharacteristicValueChanged(centralHashCodeArgs: Long, characteristicHashCodeArgs: Long, valueArgs: ByteArray, callback: (Result<Unit>) -> Unit) {
try {
val unfinishedCallback = notifyCharacteristicValueChangedCallbacks[centralHashCodeArgs]
if (unfinishedCallback != null) {
throw IllegalStateException()
}
val device = devices[centralHashCodeArgs] as BluetoothDevice
val characteristic = characteristics[characteristicHashCodeArgs] as BluetoothGattCharacteristic
val confirm = confirms[characteristicHashCodeArgs]
?: throw IllegalStateException("The characteristic is not subscribed.")
val notifying = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
val statusCode = server.notifyCharacteristicChanged(device, characteristic, confirm, valueArgs)
statusCode == BluetoothStatusCodes.SUCCESS
} else {
characteristic.value = valueArgs
server.notifyCharacteristicChanged(device, characteristic, confirm)
}
if (!notifying) {
throw IllegalStateException()
}
notifyCharacteristicValueChangedCallbacks[centralHashCodeArgs] = callback
} catch (e: Throwable) {
callback(Result.failure(e))
}
}
override fun onRequestPermissionsResult(requestCode: Int, results: IntArray): Boolean {
if (requestCode != REQUEST_CODE) {
return false
}
val authorized = results.all { r -> r == PackageManager.PERMISSION_GRANTED }
val callback = setUpCallback ?: return false
setUpCallback = null
val stateArgs = if (authorized) adapter.stateArgs
else MyBluetoothLowEnergyStateArgs.UNAUTHORIZED
val stateNumberArgs = stateArgs.raw.toLong()
val args = MyPeripheralManagerArgs(stateNumberArgs)
callback(Result.success(args))
if (authorized) {
register()
}
return true
}
override fun onReceive(intent: Intent) {
val action = intent.action
if (action != BluetoothAdapter.ACTION_STATE_CHANGED) {
return
}
val state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.STATE_OFF)
val stateArgs = state.toBluetoothLowEnergyStateArgs()
val stateNumberArgs = stateArgs.raw.toLong()
api.onStateChanged(stateNumberArgs) {}
}
fun onServiceAdded(status: Int, service: BluetoothGattService) {
val callback = addServiceCallback ?: return
addServiceCallback = null
if (status == BluetoothGatt.GATT_SUCCESS) {
callback(Result.success(Unit))
} else {
val hashCode = service.hashCode()
val serviceArgs = servicesArgs[hashCode] as MyGattServiceArgs
freeService(serviceArgs)
val error = IllegalStateException("Read rssi failed with status: $status")
callback(Result.failure(error))
}
}
fun onStartSuccess(settingsInEffect: AdvertiseSettings) {
advertising = true
val callback = startAdvertisingCallback ?: return
startAdvertisingCallback = null
callback(Result.success(Unit))
}
fun onStartFailure(errorCode: Int) {
val callback = startAdvertisingCallback ?: return
startAdvertisingCallback = null
val error = IllegalStateException("Start advertising failed with error code: $errorCode")
callback(Result.failure(error))
}
fun onMtuChanged(device: BluetoothDevice, mtu: Int) {
val hashCode = device.hashCode()
val centralArgs = centralsArgs.getOrPut(hashCode) { device.toCentralArgs() }
val centralHashCodeArgs = centralArgs.hashCodeArgs
devices[centralHashCodeArgs] = device
mtus[centralHashCodeArgs] = mtu
}
fun onCharacteristicReadRequest(device: BluetoothDevice, requestId: Int, offset: Int, characteristic: BluetoothGattCharacteristic) {
val deviceHashCode = device.hashCode()
val centralArgs = centralsArgs.getOrPut(deviceHashCode) { device.toCentralArgs() }
val centralHashCodeArgs = centralArgs.hashCodeArgs
devices[centralHashCodeArgs] = device
val characteristicHashCode = characteristic.hashCode()
val characteristicArgs = characteristicsArgs[characteristicHashCode] as MyGattCharacteristicArgs
val idArgs = requestId.toLong()
val offsetArgs = offset.toLong()
api.onReadCharacteristicCommandReceived(centralArgs, characteristicArgs, idArgs, offsetArgs) {}
}
fun onCharacteristicWriteRequest(device: BluetoothDevice, requestId: Int, characteristic: BluetoothGattCharacteristic, preparedWrite: Boolean, responseNeeded: Boolean, offset: Int, value: ByteArray) {
val deviceHashCode = device.hashCode()
val centralArgs = centralsArgs.getOrPut(deviceHashCode) { device.toCentralArgs() }
val centralHashCodeArgs = centralArgs.hashCodeArgs
devices[centralHashCodeArgs] = device
val characteristicHashCode = characteristic.hashCode()
val characteristicArgs = characteristicsArgs[characteristicHashCode] as MyGattCharacteristicArgs
val idArgs = requestId.toLong()
val offsetArgs = offset.toLong()
api.onWriteCharacteristicCommandReceived(centralArgs, characteristicArgs, idArgs, offsetArgs, value) {}
}
fun onDescriptorReadRequest(device: BluetoothDevice, requestId: Int, offset: Int, descriptor: BluetoothGattDescriptor) {
val status = BluetoothGatt.GATT_SUCCESS
val descriptorHashCode = descriptor.hashCode()
val descriptorArgs = descriptorsArgs[descriptorHashCode] as MyGattDescriptorArgs
val value = descriptorArgs.valueArgs
val sent = server.sendResponse(device, requestId, status, offset, value)
if (!sent) {
Log.e(TAG, "onDescriptorReadRequest: send response failed.")
}
}
fun onDescriptorWriteRequest(device: BluetoothDevice, requestId: Int, descriptor: BluetoothGattDescriptor, preparedWrite: Boolean, responseNeeded: Boolean, offset: Int, value: ByteArray) {
val status = if (descriptor.uuid == CLIENT_CHARACTERISTIC_CONFIG_UUID) {
val deviceHashCode = device.hashCode()
val centralArgs = centralsArgs.getOrPut(deviceHashCode) { device.toCentralArgs() }
val centralHashCodeArgs = centralArgs.hashCodeArgs
devices[centralHashCodeArgs] = device
val characteristic = descriptor.characteristic
val characteristicHashCode = characteristic.hashCode()
val characteristicArgs = characteristicsArgs[characteristicHashCode] as MyGattCharacteristicArgs
val characteristicHashCodeArgs = characteristicArgs.hashCodeArgs
// TODO: what is 中缀?
if (value contentEquals BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE) {
confirms[characteristicHashCodeArgs] = false
val stateArgs = true
api.onNotifyCharacteristicCommandReceived(centralArgs, characteristicArgs, stateArgs) {}
BluetoothGatt.GATT_SUCCESS
} else if (value contentEquals BluetoothGattDescriptor.ENABLE_INDICATION_VALUE) {
confirms[characteristicHashCodeArgs] = true
val stateArgs = true
api.onNotifyCharacteristicCommandReceived(centralArgs, characteristicArgs, stateArgs) {}
BluetoothGatt.GATT_SUCCESS
} else if (value contentEquals BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE) {
confirms.remove(characteristicHashCodeArgs)
val stateArgs = false
api.onNotifyCharacteristicCommandReceived(centralArgs, characteristicArgs, stateArgs) {}
BluetoothGatt.GATT_SUCCESS
} else {
BluetoothGatt.GATT_REQUEST_NOT_SUPPORTED
}
} else BluetoothGatt.GATT_SUCCESS
val sent = server.sendResponse(device, requestId, status, offset, value)
if (!sent) {
Log.e(TAG, "onDescriptorReadRequest: send response failed.")
}
}
fun onNotificationSent(device: BluetoothDevice, status: Int) {
val deviceHashCode = device.hashCode()
val centralArgs = centralsArgs[deviceHashCode] as MyCentralArgs
val centralHashCodeArgs = centralArgs.hashCodeArgs
val callback = notifyCharacteristicValueChangedCallbacks.remove(centralHashCodeArgs)
?: return
if (status == BluetoothGatt.GATT_SUCCESS) {
callback(Result.success(Unit))
} else {
val error = IllegalStateException("Notify characteristic value changed failed with status: $status")
callback(Result.failure(error))
}
}
}

View File

@ -2,8 +2,8 @@ package dev.yanshouwang.bluetooth_low_energy_android
import io.flutter.plugin.common.PluginRegistry.RequestPermissionsResultListener
class MyRequestPermissionResultListener(private val myCentralController: MyCentralController) : RequestPermissionsResultListener {
class MyRequestPermissionResultListener(private val bluetoothLowEnergyManager: MyBluetoothLowEnergyManager) : RequestPermissionsResultListener {
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, results: IntArray): Boolean {
return myCentralController.onRequestPermissionsResult(requestCode, results)
return bluetoothLowEnergyManager.onRequestPermissionsResult(requestCode, results)
}
}

View File

@ -3,14 +3,14 @@ package dev.yanshouwang.bluetooth_low_energy_android
import android.bluetooth.le.ScanCallback
import android.bluetooth.le.ScanResult
class MyScanCallback(private val myCentralController: MyCentralController) : ScanCallback() {
class MyScanCallback(private val centralManager: MyCentralManager) : ScanCallback() {
override fun onScanFailed(errorCode: Int) {
super.onScanFailed(errorCode)
myCentralController.onScanFailed(errorCode)
centralManager.onScanFailed(errorCode)
}
override fun onScanResult(callbackType: Int, result: ScanResult) {
super.onScanResult(callbackType, result)
myCentralController.onScanResult(result)
centralManager.onScanResult(result)
}
}

View File

@ -1,9 +1,9 @@
import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart';
import 'src/my_central_controller.dart';
import 'src/my_bluetooth_low_energy.dart';
abstract class BluetoothLowEnergyAndroid {
static void registerWith() {
CentralController.instance = MyCentralController();
BluetoothLowEnergy.instance = MyBluetoothLowEnergy();
}
}

View File

@ -0,0 +1,224 @@
import 'dart:typed_data';
import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart';
import 'my_api.g.dart';
import 'my_gatt_characteristic2.dart';
import 'my_gatt_descriptor2.dart';
import 'my_gatt_service2.dart';
export 'my_api.g.dart';
extension MyBluetoothLowEnergyStateArgsX on MyBluetoothLowEnergyStateArgs {
BluetoothLowEnergyState toState() {
return BluetoothLowEnergyState.values[index];
}
}
extension MyAdvertiseDataArgsX on MyAdvertiseDataArgs {
AdvertiseData toAdvertiseData() {
final name = nameArgs;
final serviceUUIDs = serviceUUIDsArgs
.cast<String>()
.map((args) => UUID.fromString(args))
.toList();
final serviceData = serviceDataArgs.cast<String, Uint8List>().map(
(uuidArgs, dataArgs) {
final uuid = UUID.fromString(uuidArgs);
final data = dataArgs;
return MapEntry(uuid, data);
},
);
final manufacturerSpecificData =
manufacturerSpecificDataArgs?.toManufacturerSpecificData();
return AdvertiseData(
name: name,
serviceUUIDs: serviceUUIDs,
serviceData: serviceData,
manufacturerSpecificData: manufacturerSpecificData,
);
}
}
extension MyManufacturerSpecificDataArgsX on MyManufacturerSpecificDataArgs {
ManufacturerSpecificData toManufacturerSpecificData() {
final id = idArgs;
final data = dataArgs;
return ManufacturerSpecificData(
id: id,
data: data,
);
}
}
extension MyGattCharacteristicPropertyArgsX
on MyGattCharacteristicPropertyArgs {
GattCharacteristicProperty toProperty() {
return GattCharacteristicProperty.values[index];
}
}
extension GattCharacteristicWriteTypeX on GattCharacteristicWriteType {
MyGattCharacteristicWriteTypeArgs toArgs() {
return MyGattCharacteristicWriteTypeArgs.values[index];
}
}
extension MyPeripheralArgsX on MyPeripheralArgs {
MyPeripheral toPeripheral() {
final hashCode = hashCodeArgs;
final uuid = UUID.fromString(uuidArgs);
return MyPeripheral(
hashCode: hashCode,
uuid: uuid,
);
}
}
extension MyGattServiceArgsX on MyGattServiceArgs {
MyGattService2 toService2() {
final hashCode = hashCodeArgs;
final uuid = UUID.fromString(uuidArgs);
final characteristics = characteristicsArgs
.cast<MyGattCharacteristicArgs>()
.map((args) => args.toCharacteristic2())
.toList();
return MyGattService2(
hashCode: hashCode,
uuid: uuid,
characteristics: characteristics,
);
}
}
extension MyGattCharacteristicArgsX on MyGattCharacteristicArgs {
MyGattCharacteristic2 toCharacteristic2() {
final hashCode = hashCodeArgs;
final uuid = UUID.fromString(uuidArgs);
final properties = propertyNumbersArgs.cast<int>().map(
(args) {
final propertyArgs = MyGattCharacteristicPropertyArgs.values[args];
return propertyArgs.toProperty();
},
).toList();
final descriptors = descriptorsArgs
.cast<MyGattDescriptorArgs>()
.map((args) => args.toDescriptor2())
.toList();
return MyGattCharacteristic2(
hashCode: hashCode,
uuid: uuid,
properties: properties,
descriptors: descriptors,
);
}
}
extension MyGattDescriptorArgsX on MyGattDescriptorArgs {
MyGattDescriptor2 toDescriptor2() {
final hashCode = hashCodeArgs;
final uuid = UUID.fromString(uuidArgs);
return MyGattDescriptor2(
hashCode: hashCode,
uuid: uuid,
);
}
}
extension MyCentralArgsX on MyCentralArgs {
MyCentral toCentral() {
final hashCode = hashCodeArgs;
final uuid = UUID.fromString(uuidArgs);
return MyCentral(
hashCode: hashCode,
uuid: uuid,
);
}
}
extension AdvertiseDataX on AdvertiseData {
MyAdvertiseDataArgs toArgs() {
final nameArgs = name;
final serviceUUIDsArgs =
serviceUUIDs.map((uuid) => uuid.toString()).toList();
final serviceDataArgs = serviceData.map((uuid, data) {
final uuidArgs = uuid.toString();
final dataArgs = data;
return MapEntry(uuidArgs, dataArgs);
});
final manufacturerSpecificDataArgs = manufacturerSpecificData?.toArgs();
return MyAdvertiseDataArgs(
nameArgs: nameArgs,
serviceUUIDsArgs: serviceUUIDsArgs,
serviceDataArgs: serviceDataArgs,
manufacturerSpecificDataArgs: manufacturerSpecificDataArgs,
);
}
}
extension ManufacturerSpecificDataX on ManufacturerSpecificData {
MyManufacturerSpecificDataArgs toArgs() {
final idArgs = id;
final dataArgs = data;
return MyManufacturerSpecificDataArgs(
idArgs: idArgs,
dataArgs: dataArgs,
);
}
}
extension MyGattServiceX on MyGattService {
MyGattServiceArgs toArgs() {
final hashCodeArgs = hashCode;
final uuidArgs = uuid.toString();
final characteristicsArgs = characteristics
.cast<MyGattCharacteristic>()
.map((characteristic) => characteristic.toArgs())
.toList();
return MyGattServiceArgs(
hashCodeArgs: hashCodeArgs,
uuidArgs: uuidArgs,
characteristicsArgs: characteristicsArgs,
);
}
}
extension MyGattCharacteristicX on MyGattCharacteristic {
MyGattCharacteristicArgs toArgs() {
final hashCodeArgs = hashCode;
final uuidArgs = uuid.toString();
final propertyNumbersArgs = properties.map((property) {
final propertyArgs = property.toArgs();
return propertyArgs.index;
}).toList();
final descriptorsArgs = descriptors
.cast<MyGattDescriptor>()
.map((descriptor) => descriptor.toArgs())
.toList();
return MyGattCharacteristicArgs(
hashCodeArgs: hashCodeArgs,
uuidArgs: uuidArgs,
propertyNumbersArgs: propertyNumbersArgs,
descriptorsArgs: descriptorsArgs,
);
}
}
extension MyGattDescriptorX on MyGattDescriptor {
MyGattDescriptorArgs toArgs() {
final hashCodeArgs = hashCode;
final uuidArgs = uuid.toString();
final valueArgs = value;
return MyGattDescriptorArgs(
hashCodeArgs: hashCodeArgs,
uuidArgs: uuidArgs,
valueArgs: valueArgs,
);
}
}
extension GattCharacteristicPropertyX on GattCharacteristicProperty {
MyGattCharacteristicPropertyArgs toArgs() {
return MyGattCharacteristicPropertyArgs.values[index];
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,15 @@
import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart';
import 'my_central_manager.dart';
import 'my_peripheral_manager.dart';
class MyBluetoothLowEnergy extends BluetoothLowEnergy {
@override
final MyCentralManager centralManager;
@override
final MyPeripheralManager peripheralManager;
MyBluetoothLowEnergy()
: centralManager = MyCentralManager(),
peripheralManager = MyPeripheralManager();
}

View File

@ -0,0 +1,39 @@
import 'dart:async';
import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart';
import 'package:flutter/foundation.dart';
abstract class MyBluetoothLowEnergyManager extends BluetoothLowEnergyManager {
MyBluetoothLowEnergyManager()
: _state = BluetoothLowEnergyState.unknown,
_stateChangedController = StreamController.broadcast();
final StreamController<BluetoothLowEnergyStateChangedEventArgs>
_stateChangedController;
BluetoothLowEnergyState _state;
@override
BluetoothLowEnergyState get state => _state;
@protected
set state(BluetoothLowEnergyState value) {
if (_state == value) {
return;
}
_state = value;
final eventArgs = BluetoothLowEnergyStateChangedEventArgs(state);
_stateChangedController.add(eventArgs);
}
@override
Stream<BluetoothLowEnergyStateChangedEventArgs> get stateChanged =>
_stateChangedController.stream;
@protected
Future<void> throwWithoutState(BluetoothLowEnergyState state) async {
if (this.state != state) {
throw BluetoothLowEnergyError(
'$state is expected, but current state is ${this.state}.',
);
}
}
}

View File

@ -1,366 +0,0 @@
import 'dart:async';
import 'dart:typed_data';
import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart';
import 'my_api.g.dart';
import 'my_gatt_characteristic.dart';
import 'my_gatt_descriptor.dart';
import 'my_gatt_service.dart';
import 'my_peripheral.dart';
class MyCentralController extends CentralController
implements MyCentralControllerFlutterApi {
MyCentralController()
: _myApi = MyCentralControllerHostApi(),
_stateChangedController = StreamController.broadcast(),
_discoveredController = StreamController.broadcast(),
_peripheralStateChangedController = StreamController.broadcast(),
_characteristicValueChangedController = StreamController.broadcast(),
_myPeripherals = {},
_myServices = {},
_myCharacteristics = {},
_myDescriptors = {},
_state = CentralState.unknown;
final MyCentralControllerHostApi _myApi;
final StreamController<CentralStateChangedEventArgs> _stateChangedController;
final StreamController<CentralDiscoveredEventArgs> _discoveredController;
final StreamController<PeripheralStateChangedEventArgs>
_peripheralStateChangedController;
final StreamController<GattCharacteristicValueChangedEventArgs>
_characteristicValueChangedController;
final Map<int, MyPeripheral> _myPeripherals;
final Map<int, MyGattService> _myServices;
final Map<int, MyGattCharacteristic> _myCharacteristics;
final Map<int, MyGattDescriptor> _myDescriptors;
CentralState _state;
@override
CentralState get state => _state;
@override
Stream<CentralStateChangedEventArgs> get stateChanged =>
_stateChangedController.stream;
@override
Stream<CentralDiscoveredEventArgs> get discovered =>
_discoveredController.stream;
@override
Stream<PeripheralStateChangedEventArgs> get peripheralStateChanged =>
_peripheralStateChangedController.stream;
@override
Stream<GattCharacteristicValueChangedEventArgs>
get characteristicValueChanged =>
_characteristicValueChangedController.stream;
Future<void> _throwWithState(CentralState state) async {
if (this.state == state) {
throw BluetoothLowEnergyError('$state is unexpected.');
}
}
Future<void> _throwWithoutState(CentralState state) async {
if (this.state != state) {
throw BluetoothLowEnergyError(
'$state is expected, but current state is ${this.state}.',
);
}
}
@override
Future<void> setUp() async {
await _throwWithoutState(CentralState.unknown);
final args = await _myApi.setUp();
final myStateArgs = MyCentralStateArgs.values[args.myStateNumber];
_state = myStateArgs.toState();
MyCentralControllerFlutterApi.setup(this);
}
@override
Future<void> tearDown() async {
await _throwWithState(CentralState.unknown);
await _myApi.tearDown();
MyCentralControllerFlutterApi.setup(null);
_myPeripherals.clear();
_myServices.clear();
_myCharacteristics.clear();
_myDescriptors.clear();
_state = CentralState.unknown;
}
@override
Future<void> startDiscovery() async {
await _throwWithoutState(CentralState.poweredOn);
await _myApi.startDiscovery();
}
@override
Future<void> stopDiscovery() async {
await _throwWithoutState(CentralState.poweredOn);
await _myApi.stopDiscovery();
}
@override
Future<void> connect(Peripheral peripheral) async {
await _throwWithoutState(CentralState.poweredOn);
final myPeripheral = peripheral as MyPeripheral;
await _myApi.connect(myPeripheral.hashCode);
}
@override
Future<void> disconnect(Peripheral peripheral) async {
await _throwWithoutState(CentralState.poweredOn);
final myPeripheral = peripheral as MyPeripheral;
await _myApi.disconnect(myPeripheral.hashCode);
}
@override
Future<int> getMaximumWriteLength(
Peripheral peripheral, {
required GattCharacteristicWriteType type,
}) async {
await _throwWithoutState(CentralState.poweredOn);
final myPeripheral = peripheral as MyPeripheral;
final maximumWriteLength = await _myApi.getMaximumWriteLength(
myPeripheral.hashCode,
);
return maximumWriteLength;
}
@override
Future<void> discoverGATT(Peripheral peripheral) async {
await _throwWithoutState(CentralState.poweredOn);
final myPeripheral = peripheral as MyPeripheral;
await _myApi.discoverGATT(myPeripheral.hashCode);
}
@override
Future<List<GattService>> getServices(Peripheral peripheral) async {
await _throwWithoutState(CentralState.poweredOn);
final myPeripheral = peripheral as MyPeripheral;
final myServiceArgses = await _myApi.getServices(myPeripheral.hashCode);
return myServiceArgses.cast<MyGattServiceArgs>().map(
(myServiceArgs) {
final myService = MyGattService.fromMyArgs(
myPeripheral,
myServiceArgs,
);
_myServices[myService.hashCode] = myService;
return myService;
},
).toList();
}
@override
Future<List<GattCharacteristic>> getCharacteristics(
GattService service,
) async {
await _throwWithoutState(CentralState.poweredOn);
final myService = service as MyGattService;
final myCharactersiticArgses = await _myApi.getCharacteristics(
myService.hashCode,
);
return myCharactersiticArgses.cast<MyGattCharacteristicArgs>().map(
(myCharacteristicArgs) {
final myCharacteristic = MyGattCharacteristic.fromMyArgs(
myService,
myCharacteristicArgs,
);
_myCharacteristics[myCharacteristic.hashCode] = myCharacteristic;
return myCharacteristic;
},
).toList();
}
@override
Future<List<GattDescriptor>> getDescriptors(
GattCharacteristic characteristic,
) async {
await _throwWithoutState(CentralState.poweredOn);
final myCharacteristic = characteristic as MyGattCharacteristic;
final myDescriptorArgses = await _myApi.getDescriptors(
myCharacteristic.hashCode,
);
return myDescriptorArgses.cast<MyGattDescriptorArgs>().map(
(myDescriptorArgs) {
final myDescriptor = MyGattDescriptor.fromMyArgs(
myCharacteristic,
myDescriptorArgs,
);
_myDescriptors[myDescriptor.hashCode] = myDescriptor;
return myDescriptor;
},
).toList();
}
@override
Future<Uint8List> readCharacteristic(
GattCharacteristic characteristic) async {
await _throwWithoutState(CentralState.poweredOn);
final myCharacteristic = characteristic as MyGattCharacteristic;
final myService = myCharacteristic.myService;
final myPeripheral = myService.myPeripheral;
final value = await _myApi.readCharacteristic(
myPeripheral.hashCode,
myService.hashCode,
myCharacteristic.hashCode,
);
return value;
}
@override
Future<void> writeCharacteristic(
GattCharacteristic characteristic, {
required Uint8List value,
required GattCharacteristicWriteType type,
}) async {
await _throwWithoutState(CentralState.poweredOn);
final myCharacteristic = characteristic as MyGattCharacteristic;
final myService = myCharacteristic.myService;
final myPeripheral = myService.myPeripheral;
final myTypeArgs = type.toMyArgs();
final myTypeNumber = myTypeArgs.index;
await _myApi.writeCharacteristic(
myPeripheral.hashCode,
myService.hashCode,
myCharacteristic.hashCode,
value,
myTypeNumber,
);
}
@override
Future<void> notifyCharacteristic(
GattCharacteristic characteristic, {
required bool state,
}) async {
await _throwWithoutState(CentralState.poweredOn);
final myCharacteristic = characteristic as MyGattCharacteristic;
final myService = myCharacteristic.myService;
final myPeripheral = myService.myPeripheral;
await _myApi.notifyCharacteristic(
myPeripheral.hashCode,
myService.hashCode,
myCharacteristic.hashCode,
state,
);
}
@override
Future<Uint8List> readDescriptor(GattDescriptor descriptor) async {
await _throwWithoutState(CentralState.poweredOn);
final myDescriptor = descriptor as MyGattDescriptor;
final myCharacteristic = myDescriptor.myCharacteristic;
final myService = myCharacteristic.myService;
final myPeripheral = myService.myPeripheral;
final value = await _myApi.readDescriptor(
myPeripheral.hashCode,
myCharacteristic.hashCode,
myDescriptor.hashCode,
);
return value;
}
@override
Future<void> writeDescriptor(
GattDescriptor descriptor, {
required Uint8List value,
}) async {
await _throwWithoutState(CentralState.poweredOn);
final myDescriptor = descriptor as MyGattDescriptor;
final myCharacteristic = myDescriptor.myCharacteristic;
final myService = myCharacteristic.myService;
final myPeripheral = myService.myPeripheral;
await _myApi.writeDescriptor(
myPeripheral.hashCode,
myCharacteristic.hashCode,
myDescriptor.hashCode,
value,
);
}
@override
void onStateChanged(int myStateNumber) {
final myStateArgs = MyCentralStateArgs.values[myStateNumber];
final state = myStateArgs.toState();
if (_state == state) {
return;
}
_state = state;
final eventArgs = CentralStateChangedEventArgs(state);
_stateChangedController.add(eventArgs);
}
@override
void onDiscovered(
MyPeripheralArgs myPeripheralArgs,
int rssi,
MyAdvertisementArgs myAdvertisementArgs,
) {
final myPeripheral = MyPeripheral.fromMyArgs(myPeripheralArgs);
_myPeripherals[myPeripheral.hashCode] = myPeripheral;
final advertisement = myAdvertisementArgs.toAdvertisement();
final eventArgs = CentralDiscoveredEventArgs(
myPeripheral,
rssi,
advertisement,
);
_discoveredController.add(eventArgs);
}
@override
void onPeripheralStateChanged(int myPeripheralKey, bool state) {
final myPeripheral = _myPeripherals[myPeripheralKey];
if (myPeripheral == null) {
return;
}
final eventArgs = PeripheralStateChangedEventArgs(myPeripheral, state);
_peripheralStateChangedController.add(eventArgs);
}
@override
void onCharacteristicValueChanged(int myCharacteristicKey, Uint8List value) {
final myCharacteristic =
_myCharacteristics[myCharacteristicKey] as MyGattCharacteristic;
final eventArgs = GattCharacteristicValueChangedEventArgs(
myCharacteristic,
value,
);
_characteristicValueChangedController.add(eventArgs);
}
}
extension on MyAdvertisementArgs {
Advertisement toAdvertisement() {
final serviceUUIDs = this
.serviceUUIDs
.cast<String>()
.map((uuid) => UUID.fromString(uuid))
.toList();
final serviceData = this.serviceData.cast<String, Uint8List>().map(
(uuid, data) {
final key = UUID.fromString(uuid);
final value = data;
return MapEntry(key, value);
},
);
return Advertisement(
name: name,
manufacturerSpecificData: manufacturerSpecificData.cast<int, Uint8List>(),
serviceUUIDs: serviceUUIDs,
serviceData: serviceData,
);
}
}
extension on MyCentralStateArgs {
CentralState toState() {
return CentralState.values[index];
}
}
extension on GattCharacteristicWriteType {
MyGattCharacteristicWriteTypeArgs toMyArgs() {
return MyGattCharacteristicWriteTypeArgs.values[index];
}
}

View File

@ -0,0 +1,267 @@
import 'dart:async';
import 'dart:typed_data';
import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart';
import 'my_api.dart';
import 'my_bluetooth_low_energy_manager.dart';
import 'my_gatt_characteristic2.dart';
import 'my_gatt_descriptor2.dart';
class MyCentralManager extends MyBluetoothLowEnergyManager
implements CentralManager, MyCentralManagerFlutterApi {
final MyCentralManagerHostApi _api;
final StreamController<DiscoveredEventArgs> _discoveredController;
final StreamController<PeripheralStateChangedEventArgs>
_peripheralStateChangedController;
final StreamController<GattCharacteristicValueChangedEventArgs>
_characteristicValueChangedController;
MyCentralManager()
: _api = MyCentralManagerHostApi(),
_discoveredController = StreamController.broadcast(),
_peripheralStateChangedController = StreamController.broadcast(),
_characteristicValueChangedController = StreamController.broadcast();
@override
Stream<DiscoveredEventArgs> get discovered => _discoveredController.stream;
@override
Stream<PeripheralStateChangedEventArgs> get peripheralStateChanged =>
_peripheralStateChangedController.stream;
@override
Stream<GattCharacteristicValueChangedEventArgs>
get characteristicValueChanged =>
_characteristicValueChangedController.stream;
@override
Future<void> setUp() async {
final args = await _api.setUp();
final stateArgs =
MyBluetoothLowEnergyStateArgs.values[args.stateNumberArgs];
state = stateArgs.toState();
MyCentralManagerFlutterApi.setup(this);
}
@override
Future<void> startDiscovery() async {
await throwWithoutState(BluetoothLowEnergyState.poweredOn);
await _api.startDiscovery();
}
@override
Future<void> stopDiscovery() async {
await throwWithoutState(BluetoothLowEnergyState.poweredOn);
await _api.stopDiscovery();
}
@override
Future<void> connect(Peripheral peripheral) async {
await throwWithoutState(BluetoothLowEnergyState.poweredOn);
final peripheralHashCodeArgs = peripheral.hashCode;
await _api.connect(peripheralHashCodeArgs);
}
@override
Future<void> disconnect(Peripheral peripheral) async {
await throwWithoutState(BluetoothLowEnergyState.poweredOn);
final peripheralHashCodeArgs = peripheral.hashCode;
await _api.disconnect(peripheralHashCodeArgs);
}
@override
Future<int> getMaximumWriteLength(
Peripheral peripheral, {
required GattCharacteristicWriteType type,
}) async {
await throwWithoutState(BluetoothLowEnergyState.poweredOn);
final peripheralHashCodeArgs = peripheral.hashCode;
final maximumWriteLength =
await _api.getMaximumWriteLength(peripheralHashCodeArgs);
return maximumWriteLength;
}
@override
Future<int> readRSSI(Peripheral peripheral) async {
await throwWithoutState(BluetoothLowEnergyState.poweredOn);
final peripheralHashCodeArgs = peripheral.hashCode;
final rssi = await _api.readRSSI(peripheralHashCodeArgs);
return rssi;
}
@override
Future<List<GattService>> discoverGATT(Peripheral peripheral) async {
await throwWithoutState(BluetoothLowEnergyState.poweredOn);
if (peripheral is! MyPeripheral) {
throw TypeError();
}
final peripheralHashCodeArgs = peripheral.hashCode;
final servicesArgs = await _api.discoverGATT(peripheralHashCodeArgs);
final services = servicesArgs
.cast<MyGattServiceArgs>()
.map((args) => args.toService2())
.toList();
for (var service in services) {
for (var charactersitic in service.characteristics) {
for (var descriptor in charactersitic.descriptors) {
descriptor.characteristic = charactersitic;
}
charactersitic.service = service;
}
service.peripheral = peripheral;
}
return services;
}
@override
Future<Uint8List> readCharacteristic(
GattCharacteristic characteristic,
) async {
await throwWithoutState(BluetoothLowEnergyState.poweredOn);
if (characteristic is! MyGattCharacteristic2) {
throw TypeError();
}
final service = characteristic.service;
final peripheral = service.peripheral;
final peripheralHashCodeArgs = peripheral.hashCode;
final characteristcHashCodeArgs = characteristic.hashCode;
final value = await _api.readCharacteristic(
peripheralHashCodeArgs,
characteristcHashCodeArgs,
);
return value;
}
@override
Future<void> writeCharacteristic(
GattCharacteristic characteristic, {
required Uint8List value,
required GattCharacteristicWriteType type,
}) async {
await throwWithoutState(BluetoothLowEnergyState.poweredOn);
if (characteristic is! MyGattCharacteristic2) {
throw TypeError();
}
final service = characteristic.service;
final peripheral = service.peripheral;
final peripheralHashCodeArgs = peripheral.hashCode;
final characteristcHashCodeArgs = characteristic.hashCode;
final valueArgs = value;
final typeArgs = type.toArgs();
final typeNumberArgs = typeArgs.index;
await _api.writeCharacteristic(
peripheralHashCodeArgs,
characteristcHashCodeArgs,
valueArgs,
typeNumberArgs,
);
}
@override
Future<void> notifyCharacteristic(
GattCharacteristic characteristic, {
required bool state,
}) async {
await throwWithoutState(BluetoothLowEnergyState.poweredOn);
if (characteristic is! MyGattCharacteristic2) {
throw TypeError();
}
final service = characteristic.service;
final peripheral = service.peripheral;
final peripheralHashCodeArgs = peripheral.hashCode;
final characteristcHashCodeArgs = characteristic.hashCode;
final stateArgs = state;
await _api.notifyCharacteristic(
peripheralHashCodeArgs,
characteristcHashCodeArgs,
stateArgs,
);
}
@override
Future<Uint8List> readDescriptor(GattDescriptor descriptor) async {
await throwWithoutState(BluetoothLowEnergyState.poweredOn);
if (descriptor is! MyGattDescriptor2) {
throw TypeError();
}
final characteristic = descriptor.characteristic;
final service = characteristic.service;
final peripheral = service.peripheral;
final peripheralHashCodeArgs = peripheral.hashCode;
final descriptorHashCodeArgs = descriptor.hashCode;
final value = await _api.readDescriptor(
peripheralHashCodeArgs,
descriptorHashCodeArgs,
);
return value;
}
@override
Future<void> writeDescriptor(
GattDescriptor descriptor, {
required Uint8List value,
}) async {
await throwWithoutState(BluetoothLowEnergyState.poweredOn);
if (descriptor is! MyGattDescriptor2) {
throw TypeError();
}
final characteristic = descriptor.characteristic;
final service = characteristic.service;
final peripheral = service.peripheral;
final peripheralHashCodeArgs = peripheral.hashCode;
final descriptorHashCodeArgs = descriptor.hashCode;
final valueArgs = value;
await _api.writeDescriptor(
peripheralHashCodeArgs,
descriptorHashCodeArgs,
valueArgs,
);
}
@override
void onStateChanged(int stateNumberArgs) {
final stateArgs = MyBluetoothLowEnergyStateArgs.values[stateNumberArgs];
state = stateArgs.toState();
}
@override
void onDiscovered(
MyPeripheralArgs peripheralArgs,
int rssiArgs,
MyAdvertiseDataArgs advertiseDataArgs,
) {
final peripheral = peripheralArgs.toPeripheral();
final rssi = rssiArgs;
final advertiseData = advertiseDataArgs.toAdvertiseData();
final eventArgs = DiscoveredEventArgs(
peripheral,
rssi,
advertiseData,
);
_discoveredController.add(eventArgs);
}
@override
void onPeripheralStateChanged(
MyPeripheralArgs peripheralArgs,
bool stateArgs,
) {
final peripheral = peripheralArgs.toPeripheral();
final state = stateArgs;
final eventArgs = PeripheralStateChangedEventArgs(peripheral, state);
_peripheralStateChangedController.add(eventArgs);
}
@override
void onCharacteristicValueChanged(
MyGattCharacteristicArgs characteristicArgs,
Uint8List valueArgs,
) {
final characteristic = characteristicArgs.toCharacteristic2();
final value = valueArgs;
final eventArgs = GattCharacteristicValueChangedEventArgs(
characteristic,
value,
);
_characteristicValueChangedController.add(eventArgs);
}
}

View File

@ -1,42 +0,0 @@
import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart';
import 'my_api.g.dart';
import 'my_gatt_service.dart';
import 'my_object.dart';
class MyGattCharacteristic extends MyObject implements GattCharacteristic {
final MyGattService myService;
@override
final UUID uuid;
@override
final List<GattCharacteristicProperty> properties;
MyGattCharacteristic(
super.hashCode,
this.myService,
this.uuid,
this.properties,
);
factory MyGattCharacteristic.fromMyArgs(
MyGattService myService,
MyGattCharacteristicArgs myArgs,
) {
final hashCode = myArgs.key;
final uuid = UUID.fromString(myArgs.uuid);
final properties = myArgs.myPropertyNumbers.cast<int>().map(
(myPropertyNumber) {
final myPropertyArgs =
MyGattCharacteristicPropertyArgs.values[myPropertyNumber];
return myPropertyArgs.toProperty();
},
).toList();
return MyGattCharacteristic(hashCode, myService, uuid, properties);
}
}
extension on MyGattCharacteristicPropertyArgs {
GattCharacteristicProperty toProperty() {
return GattCharacteristicProperty.values[index];
}
}

View File

@ -0,0 +1,19 @@
import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart';
import 'my_gatt_descriptor2.dart';
import 'my_gatt_service2.dart';
class MyGattCharacteristic2 extends MyGattCharacteristic {
late final MyGattService2 service;
MyGattCharacteristic2({
super.hashCode,
required super.uuid,
required super.properties,
required List<MyGattDescriptor2> descriptors,
}) : super(descriptors: descriptors);
@override
List<MyGattDescriptor2> get descriptors =>
super.descriptors.cast<MyGattDescriptor2>();
}

View File

@ -1,22 +0,0 @@
import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart';
import 'my_api.g.dart';
import 'my_gatt_characteristic.dart';
import 'my_object.dart';
class MyGattDescriptor extends MyObject implements GattDescriptor {
final MyGattCharacteristic myCharacteristic;
@override
final UUID uuid;
MyGattDescriptor(super.hashCode, this.myCharacteristic, this.uuid);
factory MyGattDescriptor.fromMyArgs(
MyGattCharacteristic myCharacteristic,
MyGattDescriptorArgs myArgs,
) {
final hashCode = myArgs.key;
final uuid = UUID.fromString(myArgs.uuid);
return MyGattDescriptor(hashCode, myCharacteristic, uuid);
}
}

View File

@ -0,0 +1,12 @@
import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart';
import 'my_gatt_characteristic2.dart';
class MyGattDescriptor2 extends MyGattDescriptor {
late final MyGattCharacteristic2 characteristic;
MyGattDescriptor2({
super.hashCode,
required super.uuid,
});
}

View File

@ -1,22 +0,0 @@
import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart';
import 'my_api.g.dart';
import 'my_object.dart';
import 'my_peripheral.dart';
class MyGattService extends MyObject implements GattService {
final MyPeripheral myPeripheral;
@override
final UUID uuid;
MyGattService(super.hashCode, this.myPeripheral, this.uuid);
factory MyGattService.fromMyArgs(
MyPeripheral myPeripheral,
MyGattServiceArgs myArgs,
) {
final hashCode = myArgs.key;
final uuid = UUID.fromString(myArgs.uuid);
return MyGattService(hashCode, myPeripheral, uuid);
}
}

View File

@ -0,0 +1,17 @@
import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart';
import 'my_gatt_characteristic2.dart';
class MyGattService2 extends MyGattService {
late final MyPeripheral peripheral;
MyGattService2({
super.hashCode,
required super.uuid,
required List<MyGattCharacteristic2> characteristics,
}) : super(characteristics: characteristics);
@override
List<MyGattCharacteristic2> get characteristics =>
super.characteristics.cast<MyGattCharacteristic2>();
}

View File

@ -1,11 +0,0 @@
abstract class MyObject {
@override
final int hashCode;
MyObject(this.hashCode);
@override
bool operator ==(Object other) {
return other is MyObject && other.hashCode == hashCode;
}
}

View File

@ -1,17 +0,0 @@
import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart';
import 'my_api.g.dart';
import 'my_object.dart';
class MyPeripheral extends MyObject implements Peripheral {
@override
final UUID uuid;
MyPeripheral(super.hashCode, this.uuid);
factory MyPeripheral.fromMyArgs(MyPeripheralArgs myArgs) {
final hashCode = myArgs.key;
final uuid = UUID.fromString(myArgs.uuid);
return MyPeripheral(hashCode, uuid);
}
}

View File

@ -0,0 +1,228 @@
import 'dart:async';
import 'dart:typed_data';
import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart';
import 'my_api.dart';
import 'my_bluetooth_low_energy_manager.dart';
class MyPeripheralManager extends MyBluetoothLowEnergyManager
implements PeripheralManager, MyPeripheralManagerFlutterApi {
final MyPeripheralManagerHostApi _api;
final StreamController<ReadGattCharacteristicCommandEventArgs>
_readCharacteristicCommandReceivedController;
final StreamController<WriteGattCharacteristicCommandEventArgs>
_writeCharacteristicCommandReceivedController;
final StreamController<NotifyGattCharacteristicCommandEventArgs>
_notifyCharacteristicCommandReceivedController;
MyPeripheralManager()
: _api = MyPeripheralManagerHostApi(),
_readCharacteristicCommandReceivedController =
StreamController.broadcast(),
_writeCharacteristicCommandReceivedController =
StreamController.broadcast(),
_notifyCharacteristicCommandReceivedController =
StreamController.broadcast();
@override
Stream<ReadGattCharacteristicCommandEventArgs>
get readCharacteristicCommandReceived =>
_readCharacteristicCommandReceivedController.stream;
@override
Stream<WriteGattCharacteristicCommandEventArgs>
get writeCharacteristicCommandReceived =>
_writeCharacteristicCommandReceivedController.stream;
@override
Stream<NotifyGattCharacteristicCommandEventArgs>
get notifyCharacteristicCommandReceived =>
_notifyCharacteristicCommandReceivedController.stream;
@override
Future<void> setUp() async {
final args = await _api.setUp();
final stateArgs =
MyBluetoothLowEnergyStateArgs.values[args.stateNumberArgs];
state = stateArgs.toState();
MyPeripheralManagerFlutterApi.setup(this);
}
@override
Future<void> addService(GattService service) async {
await throwWithoutState(BluetoothLowEnergyState.poweredOn);
if (service is! MyGattService) {
throw TypeError();
}
final serviceArgs = service.toArgs();
await _api.addService(serviceArgs);
}
@override
Future<void> removeService(GattService service) async {
await throwWithoutState(BluetoothLowEnergyState.poweredOn);
final serviceHashCodeArgs = service.hashCode;
await _api.removeService(serviceHashCodeArgs);
}
@override
Future<void> clearServices() async {
await throwWithoutState(BluetoothLowEnergyState.poweredOn);
await _api.clearServices();
}
@override
Future<void> startAdvertising(AdvertiseData advertiseData) async {
await throwWithoutState(BluetoothLowEnergyState.poweredOn);
final advertiseDataArgs = advertiseData.toArgs();
await _api.startAdvertising(advertiseDataArgs);
}
@override
Future<void> stopAdvertising() async {
await throwWithoutState(BluetoothLowEnergyState.poweredOn);
await _api.stopAdvertising();
}
@override
Future<int> getMaximumWriteLength(Central central) async {
await throwWithoutState(BluetoothLowEnergyState.poweredOn);
final centralHashCodeArgs = central.hashCode;
final maximumWriteLength =
await _api.getMaximumWriteLength(centralHashCodeArgs);
return maximumWriteLength;
}
@override
Future<void> sendReadCharacteristicReply(
Central central,
GattCharacteristic characteristic,
int id,
int offset,
bool status,
Uint8List value,
) async {
await throwWithoutState(BluetoothLowEnergyState.poweredOn);
final centralHashCodeArgs = central.hashCode;
final characteristicHashCodeArgs = characteristic.hashCode;
final idArgs = id;
final offsetArgs = offset;
final statusArgs = status;
final valueArgs = value;
await _api.sendReadCharacteristicReply(
centralHashCodeArgs,
characteristicHashCodeArgs,
idArgs,
offsetArgs,
statusArgs,
valueArgs,
);
}
@override
Future<void> sendWriteCharacteristicReply(
Central central,
GattCharacteristic characteristic,
int id,
int offset,
bool status,
) async {
await throwWithoutState(BluetoothLowEnergyState.poweredOn);
final centralHashCodeArgs = central.hashCode;
final characteristicHashCodeArgs = characteristic.hashCode;
final idArgs = id;
final offsetArgs = offset;
final statusArgs = status;
await _api.sendWriteCharacteristicReply(
centralHashCodeArgs,
characteristicHashCodeArgs,
idArgs,
offsetArgs,
statusArgs,
);
}
@override
Future<void> notifyCharacteristicValueChanged(
Central central,
GattCharacteristic characteristic,
Uint8List value,
) async {
await throwWithoutState(BluetoothLowEnergyState.poweredOn);
final centralHashCodeArgs = central.hashCode;
final characteristicHashCodeArgs = characteristic.hashCode;
final valueArgs = value;
await _api.notifyCharacteristicValueChanged(
centralHashCodeArgs,
characteristicHashCodeArgs,
valueArgs,
);
}
@override
void onStateChanged(int stateNumberArgs) {
final stateArgs = MyBluetoothLowEnergyStateArgs.values[stateNumberArgs];
state = stateArgs.toState();
}
@override
void onReadCharacteristicCommandReceived(
MyCentralArgs centralArgs,
MyGattCharacteristicArgs characteristicArgs,
int idArgs,
int offsetArgs,
) {
final central = centralArgs.toCentral();
final characteristic = characteristicArgs.toCharacteristic2();
final id = idArgs;
final offset = offsetArgs;
final eventArgs = ReadGattCharacteristicCommandEventArgs(
central,
characteristic,
id,
offset,
);
_readCharacteristicCommandReceivedController.add(eventArgs);
}
@override
void onWriteCharacteristicCommandReceived(
MyCentralArgs centralArgs,
MyGattCharacteristicArgs characteristicArgs,
int idArgs,
int offsetArgs,
Uint8List valueArgs,
) {
final central = centralArgs.toCentral();
final characteristic = characteristicArgs.toCharacteristic2();
final id = idArgs;
final offset = offsetArgs;
final value = valueArgs;
final eventArgs = WriteGattCharacteristicCommandEventArgs(
central,
characteristic,
id,
offset,
value,
);
_writeCharacteristicCommandReceivedController.add(eventArgs);
}
@override
void onNotifyCharacteristicCommandReceived(
MyCentralArgs centralArgs,
MyGattCharacteristicArgs characteristicArgs,
bool stateArgs,
) {
final central = centralArgs.toCentral();
final characteristic = characteristicArgs.toCharacteristic2();
final state = stateArgs;
final eventArgs = NotifyGattCharacteristicCommandEventArgs(
central,
characteristic,
state,
);
_notifyCharacteristicCommandReceivedController.add(eventArgs);
}
}

View File

@ -12,126 +12,215 @@ import 'package:pigeon/pigeon.dart';
),
)
@HostApi()
abstract class MyCentralControllerHostApi {
abstract class MyCentralManagerHostApi {
@async
MyCentralControllerArgs setUp();
void tearDown();
MyCentralManagerArgs setUp();
@async
void startDiscovery();
void stopDiscovery();
@async
void connect(int myPeripheralKey);
void connect(int peripheralHashCodeArgs);
@async
void disconnect(int myPeripheralKey);
void disconnect(int peripheralHashCodeArgs);
@async
int getMaximumWriteLength(int myPeripheralKey);
int getMaximumWriteLength(int peripheralHashCodeArgs);
@async
void discoverGATT(int myPeripheralKey);
List<MyGattServiceArgs> getServices(int myPeripheralKey);
List<MyGattCharacteristicArgs> getCharacteristics(int myServiceKey);
List<MyGattDescriptorArgs> getDescriptors(int myCharacteristicKey);
int readRSSI(int peripheralHashCodeArgs);
@async
List<MyGattServiceArgs> discoverGATT(int peripheralHashCodeArgs);
@async
Uint8List readCharacteristic(
int myPeripheralKey,
int myServiceKey,
int myCharacteristicKey,
int peripheralHashCodeArgs,
int characteristicHashCodeArgs,
);
@async
void writeCharacteristic(
int myPeripheralKey,
int myServiceKey,
int myCharacteristicKey,
Uint8List value,
int myTypeNumber,
int peripheralHashCodeArgs,
int characteristicHashCodeArgs,
Uint8List valueArgs,
int typeNumberArgs,
);
@async
void notifyCharacteristic(
int myPeripheralKey,
int myServiceKey,
int myCharacteristicKey,
bool state,
int peripheralHashCodeArgs,
int characteristicHashCodeArgs,
bool stateArgs,
);
@async
Uint8List readDescriptor(
int myPeripheralKey,
int myCharacteristicKey,
int myDescriptorKey,
int peripheralHashCodeArgs,
int descriptorHashCodeArgs,
);
@async
void writeDescriptor(
int myPeripheralKey,
int myCharacteristicKey,
int myDescriptorKey,
Uint8List value,
int peripheralHashCodeArgs,
int descriptorHashCodeArgs,
Uint8List valueArgs,
);
}
@FlutterApi()
abstract class MyCentralControllerFlutterApi {
void onStateChanged(int myStateNumber);
abstract class MyCentralManagerFlutterApi {
void onStateChanged(int stateNumberArgs);
void onDiscovered(
MyPeripheralArgs myPeripheralArgs,
int rssi,
MyAdvertisementArgs myAdvertisementArgs,
MyPeripheralArgs peripheralArgs,
int rssiArgs,
MyAdvertiseDataArgs advertiseDataArgs,
);
void onPeripheralStateChanged(
MyPeripheralArgs peripheralArgs,
bool stateArgs,
);
void onCharacteristicValueChanged(
MyGattCharacteristicArgs characteristicArgs,
Uint8List valueArgs,
);
void onPeripheralStateChanged(int myPeripheralKey, bool state);
void onCharacteristicValueChanged(int myCharacteristicKey, Uint8List value);
}
class MyCentralControllerArgs {
final int myStateNumber;
@HostApi()
abstract class MyPeripheralManagerHostApi {
@async
MyPeripheralManagerArgs setUp();
@async
void addService(MyGattServiceArgs serviceArgs);
void removeService(int serviceHashCodeArgs);
void clearServices();
@async
void startAdvertising(MyAdvertiseDataArgs advertiseDataArgs);
void stopAdvertising();
int getMaximumWriteLength(int centralHashCodeArgs);
void sendReadCharacteristicReply(
int centralHashCodeArgs,
int characteristicHashCodeArgs,
int idArgs,
int offsetArgs,
bool statusArgs,
Uint8List valueArgs,
);
void sendWriteCharacteristicReply(
int centralHashCodeArgs,
int characteristicHashCodeArgs,
int idArgs,
int offsetArgs,
bool statusArgs,
);
@async
void notifyCharacteristicValueChanged(
int centralHashCodeArgs,
int characteristicHashCodeArgs,
Uint8List valueArgs,
);
}
MyCentralControllerArgs(this.myStateNumber);
@FlutterApi()
abstract class MyPeripheralManagerFlutterApi {
void onStateChanged(int stateNumberArgs);
void onReadCharacteristicCommandReceived(
MyCentralArgs centralArgs,
MyGattCharacteristicArgs characteristicArgs,
int idArgs,
int offsetArgs,
);
void onWriteCharacteristicCommandReceived(
MyCentralArgs centralArgs,
MyGattCharacteristicArgs characteristicArgs,
int idArgs,
int offsetArgs,
Uint8List valueArgs,
);
void onNotifyCharacteristicCommandReceived(
MyCentralArgs centralArgs,
MyGattCharacteristicArgs characteristicArgs,
bool stateArgs,
);
}
class MyCentralManagerArgs {
final int stateNumberArgs;
MyCentralManagerArgs(this.stateNumberArgs);
}
class MyPeripheralManagerArgs {
final int stateNumberArgs;
MyPeripheralManagerArgs(this.stateNumberArgs);
}
class MyCentralArgs {
final int hashCodeArgs;
final String uuidArgs;
MyCentralArgs(this.hashCodeArgs, this.uuidArgs);
}
class MyPeripheralArgs {
final int key;
final String uuid;
final int hashCodeArgs;
final String uuidArgs;
MyPeripheralArgs(this.key, this.uuid);
MyPeripheralArgs(this.hashCodeArgs, this.uuidArgs);
}
class MyAdvertisementArgs {
final String? name;
final Map<int?, Uint8List?> manufacturerSpecificData;
final List<String?> serviceUUIDs;
final Map<String?, Uint8List?> serviceData;
class MyAdvertiseDataArgs {
final String? nameArgs;
final List<String?> serviceUUIDsArgs;
final Map<String?, Uint8List?> serviceDataArgs;
final MyManufacturerSpecificDataArgs? manufacturerSpecificDataArgs;
MyAdvertisementArgs(
this.name,
this.manufacturerSpecificData,
this.serviceUUIDs,
this.serviceData,
MyAdvertiseDataArgs(
this.nameArgs,
this.serviceUUIDsArgs,
this.serviceDataArgs,
this.manufacturerSpecificDataArgs,
);
}
class MyGattServiceArgs {
final int key;
final String uuid;
class MyManufacturerSpecificDataArgs {
final int idArgs;
final Uint8List dataArgs;
MyGattServiceArgs(this.key, this.uuid);
MyManufacturerSpecificDataArgs(this.idArgs, this.dataArgs);
}
class MyGattServiceArgs {
final int hashCodeArgs;
final String uuidArgs;
final List<MyGattCharacteristicArgs?> characteristicsArgs;
MyGattServiceArgs(
this.hashCodeArgs,
this.uuidArgs,
this.characteristicsArgs,
);
}
class MyGattCharacteristicArgs {
final int key;
final String uuid;
final List<int?> myPropertyNumbers;
final int hashCodeArgs;
final String uuidArgs;
final List<int?> propertyNumbersArgs;
final List<MyGattDescriptorArgs?> descriptorsArgs;
MyGattCharacteristicArgs(
this.key,
this.uuid,
this.myPropertyNumbers,
this.hashCodeArgs,
this.uuidArgs,
this.propertyNumbersArgs,
this.descriptorsArgs,
);
}
class MyGattDescriptorArgs {
final int key;
final String uuid;
final int hashCodeArgs;
final String uuidArgs;
final Uint8List? valueArgs;
MyGattDescriptorArgs(this.key, this.uuid);
MyGattDescriptorArgs(
this.hashCodeArgs,
this.uuidArgs,
this.valueArgs,
);
}
enum MyCentralStateArgs {
enum MyBluetoothLowEnergyStateArgs {
unknown,
unsupported,
unauthorized,

View File

@ -1,6 +1,6 @@
name: bluetooth_low_energy_android
description: Android implementation of the bluetooth_low_energy plugin.
version: 2.2.1
version: 3.0.0
homepage: https://github.com/yanshouwang/bluetooth_low_energy
environment:
@ -10,13 +10,13 @@ environment:
dependencies:
flutter:
sdk: flutter
bluetooth_low_energy_platform_interface: ^2.2.0
bluetooth_low_energy_platform_interface: ^3.0.0
dev_dependencies:
flutter_test:
sdk: flutter
flutter_lints: ^2.0.0
pigeon: ^10.1.6
pigeon: ^11.0.1
flutter:
plugin: