import 'dart:async'; import 'dart:typed_data'; import 'package:flutter/services.dart'; /// 设备类,代表一个BLE设备 class BleDevice { final String id; final String name; final int rssi; BleDevice({required this.id, required this.name, required this.rssi}); factory BleDevice.fromJson(Map json) { return BleDevice( id: json['id'] as String, name: json['name'] as String? ?? 'Unknown Device', rssi: json['rssi'] as int? ?? 0, ); } Map toJson() { return { 'id': id, 'name': name, 'rssi': rssi, }; } @override String toString() => 'BleDevice(id: $id, name: $name, rssi: $rssi)'; } /// 连接状态 enum ConnectionState { disconnected, connecting, connected, disconnecting, error } /// 事件类型 class EventType { static const String audioData = 'audio_data'; static const String connectionState = 'connection_state'; static const String scanResult = 'scan_result'; static const String wakeupSignal = 'wakeup_signal'; } /// 杰理BLE插件主类 class Jieli { static const MethodChannel _methodChannel = MethodChannel('com.yunqiinnovation.jieli'); static const EventChannel _eventChannel = EventChannel('com.yunqiinnovation.jieli/events'); /// 扫描结果流 static final StreamController> _scanController = StreamController>.broadcast(); Stream> get scanResults => _scanController.stream; /// 连接状态变化流 static final StreamController _connectionController = StreamController.broadcast(); Stream get connectionState => _connectionController.stream; /// 音频数据流 static final StreamController _audioController = StreamController.broadcast(); Stream get audioDataStream => _audioController.stream; /// 唤醒信号流 static final StreamController _wakeupController = StreamController.broadcast(); Stream get wakeupStream => _wakeupController.stream; /// 事件流订阅 StreamSubscription? _eventSubscription; /// 构造函数,初始化平台通道和事件监听 Jieli() { _setupEventListener(); } /// 设置事件监听器 void _setupEventListener() { _eventSubscription = _eventChannel.receiveBroadcastStream().listen(_handleEvent); } /// 处理来自原生平台的事件 void _handleEvent(dynamic event) { if (event is! Map) return; final eventData = Map.from(event); final String eventType = eventData['type'] as String; final dynamic data = eventData['data']; switch (eventType) { case EventType.scanResult: if (data is List) { final deviceList = data .map((item) => BleDevice.fromJson(Map.from(item))) .toList(); _scanController.add(deviceList); } break; case EventType.connectionState: if (data is int && data >= 0 && data < ConnectionState.values.length) { _connectionController.add(ConnectionState.values[data]); } break; case EventType.audioData: if (data is Uint8List) { _audioController.add(data); } break; case EventType.wakeupSignal: if (data is bool) { _wakeupController.add(data); } break; } } /// 开始扫描BLE设备 Future startScan({int scanTimeout = 10000}) async { final result = await _methodChannel.invokeMethod( 'startScan', {'scanTimeout': scanTimeout}, ); return result ?? false; } /// 停止扫描BLE设备 Future stopScan() async { final result = await _methodChannel.invokeMethod('stopScan'); return result ?? false; } /// 连接到指定设备 Future connect(String deviceId) async { final result = await _methodChannel.invokeMethod( 'connect', {'deviceId': deviceId}, ); return result ?? false; } /// 断开连接 Future disconnect() async { final result = await _methodChannel.invokeMethod('disconnect'); return result ?? false; } /// 发送开始录音命令 Future sendStartRecordingCommand() async { final result = await _methodChannel.invokeMethod('sendStartRecordingCommand'); return result ?? false; } /// 发送停止录音命令 Future sendStopRecordingCommand() async { final result = await _methodChannel.invokeMethod('sendStopRecordingCommand'); return result ?? false; } /// 关闭插件,释放资源 Future dispose() async { await stopScan(); await disconnect(); _eventSubscription?.cancel(); _scanController.close(); _connectionController.close(); _audioController.close(); _wakeupController.close(); } /// 检查蓝牙是否已启用 Future isBluetoothEnabled() async { final result = await _methodChannel.invokeMethod('isBluetoothEnabled'); return result ?? false; } /// 请求打开蓝牙 Future requestBluetoothEnable() async { final result = await _methodChannel.invokeMethod('requestBluetoothEnable'); return result ?? false; } }