From a77e1c1c6779be227f7b8a6fb8435bd5d8cc6f31 Mon Sep 17 00:00:00 2001 From: wolfplus Date: Wed, 18 Jun 2025 12:57:21 +0100 Subject: [PATCH] add --- lib/data/services/bluetooth_manager.dart | 1 + .../home/controllers/home_controller.dart | 1 + lib/modules/home/views/home_view.dart | 7 +- .../controllers/pairing_controller.dart | 1052 ++++++----------- lib/modules/pairing/views/pairing_view.dart | 184 ++- .../pairing/views/pairing_view_new.dart | 253 ++++ 6 files changed, 727 insertions(+), 771 deletions(-) create mode 100644 lib/modules/pairing/views/pairing_view_new.dart diff --git a/lib/data/services/bluetooth_manager.dart b/lib/data/services/bluetooth_manager.dart index eaa6844e5..a3faebc10 100644 --- a/lib/data/services/bluetooth_manager.dart +++ b/lib/data/services/bluetooth_manager.dart @@ -13,6 +13,7 @@ class BluetoothManager extends GetxService { // 蓝牙状态 final _bluetoothState = 'unknown'.obs; String get bluetoothState => _bluetoothState.value; + RxString get bluetoothStateRx => _bluetoothState; // 当前经典蓝牙设备 final Rx currentDeviceRx = Rx(null); diff --git a/lib/modules/home/controllers/home_controller.dart b/lib/modules/home/controllers/home_controller.dart index 650083239..748d767e6 100644 --- a/lib/modules/home/controllers/home_controller.dart +++ b/lib/modules/home/controllers/home_controller.dart @@ -24,6 +24,7 @@ class HomeController extends GetxController { // Getter方法 bool get isFullyConnected => _isFullyConnected.value; + RxBool get isFullyConnectedRx => _isFullyConnected; // 响应式getter供UI使用 BluetoothDevice? get currentDevice => _bluetoothManager.currentDevice; Rx get deviceInfo => _bleManager.deviceInfo; diff --git a/lib/modules/home/views/home_view.dart b/lib/modules/home/views/home_view.dart index 8cc809fda..b5c3653f7 100644 --- a/lib/modules/home/views/home_view.dart +++ b/lib/modules/home/views/home_view.dart @@ -225,8 +225,8 @@ class HomeView extends GetView { SizedBox(height: 12.h), Obx(() { // 移除连接失败和连接中的状态判断,直接根据是否完全连接来判断 - - return controller.isFullyConnected + // 使用响应式访问确保UI更新 + return controller.isFullyConnectedRx.value ? _buildConnectedDeviceInfo(isDarkMode) //已连接设备信息 : _buildDisconnectedInfo(isDarkMode); //未连接提示 }), @@ -234,7 +234,8 @@ class HomeView extends GetView { // 连接按钮(仅在未连接时显示) SizedBox(height: 12.h), Obx(() { - if (!controller.isFullyConnected) { + // 使用响应式访问确保UI更新 + if (!controller.isFullyConnectedRx.value) { return _buildConnectButton(); } else { return const SizedBox(); diff --git a/lib/modules/pairing/controllers/pairing_controller.dart b/lib/modules/pairing/controllers/pairing_controller.dart index ed7484c52..b9592dde7 100644 --- a/lib/modules/pairing/controllers/pairing_controller.dart +++ b/lib/modules/pairing/controllers/pairing_controller.dart @@ -9,770 +9,496 @@ import '../../../data/services/bluetooth_manager.dart'; import '../../../data/services/ble_manager.dart'; import '../../../core/utils/permission_util.dart'; -/// 配对状态枚举 +/// 配对状态枚举 - 简化版本 enum PairingState { - init, // 初始状态 - btReady, // 蓝牙已就绪 - classicOk, // 经典蓝牙已连接 - serverVerify, // 服务器验证中 - pairing, // 配对中 - paired, // 已配对 - // 错误/退出状态 - exitNoPermission, // 无权限 - exitBtOff, // 蓝牙关闭 - exitNoClassic, // 无经典蓝牙连接 - exitNotMatch, // 设备不匹配 - exitIllegal, // 非法设备 - exitBound, // 已被他人绑定 - exitPairFailed, // 配对失败 - exitTimeout, // 超时 + idle, // 空闲/初始状态 + checking, // 检查中(蓝牙、权限等) + waitingClassic, // 等待经典蓝牙连接 + verifying, // 验证设备 + waitingBle, // 等待BLE连接 + completed, // 配对完成 + error, // 错误状态 } -/// 配对控制器 - 基于有限状态机的实现 +/// 错误类型枚举 +enum ErrorType { + none, + bluetoothOff, // 蓝牙关闭 + noPermission, // 无权限 + noClassicDevice, // 无经典蓝牙设备 + deviceNotMatch, // 设备不匹配 + deviceBound, // 设备已被绑定 + bleTimeout, // BLE连接超时 + networkError, // 网络错误 +} + +/// 配对控制器 - 基于简化状态机的实现 class PairingController extends GetxController { + static const String _tag = 'PairingCtrl'; static PairingController get to => Get.find(); - // ==================== 状态管理 ==================== - final _state = PairingState.init.obs; - PairingState get state => _state.value; - - // ==================== 手动模式控制 ==================== - final isManualMode = false.obs; - final isReadyToStart = false.obs; - - // ==================== 状态数据 ==================== - final statusDescription = ''.obs; - final actionButtonText = ''.obs; + // ========== 状态管理 ========== + final _currentState = PairingState.idle.obs; + final _errorType = ErrorType.none.obs; + final _errorMessage = ''.obs; + + PairingState get currentState => _currentState.value; + ErrorType get errorType => _errorType.value; + String get errorMessage => _errorMessage.value; + + // ========== UI状态 ========== + final statusText = ''.obs; final isLoading = false.obs; + final showConnectButton = false.obs; + final showRetryButton = false.obs; + final showSkipButton = false.obs; + final connectButtonText = '连接设备'.obs; + final retryButtonText = '重试'.obs; - // 当前设备信息 - BluetoothDevice? _currentClassicDevice; + // ========== 监听器管理 ========== + final List _workers = []; + Timer? _bleTimeoutTimer; - // 计时器和异步操作管理 - Timer? _stateTimer; - StreamSubscription? _bluetoothStateSubscription; - StreamSubscription? _deviceSubscription; - StreamSubscription? _bleStatusSubscription; + // ========== 依赖服务 ========== + late final BluetoothManager _bluetoothManager; + late final BleManager _bleManager; - // 常量定义 - static const _tag = 'PairingController'; - static const _validDeviceNamePattern = 'DeapSound'; + // ========== 当前设备信息 ========== + BluetoothDevice? _currentClassicDevice; + String? _verifiedDeviceAddress; + + // Public getter for view access + BluetoothDevice? get currentDevice => _currentClassicDevice; - // ==================== 生命周期 ==================== @override void onInit() { super.onInit(); - _setupStateObserver(); - start(); + Logger.i(_tag, '配对控制器初始化'); + + // 确保服务已初始化 + _bluetoothManager = Get.find(); + _bleManager = Get.find(); + + // 集中设置所有监听器 + _setupAllListeners(); + + // 开始初始状态检查 + Future.delayed(Duration.zero, () { + _startInitialCheck(); + }); } @override void onClose() { - _cancelAllOperations(); - _bluetoothStateSubscription?.cancel(); - _deviceSubscription?.cancel(); - _bleStatusSubscription?.cancel(); + Logger.i(_tag, '配对控制器销毁'); + // 清理所有Worker + for (var worker in _workers) { + worker.dispose(); + } + _workers.clear(); + + // 清理定时器 + _bleTimeoutTimer?.cancel(); + super.onClose(); } - // ==================== 公共 API ==================== + // ==================== 监听器设置 ==================== - /// 启动配对流程 - Future start() async { - _transition(PairingState.init); + /// 集中设置所有监听器 + void _setupAllListeners() { + // 1. 监听蓝牙开关状态 + _setupBluetoothStateListener(); + + // 2. 监听经典蓝牙连接状态 + _setupClassicConnectionListener(); + + // 3. 监听BLE连接状态 + _setupBleConnectionListener(); } - /// 手动开始配对 - Future startManualPairing() async { - if (!isReadyToStart.value) return; - - Logger.i(_tag, '用户手动启动配对流程'); - isManualMode.value = false; - isReadyToStart.value = false; + /// 监听蓝牙开关状态 + void _setupBluetoothStateListener() { + _workers.add( + ever(_bluetoothManager.bluetoothStateRx, (String state) { + Logger.d(_tag, '蓝牙状态变化: $state'); + _handleBluetoothStateChange(state); + }) + ); + } - // 开始执行配对流程 - await _executeInitFlow(); + /// 监听经典蓝牙连接状态 + void _setupClassicConnectionListener() { + _workers.add( + ever(_bluetoothManager.currentDeviceRx, (BluetoothDevice? device) { + Logger.d(_tag, '经典蓝牙设备变化: ${device?.name}'); + _handleClassicDeviceChange(device); + }) + ); } - /// 重新开始配对流程 - Future restart() async { - _cancelAllOperations(); - _transition(PairingState.init); + /// 监听BLE连接状态 + void _setupBleConnectionListener() { + _workers.add( + ever(_bleManager.connectionStateRx, (int state) { + Logger.d(_tag, 'BLE连接状态变化: $state'); + _handleBleStateChange(state); + }) + ); } - /// 重置到手动模式 - void resetToManualMode() { - _cancelAllOperations(); - isManualMode.value = true; - isReadyToStart.value = true; - _transition(PairingState.init); + // ==================== 状态处理 ==================== + + /// 处理蓝牙开关状态变化 + void _handleBluetoothStateChange(String state) { + if (state == 'off') { + // 蓝牙关闭,进入错误状态 + _setError(ErrorType.bluetoothOff, '请开启蓝牙'); + } else if (state == 'on' && _errorType.value == ErrorType.bluetoothOff) { + // 蓝牙重新开启,恢复流程 + Logger.i(_tag, '检测到蓝牙已开启,自动恢复配对流程'); + _clearError(); + // 延迟一下确保蓝牙服务完全就绪 + Future.delayed(const Duration(milliseconds: 500), () { + _checkCurrentStatus(); + }); + } } - /// 处理按钮点击 - void handleActionButtonTap() { - switch (state) { - case PairingState.exitNoPermission: - _openAppSettings(); - break; - case PairingState.exitBtOff: - _openBluetoothSettings(); - break; - case PairingState.exitNoClassic: - case PairingState.exitNotMatch: - _openBluetoothSettings(); + /// 处理经典蓝牙设备变化 + void _handleClassicDeviceChange(BluetoothDevice? device) { + _currentClassicDevice = device; + + switch (_currentState.value) { + case PairingState.waitingClassic: + if (device != null) { + // 有设备连接,进入验证阶段 + Logger.i(_tag, '检测到经典蓝牙设备连接,开始验证'); + _transition(PairingState.verifying); + _verifyDevice(device); + } break; - case PairingState.exitTimeout: - restart(); + + case PairingState.error: + // 如果在错误状态下检测到设备连接,且错误是无设备连接,则自动恢复 + if (device != null && _errorType.value == ErrorType.noClassicDevice) { + Logger.i(_tag, '检测到设备重新连接,自动恢复配对流程'); + _clearError(); + _transition(PairingState.verifying); + _verifyDevice(device); + } break; - case PairingState.exitPairFailed: - case PairingState.exitBound: - _retryPairing(); + + case PairingState.verifying: + case PairingState.waitingBle: + case PairingState.completed: + if (device == null) { + // 设备断开,返回等待连接状态 + _setError(ErrorType.noClassicDevice, '设备已断开'); + } break; + default: break; } } - /// 跳过配对 - void skipPairing() async { - Logger.i(_tag, '用户选择跳过配对'); - // 权限申请 - await _checkAndRequestPermissions(); - // 启动BLE后台服务 - await BleManager.to.startAgentService(); - Logger.i(_tag, 'BLE后台服务已启动'); - _navigateToHome(); + /// 处理BLE状态变化 + void _handleBleStateChange(int state) { + if (_currentState.value == PairingState.waitingBle) { + if (state == 2) { // BLE已连接 + _bleTimeoutTimer?.cancel(); + _transition(PairingState.completed); + } + } + + // 如果在完成状态下BLE断开,显示警告但不改变状态 + if (_currentState.value == PairingState.completed && state != 2) { + Logger.w(_tag, 'BLE连接已断开,但配对流程已完成'); + } } - // ==================== FSM 核心实现 ==================== + // ==================== 状态转换 ==================== /// 状态转换 - void _transition(PairingState next) { - final current = state; - - // 允许init状态重复执行,其他状态避免重复转换 - if (current == next && next != PairingState.init) return; - - Logger.d(_tag, 'FSM: ${current.name} → ${next.name}'); - _cancelStateTimer(); - _state.value = next; - - // 根据新状态执行对应的进入方法 - switch (next) { - case PairingState.init: - _enterInit(); + void _transition(PairingState newState) { + final oldState = _currentState.value; + _currentState.value = newState; + Logger.i(_tag, '状态转换: $oldState -> $newState'); + + // 根据新状态执行相应操作 + _onStateChanged(newState); + } + + /// 状态变化处理 + void _onStateChanged(PairingState state) { + // 重置UI状态 + isLoading.value = false; + showConnectButton.value = false; + showRetryButton.value = false; + showSkipButton.value = false; + + switch (state) { + case PairingState.idle: + statusText.value = '准备开始配对'; break; - case PairingState.btReady: - _enterBtReady(); + + case PairingState.checking: + statusText.value = '正在检查环境...'; + isLoading.value = true; break; - case PairingState.classicOk: - _enterClassicOk(); + + case PairingState.waitingClassic: + statusText.value = '请确保耳机已开启并进入配对模式'; + showConnectButton.value = true; + showSkipButton.value = true; + connectButtonText.value = '前往蓝牙设置'; break; - case PairingState.serverVerify: - _enterServerVerify(); + + case PairingState.verifying: + statusText.value = '正在验证设备...'; + isLoading.value = true; break; - case PairingState.pairing: - _enterPairing(); + + case PairingState.waitingBle: + statusText.value = '正在建立安全连接...'; + isLoading.value = true; + _startBleConnection(); break; - case PairingState.paired: - _enterPaired(); + + case PairingState.completed: + statusText.value = '配对成功!'; + isLoading.value = false; + _navigateToHome(); break; - // 错误状态 - case PairingState.exitNoPermission: - case PairingState.exitBtOff: - case PairingState.exitNoClassic: - case PairingState.exitNotMatch: - case PairingState.exitIllegal: - case PairingState.exitBound: - case PairingState.exitPairFailed: - case PairingState.exitTimeout: - _handleErrorState(next); + + case PairingState.error: + statusText.value = _errorMessage.value; + isLoading.value = false; + showSkipButton.value = true; + + // 根据错误类型决定显示哪个按钮和按钮文本 + switch (_errorType.value) { + case ErrorType.bluetoothOff: + showConnectButton.value = true; + connectButtonText.value = '开启蓝牙'; + break; + case ErrorType.noClassicDevice: + showConnectButton.value = true; + connectButtonText.value = '前往蓝牙设置'; + break; + case ErrorType.noPermission: + showRetryButton.value = true; + retryButtonText.value = '授予权限'; + break; + default: + showRetryButton.value = true; + retryButtonText.value = '重试'; + break; + } break; } } - /// 设置状态观察器 - void _setupStateObserver() { - ever(_state, (PairingState state) { - _updateUI(); - }); - } - - // ==================== 状态处理方法 ==================== - - /// INIT: 初始化状态 - Future _enterInit() async { - Logger.i(_tag, '进入初始化状态'); + // ==================== 错误处理 ==================== - if (isManualMode.value) { - // 手动模式:仅显示待机状态,不执行任何操作 - isLoading.value = false; - statusDescription.value = '准备就绪'; - isReadyToStart.value = true; - return; - } - - // 自动模式:执行原有的初始化逻辑 - await _executeInitFlow(); + /// 设置错误状态 + void _setError(ErrorType type, String message) { + _errorType.value = type; + _errorMessage.value = message; + _transition(PairingState.error); } - /// 执行初始化流程 - Future _executeInitFlow() async { - isLoading.value = true; - statusDescription.value = '正在初始化...'; - - try { - // 初始视觉反馈延迟 - await Future.delayed(const Duration(milliseconds: 800)); - - // 更新状态让用户知道正在进行权限检查 - statusDescription.value = '正在检查所需权限...'; - - // iOS平台跳过权限检查 - if (!Platform.isIOS) { - // 添加额外视觉反馈延迟 - await Future.delayed(const Duration(milliseconds: 600)); - - final hasPermission = await _checkAndRequestPermissions(); - if (!hasPermission) { - statusDescription.value = '缺少必要的权限'; - await Future.delayed(const Duration(milliseconds: 500)); - _transition(PairingState.exitNoPermission); - return; - } - } - - // 设置蓝牙状态监听 - _setupBluetoothListener(); - - // 更新状态让用户知道正在进行蓝牙检查 - statusDescription.value = '检查蓝牙状态...'; - await Future.delayed(const Duration(milliseconds: 400)); + /// 清除错误状态 + void _clearError() { + _errorType.value = ErrorType.none; + _errorMessage.value = ''; + } - // 检查蓝牙状态 - final btState = BluetoothManager.to.bluetoothState; - Logger.i(_tag, '当前蓝牙状态: $btState'); + // ==================== 核心流程 ==================== - if (btState == 'on' || btState == 'unknown') { - _transition(PairingState.btReady); - } else { - statusDescription.value = '蓝牙未开启'; - await Future.delayed(const Duration(milliseconds: 500)); - _transition(PairingState.exitBtOff); - } - } catch (e) { - Logger.e(_tag, 'Init failed: $e'); - statusDescription.value = '初始化出错'; - await Future.delayed(const Duration(milliseconds: 500)); - _transition(PairingState.exitNoPermission); - } finally { - isLoading.value = false; - } + /// 开始初始检查 + void _startInitialCheck() { + _transition(PairingState.checking); + _checkCurrentStatus(); } - /// BT_READY: 蓝牙已就绪,检查经典蓝牙连接 - Future _enterBtReady() async { - isLoading.value = true; - statusDescription.value = '正在检查设备连接...'; - + /// 检查当前状态并决定下一步 + void _checkCurrentStatus() async { try { - // 刷新设备连接 - await BluetoothManager.to.refreshDeviceConnection(); - - // 获取当前连接的经典蓝牙设备 - final device = BluetoothManager.to.currentDevice; - if (device == null) { - _transition(PairingState.exitNoClassic); + // 1. 检查蓝牙状态 + if (_bluetoothManager.bluetoothState == 'off') { + _setError(ErrorType.bluetoothOff, '请开启蓝牙'); return; } - - _currentClassicDevice = device; - _transition(PairingState.classicOk); - } catch (e) { - Logger.e(_tag, 'BT ready check failed: $e'); - _transition(PairingState.exitNoClassic); - } finally { - isLoading.value = false; - } - } - - /// CLASSIC_OK: 经典蓝牙已连接,检查是否需要BLE配对 - Future _enterClassicOk() async { - if (_currentClassicDevice == null) { - _transition(PairingState.exitNoClassic); - return; - } - - statusDescription.value = '已连接: ${_currentClassicDevice!.name}'; - - // 检查设备名称是否合规 - if (!_isValidDeviceName(_currentClassicDevice!.name)) { - _transition(PairingState.exitNotMatch); - return; - } - - // 检查是否已有BLE配对记录 - try { - // 1. 检查平台是否支持Companion功能 - final isSupported = await BleManager.to.isCompanionSupported(); - if (!isSupported) { - Logger.i(_tag, '设备不支持Companion功能,跳过BLE配对'); - _transition(PairingState.paired); + + // 2. 检查权限 + if (!await _checkPermissions()) { + _setError(ErrorType.noPermission, '请授予必要权限'); return; } - - // 2. 获取已配对的MAC地址 - final pairedMac = await BleManager.to.getPairedMacAddress(); - - // 3. 检查是否已经配对且是同一设备 - if (pairedMac != null && pairedMac == _currentClassicDevice!.address) { - Logger.i(_tag, '设备已完成BLE配对: $pairedMac'); - - // 4. 检查BLE连接状态 - final isBleConnected = await BleManager.to.isBleConnected(); - if (isBleConnected) { - Logger.i(_tag, 'BLE已连接,跳转到配对成功'); - _transition(PairingState.paired); - } else { - Logger.i(_tag, 'BLE未连接,尝试重新配对'); - _transition(PairingState.pairing); - } + + // 3. 检查经典蓝牙连接 + final classicDevice = _bluetoothManager.currentDevice; + if (classicDevice == null) { + _transition(PairingState.waitingClassic); return; } - - // 5. 如果没有配对或配对的是其他设备,进入验证流程 - _transition(PairingState.serverVerify); + + // 4. 如果有设备,进行验证 + _currentClassicDevice = classicDevice; + _transition(PairingState.verifying); + await _verifyDevice(classicDevice); + } catch (e) { - Logger.e(_tag, 'Classic OK check failed: $e'); - // 出错时直接尝试配对 - _transition(PairingState.pairing); + Logger.e(_tag, '状态检查失败: $e'); + _setError(ErrorType.networkError, '检查失败,请重试'); } } - /// SERVER_VERIFY: 服务器验证 - Future _enterServerVerify() async { - isLoading.value = true; - statusDescription.value = '正在验证设备...'; - - try { - if (_currentClassicDevice == null) { - _transition(PairingState.exitNotMatch); - return; - } - - // 验证设备是否可以配对 - final canPair = await BleManager.to - .verifyDeviceCanPair(_currentClassicDevice!.address); - - if (canPair) { - Logger.i(_tag, '设备验证通过,可以配对'); - _transition(PairingState.pairing); - } else { - Logger.e(_tag, '设备验证失败,不能配对'); - _transition(PairingState.exitIllegal); - } - } catch (e) { - Logger.e(_tag, 'Server verify failed: $e'); - _transition(PairingState.exitIllegal); - } finally { - isLoading.value = false; + /// 检查权限 + Future _checkPermissions() async { + // Android权限检查 + if (Platform.isAndroid) { + // 检查蓝牙相关权限 + final bluetoothScan = await Permission.bluetoothScan.request(); + final bluetoothConnect = await Permission.bluetoothConnect.request(); + final location = await Permission.locationWhenInUse.request(); + + return bluetoothScan.isGranted && + bluetoothConnect.isGranted && + location.isGranted; } + return true; } - /// PAIRING: 执行配对 - Future _enterPairing() async { - isLoading.value = true; - statusDescription.value = '正在配对设备...'; - + /// 验证设备 + Future _verifyDevice(BluetoothDevice device) async { try { - if (_currentClassicDevice == null) { - _transition(PairingState.exitPairFailed); - return; - } - - // 启动BLE状态监听 - _setupBleStatusListener(); - - // 检查当前BLE连接状态,防止错过已经连接的事件 - final isBleConnected = await BleManager.to.isBleConnected(); - if (isBleConnected) { - Logger.i(_tag, 'BLE已经连接,直接完成配对'); - _onBleConnected(); - return; - } - - // 设置配对超时保护 - _stateTimer = Timer(const Duration(seconds: 30), () { - if (state == PairingState.pairing) { - Logger.e(_tag, 'BLE配对超时'); - _transition(PairingState.exitTimeout); - } - }); - - // 使用系统配对框架执行BLE配对 - // iOS: AccessorySetupKit - // Android: Companion Device Manager - final success = await BleManager.to.startDeviceAssociation( - deviceNamePattern: _currentClassicDevice!.name, - macAddress: _currentClassicDevice!.address, - ); - - if (success) { - Logger.i(_tag, 'BLE配对启动成功,立即注册后台扫描'); - - // 配对启动成功后立即注册后台扫描 - try { - // Android平台注册后台扫描 - if (Platform.isAndroid) { - await BleManager.to.registerBackgroundScan( - macAddress: _currentClassicDevice!.address); - Logger.i(_tag, '已注册后台扫描'); - } - } catch (e) { - Logger.e(_tag, '注册后台扫描失败: $e'); - // 注册失败不影响配对流程继续 + Logger.i(_tag, '开始验证设备: ${device.name} (${device.address})'); + + // TODO: 实现API验证逻辑 + // 暂时模拟验证通过 + await Future.delayed(const Duration(seconds: 2)); + + // 检查设备名称是否包含 DeepSound 或 DeapSound + final deviceName = device.name ?? ''; + if (deviceName.contains('DeepSound') || deviceName.contains('DeapSound')) { + Logger.i(_tag, '设备验证通过'); + _verifiedDeviceAddress = device.address; + + // 检查BLE状态 + if (_bleManager.isConnected) { + _transition(PairingState.completed); + } else { + _transition(PairingState.waitingBle); } - - Logger.i(_tag, '等待BLE连接完成...'); - // 配对结果将通过BLE状态监听回调处理 } else { - Logger.e(_tag, 'BLE配对启动失败'); - _transition(PairingState.exitPairFailed); + _setError(ErrorType.deviceNotMatch, '请连接 DeepSound 设备'); } } catch (e) { - Logger.e(_tag, 'Pairing failed: $e'); - _transition(PairingState.exitPairFailed); + Logger.e(_tag, '设备验证异常: $e'); + _setError(ErrorType.networkError, '验证失败,请重试'); } } - /// 设置BLE状态监听 - void _setupBleStatusListener() { - Logger.d(_tag, '🎯 开始设置BLE状态监听器...'); - - _bleStatusSubscription?.cancel(); - - Logger.d(_tag, '📱 当前BLE连接状态: ${BleManager.to.connectionState}'); - Logger.d(_tag, '🔗 开始监听BleManager.connectionStateRx...'); - - // 监听BleManager的连接状态变化,而不是直接监听BleService - _bleStatusSubscription = BleManager.to.connectionStateRx.listen( - (bleState) { - // 只在配对状态下处理BLE连接事件 - if (state == PairingState.pairing) { - if (bleState == BleConnectionState.connected) { - Logger.i(_tag, 'BLE连接成功,处理配对完成'); - _onBleConnected(); - } else if (bleState == BleConnectionState.error) { - Logger.e(_tag, 'BLE连接错误'); - _transition(PairingState.exitPairFailed); - } else { - Logger.d(_tag, 'BLE状态变化,但非关键状态: $bleState'); - } - } else { - Logger.d(_tag, '当前状态为${state.name},忽略BLE连接事件'); - } - }, - onError: (error) { - Logger.e(_tag, 'BLE状态监听错误: $error'); - }, - onDone: () { - Logger.w(_tag, 'BLE状态监听流已关闭'); - }, - ); - - Logger.d(_tag, 'BLE状态监听器已设置完成'); - } - - /// 处理BLE连接成功 - Future _onBleConnected() async { - Logger.i(_tag, 'BLE连接成功'); - - // 取消超时计时器 - _cancelStateTimer(); - - // BLE连接成功即表示配对完成 - // iOS使用UUID,Android使用MAC地址,但连接成功都表示配对验证通过 - Logger.i(_tag, 'BLE配对验证成功,经典蓝牙和BLE均已连接'); - _transition(PairingState.paired); - } - - /// PAIRED: 配对成功 - Future _enterPaired() async { - isLoading.value = false; - statusDescription.value = '配对成功!'; - + /// 开始BLE连接 + void _startBleConnection() async { try { - // 启动BLE后台服务 - await BleManager.to.startAgentService(); - Logger.i(_tag, 'BLE后台服务已启动'); - - // 延迟后跳转到主页 - await Future.delayed(const Duration(milliseconds: 500)); - _navigateToHome(); - } catch (e) { - Logger.e(_tag, 'Post pairing setup failed: $e'); - // 即使失败也跳转到主页 - // _navigateToHome(); - } - } - - /// 处理错误状态 - void _handleErrorState(PairingState errorState) { - isLoading.value = false; - - switch (errorState) { - case PairingState.exitNoPermission: - statusDescription.value = '需要蓝牙权限'; - actionButtonText.value = '去设置'; - break; - case PairingState.exitBtOff: - statusDescription.value = '请开启蓝牙'; - actionButtonText.value = '去设置'; - break; - case PairingState.exitNoClassic: - statusDescription.value = '请先连接DeapSound设备'; - actionButtonText.value = '去设置'; - break; - case PairingState.exitNotMatch: - statusDescription.value = '请连接DeapSound设备'; - actionButtonText.value = '去设置'; - break; - case PairingState.exitTimeout: - statusDescription.value = '扫描超时'; - actionButtonText.value = '重试'; - break; - case PairingState.exitPairFailed: - statusDescription.value = '配对失败'; - actionButtonText.value = '重新配对'; - break; - case PairingState.exitBound: - statusDescription.value = '设备已被其他用户绑定'; - actionButtonText.value = '联系客服'; - break; - case PairingState.exitIllegal: - statusDescription.value = '设备验证失败'; - actionButtonText.value = '重试'; - break; - default: - break; - } - } - - // ==================== 辅助方法 ==================== - - /// 检查并请求权限 - Future _checkAndRequestPermissions() async { - try { - if (Platform.isIOS) { - // iOS权限由系统自动处理 - return true; - } - - // Android权限检查 - 使用 PermissionUtil - // 根据 Android 版本确定需要的权限 - List requiredPermissions = [ - Permission.locationWhenInUse, - ]; - - if (Platform.isAndroid && await _getAndroidSdkVersion() >= 31) { - requiredPermissions.add(Permission.bluetoothScan); - requiredPermissions.add(Permission.bluetoothConnect); - requiredPermissions.add(Permission.locationAlways); - requiredPermissions.add(Permission.notification); + // 设置超时定时器 + _bleTimeoutTimer?.cancel(); + _bleTimeoutTimer = Timer(const Duration(seconds: 30), () { + if (_currentState.value == PairingState.waitingBle) { + _setError(ErrorType.bleTimeout, 'BLE连接超时,请重试'); + } + }); + + // 检查是否已连接 + if (_bleManager.isConnected) { + _bleTimeoutTimer?.cancel(); + _transition(PairingState.completed); + return; } - - // 逐个请求权限 - for (var permission in requiredPermissions) { - final permissionName = _getPermissionName(permission); - Logger.d(_tag, '正在请求 $permissionName 权限'); - - final granted = await PermissionUtil.instance.requestPermission( - permissionType: permission, - permissionName: permissionName, - explanationText: _getExplanationText(permission), - permanentDenialText: '您已永久拒绝$permissionName权限,请前往应用设置开启', - settingsButtonText: '去设置', - cancelButtonText: '取消', - ); - - if (!granted) { - Logger.w(_tag, '$permissionName 权限未授予'); - return false; + + // 注册后台扫描以开始BLE配对 + if (_verifiedDeviceAddress != null) { + final success = await _bleManager.registerBackgroundScan(macAddress: _verifiedDeviceAddress); + if (!success && _currentState.value == PairingState.waitingBle) { + _setError(ErrorType.bleTimeout, 'BLE配对启动失败'); } } - - return true; } catch (e) { - Logger.e(_tag, '权限检查失败: $e'); - return false; + Logger.e(_tag, 'BLE连接失败: $e'); + _setError(ErrorType.bleTimeout, 'BLE连接失败'); } } - // 获取权限的用户友好名称 - String _getPermissionName(Permission permission) { - if (permission == Permission.location) return '定位'; - if (permission == Permission.locationWhenInUse) return '定位'; - if (permission == Permission.locationAlways) return '定位'; - if (permission == Permission.notification) return '通知'; - if (permission == Permission.accessNotificationPolicy) return '通知'; - if (permission == Permission.bluetoothScan) return '蓝牙扫描'; - if (permission == Permission.bluetoothConnect) return '蓝牙连接'; - return '未知权限'; - } - - // 获取权限说明文本 - String _getExplanationText(Permission permission) { - if (permission == Permission.location || - permission == Permission.locationWhenInUse) { - return '用于发现和连接蓝牙设备。我们不会在应用后台使用定位功能。'; - } - - if (permission == Permission.bluetoothScan || - permission == Permission.bluetoothConnect) { - return '用于扫描和连接蓝牙设备,这是与耳机通信所必需的权限。'; - } - - return '此权限是应用正常运行所必需的。'; - } - - /// 设置蓝牙状态监听 - void _setupBluetoothListener() { - _bluetoothStateSubscription?.cancel(); - _bluetoothStateSubscription = - BluetoothManager.to.bluetoothState.obs.listen((btState) { - Logger.d(_tag, '蓝牙状态变化: $btState'); - - // 根据当前状态决定如何响应蓝牙状态变化 - if (btState == 'off' && state != PairingState.exitBtOff) { - _transition(PairingState.exitBtOff); - } else if (btState == 'on' && state == PairingState.exitBtOff) { - _transition(PairingState.btReady); - } - }); - - // 监听设备连接状态 - _deviceSubscription?.cancel(); - _deviceSubscription = BluetoothManager.to.currentDeviceRx.listen((device) { - Logger.d(_tag, '设备连接状态变化: ${device?.name}'); - - // 根据当前状态决定如何响应设备变化 - if (device != null && state == PairingState.exitNoClassic) { - _currentClassicDevice = device; - _transition(PairingState.classicOk); - } else if (device == null && - (state == PairingState.classicOk || - state == PairingState.pairing || - state == PairingState.serverVerify)) { - _transition(PairingState.exitNoClassic); - } + /// 导航到主页 + void _navigateToHome() { + Future.delayed(const Duration(seconds: 1), () { + Get.offAllNamed(Routes.home); }); } - /// 检查设备名称是否合规 - bool _isValidDeviceName(String deviceName) { - return deviceName.contains(_validDeviceNamePattern); - } - - /// 获取Android SDK版本 - Future _getAndroidSdkVersion() async { - // 简化处理,实际应该通过platform channel获取 - return 31; - } - - /// 更新UI - void _updateUI() { - // 根据状态更新UI文本 - switch (state) { - case PairingState.init: - if (isManualMode.value && isReadyToStart.value) { - statusDescription.value = '准备就绪'; - } else { - statusDescription.value = '初始化中...'; - } - break; - case PairingState.btReady: - statusDescription.value = '蓝牙已就绪'; - break; - case PairingState.classicOk: - if (_currentClassicDevice != null) { - statusDescription.value = '已连接: ${_currentClassicDevice!.name}'; - } - break; - case PairingState.serverVerify: - statusDescription.value = '正在验证设备...'; - break; - case PairingState.pairing: - statusDescription.value = '正在配对...'; - break; - case PairingState.paired: - statusDescription.value = '配对成功!'; - break; - default: - // 错误状态在_handleErrorState中处理 - break; - } - } - - /// 取消所有异步操作 - void _cancelAllOperations() { - _stateTimer?.cancel(); - } - - /// 取消状态计时器 - void _cancelStateTimer() { - _stateTimer?.cancel(); - } - - /// 重试配对 - void _retryPairing() { - if (_currentClassicDevice != null) { - _transition(PairingState.pairing); + // ==================== 用户操作 ==================== + + /// 用户点击连接按钮 + void onConnectTap() { + Logger.i(_tag, '用户点击连接按钮'); + + // 根据当前状态或错误类型决定行为 + if (_currentState.value == PairingState.error) { + switch (_errorType.value) { + case ErrorType.bluetoothOff: + // 引导用户去开启蓝牙 + Logger.i(_tag, '引导用户开启蓝牙'); + AppSettings.openAppSettings(type: AppSettingsType.bluetooth); + break; + case ErrorType.noClassicDevice: + // 引导用户去蓝牙设置连接设备 + Logger.i(_tag, '引导用户去蓝牙设置'); + AppSettings.openAppSettings(type: AppSettingsType.bluetooth); + break; + default: + // 其他错误情况,刷新并重试 + _bluetoothManager.refreshDeviceConnection(); + _checkCurrentStatus(); + break; + } + } else if (_currentState.value == PairingState.waitingClassic) { + // 等待经典蓝牙连接状态,引导去设置 + Logger.i(_tag, '等待经典蓝牙连接,引导用户去蓝牙设置'); + AppSettings.openAppSettings(type: AppSettingsType.bluetooth); } else { - restart(); + // 其他情况,刷新设备连接 + _bluetoothManager.refreshDeviceConnection(); + _checkCurrentStatus(); } } - /// 打开应用设置 - void _openAppSettings() { - AppSettings.openAppSettings(); - } - - /// 打开蓝牙设置 - void _openBluetoothSettings() { - if (Platform.isAndroid) { - AppSettings.openAppSettings(type: AppSettingsType.bluetooth); - } else { + /// 用户点击重试按钮 + void onRetryTap() { + Logger.i(_tag, '用户点击重试按钮'); + + // 如果是权限问题,引导去设置 + if (_errorType.value == ErrorType.noPermission) { + Logger.i(_tag, '引导用户去应用设置授予权限'); AppSettings.openAppSettings(); + } else { + // 其他情况,清除错误并重试 + _clearError(); + _startInitialCheck(); } } - /// 导航到主页 - void _navigateToHome() { - Logger.i(_tag, '跳转到主页'); + /// 用户点击跳过按钮 + void onSkipTap() { + Logger.i(_tag, '用户选择跳过配对'); Get.offAllNamed(Routes.home); } - // ==================== 公共属性 ==================== - - /// 当前连接的经典蓝牙设备 - BluetoothDevice? get currentDevice => _currentClassicDevice; - - /// 是否可以跳过配对 - bool get canSkip => - !isLoading.value && - ((isManualMode.value && isReadyToStart.value) || // 手动模式待机状态可以跳过 - state == PairingState.exitNoClassic || - state == PairingState.exitNotMatch || - state == PairingState.exitTimeout); - - /// 是否显示操作按钮 - bool get showActionButton => - state == PairingState.exitNoPermission || - state == PairingState.exitBtOff || - state == PairingState.exitNoClassic || - state == PairingState.exitNotMatch || - state == PairingState.exitTimeout || - state == PairingState.exitPairFailed || - state == PairingState.exitBound || - state == PairingState.exitIllegal; -} + /// 打开蓝牙设置 + void openBluetoothSettings() { + AppSettings.openAppSettings(type: AppSettingsType.bluetooth); + } +} \ No newline at end of file diff --git a/lib/modules/pairing/views/pairing_view.dart b/lib/modules/pairing/views/pairing_view.dart index 9d8da4845..e0577cdf1 100644 --- a/lib/modules/pairing/views/pairing_view.dart +++ b/lib/modules/pairing/views/pairing_view.dart @@ -2,8 +2,6 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import '../controllers/pairing_controller.dart'; -import '../../../core/widgets/common_bottom_nav.dart'; -import '../../../core/themes/app_theme.dart'; class PairingView extends GetView { const PairingView({Key? key}) : super(key: key); @@ -97,28 +95,25 @@ class PairingView extends GetView { // 设备动画 - 根据状态显示不同的动画效果 Widget _buildDeviceAnimation(bool isDarkMode) { return Obx(() { - final state = controller.state; + final state = controller.currentState; final isLoading = controller.isLoading.value; - final isReadyToStart = controller.isReadyToStart.value; - final isManualMode = controller.isManualMode.value; - // 根据状态决定动画颜色 Color primaryColor = Color(0xFF0066CC); Color secondaryColor = Color(0xFF5E5CE6); // 错误状态使用红色 - if (state.name.startsWith('exit')) { + if (state == PairingState.error) { primaryColor = Colors.red; secondaryColor = Colors.redAccent; } // 成功状态使用绿色 - else if (state == PairingState.paired) { + else if (state == PairingState.completed) { primaryColor = Colors.green; secondaryColor = Colors.greenAccent; } - // 手动模式待机状态的特殊处理 - if (isManualMode && isReadyToStart) { + // 待机状态的特殊处理 + if (state == PairingState.idle || state == PairingState.waitingClassic) { return Container( width: 240.w, height: 240.w, @@ -237,19 +232,20 @@ class PairingView extends GetView { // 根据状态获取图标 IconData _getIconForState(PairingState state) { switch (state) { - case PairingState.paired: + case PairingState.completed: return Icons.check_circle; - case PairingState.exitNoPermission: - case PairingState.exitBtOff: - return Icons.bluetooth_disabled; - case PairingState.exitNoClassic: - case PairingState.exitNotMatch: - return Icons.headphones_outlined; - case PairingState.exitPairFailed: - case PairingState.exitTimeout: - case PairingState.exitIllegal: - case PairingState.exitBound: - return Icons.error_outline; + case PairingState.error: + final errorType = controller.errorType; + switch (errorType) { + case ErrorType.bluetoothOff: + case ErrorType.noPermission: + return Icons.bluetooth_disabled; + case ErrorType.noClassicDevice: + case ErrorType.deviceNotMatch: + return Icons.headphones_outlined; + default: + return Icons.error_outline; + } default: return Icons.headphones; } @@ -258,8 +254,8 @@ class PairingView extends GetView { // 状态信息 Widget _buildStatusInfo(bool isDarkMode) { return Obx(() { - final status = controller.statusDescription.value; - final state = controller.state; + final status = controller.statusText.value; + final state = controller.currentState; final device = controller.currentDevice; return Column( @@ -289,10 +285,9 @@ class PairingView extends GetView { // 当前连接设备(有设备时才显示) if (device != null && - (state == PairingState.classicOk || - state == PairingState.serverVerify || - state == PairingState.pairing || - state == PairingState.paired)) ...[ + (state == PairingState.verifying || + state == PairingState.waitingBle || + state == PairingState.completed)) ...[ SizedBox(height: 24.h), _buildConnectedDevice(device, isDarkMode), ], @@ -303,37 +298,39 @@ class PairingView extends GetView { // 根据状态获取副标题 String _getSubtitleForState(PairingState state) { - if (controller.isManualMode.value && controller.isReadyToStart.value) { - return '点击下方按钮开始配对您的DeapSound设备'; - } - switch (state) { - case PairingState.init: - case PairingState.btReady: - return controller.isManualMode.value ? '准备开始配对' : '正在准备配对环境...'; - case PairingState.classicOk: + case PairingState.idle: + return '准备开始配对'; + case PairingState.checking: + return '正在准备配对环境...'; + case PairingState.waitingClassic: return '请先通过系统蓝牙连接DeapSound设备'; - case PairingState.serverVerify: - case PairingState.pairing: + case PairingState.verifying: + return '正在验证设备...'; + case PairingState.waitingBle: return '请稍候,正在完成配对...'; - case PairingState.paired: + case PairingState.completed: return '配对成功!即将跳转到主页...'; - case PairingState.exitNoPermission: - return '需要蓝牙权限才能继续'; - case PairingState.exitBtOff: - return '请开启蓝牙后重试'; - case PairingState.exitNoClassic: - return '请先通过系统蓝牙连接DeapSound设备'; - case PairingState.exitNotMatch: - return '请确保连接的是DeapSound设备'; - case PairingState.exitTimeout: - return '扫描超时,请确保设备在附近'; - case PairingState.exitPairFailed: - return '配对失败,请重试'; - case PairingState.exitBound: - return '该设备已被其他账号绑定'; - case PairingState.exitIllegal: - return '设备验证失败,请检查设备是否正常'; + case PairingState.error: + final errorType = controller.errorType; + switch (errorType) { + case ErrorType.bluetoothOff: + return '请开启蓝牙后重试'; + case ErrorType.noPermission: + return '需要蓝牙权限才能继续'; + case ErrorType.noClassicDevice: + return '请先通过系统蓝牙连接DeapSound设备'; + case ErrorType.deviceNotMatch: + return '请确保连接的是DeapSound设备'; + case ErrorType.deviceBound: + return '该设备已被其他账号绑定'; + case ErrorType.bleTimeout: + return '扫描超时,请确保设备在附近'; + case ErrorType.networkError: + return '网络错误,请检查网络连接'; + default: + return '配对失败,请重试'; + } default: return ''; } @@ -411,23 +408,32 @@ class PairingView extends GetView { ); } + // 获取连接按钮图标 + IconData _getConnectButtonIcon() { + final buttonText = controller.connectButtonText.value; + if (buttonText.contains('蓝牙')) { + return Icons.bluetooth; + } else if (buttonText.contains('设置')) { + return Icons.settings; + } + return Icons.bluetooth_searching; + } + // 操作按钮 Widget _buildActionButtons(bool isDarkMode) { return Obx(() { - final showActionButton = controller.showActionButton; - final canSkip = controller.canSkip; - final actionButtonText = controller.actionButtonText.value; - final isReadyToStart = controller.isReadyToStart.value; - final isManualMode = controller.isManualMode.value; + final showConnectButton = controller.showConnectButton.value; + final showRetryButton = controller.showRetryButton.value; + final showSkipButton = controller.showSkipButton.value; return Container( width: double.infinity, child: Column( children: [ - // 手动模式:显示开始配对按钮 - if (isManualMode && isReadyToStart) + // 显示连接按钮 + if (showConnectButton) ElevatedButton( - onPressed: () => controller.startManualPairing(), + onPressed: () => controller.onConnectTap(), style: ElevatedButton.styleFrom( backgroundColor: Color(0xFF0066CC), foregroundColor: Colors.white, @@ -441,10 +447,10 @@ class PairingView extends GetView { child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ - Icon(Icons.bluetooth_searching, size: 20.w), + Icon(_getConnectButtonIcon(), size: 20.w), SizedBox(width: 8.w), Text( - '开始配对设备', + controller.connectButtonText.value, style: TextStyle( fontSize: 16.sp, fontWeight: FontWeight.w600, @@ -454,13 +460,12 @@ class PairingView extends GetView { ), ), - // 原有的状态按钮(错误处理等) - if (showActionButton && !isManualMode) + // 显示重试按钮 + if (showRetryButton) ElevatedButton( - onPressed: () => controller.handleActionButtonTap(), + onPressed: () => controller.onRetryTap(), style: ElevatedButton.styleFrom( - backgroundColor: - _getButtonColor(controller.state, isDarkMode), + backgroundColor: Colors.red, foregroundColor: Colors.white, padding: EdgeInsets.symmetric(vertical: 16.h), shape: RoundedRectangleBorder( @@ -470,7 +475,7 @@ class PairingView extends GetView { minimumSize: Size(double.infinity, 56.h), ), child: Text( - actionButtonText, + controller.retryButtonText.value, style: TextStyle( fontSize: 16.sp, fontWeight: FontWeight.w600, @@ -478,11 +483,11 @@ class PairingView extends GetView { ), ), - // 跳过按钮和重置按钮 - if (canSkip) ...[ + // 跳过按钮 + if (showSkipButton) ...[ SizedBox(height: 12.h), TextButton( - onPressed: () => controller.skipPairing(), + onPressed: () => controller.onSkipTap(), style: TextButton.styleFrom( foregroundColor: Color(0xFF0066CC), padding: EdgeInsets.symmetric(vertical: 12.h), @@ -498,41 +503,10 @@ class PairingView extends GetView { ), ], - // 添加重置按钮(在错误状态下) - if (!isManualMode && - !controller.isLoading.value && - controller.state.name.startsWith('exit')) ...[ - SizedBox(height: 8.h), - TextButton( - onPressed: () => controller.resetToManualMode(), - style: TextButton.styleFrom( - foregroundColor: Colors.grey, - padding: EdgeInsets.symmetric(vertical: 8.h), - ), - child: Text( - '重新配对', - style: TextStyle(fontSize: 14.sp), - ), - ), - ], ], ), ); }); } - // 根据状态获取按钮颜色 - Color _getButtonColor(PairingState state, bool isDarkMode) { - switch (state) { - case PairingState.exitNoPermission: - case PairingState.exitBtOff: - return isDarkMode ? Colors.grey[800]! : Colors.grey[600]!; - case PairingState.exitPairFailed: - case PairingState.exitBound: - case PairingState.exitIllegal: - return Colors.red; - default: - return Color(0xFF0066CC); - } - } } diff --git a/lib/modules/pairing/views/pairing_view_new.dart b/lib/modules/pairing/views/pairing_view_new.dart new file mode 100644 index 000000000..8ee96a7cc --- /dev/null +++ b/lib/modules/pairing/views/pairing_view_new.dart @@ -0,0 +1,253 @@ +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import '../controllers/pairing_controller.dart'; +import '../../../core/themes/app_theme.dart'; + +/// 配对视图 - 简化版本 +class PairingView extends GetView { + const PairingView({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + final bool isDarkMode = Theme.of(context).brightness == Brightness.dark; + + return Scaffold( + backgroundColor: isDarkMode ? Colors.black : Colors.white, + body: SafeArea( + child: Padding( + padding: EdgeInsets.all(24.w), + child: Column( + children: [ + // 顶部标题 + _buildHeader(isDarkMode), + + // 中间内容 + Expanded( + child: Center( + child: Obx(() => _buildContent(isDarkMode)), + ), + ), + + // 底部按钮 + Obx(() => _buildButtons(isDarkMode)), + + SizedBox(height: 20.h), + ], + ), + ), + ), + ); + } + + /// 构建标题 + Widget _buildHeader(bool isDarkMode) { + return Column( + children: [ + Text( + '设备配对', + style: TextStyle( + fontSize: 24.sp, + fontWeight: FontWeight.bold, + color: isDarkMode ? Colors.white : Colors.black, + ), + ), + SizedBox(height: 8.h), + Text( + '连接您的 DeepSound 耳机', + style: TextStyle( + fontSize: 14.sp, + color: isDarkMode ? Colors.grey[400] : Colors.grey[600], + ), + ), + SizedBox(height: 40.h), + ], + ); + } + + /// 构建主要内容 + Widget _buildContent(bool isDarkMode) { + final state = controller.currentState; + final isLoading = controller.isLoading.value; + + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + // 状态图标 + _buildStateIcon(state, isLoading, isDarkMode), + + SizedBox(height: 40.h), + + // 状态文本 + Text( + controller.statusText.value, + style: TextStyle( + fontSize: 16.sp, + color: isDarkMode ? Colors.white : Colors.black, + ), + textAlign: TextAlign.center, + ), + + // 错误详情 + if (state == PairingState.error) ...[ + SizedBox(height: 16.h), + Container( + padding: EdgeInsets.all(16.w), + decoration: BoxDecoration( + color: Colors.red.withOpacity(0.1), + borderRadius: BorderRadius.circular(12.r), + border: Border.all(color: Colors.red.withOpacity(0.3)), + ), + child: Row( + children: [ + Icon( + Icons.error_outline, + color: Colors.red, + size: 20.sp, + ), + SizedBox(width: 12.w), + Expanded( + child: Text( + controller.errorMessage, + style: TextStyle( + fontSize: 14.sp, + color: Colors.red, + ), + ), + ), + ], + ), + ), + ], + ], + ); + } + + /// 构建状态图标 + Widget _buildStateIcon(PairingState state, bool isLoading, bool isDarkMode) { + if (isLoading) { + return CircularProgressIndicator( + valueColor: AlwaysStoppedAnimation( + Theme.of(Get.context!).primaryColor, + ), + ); + } + + IconData iconData; + Color iconColor; + + switch (state) { + case PairingState.idle: + case PairingState.checking: + iconData = Icons.bluetooth_searching; + iconColor = Colors.blue; + break; + + case PairingState.waitingClassic: + iconData = Icons.bluetooth; + iconColor = Colors.grey; + break; + + case PairingState.verifying: + iconData = Icons.verified_user; + iconColor = Colors.orange; + break; + + case PairingState.waitingBle: + iconData = Icons.sync; + iconColor = Colors.blue; + break; + + case PairingState.completed: + iconData = Icons.check_circle; + iconColor = Colors.green; + break; + + case PairingState.error: + iconData = Icons.error; + iconColor = Colors.red; + break; + } + + return Container( + width: 100.w, + height: 100.w, + decoration: BoxDecoration( + color: iconColor.withOpacity(0.1), + shape: BoxShape.circle, + ), + child: Icon( + iconData, + size: 60.sp, + color: iconColor, + ), + ); + } + + /// 构建按钮 + Widget _buildButtons(bool isDarkMode) { + final showConnectButton = controller.showConnectButton.value; + final showRetryButton = controller.showRetryButton.value; + final showSkipButton = controller.showSkipButton.value; + + return Column( + children: [ + // 主要操作按钮 + if (showConnectButton) + _buildPrimaryButton( + '连接设备', + controller.onConnectTap, + isDarkMode, + ), + + if (showRetryButton) + _buildPrimaryButton( + '重试', + controller.onRetryTap, + isDarkMode, + ), + + SizedBox(height: 12.h), + + // 跳过按钮 + if (showSkipButton) + TextButton( + onPressed: controller.onSkipTap, + child: Text( + '跳过', + style: TextStyle( + fontSize: 14.sp, + color: isDarkMode ? Colors.grey[400] : Colors.grey[600], + ), + ), + ), + ], + ); + } + + /// 构建主要按钮 + Widget _buildPrimaryButton(String text, VoidCallback onPressed, bool isDarkMode) { + return SizedBox( + width: double.infinity, + height: 48.h, + child: ElevatedButton( + onPressed: onPressed, + style: ElevatedButton.styleFrom( + backgroundColor: Theme.of(Get.context!).primaryColor, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12.r), + ), + elevation: 0, + ), + child: Text( + text, + style: TextStyle( + fontSize: 16.sp, + fontWeight: FontWeight.w600, + color: Colors.white, + ), + ), + ), + ); + } +} \ No newline at end of file