diff --git a/.cursorrules b/.cursorrules index 00538efc3..99d6aa58c 100644 --- a/.cursorrules +++ b/.cursorrules @@ -7,3 +7,4 @@ 7. 尽可能避免打印日志,仅在出错时记录日志 8. 请使用较小的代码量改动,最安全的方法进行代码编写和优化 9. 请尽可能使用中文回复问题 +10. 重要:不要过度设计!! \ No newline at end of file diff --git a/android/app/src/main/kotlin/com/example/deep_voice/AzureAsrHelper.kt b/android/app/src/main/kotlin/com/example/deep_voice/AzureAsrHelper.kt index 185b9b4dd..185cbf7bd 100644 --- a/android/app/src/main/kotlin/com/example/deep_voice/AzureAsrHelper.kt +++ b/android/app/src/main/kotlin/com/example/deep_voice/AzureAsrHelper.kt @@ -106,8 +106,8 @@ class AzureAsrHelper { try { // 先发送会话开始事件 - Log.d(TAG, "准备开始连续语音识别") - callback.onSessionStarted() + // Log.d(TAG, "准备开始连续语音识别") + // callback.onSessionStarted() // 重新创建识别器 Log.d(TAG, "重新创建识别器") diff --git a/android/app/src/main/kotlin/com/example/deep_voice/MainActivity.kt b/android/app/src/main/kotlin/com/example/deep_voice/MainActivity.kt index 2a073e7b3..f9e9ba74e 100644 --- a/android/app/src/main/kotlin/com/example/deep_voice/MainActivity.kt +++ b/android/app/src/main/kotlin/com/example/deep_voice/MainActivity.kt @@ -69,9 +69,9 @@ class MainActivity: FlutterActivity() { // 使用伴生对象存储配置信息,使其在整个应用中可访问 companion object { // Azure 语音服务配置 - var azureSpeechKey: String = "2K2VOHSuL8ZSEnbKgHSyTwo1ohj7C2gbxyVimsjpVUt6aETxVwlvJQQJ99BCACqBBLyXJ3w3AAAYACOGqCMj" - var azureSpeechRegion: String = "southeastasia" - var volcanoAiApiKey: String = "168deb3d-fd0c-4912-b9f1-aaee5c6743e6" + var azureSpeechKey: String = "" + var azureSpeechRegion: String = "" + var volcanoAiApiKey: String = "" // 标记是否已初始化 var isAzureHelpersInitialized = false @@ -99,9 +99,8 @@ class MainActivity: FlutterActivity() { // 注册广播接收器 registerVoiceInteractionReceiver() - // 自动启动语音交互服务 - Log.d(TAG, "MainActivity 已创建,自动启动 VoiceInteractionService") - autoStartVoiceInteractionService() + // 自动启动语音交互服务功能已被移除 + Log.d(TAG, "MainActivity 已创建") // 确保 Flutter 引擎准备好后立即设置事件通道 flutterEngine?.let { engine -> @@ -497,12 +496,6 @@ class MainActivity: FlutterActivity() { Log.d(TAG, "配置参数: key=${if (key.isNotEmpty()) key.take(5) + "..." else "空"}, region=$region, aiKey=${if (aiKey.isNotEmpty()) aiKey.take(5) + "..." else "空"}") - // 如果配置发生变化,需要重新初始化本地实例 - // if (key.isNotEmpty() && region.isNotEmpty()) { - // Log.d(TAG, "配置已更新,重新初始化本地实例") - // azureAsrHelper.initialize(key, region, "zh-CN") - // azureTtsHelper.initialize(key, region, "zh-CN") - // } // 创建 Intent 并启动服务 val serviceIntent = Intent(this, VoiceInteractionService::class.java) diff --git a/android/app/src/main/kotlin/com/example/deep_voice/VoiceInteractionService.kt b/android/app/src/main/kotlin/com/example/deep_voice/VoiceInteractionService.kt index 3f80fe6cf..f02340bf7 100644 --- a/android/app/src/main/kotlin/com/example/deep_voice/VoiceInteractionService.kt +++ b/android/app/src/main/kotlin/com/example/deep_voice/VoiceInteractionService.kt @@ -375,7 +375,7 @@ class VoiceInteractionService : Service() { return } // 通知 Flutter 语音识别已启动 - notifyRecognitionStarted() + notifyRecognitionStarted() isActive = true isRecognitionActive = true diff --git a/assets/images/explore.png b/assets/images/explore.png new file mode 100644 index 000000000..b26739610 Binary files /dev/null and b/assets/images/explore.png differ diff --git a/assets/images/headphone.png b/assets/images/headphone.png deleted file mode 100644 index 55cf43086..000000000 Binary files a/assets/images/headphone.png and /dev/null differ diff --git a/assets/images/headphone_dark.png b/assets/images/headphone_dark.png new file mode 100644 index 000000000..f4e36e771 Binary files /dev/null and b/assets/images/headphone_dark.png differ diff --git a/assets/images/headphone_light.png b/assets/images/headphone_light.png new file mode 100644 index 000000000..11b9b988a Binary files /dev/null and b/assets/images/headphone_light.png differ diff --git a/lib/data/services/bluetooth_service.dart b/lib/data/services/bluetooth_service.dart index 594154acc..ed9826e68 100644 --- a/lib/data/services/bluetooth_service.dart +++ b/lib/data/services/bluetooth_service.dart @@ -6,6 +6,14 @@ import '../../core/utils/logger.dart'; import 'dart:io' show Platform; import 'package:permission_handler/permission_handler.dart'; +/// 设备类型枚举 +enum DeviceType { + unknown, // 未知设备类型 + classicAudio, // 经典蓝牙音频设备(耳机等) + ble, // BLE设备 + dual // 双模设备(同时支持经典蓝牙和BLE) +} + /// 蓝牙设备服务,用于发现和管理蓝牙设备 class BluetoothService extends GetxService { static BluetoothService get to => Get.find(); @@ -21,6 +29,7 @@ class BluetoothService extends GetxService { // 已连接的设备 final _connectedDevices = [].obs; + final _systemPairedDevices = [].obs; // 扫描状态 final _isScanning = false.obs; @@ -30,6 +39,7 @@ class BluetoothService extends GetxService { // 当前选中的设备 final _selectedDevice = Rx(null); + final _deviceType = Rx(DeviceType.unknown); // 连接状态 final _connectionState = Rx(BluetoothConnectionState.disconnected); @@ -41,6 +51,13 @@ class BluetoothService extends GetxService { StreamSubscription? _stateSubscription; StreamSubscription? _scanSubscription; StreamSubscription? _connectionSubscription; + + // 连接锁,防止并发操作 + bool _isConnecting = false; + + // 记录连接尝试次数 + int _connectionAttempts = 0; + static const int MAX_CONNECTION_ATTEMPTS = 2; // 权限状态 final _permissionsGranted = false.obs; @@ -51,8 +68,10 @@ class BluetoothService extends GetxService { bool get isOn => _isOn.value; bool get isScanning => _isScanning.value; List get connectedDevices => _connectedDevices; + List get systemPairedDevices => _systemPairedDevices; List get scanResults => _scanResults; BluetoothDevice? get selectedDevice => _selectedDevice.value; + DeviceType get deviceType => _deviceType.value; BluetoothConnectionState get connectionState => _connectionState.value; Map get lastConnectedDeviceInfo => _lastConnectedDeviceInfo.value; @@ -62,16 +81,27 @@ class BluetoothService extends GetxService { RxList get connectedDevicesObs => _connectedDevices; RxList get scanResultsObs => _scanResults; Rx get selectedDeviceObs => _selectedDevice; + Rx get deviceTypeObs => _deviceType; Rx get connectionStateObs => _connectionState; Rx> get lastConnectedDeviceInfoObs => _lastConnectedDeviceInfo; + // 电池服务UUID + static const String BATTERY_SERVICE_UUID = "0000180f-0000-1000-8000-00805f9b34fb"; + // 电池电量特征UUID + static const String BATTERY_CHARACTERISTIC_UUID = "00002a19-0000-1000-8000-00805f9b34fb"; + /// 初始化蓝牙服务 Future init() async { try { - // 不在初始化时检查权限,改为在需要时才检查 - // await _checkAndRequestPermissions(); + // 确保权限检查在后台线程执行,避免主线程阻塞 + Future.microtask(() async { + // 仅在Android上请求权限 + if (Platform.isAndroid) { + await _checkAndRequestPermissions(); + } + }); - // 在后台线程初始化蓝牙状态监听,避免主线程阻塞 + // 在后台线程初始化蓝牙状态监听 Future.microtask(() { // 监听蓝牙状态 _stateSubscription = FlutterBluePlus.adapterState.listen((state) { @@ -79,25 +109,24 @@ class BluetoothService extends GetxService { _isAvailable.value = state != BluetoothAdapterState.unavailable; if (state == BluetoothAdapterState.on) { - // 蓝牙打开时,获取已连接设备,但不请求权限 - // 只有在用户主动扫描时才请求权限 - // 在后台线程执行,避免主线程阻塞 - Future.microtask(() => _getConnectedDevices()); + // 蓝牙打开时,获取已连接和已配对设备 + _refreshSystemDevices(); } else if (state == BluetoothAdapterState.off) { // 蓝牙关闭时,清空设备列表 _connectedDevices.clear(); + _systemPairedDevices.clear(); _scanResults.clear(); _selectedDevice.value = null; _lastConnectedDeviceInfo.value = {}; + _deviceType.value = DeviceType.unknown; } }); }); - // 不在初始化时立即检查蓝牙状态,而是延迟到UI渲染完成后 - // 这样可以避免启动时卡顿 + // 延迟检查蓝牙状态,避免启动时卡顿 Future.delayed(const Duration(milliseconds: 800), () async { try { - // 使用超时机制,避免无限等待蓝牙 + // 使用超时机制 bool isOn = await FlutterBluePlus.isOn .timeout(const Duration(seconds: 3), onTimeout: () { Logger.error('检查蓝牙状态超时'); @@ -108,8 +137,8 @@ class BluetoothService extends GetxService { _isAvailable.value = isOn; if (isOn) { - // 在后台线程获取设备,避免主线程阻塞 - Future.microtask(() => _getConnectedDevices()); + // 在后台刷新设备状态 + _refreshSystemDevices(); } } catch (e) { Logger.error('延迟检查蓝牙状态失败: $e'); @@ -123,60 +152,143 @@ class BluetoothService extends GetxService { } } - /// 检查并请求蓝牙所需权限 + /// 检查并请求蓝牙所需权限(Android 12+) Future _checkAndRequestPermissions() async { - // 暂时跳过权限检查,直接返回true - _permissionsGranted.value = true; - return true; + try { + if (Platform.isAndroid) { + // 对于Android 12+,需要BLUETOOTH_SCAN和BLUETOOTH_CONNECT权限 + Map statuses = await [ + Permission.bluetoothScan, + Permission.bluetoothConnect, + Permission.location, + ].request(); + + bool allGranted = statuses.values.every((status) => status.isGranted); + _permissionsGranted.value = allGranted; + + if (!allGranted) { + Logger.warning('蓝牙权限未完全授予: $statuses'); + } + + return allGranted; + } + + // iOS不需要显式请求蓝牙权限 + _permissionsGranted.value = true; + return true; + } catch (e) { + Logger.error('检查蓝牙权限失败: $e'); + _permissionsGranted.value = false; + return false; + } } /// 再次请求权限 Future requestPermissions() async { - // 暂时跳过权限检查,直接返回true - _permissionsGranted.value = true; - return true; + return await _checkAndRequestPermissions(); } - /// 获取已连接的设备 - Future _getConnectedDevices() async { + /// 刷新系统设备状态(已连接和已配对设备) + Future _refreshSystemDevices() async { try { - // 使用超时机制,避免无限等待 - List devices = await FlutterBluePlus.connectedSystemDevices - .timeout(const Duration(seconds: 3), onTimeout: () { - Logger.error('获取已连接设备超时'); - return []; - }); + // 避免重复刷新,使用标志位 + bool _isRefreshing = false; + if (_isRefreshing) return; - _connectedDevices.value = devices; + _isRefreshing = true; - // 如果有已连接的设备但没有选中设备,则选中第一个 + // 获取系统已连接设备 + try { + List devices = await FlutterBluePlus.connectedSystemDevices + .timeout(const Duration(seconds: 3), onTimeout: () { + Logger.error('获取已连接设备超时'); + return []; + }); + + _connectedDevices.value = devices; + Logger.info('已连接系统设备数量: ${_connectedDevices.length}'); + + // 如果有当前选中设备,更新其状态 + if (_selectedDevice.value != null) { + // 检查选中设备是否仍然在已连接列表中 + bool stillConnected = _connectedDevices.any( + (d) => d.remoteId.str == _selectedDevice.value!.remoteId.str + ); + + if (stillConnected) { + _connectionState.value = BluetoothConnectionState.connected; + _updateLastConnectedDeviceInfo(); + } else { + _connectionState.value = BluetoothConnectionState.disconnected; + } + } + } catch (e) { + Logger.error('获取已连接设备失败: $e'); + } + + // 获取已配对设备 + try { + List pairedDevices = await FlutterBluePlus.bondedDevices + .timeout(const Duration(seconds: 3), onTimeout: () { + Logger.error('获取已配对设备超时'); + return []; + }); + + _systemPairedDevices.value = pairedDevices; + Logger.info('系统已配对设备数量: ${_systemPairedDevices.length}'); + } catch (e) { + Logger.error('获取已配对设备失败: $e'); + } + + // 如果有已连接的设备但没有选中设备,自动选择第一个 if (_connectedDevices.isNotEmpty && _selectedDevice.value == null) { + Logger.info('自动选择第一个已连接设备: ${_connectedDevices[0].platformName}'); _selectedDevice.value = _connectedDevices[0]; - _monitorConnectionState(_selectedDevice.value!); - // 更新最后连接的设备信息 + _determineDeviceType(_selectedDevice.value!); + _connectionState.value = BluetoothConnectionState.connected; _updateLastConnectedDeviceInfo(); } - Logger.info('已连接设备数量: ${_connectedDevices.length}'); + _isRefreshing = false; } catch (e) { - Logger.error('获取已连接设备失败: $e'); + Logger.error('刷新系统设备状态失败: $e'); } } - /// 获取系统中已配对的设备 - Future> getPairedDevices() async { + /// 确定设备类型(经典蓝牙/BLE/双模) + void _determineDeviceType(BluetoothDevice device) { try { - // 获取已配对的设备(系统已知设备) - List systemDevices = await FlutterBluePlus.bondedDevices; - return systemDevices; + // 默认设为未知类型 + _deviceType.value = DeviceType.unknown; + + // 设备名称包含耳机/headphone/earphone/earbuds等关键词可能是音频设备 + final String deviceName = device.platformName.toLowerCase(); + final List audioKeywords = [ + 'headphone', 'earphone', 'earbuds', 'headset', + 'speaker', 'audio', '耳机', '音箱', '音响' + ]; + + bool hasAudioKeyword = audioKeywords.any((keyword) => deviceName.contains(keyword)); + + // 如果在已连接设备列表中,很可能是已经通过系统连接的经典蓝牙 + bool inConnectedList = _connectedDevices.any((d) => d.remoteId.str == device.remoteId.str); + + if (hasAudioKeyword || inConnectedList) { + _deviceType.value = DeviceType.classicAudio; + Logger.info('设备${device.platformName}被判定为经典蓝牙音频设备'); + } else { + // 不确定的情况,先假设是BLE设备,连接后可能会更新 + _deviceType.value = DeviceType.ble; + Logger.info('设备${device.platformName}被判定为BLE设备'); + } } catch (e) { - Logger.error('获取已配对设备失败: $e'); - return []; + Logger.error('确定设备类型失败: $e'); + _deviceType.value = DeviceType.unknown; } } - /// 开始扫描附近的蓝牙设备并尝试连接最强信号设备 - Future scanAndConnectToDevice({Duration? timeout}) async { + /// 开始扫描附近的蓝牙设备 + Future> startScan({Duration? timeout}) async { if (_isScanning.value) { await stopScan(); } @@ -185,6 +297,12 @@ class BluetoothService extends GetxService { _scanResults.clear(); try { + // 确保拥有扫描权限 + if (Platform.isAndroid && !await Permission.bluetoothScan.isGranted) { + Logger.warning('没有蓝牙扫描权限,尝试请求权限'); + await _checkAndRequestPermissions(); + } + // 设置扫描监听 _scanSubscription?.cancel(); _scanSubscription = FlutterBluePlus.scanResults.listen((results) { @@ -203,27 +321,11 @@ class BluetoothService extends GetxService { _isScanning.value = false; Logger.info('蓝牙扫描完成,发现 ${_scanResults.length} 个设备'); - // 如果找到设备,尝试连接信号最强的 - if (_scanResults.isNotEmpty) { - // 按信号强度排序 - List sortedResults = List.from(_scanResults); - sortedResults.sort((a, b) => b.rssi.compareTo(a.rssi)); - - // 尝试连接信号最强的设备 - final topDevice = sortedResults.first.device; - Logger.info('尝试连接信号最强的设备: ${topDevice.platformName}'); - bool success = await connectToDevice(topDevice); - - if (success) { - return topDevice; - } - } - - return null; + return _scanResults; } catch (e) { _isScanning.value = false; - Logger.error('蓝牙扫描或连接失败: $e'); - return null; + Logger.error('蓝牙扫描失败: $e'); + return []; } } @@ -239,37 +341,146 @@ class BluetoothService extends GetxService { } } - /// 连接到指定的蓝牙设备 - 简化版本 + /// 连接到指定的蓝牙设备 + /// 针对不同类型设备使用不同的连接策略 Future connectToDevice(BluetoothDevice device) async { - Logger.info('尝试连接设备: ${device.platformName} (${device.remoteId.str})'); + // 防止并发连接操作 + if (_isConnecting) { + Logger.warning('已有正在进行的连接操作,请等待完成'); + return false; + } + + _isConnecting = true; + _connectionAttempts = 0; try { - // 如果是当前已连接设备,直接返回成功 - if (_selectedDevice.value == device && - _connectionState.value == BluetoothConnectionState.connected) { + Logger.info('尝试连接设备: ${device.platformName} (${device.remoteId.str})'); + + // 确定设备类型 + _determineDeviceType(device); + + // 检查设备是否已在系统连接列表中 + bool alreadyConnected = _connectedDevices.any((d) => d.remoteId.str == device.remoteId.str); + + // 如果是当前已选择且已连接的设备,直接返回成功 + if (_selectedDevice.value == device && alreadyConnected) { Logger.info('设备已连接,无需重新连接'); + _connectionState.value = BluetoothConnectionState.connected; + _updateLastConnectedDeviceInfo(); + _isConnecting = false; return true; } - // 断开当前连接 + // 如果有其他已选择的设备,先取消监听并清除 if (_selectedDevice.value != null && _selectedDevice.value != device) { - await disconnectFromDevice(); + _connectionSubscription?.cancel(); + _connectionSubscription = null; } - // 直接尝试连接 - Logger.info('直接连接设备 ${device.platformName}'); - await device.connect(); - - // 更新设备信息 + // 更新选中的设备 _selectedDevice.value = device; + // 对于经典蓝牙音频设备,不尝试BLE连接,直接检查系统连接状态 + if (_deviceType.value == DeviceType.classicAudio) { + Logger.info('经典蓝牙音频设备,检查系统连接状态'); + + // 刷新系统连接设备列表 + await _refreshSystemDevices(); + + // 再次检查设备是否在已连接列表中 + alreadyConnected = _connectedDevices.any((d) => d.remoteId.str == device.remoteId.str); + + if (alreadyConnected) { + Logger.info('设备已在系统连接列表中,视为已连接'); + _connectionState.value = BluetoothConnectionState.connected; + _monitorSystemConnectionState(device); + _updateLastConnectedDeviceInfo(); + _isConnecting = false; + return true; + } else { + Logger.info('经典蓝牙设备未在系统连接列表中,需要在系统设置中配对和连接'); + _connectionState.value = BluetoothConnectionState.disconnected; + _isConnecting = false; + return false; + } + } + + // 对于BLE或双模设备,尝试进行BLE连接 + Logger.info('尝试进行BLE连接'); + return await _attemptBleConnection(device); + } catch (e) { + Logger.error('连接设备 ${device.platformName} 失败: $e'); + _connectionState.value = BluetoothConnectionState.disconnected; + _isConnecting = false; + return false; + } + } + + /// 尝试进行BLE连接 + Future _attemptBleConnection(BluetoothDevice device) async { + try { + _connectionState.value = BluetoothConnectionState.connecting; + + // 增加连接尝试次数 + _connectionAttempts++; + + // 不使用autoConnect=true来避免与mtu参数冲突 + // 根据错误日志,mtu和autoConnect是不兼容的 + bool autoConnect = false; // 始终使用false避免兼容性问题 + Logger.info('BLE连接尝试 #$_connectionAttempts,autoConnect=$autoConnect'); + + // 开始监听连接状态 _monitorConnectionState(device); + + // 尝试连接,不同的尝试使用不同的超时时间 + if (_connectionAttempts == 1) { + // 第一次尝试:短超时 + Logger.info('第一次尝试使用短超时(4秒)'); + await device.connect( + autoConnect: autoConnect, + timeout: const Duration(seconds: 4) + ); + } else { + // 第二次尝试:长超时 + Logger.info('第二次尝试使用长超时(8秒)'); + await device.connect( + autoConnect: autoConnect, + timeout: const Duration(seconds: 8) + ); + } + + // 如果连接成功,更新状态 + _connectionState.value = BluetoothConnectionState.connected; _updateLastConnectedDeviceInfo(); Logger.info('设备 ${device.platformName} 连接成功'); + _isConnecting = false; return true; } catch (e) { - Logger.error('连接设备 ${device.platformName} 失败: $e'); - return false; + // 连接失败,检查是否需要重试 + Logger.error('BLE连接尝试 #$_connectionAttempts 失败: $e'); + + if (_connectionAttempts < MAX_CONNECTION_ATTEMPTS) { + Logger.info('尝试使用不同参数重新连接'); + + // 断开连接并等待一段时间 + try { + await device.disconnect(); + } catch (disconnectError) { + // 忽略断开连接的错误 + Logger.error('断开连接时出错,继续尝试: $disconnectError'); + } + + // 等待时间更长,确保设备和系统状态重置 + await Future.delayed(const Duration(milliseconds: 2000)); + + // 重试连接 + return await _attemptBleConnection(device); + } else { + // 连接失败且达到最大尝试次数 + _connectionState.value = BluetoothConnectionState.disconnected; + _isConnecting = false; + return false; + } } } @@ -280,54 +491,91 @@ class BluetoothService extends GetxService { return true; } + // 取消设备监听 + _connectionSubscription?.cancel(); + _connectionSubscription = null; + try { + // 对于经典蓝牙设备,不调用disconnect + if (_deviceType.value == DeviceType.classicAudio) { + Logger.info('经典蓝牙设备,仅清除选择状态,不调用disconnect'); + _selectedDevice.value = null; + _connectionState.value = BluetoothConnectionState.disconnected; + return true; + } + + // 对于BLE设备,调用disconnect Logger.info('正在断开设备 ${_selectedDevice.value!.platformName} 的连接'); await _selectedDevice.value!.disconnect(); - Logger.info('设备连接已断开,取消连接状态监听'); - _connectionSubscription?.cancel(); - _connectionSubscription = null; _selectedDevice.value = null; - // 清除最后连接的设备信息 - _lastConnectedDeviceInfo.value = {}; + _connectionState.value = BluetoothConnectionState.disconnected; return true; } catch (e) { Logger.error('断开蓝牙设备连接失败: $e'); + // 即使失败也清除状态 + _selectedDevice.value = null; + _connectionState.value = BluetoothConnectionState.disconnected; return false; } } - /// 监控连接状态 + /// 监控BLE连接状态 void _monitorConnectionState(BluetoothDevice device) { - Logger.info('设置设备 ${device.platformName} 的连接状态监听'); + Logger.info('设置设备 ${device.platformName} 的BLE连接状态监听'); + // 取消之前的监听 - if (_connectionSubscription != null) { - Logger.info('取消之前的连接状态监听'); - _connectionSubscription!.cancel(); - _connectionSubscription = null; - } + _connectionSubscription?.cancel(); // 设置新的监听 _connectionSubscription = device.connectionState.listen((state) { - Logger.info('设备 ${device.platformName} 连接状态变更为: $state'); + Logger.info('设备 ${device.platformName} BLE连接状态变更为: $state'); _connectionState.value = state; if (state == BluetoothConnectionState.disconnected) { - Logger.info('设备 ${device.platformName} 已断开连接'); - _selectedDevice.value = null; - // 设备断开连接时,不清除最后连接的设备信息,以便下次可以重新连接 + Logger.info('设备 ${device.platformName} BLE连接已断开'); + // 设备断开连接时,不清除设备信息,以便下次可以重新连接 } else if (state == BluetoothConnectionState.connected) { - Logger.info('设备 ${device.platformName} 已成功连接'); + Logger.info('设备 ${device.platformName} BLE连接已成功'); // 设备连接成功时,更新最后连接的设备信息 _updateLastConnectedDeviceInfo(); - } else if (state == BluetoothConnectionState.connecting) { - Logger.info('设备 ${device.platformName} 正在连接中...'); - } else if (state == BluetoothConnectionState.disconnecting) { - Logger.info('设备 ${device.platformName} 正在断开连接...'); } }); + } + + /// 监控系统连接状态(针对经典蓝牙设备) + void _monitorSystemConnectionState(BluetoothDevice device) { + Logger.info('设置设备 ${device.platformName} 的系统连接状态监听'); - Logger.info('设备 ${device.platformName} 的连接状态监听已设置'); + // 使用定时器定期检查系统连接状态 + Timer.periodic(const Duration(seconds: 5), (timer) async { + // 如果设备已变更或服务关闭,停止监控 + if (_selectedDevice.value == null || + _selectedDevice.value!.remoteId.str != device.remoteId.str) { + timer.cancel(); + return; + } + + try { + await _refreshSystemDevices(); + + // 检查设备是否仍在系统连接列表中 + bool stillConnected = _connectedDevices.any( + (d) => d.remoteId.str == device.remoteId.str + ); + + if (stillConnected && _connectionState.value != BluetoothConnectionState.connected) { + Logger.info('设备 ${device.platformName} 在系统连接列表中,更新为已连接状态'); + _connectionState.value = BluetoothConnectionState.connected; + _updateLastConnectedDeviceInfo(); + } else if (!stillConnected && _connectionState.value == BluetoothConnectionState.connected) { + Logger.info('设备 ${device.platformName} 不在系统连接列表中,更新为断开状态'); + _connectionState.value = BluetoothConnectionState.disconnected; + } + } catch (e) { + Logger.error('监控系统连接状态失败: $e'); + } + }); } /// 获取已连接设备的信息 @@ -336,6 +584,7 @@ class BluetoothService extends GetxService { return { 'connected': false, 'device': null, + 'deviceType': _deviceType.value.toString().split('.').last }; } @@ -349,7 +598,8 @@ class BluetoothService extends GetxService { 'rssi': _scanResults .firstWhereOrNull((r) => r.device.remoteId == _selectedDevice.value!.remoteId) ?.rssi - } + }, + 'deviceType': _deviceType.value.toString().split('.').last }; // 更新最后连接的设备信息 @@ -361,101 +611,118 @@ class BluetoothService extends GetxService { /// 更新最后连接的设备信息 void _updateLastConnectedDeviceInfo() { if (_selectedDevice.value != null) { - _lastConnectedDeviceInfo.value = { - 'connected': _connectionState.value == BluetoothConnectionState.connected, - 'device': { - 'name': _selectedDevice.value!.platformName.isEmpty ? - '未命名设备' : _selectedDevice.value!.platformName, - 'id': _selectedDevice.value!.remoteId.str, - 'isConnected': _connectionState.value == BluetoothConnectionState.connected, - 'rssi': _scanResults - .firstWhereOrNull((r) => r.device.remoteId == _selectedDevice.value!.remoteId) - ?.rssi - } - }; + _lastConnectedDeviceInfo.value = getConnectedDeviceInfo(); } } - /// 一个综合方法,执行自动查找并连接设备的完整流程 - Future autoConnectDevice() async { + /// 一个综合方法,查找且尝试使用系统已连接的设备 + /// 针对经典蓝牙耳机优化,避免使用BLE连接 + Future findConnectedSystemDevice() async { try { - // 1. 先检查当前已连接设备 + // 先刷新系统设备列表 + await _refreshSystemDevices(); + + // 1. 检查当前是否有已选择且已连接的设备 if (_selectedDevice.value != null && _connectionState.value == BluetoothConnectionState.connected) { Logger.info('使用当前已连接设备: ${_selectedDevice.value!.platformName}'); return _selectedDevice.value; } - // 2. 检查系统中已连接的设备 - try { - List connectedDevices = await FlutterBluePlus.connectedSystemDevices - .timeout(const Duration(seconds: 3)); + // 2. 检查系统已连接设备列表 + if (_connectedDevices.isNotEmpty) { + // 优先选择可能是音频设备的 + BluetoothDevice audioDevice = _connectedDevices.firstWhere( + (device) { + final name = device.platformName.toLowerCase(); + return name.contains('headphone') || + name.contains('earphone') || + name.contains('earbuds') || + name.contains('headset') || + name.contains('speaker') || + name.contains('耳机') || + name.contains('音箱'); + }, + orElse: () => _connectedDevices.first + ); - if (connectedDevices.isNotEmpty) { - // 有已连接的设备,直接使用第一个 - Logger.info('发现系统已连接设备: ${connectedDevices.first.platformName}'); - - // 连接设备并更新服务状态 - await connectToDevice(connectedDevices.first); - return connectedDevices.first; - } - } catch (e) { - Logger.error('检查系统连接设备失败: $e'); - } - - // 3. 尝试连接之前用过的设备(从已配对设备中寻找) - List pairedDevices = await FlutterBluePlus.bondedDevices; - - // 如果有配对设备,尝试连接第一个 - if (pairedDevices.isNotEmpty) { - Logger.info('尝试连接已配对设备: ${pairedDevices.first.platformName}'); - bool success = await connectToDevice(pairedDevices.first); - if (success) { - return pairedDevices.first; - } + Logger.info('在系统已连接设备中找到设备: ${audioDevice.platformName}'); + + // 设置为当前设备并更新状态 + _selectedDevice.value = audioDevice; + _determineDeviceType(audioDevice); + _connectionState.value = BluetoothConnectionState.connected; + _monitorSystemConnectionState(audioDevice); + _updateLastConnectedDeviceInfo(); + + return audioDevice; } - // 4. 没有找到可用设备 + // 3. 如果没有已连接设备,检查已配对设备 return null; } catch (e) { - Logger.error('自动连接设备流程失败: $e'); + Logger.error('查找系统已连接设备失败: $e'); return null; } } - /// 获取设备连接状态的简化方法 (用于兼容现有代码) - Future getReliableConnectionState(BluetoothDevice device) async { + /// 获取设备电量 + Future getBatteryLevel() async { + if (_selectedDevice.value == null || + _connectionState.value != BluetoothConnectionState.connected) { + return null; + } + try { - // 直接获取设备当前状态 - BluetoothConnectionState state = await device.connectionState.first - .timeout(const Duration(seconds: 2), onTimeout: () { - Logger.info('获取设备 ${device.platformName} 连接状态超时'); - return BluetoothConnectionState.disconnected; - }); - - // 如果获取到的状态是已连接,直接返回 - if (state == BluetoothConnectionState.connected) { - return BluetoothConnectionState.connected; + // 对于BLE设备,尝试读取电池服务 + if (_deviceType.value == DeviceType.ble || _deviceType.value == DeviceType.dual) { + // 发现设备服务 + List services = await _selectedDevice.value!.discoverServices(); + + // 查找电池服务 + for (var service in services) { + if (service.uuid.toString().toLowerCase() == BATTERY_SERVICE_UUID) { + // 查找电池特征 + for (var characteristic in service.characteristics) { + if (characteristic.uuid.toString().toLowerCase() == BATTERY_CHARACTERISTIC_UUID) { + // 读取电池电量 + List value = await characteristic.read(); + if (value.isNotEmpty) { + return value[0]; // 电量百分比 + } + } + } + } + } } - // 作为备用检查,看设备是否在系统已连接列表中 - try { - List connectedDevices = await FlutterBluePlus.connectedSystemDevices - .timeout(const Duration(seconds: 2)); - - for (var connectedDevice in connectedDevices) { - if (connectedDevice.remoteId.str == device.remoteId.str) { - return BluetoothConnectionState.connected; + // 对于经典蓝牙设备,可能需要平台特定的方法 + if (_deviceType.value == DeviceType.classicAudio) { + // 这里需要使用平台通道或其他插件获取经典蓝牙设备电量 + // 目前根据设备名称特征尝试估计电量 + // 部分设备会在名称后附加电量信息,如"XXX耳机(80%)" + if (_selectedDevice.value != null) { + final deviceName = _selectedDevice.value!.platformName; + final percentRegex = RegExp(r'(\d+)%'); + final match = percentRegex.firstMatch(deviceName); + if (match != null && match.groupCount >= 1) { + final percentStr = match.group(1); + if (percentStr != null) { + final percent = int.tryParse(percentStr); + if (percent != null && percent >= 0 && percent <= 100) { + return percent; + } + } } } - } catch (e) { - // 忽略检查系统设备列表的错误 + Logger.info('无法从设备名称中提取电量信息'); } - return state; + // 如果以上方法都无法获取电量,返回null + return null; } catch (e) { - Logger.error('获取设备 ${device.platformName} 连接状态失败: $e'); - return BluetoothConnectionState.disconnected; + Logger.error('获取设备电量失败: $e'); + return null; } } diff --git a/lib/data/services/voice_interaction_service.dart b/lib/data/services/voice_interaction_service.dart index 4245b41bc..13fc08c66 100644 --- a/lib/data/services/voice_interaction_service.dart +++ b/lib/data/services/voice_interaction_service.dart @@ -251,9 +251,6 @@ class VoiceInteractionService extends GetxService { _eventStreamController?.add(RecognitionStartedEvent( timestamp: timestamp, )); - - - Logger.info('收到语音识别开始事件'); break; default: diff --git a/lib/modules/explore/controllers/explore_controller.dart b/lib/modules/explore/controllers/explore_controller.dart index c9cf6d1e0..4deefd81b 100644 --- a/lib/modules/explore/controllers/explore_controller.dart +++ b/lib/modules/explore/controllers/explore_controller.dart @@ -63,8 +63,8 @@ class ExploreController extends GetxController { targetRoute: Routes.chat, ), AIFeature( - title: '同声翻译', - description: '实时翻译40+种语言', + title: '同声传译', + description: '实时翻译80+种语言', icon: Icons.translate, startColor: Colors.green.shade100, endColor: Colors.green.shade200, diff --git a/lib/modules/explore/views/explore_view.dart b/lib/modules/explore/views/explore_view.dart index 15651717a..4133b4783 100644 --- a/lib/modules/explore/views/explore_view.dart +++ b/lib/modules/explore/views/explore_view.dart @@ -140,8 +140,8 @@ class ExploreView extends GetView { borderRadius: BorderRadius.circular(24.r), child: Container( color: Colors.blue.shade900, // 预设颜色,图片加载失败时显示 - child: Image.network( - 'https://images.unsplash.com/photo-1526378722484-bd91ca387e72?ixlib=rb-4.0.3&auto=format&fit=crop&w=1000&q=80', + child: Image.asset( + 'assets/images/explore.png', width: double.infinity, height: 180.h, fit: BoxFit.cover, diff --git a/lib/modules/home/controllers/home_controller.dart b/lib/modules/home/controllers/home_controller.dart index 5a2768c9c..58b479e3a 100644 --- a/lib/modules/home/controllers/home_controller.dart +++ b/lib/modules/home/controllers/home_controller.dart @@ -25,13 +25,9 @@ class HomeController extends GetxController { ConnectionStatus get connectionStatus => _connectionStatus.value; // 电池状态 - final _leftEarbudBattery = 75.obs; - final _rightEarbudBattery = 52.obs; - final _caseBattery = 92.obs; + final _deviceBattery = Rx(null); - int get leftEarbudBattery => _leftEarbudBattery.value; - int get rightEarbudBattery => _rightEarbudBattery.value; - int get caseBattery => _caseBattery.value; + int? get deviceBattery => _deviceBattery.value; // 固件版本 final String firmwareVersion = 'v2.4.1'; @@ -100,8 +96,19 @@ class HomeController extends GetxController { // 基础服务初始化,不涉及蓝牙等耗时操作 void _initBasicServices() { try { - // 初始化语音交互 - Future.microtask(() => VoiceInteractionService.to.initialize()); + // 初始化语音交互服务并启动服务 + Future.microtask(() async { + // 先初始化服务 + await VoiceInteractionService.to.initialize(); + + // 启动服务 + bool serviceStarted = await VoiceInteractionService.to.startService(); + if (serviceStarted) { + Logger.info('语音交互服务已成功启动'); + } else { + Logger.error('语音交互服务启动失败'); + } + }); // 检查是否有传入的蓝牙设备信息 if (Get.arguments != null && Get.arguments is Map) { @@ -109,7 +116,7 @@ class HomeController extends GetxController { _updateConnectionStatus(); } - // 模拟电池状态更新(不涉及设备通信) + // 模拟电池状态更新 _simulateBatteryUpdates(); } catch (e) { Logger.error('初始化基础服务失败: $e'); @@ -122,11 +129,8 @@ class HomeController extends GetxController { if (Get.isRegistered()) { _bluetoothService = Get.find(); - // 不再重复加载设备信息,因为已经在_loadCachedDeviceInfo中加载过了 - // 只进行状态同步 - - // 主动检查当前连接状态并更新 - _checkAndUpdateCurrentConnectionState(); + // 直接获取当前设备信息 + _updateDeviceInfoFromService(); // 监听连接状态变化 ever(_bluetoothService!.connectionStateObs, (state) { @@ -137,205 +141,65 @@ class HomeController extends GetxController { // 监听选中设备变化 ever(_bluetoothService!.selectedDeviceObs, (device) { Logger.info('蓝牙选中设备变化: ${device?.platformName ?? "无设备"}'); - if (device != null) { - _deviceInfo.assignAll(_bluetoothService!.getConnectedDeviceInfo()); - _updateConnectionStatus(); - // 通知UI刷新 - update(); - } else if (_isConnected.value) { - // 如果设备断开连接,更新状态 - _isConnected.value = false; - // 不清除设备信息,以便可以显示上次连接的设备 - _connectionStatus.value = ConnectionStatus.disconnected; - // 通知UI刷新 - update(); - } + _updateDeviceInfoFromService(); }); // 监听最后连接的设备信息变化 ever(_bluetoothService!.lastConnectedDeviceInfoObs, (info) { Logger.info('最后连接的设备信息变化'); if (info.isNotEmpty) { - // 只有当设备信息为空或者连接状态不是已连接时,才更新设备信息 - if (_deviceInfo.isEmpty || _connectionStatus.value != ConnectionStatus.connected) { - _deviceInfo.assignAll(info); - _updateConnectionStatus(); - // 通知UI刷新 - update(); - } + _deviceInfo.assignAll(info); + _updateConnectionStatus(); + update(); // 通知UI刷新 } }); - - // 如果当前没有连接状态,但有设备信息,尝试在后台重新连接 - if (_connectionStatus.value != ConnectionStatus.connected && - _deviceInfo.isNotEmpty && - _deviceInfo['device'] != null && - _deviceInfo['device']['id'] != null) { - // 延迟后台尝试连接设备,不阻塞UI - Future.delayed(const Duration(milliseconds: 2000), () { - _reconnectToLastDevice(); - }); - } - // 如果没有设备信息,延迟尝试获取已配对设备 - else if (_deviceInfo.isEmpty) { - // 这个操作放在最后并延迟执行,因为它可能会触发蓝牙扫描 - Future.delayed(const Duration(milliseconds: 2500), () { - _checkPairedDevices(); - }); - } } } catch (e) { Logger.error('获取蓝牙服务失败: $e'); _connectionErrorMsg.value = '获取蓝牙服务失败'; - // 通知UI刷新 - update(); + update(); // 通知UI刷新 } } - // 检查并更新当前连接状态 - void _checkAndUpdateCurrentConnectionState() { - if (_bluetoothService != null && _bluetoothService!.selectedDevice != null) { - // 如果有选择的设备,使用可靠方法获取一次状态 - _bluetoothService!.getReliableConnectionState(_bluetoothService!.selectedDevice!).then((state) { - Logger.info('主动检查设备连接状态: $state'); - _processConnectionStateChange(state); - }).catchError((e) { - Logger.error('检查连接状态失败: $e'); - }); + // 从蓝牙服务更新设备信息 + void _updateDeviceInfoFromService() { + if (_bluetoothService != null) { + // 获取当前设备信息 + Map info = _bluetoothService!.lastConnectedDeviceInfo; + if (info.isNotEmpty) { + _deviceInfo.assignAll(info); + _updateConnectionStatus(); + update(); // 通知UI刷新 + } else if (_bluetoothService!.selectedDevice != null) { + // 如果最后连接的设备信息为空,但有选中的设备,获取当前连接的设备信息 + _deviceInfo.assignAll(_bluetoothService!.getConnectedDeviceInfo()); + _updateConnectionStatus(); + update(); // 通知UI刷新 + } } } - // 后台尝试重新连接到上次的设备 - Future _reconnectToLastDevice() async { - try { - Logger.info('尝试重新连接到上次的设备'); - _connectionStatus.value = ConnectionStatus.connecting; - update(); // 立即更新UI显示连接中状态 - - // 使用超时机制,防止无响应 - return await Future.sync(() async { - // 获取设备ID - String deviceId = _deviceInfo['device']['id']; - Logger.info('上次连接的设备ID: $deviceId'); - - // 获取已配对设备,使用超时保护 - Logger.info('开始获取系统已配对设备列表...'); - List pairedDevices = await _bluetoothService!.getPairedDevices() - .timeout(const Duration(seconds: 5), onTimeout: () { - Logger.error('获取已配对设备超时'); - return []; - }); - - // 记录配对设备 - if (pairedDevices.isNotEmpty) { - Logger.info('发现 ${pairedDevices.length} 个已配对设备:'); - for (var i = 0; i < pairedDevices.length; i++) { - Logger.info('配对设备 #$i: ${pairedDevices[i].platformName ?? "未命名设备"} (${pairedDevices[i].remoteId.str})'); - } - } else { - Logger.info('未找到任何已配对设备'); - } - - // 查找匹配ID的设备 - Logger.info('开始查找匹配ID的设备: $deviceId'); - BluetoothDevice? targetDevice; - for (var device in pairedDevices) { - Logger.info('检查设备 ${device.platformName ?? "未命名设备"} (${device.remoteId.str}) 是否匹配'); - if (device.remoteId.str == deviceId) { - Logger.info('找到匹配ID的设备: ${device.platformName ?? "未命名设备"}'); - targetDevice = device; - break; - } - } - - // 如果找到目标设备,尝试连接 - if (targetDevice != null) { - Logger.info('找到目标设备: ${targetDevice.platformName ?? "未命名设备"},尝试连接'); - - // 直接尝试连接设备,不做复杂状态检查 - Logger.info('开始连接设备 ${targetDevice?.platformName ?? "未命名设备"}'); - final success = await _bluetoothService!.connectToDevice(targetDevice) - .timeout(const Duration(seconds: 5), onTimeout: () { - Logger.error('连接设备 ${targetDevice?.platformName ?? "未命名设备"} 超时'); - return false; - }); - - if (success) { - Logger.info('重新连接到设备 ${targetDevice?.platformName ?? "未命名设备"} 成功'); - _deviceInfo.assignAll(_bluetoothService!.getConnectedDeviceInfo()); - _connectionStatus.value = ConnectionStatus.connected; - _isConnected.value = true; - _updateConnectionStatus(); - update(); // 立即更新UI - } else { - Logger.error('重新连接到设备 ${targetDevice?.platformName ?? "未命名设备"} 失败'); - _connectionStatus.value = ConnectionStatus.failed; - _connectionErrorMsg.value = '重新连接设备失败,请手动刷新或重新连接'; - update(); // 立即更新UI - } - } else { - Logger.info('未找到匹配ID的设备,尝试连接第一个配对设备'); - // 如果没有找到目标设备但有其他配对设备,尝试连接第一个 - if (pairedDevices.isNotEmpty) { - var firstDevice = pairedDevices.first; - Logger.info('尝试连接第一个配对设备: ${firstDevice.platformName ?? "未命名设备"}'); - - // 直接尝试连接设备 - Logger.info('开始连接设备 ${firstDevice.platformName ?? "未命名设备"}'); - final success = await _bluetoothService!.connectToDevice(firstDevice) - .timeout(const Duration(seconds: 5), onTimeout: () { - Logger.error('连接设备 ${firstDevice.platformName ?? "未命名设备"} 超时'); - return false; - }); - - if (success) { - Logger.info('自动连接到已配对设备成功: ${firstDevice.platformName ?? "未命名设备"}'); - _deviceInfo.assignAll(_bluetoothService!.getConnectedDeviceInfo()); - _connectionStatus.value = ConnectionStatus.connected; - _isConnected.value = true; // 直接设置连接状态 - _updateConnectionStatus(); - update(); // 立即更新UI - } else { - Logger.error('连接到第一个配对设备 ${firstDevice.platformName ?? "未命名设备"} 失败'); - _connectionStatus.value = ConnectionStatus.failed; - _connectionErrorMsg.value = '连接设备失败,请重试'; - update(); // 立即更新UI - } - } else { - Logger.info('没有找到任何可用的配对设备'); - _connectionStatus.value = ConnectionStatus.disconnected; - update(); // 立即更新UI - } - } - }).timeout(const Duration(seconds: 15), onTimeout: () { - Logger.error('重新连接设备整体流程超时'); - _connectionStatus.value = ConnectionStatus.failed; - _connectionErrorMsg.value = '连接超时,请手动刷新或重新连接'; - update(); // 立即更新UI - }); - } catch (e) { - Logger.error('重新连接到设备失败: $e'); - _connectionStatus.value = ConnectionStatus.failed; - _connectionErrorMsg.value = '连接失败,请手动刷新或重新连接'; - update(); // 立即更新UI + // 检查并更新当前连接状态 + void _checkAndUpdateCurrentConnectionState() { + if (_bluetoothService != null) { + _updateDeviceInfoFromService(); } } // 处理连接状态变化 void _processConnectionStateChange(BluetoothConnectionState state) { - // 强制从蓝牙服务获取最新设备信息 - _deviceInfo.assignAll(_bluetoothService!.getConnectedDeviceInfo()); + // 从蓝牙服务获取最新设备信息 + _updateDeviceInfoFromService(); switch (state) { case BluetoothConnectionState.connected: _connectionStatus.value = ConnectionStatus.connected; _connectionErrorMsg.value = ''; - _isConnected.value = true; // 直接设置连接状态为true - // _showConnectionSuccessMessage(); + _isConnected.value = true; break; case BluetoothConnectionState.disconnected: _connectionStatus.value = ConnectionStatus.disconnected; - _isConnected.value = false; // 直接设置连接状态为false + _isConnected.value = false; break; case BluetoothConnectionState.connecting: _connectionStatus.value = ConnectionStatus.connecting; @@ -345,18 +209,13 @@ class HomeController extends GetxController { break; } - // 延迟一帧后刷新状态,确保UI能更新 - Future.microtask(() => _updateConnectionStatus()); - - // 通知UI刷新 - update(); + update(); // 通知UI刷新 } // 显示连接成功消息 void _showConnectionSuccessMessage() { - if (_bluetoothService?.selectedDevice != null) { - String deviceName = _bluetoothService!.selectedDevice!.platformName.isEmpty ? - '未命名设备' : _bluetoothService!.selectedDevice!.platformName; + if (_deviceInfo.isNotEmpty && _deviceInfo['device'] != null) { + String deviceName = _deviceInfo['device']['name'] ?? '未命名设备'; Get.snackbar( '连接成功', '已成功连接到设备: $deviceName', @@ -368,88 +227,6 @@ class HomeController extends GetxController { } } - // 尝试查找并连接已配对设备 - Future _checkPairedDevices() async { - try { - if (_bluetoothService != null) { - Logger.info('开始检查已配对设备流程'); - // 更新状态为正在连接 - _connectionStatus.value = ConnectionStatus.connecting; - update(); // 立即更新UI - - // 如果已有选中的设备并且已连接,直接使用 - if (_bluetoothService!.selectedDevice != null && - _bluetoothService!.connectionState == BluetoothConnectionState.connected) { - Logger.info('使用已连接的设备: ${_bluetoothService!.selectedDevice!.platformName}, 连接状态: ${_bluetoothService!.connectionState}'); - _deviceInfo.assignAll(_bluetoothService!.getConnectedDeviceInfo()); - _connectionStatus.value = ConnectionStatus.connected; - _isConnected.value = true; // 直接设置连接状态 - _updateConnectionStatus(); - update(); // 立即更新UI - return; - } - - // 获取已配对设备 - Logger.info('开始获取系统已配对设备列表...'); - final pairedDevices = await _bluetoothService!.getPairedDevices() - .timeout(const Duration(seconds: 5), onTimeout: () { - Logger.error('获取已配对设备超时'); - return []; - }); - - if (pairedDevices.isEmpty) { - Logger.info('没有找到已配对设备'); - _connectionStatus.value = ConnectionStatus.disconnected; - update(); // 立即更新UI - return; - } - - Logger.info('发现 ${pairedDevices.length} 个已配对设备'); - for (var i = 0; i < pairedDevices.length; i++) { - var device = pairedDevices[i]; - Logger.info('已配对设备 #$i: ${device.platformName} (${device.remoteId.str})'); - } - - // 遍历配对设备,检查是否有需要连接的设备 - Logger.info('开始查找设备进行连接...'); - if (pairedDevices.isNotEmpty) { - // 选择第一个配对设备进行连接 - var deviceToConnect = pairedDevices.first; - Logger.info('尝试连接配对设备: ${deviceToConnect.platformName ?? "未命名设备"}'); - - final success = await _bluetoothService!.connectToDevice(deviceToConnect) - .timeout(const Duration(seconds: 5), onTimeout: () { - Logger.error('连接设备 ${deviceToConnect.platformName ?? "未命名设备"} 超时'); - return false; - }); - - if (success) { - Logger.info('成功连接到设备: ${deviceToConnect.platformName ?? "未命名设备"}'); - _deviceInfo.assignAll(_bluetoothService!.getConnectedDeviceInfo()); - _connectionStatus.value = ConnectionStatus.connected; - _isConnected.value = true; - _updateConnectionStatus(); - update(); // 立即更新UI - } else { - // 连接失败 - Logger.error('连接设备 ${deviceToConnect.platformName ?? "未命名设备"} 失败'); - _connectionStatus.value = ConnectionStatus.failed; - _connectionErrorMsg.value = '连接设备失败,请重试'; - update(); // 立即更新UI - } - } else { - _connectionStatus.value = ConnectionStatus.disconnected; - update(); // 立即更新UI - } - } - } catch (e) { - Logger.error('检查已配对设备失败: $e'); - _connectionStatus.value = ConnectionStatus.failed; - _connectionErrorMsg.value = '检查设备失败: ${e.toString()}'; - update(); // 立即更新UI - } - } - // 更新连接状态 void _updateConnectionStatus() { if (_deviceInfo.containsKey('connected')) { @@ -472,13 +249,41 @@ class HomeController extends GetxController { // 模拟电池状态更新 void _simulateBatteryUpdates() { - // 在实际应用中,这里应该从设备获取真实的电池状态 - // 这里只是模拟随机波动 + // 不再获取设备电量 + _deviceBattery.value = 0; + Logger.info('不再获取设备电量'); + } + + // 获取设备电量 + Future _fetchBatteryLevel() async { + // 不再获取设备电量 + _deviceBattery.value = 0; + Logger.info('不再获取设备电量'); + } + + // 更新设备电量信息(提供给UI手动刷新使用) + Future refreshBatteryLevel() async { + await _fetchBatteryLevel(); + Get.snackbar( + '提示', + '电量信息已更新', + snackPosition: SnackPosition.TOP, + duration: const Duration(seconds: 1), + ); + } + + // 获取格式化后的电量显示文本 + String getBatteryText() { + if (_deviceBattery.value == null) { + return '电量未知'; + } else { + return '${_deviceBattery.value}%'; + } } // 探索AI功能 void exploreAIFeatures() { - Get.toNamed(Routes.explore); + Get.toNamed(Routes.chat, arguments: {'agentId': 'personal_assistant'}); } // 更新固件 @@ -605,70 +410,36 @@ class HomeController extends GetxController { return; } - // 设置正在刷新状态,但保留当前设备信息显示 + // 更新连接状态为正在连接,但保持当前设备信息显示 _connectionStatus.value = ConnectionStatus.connecting; update(); // 立即更新UI状态 - // 强制检查当前蓝牙连接状态 - _checkAndUpdateCurrentConnectionState(); + // 从BluetoothService获取最新设备信息 + _updateDeviceInfoFromService(); - if (_bluetoothService!.selectedDevice != null) { - // 如果有选中的设备,刷新设备信息 - _deviceInfo.assignAll(_bluetoothService!.getConnectedDeviceInfo()); - _updateConnectionStatus(); - update(); // 确保UI更新 - - // 检查刷新后的连接状态 - if (_isConnected.value) { - Get.snackbar( - '提示', - '设备信息已更新', - snackPosition: SnackPosition.TOP, - backgroundColor: Colors.green[100], - colorText: Colors.green[800], - ); - } else { - // 设备可能已断开连接,尝试使用存储的设备信息重新连接 - if (_bluetoothService!.lastConnectedDeviceInfo.isNotEmpty) { - _connectionStatus.value = ConnectionStatus.disconnected; - update(); // 更新状态 - _reconnectToLastDevice(); - } else { - // 上次连接的设备信息也为空,尝试连接已配对设备 - await _checkPairedDevices(); - } - } - } else if (_bluetoothService!.lastConnectedDeviceInfo.isNotEmpty) { - // 如果没有选中的设备但有上次连接的设备信息,尝试重新连接 - _deviceInfo.assignAll(_bluetoothService!.lastConnectedDeviceInfo); - _updateConnectionStatus(); - update(); // 确保UI更新 - _reconnectToLastDevice(); + // 刷新电量信息 + await _fetchBatteryLevel(); + + // 根据当前状态给出提示 + if (_isConnected.value) { + Get.snackbar( + '提示', + '设备信息已更新', + snackPosition: SnackPosition.TOP, + backgroundColor: Colors.green[100], + colorText: Colors.green[800], + ); } else { - // 如果没有选中的设备,尝试连接已配对设备 - await _checkPairedDevices(); - - if (_connectionStatus.value == ConnectionStatus.failed) { - Get.snackbar( - '提示', - _connectionErrorMsg.value.isNotEmpty ? _connectionErrorMsg.value : '连接设备失败', - snackPosition: SnackPosition.TOP, - backgroundColor: Colors.red[100], - colorText: Colors.red[800], - ); - } else if (_connectionStatus.value == ConnectionStatus.disconnected) { - Get.snackbar( - '提示', - '未找到已连接设备,请连接设备', - snackPosition: SnackPosition.TOP, - backgroundColor: Colors.amber[100], - colorText: Colors.amber[800], - ); - } + Get.snackbar( + '提示', + '无法获取设备信息,请确保设备已连接', + snackPosition: SnackPosition.TOP, + backgroundColor: Colors.amber[100], + colorText: Colors.amber[800], + ); } } catch (e) { Logger.error('刷新设备信息失败: $e'); - _connectionStatus.value = ConnectionStatus.failed; _connectionErrorMsg.value = '刷新设备信息失败: ${e.toString()}'; update(); // 确保状态更新 @@ -709,4 +480,18 @@ class HomeController extends GetxController { return Colors.red; } } + + // 快捷入口方法 + + // 打开同声翻译页面 + void openTranslation() { + Get.toNamed(Routes.translation); + } + + // 打开AI女友页面 + void openAIGirlfriend() { + Get.toNamed(Routes.chat); + } + + // 打开AI助理(已有方法 exploreAIFeatures) } \ No newline at end of file diff --git a/lib/modules/home/views/home_view.dart b/lib/modules/home/views/home_view.dart index ca6a07e95..059bb342c 100644 --- a/lib/modules/home/views/home_view.dart +++ b/lib/modules/home/views/home_view.dart @@ -2,57 +2,136 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import '../controllers/home_controller.dart'; +import '../../../core/routes/app_pages.dart'; +import '../../../core/routes/app_routes.dart'; import '../../../core/widgets/common_bottom_nav.dart'; // 导入枚举 import '../controllers/home_controller.dart' show ConnectionStatus; +// 呼吸光动画组件 +class AnimatedPulsing extends StatefulWidget { + @override + _AnimatedPulsingState createState() => _AnimatedPulsingState(); +} + +class _AnimatedPulsingState extends State with SingleTickerProviderStateMixin { + late AnimationController _controller; + late Animation _animation; + + @override + void initState() { + super.initState(); + _controller = AnimationController( + duration: Duration(seconds: 2), + vsync: this, + )..repeat(reverse: true); + _animation = Tween(begin: 0.5, end: 0.8).animate( + CurvedAnimation( + parent: _controller, + curve: Curves.easeInOut, + ), + ); + } + + @override + void dispose() { + _controller.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return AnimatedBuilder( + animation: _animation, + builder: (context, child) { + return Opacity( + opacity: _animation.value, + child: Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(16.r), + gradient: RadialGradient( + colors: [ + Colors.white.withOpacity(0.3), + Colors.transparent, + ], + center: Alignment.topRight, + radius: 0.8, + stops: [0.0, 1.0], + ), + ), + ), + ); + }, + ); + } +} + class HomeView extends GetView { const HomeView({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return Scaffold( - backgroundColor: Colors.grey[50], + backgroundColor: Color(0xFFF9FAFC), + appBar: AppBar( + elevation: 0, + backgroundColor: Color(0xFFF9FAFC), + title: Text( + '智能耳机', + style: TextStyle( + color: Colors.black87, + fontSize: 18.sp, + fontWeight: FontWeight.w600, + ), + ), + actions: [ + IconButton( + icon: Icon(Icons.settings_outlined, color: Colors.black54), + onPressed: () { + Get.toNamed(Routes.settings); + }, + ), + ], + ), body: SafeArea( - child: Column( - children: [ - // 状态栏和标题区域 - _buildHeader(), - - // 主要内容区域(可滚动) - Expanded( - child: SingleChildScrollView( - physics: const BouncingScrollPhysics(), - child: Padding( - padding: EdgeInsets.fromLTRB(16.w, 0, 16.w, 16.h), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - // 耳机图片 - _buildEarbudsImage(), - - // 设备状态卡片 - SizedBox(height: 16.h), - _buildDeviceStatusCard(), - - // AI功能部分 - SizedBox(height: 16.h), - _buildAIFunctionsCard(), - - // 固件更新提示 - SizedBox(height: 16.h), - _buildFirmwareUpdateCard(), - - // 快速功能访问 - SizedBox(height: 24.h), - _buildQuickFunctions(), - ], + child: SingleChildScrollView( + padding: EdgeInsets.all(16.w), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // 顶部图片 + Center( + child: _buildEarbudsImage(), + ), + SizedBox(height: 20.h), + + // 设备状态卡片 + _buildDeviceStatusCard(), + SizedBox(height: 20.h), + + // 热门AI体验 + Row( + children: [ + Text( + '热门AI体验', + style: TextStyle( + fontSize: 18.sp, + fontWeight: FontWeight.bold, + ), ), - ), + SizedBox(width: 8.w), + Icon( + Icons.star, + color: Colors.amber, + size: 20.w, + ), + ], ), - ), - ], + SizedBox(height: 16.h), + _buildPremiumAILayout(), + ], + ), ), ), bottomNavigationBar: const CommonBottomNav(currentIndex: 0), @@ -115,24 +194,50 @@ class HomeView extends GetView { alignment: Alignment.center, child: Container( decoration: BoxDecoration( - borderRadius: BorderRadius.circular(16.r), + borderRadius: BorderRadius.circular(20.r), boxShadow: [ BoxShadow( - color: Colors.grey.withOpacity(0.2), - blurRadius: 10, - offset: const Offset(0, 4), + color: Colors.grey.withOpacity(0.15), + blurRadius: 15, + offset: const Offset(0, 5), + ), + BoxShadow( + color: Colors.blue.withOpacity(0.05), + blurRadius: 20, + offset: const Offset(0, -2), ), ], ), child: ClipRRect( - borderRadius: BorderRadius.circular(16.r), + borderRadius: BorderRadius.circular(20.r), child: Container( color: Colors.white, - padding: EdgeInsets.all(16.w), - child: Icon( - Icons.headset, - size: 120.sp, - color: Colors.blueGrey, + padding: EdgeInsets.symmetric(horizontal: 20.w, vertical: 16.h), + child: Stack( + alignment: Alignment.center, + children: [ + // 背景光效 + Positioned.fill( + child: Container( + decoration: BoxDecoration( + gradient: RadialGradient( + colors: [ + Colors.blue.withOpacity(0.05), + Colors.white, + ], + center: Alignment.topRight, + radius: 0.8, + ), + ), + ), + ), + // 耳机图片 + Image.asset( + 'assets/images/headphone_light.png', + height: 150.h, + fit: BoxFit.contain, + ), + ], ), ), ), @@ -396,292 +501,227 @@ class HomeView extends GetView { return SizedBox(); } }), - - // 电池状态 - SizedBox(height: 12.h), - Obx(() => controller.isConnected - ? Row( - children: [ - // 左耳机 - Expanded( - child: _buildBatteryItem('左耳机', controller.leftEarbudBattery, Icons.headset), - ), - SizedBox(width: 12.w), - // 右耳机 - Expanded( - child: _buildBatteryItem('右耳机', controller.rightEarbudBattery, Icons.headset), - ), - SizedBox(width: 12.w), - // 充电盒 - Expanded( - child: _buildBatteryItem('充电盒', controller.caseBattery, Icons.cases_outlined), - ), - ], - ) - : const SizedBox()), ], ), ); } - // 电池状态项 - Widget _buildBatteryItem(String label, int percentage, IconData icon) { - Color batteryColor; - IconData batteryIcon; - - // 根据电量设置颜色和图标 - if (percentage >= 70) { - batteryColor = Colors.green; - batteryIcon = Icons.battery_full; - } else if (percentage >= 30) { - batteryColor = Colors.amber; - batteryIcon = Icons.battery_5_bar; - } else { - batteryColor = Colors.red; - batteryIcon = Icons.battery_1_bar; - } - - return Container( - decoration: BoxDecoration( - color: Colors.grey[100], - borderRadius: BorderRadius.circular(12.r), - boxShadow: [ - BoxShadow( - color: Colors.grey.withOpacity(0.1), - blurRadius: 4, - offset: const Offset(0, 2), - ), - ], - ), - padding: EdgeInsets.all(12.w), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Icon(icon, size: 24.sp, color: Colors.grey[700]), - SizedBox(height: 8.h), - Text( - label, - style: TextStyle( - fontSize: 12.sp, - color: Colors.grey[500], - ), - ), - SizedBox(height: 4.h), - Row( - mainAxisAlignment: MainAxisAlignment.center, + // 优质AI体验布局(左1右2) + Widget _buildPremiumAILayout() { + return Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // 左侧大卡片 - AI助理 + Expanded( + flex: 1, + child: Stack( children: [ - Icon(batteryIcon, size: 14.sp, color: batteryColor), - SizedBox(width: 4.w), - Text( - '$percentage%', - style: TextStyle( - fontSize: 12.sp, - fontWeight: FontWeight.w500, - color: Colors.grey[700], + // 呼吸光效果 + Positioned.fill( + child: Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(16.r), + gradient: RadialGradient( + colors: [ + Color(0xFF4ECDC4).withOpacity(0.6), + Colors.transparent, + ], + center: Alignment.topRight, + radius: 1.0, + ), + ), + child: AnimatedPulsing(), ), ), + // 卡片内容 + _buildFeatureCard( + title: 'AI全能助手', + description: '24小时专属顾问,解答问题、提供建议', + icon: Icons.smart_toy, + gradient: LinearGradient( + begin: Alignment.topLeft, + end: Alignment.bottomRight, + colors: [ + Color(0xFF4ECDC4), + Color(0xFF556270), + ], + ), + onTap: controller.exploreAIFeatures, + iconOnTop: true, + height: 175.h, // 增加高度 + ), ], ), - ], - ), - ); - } - - // AI功能卡片 - Widget _buildAIFunctionsCard() { - return Container( - decoration: BoxDecoration( - gradient: LinearGradient( - begin: Alignment.topLeft, - end: Alignment.bottomRight, - colors: [ - Colors.blue.shade100, - Colors.purple.shade100, - ], ), - borderRadius: BorderRadius.circular(16.r), - boxShadow: [ - BoxShadow( - color: Colors.grey.withOpacity(0.1), - blurRadius: 10, - offset: const Offset(0, 4), - ), - ], - ), - padding: EdgeInsets.all(16.w), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Column( - crossAxisAlignment: CrossAxisAlignment.start, + + SizedBox(width: 16.w), + + // 右侧两个卡片 + Expanded( + flex: 1, + child: Column( children: [ - Text( - 'AI助手', - style: TextStyle( - fontSize: 16.sp, - fontWeight: FontWeight.w500, - color: Colors.grey[800], + // 右上卡片 - 同声翻译 + _buildFeatureCard( + title: '同声传译', + description: '80+语言实时翻译,出国无忧', + icon: Icons.translate, + gradient: LinearGradient( + begin: Alignment.topLeft, + end: Alignment.bottomRight, + colors: [ + Color(0xFF5E72EB), + Color(0xFF4776E6), + ], ), + onTap: controller.openTranslation, + height: 83.h, // 增加高度 ), - SizedBox(height: 4.h), - Text( - '探索DeepVoice的智能功能', - style: TextStyle( - fontSize: 12.sp, - color: Colors.grey[600], + + SizedBox(height: 5.h), // 减少间距 + + // 右下卡片 - AI女友 + _buildFeatureCard( + title: 'AI伴侣', + description: '懂你心意,温暖陪伴', + icon: Icons.favorite, + gradient: LinearGradient( + begin: Alignment.topLeft, + end: Alignment.bottomRight, + colors: [ + Color(0xFFFF758C), + Color(0xFFFF7EB3), + ], ), + onTap: controller.openAIGirlfriend, + height: 85.h, // 增加高度 ), ], ), - ElevatedButton( - onPressed: controller.exploreAIFeatures, - style: ElevatedButton.styleFrom( - backgroundColor: Colors.blue[600], - foregroundColor: Colors.white, - padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 8.h), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(24.r), - ), - elevation: 2, - ), - child: Text( - '探索', - style: TextStyle( - fontSize: 14.sp, - fontWeight: FontWeight.w500, - ), - ), - ), - ], - ), + ), + ], ); } - - // 固件更新卡片 - Widget _buildFirmwareUpdateCard() { - return Container( - decoration: BoxDecoration( - color: Colors.grey[100], - borderRadius: BorderRadius.circular(16.r), - boxShadow: [ - BoxShadow( - color: Colors.grey.withOpacity(0.1), - blurRadius: 10, - offset: const Offset(0, 4), - ), - ], - ), - padding: EdgeInsets.all(16.w), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Row( - children: [ - Container( - width: 40.w, - height: 40.w, - decoration: BoxDecoration( - color: Colors.amber[100], - shape: BoxShape.circle, - ), - child: Icon( - Icons.download, - size: 20.sp, - color: Colors.amber[600], - ), - ), - SizedBox(width: 12.w), - Column( + + // 特性卡片构建方法 + Widget _buildFeatureCard({ + required String title, + required String description, + required IconData icon, + required LinearGradient gradient, + required VoidCallback onTap, + double? height, + bool iconOnTop = false, + }) { + return InkWell( + onTap: onTap, + child: Container( + height: height ?? 175.h, + padding: EdgeInsets.all(14.w), + decoration: BoxDecoration( + gradient: gradient, + borderRadius: BorderRadius.circular(16.r), + boxShadow: [ + BoxShadow( + color: Colors.black.withOpacity(0.1), + blurRadius: 5, + offset: Offset(0, 2), + ), + ], + ), + child: iconOnTop + ? Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ + // 图标靠左而不是居中 + Container( + width: 45.w, + height: 45.w, + decoration: BoxDecoration( + color: Colors.white.withOpacity(0.2), + borderRadius: BorderRadius.circular(12.r), + ), + child: Icon( + icon, + color: Colors.white, + size: 24.w, + ), + ), + SizedBox(height: 8.h), + // 标题 Text( - '固件更新', + title, style: TextStyle( + color: Colors.white, fontSize: 16.sp, - fontWeight: FontWeight.w500, - color: Colors.grey[800], + fontWeight: FontWeight.bold, ), ), - Text( - '${controller.firmwareVersion}可用', - style: TextStyle( - fontSize: 12.sp, - color: Colors.grey[500], + SizedBox(height: 4.h), + // 描述 + Expanded( + child: Text( + description, + style: TextStyle( + color: Colors.white.withOpacity(0.9), + fontSize: 12.sp, + ), + maxLines: 3, + overflow: TextOverflow.ellipsis, + ), + ), + ], + ) + : Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // 图标 + Container( + width: 34.w, + height: 34.w, + decoration: BoxDecoration( + color: Colors.white.withOpacity(0.2), + borderRadius: BorderRadius.circular(8.r), + ), + child: Icon( + icon, + color: Colors.white, + size: 18.w, + ), + ), + SizedBox(width: 10.w), + // 文本内容 + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + // 标题 + Text( + title, + style: TextStyle( + color: Colors.white, + fontSize: 14.sp, + fontWeight: FontWeight.bold, + ), + ), + SizedBox(height: 2.h), + // 描述 + Text( + description, + style: TextStyle( + color: Colors.white.withOpacity(0.9), + fontSize: 10.sp, + ), + maxLines: 2, + overflow: TextOverflow.ellipsis, + ), + ], ), ), ], ), - ], - ), - ElevatedButton( - onPressed: controller.updateFirmware, - style: ElevatedButton.styleFrom( - backgroundColor: Colors.grey[200], - foregroundColor: Colors.grey[700], - padding: EdgeInsets.symmetric(horizontal: 12.w, vertical: 6.h), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(24.r), - ), - elevation: 0, - ), - child: Text( - '更新', - style: TextStyle( - fontSize: 12.sp, - fontWeight: FontWeight.w500, - ), - ), - ), - ], ), ); } - - // 快速功能访问 - Widget _buildQuickFunctions() { - return Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - _buildQuickFunctionItem('降噪', Icons.volume_off, Colors.blue[600]!, Colors.blue[100]!, controller.toggleNoiseReduction), - _buildQuickFunctionItem('音效', Icons.music_note, Colors.purple[600]!, Colors.purple[100]!, controller.openSoundEffects), - _buildQuickFunctionItem('环境音', Icons.eco, Colors.green[600]!, Colors.green[100]!, controller.toggleAmbientSound), - _buildQuickFunctionItem('收藏', Icons.favorite, Colors.red[600]!, Colors.red[100]!, controller.openFavorites), - ], - ); - } - - // 快速功能项 - Widget _buildQuickFunctionItem(String label, IconData icon, Color iconColor, Color bgColor, VoidCallback onTap) { - return Column( - children: [ - Container( - width: 48.w, - height: 48.w, - decoration: BoxDecoration( - color: bgColor, - shape: BoxShape.circle, - boxShadow: [ - BoxShadow( - color: Colors.grey.withOpacity(0.2), - blurRadius: 4, - offset: const Offset(0, 2), - ), - ], - ), - child: IconButton( - icon: Icon(icon, color: iconColor), - onPressed: onTap, - ), - ), - SizedBox(height: 4.h), - Text( - label, - style: TextStyle( - fontSize: 12.sp, - color: Colors.grey[700], - ), - ), - ], - ); - } } \ No newline at end of file diff --git a/lib/modules/pairing/controllers/pairing_controller.dart b/lib/modules/pairing/controllers/pairing_controller.dart index 1ce7d347d..e273b726e 100644 --- a/lib/modules/pairing/controllers/pairing_controller.dart +++ b/lib/modules/pairing/controllers/pairing_controller.dart @@ -8,10 +8,11 @@ import 'package:permission_handler/permission_handler.dart'; import 'package:flutter/material.dart'; enum SearchStatus { - searching, // 正在搜索设备 - found, // 找到设备 - failed, // 搜索失败 - noPermission, // 缺少权限 + searching, // 正在搜索设备 + found, // 找到设备 + failed, // 搜索失败 + noPermission, // 缺少权限 + needsSystemPairing // 需要在系统设置中配对 } class PairingController extends GetxController { @@ -43,15 +44,22 @@ class PairingController extends GetxController { final _errorMessage = ''.obs; String get errorMessage => _errorMessage.value; + // 扫描到的设备列表 + final _scannedDevices = [].obs; + List get scannedDevices => _scannedDevices; + + // 已配对的设备列表 + final _pairedDevices = [].obs; + List get pairedDevices => _pairedDevices; + @override void onInit() { super.onInit(); // 启动搜索动画 _startSearchingAnimation(); - // 延迟更久初始化蓝牙服务,避免在应用启动时就请求权限和阻塞主线程 - // 改为延迟初始化,给UI足够时间渲染 - Future.delayed(const Duration(milliseconds: 1500), () { + // 延迟初始化蓝牙服务,避免在应用启动时就阻塞主线程 + Future.delayed(const Duration(milliseconds: 800), () { _initBluetoothService(); }); } @@ -69,7 +77,7 @@ class PairingController extends GetxController { if (Get.isRegistered()) { _bluetoothService = Get.find(); } else { - // 在后台初始化蓝牙服务 + // 初始化蓝牙服务 _bluetoothService = await Get.put(BluetoothService()).init(); } @@ -81,11 +89,11 @@ class PairingController extends GetxController { ever(_bluetoothService.isOnObs, (bool on) { _isBluetoothOn.value = on; - // 如果蓝牙开启,开始扫描设备 + // 如果蓝牙开启,开始查找设备 if (on) { - // 延迟开始扫描,避免权限请求过早和主线程阻塞 + // 延迟开始查找,给权限请求等操作留出时间 Future.delayed(const Duration(milliseconds: 800), () { - _startScanningDevices(); + _findConnectedDevices(); }); } else { _errorMessage.value = '请打开蓝牙以搜索设备'; @@ -94,12 +102,12 @@ class PairingController extends GetxController { } }); - // 如果蓝牙已经开启,直接开始扫描 + // 如果蓝牙已经开启,直接开始查找 if (_bluetoothService.isOn) { _isBluetoothOn.value = true; - // 延迟开始扫描,避免权限请求过早和主线程阻塞 + // 延迟开始查找 Future.delayed(const Duration(milliseconds: 800), () { - _startScanningDevices(); + _findConnectedDevices(); }); } else { _errorMessage.value = '请打开蓝牙以搜索设备'; @@ -132,8 +140,12 @@ class PairingController extends GetxController { // 请求必要权限 Future _requestPermissions() async { - // 暂时跳过权限检查,直接返回true - return true; + try { + return await _bluetoothService.requestPermissions(); + } catch (e) { + Logger.error('请求权限失败: $e'); + return false; + } } // 打开应用设置 @@ -153,12 +165,21 @@ class PairingController extends GetxController { } } - // 开始扫描蓝牙设备 - Future _startScanningDevices() async { + // 查找已连接的设备 + Future _findConnectedDevices() async { try { _searchStatus.value = SearchStatus.searching; - // 先检查是否有上次连接的设备信息 + // 检查权限 + bool hasPermissions = await _requestPermissions(); + if (!hasPermissions) { + Logger.warning('没有足够的蓝牙权限'); + _searchStatus.value = SearchStatus.noPermission; + _stopSearchingAnimation(); + return; + } + + // 1. 先检查是否有上次连接的设备信息 if (_bluetoothService.lastConnectedDeviceInfo.isNotEmpty && _bluetoothService.lastConnectedDeviceInfo['connected'] == true) { Logger.info('使用上次连接的设备信息'); @@ -167,34 +188,83 @@ class PairingController extends GetxController { return; } - // 使用BluetoothService的自动连接功能查找设备 - Logger.info('尝试自动连接设备'); - BluetoothDevice? device = await _bluetoothService.autoConnectDevice() - .timeout(const Duration(seconds: 8), onTimeout: () { - Logger.info('自动连接设备超时'); + // 2. 查找系统已连接的设备 + Logger.info('查找系统已连接的设备'); + BluetoothDevice? device = await _bluetoothService.findConnectedSystemDevice() + .timeout(const Duration(seconds: 5), onTimeout: () { + Logger.info('查找系统已连接设备超时'); return null; }); if (device != null) { - Logger.info('自动连接设备成功: ${device.platformName}'); + Logger.info('找到系统已连接设备: ${device.platformName}'); _deviceInfo.value = _bluetoothService.getConnectedDeviceInfo(); _onDeviceFound(); return; } - // 如果自动连接失败,尝试扫描新设备 - Logger.info('自动连接失败,开始扫描新设备'); - device = await _bluetoothService.scanAndConnectToDevice( - timeout: const Duration(seconds: 5) - ).timeout(const Duration(seconds: 6), onTimeout: () { - Logger.info('扫描并连接设备超时'); - return null; - }); + // 3. 如果没有找到已连接设备,提示用户选择 + // 获取已配对设备列表 + _pairedDevices.value = _bluetoothService.systemPairedDevices; - if (device != null) { - Logger.info('扫描并连接设备成功: ${device.platformName}'); - _deviceInfo.value = _bluetoothService.getConnectedDeviceInfo(); - _onDeviceFound(); + // 如果有已配对的设备,直接尝试连接第一个 + if (_pairedDevices.isNotEmpty) { + Logger.info('找到${_pairedDevices.length}个已配对设备,尝试连接第一个'); + + // 尝试连接第一个配对设备 + await tryConnectDevice(_pairedDevices.first); + } else { + // 如果没有已配对设备,开始扫描附近设备 + await _scanForDevices(); + } + } catch (e) { + Logger.error('查找设备失败: $e'); + _errorMessage.value = '查找设备失败: ${e.toString()}'; + _searchStatus.value = SearchStatus.failed; + _stopSearchingAnimation(); + } + } + + // 扫描附近的蓝牙设备 + Future _scanForDevices() async { + try { + Logger.info('开始扫描附近设备'); + _searchStatus.value = SearchStatus.searching; + _startSearchingAnimation(); + + // 开始扫描 + List results = await _bluetoothService.startScan( + timeout: const Duration(seconds: 10) + ); + + _scannedDevices.value = results; + + // 过滤出可能是耳机的设备 + List audioDevices = results.where((result) { + String name = result.device.platformName.toLowerCase(); + return name.contains('headphone') || + name.contains('earphone') || + name.contains('earbuds') || + name.contains('headset') || + name.contains('speaker') || + name.contains('audio') || + name.contains('耳机') || + name.contains('音箱'); + }).toList(); + + if (audioDevices.isNotEmpty) { + // 按信号强度排序 + audioDevices.sort((a, b) => b.rssi.compareTo(a.rssi)); + Logger.info('找到可能的音频设备: ${audioDevices.first.device.platformName}'); + + // 直接尝试连接信号最强的音频设备 + await tryConnectDevice(audioDevices.first.device); + } else if (results.isNotEmpty) { + // 如果没有明确的音频设备但有其他设备,尝试连接信号最强的设备 + results.sort((a, b) => b.rssi.compareTo(a.rssi)); + Logger.info('未找到明确的音频设备,尝试连接信号最强的设备: ${results.first.device.platformName}'); + + await tryConnectDevice(results.first.device); } else { // 未找到任何设备 _errorMessage.value = '未找到蓝牙设备,请确保设备已开启并在附近'; @@ -202,8 +272,45 @@ class PairingController extends GetxController { _stopSearchingAnimation(); } } catch (e) { - Logger.error('设备搜索和连接失败: $e'); - _errorMessage.value = '设备搜索失败: ${e.toString()}'; + Logger.error('扫描设备失败: $e'); + _errorMessage.value = '扫描设备失败: ${e.toString()}'; + _searchStatus.value = SearchStatus.failed; + _stopSearchingAnimation(); + } + } + + // 尝试连接特定设备 + Future tryConnectDevice(BluetoothDevice device) async { + try { + Logger.info('尝试连接设备: ${device.platformName}'); + _searchStatus.value = SearchStatus.searching; + _startSearchingAnimation(); + _errorMessage.value = ''; + + bool connected = await _bluetoothService.connectToDevice(device); + + if (connected) { + Logger.info('连接设备成功: ${device.platformName}'); + _deviceInfo.value = _bluetoothService.getConnectedDeviceInfo(); + _onDeviceFound(); + } else { + Logger.error('连接设备失败: ${device.platformName}'); + + // 检查设备类型 + if (_bluetoothService.deviceType == DeviceType.classicAudio) { + // 对于经典蓝牙音频设备,提示用户在系统设置中配对 + _searchStatus.value = SearchStatus.needsSystemPairing; + _errorMessage.value = '这是音频设备,需要在系统设置中配对并连接'; + } else { + // 其他类型设备连接失败 + _searchStatus.value = SearchStatus.failed; + _errorMessage.value = '连接设备失败,请重试或检查设备状态'; + } + _stopSearchingAnimation(); + } + } catch (e) { + Logger.error('连接设备失败: $e'); + _errorMessage.value = '连接设备失败: ${e.toString()}'; _searchStatus.value = SearchStatus.failed; _stopSearchingAnimation(); } @@ -221,8 +328,8 @@ class PairingController extends GetxController { }); } - // 手动连接 - void manualConnect() { + // 手动刷新/重新搜索 + void refreshSearch() { // 停止当前扫描(如果有) _bluetoothService.stopScan(); @@ -231,8 +338,40 @@ class PairingController extends GetxController { _searchStatus.value = SearchStatus.searching; _errorMessage.value = ''; - // 直接开始扫描并连接 - _startScanningDevices(); + // 重新开始查找过程 + _findConnectedDevices(); + } + + // 跳转到系统蓝牙设置 + void openSystemBluetoothSettings() async { + try { + Logger.info('尝试打开系统蓝牙设置'); + + // 不同平台需要不同的处理 + if (GetPlatform.isAndroid) { + await openAppSettings(); + } else { + // iOS不能直接打开蓝牙设置 + Get.snackbar( + '提示', + '请手动打开系统设置并配对您的蓝牙设备', + snackPosition: SnackPosition.TOP, + backgroundColor: Colors.blue[100], + colorText: Colors.blue[800], + duration: const Duration(seconds: 3), + ); + } + } catch (e) { + Logger.error('打开系统蓝牙设置失败: $e'); + Get.snackbar( + '提示', + '无法打开系统设置,请手动打开蓝牙设置并配对设备', + snackPosition: SnackPosition.TOP, + backgroundColor: Colors.red[100], + colorText: Colors.red[800], + duration: const Duration(seconds: 3), + ); + } } // 稍后再说 diff --git a/lib/modules/pairing/views/pairing_view.dart b/lib/modules/pairing/views/pairing_view.dart index e93309447..78eb730f6 100644 --- a/lib/modules/pairing/views/pairing_view.dart +++ b/lib/modules/pairing/views/pairing_view.dart @@ -30,24 +30,26 @@ class PairingView extends GetView { Widget _buildMainContent() { return Padding( padding: EdgeInsets.fromLTRB(24.w, 40.h, 24.w, 24.h), - child: Column( - children: [ - // 标题部分 - _buildHeader(), - - // 设备动画 - SizedBox(height: 40.h), - _buildDeviceAnimation(), - - // 搜索状态 - SizedBox(height: 40.h), - _buildSearchStatus(), - - // 底部按钮 - const Spacer(), - _buildBottomButtons(), - SizedBox(height: 80.h), // 留出底部导航的空间 - ], + child: SingleChildScrollView( + child: Column( + children: [ + // 标题部分 + _buildHeader(), + + // 设备动画 + SizedBox(height: 40.h), + _buildDeviceAnimation(), + + // 搜索状态 + SizedBox(height: 40.h), + _buildSearchStatus(), + + // 底部按钮 + SizedBox(height: 60.h), + _buildBottomButtons(), + SizedBox(height: 80.h), // 留出底部导航的空间 + ], + ), ), ); } @@ -288,6 +290,49 @@ class PairingView extends GetView { ), ], ); + case SearchStatus.needsSystemPairing: + return Column( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon(Icons.bluetooth_searching, color: Colors.blue, size: 20.sp), + SizedBox(width: 8.w), + Text( + '需要系统配对', + style: TextStyle( + fontSize: 18.sp, + fontWeight: FontWeight.w600, + color: Colors.blue, + ), + ), + ], + ), + SizedBox(height: 8.h), + Text( + controller.errorMessage.isEmpty ? + '请在系统设置中配对您的设备' : controller.errorMessage, + style: TextStyle( + fontSize: 14.sp, + color: Colors.grey[600], + ), + textAlign: TextAlign.center, + ), + SizedBox(height: 16.h), + ElevatedButton( + onPressed: () => controller.openSystemBluetoothSettings(), + style: ElevatedButton.styleFrom( + backgroundColor: Colors.blue, + foregroundColor: Colors.white, + padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 8.h), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8.r), + ), + ), + child: Text('打开蓝牙设置'), + ), + ], + ); default: return SizedBox(); } @@ -301,7 +346,7 @@ class PairingView extends GetView { SizedBox( width: double.infinity, child: ElevatedButton( - onPressed: controller.manualConnect, + onPressed: controller.refreshSearch, style: ElevatedButton.styleFrom( backgroundColor: Colors.grey[200], foregroundColor: Colors.grey[800], diff --git a/lib/modules/settings/controllers/settings_controller.dart b/lib/modules/settings/controllers/settings_controller.dart index 7d0394484..ba561cc20 100644 --- a/lib/modules/settings/controllers/settings_controller.dart +++ b/lib/modules/settings/controllers/settings_controller.dart @@ -2,49 +2,11 @@ import 'package:get/get.dart'; import 'package:flutter/material.dart'; class SettingsController extends GetxController { - // 噪声消除开关状态 - final RxBool noiseReductionEnabled = true.obs; - - // 环境音增强模式 - final RxString ambientSoundMode = '中等'.obs; - final List availableAmbientModes = ['关闭', '低', '中等', '高']; - - // 立体声平衡值 - final RxDouble stereoBalance = 0.0.obs; // -1.0 (左) 到 1.0 (右), 0 为中间 - - // AI助理默认模式 - final RxString aiAssistantMode = '通用助手'.obs; - final List availableAiModes = ['通用助手', '翻译助手', '学习助手', '交际助手']; - - // AI声音选择 - final RxString aiVoice = '默认女声'.obs; - final List availableVoices = ['默认女声', '默认男声', '温柔女声', '磁性男声', '活力女声']; - // 翻译语言对 final RxString sourceLanguage = '中文'.obs; final RxString targetLanguage = '英文'.obs; final List availableLanguages = ['中文', '英文', '日文', '韩文', '法文', '德文', '西班牙文', '俄文']; - void toggleNoiseReduction(bool value) { - noiseReductionEnabled.value = value; - } - - void updateAmbientSoundMode(String mode) { - ambientSoundMode.value = mode; - } - - void updateStereoBalance(double value) { - stereoBalance.value = value; - } - - void updateAiAssistantMode(String mode) { - aiAssistantMode.value = mode; - } - - void updateAiVoice(String voice) { - aiVoice.value = voice; - } - void setSourceLanguage(String language) { sourceLanguage.value = language; } diff --git a/lib/modules/settings/views/settings_view.dart b/lib/modules/settings/views/settings_view.dart index 74e3d162d..60f95fb09 100644 --- a/lib/modules/settings/views/settings_view.dart +++ b/lib/modules/settings/views/settings_view.dart @@ -34,10 +34,6 @@ class SettingsView extends GetView { children: [ _buildUserInfoCard(), SizedBox(height: 20.h), - _buildHeadphoneSettingsSection(), - SizedBox(height: 20.h), - _buildAISettingsSection(), - SizedBox(height: 20.h), _buildSystemSettingsSection(), SizedBox(height: 20.h), _buildAboutSection(), @@ -169,473 +165,6 @@ class SettingsView extends GetView { ); } - // 耳机设置部分 - Widget _buildHeadphoneSettingsSection() { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Padding( - padding: EdgeInsets.only(left: 4.w, bottom: 8.h), - child: Text( - '耳机设置', - style: TextStyle( - fontSize: 12.sp, - fontWeight: FontWeight.w500, - color: Colors.blue[600], - letterSpacing: 1, - ), - ), - ), - Container( - decoration: BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.circular(16.r), - boxShadow: [ - BoxShadow( - color: Colors.black.withOpacity(0.05), - blurRadius: 10, - offset: const Offset(0, 2), - ), - ], - ), - child: Column( - children: [ - // 噪声消除 - Obx(() => _buildToggleSetting( - title: '噪声消除', - subtitle: '降低周围环境噪音', - icon: Icons.volume_off_rounded, - iconBgColor: Colors.blue[100]!, - iconColor: Colors.blue[600]!, - value: controller.noiseReductionEnabled.value, - onChanged: controller.toggleNoiseReduction, - )), - const Divider(height: 1), - - // 环境音增强 - _buildNavigationSetting( - title: '环境音增强', - subtitle: '增强周围重要声音', - icon: Icons.nature, - iconBgColor: Colors.green[100]!, - iconColor: Colors.green[600]!, - value: controller.ambientSoundMode.value, - onTap: () { - // 弹出环境音增强设置对话框 - Get.dialog( - Dialog( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(16.r), - ), - child: Padding( - padding: EdgeInsets.all(16.w), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - Text( - '环境音增强', - style: TextStyle( - fontSize: 18.sp, - fontWeight: FontWeight.w600, - ), - ), - SizedBox(height: 16.h), - ...controller.availableAmbientModes.map((mode) => - RadioListTile( - title: Text(mode), - value: mode, - groupValue: controller.ambientSoundMode.value, - onChanged: (value) { - if (value != null) { - controller.updateAmbientSoundMode(value); - Get.back(); - } - }, - ), - ).toList(), - SizedBox(height: 16.h), - TextButton( - onPressed: () => Get.back(), - child: const Text('取消'), - ), - ], - ), - ), - ), - ); - }, - ), - const Divider(height: 1), - - // 立体声平衡 - Padding( - padding: EdgeInsets.all(16.w), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - children: [ - Container( - width: 32.w, - height: 32.w, - decoration: BoxDecoration( - color: Colors.purple[100], - borderRadius: BorderRadius.circular(16.r), - ), - child: Icon( - Icons.balance, - size: 16.w, - color: Colors.purple[600], - ), - ), - SizedBox(width: 12.w), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - '立体声平衡', - style: TextStyle( - fontSize: 16.sp, - fontWeight: FontWeight.w500, - color: Colors.black87, - ), - ), - SizedBox(height: 2.h), - Text( - '调整左右耳机音量平衡', - style: TextStyle( - fontSize: 12.sp, - color: Colors.black54, - ), - ), - ], - ), - ], - ), - SizedBox(height: 16.h), - Obx(() => Column( - children: [ - SliderTheme( - data: SliderThemeData( - trackHeight: 4.h, - thumbShape: RoundSliderThumbShape(enabledThumbRadius: 8.r), - ), - child: Slider( - value: controller.stereoBalance.value, - min: -1.0, - max: 1.0, - onChanged: controller.updateStereoBalance, - ), - ), - Padding( - padding: EdgeInsets.symmetric(horizontal: 8.w), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - '左', - style: TextStyle( - fontSize: 12.sp, - color: Colors.black54, - ), - ), - Text( - '平衡', - style: TextStyle( - fontSize: 12.sp, - color: Colors.black54, - ), - ), - Text( - '右', - style: TextStyle( - fontSize: 12.sp, - color: Colors.black54, - ), - ), - ], - ), - ), - ], - )), - ], - ), - ), - ], - ), - ), - ], - ); - } - - // AI设置部分 - Widget _buildAISettingsSection() { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Padding( - padding: EdgeInsets.only(left: 4.w, bottom: 8.h), - child: Text( - 'AI 设置', - style: TextStyle( - fontSize: 12.sp, - fontWeight: FontWeight.w500, - color: Colors.blue[600], - letterSpacing: 1, - ), - ), - ), - Container( - decoration: BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.circular(16.r), - boxShadow: [ - BoxShadow( - color: Colors.black.withOpacity(0.05), - blurRadius: 10, - offset: const Offset(0, 2), - ), - ], - ), - child: Column( - children: [ - // AI助理默认模式 - _buildNavigationSetting( - title: 'AI助理默认模式', - subtitle: '选择默认的AI互动风格', - icon: Icons.android, - iconBgColor: Colors.red[100]!, - iconColor: Colors.red[600]!, - value: controller.aiAssistantMode.value, - onTap: () { - // 弹出AI助理模式选择对话框 - Get.dialog( - Dialog( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(16.r), - ), - child: Padding( - padding: EdgeInsets.all(16.w), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - Text( - 'AI助理模式', - style: TextStyle( - fontSize: 18.sp, - fontWeight: FontWeight.w600, - ), - ), - SizedBox(height: 16.h), - ...controller.availableAiModes.map((mode) => - RadioListTile( - title: Text(mode), - value: mode, - groupValue: controller.aiAssistantMode.value, - onChanged: (value) { - if (value != null) { - controller.updateAiAssistantMode(value); - Get.back(); - } - }, - ), - ).toList(), - SizedBox(height: 16.h), - TextButton( - onPressed: () => Get.back(), - child: const Text('取消'), - ), - ], - ), - ), - ), - ); - }, - ), - const Divider(height: 1), - - // AI声音选择 - _buildNavigationSetting( - title: 'AI声音选择', - subtitle: '选择AI的声音和音调', - icon: Icons.record_voice_over, - iconBgColor: Colors.amber[100]!, - iconColor: Colors.amber[600]!, - value: controller.aiVoice.value, - onTap: () { - // 弹出AI声音选择对话框 - Get.dialog( - Dialog( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(16.r), - ), - child: Padding( - padding: EdgeInsets.all(16.w), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - Text( - 'AI声音选择', - style: TextStyle( - fontSize: 18.sp, - fontWeight: FontWeight.w600, - ), - ), - SizedBox(height: 16.h), - ...controller.availableVoices.map((voice) => - RadioListTile( - title: Text(voice), - value: voice, - groupValue: controller.aiVoice.value, - onChanged: (value) { - if (value != null) { - controller.updateAiVoice(value); - Get.back(); - } - }, - ), - ).toList(), - SizedBox(height: 16.h), - TextButton( - onPressed: () => Get.back(), - child: const Text('取消'), - ), - ], - ), - ), - ), - ); - }, - ), - const Divider(height: 1), - - // 翻译语言 - Padding( - padding: EdgeInsets.all(16.w), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - // 左侧标题和图标部分 - Flexible( - child: Row( - children: [ - Container( - width: 32.w, - height: 32.w, - decoration: BoxDecoration( - color: Colors.teal[100], - borderRadius: BorderRadius.circular(16.r), - ), - child: Icon( - Icons.translate, - size: 16.w, - color: Colors.teal[600], - ), - ), - SizedBox(width: 12.w), - Flexible( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - '翻译语言', - style: TextStyle( - fontSize: 16.sp, - fontWeight: FontWeight.w500, - color: Colors.black87, - ), - overflow: TextOverflow.ellipsis, - ), - SizedBox(height: 2.h), - Text( - '设置需要翻译的语言', - style: TextStyle( - fontSize: 12.sp, - color: Colors.black54, - ), - overflow: TextOverflow.ellipsis, - ), - ], - ), - ), - ], - ), - ), - // 右侧语言选择部分 - Obx(() => Row( - mainAxisSize: MainAxisSize.min, - children: [ - GestureDetector( - onTap: () { - _showLanguageSelectionDialog(true); - }, - child: Container( - padding: EdgeInsets.symmetric(horizontal: 8.w, vertical: 4.h), - decoration: BoxDecoration( - color: Colors.blue[600], - borderRadius: BorderRadius.circular(8.r), - ), - child: Text( - controller.sourceLanguage.value, - style: TextStyle( - fontSize: 12.sp, - color: Colors.white, - ), - ), - ), - ), - IconButton( - padding: EdgeInsets.zero, - constraints: BoxConstraints(), - icon: const Icon(Icons.swap_horiz, size: 20), - onPressed: controller.swapLanguages, - color: Colors.grey[500], - ), - GestureDetector( - onTap: () { - _showLanguageSelectionDialog(false); - }, - child: Container( - padding: EdgeInsets.symmetric(horizontal: 8.w, vertical: 4.h), - decoration: BoxDecoration( - color: Colors.grey[200], - borderRadius: BorderRadius.circular(8.r), - ), - child: Text( - controller.targetLanguage.value, - style: TextStyle( - fontSize: 12.sp, - color: Colors.grey[700], - ), - ), - ), - ), - SizedBox(width: 4.w), - Container( - width: 32.w, - height: 32.w, - decoration: BoxDecoration( - color: Colors.grey[100], - borderRadius: BorderRadius.circular(16.r), - ), - child: Icon( - Icons.add, - size: 16.w, - color: Colors.grey[500], - ), - ), - ], - )), - ], - ), - ), - ], - ), - ), - ], - ); - } - // 系统设置部分 Widget _buildSystemSettingsSection() { return Column( @@ -1099,60 +628,4 @@ class SettingsView extends GetView { ), ); } - - // 显示语言选择对话框 - void _showLanguageSelectionDialog(bool isSource) { - Get.dialog( - Dialog( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(16.r), - ), - child: Padding( - padding: EdgeInsets.all(16.w), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - Text( - isSource ? '选择源语言' : '选择目标语言', - style: TextStyle( - fontSize: 18.sp, - fontWeight: FontWeight.w600, - ), - ), - SizedBox(height: 16.h), - SizedBox( - height: 300.h, - child: ListView( - children: controller.availableLanguages.map((language) => - RadioListTile( - title: Text(language), - value: language, - groupValue: isSource - ? controller.sourceLanguage.value - : controller.targetLanguage.value, - onChanged: (value) { - if (value != null) { - if (isSource) { - controller.setSourceLanguage(value); - } else { - controller.setTargetLanguage(value); - } - Get.back(); - } - }, - ), - ).toList(), - ), - ), - SizedBox(height: 16.h), - TextButton( - onPressed: () => Get.back(), - child: const Text('取消'), - ), - ], - ), - ), - ), - ); - } } \ No newline at end of file diff --git a/lib/modules/translation/controllers/translation_controller.dart b/lib/modules/translation/controllers/translation_controller.dart index 9f953dc1c..898f12fe6 100644 --- a/lib/modules/translation/controllers/translation_controller.dart +++ b/lib/modules/translation/controllers/translation_controller.dart @@ -131,6 +131,8 @@ class TranslationController extends GetxController { final sourceCode = supportedLanguages[sourceLanguage.value] ?? 'zh'; final recognitionLanguage = _getRecognitionLanguage(sourceCode); + // 使用正确定义的变量顺序 + await _ttsService.initialize(); await _asrService.initialize(language: recognitionLanguage); Logger.info('Azure ASR 服务初始化成功'); } catch (e) { @@ -425,11 +427,11 @@ class TranslationController extends GetxController { try { // 获取目标语言代码 - final targetCode = supportedLanguages[targetLanguage.value] ?? 'en'; + // final targetCode = supportedLanguages[targetLanguage.value] ?? 'en'; - // 设置合适的语音 - final voiceName = _getTtsVoiceName(targetCode); - await _ttsService.setVoice(voiceName); + // // 设置合适的语音 + // final voiceName = _getTtsVoiceName(targetCode); + // await _ttsService.setVoice(voiceName); // 播放文本 await _ttsService.speak(text); diff --git a/pubspec.yaml b/pubspec.yaml index 73a536869..4d6a5b4a4 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -55,6 +55,8 @@ flutter: uses-material-design: true assets: - - assets/images/headphone.png + - assets/images/headphone_light.png + - assets/images/headphone_dark.png - assets/images/agents/ + - assets/images/explore.png - .env