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.
300 lines
9.1 KiB
300 lines
9.1 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);
|
|
|
|
// 订阅
|
|
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;
|
|
|
|
// 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;
|
|
|
|
/// 初始化蓝牙服务
|
|
Future<BluetoothService> init() async {
|
|
try {
|
|
// 不在初始化时检查权限,改为在需要时才检查
|
|
// await _checkAndRequestPermissions();
|
|
|
|
// 监听蓝牙状态
|
|
_stateSubscription = FlutterBluePlus.adapterState.listen((state) {
|
|
_isOn.value = state == BluetoothAdapterState.on;
|
|
_isAvailable.value = state != BluetoothAdapterState.unavailable;
|
|
|
|
if (state == BluetoothAdapterState.on) {
|
|
// 蓝牙打开时,获取已连接设备,但不请求权限
|
|
// 只有在用户主动扫描时才请求权限
|
|
_getConnectedDevices();
|
|
} else if (state == BluetoothAdapterState.off) {
|
|
// 蓝牙关闭时,清空设备列表
|
|
_connectedDevices.clear();
|
|
_scanResults.clear();
|
|
_selectedDevice.value = null;
|
|
}
|
|
});
|
|
|
|
// 检查当前蓝牙状态,如果已开启则获取设备
|
|
if (await FlutterBluePlus.isOn) {
|
|
_isOn.value = true;
|
|
_isAvailable.value = true;
|
|
_getConnectedDevices();
|
|
}
|
|
|
|
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;
|
|
_connectedDevices.value = devices;
|
|
|
|
// 如果有已连接的设备但没有选中设备,则选中第一个
|
|
if (_connectedDevices.isNotEmpty && _selectedDevice.value == null) {
|
|
_selectedDevice.value = _connectedDevices[0];
|
|
_monitorConnectionState(_selectedDevice.value!);
|
|
}
|
|
|
|
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<void> startScan({Duration? timeout}) async {
|
|
if (_isScanning.value) return;
|
|
|
|
// 清空旧的扫描结果
|
|
_scanResults.clear();
|
|
|
|
try {
|
|
// 开始扫描
|
|
_scanSubscription?.cancel();
|
|
_scanSubscription = FlutterBluePlus.scanResults.listen((results) {
|
|
_scanResults.value = results;
|
|
});
|
|
|
|
// 设置扫描中状态
|
|
_isScanning.value = true;
|
|
|
|
// 开始扫描
|
|
await FlutterBluePlus.startScan(
|
|
timeout: timeout ?? const Duration(seconds: 10),
|
|
);
|
|
|
|
// 扫描完成后更新状态
|
|
_isScanning.value = false;
|
|
|
|
Logger.info('蓝牙扫描完成,发现 ${_scanResults.length} 个设备');
|
|
} catch (e) {
|
|
_isScanning.value = false;
|
|
Logger.error('蓝牙扫描失败: $e');
|
|
}
|
|
}
|
|
|
|
/// 停止扫描
|
|
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 {
|
|
if (_selectedDevice.value == device) {
|
|
// 如果是当前选中设备,检查连接状态
|
|
if (_connectionState.value == BluetoothConnectionState.connected) {
|
|
Logger.info('设备 ${device.platformName} 已经连接,无需重新连接');
|
|
return true;
|
|
}
|
|
}
|
|
|
|
try {
|
|
// 检查设备是否已连接
|
|
bool isAlreadyConnected = false;
|
|
// 检查设备连接状态
|
|
BluetoothConnectionState state = await device.connectionState.first;
|
|
if (state == BluetoothConnectionState.connected) {
|
|
Logger.info('设备 ${device.platformName} 已处于连接状态');
|
|
isAlreadyConnected = true;
|
|
}
|
|
|
|
// 如果设备已连接,直接更新状态而不尝试连接
|
|
if (isAlreadyConnected) {
|
|
// 断开当前跟踪的设备(如果有)
|
|
if (_selectedDevice.value != null && _selectedDevice.value != device) {
|
|
_connectionSubscription?.cancel();
|
|
}
|
|
|
|
// 更新状态
|
|
_selectedDevice.value = device;
|
|
_monitorConnectionState(device);
|
|
|
|
return true;
|
|
}
|
|
|
|
// 断开当前连接
|
|
await disconnectFromDevice();
|
|
|
|
// 连接新设备
|
|
await device.connect();
|
|
|
|
// 更新状态
|
|
_selectedDevice.value = device;
|
|
_monitorConnectionState(device);
|
|
|
|
return true;
|
|
} catch (e) {
|
|
Logger.error('连接蓝牙设备失败: $e');
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/// 断开与当前设备的连接
|
|
Future<bool> disconnectFromDevice() async {
|
|
if (_selectedDevice.value == null) return true;
|
|
|
|
try {
|
|
await _selectedDevice.value!.disconnect();
|
|
_connectionSubscription?.cancel();
|
|
_connectionSubscription = null;
|
|
_selectedDevice.value = null;
|
|
|
|
return true;
|
|
} catch (e) {
|
|
Logger.error('断开蓝牙设备连接失败: $e');
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/// 监控连接状态
|
|
void _monitorConnectionState(BluetoothDevice device) {
|
|
_connectionSubscription?.cancel();
|
|
_connectionSubscription = device.connectionState.listen((state) {
|
|
_connectionState.value = state;
|
|
|
|
if (state == BluetoothConnectionState.disconnected) {
|
|
_selectedDevice.value = null;
|
|
}
|
|
});
|
|
}
|
|
|
|
/// 获取已连接设备的信息
|
|
Map<String, dynamic> getConnectedDeviceInfo() {
|
|
if (_selectedDevice.value == null) {
|
|
return {
|
|
'connected': false,
|
|
'device': null,
|
|
};
|
|
}
|
|
|
|
return {
|
|
'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
|
|
}
|
|
};
|
|
}
|
|
|
|
@override
|
|
void onClose() {
|
|
_stateSubscription?.cancel();
|
|
_scanSubscription?.cancel();
|
|
_connectionSubscription?.cancel();
|
|
disconnectFromDevice();
|
|
super.onClose();
|
|
}
|
|
}
|