Browse Source

blueteeth fix3

weicu
wolfplus 2 years ago
parent
commit
b1dd252002
  1. 4
      android/app/src/main/kotlin/com/example/deep_voice/VoiceInteractionService.kt
  2. 839
      lib/data/services/bluetooth_service.dart
  3. 644
      lib/modules/pairing/controllers/pairing_controller.dart
  4. 48
      lib/modules/pairing/views/pairing_view.dart
  5. 1
      pubspec.yaml

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

@ -37,7 +37,7 @@ class VoiceInteractionService : Service() {
private const val CHANNEL_ID = "voice_interaction_channel" private const val CHANNEL_ID = "voice_interaction_channel"
// 语音识别超时时间(毫秒) // 语音识别超时时间(毫秒)
private const val RECOGNITION_TIMEOUT = 10000L private const val RECOGNITION_TIMEOUT = 8000L
// 用于跟踪服务是否正在运行 // 用于跟踪服务是否正在运行
private val isRunning = AtomicBoolean(false) private val isRunning = AtomicBoolean(false)
@ -321,7 +321,7 @@ class VoiceInteractionService : Service() {
if (!hasSpeechDetected && !isTtsSpeaking && elapsedTime >= RECOGNITION_TIMEOUT) { if (!hasSpeechDetected && !isTtsSpeaking && elapsedTime >= RECOGNITION_TIMEOUT) {
Log.d(TAG, "超过${RECOGNITION_TIMEOUT/1000}秒未检测到语音,停止识别") Log.d(TAG, "超过${RECOGNITION_TIMEOUT/1000}秒未检测到语音,停止识别")
isTimeoutPaused = true isTimeoutPaused = true
playNotification("没有听到您说话,已暂停语音识别。双击耳机按钮可重新开始。") playNotification("没有听到您说话,已暂停对话。双击耳机按钮可重新开始。")
// 停止语音识别并确保资源完全释放 // 停止语音识别并确保资源完全释放
stopVoiceRecognition() stopVoiceRecognition()

839
lib/data/services/bluetooth_service.dart

File diff suppressed because it is too large

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

@ -7,22 +7,30 @@ import '../../../core/utils/logger.dart';
import 'package:permission_handler/permission_handler.dart'; import 'package:permission_handler/permission_handler.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
enum SearchStatus { enum PairingStatus {
searching, // 正在搜索设备 initial, // 初始状态
found, // 找到设备 requesting, // 请求权限中
failed, // 搜索失败 scanning, // 扫描设备中
noPermission, // 缺少权限 connecting, // 连接设备中
needsSystemPairing // 需要在系统设置中配对 found, // 找到并连接设备
noPermission, // 缺少权限
bluetoothOff, // 蓝牙未开启
needsSystemPairing, // 需要在系统设置中配对
failed // 操作失败
} }
class PairingController extends GetxController { class PairingController extends GetxController {
// 搜索状态 // 配对状态
final _searchStatus = SearchStatus.searching.obs; final _pairingStatus = PairingStatus.initial.obs;
SearchStatus get searchStatus => _searchStatus.value; PairingStatus get pairingStatus => _pairingStatus.value;
// 搜索动画的点 // 状态描述(用于UI显示)
final _searchDots = ''.obs; final _statusDescription = ''.obs;
String get searchDots => _searchDots.value; String get statusDescription => _statusDescription.value;
// 加载动画的点
final _animationDots = ''.obs;
String get animationDots => _animationDots.value;
// 动画定时器 // 动画定时器
Timer? _animationTimer; Timer? _animationTimer;
@ -40,7 +48,7 @@ class PairingController extends GetxController {
bool get isBluetoothAvailable => _isBluetoothAvailable.value; bool get isBluetoothAvailable => _isBluetoothAvailable.value;
bool get isBluetoothOn => _isBluetoothOn.value; bool get isBluetoothOn => _isBluetoothOn.value;
// 错误消息 // 错误消息(包含解决方案提示)
final _errorMessage = ''.obs; final _errorMessage = ''.obs;
String get errorMessage => _errorMessage.value; String get errorMessage => _errorMessage.value;
@ -52,236 +60,333 @@ class PairingController extends GetxController {
final _pairedDevices = <BluetoothDevice>[].obs; final _pairedDevices = <BluetoothDevice>[].obs;
List<BluetoothDevice> get pairedDevices => _pairedDevices; List<BluetoothDevice> get pairedDevices => _pairedDevices;
// 重试计数 // 重试计数(分别为不同操作设置)
int _retryCount = 0; int _initRetryCount = 0;
int _scanRetryCount = 0;
int _connectRetryCount = 0;
static const int MAX_RETRY_COUNT = 3; static const int MAX_RETRY_COUNT = 3;
@override @override
void onInit() { void onInit() {
super.onInit(); super.onInit();
// 启动搜索动画 _initializeProcess();
_startSearchingAnimation();
// 初始化蓝牙服务
_initBluetoothService();
} }
@override @override
void onClose() { void onClose() {
_stopSearchingAnimation(); _stopAnimation();
_bluetoothService.stopScan();
super.onClose(); super.onClose();
} }
// 初始化蓝牙服务 // 初始化流程
Future<void> _initBluetoothService() async { Future<void> _initializeProcess() async {
// 启动动画
_startAnimation();
_updateStatus(PairingStatus.initial, '正在初始化...');
try {
// 获取或初始化蓝牙服务
await _getBluetoothService();
// 先检查权限,再处理后续流程
await _checkAndRequestPermissions();
} catch (e) {
Logger.error('初始化过程失败: $e');
_updateStatus(PairingStatus.failed, '初始化失败');
_setError('初始化蓝牙服务失败,请重试', '可能是蓝牙服务暂时不可用');
}
}
// 获取蓝牙服务
Future<void> _getBluetoothService() async {
try { try {
// 获取蓝牙服务实例 // 检查是否已注册
if (Get.isRegistered<BluetoothService>()) { if (Get.isRegistered<BluetoothService>()) {
_bluetoothService = Get.find<BluetoothService>(); _bluetoothService = Get.find<BluetoothService>();
// 如果蓝牙服务已初始化,直接开始查找设备 // 如果服务未初始化,等待初始化完成
if (_bluetoothService.isInitialized) { if (!_bluetoothService.isInitialized) {
_setupBluetoothListeners(); // 监听初始化状态变化
} else { await _waitForInitialization();
// 等待蓝牙服务初始化完成
ever(_bluetoothService.isInitializedObs, (bool initialized) {
if (initialized) {
_setupBluetoothListeners();
}
});
} }
} else { } else {
// 初始化蓝牙服务 // 初始化并注册服务
_bluetoothService = await Get.put(BluetoothService()).init(); _bluetoothService = await Get.put(BluetoothService()).init();
_setupBluetoothListeners();
} }
} catch (e) {
Logger.error('初始化蓝牙服务失败: $e');
_errorMessage.value = '初始化蓝牙服务失败';
_searchStatus.value = SearchStatus.failed;
_stopSearchingAnimation();
// 重试初始化 // 设置蓝牙状态监听
_retryInitialization(); _setupBluetoothListeners();
} catch (e) {
Logger.error('获取蓝牙服务失败: $e');
throw Exception('获取蓝牙服务失败: $e');
}
}
// 等待蓝牙服务初始化完成
Future<void> _waitForInitialization() async {
// 使用 Completer 将事件监听转换为 Future
final Completer<void> completer = Completer<void>();
late final Worker worker;
// 一次性监听
worker = ever(_bluetoothService.isInitializedObs, (bool initialized) {
if (initialized) {
completer.complete();
worker.dispose(); // 完成后取消监听
}
});
// 如果已经初始化,直接完成
if (_bluetoothService.isInitialized) {
completer.complete();
worker.dispose();
} }
// 设置超时
return completer.future.timeout(
const Duration(seconds: 10),
onTimeout: () {
worker.dispose();
throw TimeoutException('等待蓝牙服务初始化超时');
},
);
} }
// 设置蓝牙监听器 // 设置蓝牙状态监听
void _setupBluetoothListeners() { void _setupBluetoothListeners() {
// 监听蓝牙状态变化 // 监听蓝牙可用性
_isBluetoothAvailable.value = _bluetoothService.isAvailable;
ever(_bluetoothService.isAvailableObs, (bool available) { ever(_bluetoothService.isAvailableObs, (bool available) {
_isBluetoothAvailable.value = available; _isBluetoothAvailable.value = available;
if (!available) {
_updateStatus(PairingStatus.failed, '蓝牙不可用');
_setError('蓝牙不可用', '请检查设备是否支持蓝牙功能');
}
}); });
// 监听蓝牙开关状态
_isBluetoothOn.value = _bluetoothService.isOn;
ever(_bluetoothService.isOnObs, (bool on) { ever(_bluetoothService.isOnObs, (bool on) {
_isBluetoothOn.value = on; _isBluetoothOn.value = on;
// 如果蓝牙开启,开始查找设备 // 蓝牙状态改变时处理
if (on) { if (on) {
_findConnectedDevices(); if (_pairingStatus.value == PairingStatus.bluetoothOff) {
// 如果蓝牙刚被打开,继续配对流程
_continueAfterBluetoothOn();
}
} else { } else {
_errorMessage.value = '请打开蓝牙以搜索设备'; // 蓝牙关闭,更新状态
_searchStatus.value = SearchStatus.failed; _updateStatus(PairingStatus.bluetoothOff, '蓝牙已关闭');
_stopSearchingAnimation(); _setError('蓝牙已关闭', '请开启蓝牙后再试');
} }
}); });
}
// 检查并请求权限
Future<void> _checkAndRequestPermissions() async {
_updateStatus(PairingStatus.requesting, '正在请求权限...');
// 如果蓝牙已经开启,直接开始查找 try {
if (_bluetoothService.isOn) { bool hasPermissions = await _bluetoothService.requestPermissions();
_isBluetoothOn.value = true;
_findConnectedDevices(); if (!hasPermissions) {
} else { // 权限被拒绝
_errorMessage.value = '请打开蓝牙以搜索设备'; _handlePermissionDenied();
return;
}
// 权限已授予,检查蓝牙状态
if (!_bluetoothService.isOn) {
_updateStatus(PairingStatus.bluetoothOff, '蓝牙已关闭');
_setError('蓝牙已关闭', '请开启蓝牙后再试');
return;
}
// 开始查找设备
_startFindingDevices();
} catch (e) {
Logger.error('权限请求失败: $e');
_updateStatus(PairingStatus.noPermission, '权限请求失败');
_setError('权限请求失败', '请手动授予蓝牙和位置权限');
} }
} }
// 重试初始化 // 处理权限被拒绝的情况
void _retryInitialization() { void _handlePermissionDenied() async {
if (_retryCount < MAX_RETRY_COUNT) { // 检查是否永久拒绝
_retryCount++; bool isPermanentlyDenied = false;
Logger.info('重试初始化蓝牙服务 (${_retryCount}/${MAX_RETRY_COUNT})');
if (GetPlatform.isAndroid) {
PermissionStatus bluetoothScan = await Permission.bluetoothScan.status;
PermissionStatus bluetoothConnect = await Permission.bluetoothConnect.status;
PermissionStatus location = await Permission.location.status;
Future.delayed(const Duration(seconds: 2), () { // 如果任一权限被永久拒绝
_initBluetoothService(); isPermanentlyDenied =
}); bluetoothScan.isPermanentlyDenied ||
bluetoothConnect.isPermanentlyDenied ||
location.isPermanentlyDenied;
}
_updateStatus(PairingStatus.noPermission, '缺少必要权限');
if (isPermanentlyDenied) {
_setError('缺少必要权限', '您已永久拒绝权限,请前往系统设置中手动开启');
// 显示前往设置的提示
_showGoToSettingsDialog();
} else {
_setError('缺少必要权限', '请授予蓝牙和位置权限以继续');
} }
} }
// 启动搜索动画 // 显示前往设置的对话框
void _startSearchingAnimation() { void _showGoToSettingsDialog() {
_stopSearchingAnimation(); // 清除已有的定时器 Get.dialog(
AlertDialog(
int count = 0; title: const Text('需要权限'),
_animationTimer = Timer.periodic(const Duration(milliseconds: 500), (timer) { content: const Text('蓝牙搜索需要特定权限,请前往系统设置开启'),
count = (count + 1) % 4; actions: [
_searchDots.value = '.' * count; TextButton(
}); onPressed: () => Get.back(),
child: const Text('稍后'),
),
TextButton(
onPressed: () {
Get.back();
goToAppSettings();
},
child: const Text('前往设置'),
),
],
),
barrierDismissible: false,
);
} }
// 停止搜索动画 // 蓝牙开启后继续流程
void _stopSearchingAnimation() { void _continueAfterBluetoothOn() {
_animationTimer?.cancel(); if (_bluetoothService.permissionsGranted) {
_animationTimer = null; _startFindingDevices();
_searchDots.value = ''; } else {
_checkAndRequestPermissions();
}
} }
// 请求必要权限 // 开始查找设备
Future<bool> _requestPermissions() async { Future<void> _startFindingDevices() async {
try { // 先检查上次连接的设备
return await _bluetoothService.requestPermissions(); if (_checkLastConnectedDevice()) {
} catch (e) { return;
Logger.error('请求权限失败: $e');
return false;
} }
// 查找系统已连接的设备
await _findSystemConnectedDevices();
} }
// 打开应用设置 // 检查上次连接的设备
void goToAppSettings() async { bool _checkLastConnectedDevice() {
try { // 检查是否有上次连接的设备信息
await openAppSettings(); if (_bluetoothService.lastConnectedDeviceInfo.isNotEmpty &&
} catch (e) { _bluetoothService.lastConnectedDeviceInfo['connected'] == true) {
Logger.error('打开应用设置失败: $e'); Logger.info('使用上次连接的设备信息');
Get.snackbar( _deviceInfo.value = _bluetoothService.lastConnectedDeviceInfo;
'提示', _onDeviceConnected();
'无法打开应用设置,请手动打开系统设置授予权限', return true;
snackPosition: SnackPosition.TOP,
backgroundColor: Colors.red[100],
colorText: Colors.red[800],
duration: const Duration(seconds: 3),
);
} }
return false;
} }
// 查找已连接的设备 // 查找系统已连接的设备
Future<void> _findConnectedDevices() async { Future<void> _findSystemConnectedDevices() async {
_updateStatus(PairingStatus.scanning, '正在查找已连接设备...');
try { 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('使用上次连接的设备信息');
_deviceInfo.value = _bluetoothService.lastConnectedDeviceInfo;
_onDeviceFound();
return;
}
// 2. 查找系统已连接的设备
Logger.info('查找系统已连接的设备');
BluetoothDevice? device = await _bluetoothService.findConnectedSystemDevice() BluetoothDevice? device = await _bluetoothService.findConnectedSystemDevice()
.timeout(const Duration(seconds: 5), onTimeout: () { .timeout(const Duration(seconds: 10), onTimeout: () => null);
Logger.error('获取已连接设备超时');
return null;
});
if (device != null) { if (device != null) {
Logger.info('找到系统已连接设备'); Logger.info('找到系统已连接设备');
_deviceInfo.value = _bluetoothService.getConnectedDeviceInfo(); _deviceInfo.value = _bluetoothService.getConnectedDeviceInfo();
_onDeviceFound(); _onDeviceConnected();
return; return;
} }
// 3. 如果没有找到已连接设备,提示用户选择 // 如果没有找到已连接设备,检查已配对设备
// 获取已配对设备列表 await _checkPairedDevices();
_pairedDevices.value = _bluetoothService.systemPairedDevices;
// 如果有已配对的设备,直接尝试连接第一个
if (_pairedDevices.isNotEmpty) {
Logger.info('找到已配对设备,尝试连接');
// 尝试连接第一个配对设备
await tryConnectDevice(_pairedDevices.first);
} else {
// 如果没有已配对设备,开始扫描附近设备
await _scanForDevices();
}
} catch (e) { } catch (e) {
Logger.error('查找设备失败: $e'); Logger.error('查找系统连接设备失败: $e');
_errorMessage.value = '查找设备失败'; // 继续查找已配对设备,不立即失败
_searchStatus.value = SearchStatus.failed; await _checkPairedDevices();
_stopSearchingAnimation();
// 重试查找设备
_retryFindDevices();
} }
} }
// 重试查找设备 // 检查已配对设备
void _retryFindDevices() { Future<void> _checkPairedDevices() async {
if (_retryCount < MAX_RETRY_COUNT) { _updateStatus(PairingStatus.scanning, '正在检查已配对设备...');
_retryCount++;
Logger.info('重试查找设备 (${_retryCount}/${MAX_RETRY_COUNT})'); // 获取已配对设备列表
_pairedDevices.value = _bluetoothService.systemPairedDevices;
// 如果有已配对的设备,尝试连接
if (_pairedDevices.isNotEmpty) {
Logger.info('找到${_pairedDevices.length}个已配对设备,尝试连接');
Future.delayed(const Duration(seconds: 2), () { // 根据设备名称过滤可能的音频设备
_findConnectedDevices(); BluetoothDevice deviceToConnect = _findBestAudioDevice(_pairedDevices) ?? _pairedDevices.first;
});
await _connectToDevice(deviceToConnect);
} else {
// 如果没有已配对设备,开始扫描附近设备
await _scanForDevices();
}
}
// 在设备列表中查找最可能的音频设备
BluetoothDevice? _findBestAudioDevice(List<BluetoothDevice> devices) {
if (devices.isEmpty) return null;
for (var device in devices) {
String name = device.platformName.toLowerCase();
if (name.contains('headphone') ||
name.contains('earphone') ||
name.contains('earbuds') ||
name.contains('headset') ||
name.contains('speaker') ||
name.contains('audio') ||
name.contains('耳机') ||
name.contains('音箱')) {
return device;
}
} }
return null;
} }
// 扫描附近的蓝牙设备 // 扫描附近的蓝牙设备
Future<void> _scanForDevices() async { Future<void> _scanForDevices() async {
_updateStatus(PairingStatus.scanning, '正在扫描附近设备...');
_scanRetryCount = 0;
try { try {
Logger.info('开始扫描附近设备'); // 确保先停止之前的扫描
_searchStatus.value = SearchStatus.searching; await _bluetoothService.stopScan();
_startSearchingAnimation();
// 开始扫描 // 开始扫描
List<ScanResult> results = await _bluetoothService.startScan( List<ScanResult> results = await _bluetoothService.startScan(
timeout: const Duration(seconds: 10) timeout: const Duration(seconds: 12)
); );
_scannedDevices.value = results; _scannedDevices.value = results;
// 过滤出可能是耳机的设备 if (results.isEmpty) {
_handleNoDevicesFound();
return;
}
// 过滤可能的音频设备
List<ScanResult> audioDevices = results.where((result) { List<ScanResult> audioDevices = results.where((result) {
String name = result.device.platformName.toLowerCase(); String name = result.device.platformName.toLowerCase();
return name.contains('headphone') || return name.contains('headphone') ||
@ -294,141 +399,169 @@ class PairingController extends GetxController {
name.contains('音箱'); name.contains('音箱');
}).toList(); }).toList();
// 按信号强度排序
if (audioDevices.isNotEmpty) { if (audioDevices.isNotEmpty) {
// 按信号强度排序
audioDevices.sort((a, b) => b.rssi.compareTo(a.rssi)); audioDevices.sort((a, b) => b.rssi.compareTo(a.rssi));
Logger.info('找到可能的音频设备'); Logger.info('找到${audioDevices.length}个可能的音频设备');
// 直接尝试连接信号最强的音频设备 // 连接信号最强的音频设备
await tryConnectDevice(audioDevices.first.device); await _connectToDevice(audioDevices.first.device);
} else if (results.isNotEmpty) { } else if (results.isNotEmpty) {
// 如果没有明确的音频设备但有其他设备,尝试连接信号最强的设备 // 如果没有明确的音频设备但有其他设备,按信号排序
results.sort((a, b) => b.rssi.compareTo(a.rssi)); results.sort((a, b) => b.rssi.compareTo(a.rssi));
Logger.info('未找到明确的音频设备,尝试连接信号最强的设备'); Logger.info('未找到明确的音频设备,尝试连接信号最强的设备');
await tryConnectDevice(results.first.device); await _connectToDevice(results.first.device);
} else {
// 未找到任何设备
_errorMessage.value = '未找到蓝牙设备,请确保设备已开启并在附近';
_searchStatus.value = SearchStatus.failed;
_stopSearchingAnimation();
// 重试扫描
_retryScan();
} }
} catch (e) { } catch (e) {
Logger.error('扫描设备失败: $e'); Logger.error('扫描设备失败: $e');
_errorMessage.value = '扫描设备失败'; _updateStatus(PairingStatus.failed, '扫描失败');
_searchStatus.value = SearchStatus.failed; _setError('扫描设备失败', '请确保蓝牙已开启并重试');
_stopSearchingAnimation();
// 重试扫描
_retryScan();
} }
} }
// 重试扫描 // 处理未找到设备的情况
void _retryScan() { void _handleNoDevicesFound() {
if (_retryCount < MAX_RETRY_COUNT) { if (_scanRetryCount < MAX_RETRY_COUNT) {
_retryCount++; _scanRetryCount++;
Logger.info('重试扫描设备 (${_retryCount}/${MAX_RETRY_COUNT})'); Logger.info('未找到设备,自动重试 (${_scanRetryCount}/${MAX_RETRY_COUNT})');
_updateStatus(PairingStatus.scanning, '未找到设备,重试中...');
Future.delayed(const Duration(seconds: 2), () { // 延迟2秒后重试
_scanForDevices(); Future.delayed(const Duration(seconds: 2), _scanForDevices);
}); } else {
_updateStatus(PairingStatus.failed, '未找到设备');
_setError('未找到任何蓝牙设备', '请确保设备已开启并在附近,或点击刷新重试');
} }
} }
// 尝试连接特定设备 // 连接到指定设备
Future<void> tryConnectDevice(BluetoothDevice device) async { Future<void> _connectToDevice(BluetoothDevice device) async {
_updateStatus(PairingStatus.connecting, '正在连接到 "${device.platformName}"...');
_connectRetryCount = 0;
try { try {
Logger.info('尝试连接设备'); // 停止当前扫描
_searchStatus.value = SearchStatus.searching; await _bluetoothService.stopScan();
_startSearchingAnimation();
_errorMessage.value = '';
bool connected = await _bluetoothService.connectToDevice(device); bool connected = await _bluetoothService.connectToDevice(device);
if (connected) { if (connected) {
Logger.info('连接设备成功'); Logger.info('成功连接到设备: ${device.platformName}');
_deviceInfo.value = _bluetoothService.getConnectedDeviceInfo(); _deviceInfo.value = _bluetoothService.getConnectedDeviceInfo();
_onDeviceFound(); _onDeviceConnected();
} else { } else {
Logger.error('连接设备失败'); _handleConnectionFailed(device);
// 检查设备类型
if (_bluetoothService.deviceType == DeviceType.classicAudio) {
// 对于经典蓝牙音频设备,提示用户在系统设置中配对
_searchStatus.value = SearchStatus.needsSystemPairing;
_errorMessage.value = '这是音频设备,需要在系统设置中配对并连接';
} else {
// 其他类型设备连接失败
_searchStatus.value = SearchStatus.failed;
_errorMessage.value = '连接设备失败,请重试或检查设备状态';
}
_stopSearchingAnimation();
// 重试连接
_retryConnect(device);
} }
} catch (e) { } catch (e) {
Logger.error('连接设备失败: $e'); Logger.error('连接设备失败: $e');
_errorMessage.value = '连接设备失败'; _handleConnectionFailed(device);
_searchStatus.value = SearchStatus.failed;
_stopSearchingAnimation();
// 重试连接
_retryConnect(device);
} }
} }
// 重试连接 // 处理连接失败
void _retryConnect(BluetoothDevice device) { void _handleConnectionFailed(BluetoothDevice device) {
if (_retryCount < MAX_RETRY_COUNT) { // 检查设备类型
_retryCount++; if (_bluetoothService.deviceType == DeviceType.classicAudio) {
Logger.info('重试连接设备 (${_retryCount}/${MAX_RETRY_COUNT})'); // 经典蓝牙音频设备需要在系统设置中配对
_updateStatus(PairingStatus.needsSystemPairing, '需要系统配对');
_setError('这是音频设备,需要在系统设置中配对', '请前往系统蓝牙设置完成配对');
} else if (_connectRetryCount < MAX_RETRY_COUNT) {
// 自动重试连接
_connectRetryCount++;
Logger.info('连接失败,自动重试 (${_connectRetryCount}/${MAX_RETRY_COUNT})');
_updateStatus(PairingStatus.connecting, '连接失败,重试中...');
Future.delayed(const Duration(seconds: 2), () { // 延迟2秒后重试
tryConnectDevice(device); Future.delayed(const Duration(seconds: 2), () => _connectToDevice(device));
}); } else {
// 超过重试次数
_updateStatus(PairingStatus.failed, '连接失败');
_setError('连接设备失败', '请确保设备已开启且处于配对模式,或点击刷新重试');
} }
} }
// 设备找到并连接成功后的处理 // 设备连接成功
void _onDeviceFound() { void _onDeviceConnected() {
_searchStatus.value = SearchStatus.found; _updateStatus(PairingStatus.found, '设备连接成功');
_stopSearchingAnimation();
// 更新日志并延迟跳转到主页 // 延迟跳转到主页
Logger.info('设备连接成功,准备跳转到主页');
Timer(const Duration(seconds: 1), () { Timer(const Duration(seconds: 1), () {
Get.offAllNamed(Routes.home, arguments: _deviceInfo.value); Get.offAllNamed(Routes.home, arguments: _deviceInfo.value);
}); });
} }
// 手动刷新/重新搜索 // 更新状态(内部方法)
void _updateStatus(PairingStatus status, String description) {
_pairingStatus.value = status;
_statusDescription.value = description;
// 根据状态自动控制动画
if ([PairingStatus.initial, PairingStatus.requesting, PairingStatus.scanning, PairingStatus.connecting].contains(status)) {
_startAnimation();
} else {
_stopAnimation();
}
}
// 设置错误信息
void _setError(String message, String solution) {
_errorMessage.value = '$message\n$solution';
}
// 动画控制
void _startAnimation() {
_stopAnimation(); // 确保先停止已有动画
int count = 0;
_animationTimer = Timer.periodic(const Duration(milliseconds: 500), (timer) {
count = (count + 1) % 4;
_animationDots.value = '.' * count;
});
}
void _stopAnimation() {
_animationTimer?.cancel();
_animationTimer = null;
_animationDots.value = '';
}
// 公开方法
// 刷新搜索
void refreshSearch() { void refreshSearch() {
// 停止当前扫描(如果有) // 停止当前动画和扫描
_stopAnimation();
_bluetoothService.stopScan(); _bluetoothService.stopScan();
// 重置重试计数 // 重置重试计数
_retryCount = 0; _scanRetryCount = 0;
_connectRetryCount = 0;
// 更新UI状态 // 重新开始流程
_startSearchingAnimation(); _initializeProcess();
_searchStatus.value = SearchStatus.searching; }
_errorMessage.value = '';
// 打开应用设置
// 重新开始查找过程 void goToAppSettings() async {
_findConnectedDevices(); try {
await openAppSettings();
} catch (e) {
Logger.error('打开应用设置失败: $e');
Get.snackbar(
'提示',
'无法自动打开设置,请手动前往系统设置授予权限',
snackPosition: SnackPosition.TOP,
backgroundColor: Colors.red[100],
colorText: Colors.red[800],
duration: const Duration(seconds: 3),
);
}
} }
// 跳转到系统蓝牙设置 // 跳转到系统蓝牙设置
void openSystemBluetoothSettings() async { void openSystemBluetoothSettings() async {
try { try {
Logger.info('尝试打开系统蓝牙设置');
// 不同平台需要不同的处理
if (GetPlatform.isAndroid) { if (GetPlatform.isAndroid) {
await openAppSettings(); await openAppSettings();
} else { } else {
@ -443,10 +576,10 @@ class PairingController extends GetxController {
); );
} }
} catch (e) { } catch (e) {
Logger.error('打开系统蓝牙设置失败: $e'); Logger.error('打开系统设置失败: $e');
Get.snackbar( Get.snackbar(
'提示', '提示',
'无法打开系统设置,请手动打开蓝牙设置并配对设备', '无法自动打开设置,请手动前往系统蓝牙设置',
snackPosition: SnackPosition.TOP, snackPosition: SnackPosition.TOP,
backgroundColor: Colors.red[100], backgroundColor: Colors.red[100],
colorText: Colors.red[800], colorText: Colors.red[800],
@ -455,13 +588,18 @@ class PairingController extends GetxController {
} }
} }
// 稍后再说 // 跳过连接
void skipConnecting() { void skipConnecting() {
// 停止当前扫描 // 停止扫描和动画
_bluetoothService.stopScan(); _bluetoothService.stopScan();
_stopSearchingAnimation(); _stopAnimation();
// 直接跳转到主页 // 直接跳转到主页
Get.offAllNamed(Routes.home); Get.offAllNamed(Routes.home);
} }
// 手动连接特定设备
void connectToDevice(BluetoothDevice device) {
_connectToDevice(device);
}
} }

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

@ -171,12 +171,15 @@ class PairingView extends GetView<PairingController> {
// 搜索状态 // 搜索状态
Widget _buildSearchStatus(bool isDarkMode) { Widget _buildSearchStatus(bool isDarkMode) {
return Obx(() { return Obx(() {
switch (controller.searchStatus) { switch (controller.pairingStatus) {
case SearchStatus.searching: case PairingStatus.initial:
case PairingStatus.requesting:
case PairingStatus.scanning:
case PairingStatus.connecting:
return Column( return Column(
children: [ children: [
Text( Text(
'寻找设备中${controller.searchDots}', '${controller.statusDescription}${controller.animationDots}',
style: TextStyle( style: TextStyle(
fontSize: 18.sp, fontSize: 18.sp,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
@ -193,7 +196,7 @@ class PairingView extends GetView<PairingController> {
), ),
], ],
); );
case SearchStatus.found: case PairingStatus.found:
return Column( return Column(
children: [ children: [
Row( Row(
@ -221,7 +224,7 @@ class PairingView extends GetView<PairingController> {
), ),
], ],
); );
case SearchStatus.noPermission: case PairingStatus.noPermission:
return Column( return Column(
children: [ children: [
Row( Row(
@ -263,7 +266,36 @@ class PairingView extends GetView<PairingController> {
), ),
], ],
); );
case SearchStatus.failed: case PairingStatus.bluetoothOff:
return Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.bluetooth_disabled, color: Colors.orange, size: 20.sp),
SizedBox(width: 8.w),
Text(
'蓝牙已关闭',
style: TextStyle(
fontSize: 18.sp,
fontWeight: FontWeight.w600,
color: Colors.orange,
),
),
],
),
SizedBox(height: 8.h),
Text(
controller.errorMessage,
style: TextStyle(
fontSize: 14.sp,
color: isDarkMode ? Colors.white70 : Colors.black54,
),
textAlign: TextAlign.center,
),
],
);
case PairingStatus.failed:
return Column( return Column(
children: [ children: [
Row( Row(
@ -272,7 +304,7 @@ class PairingView extends GetView<PairingController> {
Icon(Icons.error, color: Colors.red, size: 20.sp), Icon(Icons.error, color: Colors.red, size: 20.sp),
SizedBox(width: 8.w), SizedBox(width: 8.w),
Text( Text(
'未找到设备', controller.statusDescription.isEmpty ? '未找到设备' : controller.statusDescription,
style: TextStyle( style: TextStyle(
fontSize: 18.sp, fontSize: 18.sp,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
@ -293,7 +325,7 @@ class PairingView extends GetView<PairingController> {
), ),
], ],
); );
case SearchStatus.needsSystemPairing: case PairingStatus.needsSystemPairing:
return Column( return Column(
children: [ children: [
Row( Row(

1
pubspec.yaml

@ -45,6 +45,7 @@ dependencies:
image_picker: ^1.1.2 image_picker: ^1.1.2
audio_waveforms: ^1.3.0 audio_waveforms: ^1.3.0
font_awesome_flutter: ^10.8.0 font_awesome_flutter: ^10.8.0
lottie: ^3.3.1
dev_dependencies: dev_dependencies:
flutter_test: flutter_test:

Loading…
Cancel
Save