|
|
@ -3,17 +3,14 @@ import 'package:get/get.dart'; |
|
|
import 'dart:async'; |
|
|
import 'dart:async'; |
|
|
import 'package:permission_handler/permission_handler.dart'; |
|
|
import 'package:permission_handler/permission_handler.dart'; |
|
|
import '../../../routes/app_routes.dart'; |
|
|
import '../../../routes/app_routes.dart'; |
|
|
import '../../../data/services/classic_bluetooth_service.dart'; |
|
|
|
|
|
import '../../../core/utils/logger.dart'; |
|
|
import '../../../core/utils/logger.dart'; |
|
|
import 'dart:io'; |
|
|
import 'dart:io'; |
|
|
|
|
|
import 'package:classic_bluetooth/classic_bluetooth.dart'; |
|
|
|
|
|
|
|
|
/// 配对控制器,负责显示已连接经典蓝牙设备和处理基本配对流程 |
|
|
/// 配对控制器,负责检查蓝牙权限、状态,并处理配对流程 |
|
|
class PairingController extends GetxController { |
|
|
class PairingController extends GetxController { |
|
|
static PairingController get to => Get.find(); |
|
|
static PairingController get to => Get.find(); |
|
|
|
|
|
|
|
|
// 经典蓝牙服务 |
|
|
|
|
|
final ClassicBluetoothService _bluetoothService = Get.find<ClassicBluetoothService>(); |
|
|
|
|
|
|
|
|
|
|
|
// 状态标志 |
|
|
// 状态标志 |
|
|
final isLoading = false.obs; |
|
|
final isLoading = false.obs; |
|
|
|
|
|
|
|
|
@ -21,20 +18,22 @@ class PairingController extends GetxController { |
|
|
final _hasPermissions = false.obs; |
|
|
final _hasPermissions = false.obs; |
|
|
bool get hasPermissions => _hasPermissions.value; |
|
|
bool get hasPermissions => _hasPermissions.value; |
|
|
|
|
|
|
|
|
// 蓝牙服务是否可用 |
|
|
// 蓝牙状态 |
|
|
final _isBluetoothServiceAvailable = true.obs; |
|
|
final _bluetoothState = 'unknown'.obs; |
|
|
bool get isBluetoothServiceAvailable => _isBluetoothServiceAvailable.value; |
|
|
String get bluetoothState => _bluetoothState.value; |
|
|
|
|
|
|
|
|
// 状态描述(用于UI显示) |
|
|
// 状态描述(用于UI显示) |
|
|
final statusDescription = 'initializing'.tr.obs; |
|
|
final statusDescription = 'initializing'.tr.obs; |
|
|
|
|
|
|
|
|
// 是否正在跳转到主页 |
|
|
// 是否正在跳转到主页 |
|
|
bool _isNavigatingToHome = false; |
|
|
bool _isNavigatingToHome = false; |
|
|
|
|
|
|
|
|
// 当前连接设备和设备列表(直接从服务获取) |
|
|
// 当前连接设备 |
|
|
Rx<BluetoothDevice?> get currentDevice => _bluetoothService.currentDevice; |
|
|
final Rx<BluetoothDevice?> _currentDevice = Rx<BluetoothDevice?>(null); |
|
|
RxList<BluetoothDevice> get connectedDevices => _bluetoothService.connectedDevices; |
|
|
BluetoothDevice? get currentDevice => _currentDevice.value; |
|
|
RxString get bluetoothState => _bluetoothService.bluetoothState; |
|
|
|
|
|
|
|
|
// 蓝牙事件订阅 |
|
|
|
|
|
StreamSubscription<BluetoothEvent>? _bluetoothSubscription; |
|
|
|
|
|
|
|
|
@override |
|
|
@override |
|
|
void onInit() { |
|
|
void onInit() { |
|
|
@ -42,6 +41,12 @@ class PairingController extends GetxController { |
|
|
_initialize(); |
|
|
_initialize(); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@override |
|
|
|
|
|
void onClose() { |
|
|
|
|
|
_bluetoothSubscription?.cancel(); |
|
|
|
|
|
super.onClose(); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
/// 初始化流程 |
|
|
/// 初始化流程 |
|
|
Future<void> _initialize() async { |
|
|
Future<void> _initialize() async { |
|
|
try { |
|
|
try { |
|
|
@ -54,31 +59,28 @@ class PairingController extends GetxController { |
|
|
return; |
|
|
return; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
statusDescription.value = 'initializing'.tr; |
|
|
// 监听蓝牙状态变化 |
|
|
|
|
|
_setupBluetoothListener(); |
|
|
|
|
|
|
|
|
|
|
|
// 检查蓝牙状态 |
|
|
|
|
|
statusDescription.value = 'checkingBluetoothStatus'.tr; |
|
|
|
|
|
final isEnabled = await ClassicBluetooth.isBluetoothEnabled(); |
|
|
|
|
|
_bluetoothState.value = isEnabled ? 'on' : 'off'; |
|
|
|
|
|
|
|
|
// 初始化蓝牙服务 |
|
|
if (!isEnabled) { |
|
|
if (!await _bluetoothService.initialize()) { |
|
|
statusDescription.value = 'bluetoothTurnedOff'.tr; |
|
|
Logger.error('蓝牙服务初始化失败'); |
|
|
|
|
|
statusDescription.value = 'bluetoothInitializationFailure'.tr; |
|
|
|
|
|
_isBluetoothServiceAvailable.value = false; |
|
|
|
|
|
return; |
|
|
return; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// 设置状态监听 |
|
|
|
|
|
_setupStatusListeners(); |
|
|
|
|
|
|
|
|
|
|
|
// 刷新已连接设备 |
|
|
// 刷新已连接设备 |
|
|
statusDescription.value = 'searchingDevice'.tr; |
|
|
statusDescription.value = 'searchingDevice'.tr; |
|
|
await refreshConnectedDevices(); |
|
|
await refreshConnectedDevices(); |
|
|
} catch (e) { |
|
|
} catch (e) { |
|
|
Logger.error('初始化配对控制器失败: $e'); |
|
|
Logger.error('初始化配对控制器失败: $e'); |
|
|
statusDescription.value = 'initializationFailure'.tr; |
|
|
statusDescription.value = 'initializationFailure'.tr; |
|
|
_isBluetoothServiceAvailable.value = false; |
|
|
|
|
|
} finally { |
|
|
} finally { |
|
|
isLoading.value = false; |
|
|
isLoading.value = false; |
|
|
if (hasPermissions) { |
|
|
_updateStatusDescription(); |
|
|
_updateStatusDescription(); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
@ -91,11 +93,11 @@ class PairingController extends GetxController { |
|
|
// Android 12+ (SDK 31+) 需要 SCAN 和 CONNECT |
|
|
// Android 12+ (SDK 31+) 需要 SCAN 和 CONNECT |
|
|
// Android 6+ (SDK 23+) 需要 LOCATION |
|
|
// Android 6+ (SDK 23+) 需要 LOCATION |
|
|
permissionsToRequest.add(Permission.locationWhenInUse); // 基本定位权限 |
|
|
permissionsToRequest.add(Permission.locationWhenInUse); // 基本定位权限 |
|
|
// Android 12及以上版本才需要请求蓝牙扫描和连接权限 |
|
|
// Android 12及以上版本才需要请求蓝牙扫描和连接权限 |
|
|
if (await _getAndroidSdkVersion() >= 31) { |
|
|
if (await _getAndroidSdkVersion() >= 31) { |
|
|
permissionsToRequest.add(Permission.bluetoothScan); |
|
|
permissionsToRequest.add(Permission.bluetoothScan); |
|
|
permissionsToRequest.add(Permission.bluetoothConnect); |
|
|
permissionsToRequest.add(Permission.bluetoothConnect); |
|
|
} |
|
|
} |
|
|
} else if (Platform.isIOS) { |
|
|
} else if (Platform.isIOS) { |
|
|
permissionsToRequest.add(Permission.bluetooth); |
|
|
permissionsToRequest.add(Permission.bluetooth); |
|
|
} |
|
|
} |
|
|
@ -119,50 +121,71 @@ class PairingController extends GetxController { |
|
|
if (Platform.isAndroid) { |
|
|
if (Platform.isAndroid) { |
|
|
// 这里需要原生代码支持来获取确切的SDK版本, |
|
|
// 这里需要原生代码支持来获取确切的SDK版本, |
|
|
// 或者使用 device_info_plus 插件 |
|
|
// 或者使用 device_info_plus 插件 |
|
|
// 暂时返回一个较高值以触发新权限请求,如果需要精确控制则需要引入插件 |
|
|
// 暂时返回一个较高值以触发新权限请求 |
|
|
// 或者,如果你的minSdkVersion >= 31,可以直接请求新权限 |
|
|
|
|
|
// 假设minSdkVersion < 31,我们需要一种方式判断当前运行设备的SDK版本 |
|
|
|
|
|
// 为了简化,我们这里先假设需要请求新权限,如果你的目标SDK较低,需要调整 |
|
|
|
|
|
return 31; // 假设运行在需要新权限的设备上 |
|
|
return 31; // 假设运行在需要新权限的设备上 |
|
|
} |
|
|
} |
|
|
return 0; |
|
|
return 0; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/// 设置状态变化监听 |
|
|
/// 设置蓝牙状态监听 |
|
|
void _setupStatusListeners() { |
|
|
void _setupBluetoothListener() { |
|
|
// 监听蓝牙状态变化 |
|
|
_bluetoothSubscription?.cancel(); |
|
|
ever(bluetoothState, (_) => _updateStatusDescription()); |
|
|
|
|
|
|
|
|
try { |
|
|
// 监听当前设备变化 |
|
|
_bluetoothSubscription = ClassicBluetooth.events.listen( |
|
|
ever(currentDevice, (device) { |
|
|
(event) { |
|
|
_updateStatusDescription(); |
|
|
if (event.type == 'stateChanged' && event.state != null) { |
|
|
|
|
|
_bluetoothState.value = event.state!; |
|
|
// 当设备连接时自动跳转 |
|
|
_updateStatusDescription(); |
|
|
if (device != null) { |
|
|
|
|
|
_autoNavigateToHome(); |
|
|
// 如果蓝牙关闭,清除设备 |
|
|
} |
|
|
if (event.state == 'off') { |
|
|
}); |
|
|
_currentDevice.value = null; |
|
|
|
|
|
} |
|
|
|
|
|
} else if (event.type == 'deviceConnected' && event.device != null) { |
|
|
|
|
|
// 直接从事件中获取设备信息 |
|
|
|
|
|
final newDevice = event.device!; |
|
|
|
|
|
|
|
|
|
|
|
// 更新当前设备 |
|
|
|
|
|
_currentDevice.value = newDevice; |
|
|
|
|
|
_updateStatusDescription(); |
|
|
|
|
|
|
|
|
|
|
|
// 设备连接时自动跳转到主页 |
|
|
|
|
|
_autoNavigateToHome(); |
|
|
|
|
|
} else if (event.type == 'deviceDisconnected' && event.device != null) { |
|
|
|
|
|
// 直接从事件中获取设备信息 |
|
|
|
|
|
final disconnectedDevice = event.device!; |
|
|
|
|
|
|
|
|
|
|
|
// 如果断开的是当前设备,清除当前设备 |
|
|
|
|
|
if (_currentDevice.value?.address == disconnectedDevice.address) { |
|
|
|
|
|
_currentDevice.value = null; |
|
|
|
|
|
} |
|
|
|
|
|
_updateStatusDescription(); |
|
|
|
|
|
} |
|
|
|
|
|
}, |
|
|
|
|
|
onError: (error) { |
|
|
|
|
|
Logger.error('蓝牙事件流错误: $error'); |
|
|
|
|
|
}, |
|
|
|
|
|
); |
|
|
|
|
|
} catch (e) { |
|
|
|
|
|
Logger.error('设置蓝牙事件监听器失败: $e'); |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/// 更新状态描述 |
|
|
/// 更新状态描述 |
|
|
void _updateStatusDescription() { |
|
|
void _updateStatusDescription() { |
|
|
if (!hasPermissions) { |
|
|
if (!hasPermissions) { |
|
|
statusDescription.value = 'permissionDenied'.tr; |
|
|
statusDescription.value = 'permissionDenied'.tr; |
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (bluetoothState.value == 'off') { |
|
|
|
|
|
statusDescription.value = 'bluetoothTurnedOff'.tr; |
|
|
|
|
|
return; |
|
|
return; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
if (currentDevice.value != null) { |
|
|
if (bluetoothState == 'off') { |
|
|
statusDescription.value = '${'connectedDevice'.tr} ${currentDevice.value!.name}'; |
|
|
statusDescription.value = 'bluetoothTurnedOff'.tr; |
|
|
return; |
|
|
return; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
if (connectedDevices.isNotEmpty) { |
|
|
if (currentDevice != null) { |
|
|
statusDescription.value = '${'findConnectedDevice'.tr} ${connectedDevices.first.name}'; |
|
|
statusDescription.value = '${'connectedDevice'.tr} ${currentDevice!.name}'; |
|
|
return; |
|
|
return; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
@ -176,9 +199,7 @@ class PairingController extends GetxController { |
|
|
|
|
|
|
|
|
/// 刷新已连接设备 |
|
|
/// 刷新已连接设备 |
|
|
Future<void> refreshConnectedDevices() async { |
|
|
Future<void> refreshConnectedDevices() async { |
|
|
if (!_isBluetoothServiceAvailable.value) return; |
|
|
if (!hasPermissions) { |
|
|
|
|
|
|
|
|
if (!await _checkAndRequestPermissions()) { |
|
|
|
|
|
statusDescription.value = 'permissionDenied'.tr; |
|
|
statusDescription.value = 'permissionDenied'.tr; |
|
|
return; |
|
|
return; |
|
|
} |
|
|
} |
|
|
@ -187,24 +208,31 @@ class PairingController extends GetxController { |
|
|
isLoading.value = true; |
|
|
isLoading.value = true; |
|
|
statusDescription.value = 'searchingDevice'.tr; |
|
|
statusDescription.value = 'searchingDevice'.tr; |
|
|
|
|
|
|
|
|
await _bluetoothService.refreshConnectedDevices(); |
|
|
// 获取已连接的蓝牙设备 |
|
|
|
|
|
final devices = await ClassicBluetooth.getConnectedDevices(); |
|
|
|
|
|
|
|
|
|
|
|
// 如果有设备则设置为当前设备 |
|
|
|
|
|
if (devices.isNotEmpty) { |
|
|
|
|
|
_currentDevice.value = devices.first; |
|
|
|
|
|
_autoNavigateToHome(); |
|
|
|
|
|
} else { |
|
|
|
|
|
_currentDevice.value = null; |
|
|
|
|
|
} |
|
|
} catch (e) { |
|
|
} catch (e) { |
|
|
Logger.error('刷新设备失败: $e'); |
|
|
Logger.error('刷新设备失败: $e'); |
|
|
statusDescription.value = 'failedRefreshDevice'.tr; |
|
|
statusDescription.value = 'failedRefreshDevice'.tr; |
|
|
} finally { |
|
|
} finally { |
|
|
isLoading.value = false; |
|
|
isLoading.value = false; |
|
|
if (hasPermissions) { |
|
|
_updateStatusDescription(); |
|
|
_updateStatusDescription(); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/// 自动跳转到主页 |
|
|
/// 自动跳转到主页 |
|
|
void _autoNavigateToHome() { |
|
|
void _autoNavigateToHome() { |
|
|
if (_isNavigatingToHome || currentDevice.value == null) return; |
|
|
if (_isNavigatingToHome || currentDevice == null) return; |
|
|
|
|
|
|
|
|
_isNavigatingToHome = true; |
|
|
_isNavigatingToHome = true; |
|
|
Logger.info('已找到连接的设备 ${currentDevice.value?.name},自动跳转到主页'); |
|
|
Logger.info('已找到连接的设备 ${currentDevice?.name},自动跳转到主页'); |
|
|
|
|
|
|
|
|
Future.delayed(const Duration(milliseconds: 500), () { |
|
|
Future.delayed(const Duration(milliseconds: 500), () { |
|
|
Get.offAllNamed(Routes.home); |
|
|
Get.offAllNamed(Routes.home); |
|
|
@ -214,24 +242,24 @@ class PairingController extends GetxController { |
|
|
/// 打开系统蓝牙设置 |
|
|
/// 打开系统蓝牙设置 |
|
|
void openBluetoothSettings() { |
|
|
void openBluetoothSettings() { |
|
|
try { |
|
|
try { |
|
|
if (!hasPermissions) { |
|
|
if (!hasPermissions) { |
|
|
openAppSettings(); |
|
|
openAppSettings(); |
|
|
} else if (Platform.isAndroid && bluetoothState.value == 'off') { |
|
|
} else if (Platform.isAndroid && bluetoothState == 'off') { |
|
|
AppSettings.openAppSettings(type: AppSettingsType.bluetooth); |
|
|
AppSettings.openAppSettings(type: AppSettingsType.bluetooth); |
|
|
} else if (Platform.isIOS) { |
|
|
} else if (Platform.isIOS) { |
|
|
openAppSettings(); |
|
|
openAppSettings(); |
|
|
} else { |
|
|
} else { |
|
|
AppSettings.openAppSettings(type: AppSettingsType.bluetooth); |
|
|
AppSettings.openAppSettings(type: AppSettingsType.bluetooth); |
|
|
} |
|
|
} |
|
|
} catch (e) { |
|
|
} catch (e) { |
|
|
Logger.error('打开设置失败: $e'); |
|
|
Logger.error('打开设置失败: $e'); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/// 打开应用设置 |
|
|
/// 打开应用设置 |
|
|
Future<void> openAppSettings() async { |
|
|
Future<void> openAppSettings() async { |
|
|
await AppSettings.openAppSettings(); |
|
|
await AppSettings.openAppSettings(); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/// 跳过连接 |
|
|
/// 跳过连接 |
|
|
void skipConnecting() { |
|
|
void skipConnecting() { |
|
|
|