11 changed files with 1444 additions and 1408 deletions
@ -0,0 +1,242 @@ |
|||
package com.example.deep_voice |
|||
|
|||
import android.bluetooth.BluetoothAdapter |
|||
import android.bluetooth.BluetoothProfile |
|||
import android.content.Context |
|||
import android.content.pm.PackageManager |
|||
import android.os.Build |
|||
import android.util.Log |
|||
import android.Manifest |
|||
import androidx.core.content.ContextCompat |
|||
import java.util.concurrent.atomic.AtomicBoolean |
|||
import java.util.concurrent.CountDownLatch |
|||
import java.util.concurrent.TimeUnit |
|||
|
|||
/** |
|||
* 经典蓝牙辅助类,用于获取已连接的蓝牙设备信息 |
|||
*/ |
|||
class ClassicBluetoothHelper(private val context: Context) { |
|||
|
|||
private val TAG = "ClassicBluetoothHelper" |
|||
|
|||
// 缓存蓝牙代理对象 |
|||
private var a2dpProxy: BluetoothProfile? = null |
|||
private var headsetProxy: BluetoothProfile? = null |
|||
|
|||
/** |
|||
* 获取已连接的A2DP设备(音频输出设备) |
|||
* @param callback 回调接口,返回设备列表或错误信息 |
|||
*/ |
|||
fun getConnectedA2dpDevices(callback: BluetoothDevicesCallback) { |
|||
getConnectedBluetoothDevices(BluetoothProfile.A2DP, callback) |
|||
} |
|||
|
|||
/** |
|||
* 获取已连接的蓝牙耳机设备 |
|||
* @param callback 回调接口,返回设备列表或错误信息 |
|||
*/ |
|||
fun getConnectedHeadsetDevices(callback: BluetoothDevicesCallback) { |
|||
getConnectedBluetoothDevices(BluetoothProfile.HEADSET, callback) |
|||
} |
|||
|
|||
/** |
|||
* 检查是否有蓝牙连接权限 |
|||
* @return 是否有权限 |
|||
*/ |
|||
private fun hasBluetoothConnectPermission(): Boolean { |
|||
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { |
|||
ContextCompat.checkSelfPermission( |
|||
context, |
|||
Manifest.permission.BLUETOOTH_CONNECT |
|||
) == PackageManager.PERMISSION_GRANTED |
|||
} else { |
|||
true |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 检查蓝牙是否启用 |
|||
* @return 蓝牙是否启用 |
|||
*/ |
|||
fun isBluetoothEnabled(): Boolean { |
|||
try { |
|||
val bluetoothAdapter = BluetoothAdapter.getDefaultAdapter() |
|||
if (bluetoothAdapter == null) { |
|||
Log.w(TAG, "设备不支持蓝牙") |
|||
return false |
|||
} |
|||
|
|||
return bluetoothAdapter.isEnabled |
|||
} catch (e: Exception) { |
|||
Log.e(TAG, "检查蓝牙状态失败: ${e.message}") |
|||
return false |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 获取指定配置文件的已连接蓝牙设备 |
|||
* @param profile 蓝牙配置文件类型,如 BluetoothProfile.A2DP 或 BluetoothProfile.HEADSET |
|||
* @param callback 回调接口,返回设备列表或错误信息 |
|||
*/ |
|||
private fun getConnectedBluetoothDevices(profile: Int, callback: BluetoothDevicesCallback) { |
|||
val bluetoothAdapter = BluetoothAdapter.getDefaultAdapter() |
|||
if (bluetoothAdapter == null) { |
|||
Log.e(TAG, "没有找到蓝牙适配器") |
|||
callback.onError("NO_ADAPTER", "没有找到蓝牙适配器") |
|||
return |
|||
} |
|||
|
|||
// 检查蓝牙权限 |
|||
if (!hasBluetoothConnectPermission()) { |
|||
Log.e(TAG, "缺少BLUETOOTH_CONNECT权限") |
|||
callback.onError("PERMISSION_DENIED", "缺少蓝牙连接权限,请在应用设置中授予该权限") |
|||
return |
|||
} |
|||
|
|||
try { |
|||
// 检查是否已经有缓存的代理对象 |
|||
val cachedProxy = when (profile) { |
|||
BluetoothProfile.A2DP -> a2dpProxy |
|||
BluetoothProfile.HEADSET -> headsetProxy |
|||
else -> null |
|||
} |
|||
|
|||
if (cachedProxy != null) { |
|||
// 使用已缓存的代理对象 |
|||
processDeviceList(cachedProxy, profile, callback) |
|||
return |
|||
} |
|||
|
|||
// 使用原子布尔值确保回调只被调用一次 |
|||
val callbackCalled = AtomicBoolean(false) |
|||
|
|||
// 使用CountDownLatch等待服务连接 |
|||
val latch = CountDownLatch(1) |
|||
|
|||
// 通过 getProfileProxy 获取指定 Profile (A2DP/HEADSET) 的连接状态 |
|||
bluetoothAdapter.getProfileProxy(context, |
|||
object : BluetoothProfile.ServiceListener { |
|||
override fun onServiceConnected(profileId: Int, proxy: BluetoothProfile) { |
|||
Log.d(TAG, "蓝牙配置文件服务已连接: $profileId") |
|||
if (profileId == profile) { |
|||
// 缓存代理对象 |
|||
when (profileId) { |
|||
BluetoothProfile.A2DP -> a2dpProxy = proxy |
|||
BluetoothProfile.HEADSET -> headsetProxy = proxy |
|||
} |
|||
|
|||
try { |
|||
processDeviceList(proxy, profileId, callback) |
|||
latch.countDown() |
|||
} catch (e: Exception) { |
|||
Log.e(TAG, "处理设备信息时出错: ${e.message}") |
|||
|
|||
// 确保回调只被调用一次 |
|||
if (callbackCalled.compareAndSet(false, true)) { |
|||
callback.onError("DEVICE_ERROR", "处理设备信息时出错: ${e.message}") |
|||
} |
|||
latch.countDown() |
|||
} |
|||
} |
|||
} |
|||
|
|||
override fun onServiceDisconnected(profileId: Int) { |
|||
// Profile 断开连接 |
|||
Log.e(TAG, "蓝牙配置文件服务断开连接: $profileId") |
|||
|
|||
// 清除对应的缓存代理 |
|||
when (profileId) { |
|||
BluetoothProfile.A2DP -> a2dpProxy = null |
|||
BluetoothProfile.HEADSET -> headsetProxy = null |
|||
} |
|||
|
|||
// 确保回调只被调用一次,但不在这里调用错误回调 |
|||
// 避免服务断开时也返回错误导致重复回调 |
|||
if (callbackCalled.compareAndSet(false, true) && !latch.await(0, TimeUnit.MILLISECONDS)) { |
|||
callback.onError("SERVICE_DISCONNECTED", "蓝牙配置文件服务断开连接") |
|||
latch.countDown() |
|||
} |
|||
} |
|||
}, profile |
|||
) |
|||
|
|||
// 设置超时处理 |
|||
Thread { |
|||
try { |
|||
// 最多等待3秒 |
|||
if (!latch.await(3, TimeUnit.SECONDS)) { |
|||
Log.e(TAG, "获取蓝牙设备超时") |
|||
if (callbackCalled.compareAndSet(false, true)) { |
|||
callback.onError("TIMEOUT", "获取蓝牙设备超时") |
|||
} |
|||
} |
|||
} catch (e: InterruptedException) { |
|||
Log.e(TAG, "等待蓝牙服务被中断: ${e.message}") |
|||
if (callbackCalled.compareAndSet(false, true)) { |
|||
callback.onError("INTERRUPTED", "等待蓝牙服务被中断: ${e.message}") |
|||
} |
|||
} |
|||
}.start() |
|||
|
|||
} catch (e: Exception) { |
|||
Log.e(TAG, "获取蓝牙设备失败: ${e.message}") |
|||
callback.onError("BLUETOOTH_ERROR", "获取蓝牙设备失败: ${e.message}") |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 处理设备列表 |
|||
*/ |
|||
private fun processDeviceList(proxy: BluetoothProfile, profileId: Int, callback: BluetoothDevicesCallback) { |
|||
// 获取已连接设备 |
|||
val connectedDevices = proxy.connectedDevices |
|||
|
|||
// 转换为设备信息列表 |
|||
val devicesList = connectedDevices.map { device -> |
|||
mapOf( |
|||
"name" to (device.name ?: "未知设备"), |
|||
"address" to device.address |
|||
) |
|||
} |
|||
|
|||
Log.d(TAG, "获取到的设备列表:${devicesList.size}个设备") |
|||
|
|||
// 返回设备列表(不关闭代理) |
|||
callback.onSuccess(devicesList) |
|||
} |
|||
|
|||
/** |
|||
* 释放资源 |
|||
*/ |
|||
fun dispose() { |
|||
val bluetoothAdapter = BluetoothAdapter.getDefaultAdapter() ?: return |
|||
|
|||
// 释放A2DP代理 |
|||
a2dpProxy?.let { |
|||
try { |
|||
bluetoothAdapter.closeProfileProxy(BluetoothProfile.A2DP, it) |
|||
} catch (e: Exception) { |
|||
Log.e(TAG, "关闭A2DP代理失败: ${e.message}") |
|||
} |
|||
a2dpProxy = null |
|||
} |
|||
|
|||
// 释放HEADSET代理 |
|||
headsetProxy?.let { |
|||
try { |
|||
bluetoothAdapter.closeProfileProxy(BluetoothProfile.HEADSET, it) |
|||
} catch (e: Exception) { |
|||
Log.e(TAG, "关闭HEADSET代理失败: ${e.message}") |
|||
} |
|||
headsetProxy = null |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 蓝牙设备回调接口 |
|||
*/ |
|||
interface BluetoothDevicesCallback { |
|||
fun onSuccess(devices: List<Map<String, String>>) |
|||
fun onError(code: String, message: String) |
|||
} |
|||
} |
|||
@ -1,436 +0,0 @@ |
|||
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(); |
|||
} |
|||
} |
|||
@ -0,0 +1,452 @@ |
|||
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...'); |
|||
|
|||
// 检查通道是否可用 |
|||
try { |
|||
// 调用一个简单方法检查通道是否已设置 |
|||
await _channel.invokeMethod<bool>('isBluetoothEnabled'); |
|||
Logger.info('蓝牙方法通道可用'); |
|||
} catch (e) { |
|||
Logger.error('蓝牙方法通道不可用: $e'); |
|||
// 这里不要立即返回false,尝试继续设置其他内容 |
|||
} |
|||
|
|||
// 设置事件监听器 |
|||
_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(); |
|||
} |
|||
} |
|||
@ -1,303 +1,185 @@ |
|||
import 'package:get/get.dart'; |
|||
import 'dart:async'; |
|||
import '../../../routes/app_routes.dart'; |
|||
import '../../../data/services/bluetooth_service.dart'; |
|||
import 'package:flutter_blue_plus/flutter_blue_plus.dart' hide BluetoothService; |
|||
import '../../../data/services/classic_bluetooth_service.dart' as classic; |
|||
import '../../../core/utils/logger.dart'; |
|||
import 'package:permission_handler/permission_handler.dart'; |
|||
import 'package:flutter/material.dart'; |
|||
import 'package:permission_handler/permission_handler.dart'; |
|||
|
|||
/// 简化的配对控制器,仅用于显示已连接或配对的蓝牙耳机信息 |
|||
/// 简化的配对控制器,仅用于显示已连接经典蓝牙设备和处理基本配对流程 |
|||
class PairingController extends GetxController { |
|||
// 蓝牙服务 |
|||
late BluetoothService _bluetoothService; |
|||
static PairingController get to => Get.find(); |
|||
|
|||
// 设备信息 |
|||
final _deviceInfo = Rx<Map<String, dynamic>>({}); |
|||
Map<String, dynamic> get deviceInfo => _deviceInfo.value; |
|||
// 经典蓝牙服务 |
|||
late classic.ClassicBluetoothService _bluetoothService; |
|||
|
|||
// 蓝牙状态 |
|||
final _isBluetoothAvailable = false.obs; |
|||
final _isBluetoothOn = false.obs; |
|||
bool get isBluetoothAvailable => _isBluetoothAvailable.value; |
|||
bool get isBluetoothOn => _isBluetoothOn.value; |
|||
// 是否开始搜索 |
|||
final _isSearching = false.obs; |
|||
bool get isSearching => _isSearching.value; |
|||
|
|||
// 错误消息 |
|||
final _errorMessage = ''.obs; |
|||
String get errorMessage => _errorMessage.value; |
|||
// 设备连接状态 |
|||
final _isConnecting = false.obs; |
|||
bool get isConnecting => _isConnecting.value; |
|||
|
|||
// 已连接的设备列表 |
|||
final _connectedDevices = <BluetoothDevice>[].obs; |
|||
List<BluetoothDevice> get connectedDevices => _connectedDevices; |
|||
// 蓝牙服务是否可用 |
|||
final _isBluetoothServiceAvailable = true.obs; |
|||
bool get isBluetoothServiceAvailable => _isBluetoothServiceAvailable.value; |
|||
|
|||
// 已配对的设备列表 |
|||
final _pairedDevices = <BluetoothDevice>[].obs; |
|||
List<BluetoothDevice> get pairedDevices => _pairedDevices; |
|||
// 当前连接设备 |
|||
Rx<classic.BluetoothDevice?> get currentDevice => _bluetoothService.currentDevice; |
|||
|
|||
// 状态描述(用于UI显示) |
|||
final _statusDescription = ''.obs; |
|||
String get statusDescription => _statusDescription.value; |
|||
// 连接设备列表 |
|||
RxList<classic.BluetoothDevice> get connectedDevices => _bluetoothService.connectedDevices; |
|||
|
|||
// 防止重复刷新的标志 |
|||
bool _isRefreshing = false; |
|||
// 错误信息 |
|||
RxString get errorMessage => _bluetoothService.errorMessage; |
|||
|
|||
// 上次刷新时间 |
|||
DateTime? _lastRefreshTime; |
|||
// 连接设备列表(兼容旧代码) |
|||
List<classic.BluetoothDevice> get connectedClassicDevices => |
|||
_bluetoothService.currentDevice.value != null |
|||
? [_bluetoothService.currentDevice.value!] |
|||
: []; |
|||
|
|||
// 状态描述(用于UI显示) |
|||
final statusDescription = '正在初始化...'.obs; |
|||
|
|||
// 加载状态 |
|||
final isLoading = false.obs; |
|||
|
|||
@override |
|||
void onInit() { |
|||
super.onInit(); |
|||
_initializeProcess(); |
|||
_initialize(); |
|||
} |
|||
|
|||
// 初始化流程 |
|||
Future<void> _initializeProcess() async { |
|||
_updateStatus('正在初始化...'); |
|||
|
|||
try { |
|||
// 获取或初始化蓝牙服务 |
|||
await _getBluetoothService(); |
|||
|
|||
// 检查权限并获取设备信息 |
|||
await _checkAndRequestPermissions(); |
|||
} catch (e) { |
|||
Logger.error('初始化过程失败: $e'); |
|||
_updateStatus('初始化失败'); |
|||
_setError('初始化蓝牙服务失败,请重试'); |
|||
} |
|||
@override |
|||
void onReady() { |
|||
super.onReady(); |
|||
// 设置连接监听器,确保在页面完全加载后设置,避免过早监听可能导致的问题 |
|||
_setupDeviceListeners(); |
|||
} |
|||
|
|||
// 获取蓝牙服务 |
|||
Future<void> _getBluetoothService() async { |
|||
/// 设置设备监听器 |
|||
void _setupDeviceListeners() { |
|||
try { |
|||
// 检查是否已注册 |
|||
if (Get.isRegistered<BluetoothService>()) { |
|||
_bluetoothService = Get.find<BluetoothService>(); |
|||
} else { |
|||
// 初始化并注册服务 |
|||
_bluetoothService = await Get.put(BluetoothService()).init(); |
|||
} |
|||
if (!_isBluetoothServiceAvailable.value) return; |
|||
|
|||
// 设置蓝牙状态监听 |
|||
_setupBluetoothListeners(); |
|||
// 监听设备连接事件 |
|||
_bluetoothService.onDeviceConnected((device) { |
|||
Logger.info('检测到蓝牙设备连接: ${device.name}'); |
|||
|
|||
// 获取设备信息 |
|||
_refreshDeviceInfo(); |
|||
} catch (e) { |
|||
Logger.error('获取蓝牙服务失败: $e'); |
|||
throw Exception('获取蓝牙服务失败: $e'); |
|||
} |
|||
} |
|||
|
|||
// 设置蓝牙状态监听 |
|||
void _setupBluetoothListeners() { |
|||
// 监听蓝牙可用性 |
|||
_isBluetoothAvailable.value = _bluetoothService.isAvailable; |
|||
ever(_bluetoothService.isAvailableObs, (bool available) { |
|||
_isBluetoothAvailable.value = available; |
|||
|
|||
if (!available) { |
|||
_updateStatus('蓝牙不可用'); |
|||
_setError('蓝牙不可用,请检查设备是否支持蓝牙功能'); |
|||
} |
|||
}); |
|||
// 更新状态并跳转到主页 |
|||
statusDescription.value = '已连接到设备: ${device.name}'; |
|||
|
|||
// 监听蓝牙开关状态 |
|||
_isBluetoothOn.value = _bluetoothService.isOn; |
|||
ever(_bluetoothService.isOnObs, (bool on) { |
|||
_isBluetoothOn.value = on; |
|||
|
|||
if (on) { |
|||
// 蓝牙开启时,延迟一下再刷新设备信息,避免频繁刷新 |
|||
Future.delayed(const Duration(milliseconds: 500), () { |
|||
_refreshDeviceInfo(); |
|||
// 立即跳转到主页 |
|||
_navigateToHome(); |
|||
}); |
|||
} else { |
|||
_updateStatus('蓝牙已关闭'); |
|||
_setError('蓝牙已关闭,请开启蓝牙后再试'); |
|||
} catch (e) { |
|||
Logger.error('设置设备监听器失败: $e'); |
|||
} |
|||
}); |
|||
|
|||
// 监听已连接设备变化 |
|||
_connectedDevices.value = _bluetoothService.connectedDevices; |
|||
ever(_bluetoothService.connectedDevicesObs, (List<BluetoothDevice> devices) { |
|||
_connectedDevices.value = devices; |
|||
|
|||
// 只有当设备列表不为空时才刷新设备信息 |
|||
if (devices.isNotEmpty) { |
|||
// 延迟一下再刷新,避免频繁刷新 |
|||
Future.delayed(const Duration(milliseconds: 500), () { |
|||
_refreshDeviceInfo(); |
|||
}); |
|||
} |
|||
}); |
|||
|
|||
// 监听已配对设备变化 |
|||
_pairedDevices.value = _bluetoothService.systemPairedDevices; |
|||
/// 立即跳转到主页 |
|||
void _navigateToHome() { |
|||
Get.offAllNamed(Routes.home); |
|||
} |
|||
|
|||
// 检查并请求权限 |
|||
Future<void> _checkAndRequestPermissions() async { |
|||
_updateStatus('正在请求权限...'); |
|||
|
|||
/// 完整的初始化流程 |
|||
Future<void> _initialize() async { |
|||
try { |
|||
bool hasPermissions = await _bluetoothService.requestPermissions(); |
|||
|
|||
if (!hasPermissions) { |
|||
// 权限被拒绝 |
|||
_handlePermissionDenied(); |
|||
isLoading.value = true; |
|||
statusDescription.value = '正在初始化...'; |
|||
|
|||
// 1. 获取蓝牙服务 |
|||
_bluetoothService = Get.find<classic.ClassicBluetoothService>(); |
|||
|
|||
// 确保蓝牙服务已初始化 |
|||
if (!await _bluetoothService.initialize()) { |
|||
Logger.error('蓝牙服务初始化失败'); |
|||
statusDescription.value = '蓝牙服务初始化失败'; |
|||
_isBluetoothServiceAvailable.value = false; |
|||
isLoading.value = false; |
|||
return; |
|||
} |
|||
|
|||
// 权限已授予,检查蓝牙状态 |
|||
if (!_bluetoothService.isOn) { |
|||
_updateStatus('蓝牙已关闭'); |
|||
_setError('蓝牙已关闭,请开启蓝牙后再试'); |
|||
// 2. 检查权限 |
|||
statusDescription.value = '正在检查权限...'; |
|||
final hasPermission = await _bluetoothService.checkAndRequestPermissions(); |
|||
if (!hasPermission) { |
|||
statusDescription.value = '缺少蓝牙权限'; |
|||
isLoading.value = false; |
|||
return; |
|||
} |
|||
|
|||
// 刷新设备信息 |
|||
_refreshDeviceInfo(); |
|||
} catch (e) { |
|||
Logger.error('权限请求失败: $e'); |
|||
_updateStatus('权限请求失败'); |
|||
_setError('权限请求失败,请手动授予蓝牙和位置权限'); |
|||
} |
|||
} |
|||
|
|||
// 处理权限被拒绝的情况 |
|||
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; |
|||
} |
|||
// 3. 刷新设备列表 |
|||
statusDescription.value = '正在搜索设备...'; |
|||
await refreshConnectedDevices(); |
|||
|
|||
_updateStatus('缺少必要权限'); |
|||
|
|||
if (isPermanentlyDenied) { |
|||
_setError('缺少必要权限,您已永久拒绝权限,请前往系统设置中手动开启'); |
|||
// 显示前往设置的提示 |
|||
_showGoToSettingsDialog(); |
|||
// 4. 处理结果 |
|||
if (_bluetoothService.connectedDevices.isNotEmpty) { |
|||
statusDescription.value = '找到已连接的设备'; |
|||
// 延迟跳转到主页 |
|||
_autoNavigateToHome(); |
|||
} else { |
|||
_setError('缺少必要权限,请授予蓝牙和位置权限以继续'); |
|||
} |
|||
// 没有找到设备 |
|||
statusDescription.value = '未找到已连接的蓝牙设备'; |
|||
} |
|||
|
|||
// 显示前往设置的对话框 |
|||
void _showGoToSettingsDialog() { |
|||
Get.dialog( |
|||
AlertDialog( |
|||
title: const Text('需要权限'), |
|||
content: const Text('蓝牙搜索需要特定权限,请前往系统设置开启'), |
|||
actions: [ |
|||
TextButton( |
|||
onPressed: () => Get.back(), |
|||
child: const Text('稍后'), |
|||
), |
|||
TextButton( |
|||
onPressed: () { |
|||
Get.back(); |
|||
openAppSettings(); |
|||
}, |
|||
child: const Text('前往设置'), |
|||
), |
|||
], |
|||
), |
|||
barrierDismissible: false, |
|||
); |
|||
} catch (e) { |
|||
Logger.error('初始化配对控制器失败: $e'); |
|||
statusDescription.value = '初始化失败'; |
|||
_isBluetoothServiceAvailable.value = false; |
|||
} finally { |
|||
isLoading.value = false; |
|||
} |
|||
|
|||
// 刷新设备信息 |
|||
Future<void> _refreshDeviceInfo() async { |
|||
// 防止重复刷新 |
|||
if (_isRefreshing) return; |
|||
|
|||
_isRefreshing = true; |
|||
|
|||
if (!_isBluetoothOn.value) { |
|||
_updateStatus('蓝牙已关闭'); |
|||
_deviceInfo.value = {}; |
|||
_isRefreshing = false; |
|||
return; |
|||
} |
|||
|
|||
/// 刷新设备 |
|||
Future<void> refreshConnectedDevices() async { |
|||
try { |
|||
// 更新已配对设备列表 |
|||
_pairedDevices.value = _bluetoothService.systemPairedDevices; |
|||
|
|||
// 查找系统已连接设备 |
|||
BluetoothDevice? device = await _bluetoothService.findConnectedSystemDevice(); |
|||
|
|||
if (device != null) { |
|||
// 已连接设备 |
|||
_deviceInfo.value = _bluetoothService.getConnectedDeviceInfo(); |
|||
_updateStatus('已连接到蓝牙设备'); |
|||
_goToHomePage(); |
|||
} else if (_bluetoothService.lastConnectedDeviceInfo.isNotEmpty) { |
|||
// 使用上次连接的设备信息 |
|||
_deviceInfo.value = _bluetoothService.lastConnectedDeviceInfo; |
|||
_updateStatus('使用上次连接的设备信息'); |
|||
_goToHomePage(); |
|||
} else if (_pairedDevices.isNotEmpty) { |
|||
// 使用已配对设备 |
|||
BluetoothDevice pairedDevice = _pairedDevices.first; |
|||
_deviceInfo.value = _bluetoothService.getPairedDeviceInfo(pairedDevice); |
|||
_bluetoothService.setSelectedDevice(pairedDevice); |
|||
_updateStatus('已找到配对设备'); |
|||
_goToHomePage(); |
|||
isLoading.value = true; |
|||
statusDescription.value = '正在搜索设备...'; |
|||
|
|||
final hasDevices = await _bluetoothService.refreshConnectedDevices(); |
|||
|
|||
if (hasDevices) { |
|||
statusDescription.value = '找到已连接的设备'; |
|||
// 延迟跳转到主页 |
|||
_autoNavigateToHome(); |
|||
} else { |
|||
// 没有找到任何设备 |
|||
_deviceInfo.value = {}; |
|||
_updateStatus('未找到已连接或配对的蓝牙设备'); |
|||
// 没有找到设备 |
|||
statusDescription.value = '未找到已连接的蓝牙设备'; |
|||
} |
|||
} catch (e) { |
|||
Logger.error('刷新设备信息失败: $e'); |
|||
_updateStatus('获取设备信息失败'); |
|||
Logger.error('刷新设备失败: $e'); |
|||
statusDescription.value = '刷新设备失败'; |
|||
} finally { |
|||
_isRefreshing = false; |
|||
isLoading.value = false; |
|||
} |
|||
} |
|||
|
|||
// 更新状态描述 |
|||
void _updateStatus(String description) { |
|||
_statusDescription.value = description; |
|||
Logger.info('状态更新: $description'); |
|||
} |
|||
/// 自动跳转到主页 |
|||
void _autoNavigateToHome() { |
|||
if (_bluetoothService.connectedDevices.isEmpty) return; |
|||
|
|||
// 设置错误信息 |
|||
void _setError(String message) { |
|||
_errorMessage.value = message; |
|||
Logger.warning('错误: $message'); |
|||
} |
|||
Logger.info('已找到连接的设备,自动跳转到主页'); |
|||
|
|||
// 跳转到主页 |
|||
void _goToHomePage() { |
|||
Get.offAllNamed(Routes.home, arguments: _deviceInfo.value); |
|||
Future.delayed(const Duration(milliseconds: 500), () { |
|||
Get.offAllNamed(Routes.home); |
|||
}); |
|||
} |
|||
|
|||
// 刷新搜索 |
|||
void refreshSearch() { |
|||
// 防止频繁刷新,限制最小刷新间隔为2秒 |
|||
final now = DateTime.now(); |
|||
if (_lastRefreshTime != null && |
|||
now.difference(_lastRefreshTime!).inSeconds < 2) { |
|||
return; |
|||
/// 打开系统蓝牙设置 |
|||
void openBluetoothSettings() { |
|||
_bluetoothService.openBluetoothSettings(); |
|||
} |
|||
|
|||
_lastRefreshTime = now; |
|||
_refreshDeviceInfo(); |
|||
/// 刷新设备(兼容旧代码) |
|||
Future<void> refreshDevices() async { |
|||
await refreshConnectedDevices(); |
|||
} |
|||
|
|||
// 跳过连接 |
|||
/// 跳过连接 |
|||
void skipConnecting() { |
|||
Get.offAllNamed(Routes.home); |
|||
} |
|||
|
|||
Loading…
Reference in new issue