Browse Source

bluteeth fix

newdev_shunjiawei
wolfplus 2 years ago
parent
commit
c5094e6551
  1. 1
      .cursorrules
  2. 4
      android/app/src/main/kotlin/com/example/deep_voice/AzureAsrHelper.kt
  3. 17
      android/app/src/main/kotlin/com/example/deep_voice/MainActivity.kt
  4. 2
      android/app/src/main/kotlin/com/example/deep_voice/VoiceInteractionService.kt
  5. BIN
      assets/images/explore.png
  6. BIN
      assets/images/headphone.png
  7. BIN
      assets/images/headphone_dark.png
  8. BIN
      assets/images/headphone_light.png
  9. 599
      lib/data/services/bluetooth_service.dart
  10. 3
      lib/data/services/voice_interaction_service.dart
  11. 4
      lib/modules/explore/controllers/explore_controller.dart
  12. 4
      lib/modules/explore/views/explore_view.dart
  13. 447
      lib/modules/home/controllers/home_controller.dart
  14. 642
      lib/modules/home/views/home_view.dart
  15. 225
      lib/modules/pairing/controllers/pairing_controller.dart
  16. 83
      lib/modules/pairing/views/pairing_view.dart
  17. 38
      lib/modules/settings/controllers/settings_controller.dart
  18. 527
      lib/modules/settings/views/settings_view.dart
  19. 10
      lib/modules/translation/controllers/translation_controller.dart
  20. 4
      pubspec.yaml

1
.cursorrules

@ -7,3 +7,4 @@
7. 尽可能避免打印日志,仅在出错时记录日志 7. 尽可能避免打印日志,仅在出错时记录日志
8. 请使用较小的代码量改动,最安全的方法进行代码编写和优化 8. 请使用较小的代码量改动,最安全的方法进行代码编写和优化
9. 请尽可能使用中文回复问题 9. 请尽可能使用中文回复问题
10. 重要:不要过度设计!!

4
android/app/src/main/kotlin/com/example/deep_voice/AzureAsrHelper.kt

@ -106,8 +106,8 @@ class AzureAsrHelper {
try { try {
// 先发送会话开始事件 // 先发送会话开始事件
Log.d(TAG, "准备开始连续语音识别") // Log.d(TAG, "准备开始连续语音识别")
callback.onSessionStarted() // callback.onSessionStarted()
// 重新创建识别器 // 重新创建识别器
Log.d(TAG, "重新创建识别器") Log.d(TAG, "重新创建识别器")

17
android/app/src/main/kotlin/com/example/deep_voice/MainActivity.kt

@ -69,9 +69,9 @@ class MainActivity: FlutterActivity() {
// 使用伴生对象存储配置信息,使其在整个应用中可访问 // 使用伴生对象存储配置信息,使其在整个应用中可访问
companion object { companion object {
// Azure 语音服务配置 // Azure 语音服务配置
var azureSpeechKey: String = "2K2VOHSuL8ZSEnbKgHSyTwo1ohj7C2gbxyVimsjpVUt6aETxVwlvJQQJ99BCACqBBLyXJ3w3AAAYACOGqCMj" var azureSpeechKey: String = ""
var azureSpeechRegion: String = "southeastasia" var azureSpeechRegion: String = ""
var volcanoAiApiKey: String = "168deb3d-fd0c-4912-b9f1-aaee5c6743e6" var volcanoAiApiKey: String = ""
// 标记是否已初始化 // 标记是否已初始化
var isAzureHelpersInitialized = false var isAzureHelpersInitialized = false
@ -99,9 +99,8 @@ class MainActivity: FlutterActivity() {
// 注册广播接收器 // 注册广播接收器
registerVoiceInteractionReceiver() registerVoiceInteractionReceiver()
// 自动启动语音交互服务 // 自动启动语音交互服务功能已被移除
Log.d(TAG, "MainActivity 已创建,自动启动 VoiceInteractionService") Log.d(TAG, "MainActivity 已创建")
autoStartVoiceInteractionService()
// 确保 Flutter 引擎准备好后立即设置事件通道 // 确保 Flutter 引擎准备好后立即设置事件通道
flutterEngine?.let { engine -> 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 "空"}") 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 并启动服务 // 创建 Intent 并启动服务
val serviceIntent = Intent(this, VoiceInteractionService::class.java) val serviceIntent = Intent(this, VoiceInteractionService::class.java)

2
android/app/src/main/kotlin/com/example/deep_voice/VoiceInteractionService.kt

@ -375,7 +375,7 @@ class VoiceInteractionService : Service() {
return return
} }
// 通知 Flutter 语音识别已启动 // 通知 Flutter 语音识别已启动
notifyRecognitionStarted() notifyRecognitionStarted()
isActive = true isActive = true
isRecognitionActive = true isRecognitionActive = true

BIN
assets/images/explore.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 437 KiB

BIN
assets/images/headphone.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 156 KiB

BIN
assets/images/headphone_dark.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 561 KiB

BIN
assets/images/headphone_light.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 140 KiB

599
lib/data/services/bluetooth_service.dart

@ -6,6 +6,14 @@ import '../../core/utils/logger.dart';
import 'dart:io' show Platform; import 'dart:io' show Platform;
import 'package:permission_handler/permission_handler.dart'; import 'package:permission_handler/permission_handler.dart';
/// 设备类型枚举
enum DeviceType {
unknown, // 未知设备类型
classicAudio, // 经典蓝牙音频设备(耳机等)
ble, // BLE设备
dual // 双模设备(同时支持经典蓝牙和BLE)
}
/// 蓝牙设备服务,用于发现和管理蓝牙设备 /// 蓝牙设备服务,用于发现和管理蓝牙设备
class BluetoothService extends GetxService { class BluetoothService extends GetxService {
static BluetoothService get to => Get.find<BluetoothService>(); static BluetoothService get to => Get.find<BluetoothService>();
@ -21,6 +29,7 @@ class BluetoothService extends GetxService {
// 已连接的设备 // 已连接的设备
final _connectedDevices = <BluetoothDevice>[].obs; final _connectedDevices = <BluetoothDevice>[].obs;
final _systemPairedDevices = <BluetoothDevice>[].obs;
// 扫描状态 // 扫描状态
final _isScanning = false.obs; final _isScanning = false.obs;
@ -30,6 +39,7 @@ class BluetoothService extends GetxService {
// 当前选中的设备 // 当前选中的设备
final _selectedDevice = Rx<BluetoothDevice?>(null); final _selectedDevice = Rx<BluetoothDevice?>(null);
final _deviceType = Rx<DeviceType>(DeviceType.unknown);
// 连接状态 // 连接状态
final _connectionState = Rx<BluetoothConnectionState>(BluetoothConnectionState.disconnected); final _connectionState = Rx<BluetoothConnectionState>(BluetoothConnectionState.disconnected);
@ -41,6 +51,13 @@ class BluetoothService extends GetxService {
StreamSubscription? _stateSubscription; StreamSubscription? _stateSubscription;
StreamSubscription? _scanSubscription; StreamSubscription? _scanSubscription;
StreamSubscription? _connectionSubscription; StreamSubscription? _connectionSubscription;
// 连接锁,防止并发操作
bool _isConnecting = false;
// 记录连接尝试次数
int _connectionAttempts = 0;
static const int MAX_CONNECTION_ATTEMPTS = 2;
// 权限状态 // 权限状态
final _permissionsGranted = false.obs; final _permissionsGranted = false.obs;
@ -51,8 +68,10 @@ class BluetoothService extends GetxService {
bool get isOn => _isOn.value; bool get isOn => _isOn.value;
bool get isScanning => _isScanning.value; bool get isScanning => _isScanning.value;
List<BluetoothDevice> get connectedDevices => _connectedDevices; List<BluetoothDevice> get connectedDevices => _connectedDevices;
List<BluetoothDevice> get systemPairedDevices => _systemPairedDevices;
List<ScanResult> get scanResults => _scanResults; List<ScanResult> get scanResults => _scanResults;
BluetoothDevice? get selectedDevice => _selectedDevice.value; BluetoothDevice? get selectedDevice => _selectedDevice.value;
DeviceType get deviceType => _deviceType.value;
BluetoothConnectionState get connectionState => _connectionState.value; BluetoothConnectionState get connectionState => _connectionState.value;
Map<String, dynamic> get lastConnectedDeviceInfo => _lastConnectedDeviceInfo.value; Map<String, dynamic> get lastConnectedDeviceInfo => _lastConnectedDeviceInfo.value;
@ -62,16 +81,27 @@ class BluetoothService extends GetxService {
RxList<BluetoothDevice> get connectedDevicesObs => _connectedDevices; RxList<BluetoothDevice> get connectedDevicesObs => _connectedDevices;
RxList<ScanResult> get scanResultsObs => _scanResults; RxList<ScanResult> get scanResultsObs => _scanResults;
Rx<BluetoothDevice?> get selectedDeviceObs => _selectedDevice; Rx<BluetoothDevice?> get selectedDeviceObs => _selectedDevice;
Rx<DeviceType> get deviceTypeObs => _deviceType;
Rx<BluetoothConnectionState> get connectionStateObs => _connectionState; Rx<BluetoothConnectionState> get connectionStateObs => _connectionState;
Rx<Map<String, dynamic>> get lastConnectedDeviceInfoObs => _lastConnectedDeviceInfo; Rx<Map<String, dynamic>> 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<BluetoothService> init() async { Future<BluetoothService> init() async {
try { try {
// 不在初始化时检查权限,改为在需要时才检查 // 确保权限检查在后台线程执行,避免主线程阻塞
// await _checkAndRequestPermissions(); Future.microtask(() async {
// 仅在Android上请求权限
if (Platform.isAndroid) {
await _checkAndRequestPermissions();
}
});
// 在后台线程初始化蓝牙状态监听,避免主线程阻塞 // 在后台线程初始化蓝牙状态监听
Future.microtask(() { Future.microtask(() {
// 监听蓝牙状态 // 监听蓝牙状态
_stateSubscription = FlutterBluePlus.adapterState.listen((state) { _stateSubscription = FlutterBluePlus.adapterState.listen((state) {
@ -79,25 +109,24 @@ class BluetoothService extends GetxService {
_isAvailable.value = state != BluetoothAdapterState.unavailable; _isAvailable.value = state != BluetoothAdapterState.unavailable;
if (state == BluetoothAdapterState.on) { if (state == BluetoothAdapterState.on) {
// 蓝牙打开时,获取已连接设备,但不请求权限 // 蓝牙打开时,获取已连接和已配对设备
// 只有在用户主动扫描时才请求权限 _refreshSystemDevices();
// 在后台线程执行,避免主线程阻塞
Future.microtask(() => _getConnectedDevices());
} else if (state == BluetoothAdapterState.off) { } else if (state == BluetoothAdapterState.off) {
// 蓝牙关闭时,清空设备列表 // 蓝牙关闭时,清空设备列表
_connectedDevices.clear(); _connectedDevices.clear();
_systemPairedDevices.clear();
_scanResults.clear(); _scanResults.clear();
_selectedDevice.value = null; _selectedDevice.value = null;
_lastConnectedDeviceInfo.value = {}; _lastConnectedDeviceInfo.value = {};
_deviceType.value = DeviceType.unknown;
} }
}); });
}); });
// 不在初始化时立即检查蓝牙状态,而是延迟到UI渲染完成后 // 延迟检查蓝牙状态,避免启动时卡顿
// 这样可以避免启动时卡顿
Future.delayed(const Duration(milliseconds: 800), () async { Future.delayed(const Duration(milliseconds: 800), () async {
try { try {
// 使用超时机制,避免无限等待蓝牙 // 使用超时机制
bool isOn = await FlutterBluePlus.isOn bool isOn = await FlutterBluePlus.isOn
.timeout(const Duration(seconds: 3), onTimeout: () { .timeout(const Duration(seconds: 3), onTimeout: () {
Logger.error('检查蓝牙状态超时'); Logger.error('检查蓝牙状态超时');
@ -108,8 +137,8 @@ class BluetoothService extends GetxService {
_isAvailable.value = isOn; _isAvailable.value = isOn;
if (isOn) { if (isOn) {
// 在后台线程获取设备,避免主线程阻塞 // 在后台刷新设备状态
Future.microtask(() => _getConnectedDevices()); _refreshSystemDevices();
} }
} catch (e) { } catch (e) {
Logger.error('延迟检查蓝牙状态失败: $e'); Logger.error('延迟检查蓝牙状态失败: $e');
@ -123,60 +152,143 @@ class BluetoothService extends GetxService {
} }
} }
/// 检查并请求蓝牙所需权限 /// 检查并请求蓝牙所需权限(Android 12+)
Future<bool> _checkAndRequestPermissions() async { Future<bool> _checkAndRequestPermissions() async {
// 暂时跳过权限检查,直接返回true try {
_permissionsGranted.value = true; if (Platform.isAndroid) {
return true; // 对于Android 12+,需要BLUETOOTH_SCAN和BLUETOOTH_CONNECT权限
Map<Permission, PermissionStatus> 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<bool> requestPermissions() async { Future<bool> requestPermissions() async {
// 暂时跳过权限检查,直接返回true return await _checkAndRequestPermissions();
_permissionsGranted.value = true;
return true;
} }
/// 获取已连接的设备 /// 刷新系统设备状态(已连接和已配对设备)
Future<void> _getConnectedDevices() async { Future<void> _refreshSystemDevices() async {
try { try {
// 使用超时机制,避免无限等待 // 避免重复刷新,使用标志位
List<BluetoothDevice> devices = await FlutterBluePlus.connectedSystemDevices bool _isRefreshing = false;
.timeout(const Duration(seconds: 3), onTimeout: () { if (_isRefreshing) return;
Logger.error('获取已连接设备超时');
return <BluetoothDevice>[];
});
_connectedDevices.value = devices; _isRefreshing = true;
// 如果有已连接的设备但没有选中设备,则选中第一个 // 获取系统已连接设备
try {
List<BluetoothDevice> devices = await FlutterBluePlus.connectedSystemDevices
.timeout(const Duration(seconds: 3), onTimeout: () {
Logger.error('获取已连接设备超时');
return <BluetoothDevice>[];
});
_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<BluetoothDevice> pairedDevices = await FlutterBluePlus.bondedDevices
.timeout(const Duration(seconds: 3), onTimeout: () {
Logger.error('获取已配对设备超时');
return <BluetoothDevice>[];
});
_systemPairedDevices.value = pairedDevices;
Logger.info('系统已配对设备数量: ${_systemPairedDevices.length}');
} catch (e) {
Logger.error('获取已配对设备失败: $e');
}
// 如果有已连接的设备但没有选中设备,自动选择第一个
if (_connectedDevices.isNotEmpty && _selectedDevice.value == null) { if (_connectedDevices.isNotEmpty && _selectedDevice.value == null) {
Logger.info('自动选择第一个已连接设备: ${_connectedDevices[0].platformName}');
_selectedDevice.value = _connectedDevices[0]; _selectedDevice.value = _connectedDevices[0];
_monitorConnectionState(_selectedDevice.value!); _determineDeviceType(_selectedDevice.value!);
// 更新最后连接的设备信息 _connectionState.value = BluetoothConnectionState.connected;
_updateLastConnectedDeviceInfo(); _updateLastConnectedDeviceInfo();
} }
Logger.info('已连接设备数量: ${_connectedDevices.length}'); _isRefreshing = false;
} catch (e) { } catch (e) {
Logger.error('获取已连接设备失败: $e'); Logger.error('刷新系统设备状态失败: $e');
} }
} }
/// 获取系统中已配对的设备 /// 确定设备类型(经典蓝牙/BLE/双模)
Future<List<BluetoothDevice>> getPairedDevices() async { void _determineDeviceType(BluetoothDevice device) {
try { try {
// 获取已配对的设备(系统已知设备) // 默认设为未知类型
List<BluetoothDevice> systemDevices = await FlutterBluePlus.bondedDevices; _deviceType.value = DeviceType.unknown;
return systemDevices;
// 设备名称包含耳机/headphone/earphone/earbuds等关键词可能是音频设备
final String deviceName = device.platformName.toLowerCase();
final List<String> 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) { } catch (e) {
Logger.error('获取已配对设备失败: $e'); Logger.error('确定设备类型失败: $e');
return []; _deviceType.value = DeviceType.unknown;
} }
} }
/// 开始扫描附近的蓝牙设备并尝试连接最强信号设备 /// 开始扫描附近的蓝牙设备
Future<BluetoothDevice?> scanAndConnectToDevice({Duration? timeout}) async { Future<List<ScanResult>> startScan({Duration? timeout}) async {
if (_isScanning.value) { if (_isScanning.value) {
await stopScan(); await stopScan();
} }
@ -185,6 +297,12 @@ class BluetoothService extends GetxService {
_scanResults.clear(); _scanResults.clear();
try { try {
// 确保拥有扫描权限
if (Platform.isAndroid && !await Permission.bluetoothScan.isGranted) {
Logger.warning('没有蓝牙扫描权限,尝试请求权限');
await _checkAndRequestPermissions();
}
// 设置扫描监听 // 设置扫描监听
_scanSubscription?.cancel(); _scanSubscription?.cancel();
_scanSubscription = FlutterBluePlus.scanResults.listen((results) { _scanSubscription = FlutterBluePlus.scanResults.listen((results) {
@ -203,27 +321,11 @@ class BluetoothService extends GetxService {
_isScanning.value = false; _isScanning.value = false;
Logger.info('蓝牙扫描完成,发现 ${_scanResults.length} 个设备'); Logger.info('蓝牙扫描完成,发现 ${_scanResults.length} 个设备');
// 如果找到设备,尝试连接信号最强的 return _scanResults;
if (_scanResults.isNotEmpty) {
// 按信号强度排序
List<ScanResult> 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;
} catch (e) { } catch (e) {
_isScanning.value = false; _isScanning.value = false;
Logger.error('蓝牙扫描或连接失败: $e'); Logger.error('蓝牙扫描失败: $e');
return null; return [];
} }
} }
@ -239,37 +341,146 @@ class BluetoothService extends GetxService {
} }
} }
/// 连接到指定的蓝牙设备 - 简化版本 /// 连接到指定的蓝牙设备
/// 针对不同类型设备使用不同的连接策略
Future<bool> connectToDevice(BluetoothDevice device) async { Future<bool> connectToDevice(BluetoothDevice device) async {
Logger.info('尝试连接设备: ${device.platformName} (${device.remoteId.str})'); // 防止并发连接操作
if (_isConnecting) {
Logger.warning('已有正在进行的连接操作,请等待完成');
return false;
}
_isConnecting = true;
_connectionAttempts = 0;
try { try {
// 如果是当前已连接设备,直接返回成功 Logger.info('尝试连接设备: ${device.platformName} (${device.remoteId.str})');
if (_selectedDevice.value == device &&
_connectionState.value == BluetoothConnectionState.connected) { // 确定设备类型
_determineDeviceType(device);
// 检查设备是否已在系统连接列表中
bool alreadyConnected = _connectedDevices.any((d) => d.remoteId.str == device.remoteId.str);
// 如果是当前已选择且已连接的设备,直接返回成功
if (_selectedDevice.value == device && alreadyConnected) {
Logger.info('设备已连接,无需重新连接'); Logger.info('设备已连接,无需重新连接');
_connectionState.value = BluetoothConnectionState.connected;
_updateLastConnectedDeviceInfo();
_isConnecting = false;
return true; return true;
} }
// 断开当前连接 // 如果有其他已选择的设备,先取消监听并清除
if (_selectedDevice.value != null && _selectedDevice.value != device) { if (_selectedDevice.value != null && _selectedDevice.value != device) {
await disconnectFromDevice(); _connectionSubscription?.cancel();
_connectionSubscription = null;
} }
// 直接尝试连接 // 更新选中的设备
Logger.info('直接连接设备 ${device.platformName}');
await device.connect();
// 更新设备信息
_selectedDevice.value = device; _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<bool> _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); _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(); _updateLastConnectedDeviceInfo();
Logger.info('设备 ${device.platformName} 连接成功'); Logger.info('设备 ${device.platformName} 连接成功');
_isConnecting = false;
return true; return true;
} catch (e) { } 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; return true;
} }
// 取消设备监听
_connectionSubscription?.cancel();
_connectionSubscription = null;
try { 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} 的连接'); Logger.info('正在断开设备 ${_selectedDevice.value!.platformName} 的连接');
await _selectedDevice.value!.disconnect(); await _selectedDevice.value!.disconnect();
Logger.info('设备连接已断开,取消连接状态监听');
_connectionSubscription?.cancel();
_connectionSubscription = null;
_selectedDevice.value = null; _selectedDevice.value = null;
// 清除最后连接的设备信息 _connectionState.value = BluetoothConnectionState.disconnected;
_lastConnectedDeviceInfo.value = {};
return true; return true;
} catch (e) { } catch (e) {
Logger.error('断开蓝牙设备连接失败: $e'); Logger.error('断开蓝牙设备连接失败: $e');
// 即使失败也清除状态
_selectedDevice.value = null;
_connectionState.value = BluetoothConnectionState.disconnected;
return false; return false;
} }
} }
/// 监控连接状态 /// 监控BLE连接状态
void _monitorConnectionState(BluetoothDevice device) { void _monitorConnectionState(BluetoothDevice device) {
Logger.info('设置设备 ${device.platformName} 的连接状态监听'); Logger.info('设置设备 ${device.platformName} 的BLE连接状态监听');
// 取消之前的监听 // 取消之前的监听
if (_connectionSubscription != null) { _connectionSubscription?.cancel();
Logger.info('取消之前的连接状态监听');
_connectionSubscription!.cancel();
_connectionSubscription = null;
}
// 设置新的监听 // 设置新的监听
_connectionSubscription = device.connectionState.listen((state) { _connectionSubscription = device.connectionState.listen((state) {
Logger.info('设备 ${device.platformName} 连接状态变更为: $state'); Logger.info('设备 ${device.platformName} BLE连接状态变更为: $state');
_connectionState.value = state; _connectionState.value = state;
if (state == BluetoothConnectionState.disconnected) { if (state == BluetoothConnectionState.disconnected) {
Logger.info('设备 ${device.platformName} 已断开连接'); Logger.info('设备 ${device.platformName} BLE连接已断开');
_selectedDevice.value = null; // 设备断开连接时,不清除设备信息,以便下次可以重新连接
// 设备断开连接时,不清除最后连接的设备信息,以便下次可以重新连接
} else if (state == BluetoothConnectionState.connected) { } else if (state == BluetoothConnectionState.connected) {
Logger.info('设备 ${device.platformName} 已成功连接'); Logger.info('设备 ${device.platformName} BLE连接已成功');
// 设备连接成功时,更新最后连接的设备信息 // 设备连接成功时,更新最后连接的设备信息
_updateLastConnectedDeviceInfo(); _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 { return {
'connected': false, 'connected': false,
'device': null, 'device': null,
'deviceType': _deviceType.value.toString().split('.').last
}; };
} }
@ -349,7 +598,8 @@ class BluetoothService extends GetxService {
'rssi': _scanResults 'rssi': _scanResults
.firstWhereOrNull((r) => r.device.remoteId == _selectedDevice.value!.remoteId) .firstWhereOrNull((r) => r.device.remoteId == _selectedDevice.value!.remoteId)
?.rssi ?.rssi
} },
'deviceType': _deviceType.value.toString().split('.').last
}; };
// 更新最后连接的设备信息 // 更新最后连接的设备信息
@ -361,101 +611,118 @@ class BluetoothService extends GetxService {
/// 更新最后连接的设备信息 /// 更新最后连接的设备信息
void _updateLastConnectedDeviceInfo() { void _updateLastConnectedDeviceInfo() {
if (_selectedDevice.value != null) { if (_selectedDevice.value != null) {
_lastConnectedDeviceInfo.value = { _lastConnectedDeviceInfo.value = getConnectedDeviceInfo();
'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
}
};
} }
} }
/// 一个综合方法,执行自动查找并连接设备的完整流程 /// 一个综合方法,查找且尝试使用系统已连接的设备
Future<BluetoothDevice?> autoConnectDevice() async { /// 针对经典蓝牙耳机优化,避免使用BLE连接
Future<BluetoothDevice?> findConnectedSystemDevice() async {
try { try {
// 1. 先检查当前已连接设备 // 先刷新系统设备列表
await _refreshSystemDevices();
// 1. 检查当前是否有已选择且已连接的设备
if (_selectedDevice.value != null && if (_selectedDevice.value != null &&
_connectionState.value == BluetoothConnectionState.connected) { _connectionState.value == BluetoothConnectionState.connected) {
Logger.info('使用当前已连接设备: ${_selectedDevice.value!.platformName}'); Logger.info('使用当前已连接设备: ${_selectedDevice.value!.platformName}');
return _selectedDevice.value; return _selectedDevice.value;
} }
// 2. 检查系统中已连接的设备 // 2. 检查系统已连接设备列表
try { if (_connectedDevices.isNotEmpty) {
List<BluetoothDevice> connectedDevices = await FlutterBluePlus.connectedSystemDevices // 优先选择可能是音频设备的
.timeout(const Duration(seconds: 3)); 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('在系统已连接设备中找到设备: ${audioDevice.platformName}');
// 有已连接的设备,直接使用第一个
Logger.info('发现系统已连接设备: ${connectedDevices.first.platformName}'); // 设置为当前设备并更新状态
_selectedDevice.value = audioDevice;
// 连接设备并更新服务状态 _determineDeviceType(audioDevice);
await connectToDevice(connectedDevices.first); _connectionState.value = BluetoothConnectionState.connected;
return connectedDevices.first; _monitorSystemConnectionState(audioDevice);
} _updateLastConnectedDeviceInfo();
} catch (e) {
Logger.error('检查系统连接设备失败: $e'); return audioDevice;
}
// 3. 尝试连接之前用过的设备(从已配对设备中寻找)
List<BluetoothDevice> pairedDevices = await FlutterBluePlus.bondedDevices;
// 如果有配对设备,尝试连接第一个
if (pairedDevices.isNotEmpty) {
Logger.info('尝试连接已配对设备: ${pairedDevices.first.platformName}');
bool success = await connectToDevice(pairedDevices.first);
if (success) {
return pairedDevices.first;
}
} }
// 4. 没有找到可用设备 // 3. 如果没有已连接设备,检查已配对设备
return null; return null;
} catch (e) { } catch (e) {
Logger.error('自动连接设备流程失败: $e'); Logger.error('查找系统已连接设备失败: $e');
return null; return null;
} }
} }
/// 获取设备连接状态的简化方法 (用于兼容现有代码) /// 获取设备电量
Future<BluetoothConnectionState> getReliableConnectionState(BluetoothDevice device) async { Future<int?> getBatteryLevel() async {
if (_selectedDevice.value == null ||
_connectionState.value != BluetoothConnectionState.connected) {
return null;
}
try { try {
// 直接获取设备当前状态 // 对于BLE设备,尝试读取电池服务
BluetoothConnectionState state = await device.connectionState.first if (_deviceType.value == DeviceType.ble || _deviceType.value == DeviceType.dual) {
.timeout(const Duration(seconds: 2), onTimeout: () { // 发现设备服务
Logger.info('获取设备 ${device.platformName} 连接状态超时'); List<dynamic> services = await _selectedDevice.value!.discoverServices();
return BluetoothConnectionState.disconnected;
}); // 查找电池服务
for (var service in services) {
// 如果获取到的状态是已连接,直接返回 if (service.uuid.toString().toLowerCase() == BATTERY_SERVICE_UUID) {
if (state == BluetoothConnectionState.connected) { // 查找电池特征
return BluetoothConnectionState.connected; for (var characteristic in service.characteristics) {
if (characteristic.uuid.toString().toLowerCase() == BATTERY_CHARACTERISTIC_UUID) {
// 读取电池电量
List<int> value = await characteristic.read();
if (value.isNotEmpty) {
return value[0]; // 电量百分比
}
}
}
}
}
} }
// 作为备用检查,看设备是否在系统已连接列表中 // 对于经典蓝牙设备,可能需要平台特定的方法
try { if (_deviceType.value == DeviceType.classicAudio) {
List<BluetoothDevice> connectedDevices = await FlutterBluePlus.connectedSystemDevices // 这里需要使用平台通道或其他插件获取经典蓝牙设备电量
.timeout(const Duration(seconds: 2)); // 目前根据设备名称特征尝试估计电量
// 部分设备会在名称后附加电量信息,如"XXX耳机(80%)"
for (var connectedDevice in connectedDevices) { if (_selectedDevice.value != null) {
if (connectedDevice.remoteId.str == device.remoteId.str) { final deviceName = _selectedDevice.value!.platformName;
return BluetoothConnectionState.connected; 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) { } catch (e) {
Logger.error('获取设备 ${device.platformName} 连接状态失败: $e'); Logger.error('获取设备电量失败: $e');
return BluetoothConnectionState.disconnected; return null;
} }
} }

3
lib/data/services/voice_interaction_service.dart

@ -251,9 +251,6 @@ class VoiceInteractionService extends GetxService {
_eventStreamController?.add(RecognitionStartedEvent( _eventStreamController?.add(RecognitionStartedEvent(
timestamp: timestamp, timestamp: timestamp,
)); ));
Logger.info('收到语音识别开始事件');
break; break;
default: default:

4
lib/modules/explore/controllers/explore_controller.dart

@ -63,8 +63,8 @@ class ExploreController extends GetxController {
targetRoute: Routes.chat, targetRoute: Routes.chat,
), ),
AIFeature( AIFeature(
title: '同声翻译', title: '同声传译',
description: '实时翻译40+种语言', description: '实时翻译80+种语言',
icon: Icons.translate, icon: Icons.translate,
startColor: Colors.green.shade100, startColor: Colors.green.shade100,
endColor: Colors.green.shade200, endColor: Colors.green.shade200,

4
lib/modules/explore/views/explore_view.dart

@ -140,8 +140,8 @@ class ExploreView extends GetView<ExploreController> {
borderRadius: BorderRadius.circular(24.r), borderRadius: BorderRadius.circular(24.r),
child: Container( child: Container(
color: Colors.blue.shade900, // 预设颜色,图片加载失败时显示 color: Colors.blue.shade900, // 预设颜色,图片加载失败时显示
child: Image.network( child: Image.asset(
'https://images.unsplash.com/photo-1526378722484-bd91ca387e72?ixlib=rb-4.0.3&auto=format&fit=crop&w=1000&q=80', 'assets/images/explore.png',
width: double.infinity, width: double.infinity,
height: 180.h, height: 180.h,
fit: BoxFit.cover, fit: BoxFit.cover,

447
lib/modules/home/controllers/home_controller.dart

@ -25,13 +25,9 @@ class HomeController extends GetxController {
ConnectionStatus get connectionStatus => _connectionStatus.value; ConnectionStatus get connectionStatus => _connectionStatus.value;
// 电池状态 // 电池状态
final _leftEarbudBattery = 75.obs; final _deviceBattery = Rx<int?>(null);
final _rightEarbudBattery = 52.obs;
final _caseBattery = 92.obs;
int get leftEarbudBattery => _leftEarbudBattery.value; int? get deviceBattery => _deviceBattery.value;
int get rightEarbudBattery => _rightEarbudBattery.value;
int get caseBattery => _caseBattery.value;
// 固件版本 // 固件版本
final String firmwareVersion = 'v2.4.1'; final String firmwareVersion = 'v2.4.1';
@ -100,8 +96,19 @@ class HomeController extends GetxController {
// 基础服务初始化,不涉及蓝牙等耗时操作 // 基础服务初始化,不涉及蓝牙等耗时操作
void _initBasicServices() { void _initBasicServices() {
try { 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<String, dynamic>) { if (Get.arguments != null && Get.arguments is Map<String, dynamic>) {
@ -109,7 +116,7 @@ class HomeController extends GetxController {
_updateConnectionStatus(); _updateConnectionStatus();
} }
// 模拟电池状态更新(不涉及设备通信) // 模拟电池状态更新
_simulateBatteryUpdates(); _simulateBatteryUpdates();
} catch (e) { } catch (e) {
Logger.error('初始化基础服务失败: $e'); Logger.error('初始化基础服务失败: $e');
@ -122,11 +129,8 @@ class HomeController extends GetxController {
if (Get.isRegistered<BluetoothService>()) { if (Get.isRegistered<BluetoothService>()) {
_bluetoothService = Get.find<BluetoothService>(); _bluetoothService = Get.find<BluetoothService>();
// 不再重复加载设备信息,因为已经在_loadCachedDeviceInfo中加载过了 // 直接获取当前设备信息
// 只进行状态同步 _updateDeviceInfoFromService();
// 主动检查当前连接状态并更新
_checkAndUpdateCurrentConnectionState();
// 监听连接状态变化 // 监听连接状态变化
ever(_bluetoothService!.connectionStateObs, (state) { ever(_bluetoothService!.connectionStateObs, (state) {
@ -137,205 +141,65 @@ class HomeController extends GetxController {
// 监听选中设备变化 // 监听选中设备变化
ever(_bluetoothService!.selectedDeviceObs, (device) { ever(_bluetoothService!.selectedDeviceObs, (device) {
Logger.info('蓝牙选中设备变化: ${device?.platformName ?? "无设备"}'); Logger.info('蓝牙选中设备变化: ${device?.platformName ?? "无设备"}');
if (device != null) { _updateDeviceInfoFromService();
_deviceInfo.assignAll(_bluetoothService!.getConnectedDeviceInfo());
_updateConnectionStatus();
// 通知UI刷新
update();
} else if (_isConnected.value) {
// 如果设备断开连接,更新状态
_isConnected.value = false;
// 不清除设备信息,以便可以显示上次连接的设备
_connectionStatus.value = ConnectionStatus.disconnected;
// 通知UI刷新
update();
}
}); });
// 监听最后连接的设备信息变化 // 监听最后连接的设备信息变化
ever(_bluetoothService!.lastConnectedDeviceInfoObs, (info) { ever(_bluetoothService!.lastConnectedDeviceInfoObs, (info) {
Logger.info('最后连接的设备信息变化'); Logger.info('最后连接的设备信息变化');
if (info.isNotEmpty) { if (info.isNotEmpty) {
// 只有当设备信息为空或者连接状态不是已连接时,才更新设备信息 _deviceInfo.assignAll(info);
if (_deviceInfo.isEmpty || _connectionStatus.value != ConnectionStatus.connected) { _updateConnectionStatus();
_deviceInfo.assignAll(info); update(); // 通知UI刷新
_updateConnectionStatus();
// 通知UI刷新
update();
}
} }
}); });
// 如果当前没有连接状态,但有设备信息,尝试在后台重新连接
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) { } catch (e) {
Logger.error('获取蓝牙服务失败: $e'); Logger.error('获取蓝牙服务失败: $e');
_connectionErrorMsg.value = '获取蓝牙服务失败'; _connectionErrorMsg.value = '获取蓝牙服务失败';
// 通知UI刷新 update(); // 通知UI刷新
update();
} }
} }
// 检查并更新当前连接状态 // 从蓝牙服务更新设备信息
void _checkAndUpdateCurrentConnectionState() { void _updateDeviceInfoFromService() {
if (_bluetoothService != null && _bluetoothService!.selectedDevice != null) { if (_bluetoothService != null) {
// 如果有选择的设备,使用可靠方法获取一次状态 // 获取当前设备信息
_bluetoothService!.getReliableConnectionState(_bluetoothService!.selectedDevice!).then((state) { Map<String, dynamic> info = _bluetoothService!.lastConnectedDeviceInfo;
Logger.info('主动检查设备连接状态: $state'); if (info.isNotEmpty) {
_processConnectionStateChange(state); _deviceInfo.assignAll(info);
}).catchError((e) { _updateConnectionStatus();
Logger.error('检查连接状态失败: $e'); update(); // 通知UI刷新
}); } else if (_bluetoothService!.selectedDevice != null) {
// 如果最后连接的设备信息为空,但有选中的设备,获取当前连接的设备信息
_deviceInfo.assignAll(_bluetoothService!.getConnectedDeviceInfo());
_updateConnectionStatus();
update(); // 通知UI刷新
}
} }
} }
// 后台尝试重新连接到上次的设备 // 检查并更新当前连接状态
Future<void> _reconnectToLastDevice() async { void _checkAndUpdateCurrentConnectionState() {
try { if (_bluetoothService != null) {
Logger.info('尝试重新连接到上次的设备'); _updateDeviceInfoFromService();
_connectionStatus.value = ConnectionStatus.connecting;
update(); // 立即更新UI显示连接中状态
// 使用超时机制,防止无响应
return await Future.sync(() async {
// 获取设备ID
String deviceId = _deviceInfo['device']['id'];
Logger.info('上次连接的设备ID: $deviceId');
// 获取已配对设备,使用超时保护
Logger.info('开始获取系统已配对设备列表...');
List<BluetoothDevice> pairedDevices = await _bluetoothService!.getPairedDevices()
.timeout(const Duration(seconds: 5), onTimeout: () {
Logger.error('获取已配对设备超时');
return <BluetoothDevice>[];
});
// 记录配对设备
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 _processConnectionStateChange(BluetoothConnectionState state) { void _processConnectionStateChange(BluetoothConnectionState state) {
// 强制从蓝牙服务获取最新设备信息 // 从蓝牙服务获取最新设备信息
_deviceInfo.assignAll(_bluetoothService!.getConnectedDeviceInfo()); _updateDeviceInfoFromService();
switch (state) { switch (state) {
case BluetoothConnectionState.connected: case BluetoothConnectionState.connected:
_connectionStatus.value = ConnectionStatus.connected; _connectionStatus.value = ConnectionStatus.connected;
_connectionErrorMsg.value = ''; _connectionErrorMsg.value = '';
_isConnected.value = true; // 直接设置连接状态为true _isConnected.value = true;
// _showConnectionSuccessMessage();
break; break;
case BluetoothConnectionState.disconnected: case BluetoothConnectionState.disconnected:
_connectionStatus.value = ConnectionStatus.disconnected; _connectionStatus.value = ConnectionStatus.disconnected;
_isConnected.value = false; // 直接设置连接状态为false _isConnected.value = false;
break; break;
case BluetoothConnectionState.connecting: case BluetoothConnectionState.connecting:
_connectionStatus.value = ConnectionStatus.connecting; _connectionStatus.value = ConnectionStatus.connecting;
@ -345,18 +209,13 @@ class HomeController extends GetxController {
break; break;
} }
// 延迟一帧后刷新状态,确保UI能更新 update(); // 通知UI刷新
Future.microtask(() => _updateConnectionStatus());
// 通知UI刷新
update();
} }
// 显示连接成功消息 // 显示连接成功消息
void _showConnectionSuccessMessage() { void _showConnectionSuccessMessage() {
if (_bluetoothService?.selectedDevice != null) { if (_deviceInfo.isNotEmpty && _deviceInfo['device'] != null) {
String deviceName = _bluetoothService!.selectedDevice!.platformName.isEmpty ? String deviceName = _deviceInfo['device']['name'] ?? '未命名设备';
'未命名设备' : _bluetoothService!.selectedDevice!.platformName;
Get.snackbar( Get.snackbar(
'连接成功', '连接成功',
'已成功连接到设备: $deviceName', '已成功连接到设备: $deviceName',
@ -368,88 +227,6 @@ class HomeController extends GetxController {
} }
} }
// 尝试查找并连接已配对设备
Future<void> _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 <BluetoothDevice>[];
});
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() { void _updateConnectionStatus() {
if (_deviceInfo.containsKey('connected')) { if (_deviceInfo.containsKey('connected')) {
@ -472,13 +249,41 @@ class HomeController extends GetxController {
// 模拟电池状态更新 // 模拟电池状态更新
void _simulateBatteryUpdates() { void _simulateBatteryUpdates() {
// 在实际应用中,这里应该从设备获取真实的电池状态 // 不再获取设备电量
// 这里只是模拟随机波动 _deviceBattery.value = 0;
Logger.info('不再获取设备电量');
}
// 获取设备电量
Future<void> _fetchBatteryLevel() async {
// 不再获取设备电量
_deviceBattery.value = 0;
Logger.info('不再获取设备电量');
}
// 更新设备电量信息(提供给UI手动刷新使用)
Future<void> 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功能 // 探索AI功能
void exploreAIFeatures() { void exploreAIFeatures() {
Get.toNamed(Routes.explore); Get.toNamed(Routes.chat, arguments: {'agentId': 'personal_assistant'});
} }
// 更新固件 // 更新固件
@ -605,70 +410,36 @@ class HomeController extends GetxController {
return; return;
} }
// 设置正在刷新状态,但保留当前设备信息显示 // 更新连接状态为正在连接,但保持当前设备信息显示
_connectionStatus.value = ConnectionStatus.connecting; _connectionStatus.value = ConnectionStatus.connecting;
update(); // 立即更新UI状态 update(); // 立即更新UI状态
// 强制检查当前蓝牙连接状态 // 从BluetoothService获取最新设备信息
_checkAndUpdateCurrentConnectionState(); _updateDeviceInfoFromService();
if (_bluetoothService!.selectedDevice != null) { // 刷新电量信息
// 如果有选中的设备,刷新设备信息 await _fetchBatteryLevel();
_deviceInfo.assignAll(_bluetoothService!.getConnectedDeviceInfo());
_updateConnectionStatus(); // 根据当前状态给出提示
update(); // 确保UI更新 if (_isConnected.value) {
Get.snackbar(
// 检查刷新后的连接状态 '提示',
if (_isConnected.value) { '设备信息已更新',
Get.snackbar( snackPosition: SnackPosition.TOP,
'提示', backgroundColor: Colors.green[100],
'设备信息已更新', colorText: Colors.green[800],
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();
} else { } else {
// 如果没有选中的设备,尝试连接已配对设备 Get.snackbar(
await _checkPairedDevices(); '提示',
'无法获取设备信息,请确保设备已连接',
if (_connectionStatus.value == ConnectionStatus.failed) { snackPosition: SnackPosition.TOP,
Get.snackbar( backgroundColor: Colors.amber[100],
'提示', colorText: Colors.amber[800],
_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],
);
}
} }
} catch (e) { } catch (e) {
Logger.error('刷新设备信息失败: $e'); Logger.error('刷新设备信息失败: $e');
_connectionStatus.value = ConnectionStatus.failed;
_connectionErrorMsg.value = '刷新设备信息失败: ${e.toString()}'; _connectionErrorMsg.value = '刷新设备信息失败: ${e.toString()}';
update(); // 确保状态更新 update(); // 确保状态更新
@ -709,4 +480,18 @@ class HomeController extends GetxController {
return Colors.red; return Colors.red;
} }
} }
// 快捷入口方法
// 打开同声翻译页面
void openTranslation() {
Get.toNamed(Routes.translation);
}
// 打开AI女友页面
void openAIGirlfriend() {
Get.toNamed(Routes.chat);
}
// 打开AI助理(已有方法 exploreAIFeatures)
} }

642
lib/modules/home/views/home_view.dart

@ -2,57 +2,136 @@ import 'package:flutter/material.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart';
import '../controllers/home_controller.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 '../../../core/widgets/common_bottom_nav.dart';
// 导入枚举 // 导入枚举
import '../controllers/home_controller.dart' show ConnectionStatus; import '../controllers/home_controller.dart' show ConnectionStatus;
// 呼吸光动画组件
class AnimatedPulsing extends StatefulWidget {
@override
_AnimatedPulsingState createState() => _AnimatedPulsingState();
}
class _AnimatedPulsingState extends State<AnimatedPulsing> with SingleTickerProviderStateMixin {
late AnimationController _controller;
late Animation<double> _animation;
@override
void initState() {
super.initState();
_controller = AnimationController(
duration: Duration(seconds: 2),
vsync: this,
)..repeat(reverse: true);
_animation = Tween<double>(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<HomeController> { class HomeView extends GetView<HomeController> {
const HomeView({Key? key}) : super(key: key); const HomeView({Key? key}) : super(key: key);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( 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( body: SafeArea(
child: Column( child: SingleChildScrollView(
children: [ padding: EdgeInsets.all(16.w),
// 状态栏和标题区域 child: Column(
_buildHeader(), crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 主要内容区域(可滚动) // 顶部图片
Expanded( Center(
child: SingleChildScrollView( child: _buildEarbudsImage(),
physics: const BouncingScrollPhysics(), ),
child: Padding( SizedBox(height: 20.h),
padding: EdgeInsets.fromLTRB(16.w, 0, 16.w, 16.h),
child: Column( // 设备状态卡片
crossAxisAlignment: CrossAxisAlignment.start, _buildDeviceStatusCard(),
children: [ SizedBox(height: 20.h),
// 耳机图片
_buildEarbudsImage(), // 热门AI体验
Row(
// 设备状态卡片 children: [
SizedBox(height: 16.h), Text(
_buildDeviceStatusCard(), '热门AI体验',
style: TextStyle(
// AI功能部分 fontSize: 18.sp,
SizedBox(height: 16.h), fontWeight: FontWeight.bold,
_buildAIFunctionsCard(), ),
// 固件更新提示
SizedBox(height: 16.h),
_buildFirmwareUpdateCard(),
// 快速功能访问
SizedBox(height: 24.h),
_buildQuickFunctions(),
],
), ),
), SizedBox(width: 8.w),
Icon(
Icons.star,
color: Colors.amber,
size: 20.w,
),
],
), ),
), SizedBox(height: 16.h),
], _buildPremiumAILayout(),
],
),
), ),
), ),
bottomNavigationBar: const CommonBottomNav(currentIndex: 0), bottomNavigationBar: const CommonBottomNav(currentIndex: 0),
@ -115,24 +194,50 @@ class HomeView extends GetView<HomeController> {
alignment: Alignment.center, alignment: Alignment.center,
child: Container( child: Container(
decoration: BoxDecoration( decoration: BoxDecoration(
borderRadius: BorderRadius.circular(16.r), borderRadius: BorderRadius.circular(20.r),
boxShadow: [ boxShadow: [
BoxShadow( BoxShadow(
color: Colors.grey.withOpacity(0.2), color: Colors.grey.withOpacity(0.15),
blurRadius: 10, blurRadius: 15,
offset: const Offset(0, 4), offset: const Offset(0, 5),
),
BoxShadow(
color: Colors.blue.withOpacity(0.05),
blurRadius: 20,
offset: const Offset(0, -2),
), ),
], ],
), ),
child: ClipRRect( child: ClipRRect(
borderRadius: BorderRadius.circular(16.r), borderRadius: BorderRadius.circular(20.r),
child: Container( child: Container(
color: Colors.white, color: Colors.white,
padding: EdgeInsets.all(16.w), padding: EdgeInsets.symmetric(horizontal: 20.w, vertical: 16.h),
child: Icon( child: Stack(
Icons.headset, alignment: Alignment.center,
size: 120.sp, children: [
color: Colors.blueGrey, // 背景光效
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<HomeController> {
return SizedBox(); 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()),
], ],
), ),
); );
} }
// 电池状态项 // 优质AI体验布局(左1右2)
Widget _buildBatteryItem(String label, int percentage, IconData icon) { Widget _buildPremiumAILayout() {
Color batteryColor; return Row(
IconData batteryIcon; crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 根据电量设置颜色和图标 // 左侧大卡片 - AI助理
if (percentage >= 70) { Expanded(
batteryColor = Colors.green; flex: 1,
batteryIcon = Icons.battery_full; child: Stack(
} 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,
children: [ children: [
Icon(batteryIcon, size: 14.sp, color: batteryColor), // 呼吸光效果
SizedBox(width: 4.w), Positioned.fill(
Text( child: Container(
'$percentage%', decoration: BoxDecoration(
style: TextStyle( borderRadius: BorderRadius.circular(16.r),
fontSize: 12.sp, gradient: RadialGradient(
fontWeight: FontWeight.w500, colors: [
color: Colors.grey[700], 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: [ SizedBox(width: 16.w),
BoxShadow(
color: Colors.grey.withOpacity(0.1), // 右侧两个卡片
blurRadius: 10, Expanded(
offset: const Offset(0, 4), flex: 1,
), child: Column(
],
),
padding: EdgeInsets.all(16.w),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text( // 右上卡片 - 同声翻译
'AI助手', _buildFeatureCard(
style: TextStyle( title: '同声传译',
fontSize: 16.sp, description: '80+语言实时翻译,出国无忧',
fontWeight: FontWeight.w500, icon: Icons.translate,
color: Colors.grey[800], gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [
Color(0xFF5E72EB),
Color(0xFF4776E6),
],
), ),
onTap: controller.openTranslation,
height: 83.h, // 增加高度
), ),
SizedBox(height: 4.h),
Text( SizedBox(height: 5.h), // 减少间距
'探索DeepVoice的智能功能',
style: TextStyle( // 右下卡片 - AI女友
fontSize: 12.sp, _buildFeatureCard(
color: Colors.grey[600], 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() { Widget _buildFeatureCard({
return Container( required String title,
decoration: BoxDecoration( required String description,
color: Colors.grey[100], required IconData icon,
borderRadius: BorderRadius.circular(16.r), required LinearGradient gradient,
boxShadow: [ required VoidCallback onTap,
BoxShadow( double? height,
color: Colors.grey.withOpacity(0.1), bool iconOnTop = false,
blurRadius: 10, }) {
offset: const Offset(0, 4), return InkWell(
), onTap: onTap,
], child: Container(
), height: height ?? 175.h,
padding: EdgeInsets.all(16.w), padding: EdgeInsets.all(14.w),
child: Row( decoration: BoxDecoration(
mainAxisAlignment: MainAxisAlignment.spaceBetween, gradient: gradient,
children: [ borderRadius: BorderRadius.circular(16.r),
Row( boxShadow: [
children: [ BoxShadow(
Container( color: Colors.black.withOpacity(0.1),
width: 40.w, blurRadius: 5,
height: 40.w, offset: Offset(0, 2),
decoration: BoxDecoration( ),
color: Colors.amber[100], ],
shape: BoxShape.circle, ),
), child: iconOnTop
child: Icon( ? Column(
Icons.download,
size: 20.sp,
color: Colors.amber[600],
),
),
SizedBox(width: 12.w),
Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ 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( Text(
'固件更新', title,
style: TextStyle( style: TextStyle(
color: Colors.white,
fontSize: 16.sp, fontSize: 16.sp,
fontWeight: FontWeight.w500, fontWeight: FontWeight.bold,
color: Colors.grey[800],
), ),
), ),
Text( SizedBox(height: 4.h),
'${controller.firmwareVersion}可用', // 描述
style: TextStyle( Expanded(
fontSize: 12.sp, child: Text(
color: Colors.grey[500], 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],
),
),
],
);
}
} }

225
lib/modules/pairing/controllers/pairing_controller.dart

@ -8,10 +8,11 @@ import 'package:permission_handler/permission_handler.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
enum SearchStatus { enum SearchStatus {
searching, // 正在搜索设备 searching, // 正在搜索设备
found, // 找到设备 found, // 找到设备
failed, // 搜索失败 failed, // 搜索失败
noPermission, // 缺少权限 noPermission, // 缺少权限
needsSystemPairing // 需要在系统设置中配对
} }
class PairingController extends GetxController { class PairingController extends GetxController {
@ -43,15 +44,22 @@ class PairingController extends GetxController {
final _errorMessage = ''.obs; final _errorMessage = ''.obs;
String get errorMessage => _errorMessage.value; String get errorMessage => _errorMessage.value;
// 扫描到的设备列表
final _scannedDevices = <ScanResult>[].obs;
List<ScanResult> get scannedDevices => _scannedDevices;
// 已配对的设备列表
final _pairedDevices = <BluetoothDevice>[].obs;
List<BluetoothDevice> get pairedDevices => _pairedDevices;
@override @override
void onInit() { void onInit() {
super.onInit(); super.onInit();
// 启动搜索动画 // 启动搜索动画
_startSearchingAnimation(); _startSearchingAnimation();
// 延迟更久初始化蓝牙服务,避免在应用启动时就请求权限和阻塞主线程 // 延迟初始化蓝牙服务,避免在应用启动时就阻塞主线程
// 改为延迟初始化,给UI足够时间渲染 Future.delayed(const Duration(milliseconds: 800), () {
Future.delayed(const Duration(milliseconds: 1500), () {
_initBluetoothService(); _initBluetoothService();
}); });
} }
@ -69,7 +77,7 @@ class PairingController extends GetxController {
if (Get.isRegistered<BluetoothService>()) { if (Get.isRegistered<BluetoothService>()) {
_bluetoothService = Get.find<BluetoothService>(); _bluetoothService = Get.find<BluetoothService>();
} else { } else {
// 在后台初始化蓝牙服务 // 初始化蓝牙服务
_bluetoothService = await Get.put(BluetoothService()).init(); _bluetoothService = await Get.put(BluetoothService()).init();
} }
@ -81,11 +89,11 @@ class PairingController extends GetxController {
ever(_bluetoothService.isOnObs, (bool on) { ever(_bluetoothService.isOnObs, (bool on) {
_isBluetoothOn.value = on; _isBluetoothOn.value = on;
// 如果蓝牙开启,开始扫描设备 // 如果蓝牙开启,开始查找设备
if (on) { if (on) {
// 延迟开始扫描,避免权限请求过早和主线程阻塞 // 延迟开始查找,给权限请求等操作留出时间
Future.delayed(const Duration(milliseconds: 800), () { Future.delayed(const Duration(milliseconds: 800), () {
_startScanningDevices(); _findConnectedDevices();
}); });
} else { } else {
_errorMessage.value = '请打开蓝牙以搜索设备'; _errorMessage.value = '请打开蓝牙以搜索设备';
@ -94,12 +102,12 @@ class PairingController extends GetxController {
} }
}); });
// 如果蓝牙已经开启,直接开始扫描 // 如果蓝牙已经开启,直接开始查找
if (_bluetoothService.isOn) { if (_bluetoothService.isOn) {
_isBluetoothOn.value = true; _isBluetoothOn.value = true;
// 延迟开始扫描,避免权限请求过早和主线程阻塞 // 延迟开始查找
Future.delayed(const Duration(milliseconds: 800), () { Future.delayed(const Duration(milliseconds: 800), () {
_startScanningDevices(); _findConnectedDevices();
}); });
} else { } else {
_errorMessage.value = '请打开蓝牙以搜索设备'; _errorMessage.value = '请打开蓝牙以搜索设备';
@ -132,8 +140,12 @@ class PairingController extends GetxController {
// 请求必要权限 // 请求必要权限
Future<bool> _requestPermissions() async { Future<bool> _requestPermissions() async {
// 暂时跳过权限检查,直接返回true try {
return true; return await _bluetoothService.requestPermissions();
} catch (e) {
Logger.error('请求权限失败: $e');
return false;
}
} }
// 打开应用设置 // 打开应用设置
@ -153,12 +165,21 @@ class PairingController extends GetxController {
} }
} }
// 开始扫描蓝牙设备 // 查找已连接的设备
Future<void> _startScanningDevices() async { Future<void> _findConnectedDevices() async {
try { try {
_searchStatus.value = SearchStatus.searching; _searchStatus.value = SearchStatus.searching;
// 先检查是否有上次连接的设备信息 // 检查权限
bool hasPermissions = await _requestPermissions();
if (!hasPermissions) {
Logger.warning('没有足够的蓝牙权限');
_searchStatus.value = SearchStatus.noPermission;
_stopSearchingAnimation();
return;
}
// 1. 先检查是否有上次连接的设备信息
if (_bluetoothService.lastConnectedDeviceInfo.isNotEmpty && if (_bluetoothService.lastConnectedDeviceInfo.isNotEmpty &&
_bluetoothService.lastConnectedDeviceInfo['connected'] == true) { _bluetoothService.lastConnectedDeviceInfo['connected'] == true) {
Logger.info('使用上次连接的设备信息'); Logger.info('使用上次连接的设备信息');
@ -167,34 +188,83 @@ class PairingController extends GetxController {
return; return;
} }
// 使用BluetoothService的自动连接功能查找设备 // 2. 查找系统已连接的设备
Logger.info('尝试自动连接设备'); Logger.info('查找系统已连接的设备');
BluetoothDevice? device = await _bluetoothService.autoConnectDevice() BluetoothDevice? device = await _bluetoothService.findConnectedSystemDevice()
.timeout(const Duration(seconds: 8), onTimeout: () { .timeout(const Duration(seconds: 5), onTimeout: () {
Logger.info('自动连接设备超时'); Logger.info('查找系统已连接设备超时');
return null; return null;
}); });
if (device != null) { if (device != null) {
Logger.info('自动连接设备成功: ${device.platformName}'); Logger.info('找到系统已连接设备: ${device.platformName}');
_deviceInfo.value = _bluetoothService.getConnectedDeviceInfo(); _deviceInfo.value = _bluetoothService.getConnectedDeviceInfo();
_onDeviceFound(); _onDeviceFound();
return; return;
} }
// 如果自动连接失败,尝试扫描新设备 // 3. 如果没有找到已连接设备,提示用户选择
Logger.info('自动连接失败,开始扫描新设备'); // 获取已配对设备列表
device = await _bluetoothService.scanAndConnectToDevice( _pairedDevices.value = _bluetoothService.systemPairedDevices;
timeout: const Duration(seconds: 5)
).timeout(const Duration(seconds: 6), onTimeout: () {
Logger.info('扫描并连接设备超时');
return null;
});
if (device != null) { // 如果有已配对的设备,直接尝试连接第一个
Logger.info('扫描并连接设备成功: ${device.platformName}'); if (_pairedDevices.isNotEmpty) {
_deviceInfo.value = _bluetoothService.getConnectedDeviceInfo(); Logger.info('找到${_pairedDevices.length}个已配对设备,尝试连接第一个');
_onDeviceFound();
// 尝试连接第一个配对设备
await tryConnectDevice(_pairedDevices.first);
} else {
// 如果没有已配对设备,开始扫描附近设备
await _scanForDevices();
}
} catch (e) {
Logger.error('查找设备失败: $e');
_errorMessage.value = '查找设备失败: ${e.toString()}';
_searchStatus.value = SearchStatus.failed;
_stopSearchingAnimation();
}
}
// 扫描附近的蓝牙设备
Future<void> _scanForDevices() async {
try {
Logger.info('开始扫描附近设备');
_searchStatus.value = SearchStatus.searching;
_startSearchingAnimation();
// 开始扫描
List<ScanResult> results = await _bluetoothService.startScan(
timeout: const Duration(seconds: 10)
);
_scannedDevices.value = results;
// 过滤出可能是耳机的设备
List<ScanResult> 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 { } else {
// 未找到任何设备 // 未找到任何设备
_errorMessage.value = '未找到蓝牙设备,请确保设备已开启并在附近'; _errorMessage.value = '未找到蓝牙设备,请确保设备已开启并在附近';
@ -202,8 +272,45 @@ class PairingController extends GetxController {
_stopSearchingAnimation(); _stopSearchingAnimation();
} }
} catch (e) { } catch (e) {
Logger.error('设备搜索和连接失败: $e'); Logger.error('扫描设备失败: $e');
_errorMessage.value = '设备搜索失败: ${e.toString()}'; _errorMessage.value = '扫描设备失败: ${e.toString()}';
_searchStatus.value = SearchStatus.failed;
_stopSearchingAnimation();
}
}
// 尝试连接特定设备
Future<void> 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; _searchStatus.value = SearchStatus.failed;
_stopSearchingAnimation(); _stopSearchingAnimation();
} }
@ -221,8 +328,8 @@ class PairingController extends GetxController {
}); });
} }
// 手动连接 // 手动刷新/重新搜索
void manualConnect() { void refreshSearch() {
// 停止当前扫描(如果有) // 停止当前扫描(如果有)
_bluetoothService.stopScan(); _bluetoothService.stopScan();
@ -231,8 +338,40 @@ class PairingController extends GetxController {
_searchStatus.value = SearchStatus.searching; _searchStatus.value = SearchStatus.searching;
_errorMessage.value = ''; _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),
);
}
} }
// 稍后再说 // 稍后再说

83
lib/modules/pairing/views/pairing_view.dart

@ -30,24 +30,26 @@ class PairingView extends GetView<PairingController> {
Widget _buildMainContent() { Widget _buildMainContent() {
return Padding( return Padding(
padding: EdgeInsets.fromLTRB(24.w, 40.h, 24.w, 24.h), padding: EdgeInsets.fromLTRB(24.w, 40.h, 24.w, 24.h),
child: Column( child: SingleChildScrollView(
children: [ child: Column(
// 标题部分 children: [
_buildHeader(), // 标题部分
_buildHeader(),
// 设备动画
SizedBox(height: 40.h), // 设备动画
_buildDeviceAnimation(), SizedBox(height: 40.h),
_buildDeviceAnimation(),
// 搜索状态
SizedBox(height: 40.h), // 搜索状态
_buildSearchStatus(), SizedBox(height: 40.h),
_buildSearchStatus(),
// 底部按钮
const Spacer(), // 底部按钮
_buildBottomButtons(), SizedBox(height: 60.h),
SizedBox(height: 80.h), // 留出底部导航的空间 _buildBottomButtons(),
], SizedBox(height: 80.h), // 留出底部导航的空间
],
),
), ),
); );
} }
@ -288,6 +290,49 @@ class PairingView extends GetView<PairingController> {
), ),
], ],
); );
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: default:
return SizedBox(); return SizedBox();
} }
@ -301,7 +346,7 @@ class PairingView extends GetView<PairingController> {
SizedBox( SizedBox(
width: double.infinity, width: double.infinity,
child: ElevatedButton( child: ElevatedButton(
onPressed: controller.manualConnect, onPressed: controller.refreshSearch,
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
backgroundColor: Colors.grey[200], backgroundColor: Colors.grey[200],
foregroundColor: Colors.grey[800], foregroundColor: Colors.grey[800],

38
lib/modules/settings/controllers/settings_controller.dart

@ -2,49 +2,11 @@ import 'package:get/get.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
class SettingsController extends GetxController { class SettingsController extends GetxController {
// 噪声消除开关状态
final RxBool noiseReductionEnabled = true.obs;
// 环境音增强模式
final RxString ambientSoundMode = '中等'.obs;
final List<String> availableAmbientModes = ['关闭', '低', '中等', '高'];
// 立体声平衡值
final RxDouble stereoBalance = 0.0.obs; // -1.0 (左) 到 1.0 (右), 0 为中间
// AI助理默认模式
final RxString aiAssistantMode = '通用助手'.obs;
final List<String> availableAiModes = ['通用助手', '翻译助手', '学习助手', '交际助手'];
// AI声音选择
final RxString aiVoice = '默认女声'.obs;
final List<String> availableVoices = ['默认女声', '默认男声', '温柔女声', '磁性男声', '活力女声'];
// 翻译语言对 // 翻译语言对
final RxString sourceLanguage = '中文'.obs; final RxString sourceLanguage = '中文'.obs;
final RxString targetLanguage = '英文'.obs; final RxString targetLanguage = '英文'.obs;
final List<String> availableLanguages = ['中文', '英文', '日文', '韩文', '法文', '德文', '西班牙文', '俄文']; final List<String> 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) { void setSourceLanguage(String language) {
sourceLanguage.value = language; sourceLanguage.value = language;
} }

527
lib/modules/settings/views/settings_view.dart

@ -34,10 +34,6 @@ class SettingsView extends GetView<SettingsController> {
children: [ children: [
_buildUserInfoCard(), _buildUserInfoCard(),
SizedBox(height: 20.h), SizedBox(height: 20.h),
_buildHeadphoneSettingsSection(),
SizedBox(height: 20.h),
_buildAISettingsSection(),
SizedBox(height: 20.h),
_buildSystemSettingsSection(), _buildSystemSettingsSection(),
SizedBox(height: 20.h), SizedBox(height: 20.h),
_buildAboutSection(), _buildAboutSection(),
@ -169,473 +165,6 @@ class SettingsView extends GetView<SettingsController> {
); );
} }
// 耳机设置部分
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<String>(
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<String>(
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<String>(
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() { Widget _buildSystemSettingsSection() {
return Column( return Column(
@ -1099,60 +628,4 @@ class SettingsView extends GetView<SettingsController> {
), ),
); );
} }
// 显示语言选择对话框
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<String>(
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('取消'),
),
],
),
),
),
);
}
} }

10
lib/modules/translation/controllers/translation_controller.dart

@ -131,6 +131,8 @@ class TranslationController extends GetxController {
final sourceCode = supportedLanguages[sourceLanguage.value] ?? 'zh'; final sourceCode = supportedLanguages[sourceLanguage.value] ?? 'zh';
final recognitionLanguage = _getRecognitionLanguage(sourceCode); final recognitionLanguage = _getRecognitionLanguage(sourceCode);
// 使用正确定义的变量顺序
await _ttsService.initialize();
await _asrService.initialize(language: recognitionLanguage); await _asrService.initialize(language: recognitionLanguage);
Logger.info('Azure ASR 服务初始化成功'); Logger.info('Azure ASR 服务初始化成功');
} catch (e) { } catch (e) {
@ -425,11 +427,11 @@ class TranslationController extends GetxController {
try { try {
// 获取目标语言代码 // 获取目标语言代码
final targetCode = supportedLanguages[targetLanguage.value] ?? 'en'; // final targetCode = supportedLanguages[targetLanguage.value] ?? 'en';
// 设置合适的语音 // // 设置合适的语音
final voiceName = _getTtsVoiceName(targetCode); // final voiceName = _getTtsVoiceName(targetCode);
await _ttsService.setVoice(voiceName); // await _ttsService.setVoice(voiceName);
// 播放文本 // 播放文本
await _ttsService.speak(text); await _ttsService.speak(text);

4
pubspec.yaml

@ -55,6 +55,8 @@ flutter:
uses-material-design: true uses-material-design: true
assets: assets:
- assets/images/headphone.png - assets/images/headphone_light.png
- assets/images/headphone_dark.png
- assets/images/agents/ - assets/images/agents/
- assets/images/explore.png
- .env - .env

Loading…
Cancel
Save