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.
298 lines
8.3 KiB
298 lines
8.3 KiB
import 'dart:async';
|
|
import 'dart:typed_data';
|
|
import 'package:get/get.dart';
|
|
import 'package:logger/logger.dart';
|
|
import 'package:jieli/jieli.dart';
|
|
|
|
/// 杰理测试控制器
|
|
class JieliTestController extends GetxController {
|
|
static final Logger logger = Logger();
|
|
|
|
// Jieli插件实例
|
|
late Jieli jieli;
|
|
|
|
// 状态变量
|
|
var isScanning = false.obs;
|
|
var deviceInfo = '未获取'.obs;
|
|
var connectStatus = '未连接'.obs;
|
|
var currentDevice = Rx<BleDevice?>(null);
|
|
var scannedDevices = RxList<BleDevice>([]);
|
|
var wakeupReceived = false.obs;
|
|
var audioDataSize = 0.obs;
|
|
var receivedFrames = 0.obs;
|
|
var audioDataReceiving = false.obs;
|
|
|
|
// 事件订阅
|
|
StreamSubscription? _scanSubscription;
|
|
StreamSubscription? _connectionSubscription;
|
|
StreamSubscription? _wakeupSubscription;
|
|
StreamSubscription? _audioSubscription;
|
|
|
|
@override
|
|
void onInit() {
|
|
super.onInit();
|
|
logger.d('JieliTestController 初始化');
|
|
_initializeJieli();
|
|
}
|
|
|
|
@override
|
|
void onClose() {
|
|
logger.d('JieliTestController 关闭');
|
|
_cancelAllSubscriptions();
|
|
_disposeJieli();
|
|
super.onClose();
|
|
}
|
|
|
|
/// 取消所有订阅
|
|
void _cancelAllSubscriptions() {
|
|
_scanSubscription?.cancel();
|
|
_connectionSubscription?.cancel();
|
|
_wakeupSubscription?.cancel();
|
|
_audioSubscription?.cancel();
|
|
}
|
|
|
|
/// 初始化杰理SDK
|
|
void _initializeJieli() {
|
|
try {
|
|
logger.d('初始化Jieli插件');
|
|
jieli = Jieli();
|
|
_setupEventListeners();
|
|
} catch (e) {
|
|
logger.e('初始化Jieli插件错误', error: e);
|
|
}
|
|
}
|
|
|
|
/// 设置事件监听器
|
|
void _setupEventListeners() {
|
|
// 监听扫描结果
|
|
_scanSubscription = jieli.scanResults.listen((devices) {
|
|
// logger.d('扫描到设备: ${devices.length}个');
|
|
scannedDevices.assignAll(devices);
|
|
});
|
|
|
|
// 监听连接状态
|
|
_connectionSubscription = jieli.connectionState.listen((state) {
|
|
logger.d('连接状态变化: $state');
|
|
switch (state) {
|
|
case ConnectionState.connected:
|
|
connectStatus.value = '已连接';
|
|
break;
|
|
case ConnectionState.disconnected:
|
|
connectStatus.value = '已断开';
|
|
currentDevice.value = null;
|
|
break;
|
|
case ConnectionState.connecting:
|
|
connectStatus.value = '连接中...';
|
|
break;
|
|
case ConnectionState.disconnecting:
|
|
connectStatus.value = '断开中...';
|
|
break;
|
|
case ConnectionState.error:
|
|
connectStatus.value = '连接错误';
|
|
break;
|
|
}
|
|
});
|
|
|
|
// 监听唤醒信号
|
|
_wakeupSubscription = jieli.wakeupStream.listen((wakeup) {
|
|
logger.d('收到唤醒信号');
|
|
wakeupReceived.value = true;
|
|
Get.snackbar('收到唤醒信号', '耳机发送了唤醒信号');
|
|
});
|
|
|
|
// 监听音频数据
|
|
_audioSubscription = jieli.audioDataStream.listen((data) {
|
|
// 更新音频数据统计
|
|
audioDataSize.value += data.length;
|
|
receivedFrames.value++;
|
|
audioDataReceiving.value = true;
|
|
|
|
// 每100帧更新一次日志,避免日志过多
|
|
if (receivedFrames.value % 100 == 0) {
|
|
logger.d('已接收音频数据: ${audioDataSize.value} 字节, ${receivedFrames.value} 帧');
|
|
}
|
|
});
|
|
}
|
|
|
|
/// 检查蓝牙状态
|
|
Future<bool> checkBluetoothStatus() async {
|
|
try {
|
|
final isEnabled = await jieli.isBluetoothEnabled();
|
|
if (!isEnabled) {
|
|
logger.d('蓝牙未启用,请求启用蓝牙');
|
|
return await jieli.requestBluetoothEnable();
|
|
}
|
|
return true;
|
|
} catch (e) {
|
|
logger.e('检查蓝牙状态错误', error: e);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/// 开始扫描设备
|
|
Future<void> startScan() async {
|
|
if (isScanning.value) return;
|
|
|
|
final bluetoothReady = await checkBluetoothStatus();
|
|
if (!bluetoothReady) {
|
|
Get.snackbar('蓝牙未启用', '请启用蓝牙后再试');
|
|
return;
|
|
}
|
|
|
|
try {
|
|
logger.d('开始扫描设备');
|
|
isScanning.value = true;
|
|
|
|
// 清空之前的设备列表
|
|
scannedDevices.clear();
|
|
|
|
// 开始扫描,10秒后自动停止
|
|
final success = await jieli.startScan(scanTimeout: 10000);
|
|
|
|
if (!success) {
|
|
logger.e('启动扫描失败');
|
|
isScanning.value = false;
|
|
Get.snackbar('扫描失败', '启动扫描设备失败,请检查蓝牙权限');
|
|
}
|
|
|
|
// 10秒后自动更新UI状态
|
|
Future.delayed(Duration(milliseconds: 10000), () {
|
|
isScanning.value = false;
|
|
});
|
|
|
|
} catch (e) {
|
|
logger.e('扫描设备错误', error: e);
|
|
isScanning.value = false;
|
|
Get.snackbar('扫描错误', e.toString());
|
|
}
|
|
}
|
|
|
|
/// 停止扫描
|
|
Future<void> stopScan() async {
|
|
if (!isScanning.value) return;
|
|
|
|
try {
|
|
logger.d('停止扫描');
|
|
await jieli.stopScan();
|
|
isScanning.value = false;
|
|
} catch (e) {
|
|
logger.e('停止扫描错误', error: e);
|
|
}
|
|
}
|
|
|
|
/// 连接到指定设备
|
|
Future<void> connectToDevice(BleDevice device) async {
|
|
try {
|
|
// 先断开当前连接
|
|
if (currentDevice.value != null) {
|
|
await disconnectDevice();
|
|
}
|
|
|
|
// 更新UI状态
|
|
connectStatus.value = '连接中...';
|
|
logger.d('连接设备: ${device.name} (${device.id})');
|
|
|
|
// 重置音频和唤醒相关的状态
|
|
wakeupReceived.value = false;
|
|
audioDataSize.value = 0;
|
|
receivedFrames.value = 0;
|
|
audioDataReceiving.value = false;
|
|
|
|
// 连接设备
|
|
final success = await jieli.connect(device.id);
|
|
|
|
if (success) {
|
|
// 连接成功,更新当前设备
|
|
currentDevice.value = device;
|
|
deviceInfo.value = '设备名称: ${device.name}\n设备ID: ${device.id}\nRSSI: ${device.rssi} dBm';
|
|
logger.d('设备连接成功');
|
|
} else {
|
|
deviceInfo.value = '连接失败';
|
|
logger.d('设备连接失败');
|
|
}
|
|
} catch (e) {
|
|
deviceInfo.value = '连接错误: ${e.toString()}';
|
|
logger.e('连接设备错误', error: e);
|
|
}
|
|
}
|
|
|
|
/// 断开设备连接
|
|
Future<void> disconnectDevice() async {
|
|
if (currentDevice.value == null) return;
|
|
|
|
try {
|
|
connectStatus.value = '断开中...';
|
|
logger.d('断开设备连接');
|
|
|
|
final success = await jieli.disconnect();
|
|
|
|
if (success) {
|
|
deviceInfo.value = '未连接设备';
|
|
logger.d('设备断开连接成功');
|
|
} else {
|
|
logger.d('设备断开连接失败');
|
|
}
|
|
|
|
} catch (e) {
|
|
logger.e('断开设备错误', error: e);
|
|
}
|
|
}
|
|
|
|
/// 发送开始录音命令
|
|
Future<void> sendStartRecordingCommand() async {
|
|
if (currentDevice.value == null) {
|
|
Get.snackbar('操作失败', '未连接设备');
|
|
return;
|
|
}
|
|
|
|
try {
|
|
logger.d('发送开始录音命令');
|
|
final success = await jieli.sendStartRecordingCommand();
|
|
|
|
if (success) {
|
|
Get.snackbar('命令已发送', '已发送开始录音命令');
|
|
logger.d('开始录音命令发送成功');
|
|
} else {
|
|
Get.snackbar('命令发送失败', '发送开始录音命令失败');
|
|
logger.d('开始录音命令发送失败');
|
|
}
|
|
} catch (e) {
|
|
logger.e('发送开始录音命令错误', error: e);
|
|
Get.snackbar('命令错误', e.toString());
|
|
}
|
|
}
|
|
|
|
/// 发送停止录音命令
|
|
Future<void> sendStopRecordingCommand() async {
|
|
if (currentDevice.value == null) {
|
|
Get.snackbar('操作失败', '未连接设备');
|
|
return;
|
|
}
|
|
|
|
try {
|
|
logger.d('发送停止录音命令');
|
|
final success = await jieli.sendStopRecordingCommand();
|
|
|
|
if (success) {
|
|
Get.snackbar('命令已发送', '已发送停止录音命令');
|
|
logger.d('停止录音命令发送成功');
|
|
} else {
|
|
Get.snackbar('命令发送失败', '发送停止录音命令失败');
|
|
logger.d('停止录音命令发送失败');
|
|
}
|
|
} catch (e) {
|
|
logger.e('发送停止录音命令错误', error: e);
|
|
Get.snackbar('命令错误', e.toString());
|
|
}
|
|
}
|
|
|
|
/// 清理资源
|
|
void _disposeJieli() {
|
|
try {
|
|
logger.d('释放Jieli插件资源');
|
|
jieli.dispose();
|
|
} catch (e) {
|
|
logger.e('释放Jieli插件资源错误', error: e);
|
|
}
|
|
}
|
|
}
|