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:
9
bluetooth_low_energy_android/android/.gitignore
vendored
Normal file
9
bluetooth_low_energy_android/android/.gitignore
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
*.iml
|
||||
.gradle
|
||||
/local.properties
|
||||
/.idea/workspace.xml
|
||||
/.idea/libraries
|
||||
.DS_Store
|
||||
/build
|
||||
/captures
|
||||
.cxx
|
64
bluetooth_low_energy_android/android/build.gradle
Normal file
64
bluetooth_low_energy_android/android/build.gradle
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
1
bluetooth_low_energy_android/android/settings.gradle
Normal file
1
bluetooth_low_energy_android/android/settings.gradle
Normal file
@ -0,0 +1 @@
|
||||
rootProject.name = 'bluetooth_low_energy'
|
@ -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>
|
@ -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()
|
||||
}
|
||||
}
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user