You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
356 lines
9.4 KiB
356 lines
9.4 KiB
import 'dart:async';
|
|
import 'dart:typed_data';
|
|
|
|
import 'package:flutter/services.dart';
|
|
|
|
/// BLE连接状态常量
|
|
class BleConnectionState {
|
|
static const int disconnected = 0;
|
|
static const int connecting = 1;
|
|
static const int connected = 2;
|
|
static const int disconnecting = 3;
|
|
static const int error = 4;
|
|
}
|
|
|
|
/// BLE设备数据模型
|
|
class BleDevice {
|
|
final String id;
|
|
final String name;
|
|
final int rssi;
|
|
final List<String> uuids;
|
|
|
|
BleDevice({
|
|
required this.id,
|
|
required this.name,
|
|
required this.rssi,
|
|
required this.uuids,
|
|
});
|
|
|
|
factory BleDevice.fromMap(Map<dynamic, dynamic> map) {
|
|
final uuidsList = (map['uuids'] as List?)?.cast<String>() ?? [];
|
|
|
|
return BleDevice(
|
|
id: map['id'] as String,
|
|
name: map['name'] as String? ?? 'Unknown',
|
|
rssi: map['rssi'] as int,
|
|
uuids: uuidsList,
|
|
);
|
|
}
|
|
|
|
@override
|
|
String toString() => 'BleDevice(id: $id, name: $name, rssi: $rssi, uuids: $uuids)';
|
|
}
|
|
|
|
/// BLE服务插件
|
|
class BleService {
|
|
static final BleService _instance = BleService._internal();
|
|
|
|
/// 获取单例
|
|
factory BleService() => _instance;
|
|
|
|
// 方法通道
|
|
final MethodChannel _methodChannel = const MethodChannel('com.yunqiinnovation.ble_service');
|
|
|
|
// 事件通道
|
|
late final EventChannel _scanEventChannel;
|
|
late final EventChannel _connectionEventChannel;
|
|
late final EventChannel _dataEventChannel;
|
|
late final EventChannel _statusEventChannel;
|
|
|
|
// 事件流
|
|
late final Stream<List<BleDevice>> _scanStream;
|
|
late final Stream<dynamic> _connectionStream;
|
|
late final Stream<dynamic> _dataStream;
|
|
late final Stream<dynamic> _statusStream;
|
|
|
|
// 获取事件流
|
|
Stream<List<BleDevice>> get onScanResults => _scanStream;
|
|
Stream<dynamic> get onConnectionChanged => _connectionStream;
|
|
Stream<dynamic> get onDataReceived => _dataStream;
|
|
Stream<dynamic> get onStatusUpdated => _statusStream;
|
|
|
|
// 内部构造函数
|
|
BleService._internal() {
|
|
_scanEventChannel = const EventChannel('com.yunqiinnovation.ble_service/scan_events');
|
|
_connectionEventChannel = const EventChannel('com.yunqiinnovation.ble_service/connection_events');
|
|
_dataEventChannel = const EventChannel('com.yunqiinnovation.ble_service/data_events');
|
|
_statusEventChannel = const EventChannel('com.yunqiinnovation.ble_service/status_events');
|
|
|
|
// 初始化事件流
|
|
_scanStream = _scanEventChannel.receiveBroadcastStream().map<List<BleDevice>>((event) {
|
|
final List<dynamic> devices = (event['devices'] as List?) ?? [];
|
|
return devices.map((device) => BleDevice.fromMap(device)).toList();
|
|
});
|
|
|
|
_connectionStream = _connectionEventChannel.receiveBroadcastStream();
|
|
_dataStream = _dataEventChannel.receiveBroadcastStream();
|
|
_statusStream = _statusEventChannel.receiveBroadcastStream();
|
|
}
|
|
|
|
/// 初始化BLE服务
|
|
Future<bool> initialize() async {
|
|
try {
|
|
final result = await _methodChannel.invokeMethod<bool>('initialize');
|
|
return result ?? false;
|
|
} catch (e) {
|
|
print('初始化BLE服务失败: $e');
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/// 注册BLE回调
|
|
Future<bool> registerCallback() async {
|
|
try {
|
|
final result = await _methodChannel.invokeMethod<bool>('registerCallback');
|
|
return result ?? false;
|
|
} catch (e) {
|
|
print('注册BLE回调失败: $e');
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/// 设置BLE方法回调处理器
|
|
void setMethodCallHandler(Future<dynamic> Function(MethodCall call) handler) {
|
|
_methodChannel.setMethodCallHandler(handler);
|
|
}
|
|
|
|
/// 开始扫描设备
|
|
Future<bool> startScan({int timeout = 10000}) async {
|
|
try {
|
|
final result = await _methodChannel.invokeMethod<bool>(
|
|
'startScan',
|
|
{'timeout': timeout},
|
|
);
|
|
return result ?? false;
|
|
} catch (e) {
|
|
print('开始扫描设备失败: $e');
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/// 停止扫描设备
|
|
Future<bool> stopScan() async {
|
|
try {
|
|
final result = await _methodChannel.invokeMethod<bool>('stopScan');
|
|
return result ?? false;
|
|
} catch (e) {
|
|
print('停止扫描设备失败: $e');
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/// 连接到设备
|
|
Future<bool> connect(String address) async {
|
|
try {
|
|
final result = await _methodChannel.invokeMethod<bool>(
|
|
'connect',
|
|
{'address': address},
|
|
);
|
|
return result ?? false;
|
|
} catch (e) {
|
|
print('连接设备失败: $e');
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/// 断开设备连接
|
|
Future<bool> disconnect() async {
|
|
try {
|
|
final result = await _methodChannel.invokeMethod<bool>('disconnect');
|
|
return result ?? false;
|
|
} catch (e) {
|
|
print('断开设备连接失败: $e');
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/// 检查是否已连接
|
|
Future<bool> isConnected() async {
|
|
try {
|
|
final result = await _methodChannel.invokeMethod<bool>('isConnected');
|
|
return result ?? false;
|
|
} catch (e) {
|
|
print('检查连接状态失败: $e');
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/// 获取连接状态
|
|
Future<int> getConnectionState() async {
|
|
try {
|
|
final result = await _methodChannel.invokeMethod<int>('getConnectionState');
|
|
return result ?? BleConnectionState.disconnected;
|
|
} catch (e) {
|
|
print('获取连接状态失败: $e');
|
|
return BleConnectionState.disconnected;
|
|
}
|
|
}
|
|
|
|
/// 获取已连接设备地址
|
|
Future<String?> getConnectedDeviceAddress() async {
|
|
try {
|
|
return await _methodChannel.invokeMethod<String>('getConnectedDeviceAddress');
|
|
} catch (e) {
|
|
print('获取已连接设备地址失败: $e');
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/// 获取电池电量
|
|
Future<int> getBatteryLevel() async {
|
|
try {
|
|
final result = await _methodChannel.invokeMethod<int>('getBatteryLevel');
|
|
return result ?? 0;
|
|
} catch (e) {
|
|
print('获取电池电量失败: $e');
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/// 获取固件版本
|
|
Future<String?> getFirmwareVersion() async {
|
|
try {
|
|
return await _methodChannel.invokeMethod<String>('getFirmwareVersion');
|
|
} catch (e) {
|
|
print('获取固件版本失败: $e');
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/// 获取设备版本信息
|
|
Future<bool> getDeviceVersion() async {
|
|
try {
|
|
final result = await _methodChannel.invokeMethod<bool>('getDeviceVersion');
|
|
return result ?? false;
|
|
} catch (e) {
|
|
print('获取设备版本失败: $e');
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/// 获取电池信息
|
|
Future<bool> getBatteryInfo() async {
|
|
try {
|
|
final result = await _methodChannel.invokeMethod<bool>('getBatteryInfo');
|
|
return result ?? false;
|
|
} catch (e) {
|
|
print('获取电池信息失败: $e');
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/// 打开编码器
|
|
Future<bool> openEncoder() async {
|
|
try {
|
|
final result = await _methodChannel.invokeMethod<bool>('openEncoder');
|
|
return result ?? false;
|
|
} catch (e) {
|
|
print('打开编码器失败: $e');
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/// 打开解码器
|
|
Future<bool> openDecoder() async {
|
|
try {
|
|
final result = await _methodChannel.invokeMethod<bool>('openDecoder');
|
|
return result ?? false;
|
|
} catch (e) {
|
|
print('打开解码器失败: $e');
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/// 关闭编解码器
|
|
Future<bool> closeCodec() async {
|
|
try {
|
|
final result = await _methodChannel.invokeMethod<bool>('closeCodec');
|
|
return result ?? false;
|
|
} catch (e) {
|
|
print('关闭编解码器失败: $e');
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/// 播放A2DP
|
|
Future<bool> playA2DP() async {
|
|
try {
|
|
final result = await _methodChannel.invokeMethod<bool>('playA2DP');
|
|
return result ?? false;
|
|
} catch (e) {
|
|
print('播放A2DP失败: $e');
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/// 增加音量
|
|
Future<bool> volumeUp() async {
|
|
try {
|
|
final result = await _methodChannel.invokeMethod<bool>('volumeUp');
|
|
return result ?? false;
|
|
} catch (e) {
|
|
print('增加音量失败: $e');
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/// 减小音量
|
|
Future<bool> volumeDown() async {
|
|
try {
|
|
final result = await _methodChannel.invokeMethod<bool>('volumeDown');
|
|
return result ?? false;
|
|
} catch (e) {
|
|
print('减小音量失败: $e');
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/// 发送音频数据
|
|
Future<bool> sendAudioData(Uint8List data) async {
|
|
try {
|
|
final result = await _methodChannel.invokeMethod<bool>(
|
|
'sendAudioData',
|
|
{'data': data},
|
|
);
|
|
return result ?? false;
|
|
} catch (e) {
|
|
print('发送音频数据失败: $e');
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/// 清理资源
|
|
Future<bool> cleanup() async {
|
|
try {
|
|
final result = await _methodChannel.invokeMethod<bool>('cleanup');
|
|
return result ?? false;
|
|
} catch (e) {
|
|
print('清理资源失败: $e');
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/// 获取已扫描到的设备列表
|
|
Future<List<BleDevice>> getScannedDevices() async {
|
|
try {
|
|
final result = await _methodChannel.invokeMethod<List<dynamic>>('getScannedDevices');
|
|
if (result != null) {
|
|
return result.map((item) => BleDevice.fromMap(Map<String, dynamic>.from(item as Map))).toList();
|
|
}
|
|
return [];
|
|
} catch (e) {
|
|
print('获取扫描设备列表失败: $e');
|
|
return [];
|
|
}
|
|
}
|
|
|
|
/// 获取平台版本
|
|
Future<String?> getPlatformVersion() async {
|
|
try {
|
|
return await _methodChannel.invokeMethod<String>('getPlatformVersion');
|
|
} catch (e) {
|
|
print('获取平台版本失败: $e');
|
|
return null;
|
|
}
|
|
}
|
|
}
|