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:
30
bluetooth_low_energy_android/.gitignore
vendored
Normal file
30
bluetooth_low_energy_android/.gitignore
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
# Miscellaneous
|
||||
*.class
|
||||
*.log
|
||||
*.pyc
|
||||
*.swp
|
||||
.DS_Store
|
||||
.atom/
|
||||
.buildlog/
|
||||
.history
|
||||
.svn/
|
||||
migrate_working_dir/
|
||||
|
||||
# IntelliJ related
|
||||
*.iml
|
||||
*.ipr
|
||||
*.iws
|
||||
.idea/
|
||||
|
||||
# The .vscode folder contains launch configuration and tasks you configure in
|
||||
# VS Code which you may wish to be included in version control, so this line
|
||||
# is commented out by default.
|
||||
#.vscode/
|
||||
|
||||
# Flutter/Dart/Pub related
|
||||
# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock.
|
||||
/pubspec.lock
|
||||
**/doc/api/
|
||||
.dart_tool/
|
||||
.packages
|
||||
build/
|
30
bluetooth_low_energy_android/.metadata
Normal file
30
bluetooth_low_energy_android/.metadata
Normal file
@ -0,0 +1,30 @@
|
||||
# This file tracks properties of this Flutter project.
|
||||
# Used by Flutter tool to assess capabilities and perform upgrades etc.
|
||||
#
|
||||
# This file should be version controlled.
|
||||
|
||||
version:
|
||||
revision: 4d9e56e694b656610ab87fcf2efbcd226e0ed8cf
|
||||
channel: stable
|
||||
|
||||
project_type: plugin
|
||||
|
||||
# Tracks metadata for the flutter migrate command
|
||||
migration:
|
||||
platforms:
|
||||
- platform: root
|
||||
create_revision: 4d9e56e694b656610ab87fcf2efbcd226e0ed8cf
|
||||
base_revision: 4d9e56e694b656610ab87fcf2efbcd226e0ed8cf
|
||||
- platform: android
|
||||
create_revision: 4d9e56e694b656610ab87fcf2efbcd226e0ed8cf
|
||||
base_revision: 4d9e56e694b656610ab87fcf2efbcd226e0ed8cf
|
||||
|
||||
# User provided section
|
||||
|
||||
# List of Local paths (relative to this file) that should be
|
||||
# ignored by the migrate tool.
|
||||
#
|
||||
# Files that are not part of the templates will be ignored by default.
|
||||
unmanaged_files:
|
||||
- 'lib/main.dart'
|
||||
- 'ios/Runner.xcodeproj/project.pbxproj'
|
4
bluetooth_low_energy_android/CHANGELOG.md
Normal file
4
bluetooth_low_energy_android/CHANGELOG.md
Normal file
@ -0,0 +1,4 @@
|
||||
## 2.0.0
|
||||
|
||||
- Rewrite the whole project with federated plugins.
|
||||
- Support macOS and Linux.
|
21
bluetooth_low_energy_android/LICENSE
Normal file
21
bluetooth_low_energy_android/LICENSE
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021 yanshouwang
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
15
bluetooth_low_energy_android/README.md
Normal file
15
bluetooth_low_energy_android/README.md
Normal file
@ -0,0 +1,15 @@
|
||||
# bluetooth_low_energy_android
|
||||
|
||||
The Android implementation of [`bluetooth_low_energy`][1].
|
||||
|
||||
## Usage
|
||||
|
||||
This package is [endorsed][2], which means you can simply use `bluetooth_low_energy`
|
||||
normally. This package will be automatically included in your app when you do,
|
||||
so you do not need to add it to your `pubspec.yaml`.
|
||||
|
||||
However, if you `import` this package to use any of its APIs directly, you
|
||||
should add it to your `pubspec.yaml` as usual.
|
||||
|
||||
[1]: https://pub.dev/packages/bluetooth_low_energy
|
||||
[2]: https://flutter.dev/docs/development/packages-and-plugins/developing-packages#endorsed-federated-plugin
|
4
bluetooth_low_energy_android/analysis_options.yaml
Normal file
4
bluetooth_low_energy_android/analysis_options.yaml
Normal file
@ -0,0 +1,4 @@
|
||||
include: package:flutter_lints/flutter.yaml
|
||||
|
||||
# Additional information about this file can be found at
|
||||
# https://dart.dev/guides/language/analysis-options
|
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)
|
||||
}
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart';
|
||||
|
||||
import 'src/my_central_controller.dart';
|
||||
|
||||
abstract class BluetoothLowEnergyAndroid {
|
||||
static void registerWith() {
|
||||
CentralController.instance = MyCentralController();
|
||||
}
|
||||
}
|
736
bluetooth_low_energy_android/lib/src/my_api.g.dart
Normal file
736
bluetooth_low_energy_android/lib/src/my_api.g.dart
Normal file
@ -0,0 +1,736 @@
|
||||
// Autogenerated from Pigeon (v10.1.6), do not edit directly.
|
||||
// See also: https://pub.dev/packages/pigeon
|
||||
// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name, unnecessary_import
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:typed_data' show Float64List, Int32List, Int64List, Uint8List;
|
||||
|
||||
import 'package:flutter/foundation.dart' show ReadBuffer, WriteBuffer;
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
enum MyCentralStateArgs {
|
||||
unknown,
|
||||
unsupported,
|
||||
unauthorized,
|
||||
poweredOff,
|
||||
poweredOn,
|
||||
}
|
||||
|
||||
enum MyGattCharacteristicPropertyArgs {
|
||||
read,
|
||||
write,
|
||||
writeWithoutResponse,
|
||||
notify,
|
||||
indicate,
|
||||
}
|
||||
|
||||
enum MyGattCharacteristicWriteTypeArgs {
|
||||
withResponse,
|
||||
withoutResponse,
|
||||
}
|
||||
|
||||
class MyCentralControllerArgs {
|
||||
MyCentralControllerArgs({
|
||||
required this.myStateNumber,
|
||||
});
|
||||
|
||||
int myStateNumber;
|
||||
|
||||
Object encode() {
|
||||
return <Object?>[
|
||||
myStateNumber,
|
||||
];
|
||||
}
|
||||
|
||||
static MyCentralControllerArgs decode(Object result) {
|
||||
result as List<Object?>;
|
||||
return MyCentralControllerArgs(
|
||||
myStateNumber: result[0]! as int,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class MyPeripheralArgs {
|
||||
MyPeripheralArgs({
|
||||
required this.key,
|
||||
required this.uuid,
|
||||
});
|
||||
|
||||
int key;
|
||||
|
||||
String uuid;
|
||||
|
||||
Object encode() {
|
||||
return <Object?>[
|
||||
key,
|
||||
uuid,
|
||||
];
|
||||
}
|
||||
|
||||
static MyPeripheralArgs decode(Object result) {
|
||||
result as List<Object?>;
|
||||
return MyPeripheralArgs(
|
||||
key: result[0]! as int,
|
||||
uuid: result[1]! as String,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class MyAdvertisementArgs {
|
||||
MyAdvertisementArgs({
|
||||
this.name,
|
||||
required this.manufacturerSpecificData,
|
||||
required this.serviceUUIDs,
|
||||
required this.serviceData,
|
||||
});
|
||||
|
||||
String? name;
|
||||
|
||||
Map<int?, Uint8List?> manufacturerSpecificData;
|
||||
|
||||
List<String?> serviceUUIDs;
|
||||
|
||||
Map<String?, Uint8List?> serviceData;
|
||||
|
||||
Object encode() {
|
||||
return <Object?>[
|
||||
name,
|
||||
manufacturerSpecificData,
|
||||
serviceUUIDs,
|
||||
serviceData,
|
||||
];
|
||||
}
|
||||
|
||||
static MyAdvertisementArgs decode(Object result) {
|
||||
result as List<Object?>;
|
||||
return MyAdvertisementArgs(
|
||||
name: result[0] as String?,
|
||||
manufacturerSpecificData: (result[1] as Map<Object?, Object?>?)!.cast<int?, Uint8List?>(),
|
||||
serviceUUIDs: (result[2] as List<Object?>?)!.cast<String?>(),
|
||||
serviceData: (result[3] as Map<Object?, Object?>?)!.cast<String?, Uint8List?>(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class MyGattServiceArgs {
|
||||
MyGattServiceArgs({
|
||||
required this.key,
|
||||
required this.uuid,
|
||||
});
|
||||
|
||||
int key;
|
||||
|
||||
String uuid;
|
||||
|
||||
Object encode() {
|
||||
return <Object?>[
|
||||
key,
|
||||
uuid,
|
||||
];
|
||||
}
|
||||
|
||||
static MyGattServiceArgs decode(Object result) {
|
||||
result as List<Object?>;
|
||||
return MyGattServiceArgs(
|
||||
key: result[0]! as int,
|
||||
uuid: result[1]! as String,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class MyGattCharacteristicArgs {
|
||||
MyGattCharacteristicArgs({
|
||||
required this.key,
|
||||
required this.uuid,
|
||||
required this.myPropertyNumbers,
|
||||
});
|
||||
|
||||
int key;
|
||||
|
||||
String uuid;
|
||||
|
||||
List<int?> myPropertyNumbers;
|
||||
|
||||
Object encode() {
|
||||
return <Object?>[
|
||||
key,
|
||||
uuid,
|
||||
myPropertyNumbers,
|
||||
];
|
||||
}
|
||||
|
||||
static MyGattCharacteristicArgs decode(Object result) {
|
||||
result as List<Object?>;
|
||||
return MyGattCharacteristicArgs(
|
||||
key: result[0]! as int,
|
||||
uuid: result[1]! as String,
|
||||
myPropertyNumbers: (result[2] as List<Object?>?)!.cast<int?>(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class MyGattDescriptorArgs {
|
||||
MyGattDescriptorArgs({
|
||||
required this.key,
|
||||
required this.uuid,
|
||||
});
|
||||
|
||||
int key;
|
||||
|
||||
String uuid;
|
||||
|
||||
Object encode() {
|
||||
return <Object?>[
|
||||
key,
|
||||
uuid,
|
||||
];
|
||||
}
|
||||
|
||||
static MyGattDescriptorArgs decode(Object result) {
|
||||
result as List<Object?>;
|
||||
return MyGattDescriptorArgs(
|
||||
key: result[0]! as int,
|
||||
uuid: result[1]! as String,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _MyCentralControllerHostApiCodec extends StandardMessageCodec {
|
||||
const _MyCentralControllerHostApiCodec();
|
||||
@override
|
||||
void writeValue(WriteBuffer buffer, Object? value) {
|
||||
if (value is MyCentralControllerArgs) {
|
||||
buffer.putUint8(128);
|
||||
writeValue(buffer, value.encode());
|
||||
} else if (value is MyGattCharacteristicArgs) {
|
||||
buffer.putUint8(129);
|
||||
writeValue(buffer, value.encode());
|
||||
} else if (value is MyGattDescriptorArgs) {
|
||||
buffer.putUint8(130);
|
||||
writeValue(buffer, value.encode());
|
||||
} else if (value is MyGattServiceArgs) {
|
||||
buffer.putUint8(131);
|
||||
writeValue(buffer, value.encode());
|
||||
} else {
|
||||
super.writeValue(buffer, value);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Object? readValueOfType(int type, ReadBuffer buffer) {
|
||||
switch (type) {
|
||||
case 128:
|
||||
return MyCentralControllerArgs.decode(readValue(buffer)!);
|
||||
case 129:
|
||||
return MyGattCharacteristicArgs.decode(readValue(buffer)!);
|
||||
case 130:
|
||||
return MyGattDescriptorArgs.decode(readValue(buffer)!);
|
||||
case 131:
|
||||
return MyGattServiceArgs.decode(readValue(buffer)!);
|
||||
default:
|
||||
return super.readValueOfType(type, buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class MyCentralControllerHostApi {
|
||||
/// Constructor for [MyCentralControllerHostApi]. The [binaryMessenger] named argument is
|
||||
/// available for dependency injection. If it is left null, the default
|
||||
/// BinaryMessenger will be used which routes to the host platform.
|
||||
MyCentralControllerHostApi({BinaryMessenger? binaryMessenger})
|
||||
: _binaryMessenger = binaryMessenger;
|
||||
final BinaryMessenger? _binaryMessenger;
|
||||
|
||||
static const MessageCodec<Object?> codec = _MyCentralControllerHostApiCodec();
|
||||
|
||||
Future<MyCentralControllerArgs> setUp() async {
|
||||
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
|
||||
'dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralControllerHostApi.setUp', codec,
|
||||
binaryMessenger: _binaryMessenger);
|
||||
final List<Object?>? replyList =
|
||||
await channel.send(null) as List<Object?>?;
|
||||
if (replyList == null) {
|
||||
throw PlatformException(
|
||||
code: 'channel-error',
|
||||
message: 'Unable to establish connection on channel.',
|
||||
);
|
||||
} else if (replyList.length > 1) {
|
||||
throw PlatformException(
|
||||
code: replyList[0]! as String,
|
||||
message: replyList[1] as String?,
|
||||
details: replyList[2],
|
||||
);
|
||||
} else if (replyList[0] == null) {
|
||||
throw PlatformException(
|
||||
code: 'null-error',
|
||||
message: 'Host platform returned null value for non-null return value.',
|
||||
);
|
||||
} else {
|
||||
return (replyList[0] as MyCentralControllerArgs?)!;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> tearDown() async {
|
||||
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
|
||||
'dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralControllerHostApi.tearDown', codec,
|
||||
binaryMessenger: _binaryMessenger);
|
||||
final List<Object?>? replyList =
|
||||
await channel.send(null) as List<Object?>?;
|
||||
if (replyList == null) {
|
||||
throw PlatformException(
|
||||
code: 'channel-error',
|
||||
message: 'Unable to establish connection on channel.',
|
||||
);
|
||||
} else if (replyList.length > 1) {
|
||||
throw PlatformException(
|
||||
code: replyList[0]! as String,
|
||||
message: replyList[1] as String?,
|
||||
details: replyList[2],
|
||||
);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> startDiscovery() async {
|
||||
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
|
||||
'dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralControllerHostApi.startDiscovery', codec,
|
||||
binaryMessenger: _binaryMessenger);
|
||||
final List<Object?>? replyList =
|
||||
await channel.send(null) as List<Object?>?;
|
||||
if (replyList == null) {
|
||||
throw PlatformException(
|
||||
code: 'channel-error',
|
||||
message: 'Unable to establish connection on channel.',
|
||||
);
|
||||
} else if (replyList.length > 1) {
|
||||
throw PlatformException(
|
||||
code: replyList[0]! as String,
|
||||
message: replyList[1] as String?,
|
||||
details: replyList[2],
|
||||
);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> stopDiscovery() async {
|
||||
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
|
||||
'dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralControllerHostApi.stopDiscovery', codec,
|
||||
binaryMessenger: _binaryMessenger);
|
||||
final List<Object?>? replyList =
|
||||
await channel.send(null) as List<Object?>?;
|
||||
if (replyList == null) {
|
||||
throw PlatformException(
|
||||
code: 'channel-error',
|
||||
message: 'Unable to establish connection on channel.',
|
||||
);
|
||||
} else if (replyList.length > 1) {
|
||||
throw PlatformException(
|
||||
code: replyList[0]! as String,
|
||||
message: replyList[1] as String?,
|
||||
details: replyList[2],
|
||||
);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> connect(int arg_myPeripheralKey) async {
|
||||
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
|
||||
'dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralControllerHostApi.connect', codec,
|
||||
binaryMessenger: _binaryMessenger);
|
||||
final List<Object?>? replyList =
|
||||
await channel.send(<Object?>[arg_myPeripheralKey]) as List<Object?>?;
|
||||
if (replyList == null) {
|
||||
throw PlatformException(
|
||||
code: 'channel-error',
|
||||
message: 'Unable to establish connection on channel.',
|
||||
);
|
||||
} else if (replyList.length > 1) {
|
||||
throw PlatformException(
|
||||
code: replyList[0]! as String,
|
||||
message: replyList[1] as String?,
|
||||
details: replyList[2],
|
||||
);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> disconnect(int arg_myPeripheralKey) async {
|
||||
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
|
||||
'dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralControllerHostApi.disconnect', codec,
|
||||
binaryMessenger: _binaryMessenger);
|
||||
final List<Object?>? replyList =
|
||||
await channel.send(<Object?>[arg_myPeripheralKey]) as List<Object?>?;
|
||||
if (replyList == null) {
|
||||
throw PlatformException(
|
||||
code: 'channel-error',
|
||||
message: 'Unable to establish connection on channel.',
|
||||
);
|
||||
} else if (replyList.length > 1) {
|
||||
throw PlatformException(
|
||||
code: replyList[0]! as String,
|
||||
message: replyList[1] as String?,
|
||||
details: replyList[2],
|
||||
);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> discoverGATT(int arg_myPeripheralKey) async {
|
||||
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
|
||||
'dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralControllerHostApi.discoverGATT', codec,
|
||||
binaryMessenger: _binaryMessenger);
|
||||
final List<Object?>? replyList =
|
||||
await channel.send(<Object?>[arg_myPeripheralKey]) as List<Object?>?;
|
||||
if (replyList == null) {
|
||||
throw PlatformException(
|
||||
code: 'channel-error',
|
||||
message: 'Unable to establish connection on channel.',
|
||||
);
|
||||
} else if (replyList.length > 1) {
|
||||
throw PlatformException(
|
||||
code: replyList[0]! as String,
|
||||
message: replyList[1] as String?,
|
||||
details: replyList[2],
|
||||
);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Future<List<MyGattServiceArgs?>> getServices(int arg_myPeripheralKey) async {
|
||||
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
|
||||
'dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralControllerHostApi.getServices', codec,
|
||||
binaryMessenger: _binaryMessenger);
|
||||
final List<Object?>? replyList =
|
||||
await channel.send(<Object?>[arg_myPeripheralKey]) as List<Object?>?;
|
||||
if (replyList == null) {
|
||||
throw PlatformException(
|
||||
code: 'channel-error',
|
||||
message: 'Unable to establish connection on channel.',
|
||||
);
|
||||
} else if (replyList.length > 1) {
|
||||
throw PlatformException(
|
||||
code: replyList[0]! as String,
|
||||
message: replyList[1] as String?,
|
||||
details: replyList[2],
|
||||
);
|
||||
} else if (replyList[0] == null) {
|
||||
throw PlatformException(
|
||||
code: 'null-error',
|
||||
message: 'Host platform returned null value for non-null return value.',
|
||||
);
|
||||
} else {
|
||||
return (replyList[0] as List<Object?>?)!.cast<MyGattServiceArgs?>();
|
||||
}
|
||||
}
|
||||
|
||||
Future<List<MyGattCharacteristicArgs?>> getCharacteristics(int arg_myServiceKey) async {
|
||||
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
|
||||
'dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralControllerHostApi.getCharacteristics', codec,
|
||||
binaryMessenger: _binaryMessenger);
|
||||
final List<Object?>? replyList =
|
||||
await channel.send(<Object?>[arg_myServiceKey]) as List<Object?>?;
|
||||
if (replyList == null) {
|
||||
throw PlatformException(
|
||||
code: 'channel-error',
|
||||
message: 'Unable to establish connection on channel.',
|
||||
);
|
||||
} else if (replyList.length > 1) {
|
||||
throw PlatformException(
|
||||
code: replyList[0]! as String,
|
||||
message: replyList[1] as String?,
|
||||
details: replyList[2],
|
||||
);
|
||||
} else if (replyList[0] == null) {
|
||||
throw PlatformException(
|
||||
code: 'null-error',
|
||||
message: 'Host platform returned null value for non-null return value.',
|
||||
);
|
||||
} else {
|
||||
return (replyList[0] as List<Object?>?)!.cast<MyGattCharacteristicArgs?>();
|
||||
}
|
||||
}
|
||||
|
||||
Future<List<MyGattDescriptorArgs?>> getDescriptors(int arg_myCharacteristicKey) async {
|
||||
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
|
||||
'dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralControllerHostApi.getDescriptors', codec,
|
||||
binaryMessenger: _binaryMessenger);
|
||||
final List<Object?>? replyList =
|
||||
await channel.send(<Object?>[arg_myCharacteristicKey]) as List<Object?>?;
|
||||
if (replyList == null) {
|
||||
throw PlatformException(
|
||||
code: 'channel-error',
|
||||
message: 'Unable to establish connection on channel.',
|
||||
);
|
||||
} else if (replyList.length > 1) {
|
||||
throw PlatformException(
|
||||
code: replyList[0]! as String,
|
||||
message: replyList[1] as String?,
|
||||
details: replyList[2],
|
||||
);
|
||||
} else if (replyList[0] == null) {
|
||||
throw PlatformException(
|
||||
code: 'null-error',
|
||||
message: 'Host platform returned null value for non-null return value.',
|
||||
);
|
||||
} else {
|
||||
return (replyList[0] as List<Object?>?)!.cast<MyGattDescriptorArgs?>();
|
||||
}
|
||||
}
|
||||
|
||||
Future<Uint8List> readCharacteristic(int arg_myPeripheralKey, int arg_myCharacteristicKey) async {
|
||||
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
|
||||
'dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralControllerHostApi.readCharacteristic', codec,
|
||||
binaryMessenger: _binaryMessenger);
|
||||
final List<Object?>? replyList =
|
||||
await channel.send(<Object?>[arg_myPeripheralKey, arg_myCharacteristicKey]) as List<Object?>?;
|
||||
if (replyList == null) {
|
||||
throw PlatformException(
|
||||
code: 'channel-error',
|
||||
message: 'Unable to establish connection on channel.',
|
||||
);
|
||||
} else if (replyList.length > 1) {
|
||||
throw PlatformException(
|
||||
code: replyList[0]! as String,
|
||||
message: replyList[1] as String?,
|
||||
details: replyList[2],
|
||||
);
|
||||
} else if (replyList[0] == null) {
|
||||
throw PlatformException(
|
||||
code: 'null-error',
|
||||
message: 'Host platform returned null value for non-null return value.',
|
||||
);
|
||||
} else {
|
||||
return (replyList[0] as Uint8List?)!;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> writeCharacteristic(int arg_myPeripheralKey, int arg_myCharacteristicKey, Uint8List arg_value, int arg_myTypeNumber) async {
|
||||
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
|
||||
'dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralControllerHostApi.writeCharacteristic', codec,
|
||||
binaryMessenger: _binaryMessenger);
|
||||
final List<Object?>? replyList =
|
||||
await channel.send(<Object?>[arg_myPeripheralKey, arg_myCharacteristicKey, arg_value, arg_myTypeNumber]) as List<Object?>?;
|
||||
if (replyList == null) {
|
||||
throw PlatformException(
|
||||
code: 'channel-error',
|
||||
message: 'Unable to establish connection on channel.',
|
||||
);
|
||||
} else if (replyList.length > 1) {
|
||||
throw PlatformException(
|
||||
code: replyList[0]! as String,
|
||||
message: replyList[1] as String?,
|
||||
details: replyList[2],
|
||||
);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> notifyCharacteristic(int arg_myPeripheralKey, int arg_myCharacteristicKey, bool arg_state) async {
|
||||
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
|
||||
'dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralControllerHostApi.notifyCharacteristic', codec,
|
||||
binaryMessenger: _binaryMessenger);
|
||||
final List<Object?>? replyList =
|
||||
await channel.send(<Object?>[arg_myPeripheralKey, arg_myCharacteristicKey, arg_state]) as List<Object?>?;
|
||||
if (replyList == null) {
|
||||
throw PlatformException(
|
||||
code: 'channel-error',
|
||||
message: 'Unable to establish connection on channel.',
|
||||
);
|
||||
} else if (replyList.length > 1) {
|
||||
throw PlatformException(
|
||||
code: replyList[0]! as String,
|
||||
message: replyList[1] as String?,
|
||||
details: replyList[2],
|
||||
);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Future<Uint8List> readDescriptor(int arg_myPeripheralKey, int arg_myDescriptorKey) async {
|
||||
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
|
||||
'dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralControllerHostApi.readDescriptor', codec,
|
||||
binaryMessenger: _binaryMessenger);
|
||||
final List<Object?>? replyList =
|
||||
await channel.send(<Object?>[arg_myPeripheralKey, arg_myDescriptorKey]) as List<Object?>?;
|
||||
if (replyList == null) {
|
||||
throw PlatformException(
|
||||
code: 'channel-error',
|
||||
message: 'Unable to establish connection on channel.',
|
||||
);
|
||||
} else if (replyList.length > 1) {
|
||||
throw PlatformException(
|
||||
code: replyList[0]! as String,
|
||||
message: replyList[1] as String?,
|
||||
details: replyList[2],
|
||||
);
|
||||
} else if (replyList[0] == null) {
|
||||
throw PlatformException(
|
||||
code: 'null-error',
|
||||
message: 'Host platform returned null value for non-null return value.',
|
||||
);
|
||||
} else {
|
||||
return (replyList[0] as Uint8List?)!;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> writeDescriptor(int arg_myPeripheralKey, int arg_myDescriptorKey, Uint8List arg_value) async {
|
||||
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
|
||||
'dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralControllerHostApi.writeDescriptor', codec,
|
||||
binaryMessenger: _binaryMessenger);
|
||||
final List<Object?>? replyList =
|
||||
await channel.send(<Object?>[arg_myPeripheralKey, arg_myDescriptorKey, arg_value]) as List<Object?>?;
|
||||
if (replyList == null) {
|
||||
throw PlatformException(
|
||||
code: 'channel-error',
|
||||
message: 'Unable to establish connection on channel.',
|
||||
);
|
||||
} else if (replyList.length > 1) {
|
||||
throw PlatformException(
|
||||
code: replyList[0]! as String,
|
||||
message: replyList[1] as String?,
|
||||
details: replyList[2],
|
||||
);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class _MyCentralControllerFlutterApiCodec extends StandardMessageCodec {
|
||||
const _MyCentralControllerFlutterApiCodec();
|
||||
@override
|
||||
void writeValue(WriteBuffer buffer, Object? value) {
|
||||
if (value is MyAdvertisementArgs) {
|
||||
buffer.putUint8(128);
|
||||
writeValue(buffer, value.encode());
|
||||
} else if (value is MyPeripheralArgs) {
|
||||
buffer.putUint8(129);
|
||||
writeValue(buffer, value.encode());
|
||||
} else {
|
||||
super.writeValue(buffer, value);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Object? readValueOfType(int type, ReadBuffer buffer) {
|
||||
switch (type) {
|
||||
case 128:
|
||||
return MyAdvertisementArgs.decode(readValue(buffer)!);
|
||||
case 129:
|
||||
return MyPeripheralArgs.decode(readValue(buffer)!);
|
||||
default:
|
||||
return super.readValueOfType(type, buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
abstract class MyCentralControllerFlutterApi {
|
||||
static const MessageCodec<Object?> codec = _MyCentralControllerFlutterApiCodec();
|
||||
|
||||
void onStateChanged(int myStateNumber);
|
||||
|
||||
void onDiscovered(MyPeripheralArgs myPeripheralArgs, int rssi, MyAdvertisementArgs myAdvertisementArgs);
|
||||
|
||||
void onPeripheralStateChanged(int myPeripheralKey, bool state);
|
||||
|
||||
void onCharacteristicValueChanged(int myCharacteristicKey, Uint8List value);
|
||||
|
||||
static void setup(MyCentralControllerFlutterApi? api, {BinaryMessenger? binaryMessenger}) {
|
||||
{
|
||||
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
|
||||
'dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralControllerFlutterApi.onStateChanged', codec,
|
||||
binaryMessenger: binaryMessenger);
|
||||
if (api == null) {
|
||||
channel.setMessageHandler(null);
|
||||
} else {
|
||||
channel.setMessageHandler((Object? message) async {
|
||||
assert(message != null,
|
||||
'Argument for dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralControllerFlutterApi.onStateChanged was null.');
|
||||
final List<Object?> args = (message as List<Object?>?)!;
|
||||
final int? arg_myStateNumber = (args[0] as int?);
|
||||
assert(arg_myStateNumber != null,
|
||||
'Argument for dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralControllerFlutterApi.onStateChanged was null, expected non-null int.');
|
||||
api.onStateChanged(arg_myStateNumber!);
|
||||
return;
|
||||
});
|
||||
}
|
||||
}
|
||||
{
|
||||
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
|
||||
'dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralControllerFlutterApi.onDiscovered', codec,
|
||||
binaryMessenger: binaryMessenger);
|
||||
if (api == null) {
|
||||
channel.setMessageHandler(null);
|
||||
} else {
|
||||
channel.setMessageHandler((Object? message) async {
|
||||
assert(message != null,
|
||||
'Argument for dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralControllerFlutterApi.onDiscovered was null.');
|
||||
final List<Object?> args = (message as List<Object?>?)!;
|
||||
final MyPeripheralArgs? arg_myPeripheralArgs = (args[0] as MyPeripheralArgs?);
|
||||
assert(arg_myPeripheralArgs != null,
|
||||
'Argument for dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralControllerFlutterApi.onDiscovered was null, expected non-null MyPeripheralArgs.');
|
||||
final int? arg_rssi = (args[1] as int?);
|
||||
assert(arg_rssi != null,
|
||||
'Argument for dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralControllerFlutterApi.onDiscovered was null, expected non-null int.');
|
||||
final MyAdvertisementArgs? arg_myAdvertisementArgs = (args[2] as MyAdvertisementArgs?);
|
||||
assert(arg_myAdvertisementArgs != null,
|
||||
'Argument for dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralControllerFlutterApi.onDiscovered was null, expected non-null MyAdvertisementArgs.');
|
||||
api.onDiscovered(arg_myPeripheralArgs!, arg_rssi!, arg_myAdvertisementArgs!);
|
||||
return;
|
||||
});
|
||||
}
|
||||
}
|
||||
{
|
||||
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
|
||||
'dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralControllerFlutterApi.onPeripheralStateChanged', codec,
|
||||
binaryMessenger: binaryMessenger);
|
||||
if (api == null) {
|
||||
channel.setMessageHandler(null);
|
||||
} else {
|
||||
channel.setMessageHandler((Object? message) async {
|
||||
assert(message != null,
|
||||
'Argument for dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralControllerFlutterApi.onPeripheralStateChanged was null.');
|
||||
final List<Object?> args = (message as List<Object?>?)!;
|
||||
final int? arg_myPeripheralKey = (args[0] as int?);
|
||||
assert(arg_myPeripheralKey != null,
|
||||
'Argument for dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralControllerFlutterApi.onPeripheralStateChanged was null, expected non-null int.');
|
||||
final bool? arg_state = (args[1] as bool?);
|
||||
assert(arg_state != null,
|
||||
'Argument for dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralControllerFlutterApi.onPeripheralStateChanged was null, expected non-null bool.');
|
||||
api.onPeripheralStateChanged(arg_myPeripheralKey!, arg_state!);
|
||||
return;
|
||||
});
|
||||
}
|
||||
}
|
||||
{
|
||||
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
|
||||
'dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralControllerFlutterApi.onCharacteristicValueChanged', codec,
|
||||
binaryMessenger: binaryMessenger);
|
||||
if (api == null) {
|
||||
channel.setMessageHandler(null);
|
||||
} else {
|
||||
channel.setMessageHandler((Object? message) async {
|
||||
assert(message != null,
|
||||
'Argument for dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralControllerFlutterApi.onCharacteristicValueChanged was null.');
|
||||
final List<Object?> args = (message as List<Object?>?)!;
|
||||
final int? arg_myCharacteristicKey = (args[0] as int?);
|
||||
assert(arg_myCharacteristicKey != null,
|
||||
'Argument for dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralControllerFlutterApi.onCharacteristicValueChanged was null, expected non-null int.');
|
||||
final Uint8List? arg_value = (args[1] as Uint8List?);
|
||||
assert(arg_value != null,
|
||||
'Argument for dev.flutter.pigeon.bluetooth_low_energy_android.MyCentralControllerFlutterApi.onCharacteristicValueChanged was null, expected non-null Uint8List.');
|
||||
api.onCharacteristicValueChanged(arg_myCharacteristicKey!, arg_value!);
|
||||
return;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
356
bluetooth_low_energy_android/lib/src/my_central_controller.dart
Normal file
356
bluetooth_low_energy_android/lib/src/my_central_controller.dart
Normal file
@ -0,0 +1,356 @@
|
||||
import 'dart:async';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart';
|
||||
|
||||
import 'my_api.g.dart';
|
||||
import 'my_gatt_characteristic.dart';
|
||||
import 'my_gatt_descriptor.dart';
|
||||
import 'my_gatt_service.dart';
|
||||
import 'my_peripheral.dart';
|
||||
|
||||
class MyCentralController extends CentralController
|
||||
implements MyCentralControllerFlutterApi {
|
||||
MyCentralController()
|
||||
: _myApi = MyCentralControllerHostApi(),
|
||||
_stateChangedController = StreamController.broadcast(),
|
||||
_discoveredController = StreamController.broadcast(),
|
||||
_peripheralStateChangedController = StreamController.broadcast(),
|
||||
_characteristicValueChangedController = StreamController.broadcast(),
|
||||
_myPeripherals = {},
|
||||
_myServices = {},
|
||||
_myCharacteristics = {},
|
||||
_myDescriptors = {},
|
||||
_state = CentralState.unknown;
|
||||
|
||||
final MyCentralControllerHostApi _myApi;
|
||||
final StreamController<CentralStateChangedEventArgs> _stateChangedController;
|
||||
final StreamController<CentralDiscoveredEventArgs> _discoveredController;
|
||||
final StreamController<PeripheralStateChangedEventArgs>
|
||||
_peripheralStateChangedController;
|
||||
final StreamController<GattCharacteristicValueChangedEventArgs>
|
||||
_characteristicValueChangedController;
|
||||
final Map<int, MyPeripheral> _myPeripherals;
|
||||
final Map<int, MyGattService> _myServices;
|
||||
final Map<int, MyGattCharacteristic> _myCharacteristics;
|
||||
final Map<int, MyGattDescriptor> _myDescriptors;
|
||||
|
||||
CentralState _state;
|
||||
@override
|
||||
CentralState get state => _state;
|
||||
|
||||
@override
|
||||
Stream<CentralStateChangedEventArgs> get stateChanged =>
|
||||
_stateChangedController.stream;
|
||||
@override
|
||||
Stream<CentralDiscoveredEventArgs> get discovered =>
|
||||
_discoveredController.stream;
|
||||
@override
|
||||
Stream<PeripheralStateChangedEventArgs> get peripheralStateChanged =>
|
||||
_peripheralStateChangedController.stream;
|
||||
@override
|
||||
Stream<GattCharacteristicValueChangedEventArgs>
|
||||
get characteristicValueChanged =>
|
||||
_characteristicValueChangedController.stream;
|
||||
|
||||
Future<void> _throwWithState(CentralState state) async {
|
||||
if (this.state == state) {
|
||||
throw BluetoothLowEnergyError('$state is unexpected.');
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _throwWithoutState(CentralState state) async {
|
||||
if (this.state != state) {
|
||||
throw BluetoothLowEnergyError(
|
||||
'$state is expected, but current state is ${this.state}.',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> setUp() async {
|
||||
await _throwWithoutState(CentralState.unknown);
|
||||
final args = await _myApi.setUp();
|
||||
final myStateArgs = MyCentralStateArgs.values[args.myStateNumber];
|
||||
_state = myStateArgs.toState();
|
||||
MyCentralControllerFlutterApi.setup(this);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> tearDown() async {
|
||||
await _throwWithState(CentralState.unknown);
|
||||
await _myApi.tearDown();
|
||||
MyCentralControllerFlutterApi.setup(null);
|
||||
_myPeripherals.clear();
|
||||
_myServices.clear();
|
||||
_myCharacteristics.clear();
|
||||
_myDescriptors.clear();
|
||||
_state = CentralState.unknown;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> startDiscovery() async {
|
||||
await _throwWithoutState(CentralState.poweredOn);
|
||||
await _myApi.startDiscovery();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> stopDiscovery() async {
|
||||
await _throwWithoutState(CentralState.poweredOn);
|
||||
await _myApi.stopDiscovery();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> connect(Peripheral peripheral) async {
|
||||
await _throwWithoutState(CentralState.poweredOn);
|
||||
final myPeripheral = peripheral as MyPeripheral;
|
||||
await _myApi.connect(myPeripheral.hashCode);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> disconnect(Peripheral peripheral) async {
|
||||
await _throwWithoutState(CentralState.poweredOn);
|
||||
final myPeripheral = peripheral as MyPeripheral;
|
||||
await _myApi.disconnect(myPeripheral.hashCode);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> discoverGATT(Peripheral peripheral) async {
|
||||
await _throwWithoutState(CentralState.poweredOn);
|
||||
final myPeripheral = peripheral as MyPeripheral;
|
||||
await _myApi.discoverGATT(myPeripheral.hashCode);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<GattService>> getServices(Peripheral peripheral) async {
|
||||
await _throwWithoutState(CentralState.poweredOn);
|
||||
final myPeripheral = peripheral as MyPeripheral;
|
||||
final myServiceArgses = await _myApi.getServices(myPeripheral.hashCode);
|
||||
return myServiceArgses
|
||||
.cast<MyGattServiceArgs>()
|
||||
.map(
|
||||
(myServiceArgs) => _myServices.putIfAbsent(
|
||||
myServiceArgs.key,
|
||||
() => MyGattService.fromMyArgs(
|
||||
myPeripheral,
|
||||
myServiceArgs,
|
||||
),
|
||||
),
|
||||
)
|
||||
.toList();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<GattCharacteristic>> getCharacteristics(
|
||||
GattService service,
|
||||
) async {
|
||||
await _throwWithoutState(CentralState.poweredOn);
|
||||
final myService = service as MyGattService;
|
||||
final myCharactersiticArgses = await _myApi.getCharacteristics(
|
||||
myService.hashCode,
|
||||
);
|
||||
return myCharactersiticArgses
|
||||
.cast<MyGattCharacteristicArgs>()
|
||||
.map(
|
||||
(myCharacteristicArgs) => _myCharacteristics.putIfAbsent(
|
||||
myCharacteristicArgs.key,
|
||||
() => MyGattCharacteristic.fromMyArgs(
|
||||
myService,
|
||||
myCharacteristicArgs,
|
||||
),
|
||||
),
|
||||
)
|
||||
.toList();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<GattDescriptor>> getDescriptors(
|
||||
GattCharacteristic characteristic,
|
||||
) async {
|
||||
await _throwWithoutState(CentralState.poweredOn);
|
||||
final myCharacteristic = characteristic as MyGattCharacteristic;
|
||||
final myDescriptorArgses = await _myApi.getDescriptors(
|
||||
myCharacteristic.hashCode,
|
||||
);
|
||||
return myDescriptorArgses
|
||||
.cast<MyGattDescriptorArgs>()
|
||||
.map(
|
||||
(myDescriptorArgs) => _myDescriptors.putIfAbsent(
|
||||
myDescriptorArgs.key,
|
||||
() => MyGattDescriptor.fromMyArgs(
|
||||
myCharacteristic,
|
||||
myDescriptorArgs,
|
||||
),
|
||||
),
|
||||
)
|
||||
.toList();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Uint8List> readCharacteristic(
|
||||
GattCharacteristic characteristic) async {
|
||||
await _throwWithoutState(CentralState.poweredOn);
|
||||
final myCharacteristic = characteristic as MyGattCharacteristic;
|
||||
final myService = myCharacteristic.myService;
|
||||
final myPeripheral = myService.myPeripheral;
|
||||
final value = await _myApi.readCharacteristic(
|
||||
myPeripheral.hashCode,
|
||||
myCharacteristic.hashCode,
|
||||
);
|
||||
return value;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> writeCharacteristic(
|
||||
GattCharacteristic characteristic, {
|
||||
required Uint8List value,
|
||||
required GattCharacteristicWriteType type,
|
||||
}) async {
|
||||
await _throwWithoutState(CentralState.poweredOn);
|
||||
final myCharacteristic = characteristic as MyGattCharacteristic;
|
||||
final myService = myCharacteristic.myService;
|
||||
final myPeripheral = myService.myPeripheral;
|
||||
final typeArgs = type.toMyArgs();
|
||||
final typeNumber = typeArgs.index;
|
||||
await _myApi.writeCharacteristic(
|
||||
myPeripheral.hashCode,
|
||||
myCharacteristic.hashCode,
|
||||
value,
|
||||
typeNumber,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> notifyCharacteristic(
|
||||
GattCharacteristic characteristic, {
|
||||
required bool state,
|
||||
}) async {
|
||||
await _throwWithoutState(CentralState.poweredOn);
|
||||
final myCharacteristic = characteristic as MyGattCharacteristic;
|
||||
final myService = myCharacteristic.myService;
|
||||
final myPeripheral = myService.myPeripheral;
|
||||
await _myApi.notifyCharacteristic(
|
||||
myPeripheral.hashCode,
|
||||
myCharacteristic.hashCode,
|
||||
state,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Uint8List> readDescriptor(GattDescriptor descriptor) async {
|
||||
await _throwWithoutState(CentralState.poweredOn);
|
||||
final myDescriptor = descriptor as MyGattDescriptor;
|
||||
final myCharacteristic = myDescriptor.myCharacteristic;
|
||||
final myService = myCharacteristic.myService;
|
||||
final myPeripheral = myService.myPeripheral;
|
||||
final value = await _myApi.readDescriptor(
|
||||
myPeripheral.hashCode,
|
||||
myDescriptor.hashCode,
|
||||
);
|
||||
return value;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> writeDescriptor(
|
||||
GattDescriptor descriptor, {
|
||||
required Uint8List value,
|
||||
}) async {
|
||||
await _throwWithoutState(CentralState.poweredOn);
|
||||
final myDescriptor = descriptor as MyGattDescriptor;
|
||||
final myCharacteristic = myDescriptor.myCharacteristic;
|
||||
final myService = myCharacteristic.myService;
|
||||
final myPeripheral = myService.myPeripheral;
|
||||
await _myApi.writeDescriptor(
|
||||
myPeripheral.hashCode,
|
||||
myDescriptor.hashCode,
|
||||
value,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void onStateChanged(int myStateNumber) {
|
||||
final myStateArgs = MyCentralStateArgs.values[myStateNumber];
|
||||
final state = myStateArgs.toState();
|
||||
if (_state == state) {
|
||||
return;
|
||||
}
|
||||
_state = state;
|
||||
final eventArgs = CentralStateChangedEventArgs(state);
|
||||
_stateChangedController.add(eventArgs);
|
||||
}
|
||||
|
||||
@override
|
||||
void onDiscovered(
|
||||
MyPeripheralArgs myPeripheralArgs,
|
||||
int rssi,
|
||||
MyAdvertisementArgs myAdvertisementArgs,
|
||||
) {
|
||||
final myPeripheral = _myPeripherals.putIfAbsent(
|
||||
myPeripheralArgs.key,
|
||||
() => MyPeripheral.fromMyArgs(myPeripheralArgs),
|
||||
);
|
||||
final advertisement = myAdvertisementArgs.toAdvertisement();
|
||||
final eventArgs = CentralDiscoveredEventArgs(
|
||||
myPeripheral,
|
||||
rssi,
|
||||
advertisement,
|
||||
);
|
||||
_discoveredController.add(eventArgs);
|
||||
}
|
||||
|
||||
@override
|
||||
void onPeripheralStateChanged(int myPeripheralKey, bool state) {
|
||||
final myPeripheral = _myPeripherals[myPeripheralKey];
|
||||
if (myPeripheral == null) {
|
||||
return;
|
||||
}
|
||||
final eventArgs = PeripheralStateChangedEventArgs(myPeripheral, state);
|
||||
_peripheralStateChangedController.add(eventArgs);
|
||||
}
|
||||
|
||||
@override
|
||||
void onCharacteristicValueChanged(int myCharacteristicKey, Uint8List value) {
|
||||
final myCharacteristic =
|
||||
_myCharacteristics[myCharacteristicKey] as MyGattCharacteristic;
|
||||
final eventArgs = GattCharacteristicValueChangedEventArgs(
|
||||
myCharacteristic,
|
||||
value,
|
||||
);
|
||||
_characteristicValueChangedController.add(eventArgs);
|
||||
}
|
||||
}
|
||||
|
||||
extension on MyAdvertisementArgs {
|
||||
Advertisement toAdvertisement() {
|
||||
final serviceUUIDs = this
|
||||
.serviceUUIDs
|
||||
.cast<String>()
|
||||
.map((uuid) => UUID.fromString(uuid))
|
||||
.toList();
|
||||
final serviceData = this.serviceData.cast<String, Uint8List>().map(
|
||||
(uuid, data) {
|
||||
final key = UUID.fromString(uuid);
|
||||
final value = data;
|
||||
return MapEntry(key, value);
|
||||
},
|
||||
);
|
||||
return Advertisement(
|
||||
name: name,
|
||||
manufacturerSpecificData: manufacturerSpecificData.cast<int, Uint8List>(),
|
||||
serviceUUIDs: serviceUUIDs,
|
||||
serviceData: serviceData,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
extension on MyCentralStateArgs {
|
||||
CentralState toState() {
|
||||
return CentralState.values[index];
|
||||
}
|
||||
}
|
||||
|
||||
extension on GattCharacteristicWriteType {
|
||||
MyGattCharacteristicWriteTypeArgs toMyArgs() {
|
||||
return MyGattCharacteristicWriteTypeArgs.values[index];
|
||||
}
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart';
|
||||
|
||||
import 'my_api.g.dart';
|
||||
import 'my_gatt_service.dart';
|
||||
import 'my_object.dart';
|
||||
|
||||
class MyGattCharacteristic extends MyObject implements GattCharacteristic {
|
||||
final MyGattService myService;
|
||||
@override
|
||||
final UUID uuid;
|
||||
@override
|
||||
final List<GattCharacteristicProperty> properties;
|
||||
|
||||
MyGattCharacteristic(
|
||||
super.hashCode,
|
||||
this.myService,
|
||||
this.uuid,
|
||||
this.properties,
|
||||
);
|
||||
|
||||
factory MyGattCharacteristic.fromMyArgs(
|
||||
MyGattService myService,
|
||||
MyGattCharacteristicArgs myArgs,
|
||||
) {
|
||||
final hashCode = myArgs.key;
|
||||
final uuid = UUID.fromString(myArgs.uuid);
|
||||
final properties = myArgs.myPropertyNumbers.cast<int>().map(
|
||||
(myPropertyNumber) {
|
||||
final myPropertyArgs =
|
||||
MyGattCharacteristicPropertyArgs.values[myPropertyNumber];
|
||||
return myPropertyArgs.toProperty();
|
||||
},
|
||||
).toList();
|
||||
return MyGattCharacteristic(hashCode, myService, uuid, properties);
|
||||
}
|
||||
}
|
||||
|
||||
extension on MyGattCharacteristicPropertyArgs {
|
||||
GattCharacteristicProperty toProperty() {
|
||||
return GattCharacteristicProperty.values[index];
|
||||
}
|
||||
}
|
22
bluetooth_low_energy_android/lib/src/my_gatt_descriptor.dart
Normal file
22
bluetooth_low_energy_android/lib/src/my_gatt_descriptor.dart
Normal file
@ -0,0 +1,22 @@
|
||||
import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart';
|
||||
|
||||
import 'my_api.g.dart';
|
||||
import 'my_gatt_characteristic.dart';
|
||||
import 'my_object.dart';
|
||||
|
||||
class MyGattDescriptor extends MyObject implements GattDescriptor {
|
||||
final MyGattCharacteristic myCharacteristic;
|
||||
@override
|
||||
final UUID uuid;
|
||||
|
||||
MyGattDescriptor(super.hashCode, this.myCharacteristic, this.uuid);
|
||||
|
||||
factory MyGattDescriptor.fromMyArgs(
|
||||
MyGattCharacteristic myCharacteristic,
|
||||
MyGattDescriptorArgs myArgs,
|
||||
) {
|
||||
final hashCode = myArgs.key;
|
||||
final uuid = UUID.fromString(myArgs.uuid);
|
||||
return MyGattDescriptor(hashCode, myCharacteristic, uuid);
|
||||
}
|
||||
}
|
22
bluetooth_low_energy_android/lib/src/my_gatt_service.dart
Normal file
22
bluetooth_low_energy_android/lib/src/my_gatt_service.dart
Normal file
@ -0,0 +1,22 @@
|
||||
import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart';
|
||||
|
||||
import 'my_api.g.dart';
|
||||
import 'my_object.dart';
|
||||
import 'my_peripheral.dart';
|
||||
|
||||
class MyGattService extends MyObject implements GattService {
|
||||
final MyPeripheral myPeripheral;
|
||||
@override
|
||||
final UUID uuid;
|
||||
|
||||
MyGattService(super.hashCode, this.myPeripheral, this.uuid);
|
||||
|
||||
factory MyGattService.fromMyArgs(
|
||||
MyPeripheral myPeripheral,
|
||||
MyGattServiceArgs myArgs,
|
||||
) {
|
||||
final hashCode = myArgs.key;
|
||||
final uuid = UUID.fromString(myArgs.uuid);
|
||||
return MyGattService(hashCode, myPeripheral, uuid);
|
||||
}
|
||||
}
|
11
bluetooth_low_energy_android/lib/src/my_object.dart
Normal file
11
bluetooth_low_energy_android/lib/src/my_object.dart
Normal file
@ -0,0 +1,11 @@
|
||||
abstract class MyObject {
|
||||
@override
|
||||
final int hashCode;
|
||||
|
||||
MyObject(this.hashCode);
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return other is MyObject && other.hashCode == hashCode;
|
||||
}
|
||||
}
|
17
bluetooth_low_energy_android/lib/src/my_peripheral.dart
Normal file
17
bluetooth_low_energy_android/lib/src/my_peripheral.dart
Normal file
@ -0,0 +1,17 @@
|
||||
import 'package:bluetooth_low_energy_platform_interface/bluetooth_low_energy_platform_interface.dart';
|
||||
|
||||
import 'my_api.g.dart';
|
||||
import 'my_object.dart';
|
||||
|
||||
class MyPeripheral extends MyObject implements Peripheral {
|
||||
@override
|
||||
final UUID uuid;
|
||||
|
||||
MyPeripheral(super.hashCode, this.uuid);
|
||||
|
||||
factory MyPeripheral.fromMyArgs(MyPeripheralArgs myArgs) {
|
||||
final hashCode = myArgs.key;
|
||||
final uuid = UUID.fromString(myArgs.uuid);
|
||||
return MyPeripheral(hashCode, uuid);
|
||||
}
|
||||
}
|
144
bluetooth_low_energy_android/my_api.dart
Normal file
144
bluetooth_low_energy_android/my_api.dart
Normal file
@ -0,0 +1,144 @@
|
||||
import 'package:pigeon/pigeon.dart';
|
||||
|
||||
@ConfigurePigeon(
|
||||
PigeonOptions(
|
||||
dartOut: 'lib/src/my_api.g.dart',
|
||||
dartOptions: DartOptions(),
|
||||
kotlinOut:
|
||||
'android/src/main/kotlin/dev/yanshouwang/bluetooth_low_energy/MyApi.g.kt',
|
||||
kotlinOptions: KotlinOptions(
|
||||
package: 'dev.yanshouwang.bluetooth_low_energy',
|
||||
),
|
||||
),
|
||||
)
|
||||
@HostApi()
|
||||
abstract class MyCentralControllerHostApi {
|
||||
@async
|
||||
MyCentralControllerArgs setUp();
|
||||
void tearDown();
|
||||
@async
|
||||
void startDiscovery();
|
||||
void stopDiscovery();
|
||||
@async
|
||||
void connect(int myPeripheralKey);
|
||||
@async
|
||||
void disconnect(int myPeripheralKey);
|
||||
@async
|
||||
void discoverGATT(int myPeripheralKey);
|
||||
List<MyGattServiceArgs> getServices(int myPeripheralKey);
|
||||
List<MyGattCharacteristicArgs> getCharacteristics(int myServiceKey);
|
||||
List<MyGattDescriptorArgs> getDescriptors(int myCharacteristicKey);
|
||||
@async
|
||||
Uint8List readCharacteristic(int myPeripheralKey, int myCharacteristicKey);
|
||||
@async
|
||||
void writeCharacteristic(
|
||||
int myPeripheralKey,
|
||||
int myCharacteristicKey,
|
||||
Uint8List value,
|
||||
int myTypeNumber,
|
||||
);
|
||||
@async
|
||||
void notifyCharacteristic(
|
||||
int myPeripheralKey,
|
||||
int myCharacteristicKey,
|
||||
bool state,
|
||||
);
|
||||
@async
|
||||
Uint8List readDescriptor(int myPeripheralKey, int myDescriptorKey);
|
||||
@async
|
||||
void writeDescriptor(
|
||||
int myPeripheralKey,
|
||||
int myDescriptorKey,
|
||||
Uint8List value,
|
||||
);
|
||||
}
|
||||
|
||||
@FlutterApi()
|
||||
abstract class MyCentralControllerFlutterApi {
|
||||
void onStateChanged(int myStateNumber);
|
||||
void onDiscovered(
|
||||
MyPeripheralArgs myPeripheralArgs,
|
||||
int rssi,
|
||||
MyAdvertisementArgs myAdvertisementArgs,
|
||||
);
|
||||
void onPeripheralStateChanged(int myPeripheralKey, bool state);
|
||||
void onCharacteristicValueChanged(int myCharacteristicKey, Uint8List value);
|
||||
}
|
||||
|
||||
class MyCentralControllerArgs {
|
||||
final int myStateNumber;
|
||||
|
||||
MyCentralControllerArgs(this.myStateNumber);
|
||||
}
|
||||
|
||||
class MyPeripheralArgs {
|
||||
final int key;
|
||||
final String uuid;
|
||||
|
||||
MyPeripheralArgs(this.key, this.uuid);
|
||||
}
|
||||
|
||||
class MyAdvertisementArgs {
|
||||
final String? name;
|
||||
final Map<int?, Uint8List?> manufacturerSpecificData;
|
||||
final List<String?> serviceUUIDs;
|
||||
final Map<String?, Uint8List?> serviceData;
|
||||
|
||||
MyAdvertisementArgs(
|
||||
this.name,
|
||||
this.manufacturerSpecificData,
|
||||
this.serviceUUIDs,
|
||||
this.serviceData,
|
||||
);
|
||||
}
|
||||
|
||||
class MyGattServiceArgs {
|
||||
final int key;
|
||||
final String uuid;
|
||||
|
||||
MyGattServiceArgs(this.key, this.uuid);
|
||||
}
|
||||
|
||||
class MyGattCharacteristicArgs {
|
||||
final int key;
|
||||
final String uuid;
|
||||
final List<int?> myPropertyNumbers;
|
||||
|
||||
MyGattCharacteristicArgs(
|
||||
this.key,
|
||||
this.uuid,
|
||||
this.myPropertyNumbers,
|
||||
);
|
||||
}
|
||||
|
||||
class MyGattDescriptorArgs {
|
||||
final int key;
|
||||
final String uuid;
|
||||
|
||||
MyGattDescriptorArgs(this.key, this.uuid);
|
||||
}
|
||||
|
||||
enum MyCentralStateArgs {
|
||||
unknown,
|
||||
unsupported,
|
||||
unauthorized,
|
||||
poweredOff,
|
||||
poweredOn,
|
||||
}
|
||||
|
||||
enum MyGattCharacteristicPropertyArgs {
|
||||
read,
|
||||
write,
|
||||
writeWithoutResponse,
|
||||
notify,
|
||||
indicate,
|
||||
}
|
||||
|
||||
enum MyGattCharacteristicWriteTypeArgs {
|
||||
// Write with response
|
||||
withResponse,
|
||||
// Write without response
|
||||
withoutResponse,
|
||||
// Write with response and waiting for confirmation
|
||||
// reliable,
|
||||
}
|
29
bluetooth_low_energy_android/pubspec.yaml
Normal file
29
bluetooth_low_energy_android/pubspec.yaml
Normal file
@ -0,0 +1,29 @@
|
||||
name: bluetooth_low_energy_android
|
||||
description: Android implementation of the bluetooth_low_energy plugin.
|
||||
version: 2.0.0
|
||||
homepage: https://github.com/yanshouwang/bluetooth_low_energy
|
||||
|
||||
environment:
|
||||
sdk: ">=3.0.0 <4.0.0"
|
||||
flutter: ">=3.3.0"
|
||||
|
||||
dependencies:
|
||||
flutter:
|
||||
sdk: flutter
|
||||
bluetooth_low_energy_platform_interface:
|
||||
path: ../bluetooth_low_energy_platform_interface
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
flutter_lints: ^2.0.0
|
||||
pigeon: ^10.1.6
|
||||
|
||||
flutter:
|
||||
plugin:
|
||||
implements: bluetooth_low_energy
|
||||
platforms:
|
||||
android:
|
||||
package: dev.yanshouwang.bluetooth_low_energy
|
||||
pluginClass: BluetoothLowEnergyAndroid
|
||||
dartPluginClass: BluetoothLowEnergyAndroid
|
Reference in New Issue
Block a user