diff --git a/lib/data/services/ble_manager.dart b/lib/data/services/ble_manager.dart index acf36ac6b..f379b53d8 100644 --- a/lib/data/services/ble_manager.dart +++ b/lib/data/services/ble_manager.dart @@ -184,6 +184,19 @@ class BleManager extends GetxService { state == BleConnectionState.error) { Logger.info('BLE设备已断开或发生错误'); _isConnected.value = false; + // 更新设备信息Add commentMore actions + _updateDeviceInfoIfNeeded( + batteryLevel: 0, + leftEarBattery: 0, + rightEarBattery: 0, + caseBattery: 0, + leftEarCharging: false, + rightEarCharging: false, + caseCharging: false, + firmwareVersion: '', + firmwareProductId: '', + firmwareProductColor: '', + ); // 断开连接时不清除设备信息,保留最后的电量信息 // 只有在需要完全清除设备时才重置信息 } @@ -405,7 +418,7 @@ class BleManager extends GetxService { // 使用保存的设备信息创建新的DeviceInfo对象 String deviceAddress = _currentDeviceAddress ?? ""; String deviceName = _currentDeviceName ?? ""; - + Logger.info('创建新的设备信息对象: address=$deviceAddress, name=$deviceName'); deviceInfo.value = DeviceInfo( address: deviceAddress, @@ -442,7 +455,7 @@ class BleManager extends GetxService { // 创建初始设备信息对象 String deviceAddress = _currentDeviceAddress ?? ""; String deviceName = _currentDeviceName ?? ""; - + Logger.info('初始化设备信息对象: address=$deviceAddress, name=$deviceName'); deviceInfo.value = DeviceInfo( address: deviceAddress, @@ -467,8 +480,9 @@ class BleManager extends GetxService { _bleService.getProductId(), ]); - Logger.info('设备信息获取结果: 电池=${results[0]}, 版本=${results[1]}, 产品ID=${results[2]}'); - + Logger.info( + '设备信息获取结果: 电池=${results[0]}, 版本=${results[1]}, 产品ID=${results[2]}'); + if (!results[0]) { Logger.warning('电池信息获取指令发送失败'); } @@ -788,9 +802,10 @@ class BleManager extends GetxService { // 从蓝牙管理器获取设备名称 final bluetoothManager = Get.find(); _currentDeviceName = bluetoothManager.currentDevice?.name ?? ""; - Logger.info('保存设备信息: address=$_currentDeviceAddress, name=$_currentDeviceName'); + Logger.info( + '保存设备信息: address=$_currentDeviceAddress, name=$_currentDeviceName'); } - + return await _bleService.registerBackgroundScan(macAddress: macAddress); } catch (e) { Logger.error('注册后台扫描失败: ${e.toString()}'); diff --git a/lib/data/services/speech_impl/volcano_tts_service.dart b/lib/data/services/speech_impl/volcano_tts_service.dart index 402aac5b8..f84ed6b0a 100644 --- a/lib/data/services/speech_impl/volcano_tts_service.dart +++ b/lib/data/services/speech_impl/volcano_tts_service.dart @@ -9,82 +9,84 @@ import 'package:flutter_dotenv/flutter_dotenv.dart'; class VolcanoTtsService extends GetxService implements TtsService { /// 方法通道 static const MethodChannel _channel = MethodChannel('volcano_speech/tts'); - + /// 事件通道 - static const EventChannel _eventChannel = EventChannel('volcano_speech/tts_events'); - + static const EventChannel _eventChannel = + EventChannel('volcano_speech/tts_events'); + /// 音频数据通道 - static const EventChannel _audioDataChannel = EventChannel('volcano_speech/tts_audio_data'); - + static const EventChannel _audioDataChannel = + EventChannel('volcano_speech/tts_audio_data'); + /// 事件流控制器 - final StreamController _eventStreamController = + final StreamController _eventStreamController = StreamController.broadcast(); - + /// 音频数据流控制器 - final StreamController _audioDataStreamController = + final StreamController _audioDataStreamController = StreamController.broadcast(); - + /// 事件处理器 StreamSubscription? _eventSubscription; - + /// 音频数据处理器 StreamSubscription? _audioDataSubscription; - + /// 是否初始化完成 bool _isInitialized = false; - + /// 当前语音名称 String _currentVoice = "zh_female_qingxin"; - + /// 是否启用音频回调 bool _isAudioCallbackEnabled = false; - + @override String get currentVoice => _currentVoice; - + @override bool get isSpeaking => _isSpeaking; - + /// 是否正在播放 bool _isSpeaking = false; - + @override Stream get onEvent => _eventStreamController.stream; - + /// 音频数据流 Stream get onAudioData => _audioDataStreamController.stream; - + /// 构造函数 VolcanoTtsService() { _setupEventListeners(); _setupAudioDataListeners(); } - + /// 设置事件监听 void _setupEventListeners() { _eventSubscription = _eventChannel.receiveBroadcastStream().listen( (dynamic event) { if (event is! Map) return; - + final Map eventMap = event; final String? type = eventMap['type'] as String?; - + switch (type) { case 'started': _isSpeaking = true; _eventStreamController.add(TtsEvent(type: TtsEventType.started)); break; - + case 'completed': _isSpeaking = false; _eventStreamController.add(TtsEvent(type: TtsEventType.completed)); break; - + case 'canceled': _isSpeaking = false; _eventStreamController.add(TtsEvent(type: TtsEventType.canceled)); break; - + case 'error': _isSpeaking = false; final errorCode = eventMap['errorCode'] as String? ?? "-1"; @@ -105,7 +107,7 @@ class VolcanoTtsService extends GetxService implements TtsService { }, ); } - + /// 设置音频数据监听 void _setupAudioDataListeners() { _audioDataSubscription = _audioDataChannel.receiveBroadcastStream().listen( @@ -119,32 +121,34 @@ class VolcanoTtsService extends GetxService implements TtsService { }, ); } - + @override Future initialize({List? supportedLanguages}) async { if (_isInitialized) return true; - + try { // 从.env文件读取配置 final appId = dotenv.env['VOLCANO_SPEECH_APP_ID'] ?? ''; final appToken = dotenv.env['VOLCANO_SPEECH_APP_TOKEN'] ?? ''; - + if (appId.isEmpty || appToken.isEmpty) { - debugPrint('火山语音初始化失败:缺少VOLCANO_SPEECH_APP_ID或VOLCANO_SPEECH_APP_TOKEN环境变量'); + debugPrint( + '火山语音初始化失败:缺少VOLCANO_SPEECH_APP_ID或VOLCANO_SPEECH_APP_TOKEN环境变量'); return false; } - + // 初始化火山语音TTS final result = await _channel.invokeMethod('initialize', { - 'appId': appId, - 'appToken': appToken, - }) ?? false; - + 'appId': appId, + 'appToken': appToken, + }) ?? + false; + if (!result) { debugPrint('火山语音TTS初始化失败'); return false; } - + _isInitialized = true; return true; } catch (e) { @@ -160,16 +164,17 @@ class VolcanoTtsService extends GetxService implements TtsService { } return true; } - + @override Future setVoice(String voiceName) async { if (!await _ensureInitialized()) return false; - + try { final result = await _channel.invokeMethod('setVoice', { - 'voice': voiceName, - }) ?? false; - + 'voice': voiceName, + }) ?? + false; + if (result) { _currentVoice = voiceName; } @@ -179,16 +184,17 @@ class VolcanoTtsService extends GetxService implements TtsService { return false; } } - + /// 设置是否启用音频数据回调 Future setEnableAudioCallback(bool enable) async { if (!await _ensureInitialized()) return false; - + try { final result = await _channel.invokeMethod('enableAudioCallback', { - 'enable': enable, - }) ?? false; - + 'enable': enable, + }) ?? + false; + if (result) { _isAudioCallbackEnabled = enable; } @@ -198,17 +204,18 @@ class VolcanoTtsService extends GetxService implements TtsService { return false; } } - + @override Future speakOnce(String text) async { if (!await _ensureInitialized()) return false; - + try { // 单次播放 final result = await _channel.invokeMethod('speakOnce', { - 'text': text, - }) ?? false; - + 'text': text, + }) ?? + false; + return result; } catch (e) { debugPrint('单次播放失败:$e'); @@ -216,17 +223,18 @@ class VolcanoTtsService extends GetxService implements TtsService { return false; } } - + @override Future speakStream(String text) async { if (!await _ensureInitialized()) return false; - + try { // 流式添加文本 final result = await _channel.invokeMethod('speakStream', { - 'text': text, - }) ?? false; - + 'text': text, + }) ?? + false; + return result; } catch (e) { debugPrint('流式播放失败:$e'); @@ -234,11 +242,11 @@ class VolcanoTtsService extends GetxService implements TtsService { return false; } } - + @override Future flushStream() async { if (!await _ensureInitialized()) return false; - + try { // 刷新流式文本 return await _channel.invokeMethod('flushStream') ?? false; @@ -247,11 +255,11 @@ class VolcanoTtsService extends GetxService implements TtsService { return false; } } - + @override Future stop() async { if (!_isInitialized) return false; - + try { return await _channel.invokeMethod('stop') ?? false; } catch (e) { @@ -259,27 +267,33 @@ class VolcanoTtsService extends GetxService implements TtsService { return false; } } - + @override Future dispose() async { if (_isSpeaking) { await stop(); } - + await _eventSubscription?.cancel(); await _audioDataSubscription?.cancel(); - + if (!_eventStreamController.isClosed) { await _eventStreamController.close(); } - + if (!_audioDataStreamController.isClosed) { await _audioDataStreamController.close(); } - + if (_isInitialized) { await _channel.invokeMethod('release'); _isInitialized = false; } } -} \ No newline at end of file + + @override + Future setAudioOutputDevice(int type) { + // TODO: implement setAudioOutputDevice + throw UnimplementedError(); + } +} diff --git a/lib/modules/ota_upgrade/controllers/ota_upgrade_controller.dart b/lib/modules/ota_upgrade/controllers/ota_upgrade_controller.dart index c175e332d..d98324272 100644 --- a/lib/modules/ota_upgrade/controllers/ota_upgrade_controller.dart +++ b/lib/modules/ota_upgrade/controllers/ota_upgrade_controller.dart @@ -327,7 +327,7 @@ class OtaUpgradeController extends GetxController { } if (existingSameFile) { - Get.snackbar('文件已存在', '跳过下载,直接更新'); + //Get.snackbar('文件已存在', '跳过下载,直接更新'); startUpgrading(savePath); return; @@ -367,7 +367,7 @@ class OtaUpgradeController extends GetxController { ); File file = File(savePath); if (await file.exists()) { - Get.snackbar('下载完成', '文件已保存至: $savePath'); + // Get.snackbar('下载完成', '文件已保存至: $savePath'); } else { Get.snackbar('错误', '文件未找到,请检查写入权限'); } diff --git a/lib/modules/settings/views/settings_view.dart b/lib/modules/settings/views/settings_view.dart index 4cc3e7360..6c33ea86e 100644 --- a/lib/modules/settings/views/settings_view.dart +++ b/lib/modules/settings/views/settings_view.dart @@ -525,114 +525,114 @@ class SettingsView extends GetView { ? Colors.white.withOpacity(0.1) : Colors.grey[200]), - // 杰理测试 - _buildSimpleNavigationSetting( - title: '杰理蓝牙测试', - subtitle: '测试杰理蓝牙设备扫描、连接和功能', - icon: Icons.bluetooth, - iconBgColor: isDarkMode - ? Colors.blue[900]!.withOpacity(0.3) - : Colors.blue[100]!, - iconColor: - isDarkMode ? Colors.blue[300]! : Colors.blue[600]!, - titleColor: isDarkMode ? Colors.white : null, - subtitleColor: isDarkMode ? Colors.white70 : null, - onTap: () { - Get.toNamed(Routes.jieliTest); - }, - isDarkMode: isDarkMode, - ), - Divider( - height: 1, - color: isDarkMode - ? Colors.white.withOpacity(0.1) - : Colors.grey[200]), - // 面对面翻译测试 - _buildSimpleNavigationSetting( - title: '面对面翻译测试', - subtitle: '测试面对面翻译', - icon: Icons.bluetooth_searching, - iconBgColor: isDarkMode - ? Colors.green[900]!.withOpacity(0.3) - : Colors.green[100]!, - iconColor: - isDarkMode ? Colors.green[300]! : Colors.green[600]!, - titleColor: isDarkMode ? Colors.white : null, - subtitleColor: isDarkMode ? Colors.white70 : null, - onTap: () { - Get.toNamed(Routes.ftftranslation); - }, - isDarkMode: isDarkMode, - ), - Divider( - height: 1, - color: isDarkMode - ? Colors.white.withOpacity(0.1) - : Colors.grey[200]), - // BLE测试 - _buildSimpleNavigationSetting( - title: 'opus解码测试', - subtitle: '测试opus解码', - icon: Icons.bluetooth_searching, - iconBgColor: isDarkMode - ? Colors.green[900]!.withOpacity(0.3) - : Colors.green[100]!, - iconColor: - isDarkMode ? Colors.green[300]! : Colors.green[600]!, - titleColor: isDarkMode ? Colors.white : null, - subtitleColor: isDarkMode ? Colors.white70 : null, - onTap: () { - Get.toNamed(Routes.opusTest); - }, - isDarkMode: isDarkMode, - ), - Divider( - height: 1, - color: isDarkMode - ? Colors.white.withOpacity(0.1) - : Colors.grey[200]), + // // 杰理测试 + // _buildSimpleNavigationSetting( + // title: '杰理蓝牙测试', + // subtitle: '测试杰理蓝牙设备扫描、连接和功能', + // icon: Icons.bluetooth, + // iconBgColor: isDarkMode + // ? Colors.blue[900]!.withOpacity(0.3) + // : Colors.blue[100]!, + // iconColor: + // isDarkMode ? Colors.blue[300]! : Colors.blue[600]!, + // titleColor: isDarkMode ? Colors.white : null, + // subtitleColor: isDarkMode ? Colors.white70 : null, + // onTap: () { + // Get.toNamed(Routes.jieliTest); + // }, + // isDarkMode: isDarkMode, + // ), + // Divider( + // height: 1, + // color: isDarkMode + // ? Colors.white.withOpacity(0.1) + // : Colors.grey[200]), + // // 面对面翻译测试 + // _buildSimpleNavigationSetting( + // title: '面对面翻译测试', + // subtitle: '测试面对面翻译', + // icon: Icons.bluetooth_searching, + // iconBgColor: isDarkMode + // ? Colors.green[900]!.withOpacity(0.3) + // : Colors.green[100]!, + // iconColor: + // isDarkMode ? Colors.green[300]! : Colors.green[600]!, + // titleColor: isDarkMode ? Colors.white : null, + // subtitleColor: isDarkMode ? Colors.white70 : null, + // onTap: () { + // Get.toNamed(Routes.ftftranslation); + // }, + // isDarkMode: isDarkMode, + // ), + // Divider( + // height: 1, + // color: isDarkMode + // ? Colors.white.withOpacity(0.1) + // : Colors.grey[200]), + // // BLE测试 + // _buildSimpleNavigationSetting( + // title: 'opus解码测试', + // subtitle: '测试opus解码', + // icon: Icons.bluetooth_searching, + // iconBgColor: isDarkMode + // ? Colors.green[900]!.withOpacity(0.3) + // : Colors.green[100]!, + // iconColor: + // isDarkMode ? Colors.green[300]! : Colors.green[600]!, + // titleColor: isDarkMode ? Colors.white : null, + // subtitleColor: isDarkMode ? Colors.white70 : null, + // onTap: () { + // Get.toNamed(Routes.opusTest); + // }, + // isDarkMode: isDarkMode, + // ), + // Divider( + // height: 1, + // color: isDarkMode + // ? Colors.white.withOpacity(0.1) + // : Colors.grey[200]), // BLE测试 - _buildSimpleNavigationSetting( - title: 'BLE蓝牙测试', - subtitle: '测试BLE蓝牙设备扫描、连接和功能', - icon: Icons.bluetooth_searching, - iconBgColor: isDarkMode - ? Colors.green[900]!.withOpacity(0.3) - : Colors.green[100]!, - iconColor: - isDarkMode ? Colors.green[300]! : Colors.green[600]!, - titleColor: isDarkMode ? Colors.white : null, - subtitleColor: isDarkMode ? Colors.white70 : null, - onTap: () { - Get.toNamed(Routes.bleTest); - }, - isDarkMode: isDarkMode, - ), - Divider( - height: 1, - color: isDarkMode - ? Colors.white.withOpacity(0.1) - : Colors.grey[200]), + // _buildSimpleNavigationSetting( + // title: 'BLE蓝牙测试', + // subtitle: '测试BLE蓝牙设备扫描、连接和功能', + // icon: Icons.bluetooth_searching, + // iconBgColor: isDarkMode + // ? Colors.green[900]!.withOpacity(0.3) + // : Colors.green[100]!, + // iconColor: + // isDarkMode ? Colors.green[300]! : Colors.green[600]!, + // titleColor: isDarkMode ? Colors.white : null, + // subtitleColor: isDarkMode ? Colors.white70 : null, + // onTap: () { + // Get.toNamed(Routes.bleTest); + // }, + // isDarkMode: isDarkMode, + // ), + // Divider( + // height: 1, + // color: isDarkMode + // ? Colors.white.withOpacity(0.1) + // : Colors.grey[200]), - // 火山语音测试 - _buildSimpleNavigationSetting( - title: '语音测试', - subtitle: '测试语音识别和合成功能,支持多种服务实现', - icon: Icons.record_voice_over, - iconBgColor: isDarkMode - ? Colors.orange[900]!.withOpacity(0.3) - : Colors.orange[100]!, - iconColor: - isDarkMode ? Colors.orange[300]! : Colors.orange[600]!, - onTap: () => Get.toNamed(Routes.speechTest), - isDarkMode: isDarkMode, - ), - Divider( - height: 1, - color: isDarkMode - ? Colors.white.withOpacity(0.1) - : Colors.grey[200]), + // // 火山语音测试 + // _buildSimpleNavigationSetting( + // title: '语音测试', + // subtitle: '测试语音识别和合成功能,支持多种服务实现', + // icon: Icons.record_voice_over, + // iconBgColor: isDarkMode + // ? Colors.orange[900]!.withOpacity(0.3) + // : Colors.orange[100]!, + // iconColor: + // isDarkMode ? Colors.orange[300]! : Colors.orange[600]!, + // onTap: () => Get.toNamed(Routes.speechTest), + // isDarkMode: isDarkMode, + // ), + // Divider( + // height: 1, + // color: isDarkMode + // ? Colors.white.withOpacity(0.1) + // : Colors.grey[200]), // 测试ble耳机固件版本升级 _buildSimpleNavigationSetting( title: 'ota升级', diff --git a/local_plugins/ota/android/src/main/kotlin/com/example/ota/OtaPlugin.kt b/local_plugins/ota/android/src/main/kotlin/com/example/ota/OtaPlugin.kt index c2ddcfe1f..8f68906de 100644 --- a/local_plugins/ota/android/src/main/kotlin/com/example/ota/OtaPlugin.kt +++ b/local_plugins/ota/android/src/main/kotlin/com/example/ota/OtaPlugin.kt @@ -146,9 +146,10 @@ class OtaPlugin : BleService.Callback, FlutterPlugin, MethodCallHandler { private lateinit var channel: MethodChannel private lateinit var context: Context private lateinit var otaDevice: BluetoothDevice - private lateinit var bluetoothHelper: BluetoothHelper + private var bluetoothHelper: BluetoothHelper? = null + + private var otaManager: OTAManager? = null - private lateinit var otaManager: OTAManager private lateinit var filePath: String//升级文件路径 private var isDisconnect = false//是否主动断开 private var streamSink: EventChannel.EventSink? = null @@ -216,8 +217,8 @@ class OtaPlugin : BleService.Callback, FlutterPlugin, MethodCallHandler { private val bluetoothCallback = object : BtEventCallback() { override fun onConnection(device: BluetoothDevice?, status: Int) { - if ((status == StateCode.CONNECTION_FAILED || status == StateCode.CONNECTION_DISCONNECT) && bluetoothHelper.getConnectedDevice() != null) { - bluetoothHelper.disconnectDevice(bluetoothHelper.getConnectedDevice()) + if ((status == StateCode.CONNECTION_FAILED || status == StateCode.CONNECTION_DISCONNECT) && bluetoothHelper?.getConnectedDevice() != null) { + bluetoothHelper?.disconnectDevice(bluetoothHelper?.getConnectedDevice()) } otaDevice = device!! @@ -238,10 +239,10 @@ class OtaPlugin : BleService.Callback, FlutterPlugin, MethodCallHandler { ) ) Log.d("TAG", "startUpgrade,filePath=${filePath}") - otaManager.bluetoothOption.firmwareFilePath = filePath - otaManager.startOTA( + otaManager?.bluetoothOption?.firmwareFilePath = filePath + otaManager?.startOTA( CustomUpdateCallback( - otaManager.bluetoothHelper.getConnectedDevice(), streamSink + otaManager?.bluetoothHelper?.getConnectedDevice(), streamSink ) ) } @@ -280,7 +281,7 @@ class OtaPlugin : BleService.Callback, FlutterPlugin, MethodCallHandler { otaManager = OTAManager(context) bluetoothHelper = BluetoothHelper(context) // 如果需要额外的初始化逻辑,请在这里添加 - otaManager.registerBluetoothCallback(bluetoothCallback) + otaManager!!.registerBluetoothCallback(bluetoothCallback) // 初始化BleService // 2. 初始化BleService并注册回调 if (BleService.initialize(context)) { @@ -335,10 +336,12 @@ class OtaPlugin : BleService.Callback, FlutterPlugin, MethodCallHandler { } else if (call.method == "onOtaClose") { BleService.isEnterOta = false isDisconnect = false + if(otaManager!= null) { + otaManager?.bluetoothHelper?.stopScan() otaManager?.bluetoothHelper?.disconnectDevice(otaDevice) - + } result.success("") } else { result.notImplemented() @@ -370,7 +373,7 @@ class OtaPlugin : BleService.Callback, FlutterPlugin, MethodCallHandler { if (state == BleConst.STATE_DISCONNECTED && isDisconnect) { Log.d("TAG", "Ble设备已断开,onConnectionStateChanged") Handler(Looper.getMainLooper()).postDelayed({ - otaManager.bluetoothHelper.startScan(otaDevice) + otaManager?.bluetoothHelper?.startScan(otaDevice) }, 2000) // 2000 毫秒 = 2 秒 } }