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.

436 lines
14 KiB

import 'dart:async';
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';
/// 设备类型枚举
enum DeviceType {
unknown, // 未知设备类型
classicAudio, // 经典蓝牙音频设备(耳机等)
ble, // BLE设备
dual // 双模设备(同时支持经典蓝牙和BLE)
}
/// 简化的蓝牙设备服务,仅用于获取已连接或配对的蓝牙耳机信息
class BluetoothService extends GetxService {
static BluetoothService get to => Get.find<BluetoothService>();
// 内部实例
BluetoothService._internal();
static final BluetoothService _instance = BluetoothService._internal();
factory BluetoothService() => _instance;
// ====== 状态管理 ======
// 蓝牙适配器状态
final _isAvailable = false.obs;
final _isOn = false.obs;
final _isInitialized = false.obs;
// 权限状态
final _permissionsGranted = false.obs;
// 设备管理
final _connectedDevices = <BluetoothDevice>[].obs;
final _systemPairedDevices = <BluetoothDevice>[].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;
// 防止重复刷新的标志
bool _isRefreshingDevices = false;
// ====== Getters ======
// 基本getter
bool get isAvailable => _isAvailable.value;
bool get isOn => _isOn.value;
bool get isInitialized => _isInitialized.value;
bool get permissionsGranted => _permissionsGranted.value;
// 设备相关getter
List<BluetoothDevice> get connectedDevices => _connectedDevices;
List<BluetoothDevice> get systemPairedDevices => _systemPairedDevices;
BluetoothDevice? get selectedDevice => _selectedDevice.value;
DeviceType get deviceType => _deviceType.value;
BluetoothConnectionState get connectionState => _connectionState.value;
Map<String, dynamic> get lastConnectedDeviceInfo => _lastConnectedDeviceInfo.value;
// Observable getters
RxBool get isAvailableObs => _isAvailable;
RxBool get isOnObs => _isOn;
RxBool get isInitializedObs => _isInitialized;
RxBool get permissionsGrantedObs => _permissionsGranted;
RxList<BluetoothDevice> get connectedDevicesObs => _connectedDevices;
Rx<BluetoothDevice?> get selectedDeviceObs => _selectedDevice;
Rx<DeviceType> get deviceTypeObs => _deviceType;
Rx<BluetoothConnectionState> get connectionStateObs => _connectionState;
Rx<Map<String, dynamic>> get lastConnectedDeviceInfoObs => _lastConnectedDeviceInfo;
/// 初始化蓝牙服务
Future<BluetoothService> init() async {
if (_isInitialized.value) {
Logger.info('蓝牙服务已初始化,无需重复初始化');
return this;
}
Logger.info('开始初始化蓝牙服务');
try {
// 1. 设置蓝牙状态监听
_setupBluetoothListeners();
// 2. 初始检查蓝牙状态
_checkBluetoothState();
// 3. 请求权限
await requestPermissions();
// 4. 刷新系统设备
if (_isOn.value && _permissionsGranted.value) {
await _refreshSystemDevices();
}
// 5. 标记初始化完成
_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) {
Logger.info('蓝牙已关闭');
_resetDeviceState();
}
});
}
/// 初始检查蓝牙状态
Future<void> _checkBluetoothState() async {
try {
bool isOn = await FlutterBluePlus.isOn
.timeout(const Duration(seconds: 5), onTimeout: () => false);
_isOn.value = isOn;
_isAvailable.value = isOn;
Logger.info('当前蓝牙是否已开启: $isOn');
} catch (e) {
Logger.error('检查蓝牙状态失败: $e');
_isOn.value = false;
_isAvailable.value = false;
}
}
/// 重置设备状态
void _resetDeviceState() {
_connectedDevices.clear();
_systemPairedDevices.clear();
_selectedDevice.value = null;
_lastConnectedDeviceInfo.value = {};
_deviceType.value = DeviceType.unknown;
}
/// 检查并请求蓝牙权限
Future<bool> requestPermissions() async {
try {
// iOS无需额外权限
if (!Platform.isAndroid) {
_permissionsGranted.value = true;
return true;
}
// 请求所有必要权限
final permissions = <Permission>[
// Permission.bluetoothScan,
// Permission.bluetoothConnect,
// 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) {
// 检查哪些权限被拒绝
List<String> deniedPermissions = [];
statuses.forEach((permission, status) {
if (!status.isGranted) {
deniedPermissions.add(permission.toString().split('.').last);
}
});
Logger.warning('权限被拒绝: ${deniedPermissions.join(", ")}');
}
return allGranted;
} catch (e) {
Logger.error('请求权限失败: $e');
_permissionsGranted.value = false;
return false;
}
}
/// 刷新系统蓝牙设备(已连接和已配对)
Future<void> _refreshSystemDevices() async {
if (!_permissionsGranted.value || !_isOn.value) {
Logger.warning('无法刷新系统设备:蓝牙未开启或缺少权限');
return;
}
// 防止重复刷新
if (_isRefreshingDevices) {
return;
}
_isRefreshingDevices = true;
try {
// 获取已连接的系统设备
List<BluetoothDevice> devices = await FlutterBluePlus.connectedSystemDevices
.timeout(const Duration(seconds: 10), onTimeout: () => <BluetoothDevice>[]);
_connectedDevices.value = devices;
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) {
_updateLastConnectedDeviceInfo();
}
}
// 获取系统已配对设备
List<BluetoothDevice> pairedDevices = await FlutterBluePlus.bondedDevices
.timeout(const Duration(seconds: 10), onTimeout: () => <BluetoothDevice>[]);
_systemPairedDevices.value = pairedDevices;
Logger.info('获取到${pairedDevices.length}个系统已配对设备');
} catch (e) {
Logger.error('刷新系统设备失败: $e');
} finally {
_isRefreshingDevices = false;
}
}
/// 查找音频设备
BluetoothDevice? _findAudioDevice(List<BluetoothDevice> devices) {
if (devices.isEmpty) return null;
// 音频设备的可能名称关键词
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;
}
}
return null;
}
/// 确定设备类型
void _determineDeviceType(BluetoothDevice device) {
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;
} else {
_deviceType.value = DeviceType.ble;
}
}
/// 获取已连接设备的信息
Map<String, dynamic> getConnectedDeviceInfo() {
if (_selectedDevice.value == null) {
return {
'connected': false,
'device': null,
'deviceType': _deviceType.value.toString().split('.').last
};
}
// 判断设备是否在已连接列表中
bool isConnected = _connectedDevices.any(
(d) => d.remoteId.str == _selectedDevice.value!.remoteId.str
);
// 判断设备是否在已配对列表中
bool isPaired = _systemPairedDevices.any(
(d) => d.remoteId.str == _selectedDevice.value!.remoteId.str
);
final deviceInfo = {
'connected': isConnected,
'paired': isPaired,
'device': {
'name': _selectedDevice.value!.platformName.isEmpty ?
'未命名设备' : _selectedDevice.value!.platformName,
'id': _selectedDevice.value!.remoteId.str,
'isConnected': isConnected,
'isPaired': isPaired,
},
'deviceType': _deviceType.value.toString().split('.').last
};
return deviceInfo;
}
/// 获取已配对设备的信息
Map<String, dynamic> getPairedDeviceInfo(BluetoothDevice device) {
if (device == null) {
return {
'connected': false,
'paired': false,
'device': null,
'deviceType': DeviceType.unknown.toString().split('.').last
};
}
// 判断设备是否在已连接列表中
bool isConnected = _connectedDevices.any(
(d) => d.remoteId.str == device.remoteId.str
);
// 确定设备类型
DeviceType type = DeviceType.unknown;
final String deviceName = device.platformName.toLowerCase();
// 音频设备关键词
final List<String> audioKeywords = [
'headphone', 'earphone', 'earbuds', 'headset',
'speaker', 'audio', '耳机', '音箱', '音响'
];
bool hasAudioKeyword = audioKeywords.any((keyword) => deviceName.contains(keyword));
if (hasAudioKeyword || isConnected) {
type = DeviceType.classicAudio;
} else {
type = DeviceType.ble;
}
final deviceInfo = {
'connected': isConnected,
'paired': true,
'device': {
'name': device.platformName.isEmpty ?
'未命名设备' : device.platformName,
'id': device.remoteId.str,
'isConnected': isConnected,
'isPaired': true,
},
'deviceType': type.toString().split('.').last
};
return deviceInfo;
}
/// 设置选中的设备(不进行连接操作)
void setSelectedDevice(BluetoothDevice device) {
if (device != null) {
_selectedDevice.value = device;
_determineDeviceType(device);
_updateLastConnectedDeviceInfo();
Logger.info('已设置选中设备: ${device.platformName}');
}
}
/// 更新最后连接的设备信息
void _updateLastConnectedDeviceInfo() {
if (_selectedDevice.value != null) {
_lastConnectedDeviceInfo.value = getConnectedDeviceInfo();
}
}
/// 查找系统已连接设备
Future<BluetoothDevice?> findConnectedSystemDevice() async {
if (!_permissionsGranted.value || !_isOn.value) {
Logger.warning('蓝牙未开启或缺少权限,无法查找系统连接设备');
return null;
}
try {
// 刷新系统设备列表(避免重复刷新)
if (!_isRefreshingDevices) {
await _refreshSystemDevices();
}
// 如果没有已连接设备,直接返回null
if (_connectedDevices.isEmpty) {
Logger.info('未找到已连接的蓝牙设备');
return null;
}
// 优先找出可能的音频设备
BluetoothDevice? audioDevice = _findAudioDevice(_connectedDevices);
BluetoothDevice device = audioDevice ?? _connectedDevices.first;
// 自动选择设备
_selectedDevice.value = device;
_determineDeviceType(device);
_connectionState.value = BluetoothConnectionState.connected;
_updateLastConnectedDeviceInfo();
Logger.info('找到已连接的蓝牙设备: ${device.platformName}');
return device;
} catch (e) {
Logger.error('查找系统已连接设备失败: $e');
return null;
}
}
/// 清理资源
@override
void onClose() {
// 取消所有订阅
_stateSubscription?.cancel();
super.onClose();
}
}