Browse Source

blueteeth fix3

newdev_shunjiawei
wolfplus 2 years ago
parent
commit
12b4ed18da
  1. 4
      android/app/src/main/kotlin/com/example/deep_voice/VoiceInteractionService.kt
  2. 729
      lib/data/services/bluetooth_service.dart
  3. 608
      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 RECOGNITION_TIMEOUT = 10000L
private const val RECOGNITION_TIMEOUT = 8000L
// 用于跟踪服务是否正在运行
private val isRunning = AtomicBoolean(false)
@ -321,7 +321,7 @@ class VoiceInteractionService : Service() {
if (!hasSpeechDetected && !isTtsSpeaking && elapsedTime >= RECOGNITION_TIMEOUT) {
Log.d(TAG, "超过${RECOGNITION_TIMEOUT/1000}秒未检测到语音,停止识别")
isTimeoutPaused = true
playNotification("没有听到您说话,已暂停语音识别。双击耳机按钮可重新开始。")
playNotification("没有听到您说话,已暂停对话。双击耳机按钮可重新开始。")
// 停止语音识别并确保资源完全释放
stopVoiceRecognition()

729
lib/data/services/bluetooth_service.dart

@ -18,60 +18,44 @@ enum DeviceType {
class BluetoothService extends GetxService {
static BluetoothService get to => Get.find<BluetoothService>();
// 单例实例
// 内部实例
BluetoothService._internal();
static final BluetoothService _instance = BluetoothService._internal();
factory BluetoothService() => _instance;
BluetoothService._internal();
// 设备状态
// ====== 状态管理 ======
// 蓝牙适配器状态
final _isAvailable = false.obs;
final _isOn = false.obs;
final _isInitialized = false.obs;
// 已连接的设备
final _connectedDevices = <BluetoothDevice>[].obs;
final _systemPairedDevices = <BluetoothDevice>[].obs;
// 扫描状态
// 操作状态
final _isScanning = false.obs;
final _permissionsGranted = false.obs;
// 扫描结果
// 设备管理
final _connectedDevices = <BluetoothDevice>[].obs;
final _systemPairedDevices = <BluetoothDevice>[].obs;
final _scanResults = <ScanResult>[].obs;
// 当前选中的设备
final _selectedDevice = Rx<BluetoothDevice?>(null);
final _deviceType = Rx<DeviceType>(DeviceType.unknown);
// 连接状态
final _connectionState = Rx<BluetoothConnectionState>(BluetoothConnectionState.disconnected);
// 存储最后连接的设备信息,供其他控制器使用
final _lastConnectedDeviceInfo = Rx<Map<String, dynamic>>({});
// 订阅
// 流订阅
StreamSubscription? _stateSubscription;
StreamSubscription? _scanSubscription;
StreamSubscription? _connectionSubscription;
// 连接锁,防止并发操作
bool _isConnecting = false;
// 记录连接尝试次数
int _connectionAttempts = 0;
static const int MAX_CONNECTION_ATTEMPTS = 2;
// 权限状态
final _permissionsGranted = false.obs;
bool get permissionsGranted => _permissionsGranted.value;
// 初始化完成标志
final _isInitialized = false.obs;
bool get isInitialized => _isInitialized.value;
RxBool get isInitializedObs => _isInitialized;
// Getters
// ====== Getters ======
// 基本getter
bool get isAvailable => _isAvailable.value;
bool get isOn => _isOn.value;
bool get isInitialized => _isInitialized.value;
bool get isScanning => _isScanning.value;
bool get permissionsGranted => _permissionsGranted.value;
// 设备相关getter
List<BluetoothDevice> get connectedDevices => _connectedDevices;
List<BluetoothDevice> get systemPairedDevices => _systemPairedDevices;
List<ScanResult> get scanResults => _scanResults;
@ -80,9 +64,12 @@ class BluetoothService extends GetxService {
BluetoothConnectionState get connectionState => _connectionState.value;
Map<String, dynamic> get lastConnectedDeviceInfo => _lastConnectedDeviceInfo.value;
// Observable getters (for reactive programming)
// Observable getters
RxBool get isAvailableObs => _isAvailable;
RxBool get isOnObs => _isOn;
RxBool get isInitializedObs => _isInitialized;
RxBool get isScanningObs => _isScanning;
RxBool get permissionsGrantedObs => _permissionsGranted;
RxList<BluetoothDevice> get connectedDevicesObs => _connectedDevices;
RxList<ScanResult> get scanResultsObs => _scanResults;
Rx<BluetoothDevice?> get selectedDeviceObs => _selectedDevice;
@ -90,242 +77,212 @@ class BluetoothService extends GetxService {
Rx<BluetoothConnectionState> get connectionStateObs => _connectionState;
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 {
try {
if (_isInitialized.value) {
Logger.info('蓝牙服务已初始化,无需重复初始化');
return this;
}
Logger.info('开始初始化蓝牙服务');
// 先请求权限,确保权限请求完成后再进行后续操作
if (Platform.isAndroid) {
Logger.info('Android平台,请求蓝牙权限');
await _checkAndRequestPermissions();
try {
// 1. 设置蓝牙状态监听
_setupBluetoothListeners();
// 2. 初始检查蓝牙状态
_checkBluetoothState();
// 3. 标记初始化完成
_isInitialized.value = true;
Logger.info('蓝牙服务初始化完成');
return this;
} catch (e) {
Logger.error('初始化蓝牙服务失败: $e');
return this;
}
}
// 设置蓝牙状态监听
/// 设置蓝牙状态监听
void _setupBluetoothListeners() {
// 取消旧订阅(如果有)
_stateSubscription?.cancel();
// 监听蓝牙适配器状态变化
_stateSubscription = FlutterBluePlus.adapterState.listen((state) {
_isOn.value = state == BluetoothAdapterState.on;
_isAvailable.value = state != BluetoothAdapterState.unavailable;
if (state == BluetoothAdapterState.on) {
// 蓝牙打开时,获取已连接和已配对设备
Logger.info('蓝牙已开启');
if (_permissionsGranted.value) {
_refreshSystemDevices();
}
} else if (state == BluetoothAdapterState.off) {
// 蓝牙关闭时,清空设备列表
_connectedDevices.clear();
_systemPairedDevices.clear();
_scanResults.clear();
_selectedDevice.value = null;
_lastConnectedDeviceInfo.value = {};
_deviceType.value = DeviceType.unknown;
Logger.info('蓝牙已关闭');
_resetDeviceState();
}
});
}
// 检查蓝牙状态
/// 初始检查蓝牙状态
Future<void> _checkBluetoothState() async {
try {
bool isOn = await FlutterBluePlus.isOn
.timeout(const Duration(seconds: 5), onTimeout: () {
Logger.error('检查蓝牙状态超时');
return false;
});
.timeout(const Duration(seconds: 5), onTimeout: () => false);
_isOn.value = isOn;
_isAvailable.value = isOn;
if (isOn) {
// 刷新设备状态
await _refreshSystemDevices();
}
Logger.info('当前蓝牙是否已开启: $isOn');
} catch (e) {
Logger.error('检查蓝牙状态失败: $e');
_isOn.value = false;
_isAvailable.value = false;
}
// 标记初始化完成
_isInitialized.value = true;
Logger.info('蓝牙服务初始化完成');
return this;
} catch (e) {
Logger.error('初始化蓝牙服务失败: $e');
return this;
}
/// 重置设备状态
void _resetDeviceState() {
_connectedDevices.clear();
_systemPairedDevices.clear();
_scanResults.clear();
_selectedDevice.value = null;
_lastConnectedDeviceInfo.value = {};
_deviceType.value = DeviceType.unknown;
// 取消正在进行的操作
_scanSubscription?.cancel();
_connectionSubscription?.cancel();
_isScanning.value = false;
}
/// 检查并请求蓝牙所需权限(Android 12+)
Future<bool> _checkAndRequestPermissions() async {
/// 检查并请求蓝牙权限
Future<bool> requestPermissions() async {
try {
if (Platform.isAndroid) {
// 对于Android 12+,需要BLUETOOTH_SCAN和BLUETOOTH_CONNECT权限
Map<Permission, PermissionStatus> statuses = await [
// iOS无需额外权限
if (!Platform.isAndroid) {
_permissionsGranted.value = true;
return true;
}
// 请求所有必要权限
final permissions = <Permission>[
Permission.bluetoothScan,
Permission.bluetoothConnect,
Permission.location,
].request();
Permission.location, // Android需要定位权限来扫描BLE设备
Permission.microphone
];
// 一次性请求所有权限
Map<Permission, PermissionStatus> statuses = await permissions.request();
// 检查所有权限是否已授予
bool allGranted = statuses.values.every((status) => status.isGranted);
_permissionsGranted.value = allGranted;
if (!allGranted) {
Logger.warning('蓝牙权限未完全授予: $statuses');
// 检查哪些权限被拒绝
List<String> deniedPermissions = [];
statuses.forEach((permission, status) {
if (!status.isGranted) {
deniedPermissions.add(permission.toString().split('.').last);
}
});
return allGranted;
Logger.warning('权限被拒绝: ${deniedPermissions.join(", ")}');
}
// iOS不需要显式请求蓝牙权限
_permissionsGranted.value = true;
return true;
return allGranted;
} catch (e) {
Logger.error('检查蓝牙权限失败: $e');
Logger.error('请求权限失败: $e');
_permissionsGranted.value = false;
return false;
}
}
/// 再次请求权限
Future<bool> requestPermissions() async {
return await _checkAndRequestPermissions();
}
/// 刷新系统设备状态(已连接和已配对设备)
/// 刷新系统蓝牙设备(已连接和已配对)
Future<void> _refreshSystemDevices() async {
try {
// 避免重复刷新,使用标志位
bool _isRefreshing = false;
if (_isRefreshing) return;
_isRefreshing = true;
if (!_permissionsGranted.value || !_isOn.value) {
Logger.warning('无法刷新系统设备:蓝牙未开启或缺少权限');
return;
}
// 获取系统已连接设备
try {
// 获取已连接的系统设备
List<BluetoothDevice> devices = await FlutterBluePlus.connectedSystemDevices
.timeout(const Duration(seconds: 5), onTimeout: () {
Logger.error('获取已连接设备超时');
return <BluetoothDevice>[];
});
.timeout(const Duration(seconds: 10), onTimeout: () => <BluetoothDevice>[]);
_connectedDevices.value = devices;
Logger.info('已连接系统设备数量: ${_connectedDevices.length}');
Logger.info('获取到${devices.length}个系统已连接设备');
// 如果有当前选中设备,更新其状态
// 更新选中设备的连接状态(如果有)
if (_selectedDevice.value != null) {
// 检查选中设备是否仍然在已连接列表中
bool stillConnected = _connectedDevices.any(
(d) => d.remoteId.str == _selectedDevice.value!.remoteId.str
);
_connectionState.value = stillConnected
? BluetoothConnectionState.connected
: BluetoothConnectionState.disconnected;
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: 5), onTimeout: () {
Logger.error('获取已配对设备超时');
return <BluetoothDevice>[];
});
.timeout(const Duration(seconds: 10), onTimeout: () => <BluetoothDevice>[]);
_systemPairedDevices.value = pairedDevices;
Logger.info('系统已配对设备数量: ${_systemPairedDevices.length}');
Logger.info('获取到${pairedDevices.length}个系统已配对设备');
} catch (e) {
Logger.error('获取已配对设备失败: $e');
Logger.error('刷新系统设备失败: $e');
}
// 如果有已连接的设备但没有选中设备,自动选择第一个
if (_connectedDevices.isNotEmpty && _selectedDevice.value == null) {
_selectedDevice.value = _connectedDevices[0];
_determineDeviceType(_selectedDevice.value!);
_connectionState.value = BluetoothConnectionState.connected;
_updateLastConnectedDeviceInfo();
}
_isRefreshing = false;
} catch (e) {
Logger.error('刷新系统设备状态失败: $e');
}
/// 开始扫描附近的蓝牙设备
Future<List<ScanResult>> startScan({Duration? timeout}) async {
// 检查条件
if (!_permissionsGranted.value) {
Logger.warning('缺少蓝牙扫描权限,无法扫描设备');
return [];
}
/// 确定设备类型(经典蓝牙/BLE/双模)
void _determineDeviceType(BluetoothDevice device) {
try {
// 默认设为未知类型
_deviceType.value = DeviceType.unknown;
// 设备名称包含耳机/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) {
Logger.error('确定设备类型失败: $e');
_deviceType.value = DeviceType.unknown;
}
if (!_isOn.value) {
Logger.warning('蓝牙未开启,无法扫描设备');
return [];
}
/// 开始扫描附近的蓝牙设备
Future<List<ScanResult>> startScan({Duration? timeout}) async {
// 如果已经在扫描,先停止
if (_isScanning.value) {
await stopScan();
}
// 清空旧的扫描结果
_scanResults.clear();
Logger.info('开始扫描附近蓝牙设备...');
try {
// 确保拥有扫描权限
if (Platform.isAndroid && !await Permission.bluetoothScan.isGranted) {
Logger.warning('没有蓝牙扫描权限,尝试请求权限');
await _checkAndRequestPermissions();
}
// 设置扫描监听
// 设置结果监听
_scanSubscription?.cancel();
_scanSubscription = FlutterBluePlus.scanResults.listen((results) {
_scanResults.value = results;
});
// 设置扫描中状态
_isScanning.value = true;
// 开始扫描
_isScanning.value = true;
await FlutterBluePlus.startScan(
timeout: timeout ?? const Duration(seconds: 5),
timeout: timeout ?? const Duration(seconds: 10),
androidScanMode: AndroidScanMode.lowLatency,
);
// 扫描完成
_isScanning.value = false;
Logger.info('蓝牙扫描完成,发现 ${_scanResults.length} 个设备');
Logger.info('扫描完成,发现${_scanResults.length}个设备');
return _scanResults;
} catch (e) {
_isScanning.value = false;
Logger.error('蓝牙扫描失败: $e');
Logger.error('扫描设备失败: $e');
return [];
}
}
@ -337,246 +294,261 @@ class BluetoothService extends GetxService {
try {
await FlutterBluePlus.stopScan();
_isScanning.value = false;
Logger.info('已停止蓝牙扫描');
} catch (e) {
Logger.error('停止蓝牙扫描失败: $e');
Logger.error('停止扫描失败: $e');
}
}
/// 连接到指定的蓝牙设备
/// 针对不同类型设备使用不同的连接策略
/// 连接到指定蓝牙设备
Future<bool> connectToDevice(BluetoothDevice device) async {
// 防止并发连接操作
if (_isConnecting) {
Logger.warning('已有正在进行的连接操作,请等待完成');
// 检查条件
if (!_permissionsGranted.value) {
Logger.warning('缺少蓝牙连接权限,无法连接设备');
return false;
}
_isConnecting = true;
_connectionAttempts = 0;
if (!_isOn.value) {
Logger.warning('蓝牙未开启,无法连接设备');
return false;
}
try {
Logger.info('尝试连接设备: ${device.platformName} (${device.remoteId.str})');
Logger.info('尝试连接设备: ${device.platformName}');
try {
// 确定设备类型
_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;
}
// 如果有其他已选择的设备,先取消监听并清除
// 如果已选择了其他设备,先断开
if (_selectedDevice.value != null && _selectedDevice.value != device) {
_connectionSubscription?.cancel();
_connectionSubscription = null;
await disconnectFromDevice();
}
// 更新选中的设备
// 设置新的目标设备
_selectedDevice.value = device;
// 对于经典蓝牙音频设备,不尝试BLE连接,直接检查系统连接状态
// 针对不同类型设备采取不同连接策略
if (_deviceType.value == DeviceType.classicAudio) {
Logger.info('经典蓝牙音频设备,检查系统连接状态');
// 经典蓝牙设备(如音频设备)依赖系统连接
return _connectToClassicAudioDevice(device);
} else {
// BLE设备直接通过库连接
return _connectToBLEDevice(device);
}
} catch (e) {
Logger.error('连接设备过程中出错: $e');
_connectionState.value = BluetoothConnectionState.disconnected;
return false;
}
}
/// 连接经典蓝牙音频设备
Future<bool> _connectToClassicAudioDevice(BluetoothDevice device) async {
Logger.info('尝试连接经典蓝牙音频设备');
// 刷新系统连接设备列表
try {
// 刷新系统设备列表
await _refreshSystemDevices();
// 再次检查设备是否在已连接列表中
alreadyConnected = _connectedDevices.any((d) => d.remoteId.str == device.remoteId.str);
// 检查设备是否在系统已连接列表中
bool connected = _connectedDevices.any((d) => d.remoteId.str == device.remoteId.str);
if (alreadyConnected) {
Logger.info('设备已在系统连接列表中,视为已连接');
if (connected) {
Logger.info('经典蓝牙设备已在系统连接列表中');
_connectionState.value = BluetoothConnectionState.connected;
_monitorSystemConnectionState(device);
_updateLastConnectedDeviceInfo();
_isConnecting = false;
return true;
} else {
Logger.info('经典蓝牙设备未在系统连接列表中,需要在系统设置中配对和连接');
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');
Logger.error('连接经典蓝牙设备失败: $e');
_connectionState.value = BluetoothConnectionState.disconnected;
_isConnecting = false;
return false;
}
}
/// 尝试进行BLE连接
Future<bool> _attemptBleConnection(BluetoothDevice device) async {
try {
/// 连接BLE设备
Future<bool> _connectToBLEDevice(BluetoothDevice device) async {
Logger.info('尝试连接BLE设备');
_connectionState.value = BluetoothConnectionState.connecting;
// 增加连接尝试次数
_connectionAttempts++;
// 不使用autoConnect=true来避免与mtu参数冲突
// 根据错误日志,mtu和autoConnect是不兼容的
bool autoConnect = false; // 始终使用false避免兼容性问题
Logger.info('BLE连接尝试 #$_connectionAttempts,autoConnect=$autoConnect');
try {
// 设置连接状态监听
_connectionSubscription?.cancel();
_connectionSubscription = device.connectionState.listen((state) {
Logger.info('BLE连接状态变化: $state');
_connectionState.value = state;
// 开始监听连接状态
_monitorConnectionState(device);
if (state == BluetoothConnectionState.connected) {
_updateLastConnectedDeviceInfo();
}
});
// 尝试连接,不同的尝试使用不同的超时时间
if (_connectionAttempts == 1) {
// 第一次尝试:短超时
Logger.info('第一次尝试使用短超时(4秒)');
// 尝试连接
await device.connect(
autoConnect: autoConnect,
timeout: const Duration(seconds: 4)
autoConnect: false,
timeout: const Duration(seconds: 10)
);
// 等待连接状态
final completer = Completer<bool>();
bool isCompleted = false; // 添加一个本地变量跟踪完成状态
// 检查当前状态
if (_connectionState.value == BluetoothConnectionState.connected) {
completer.complete(true);
isCompleted = true;
} else {
// 第二次尝试:长超时
Logger.info('第二次尝试使用长超时(8秒)');
await device.connect(
autoConnect: autoConnect,
timeout: const Duration(seconds: 8)
);
// 设置超时
Timer(const Duration(seconds: 12), () {
if (!isCompleted) { // 使用本地变量而不是 completer.isCompleted
Logger.warning('连接设备超时');
completer.complete(false);
isCompleted = true;
}
});
}
// 如果连接成功,更新状态
_connectionState.value = BluetoothConnectionState.connected;
bool result = await completer.future;
if (result) {
Logger.info('BLE设备连接成功');
_updateLastConnectedDeviceInfo();
} else {
Logger.warning('BLE设备连接失败');
// 尝试断开连接
await device.disconnect().catchError((_) {});
}
Logger.info('设备 ${device.platformName} 连接成功');
_isConnecting = false;
return true;
return result;
} catch (e) {
// 连接失败,检查是否需要重试
Logger.error('BLE连接尝试 #$_connectionAttempts 失败: $e');
if (_connectionAttempts < MAX_CONNECTION_ATTEMPTS) {
Logger.info('尝试使用不同参数重新连接');
Logger.error('连接BLE设备失败: $e');
_connectionState.value = BluetoothConnectionState.disconnected;
// 断开连接并等待一段时间
// 尝试断开连接
try {
await device.disconnect();
} catch (disconnectError) {
// 忽略断开连接的错误
Logger.error('断开连接时出错,继续尝试: $disconnectError');
}
// 等待时间更长,确保设备和系统状态重置
await Future.delayed(const Duration(milliseconds: 2000));
} catch (_) {}
// 重试连接
return await _attemptBleConnection(device);
} else {
// 连接失败且达到最大尝试次数
_connectionState.value = BluetoothConnectionState.disconnected;
_isConnecting = false;
return false;
}
}
}
/// 断开与当前设备的连接
/// 断开当前设备连接
Future<bool> disconnectFromDevice() async {
// 没有已选择的设备
if (_selectedDevice.value == null) {
Logger.info('没有当前选中的设备,无需断开连接');
return true;
}
// 取消设备监听
// 取消连接状态监听
_connectionSubscription?.cancel();
_connectionSubscription = null;
try {
// 对于经典蓝牙设备,不调用disconnect
if (_deviceType.value == DeviceType.classicAudio) {
Logger.info('经典蓝牙设备,仅清除选择状态,不调用disconnect');
_selectedDevice.value = null;
_connectionState.value = BluetoothConnectionState.disconnected;
return true;
// 对BLE设备才需要执行断开操作
if (_deviceType.value != DeviceType.classicAudio) {
await _selectedDevice.value!.disconnect();
}
// 对于BLE设备,调用disconnect
Logger.info('正在断开设备 ${_selectedDevice.value!.platformName} 的连接');
await _selectedDevice.value!.disconnect();
// 清除设备状态
_selectedDevice.value = null;
_connectionState.value = BluetoothConnectionState.disconnected;
return true;
} catch (e) {
Logger.error('断开蓝牙设备连接失败: $e');
// 即使失败也清除状态
Logger.error('断开设备连接失败: $e');
// 即使失败,也清除选中设备
_selectedDevice.value = null;
_connectionState.value = BluetoothConnectionState.disconnected;
return false;
}
}
/// 监控BLE连接状态
void _monitorConnectionState(BluetoothDevice device) {
Logger.info('设置设备 ${device.platformName} 的BLE连接状态监听');
/// 查找系统已连接设备
Future<BluetoothDevice?> findConnectedSystemDevice() async {
if (!_permissionsGranted.value || !_isOn.value) {
Logger.warning('蓝牙未开启或缺少权限,无法查找系统连接设备');
return null;
}
// 取消之前的监听
_connectionSubscription?.cancel();
try {
// 刷新系统设备列表
await _refreshSystemDevices();
// 设置新的监听
_connectionSubscription = device.connectionState.listen((state) {
Logger.info('设备 ${device.platformName} BLE连接状态变更为: $state');
_connectionState.value = state;
if (_connectedDevices.isEmpty) {
return null;
}
if (state == BluetoothConnectionState.disconnected) {
Logger.info('设备 ${device.platformName} BLE连接已断开');
// 设备断开连接时,不清除设备信息,以便下次可以重新连接
} else if (state == BluetoothConnectionState.connected) {
Logger.info('设备 ${device.platformName} BLE连接已成功');
// 设备连接成功时,更新最后连接的设备信息
// 优先找出可能的音频设备
BluetoothDevice device = _findAudioDevice(_connectedDevices) ?? _connectedDevices.first;
// 自动选择设备
_selectedDevice.value = device;
_determineDeviceType(device);
_connectionState.value = BluetoothConnectionState.connected;
_updateLastConnectedDeviceInfo();
return device;
} catch (e) {
Logger.error('查找系统已连接设备失败: $e');
return null;
}
});
}
/// 监控系统连接状态(针对经典蓝牙设备)
void _monitorSystemConnectionState(BluetoothDevice device) {
Logger.info('设置设备 ${device.platformName} 的系统连接状态监听');
/// 查找音频设备
BluetoothDevice? _findAudioDevice(List<BluetoothDevice> devices) {
if (devices.isEmpty) return null;
// 使用定时器定期检查系统连接状态
Timer.periodic(const Duration(seconds: 5), (timer) async {
// 如果设备已变更或服务关闭,停止监控
if (_selectedDevice.value == null ||
_selectedDevice.value!.remoteId.str != device.remoteId.str) {
timer.cancel();
return;
// 音频设备的可能名称关键词
final List<String> audioKeywords = [
'headphone', 'earphone', 'earbuds', 'headset',
'speaker', 'audio', '耳机', '音箱', '音响'
];
// 尝试匹配音频设备名称
for (BluetoothDevice device in devices) {
String name = device.platformName.toLowerCase();
if (audioKeywords.any((keyword) => name.contains(keyword))) {
return device;
}
}
try {
await _refreshSystemDevices();
return null;
}
// 检查设备是否仍在系统连接列表中
bool stillConnected = _connectedDevices.any(
(d) => d.remoteId.str == device.remoteId.str
);
/// 确定设备类型
void _determineDeviceType(BluetoothDevice device) {
final String deviceName = device.platformName.toLowerCase();
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');
// 音频设备关键词
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;
} else {
_deviceType.value = DeviceType.ble;
}
});
}
/// 获取已连接设备的信息
@ -603,9 +575,6 @@ class BluetoothService extends GetxService {
'deviceType': _deviceType.value.toString().split('.').last
};
// 更新最后连接的设备信息
_lastConnectedDeviceInfo.value = deviceInfo;
return deviceInfo;
}
@ -616,123 +585,17 @@ class BluetoothService extends GetxService {
}
}
/// 一个综合方法,查找且尝试使用系统已连接的设备
/// 针对经典蓝牙耳机优化,避免使用BLE连接
Future<BluetoothDevice?> findConnectedSystemDevice() async {
try {
// 先刷新系统设备列表
await _refreshSystemDevices();
// 1. 检查当前是否有已选择且已连接的设备
if (_selectedDevice.value != null &&
_connectionState.value == BluetoothConnectionState.connected) {
Logger.info('使用当前已连接设备: ${_selectedDevice.value!.platformName}');
return _selectedDevice.value;
}
// 2. 检查系统已连接设备列表
if (_connectedDevices.isNotEmpty) {
// 优先选择可能是音频设备的
BluetoothDevice audioDevice = _connectedDevices.firstWhere(
(device) {
final name = device.platformName.toLowerCase();
return name.contains('headphone') ||
name.contains('earphone') ||
name.contains('earbuds') ||
name.contains('headset') ||
name.contains('speaker') ||
name.contains('耳机') ||
name.contains('音箱');
},
orElse: () => _connectedDevices.first
);
Logger.info('在系统已连接设备中找到设备: ${audioDevice.platformName}');
// 设置为当前设备并更新状态
_selectedDevice.value = audioDevice;
_determineDeviceType(audioDevice);
_connectionState.value = BluetoothConnectionState.connected;
_monitorSystemConnectionState(audioDevice);
_updateLastConnectedDeviceInfo();
return audioDevice;
}
// 3. 如果没有已连接设备,检查已配对设备
return null;
} catch (e) {
Logger.error('查找系统已连接设备失败: $e');
return null;
}
}
/// 获取设备电量
Future<int?> getBatteryLevel() async {
if (_selectedDevice.value == null ||
_connectionState.value != BluetoothConnectionState.connected) {
return null;
}
try {
// 对于BLE设备,尝试读取电池服务
if (_deviceType.value == DeviceType.ble || _deviceType.value == DeviceType.dual) {
// 发现设备服务
List<dynamic> services = await _selectedDevice.value!.discoverServices();
// 查找电池服务
for (var service in services) {
if (service.uuid.toString().toLowerCase() == BATTERY_SERVICE_UUID) {
// 查找电池特征
for (var characteristic in service.characteristics) {
if (characteristic.uuid.toString().toLowerCase() == BATTERY_CHARACTERISTIC_UUID) {
// 读取电池电量
List<int> value = await characteristic.read();
if (value.isNotEmpty) {
return value[0]; // 电量百分比
}
}
}
}
}
}
// 对于经典蓝牙设备,可能需要平台特定的方法
if (_deviceType.value == DeviceType.classicAudio) {
// 这里需要使用平台通道或其他插件获取经典蓝牙设备电量
// 目前根据设备名称特征尝试估计电量
// 部分设备会在名称后附加电量信息,如"XXX耳机(80%)"
if (_selectedDevice.value != null) {
final deviceName = _selectedDevice.value!.platformName;
final percentRegex = RegExp(r'(\d+)%');
final match = percentRegex.firstMatch(deviceName);
if (match != null && match.groupCount >= 1) {
final percentStr = match.group(1);
if (percentStr != null) {
final percent = int.tryParse(percentStr);
if (percent != null && percent >= 0 && percent <= 100) {
return percent;
}
}
}
}
Logger.info('无法从设备名称中提取电量信息');
}
// 如果以上方法都无法获取电量,返回null
return null;
} catch (e) {
Logger.error('获取设备电量失败: $e');
return null;
}
}
/// 清理资源
@override
void onClose() {
// 取消所有订阅
_stateSubscription?.cancel();
_scanSubscription?.cancel();
_connectionSubscription?.cancel();
// 断开设备连接
disconnectFromDevice();
super.onClose();
}
}

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

1
pubspec.yaml

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

Loading…
Cancel
Save