You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
470 lines
16 KiB
470 lines
16 KiB
import 'dart:async';
|
|
import 'package:flutter/foundation.dart';
|
|
import 'package:flutter_blue_plus/flutter_blue_plus.dart';
|
|
import 'package:get/get.dart';
|
|
import '../../core/utils/logger.dart';
|
|
import 'dart:io' show Platform;
|
|
import 'package:permission_handler/permission_handler.dart';
|
|
|
|
/// 蓝牙设备服务,用于发现和管理蓝牙设备
|
|
class BluetoothService extends GetxService {
|
|
static BluetoothService get to => Get.find<BluetoothService>();
|
|
|
|
// 单例实例
|
|
static final BluetoothService _instance = BluetoothService._internal();
|
|
factory BluetoothService() => _instance;
|
|
BluetoothService._internal();
|
|
|
|
// 设备状态
|
|
final _isAvailable = false.obs;
|
|
final _isOn = false.obs;
|
|
|
|
// 已连接的设备
|
|
final _connectedDevices = <BluetoothDevice>[].obs;
|
|
|
|
// 扫描状态
|
|
final _isScanning = false.obs;
|
|
|
|
// 扫描结果
|
|
final _scanResults = <ScanResult>[].obs;
|
|
|
|
// 当前选中的设备
|
|
final _selectedDevice = Rx<BluetoothDevice?>(null);
|
|
|
|
// 连接状态
|
|
final _connectionState = Rx<BluetoothConnectionState>(BluetoothConnectionState.disconnected);
|
|
|
|
// 存储最后连接的设备信息,供其他控制器使用
|
|
final _lastConnectedDeviceInfo = Rx<Map<String, dynamic>>({});
|
|
|
|
// 订阅
|
|
StreamSubscription? _stateSubscription;
|
|
StreamSubscription? _scanSubscription;
|
|
StreamSubscription? _connectionSubscription;
|
|
|
|
// 权限状态
|
|
final _permissionsGranted = false.obs;
|
|
bool get permissionsGranted => _permissionsGranted.value;
|
|
|
|
// Getters
|
|
bool get isAvailable => _isAvailable.value;
|
|
bool get isOn => _isOn.value;
|
|
bool get isScanning => _isScanning.value;
|
|
List<BluetoothDevice> get connectedDevices => _connectedDevices;
|
|
List<ScanResult> get scanResults => _scanResults;
|
|
BluetoothDevice? get selectedDevice => _selectedDevice.value;
|
|
BluetoothConnectionState get connectionState => _connectionState.value;
|
|
Map<String, dynamic> get lastConnectedDeviceInfo => _lastConnectedDeviceInfo.value;
|
|
|
|
// Observable getters (for reactive programming)
|
|
RxBool get isAvailableObs => _isAvailable;
|
|
RxBool get isOnObs => _isOn;
|
|
RxList<BluetoothDevice> get connectedDevicesObs => _connectedDevices;
|
|
RxList<ScanResult> get scanResultsObs => _scanResults;
|
|
Rx<BluetoothDevice?> get selectedDeviceObs => _selectedDevice;
|
|
Rx<BluetoothConnectionState> get connectionStateObs => _connectionState;
|
|
Rx<Map<String, dynamic>> get lastConnectedDeviceInfoObs => _lastConnectedDeviceInfo;
|
|
|
|
/// 初始化蓝牙服务
|
|
Future<BluetoothService> init() async {
|
|
try {
|
|
// 不在初始化时检查权限,改为在需要时才检查
|
|
// await _checkAndRequestPermissions();
|
|
|
|
// 在后台线程初始化蓝牙状态监听,避免主线程阻塞
|
|
Future.microtask(() {
|
|
// 监听蓝牙状态
|
|
_stateSubscription = FlutterBluePlus.adapterState.listen((state) {
|
|
_isOn.value = state == BluetoothAdapterState.on;
|
|
_isAvailable.value = state != BluetoothAdapterState.unavailable;
|
|
|
|
if (state == BluetoothAdapterState.on) {
|
|
// 蓝牙打开时,获取已连接设备,但不请求权限
|
|
// 只有在用户主动扫描时才请求权限
|
|
// 在后台线程执行,避免主线程阻塞
|
|
Future.microtask(() => _getConnectedDevices());
|
|
} else if (state == BluetoothAdapterState.off) {
|
|
// 蓝牙关闭时,清空设备列表
|
|
_connectedDevices.clear();
|
|
_scanResults.clear();
|
|
_selectedDevice.value = null;
|
|
_lastConnectedDeviceInfo.value = {};
|
|
}
|
|
});
|
|
});
|
|
|
|
// 不在初始化时立即检查蓝牙状态,而是延迟到UI渲染完成后
|
|
// 这样可以避免启动时卡顿
|
|
Future.delayed(const Duration(milliseconds: 800), () async {
|
|
try {
|
|
// 使用超时机制,避免无限等待蓝牙
|
|
bool isOn = await FlutterBluePlus.isOn
|
|
.timeout(const Duration(seconds: 3), onTimeout: () {
|
|
Logger.error('检查蓝牙状态超时');
|
|
return false;
|
|
});
|
|
|
|
_isOn.value = isOn;
|
|
_isAvailable.value = isOn;
|
|
|
|
if (isOn) {
|
|
// 在后台线程获取设备,避免主线程阻塞
|
|
Future.microtask(() => _getConnectedDevices());
|
|
}
|
|
} catch (e) {
|
|
Logger.error('延迟检查蓝牙状态失败: $e');
|
|
}
|
|
});
|
|
|
|
return this;
|
|
} catch (e) {
|
|
Logger.error('初始化蓝牙服务失败: $e');
|
|
return this;
|
|
}
|
|
}
|
|
|
|
/// 检查并请求蓝牙所需权限
|
|
Future<bool> _checkAndRequestPermissions() async {
|
|
// 暂时跳过权限检查,直接返回true
|
|
_permissionsGranted.value = true;
|
|
return true;
|
|
}
|
|
|
|
/// 再次请求权限
|
|
Future<bool> requestPermissions() async {
|
|
// 暂时跳过权限检查,直接返回true
|
|
_permissionsGranted.value = true;
|
|
return true;
|
|
}
|
|
|
|
/// 获取已连接的设备
|
|
Future<void> _getConnectedDevices() async {
|
|
try {
|
|
// 使用超时机制,避免无限等待
|
|
List<BluetoothDevice> devices = await FlutterBluePlus.connectedSystemDevices
|
|
.timeout(const Duration(seconds: 3), onTimeout: () {
|
|
Logger.error('获取已连接设备超时');
|
|
return <BluetoothDevice>[];
|
|
});
|
|
|
|
_connectedDevices.value = devices;
|
|
|
|
// 如果有已连接的设备但没有选中设备,则选中第一个
|
|
if (_connectedDevices.isNotEmpty && _selectedDevice.value == null) {
|
|
_selectedDevice.value = _connectedDevices[0];
|
|
_monitorConnectionState(_selectedDevice.value!);
|
|
// 更新最后连接的设备信息
|
|
_updateLastConnectedDeviceInfo();
|
|
}
|
|
|
|
Logger.info('已连接设备数量: ${_connectedDevices.length}');
|
|
} catch (e) {
|
|
Logger.error('获取已连接设备失败: $e');
|
|
}
|
|
}
|
|
|
|
/// 获取系统中已配对的设备
|
|
Future<List<BluetoothDevice>> getPairedDevices() async {
|
|
try {
|
|
// 获取已配对的设备(系统已知设备)
|
|
List<BluetoothDevice> systemDevices = await FlutterBluePlus.bondedDevices;
|
|
return systemDevices;
|
|
} catch (e) {
|
|
Logger.error('获取已配对设备失败: $e');
|
|
return [];
|
|
}
|
|
}
|
|
|
|
/// 开始扫描附近的蓝牙设备并尝试连接最强信号设备
|
|
Future<BluetoothDevice?> scanAndConnectToDevice({Duration? timeout}) async {
|
|
if (_isScanning.value) {
|
|
await stopScan();
|
|
}
|
|
|
|
// 清空旧的扫描结果
|
|
_scanResults.clear();
|
|
|
|
try {
|
|
// 设置扫描监听
|
|
_scanSubscription?.cancel();
|
|
_scanSubscription = FlutterBluePlus.scanResults.listen((results) {
|
|
_scanResults.value = results;
|
|
});
|
|
|
|
// 设置扫描中状态
|
|
_isScanning.value = true;
|
|
|
|
// 开始扫描
|
|
await FlutterBluePlus.startScan(
|
|
timeout: timeout ?? const Duration(seconds: 5),
|
|
);
|
|
|
|
// 扫描完成
|
|
_isScanning.value = false;
|
|
Logger.info('蓝牙扫描完成,发现 ${_scanResults.length} 个设备');
|
|
|
|
// 如果找到设备,尝试连接信号最强的
|
|
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) {
|
|
_isScanning.value = false;
|
|
Logger.error('蓝牙扫描或连接失败: $e');
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/// 停止扫描
|
|
Future<void> stopScan() async {
|
|
if (!_isScanning.value) return;
|
|
|
|
try {
|
|
await FlutterBluePlus.stopScan();
|
|
_isScanning.value = false;
|
|
} catch (e) {
|
|
Logger.error('停止蓝牙扫描失败: $e');
|
|
}
|
|
}
|
|
|
|
/// 连接到指定的蓝牙设备 - 简化版本
|
|
Future<bool> connectToDevice(BluetoothDevice device) async {
|
|
Logger.info('尝试连接设备: ${device.platformName} (${device.remoteId.str})');
|
|
|
|
try {
|
|
// 如果是当前已连接设备,直接返回成功
|
|
if (_selectedDevice.value == device &&
|
|
_connectionState.value == BluetoothConnectionState.connected) {
|
|
Logger.info('设备已连接,无需重新连接');
|
|
return true;
|
|
}
|
|
|
|
// 断开当前连接
|
|
if (_selectedDevice.value != null && _selectedDevice.value != device) {
|
|
await disconnectFromDevice();
|
|
}
|
|
|
|
// 直接尝试连接
|
|
Logger.info('直接连接设备 ${device.platformName}');
|
|
await device.connect();
|
|
|
|
// 更新设备信息
|
|
_selectedDevice.value = device;
|
|
_monitorConnectionState(device);
|
|
_updateLastConnectedDeviceInfo();
|
|
|
|
Logger.info('设备 ${device.platformName} 连接成功');
|
|
return true;
|
|
} catch (e) {
|
|
Logger.error('连接设备 ${device.platformName} 失败: $e');
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/// 断开与当前设备的连接
|
|
Future<bool> disconnectFromDevice() async {
|
|
if (_selectedDevice.value == null) {
|
|
Logger.info('没有当前选中的设备,无需断开连接');
|
|
return true;
|
|
}
|
|
|
|
try {
|
|
Logger.info('正在断开设备 ${_selectedDevice.value!.platformName} 的连接');
|
|
await _selectedDevice.value!.disconnect();
|
|
Logger.info('设备连接已断开,取消连接状态监听');
|
|
_connectionSubscription?.cancel();
|
|
_connectionSubscription = null;
|
|
_selectedDevice.value = null;
|
|
// 清除最后连接的设备信息
|
|
_lastConnectedDeviceInfo.value = {};
|
|
|
|
return true;
|
|
} catch (e) {
|
|
Logger.error('断开蓝牙设备连接失败: $e');
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/// 监控连接状态
|
|
void _monitorConnectionState(BluetoothDevice device) {
|
|
Logger.info('设置设备 ${device.platformName} 的连接状态监听');
|
|
// 取消之前的监听
|
|
if (_connectionSubscription != null) {
|
|
Logger.info('取消之前的连接状态监听');
|
|
_connectionSubscription!.cancel();
|
|
_connectionSubscription = null;
|
|
}
|
|
|
|
// 设置新的监听
|
|
_connectionSubscription = device.connectionState.listen((state) {
|
|
Logger.info('设备 ${device.platformName} 连接状态变更为: $state');
|
|
_connectionState.value = state;
|
|
|
|
if (state == BluetoothConnectionState.disconnected) {
|
|
Logger.info('设备 ${device.platformName} 已断开连接');
|
|
_selectedDevice.value = null;
|
|
// 设备断开连接时,不清除最后连接的设备信息,以便下次可以重新连接
|
|
} else if (state == BluetoothConnectionState.connected) {
|
|
Logger.info('设备 ${device.platformName} 已成功连接');
|
|
// 设备连接成功时,更新最后连接的设备信息
|
|
_updateLastConnectedDeviceInfo();
|
|
} else if (state == BluetoothConnectionState.connecting) {
|
|
Logger.info('设备 ${device.platformName} 正在连接中...');
|
|
} else if (state == BluetoothConnectionState.disconnecting) {
|
|
Logger.info('设备 ${device.platformName} 正在断开连接...');
|
|
}
|
|
});
|
|
|
|
Logger.info('设备 ${device.platformName} 的连接状态监听已设置');
|
|
}
|
|
|
|
/// 获取已连接设备的信息
|
|
Map<String, dynamic> getConnectedDeviceInfo() {
|
|
if (_selectedDevice.value == null) {
|
|
return {
|
|
'connected': false,
|
|
'device': null,
|
|
};
|
|
}
|
|
|
|
final deviceInfo = {
|
|
'connected': _connectionState.value == BluetoothConnectionState.connected,
|
|
'device': {
|
|
'name': _selectedDevice.value!.platformName.isEmpty ?
|
|
'未命名设备' : _selectedDevice.value!.platformName,
|
|
'id': _selectedDevice.value!.remoteId.str,
|
|
'isConnected': _connectionState.value == BluetoothConnectionState.connected,
|
|
'rssi': _scanResults
|
|
.firstWhereOrNull((r) => r.device.remoteId == _selectedDevice.value!.remoteId)
|
|
?.rssi
|
|
}
|
|
};
|
|
|
|
// 更新最后连接的设备信息
|
|
_lastConnectedDeviceInfo.value = deviceInfo;
|
|
|
|
return deviceInfo;
|
|
}
|
|
|
|
/// 更新最后连接的设备信息
|
|
void _updateLastConnectedDeviceInfo() {
|
|
if (_selectedDevice.value != null) {
|
|
_lastConnectedDeviceInfo.value = {
|
|
'connected': _connectionState.value == BluetoothConnectionState.connected,
|
|
'device': {
|
|
'name': _selectedDevice.value!.platformName.isEmpty ?
|
|
'未命名设备' : _selectedDevice.value!.platformName,
|
|
'id': _selectedDevice.value!.remoteId.str,
|
|
'isConnected': _connectionState.value == BluetoothConnectionState.connected,
|
|
'rssi': _scanResults
|
|
.firstWhereOrNull((r) => r.device.remoteId == _selectedDevice.value!.remoteId)
|
|
?.rssi
|
|
}
|
|
};
|
|
}
|
|
}
|
|
|
|
/// 一个综合方法,执行自动查找并连接设备的完整流程
|
|
Future<BluetoothDevice?> autoConnectDevice() async {
|
|
try {
|
|
// 1. 先检查当前已连接设备
|
|
if (_selectedDevice.value != null &&
|
|
_connectionState.value == BluetoothConnectionState.connected) {
|
|
Logger.info('使用当前已连接设备: ${_selectedDevice.value!.platformName}');
|
|
return _selectedDevice.value;
|
|
}
|
|
|
|
// 2. 检查系统中已连接的设备
|
|
try {
|
|
List<BluetoothDevice> connectedDevices = await FlutterBluePlus.connectedSystemDevices
|
|
.timeout(const Duration(seconds: 3));
|
|
|
|
if (connectedDevices.isNotEmpty) {
|
|
// 有已连接的设备,直接使用第一个
|
|
Logger.info('发现系统已连接设备: ${connectedDevices.first.platformName}');
|
|
|
|
// 连接设备并更新服务状态
|
|
await connectToDevice(connectedDevices.first);
|
|
return connectedDevices.first;
|
|
}
|
|
} catch (e) {
|
|
Logger.error('检查系统连接设备失败: $e');
|
|
}
|
|
|
|
// 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. 没有找到可用设备
|
|
return null;
|
|
} catch (e) {
|
|
Logger.error('自动连接设备流程失败: $e');
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/// 获取设备连接状态的简化方法 (用于兼容现有代码)
|
|
Future<BluetoothConnectionState> getReliableConnectionState(BluetoothDevice device) async {
|
|
try {
|
|
// 直接获取设备当前状态
|
|
BluetoothConnectionState state = await device.connectionState.first
|
|
.timeout(const Duration(seconds: 2), onTimeout: () {
|
|
Logger.info('获取设备 ${device.platformName} 连接状态超时');
|
|
return BluetoothConnectionState.disconnected;
|
|
});
|
|
|
|
// 如果获取到的状态是已连接,直接返回
|
|
if (state == BluetoothConnectionState.connected) {
|
|
return BluetoothConnectionState.connected;
|
|
}
|
|
|
|
// 作为备用检查,看设备是否在系统已连接列表中
|
|
try {
|
|
List<BluetoothDevice> connectedDevices = await FlutterBluePlus.connectedSystemDevices
|
|
.timeout(const Duration(seconds: 2));
|
|
|
|
for (var connectedDevice in connectedDevices) {
|
|
if (connectedDevice.remoteId.str == device.remoteId.str) {
|
|
return BluetoothConnectionState.connected;
|
|
}
|
|
}
|
|
} catch (e) {
|
|
// 忽略检查系统设备列表的错误
|
|
}
|
|
|
|
return state;
|
|
} catch (e) {
|
|
Logger.error('获取设备 ${device.platformName} 连接状态失败: $e');
|
|
return BluetoothConnectionState.disconnected;
|
|
}
|
|
}
|
|
|
|
@override
|
|
void onClose() {
|
|
_stateSubscription?.cancel();
|
|
_scanSubscription?.cancel();
|
|
_connectionSubscription?.cancel();
|
|
disconnectFromDevice();
|
|
super.onClose();
|
|
}
|
|
}
|