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.
444 lines
13 KiB
444 lines
13 KiB
import 'dart:async';
|
|
import 'package:flutter/services.dart';
|
|
import 'package:get/get.dart';
|
|
import 'package:permission_handler/permission_handler.dart';
|
|
import 'package:app_settings/app_settings.dart';
|
|
import '../../core/utils/logger.dart';
|
|
|
|
/// 蓝牙设备信息
|
|
class BluetoothDevice {
|
|
final String name;
|
|
final String address;
|
|
final String type;
|
|
|
|
BluetoothDevice({
|
|
required this.name,
|
|
required this.address,
|
|
required this.type
|
|
});
|
|
|
|
@override
|
|
String toString() => 'BluetoothDevice(name: $name, address: $address, type: $type)';
|
|
}
|
|
|
|
/// 蓝牙事件类型
|
|
enum BluetoothEventType {
|
|
/// 设备已连接
|
|
deviceConnected,
|
|
|
|
/// 设备已断开
|
|
deviceDisconnected,
|
|
|
|
/// 蓝牙状态改变(开启/关闭)
|
|
bluetoothStateChanged
|
|
}
|
|
|
|
/// 蓝牙连接状态事件
|
|
class BluetoothConnectionEvent {
|
|
/// 事件类型
|
|
final BluetoothEventType type;
|
|
|
|
/// 相关设备(可能为空,比如蓝牙状态改变事件)
|
|
final BluetoothDevice? device;
|
|
|
|
/// 蓝牙状态(仅适用于bluetoothStateChanged事件)
|
|
final String? state;
|
|
|
|
/// 事件发生时间戳
|
|
final int timestamp;
|
|
|
|
BluetoothConnectionEvent({
|
|
required this.type,
|
|
this.device,
|
|
this.state,
|
|
required this.timestamp,
|
|
});
|
|
|
|
factory BluetoothConnectionEvent.fromMap(Map<dynamic, dynamic> map) {
|
|
BluetoothEventType eventType;
|
|
|
|
switch (map['type']) {
|
|
case 'deviceConnected':
|
|
eventType = BluetoothEventType.deviceConnected;
|
|
break;
|
|
case 'deviceDisconnected':
|
|
eventType = BluetoothEventType.deviceDisconnected;
|
|
break;
|
|
case 'bluetoothStateChanged':
|
|
eventType = BluetoothEventType.bluetoothStateChanged;
|
|
break;
|
|
default:
|
|
throw ArgumentError('未知事件类型: ${map['type']}');
|
|
}
|
|
|
|
final deviceMap = map['device'] as Map<dynamic, dynamic>?;
|
|
final BluetoothDevice? device = deviceMap != null
|
|
? BluetoothDevice(
|
|
name: deviceMap['name'] ?? '未知设备',
|
|
address: deviceMap['address'] ?? '',
|
|
type: deviceMap['type'] ?? 'unknown'
|
|
)
|
|
: null;
|
|
|
|
return BluetoothConnectionEvent(
|
|
type: eventType,
|
|
device: device,
|
|
state: map['state'] as String?,
|
|
timestamp: map['timestamp'] as int? ?? DateTime.now().millisecondsSinceEpoch,
|
|
);
|
|
}
|
|
|
|
@override
|
|
String toString() {
|
|
switch (type) {
|
|
case BluetoothEventType.deviceConnected:
|
|
return '蓝牙设备已连接: ${device?.name}';
|
|
case BluetoothEventType.deviceDisconnected:
|
|
return '蓝牙设备已断开: ${device?.name}';
|
|
case BluetoothEventType.bluetoothStateChanged:
|
|
return '蓝牙状态改变: $state';
|
|
}
|
|
}
|
|
}
|
|
|
|
/// 经典蓝牙服务
|
|
class ClassicBluetoothService extends GetxService {
|
|
static ClassicBluetoothService get to => Get.find<ClassicBluetoothService>();
|
|
|
|
// 注意: MainActivity.kt中定义的通道名称为 com.deep_voice.classic_bluetooth
|
|
final MethodChannel _channel = const MethodChannel('com.deep_voice.classic_bluetooth');
|
|
final EventChannel _eventChannel = const EventChannel('com.deep_voice.classic_bluetooth_events');
|
|
|
|
/// 蓝牙事件流控制器
|
|
final _connectionEventController = StreamController<BluetoothConnectionEvent>.broadcast();
|
|
|
|
/// 蓝牙事件流
|
|
Stream<BluetoothConnectionEvent> get connectionEvents => _connectionEventController.stream;
|
|
|
|
/// 当前连接的设备
|
|
final currentDevice = Rx<BluetoothDevice?>(null);
|
|
|
|
/// 已连接设备列表
|
|
final connectedDevices = <BluetoothDevice>[].obs;
|
|
|
|
/// 蓝牙是否可用
|
|
final _isBluetoothEnabled = true.obs;
|
|
bool get isBluetoothEnabled => _isBluetoothEnabled.value;
|
|
|
|
/// 是否正在加载
|
|
final isLoading = false.obs;
|
|
|
|
/// 错误信息
|
|
final errorMessage = ''.obs;
|
|
|
|
/// 蓝牙状态变化事件流
|
|
StreamSubscription? _bluetoothStateListener;
|
|
|
|
/// 设备连接回调
|
|
final List<Function(BluetoothDevice)> _deviceConnectedCallbacks = [];
|
|
|
|
/// 设备断开回调
|
|
final List<Function(BluetoothDevice)> _deviceDisconnectedCallbacks = [];
|
|
|
|
/// 蓝牙状态变化回调
|
|
final List<Function(String)> _bluetoothStateChangedCallbacks = [];
|
|
|
|
/// 服务是否已初始化
|
|
bool _isInitialized = false;
|
|
|
|
ClassicBluetoothService() {
|
|
// 创建服务时不立即初始化,避免还没有原生支持时就调用方法
|
|
Logger.info('创建 ClassicBluetoothService 实例');
|
|
}
|
|
|
|
/// 初始化蓝牙服务
|
|
/// 必须在使用任何方法前调用此方法确保通道已设置
|
|
Future<bool> initialize() async {
|
|
if (_isInitialized) {
|
|
Logger.info('ClassicBluetoothService 已经初始化');
|
|
return true;
|
|
}
|
|
|
|
try {
|
|
Logger.info('正在初始化 ClassicBluetoothService...');
|
|
|
|
|
|
|
|
// 设置事件监听器
|
|
_setupBluetoothStateListener();
|
|
|
|
_isInitialized = true;
|
|
Logger.info('ClassicBluetoothService 初始化成功');
|
|
return true;
|
|
} catch (e) {
|
|
Logger.error('初始化 ClassicBluetoothService 失败: $e');
|
|
errorMessage.value = '初始化蓝牙服务失败';
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/// 检查并请求蓝牙权限
|
|
Future<bool> checkAndRequestPermissions() async {
|
|
try {
|
|
// 首先确保服务已初始化
|
|
if (!_isInitialized && !(await initialize())) {
|
|
Logger.error('无法初始化蓝牙服务');
|
|
errorMessage.value = '无法初始化蓝牙服务';
|
|
return false;
|
|
}
|
|
|
|
// 检查蓝牙相关权限
|
|
Map<Permission, PermissionStatus> statuses = await [
|
|
Permission.bluetooth,
|
|
Permission.bluetoothConnect,
|
|
// Permission.bluetoothScan,
|
|
// Permission.location,
|
|
Permission.microphone,
|
|
].request();
|
|
|
|
// 检查蓝牙连接权限
|
|
if (statuses[Permission.bluetoothConnect] != PermissionStatus.granted) {
|
|
Logger.error('蓝牙连接权限被拒绝');
|
|
errorMessage.value = '蓝牙连接权限被拒绝';
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
} catch (e) {
|
|
Logger.error('检查蓝牙权限失败: $e');
|
|
errorMessage.value = '检查蓝牙权限失败';
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/// 刷新已连接的蓝牙设备
|
|
Future<bool> refreshConnectedDevices() async {
|
|
try {
|
|
// 首先确保服务已初始化
|
|
if (!_isInitialized && !(await initialize())) {
|
|
Logger.error('无法初始化蓝牙服务');
|
|
errorMessage.value = '无法初始化蓝牙服务';
|
|
return false;
|
|
}
|
|
|
|
errorMessage.value = '';
|
|
|
|
// 获取A2DP设备
|
|
final a2dpDevices = await getConnectedA2dpDevices();
|
|
Logger.info('已连接的A2DP设备: ${a2dpDevices.length}');
|
|
|
|
// 获取耳机设备
|
|
final headsetDevices = await getConnectedHeadsetDevices();
|
|
Logger.info('已连接的耳机设备: ${headsetDevices.length}');
|
|
|
|
// 更新设备列表
|
|
connectedDevices.value = [...a2dpDevices, ...headsetDevices];
|
|
|
|
// 更新当前设备
|
|
if (connectedDevices.isNotEmpty && currentDevice.value == null) {
|
|
currentDevice.value = connectedDevices.first;
|
|
}
|
|
|
|
return connectedDevices.isNotEmpty;
|
|
} catch (e) {
|
|
Logger.error('刷新已连接设备失败: $e');
|
|
errorMessage.value = '获取设备信息失败';
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/// 获取已连接的A2DP设备(音频设备)
|
|
Future<List<BluetoothDevice>> getConnectedA2dpDevices() async {
|
|
try {
|
|
// 确保服务已初始化
|
|
if (!_isInitialized && !(await initialize())) {
|
|
Logger.error('无法初始化蓝牙服务');
|
|
return [];
|
|
}
|
|
|
|
final result = await _channel.invokeMethod('getConnectedA2dpDevices');
|
|
Logger.info('获取已连接的A2DP设备结果: $result');
|
|
|
|
if (result is List) {
|
|
final devices = result.map((device) => BluetoothDevice(
|
|
name: device['name'] ?? '未知设备',
|
|
address: device['address'] ?? '',
|
|
type: 'a2dp'
|
|
)).toList();
|
|
|
|
return devices;
|
|
}
|
|
|
|
return [];
|
|
} catch (e) {
|
|
Logger.error('获取已连接的A2DP设备失败: $e');
|
|
return [];
|
|
}
|
|
}
|
|
|
|
/// 获取已连接的耳机设备
|
|
Future<List<BluetoothDevice>> getConnectedHeadsetDevices() async {
|
|
try {
|
|
// 确保服务已初始化
|
|
if (!_isInitialized && !(await initialize())) {
|
|
Logger.error('无法初始化蓝牙服务');
|
|
return [];
|
|
}
|
|
|
|
final result = await _channel.invokeMethod('getConnectedHeadsetDevices');
|
|
Logger.info('获取已连接的耳机设备结果: $result');
|
|
|
|
if (result is List) {
|
|
final devices = result.map((device) => BluetoothDevice(
|
|
name: device['name'] ?? '未知设备',
|
|
address: device['address'] ?? '',
|
|
type: 'headset'
|
|
)).toList();
|
|
|
|
return devices;
|
|
}
|
|
|
|
return [];
|
|
} catch (e) {
|
|
Logger.error('获取已连接的耳机设备失败: $e');
|
|
return [];
|
|
}
|
|
}
|
|
|
|
/// 设置蓝牙状态监听
|
|
void _setupBluetoothStateListener() {
|
|
try {
|
|
_bluetoothStateListener = _eventChannel
|
|
.receiveBroadcastStream()
|
|
.listen((event) {
|
|
Logger.info('收到蓝牙事件: $event');
|
|
|
|
if (event is Map) {
|
|
final type = event['type'] as String?;
|
|
|
|
if (type == 'stateChanged') {
|
|
final state = event['state'] as String?;
|
|
if (state != null) {
|
|
_handleBluetoothStateChange(state);
|
|
}
|
|
} else if (type == 'deviceConnected') {
|
|
final deviceMap = event['device'] as Map?;
|
|
if (deviceMap != null) {
|
|
final device = BluetoothDevice(
|
|
name: deviceMap['name'] ?? '未知设备',
|
|
address: deviceMap['address'] ?? '',
|
|
type: deviceMap['type'] ?? 'unknown'
|
|
);
|
|
_handleDeviceConnected(device);
|
|
}
|
|
} else if (type == 'deviceDisconnected') {
|
|
final deviceMap = event['device'] as Map?;
|
|
if (deviceMap != null) {
|
|
final device = BluetoothDevice(
|
|
name: deviceMap['name'] ?? '未知设备',
|
|
address: deviceMap['address'] ?? '',
|
|
type: deviceMap['type'] ?? 'unknown'
|
|
);
|
|
_handleDeviceDisconnected(device);
|
|
}
|
|
}
|
|
}
|
|
}, onError: (error) {
|
|
Logger.error('蓝牙状态监听错误: $error');
|
|
});
|
|
} catch (e) {
|
|
Logger.error('设置蓝牙状态监听失败: $e');
|
|
}
|
|
}
|
|
|
|
/// 处理蓝牙状态变化
|
|
void _handleBluetoothStateChange(String state) {
|
|
Logger.info('蓝牙状态变化: $state');
|
|
|
|
// 如果蓝牙关闭,清除设备信息
|
|
if (state == 'off') {
|
|
connectedDevices.clear();
|
|
currentDevice.value = null;
|
|
}
|
|
|
|
// 通知所有回调
|
|
for (var callback in _bluetoothStateChangedCallbacks) {
|
|
callback(state);
|
|
}
|
|
}
|
|
|
|
/// 处理设备连接
|
|
void _handleDeviceConnected(BluetoothDevice device) {
|
|
Logger.info('设备已连接: ${device.name}');
|
|
|
|
// 更新设备列表
|
|
if (!connectedDevices.any((d) => d.address == device.address)) {
|
|
connectedDevices.add(device);
|
|
}
|
|
|
|
// 更新当前设备
|
|
currentDevice.value = device;
|
|
|
|
// 通知所有回调
|
|
for (var callback in _deviceConnectedCallbacks) {
|
|
callback(device);
|
|
}
|
|
}
|
|
|
|
/// 处理设备断开
|
|
void _handleDeviceDisconnected(BluetoothDevice device) {
|
|
Logger.info('设备已断开: ${device.name}');
|
|
|
|
// 更新设备列表
|
|
connectedDevices.removeWhere((d) => d.address == device.address);
|
|
|
|
// 如果是当前设备,清除当前设备
|
|
if (currentDevice.value?.address == device.address) {
|
|
currentDevice.value = connectedDevices.isEmpty ? null : connectedDevices.first;
|
|
}
|
|
|
|
// 通知所有回调
|
|
for (var callback in _deviceDisconnectedCallbacks) {
|
|
callback(device);
|
|
}
|
|
}
|
|
|
|
/// 注册设备连接回调
|
|
void onDeviceConnected(Function(BluetoothDevice) callback) {
|
|
_deviceConnectedCallbacks.add(callback);
|
|
}
|
|
|
|
/// 注册设备断开回调
|
|
void onDeviceDisconnected(Function(BluetoothDevice) callback) {
|
|
_deviceDisconnectedCallbacks.add(callback);
|
|
}
|
|
|
|
/// 注册蓝牙状态变化回调
|
|
void onBluetoothStateChanged(Function(String) callback) {
|
|
_bluetoothStateChangedCallbacks.add(callback);
|
|
}
|
|
|
|
/// 打开蓝牙设置
|
|
Future<bool> openBluetoothSettings() async {
|
|
try {
|
|
await AppSettings.openAppSettings();
|
|
return true;
|
|
} catch (e) {
|
|
Logger.error('打开蓝牙设置失败: $e');
|
|
return false;
|
|
}
|
|
}
|
|
|
|
@override
|
|
void onClose() {
|
|
// 取消蓝牙状态监听
|
|
_bluetoothStateListener?.cancel();
|
|
|
|
// 清除回调
|
|
_deviceConnectedCallbacks.clear();
|
|
_deviceDisconnectedCallbacks.clear();
|
|
_bluetoothStateChangedCallbacks.clear();
|
|
|
|
super.onClose();
|
|
}
|
|
}
|