feat: 重构项目 2.0.0 (#6)

* feat: 重构项目

* feat: 添加 bluez_central_manager

* feat: 联合插件

* feat: 拆分项目

* feat: 实现 linux 部分接口

* feat: 重新创建项目

* feat: 定义接口

* feat: 实现接入插件

* feat: 清空接入插件示例代码

* feat: 开发 linux 插件

* feat: 调整接口

* 临时提交

* feat: 实现 Android 接口

* fix: 修复 Android 问题

* fix: 移除多余文件

* feat: 重构项目 (#5)

* fix: 移除多余的状态判断

* fix: 外围设备断开时检查是否存在未完成的操作

* feat: 尝试使用 win32 实现接口

* fix: 修复大小写问题

* feat: 实现 macOS 接口

* feat: 实现 macOS 接口

* fix:支持使用16位短字符串生成UUID

* fix: 修复未清理已完成操作的问题

* fix: 规范命名

* 添加蓝牙使用描述

* fix: 更新 README.md
This commit is contained in:
Mr剑侠客
2023-08-17 17:49:26 +08:00
committed by GitHub
parent 3abe9d5b3d
commit d1726b52fa
371 changed files with 15666 additions and 15993 deletions

View File

@ -0,0 +1,9 @@
*.iml
.gradle
/local.properties
/.idea/workspace.xml
/.idea/libraries
.DS_Store
/build
/captures
.cxx

View File

@ -0,0 +1,64 @@
group 'dev.yanshouwang.bluetooth_low_energy'
version '1.0-SNAPSHOT'
buildscript {
ext.kotlin_version = '1.7.10'
repositories {
google()
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:7.3.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
allprojects {
repositories {
google()
mavenCentral()
}
}
apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
android {
compileSdkVersion 31
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
sourceSets {
main.java.srcDirs += 'src/main/kotlin'
test.java.srcDirs += 'src/test/kotlin'
}
defaultConfig {
minSdkVersion 21
}
dependencies {
testImplementation 'org.jetbrains.kotlin:kotlin-test'
testImplementation 'org.mockito:mockito-core:5.0.0'
}
testOptions {
unitTests.all {
useJUnitPlatform()
testLogging {
events "passed", "skipped", "failed", "standardOut", "standardError"
outputs.upToDateWhen {false}
showStandardStreams = true
}
}
}
}

View File

@ -0,0 +1 @@
rootProject.name = 'bluetooth_low_energy'

View File

@ -0,0 +1,13 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="dev.yanshouwang.bluetooth_low_energy">
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.BLUETOOTH"
android:maxSdkVersion="30" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"
android:maxSdkVersion="30" />
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
</manifest>

View File

@ -0,0 +1,38 @@
package dev.yanshouwang.bluetooth_low_energy
import io.flutter.embedding.engine.plugins.FlutterPlugin
import io.flutter.embedding.engine.plugins.activity.ActivityAware
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding
/** BluetoothLowEnergyAndroid */
class BluetoothLowEnergyAndroid : FlutterPlugin, ActivityAware {
private lateinit var centralController: MyCentralController
override fun onAttachedToEngine(binding: FlutterPlugin.FlutterPluginBinding) {
val context = binding.applicationContext
val binaryMessenger = binding.binaryMessenger
centralController = MyCentralController(context, binaryMessenger)
MyCentralControllerHostApi.setUp(binaryMessenger, centralController)
}
override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) {
val binaryMessenger = binding.binaryMessenger
MyCentralControllerHostApi.setUp(binaryMessenger, null)
}
override fun onAttachedToActivity(binding: ActivityPluginBinding) {
centralController.onAttachedToActivity(binding)
}
override fun onDetachedFromActivityForConfigChanges() {
centralController.onDetachedFromActivity()
}
override fun onReattachedToActivityForConfigChanges(binding: ActivityPluginBinding) {
centralController.onAttachedToActivity(binding)
}
override fun onDetachedFromActivity() {
centralController.onDetachedFromActivity()
}
}

View File

@ -0,0 +1,653 @@
// Autogenerated from Pigeon (v10.1.6), do not edit directly.
// See also: https://pub.dev/packages/pigeon
package dev.yanshouwang.bluetooth_low_energy
import android.util.Log
import io.flutter.plugin.common.BasicMessageChannel
import io.flutter.plugin.common.BinaryMessenger
import io.flutter.plugin.common.MessageCodec
import io.flutter.plugin.common.StandardMessageCodec
import java.io.ByteArrayOutputStream
import java.nio.ByteBuffer
private fun wrapResult(result: Any?): List<Any?> {
return listOf(result)
}
private fun wrapError(exception: Throwable): List<Any?> {
if (exception is FlutterError) {
return listOf(
exception.code,
exception.message,
exception.details
)
} else {
return listOf(
exception.javaClass.simpleName,
exception.toString(),
"Cause: " + exception.cause + ", Stacktrace: " + Log.getStackTraceString(exception)
)
}
}
/**
* Error class for passing custom error details to Flutter via a thrown PlatformException.
* @property code The error code.
* @property message The error message.
* @property details The error details. Must be a datatype supported by the api codec.
*/
class FlutterError (
val code: String,
override val message: String? = null,
val details: Any? = null
) : Throwable()
enum class MyCentralStateArgs(val raw: Int) {
UNKNOWN(0),
UNSUPPORTED(1),
UNAUTHORIZED(2),
POWEREDOFF(3),
POWEREDON(4);
companion object {
fun ofRaw(raw: Int): MyCentralStateArgs? {
return values().firstOrNull { it.raw == raw }
}
}
}
enum class MyGattCharacteristicPropertyArgs(val raw: Int) {
READ(0),
WRITE(1),
WRITEWITHOUTRESPONSE(2),
NOTIFY(3),
INDICATE(4);
companion object {
fun ofRaw(raw: Int): MyGattCharacteristicPropertyArgs? {
return values().firstOrNull { it.raw == raw }
}
}
}
enum class MyGattCharacteristicWriteTypeArgs(val raw: Int) {
WITHRESPONSE(0),
WITHOUTRESPONSE(1);
companion object {
fun ofRaw(raw: Int): MyGattCharacteristicWriteTypeArgs? {
return values().firstOrNull { it.raw == raw }
}
}
}
/** Generated class from Pigeon that represents data sent in messages. */
data class MyCentralControllerArgs (
val myStateNumber: Long
) {
companion object {
@Suppress("UNCHECKED_CAST")
fun fromList(list: List<Any?>): MyCentralControllerArgs {
val myStateNumber = list[0].let { if (it is Int) it.toLong() else it as Long }
return MyCentralControllerArgs(myStateNumber)
}
}
fun toList(): List<Any?> {
return listOf<Any?>(
myStateNumber,
)
}
}
/** Generated class from Pigeon that represents data sent in messages. */
data class MyPeripheralArgs (
val key: Long,
val uuid: String
) {
companion object {
@Suppress("UNCHECKED_CAST")
fun fromList(list: List<Any?>): MyPeripheralArgs {
val key = list[0].let { if (it is Int) it.toLong() else it as Long }
val uuid = list[1] as String
return MyPeripheralArgs(key, uuid)
}
}
fun toList(): List<Any?> {
return listOf<Any?>(
key,
uuid,
)
}
}
/** Generated class from Pigeon that represents data sent in messages. */
data class MyAdvertisementArgs (
val name: String? = null,
val manufacturerSpecificData: Map<Long?, ByteArray?>,
val serviceUUIDs: List<String?>,
val serviceData: Map<String?, ByteArray?>
) {
companion object {
@Suppress("UNCHECKED_CAST")
fun fromList(list: List<Any?>): MyAdvertisementArgs {
val name = list[0] as String?
val manufacturerSpecificData = list[1] as Map<Long?, ByteArray?>
val serviceUUIDs = list[2] as List<String?>
val serviceData = list[3] as Map<String?, ByteArray?>
return MyAdvertisementArgs(name, manufacturerSpecificData, serviceUUIDs, serviceData)
}
}
fun toList(): List<Any?> {
return listOf<Any?>(
name,
manufacturerSpecificData,
serviceUUIDs,
serviceData,
)
}
}
/** Generated class from Pigeon that represents data sent in messages. */
data class MyGattServiceArgs (
val key: Long,
val uuid: String
) {
companion object {
@Suppress("UNCHECKED_CAST")
fun fromList(list: List<Any?>): MyGattServiceArgs {
val key = list[0].let { if (it is Int) it.toLong() else it as Long }
val uuid = list[1] as String
return MyGattServiceArgs(key, uuid)
}
}
fun toList(): List<Any?> {
return listOf<Any?>(
key,
uuid,
)
}
}
/** Generated class from Pigeon that represents data sent in messages. */
data class MyGattCharacteristicArgs (
val key: Long,
val uuid: String,
val myPropertyNumbers: List<Long?>
) {
companion object {
@Suppress("UNCHECKED_CAST")
fun fromList(list: List<Any?>): MyGattCharacteristicArgs {
val key = list[0].let { if (it is Int) it.toLong() else it as Long }
val uuid = list[1] as String
val myPropertyNumbers = list[2] as List<Long?>
return MyGattCharacteristicArgs(key, uuid, myPropertyNumbers)
}
}
fun toList(): List<Any?> {
return listOf<Any?>(
key,
uuid,
myPropertyNumbers,
)
}
}
/** Generated class from Pigeon that represents data sent in messages. */
data class MyGattDescriptorArgs (
val key: Long,
val uuid: String
) {
companion object {
@Suppress("UNCHECKED_CAST")
fun fromList(list: List<Any?>): MyGattDescriptorArgs {
val key = list[0].let { if (it is Int) it.toLong() else it as Long }
val uuid = list[1] as String
return MyGattDescriptorArgs(key, uuid)
}
}
fun toList(): List<Any?> {
return listOf<Any?>(
key,
uuid,
)
}
}
@Suppress("UNCHECKED_CAST")
private object MyCentralControllerHostApiCodec : StandardMessageCodec() {
override fun readValueOfType(type: Byte, buffer: ByteBuffer): Any? {
return when (type) {
128.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
MyCentralControllerArgs.fromList(it)
}
}
129.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
MyGattCharacteristicArgs.fromList(it)
}
}
130.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
MyGattDescriptorArgs.fromList(it)
}
}
131.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
MyGattServiceArgs.fromList(it)
}
}
else -> super.readValueOfType(type, buffer)
}
}
override fun writeValue(stream: ByteArrayOutputStream, value: Any?) {
when (value) {
is MyCentralControllerArgs -> {
stream.write(128)
writeValue(stream, value.toList())
}
is MyGattCharacteristicArgs -> {
stream.write(129)
writeValue(stream, value.toList())
}
is MyGattDescriptorArgs -> {
stream.write(130)
writeValue(stream, value.toList())
}
is MyGattServiceArgs -> {
stream.write(131)
writeValue(stream, value.toList())
}
else -> super.writeValue(stream, value)
}
}
}
/** Generated interface from Pigeon that represents a handler of messages from Flutter. */
interface MyCentralControllerHostApi {
fun setUp(callback: (Result<MyCentralControllerArgs>) -> Unit)
fun tearDown()
fun startDiscovery(callback: (Result<Unit>) -> Unit)
fun stopDiscovery()
fun connect(myPeripheralKey: Long, callback: (Result<Unit>) -> Unit)
fun disconnect(myPeripheralKey: Long, callback: (Result<Unit>) -> Unit)
fun discoverGATT(myPeripheralKey: Long, callback: (Result<Unit>) -> Unit)
fun getServices(myPeripheralKey: Long): List<MyGattServiceArgs>
fun getCharacteristics(myServiceKey: Long): List<MyGattCharacteristicArgs>
fun getDescriptors(myCharacteristicKey: Long): List<MyGattDescriptorArgs>
fun readCharacteristic(myPeripheralKey: Long, myCharacteristicKey: Long, callback: (Result<ByteArray>) -> Unit)
fun writeCharacteristic(myPeripheralKey: Long, myCharacteristicKey: Long, value: ByteArray, myTypeNumber: Long, callback: (Result<Unit>) -> Unit)
fun notifyCharacteristic(myPeripheralKey: Long, myCharacteristicKey: Long, state: Boolean, callback: (Result<Unit>) -> Unit)
fun readDescriptor(myPeripheralKey: Long, myDescriptorKey: Long, callback: (Result<ByteArray>) -> Unit)
fun writeDescriptor(myPeripheralKey: Long, myDescriptorKey: Long, value: ByteArray, callback: (Result<Unit>) -> Unit)
companion object {
/** The codec used by MyCentralControllerHostApi. */
val codec: MessageCodec<Any?> by lazy {
MyCentralControllerHostApiCodec
}
/** Sets up an instance of `MyCentralControllerHostApi` to handle messages through the `binaryMessenger`. */
@Suppress("UNCHECKED_CAST")
fun setUp(binaryMessenger: BinaryMessenger, api: MyCentralControllerHostApi?) {
run {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralControllerHostApi.setUp", codec)
if (api != null) {
channel.setMessageHandler { _, reply ->
api.setUp() { result: Result<MyCentralControllerArgs> ->
val error = result.exceptionOrNull()
if (error != null) {
reply.reply(wrapError(error))
} else {
val data = result.getOrNull()
reply.reply(wrapResult(data))
}
}
}
} else {
channel.setMessageHandler(null)
}
}
run {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralControllerHostApi.tearDown", codec)
if (api != null) {
channel.setMessageHandler { _, reply ->
var wrapped: List<Any?>
try {
api.tearDown()
wrapped = listOf<Any?>(null)
} catch (exception: Throwable) {
wrapped = wrapError(exception)
}
reply.reply(wrapped)
}
} else {
channel.setMessageHandler(null)
}
}
run {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralControllerHostApi.startDiscovery", codec)
if (api != null) {
channel.setMessageHandler { _, reply ->
api.startDiscovery() { result: Result<Unit> ->
val error = result.exceptionOrNull()
if (error != null) {
reply.reply(wrapError(error))
} else {
reply.reply(wrapResult(null))
}
}
}
} else {
channel.setMessageHandler(null)
}
}
run {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralControllerHostApi.stopDiscovery", codec)
if (api != null) {
channel.setMessageHandler { _, reply ->
var wrapped: List<Any?>
try {
api.stopDiscovery()
wrapped = listOf<Any?>(null)
} catch (exception: Throwable) {
wrapped = wrapError(exception)
}
reply.reply(wrapped)
}
} else {
channel.setMessageHandler(null)
}
}
run {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralControllerHostApi.connect", codec)
if (api != null) {
channel.setMessageHandler { message, reply ->
val args = message as List<Any?>
val myPeripheralKeyArg = args[0].let { if (it is Int) it.toLong() else it as Long }
api.connect(myPeripheralKeyArg) { result: Result<Unit> ->
val error = result.exceptionOrNull()
if (error != null) {
reply.reply(wrapError(error))
} else {
reply.reply(wrapResult(null))
}
}
}
} else {
channel.setMessageHandler(null)
}
}
run {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralControllerHostApi.disconnect", codec)
if (api != null) {
channel.setMessageHandler { message, reply ->
val args = message as List<Any?>
val myPeripheralKeyArg = args[0].let { if (it is Int) it.toLong() else it as Long }
api.disconnect(myPeripheralKeyArg) { result: Result<Unit> ->
val error = result.exceptionOrNull()
if (error != null) {
reply.reply(wrapError(error))
} else {
reply.reply(wrapResult(null))
}
}
}
} else {
channel.setMessageHandler(null)
}
}
run {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralControllerHostApi.discoverGATT", codec)
if (api != null) {
channel.setMessageHandler { message, reply ->
val args = message as List<Any?>
val myPeripheralKeyArg = args[0].let { if (it is Int) it.toLong() else it as Long }
api.discoverGATT(myPeripheralKeyArg) { result: Result<Unit> ->
val error = result.exceptionOrNull()
if (error != null) {
reply.reply(wrapError(error))
} else {
reply.reply(wrapResult(null))
}
}
}
} else {
channel.setMessageHandler(null)
}
}
run {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralControllerHostApi.getServices", codec)
if (api != null) {
channel.setMessageHandler { message, reply ->
val args = message as List<Any?>
val myPeripheralKeyArg = args[0].let { if (it is Int) it.toLong() else it as Long }
var wrapped: List<Any?>
try {
wrapped = listOf<Any?>(api.getServices(myPeripheralKeyArg))
} catch (exception: Throwable) {
wrapped = wrapError(exception)
}
reply.reply(wrapped)
}
} else {
channel.setMessageHandler(null)
}
}
run {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralControllerHostApi.getCharacteristics", codec)
if (api != null) {
channel.setMessageHandler { message, reply ->
val args = message as List<Any?>
val myServiceKeyArg = args[0].let { if (it is Int) it.toLong() else it as Long }
var wrapped: List<Any?>
try {
wrapped = listOf<Any?>(api.getCharacteristics(myServiceKeyArg))
} catch (exception: Throwable) {
wrapped = wrapError(exception)
}
reply.reply(wrapped)
}
} else {
channel.setMessageHandler(null)
}
}
run {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralControllerHostApi.getDescriptors", codec)
if (api != null) {
channel.setMessageHandler { message, reply ->
val args = message as List<Any?>
val myCharacteristicKeyArg = args[0].let { if (it is Int) it.toLong() else it as Long }
var wrapped: List<Any?>
try {
wrapped = listOf<Any?>(api.getDescriptors(myCharacteristicKeyArg))
} catch (exception: Throwable) {
wrapped = wrapError(exception)
}
reply.reply(wrapped)
}
} else {
channel.setMessageHandler(null)
}
}
run {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralControllerHostApi.readCharacteristic", codec)
if (api != null) {
channel.setMessageHandler { message, reply ->
val args = message as List<Any?>
val myPeripheralKeyArg = args[0].let { if (it is Int) it.toLong() else it as Long }
val myCharacteristicKeyArg = args[1].let { if (it is Int) it.toLong() else it as Long }
api.readCharacteristic(myPeripheralKeyArg, myCharacteristicKeyArg) { result: Result<ByteArray> ->
val error = result.exceptionOrNull()
if (error != null) {
reply.reply(wrapError(error))
} else {
val data = result.getOrNull()
reply.reply(wrapResult(data))
}
}
}
} else {
channel.setMessageHandler(null)
}
}
run {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralControllerHostApi.writeCharacteristic", codec)
if (api != null) {
channel.setMessageHandler { message, reply ->
val args = message as List<Any?>
val myPeripheralKeyArg = args[0].let { if (it is Int) it.toLong() else it as Long }
val myCharacteristicKeyArg = args[1].let { if (it is Int) it.toLong() else it as Long }
val valueArg = args[2] as ByteArray
val myTypeNumberArg = args[3].let { if (it is Int) it.toLong() else it as Long }
api.writeCharacteristic(myPeripheralKeyArg, myCharacteristicKeyArg, valueArg, myTypeNumberArg) { result: Result<Unit> ->
val error = result.exceptionOrNull()
if (error != null) {
reply.reply(wrapError(error))
} else {
reply.reply(wrapResult(null))
}
}
}
} else {
channel.setMessageHandler(null)
}
}
run {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralControllerHostApi.notifyCharacteristic", codec)
if (api != null) {
channel.setMessageHandler { message, reply ->
val args = message as List<Any?>
val myPeripheralKeyArg = args[0].let { if (it is Int) it.toLong() else it as Long }
val myCharacteristicKeyArg = args[1].let { if (it is Int) it.toLong() else it as Long }
val stateArg = args[2] as Boolean
api.notifyCharacteristic(myPeripheralKeyArg, myCharacteristicKeyArg, stateArg) { result: Result<Unit> ->
val error = result.exceptionOrNull()
if (error != null) {
reply.reply(wrapError(error))
} else {
reply.reply(wrapResult(null))
}
}
}
} else {
channel.setMessageHandler(null)
}
}
run {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralControllerHostApi.readDescriptor", codec)
if (api != null) {
channel.setMessageHandler { message, reply ->
val args = message as List<Any?>
val myPeripheralKeyArg = args[0].let { if (it is Int) it.toLong() else it as Long }
val myDescriptorKeyArg = args[1].let { if (it is Int) it.toLong() else it as Long }
api.readDescriptor(myPeripheralKeyArg, myDescriptorKeyArg) { result: Result<ByteArray> ->
val error = result.exceptionOrNull()
if (error != null) {
reply.reply(wrapError(error))
} else {
val data = result.getOrNull()
reply.reply(wrapResult(data))
}
}
}
} else {
channel.setMessageHandler(null)
}
}
run {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralControllerHostApi.writeDescriptor", codec)
if (api != null) {
channel.setMessageHandler { message, reply ->
val args = message as List<Any?>
val myPeripheralKeyArg = args[0].let { if (it is Int) it.toLong() else it as Long }
val myDescriptorKeyArg = args[1].let { if (it is Int) it.toLong() else it as Long }
val valueArg = args[2] as ByteArray
api.writeDescriptor(myPeripheralKeyArg, myDescriptorKeyArg, valueArg) { result: Result<Unit> ->
val error = result.exceptionOrNull()
if (error != null) {
reply.reply(wrapError(error))
} else {
reply.reply(wrapResult(null))
}
}
}
} else {
channel.setMessageHandler(null)
}
}
}
}
}
@Suppress("UNCHECKED_CAST")
private object MyCentralControllerFlutterApiCodec : StandardMessageCodec() {
override fun readValueOfType(type: Byte, buffer: ByteBuffer): Any? {
return when (type) {
128.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
MyAdvertisementArgs.fromList(it)
}
}
129.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
MyPeripheralArgs.fromList(it)
}
}
else -> super.readValueOfType(type, buffer)
}
}
override fun writeValue(stream: ByteArrayOutputStream, value: Any?) {
when (value) {
is MyAdvertisementArgs -> {
stream.write(128)
writeValue(stream, value.toList())
}
is MyPeripheralArgs -> {
stream.write(129)
writeValue(stream, value.toList())
}
else -> super.writeValue(stream, value)
}
}
}
/** Generated class from Pigeon that represents Flutter messages that can be called from Kotlin. */
@Suppress("UNCHECKED_CAST")
class MyCentralControllerFlutterApi(private val binaryMessenger: BinaryMessenger) {
companion object {
/** The codec used by MyCentralControllerFlutterApi. */
val codec: MessageCodec<Any?> by lazy {
MyCentralControllerFlutterApiCodec
}
}
fun onStateChanged(myStateNumberArg: Long, callback: () -> Unit) {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralControllerFlutterApi.onStateChanged", codec)
channel.send(listOf(myStateNumberArg)) {
callback()
}
}
fun onDiscovered(myPeripheralArgsArg: MyPeripheralArgs, rssiArg: Long, myAdvertisementArgsArg: MyAdvertisementArgs, callback: () -> Unit) {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralControllerFlutterApi.onDiscovered", codec)
channel.send(listOf(myPeripheralArgsArg, rssiArg, myAdvertisementArgsArg)) {
callback()
}
}
fun onPeripheralStateChanged(myPeripheralKeyArg: Long, stateArg: Boolean, callback: () -> Unit) {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralControllerFlutterApi.onPeripheralStateChanged", codec)
channel.send(listOf(myPeripheralKeyArg, stateArg)) {
callback()
}
}
fun onCharacteristicValueChanged(myCharacteristicKeyArg: Long, valueArg: ByteArray, callback: () -> Unit) {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralControllerFlutterApi.onCharacteristicValueChanged", codec)
channel.send(listOf(myCharacteristicKeyArg, valueArg)) {
callback()
}
}
}

View File

@ -0,0 +1,57 @@
package dev.yanshouwang.bluetooth_low_energy
import android.bluetooth.BluetoothGatt
import android.bluetooth.BluetoothGattCallback
import android.bluetooth.BluetoothGattCharacteristic
import android.bluetooth.BluetoothGattDescriptor
import java.util.concurrent.Executor
class MyBluetoothGattCallback(private val myCentralController: MyCentralController, 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)
}
}
override fun onServicesDiscovered(gatt: BluetoothGatt, status: Int) {
super.onServicesDiscovered(gatt, status)
executor.execute {
myCentralController.onServicesDiscovered(gatt, status)
}
}
override fun onCharacteristicRead(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic, status: Int) {
super.onCharacteristicRead(gatt, characteristic, status)
executor.execute {
myCentralController.onCharacteristicRead(characteristic, status)
}
}
override fun onCharacteristicWrite(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic, status: Int) {
super.onCharacteristicWrite(gatt, characteristic, status)
executor.execute {
myCentralController.onCharacteristicWrite(characteristic, status)
}
}
override fun onCharacteristicChanged(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic) {
executor.execute {
myCentralController.onCharacteristicChanged(characteristic)
}
}
override fun onDescriptorRead(gatt: BluetoothGatt, descriptor: BluetoothGattDescriptor, status: Int) {
super.onDescriptorRead(gatt, descriptor, status)
executor.execute {
myCentralController.onDescriptorRead(descriptor, status)
}
}
override fun onDescriptorWrite(gatt: BluetoothGatt, descriptor: BluetoothGattDescriptor, status: Int) {
super.onDescriptorWrite(gatt, descriptor, status)
executor.execute {
myCentralController.onDescriptorWrite(descriptor, status)
}
}
}

View File

@ -0,0 +1,11 @@
package dev.yanshouwang.bluetooth_low_energy
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
class MyBroadcastReceiver(private val myCentralController: MyCentralController) : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
myCentralController.onReceive(intent)
}
}

View File

@ -0,0 +1,685 @@
package dev.yanshouwang.bluetooth_low_energy
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.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 myReceiver = MyBroadcastReceiver(this)
private val myScanCallback = MyScanCallback(this)
private val myGattCallback = MyBluetoothGattCallback(this, executor)
private val devices = mutableMapOf<Int, BluetoothDevice>()
private val gatts = mutableMapOf<Int, BluetoothGatt>()
private val services = mutableMapOf<Int, BluetoothGattService>()
private val characteristics = mutableMapOf<Int, BluetoothGattCharacteristic>()
private val descriptors = mutableMapOf<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 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_FINE_LOCATION, android.Manifest.permission.BLUETOOTH_SCAN, android.Manifest.permission.BLUETOOTH_CONNECT)
} else {
arrayOf(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 gatts.values) {
gatt.disconnect()
}
devices.clear()
gatts.clear()
services.clear()
characteristics.clear()
descriptors.clear()
}
private fun register() {
val filter = IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED)
context.registerReceiver(myReceiver, filter)
registered = true
}
private fun unregister() {
context.unregisterReceiver(myReceiver)
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 = devices[deviceKey] as BluetoothDevice
val autoConnect = false
gatts[deviceKey] = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
val transport = BluetoothDevice.TRANSPORT_LE
device.connectGatt(context, autoConnect, myGattCallback, transport)
} else {
device.connectGatt(context, autoConnect, myGattCallback)
}
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 = gatts[deviceKey] as BluetoothGatt
gatt.disconnect()
disconnectCallbacks[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 = gatts[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 gatt = gatts[deviceKey] as BluetoothGatt
val services = gatt.services
return services.map { service ->
val serviceKey = service.hashCode()
if (this.services[serviceKey] == null) {
this.services[serviceKey] = service
}
return@map service.toMyArgs()
}
}
override fun getCharacteristics(myServiceKey: Long): List<MyGattCharacteristicArgs> {
val serviceKey = myServiceKey.toInt()
val service = services[serviceKey] as BluetoothGattService
val characteristics = service.characteristics
return characteristics.map { characteristic ->
val characteristicKey = characteristic.hashCode()
if (this.characteristics[characteristicKey] == null) {
this.characteristics[characteristicKey] = characteristic
}
return@map characteristic.toMyArgs()
}
}
override fun getDescriptors(myCharacteristicKey: Long): List<MyGattDescriptorArgs> {
val characteristicKey = myCharacteristicKey.toInt()
val characteristic = characteristics[characteristicKey] as BluetoothGattCharacteristic
val descriptors = characteristic.descriptors
return descriptors.map { descriptor ->
val descriptorKey = descriptor.hashCode()
if (this.descriptors[descriptorKey] == null) {
this.descriptors[descriptorKey] = descriptor
}
return@map descriptor.toMyArgs()
}
}
override fun readCharacteristic(myPeripheralKey: Long, myCharacteristicKey: Long, callback: (Result<ByteArray>) -> Unit) {
try {
val deviceKey = myPeripheralKey.toInt()
val characteristicKey = myCharacteristicKey.toInt()
val unfinishedCallback = readCharacteristicCallbacks[characteristicKey]
if (unfinishedCallback != null) {
throw IllegalStateException()
}
val gatt = gatts[deviceKey] as BluetoothGatt
val characteristic = characteristics[characteristicKey] as BluetoothGattCharacteristic
val reading = gatt.readCharacteristic(characteristic)
if (!reading) {
throw IllegalStateException()
}
readCharacteristicCallbacks[characteristicKey] = callback
} catch (e: Throwable) {
callback(Result.failure(e))
}
}
override fun writeCharacteristic(myPeripheralKey: Long, myCharacteristicKey: Long, value: ByteArray, myTypeNumber: Long, callback: (Result<Unit>) -> Unit) {
try {
val deviceKey = myPeripheralKey.toInt()
val characteristicKey = myCharacteristicKey.toInt()
val unfinishedCallback = writeCharacteristicCallbacks[characteristicKey]
if (unfinishedCallback != null) {
throw IllegalStateException()
}
val gatt = gatts[deviceKey] as BluetoothGatt
val characteristic = characteristics[characteristicKey] as BluetoothGattCharacteristic
val myTypeArgs = myTypeNumber.toMyGattCharacteristicTypeArgs()
val writeType = myTypeArgs.toType()
characteristic.value = value
characteristic.writeType = writeType
val writing = gatt.writeCharacteristic(characteristic)
if (!writing) {
throw IllegalStateException()
}
writeCharacteristicCallbacks[characteristicKey] = callback
} catch (e: Throwable) {
callback(Result.failure(e))
}
}
override fun notifyCharacteristic(myPeripheralKey: Long, myCharacteristicKey: Long, state: Boolean, callback: (Result<Unit>) -> Unit) {
try {
val deviceKey = myPeripheralKey.toInt()
val characteristicKey = myCharacteristicKey.toInt()
val gatt = gatts[deviceKey] as BluetoothGatt
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
descriptor.value = value
val writing = 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, myDescriptorKey: Long, callback: (Result<ByteArray>) -> Unit) {
try {
val deviceKey = myPeripheralKey.toInt()
val descriptorKey = myDescriptorKey.toInt()
val unfinishedCallback = readDescriptorCallbacks[descriptorKey]
if (unfinishedCallback != null) {
throw IllegalStateException()
}
val gatt = gatts[deviceKey] as BluetoothGatt
val descriptor = descriptors[descriptorKey] as BluetoothGattDescriptor
val reading = gatt.readDescriptor(descriptor)
if (!reading) {
throw IllegalStateException()
}
readDescriptorCallbacks[descriptorKey] = callback
} catch (e: Throwable) {
callback(Result.failure(e))
}
}
override fun writeDescriptor(myPeripheralKey: Long, myDescriptorKey: Long, value: ByteArray, callback: (Result<Unit>) -> Unit) {
try {
val deviceKey = myPeripheralKey.toInt()
val descriptorKey = myDescriptorKey.toInt()
val unfinishedCallback = writeDescriptorCallbacks[descriptorKey]
if (unfinishedCallback != null) {
throw IllegalStateException()
}
val gatt = gatts[deviceKey] as BluetoothGatt
val descriptor = descriptors[descriptorKey] as BluetoothGattDescriptor
descriptor.value = value
val writing = 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 previousState = intent.getIntExtra(BluetoothAdapter.EXTRA_PREVIOUS_STATE, BluetoothAdapter.STATE_OFF)
val state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.STATE_OFF)
// val myPreviousStateArgs = previousState.toMyCentralStateArgs()
val myStateArgs = state.toMyCentralStateArgs()
// if (myStateArgs == myPreviousStateArgs) {
// return
// }
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()
if (devices[deviceKey] == null) {
devices[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()
gatts.remove(deviceKey)
val error = IllegalStateException("GATT is disconnected with status: $status")
val discoverGattCallback = discoverGattCallbacks.remove(deviceKey)
if (discoverGattCallback != null) {
discoverGattCallback(Result.failure(error))
}
for (service in gatt.services) {
for (characteristic in service.characteristics) {
val characteristicKey = characteristic.hashCode()
val readCharacteristicCallback = readCharacteristicCallbacks.remove(characteristicKey)
val writeCharacteristicCallback = writeCharacteristicCallbacks.remove(characteristicKey)
if (readCharacteristicCallback != null) {
readCharacteristicCallback(Result.failure(error))
}
if (writeCharacteristicCallback != null) {
writeCharacteristicCallback(Result.failure(error))
}
for (descriptor in characteristic.descriptors) {
val descriptorKey = descriptor.hashCode()
val readDescriptorCallback = readDescriptorCallbacks.remove(descriptorKey)
val writeDescriptorCallback = writeDescriptorCallbacks.remove(descriptorKey)
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 onServicesDiscovered(gatt: BluetoothGatt, status: Int) {
val device = gatt.device
val deviceKey = device.hashCode()
val callback = discoverGattCallbacks.remove(deviceKey) ?: return
if (status == BluetoothGatt.GATT_SUCCESS) {
callback(Result.success(Unit))
} else {
val error = IllegalStateException("Discover GATT failed with status: $status")
callback(Result.failure(error))
}
}
fun onCharacteristicRead(characteristic: BluetoothGattCharacteristic, status: Int) {
val characteristicKey = characteristic.hashCode()
val callback = readCharacteristicCallbacks.remove(characteristicKey) ?: return
if (status == BluetoothGatt.GATT_SUCCESS) {
val value = characteristic.value
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) {
val characteristicKey = characteristic.hashCode()
val myCharacteristicKey = characteristicKey.toLong()
val value = characteristic.value
myApi.onCharacteristicValueChanged(myCharacteristicKey, value) {}
}
fun onDescriptorRead(descriptor: BluetoothGattDescriptor, status: Int) {
val descriptorKey = descriptor.hashCode()
val callback = readDescriptorCallbacks.remove(descriptorKey) ?: return
if (status == BluetoothGatt.GATT_SUCCESS) {
val value = descriptor.value
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,9 @@
package dev.yanshouwang.bluetooth_low_energy
import io.flutter.plugin.common.PluginRegistry.RequestPermissionsResultListener
class MyRequestPermissionResultListener(private val myCentralController: MyCentralController) : RequestPermissionsResultListener {
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, results: IntArray): Boolean {
return myCentralController.onRequestPermissionsResult(requestCode, results)
}
}

View File

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

View File

@ -0,0 +1,27 @@
package dev.yanshouwang.bluetooth_low_energy
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
import kotlin.test.Test
import org.mockito.Mockito
/*
* This demonstrates a simple unit test of the Kotlin portion of this plugin's implementation.
*
* Once you have built the plugin's example app, you can run these tests from the command
* line by running `./gradlew testDebugUnitTest` in the `example/android/` directory, or
* you can run them directly from IDEs that support JUnit such as Android Studio.
*/
internal class BluetoothLowEnergyPluginTest {
@Test
fun onMethodCall_getPlatformVersion_returnsExpectedValue() {
val plugin = BluetoothLowEnergyAndroid()
val call = MethodCall("getPlatformVersion", null)
val mockResult: MethodChannel.Result = Mockito.mock(MethodChannel.Result::class.java)
plugin.onMethodCall(call, mockResult)
Mockito.verify(mockResult).success("Android " + android.os.Build.VERSION.RELEASE)
}
}