You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

735 lines
22 KiB

import 'package:app_settings/app_settings.dart';
import 'package:get/get.dart';
import 'dart:async';
import 'package:permission_handler/permission_handler.dart';
import '../../../routes/app_routes.dart';
import '../../../core/utils/logger.dart';
import 'dart:io';
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, // 超时
}
/// 配对控制器 - 基于有限状态机的实现
class PairingController extends GetxController {
static PairingController get to => Get.find();
// ==================== 状态管理 ====================
final _state = PairingState.init.obs;
PairingState get state => _state.value;
// ==================== 状态数据 ====================
final statusDescription = ''.obs;
final actionButtonText = ''.obs;
final isLoading = false.obs;
// 当前设备信息
BluetoothDevice? _currentClassicDevice;
// 计时器和异步操作管理
Timer? _stateTimer;
StreamSubscription? _bluetoothStateSubscription;
StreamSubscription? _deviceSubscription;
StreamSubscription? _bleStatusSubscription;
// 常量定义
static const _tag = 'PairingController';
static const _validDeviceNamePattern = 'DeapSound';
// ==================== 生命周期 ====================
@override
void onInit() {
super.onInit();
_setupStateObserver();
start();
}
@override
void onClose() {
_cancelAllOperations();
_bluetoothStateSubscription?.cancel();
_deviceSubscription?.cancel();
_bleStatusSubscription?.cancel();
super.onClose();
}
// ==================== 公共 API ====================
/// 启动配对流程
Future<void> start() async {
_transition(PairingState.init);
}
/// 重新开始配对流程
Future<void> restart() async {
_cancelAllOperations();
_transition(PairingState.init);
}
/// 处理按钮点击
void handleActionButtonTap() {
switch (state) {
case PairingState.exitNoPermission:
_openAppSettings();
break;
case PairingState.exitBtOff:
_openBluetoothSettings();
break;
case PairingState.exitNoClassic:
case PairingState.exitNotMatch:
_openBluetoothSettings();
break;
case PairingState.exitTimeout:
restart();
break;
case PairingState.exitPairFailed:
case PairingState.exitBound:
_retryPairing();
break;
default:
break;
}
}
/// 跳过配对
void skipPairing() async {
Logger.i(_tag, '用户选择跳过配对');
// 启动BLE后台服务
await BleManager.to.startAgentService();
Logger.i(_tag, 'BLE后台服务已启动');
_navigateToHome();
}
// ==================== 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();
break;
case PairingState.btReady:
_enterBtReady();
break;
case PairingState.classicOk:
_enterClassicOk();
break;
case PairingState.serverVerify:
_enterServerVerify();
break;
case PairingState.pairing:
_enterPairing();
break;
case PairingState.paired:
_enterPaired();
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);
break;
}
}
/// 设置状态观察器
void _setupStateObserver() {
ever(_state, (PairingState state) {
_updateUI();
});
}
// ==================== 状态处理方法 ====================
/// INIT: 初始化,检查权限
Future<void> _enterInit() async {
Logger.i(_tag, '开始执行初始化流程');
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));
// 检查蓝牙状态
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;
}
}
/// BT_READY: 蓝牙已就绪,检查经典蓝牙连接
Future<void> _enterBtReady() async {
isLoading.value = true;
statusDescription.value = '正在检查设备连接...';
try {
// 刷新设备连接
await BluetoothManager.to.refreshDeviceConnection();
// 获取当前连接的经典蓝牙设备
final device = BluetoothManager.to.currentDevice;
if (device == null) {
_transition(PairingState.exitNoClassic);
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<void> _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);
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);
}
return;
}
// 5. 如果没有配对或配对的是其他设备,进入验证流程
_transition(PairingState.serverVerify);
} catch (e) {
Logger.e(_tag, 'Classic OK check failed: $e');
// 出错时直接尝试配对
_transition(PairingState.pairing);
}
}
/// SERVER_VERIFY: 服务器验证
Future<void> _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;
}
}
/// PAIRING: 执行配对
Future<void> _enterPairing() async {
isLoading.value = true;
statusDescription.value = '正在配对设备...';
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, '等待BLE连接完成...');
// 配对结果将通过BLE状态监听回调处理
} else {
Logger.e(_tag, 'BLE配对启动失败');
_transition(PairingState.exitPairFailed);
}
} catch (e) {
Logger.e(_tag, 'Pairing failed: $e');
_transition(PairingState.exitPairFailed);
}
}
/// 设置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<void> _onBleConnected() async {
Logger.i(_tag, 'BLE连接成功');
// 取消超时计时器
_cancelStateTimer();
// BLE连接成功即表示配对完成
// iOS使用UUID,Android使用MAC地址,但连接成功都表示配对验证通过
Logger.i(_tag, 'BLE配对验证成功,经典蓝牙和BLE均已连接');
_transition(PairingState.paired);
}
/// PAIRED: 配对成功
Future<void> _enterPaired() async {
isLoading.value = false;
statusDescription.value = '配对成功!';
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<bool> _checkAndRequestPermissions() async {
try {
if (Platform.isIOS) {
// iOS权限由系统自动处理
return true;
}
// Android权限检查 - 使用 PermissionUtil
// final context = Get.context;
// if (context == null) {
// Logger.e(_tag, '无法获取有效上下文,跳过权限请求');
// return false;
// }
// 根据 Android 版本确定需要的权限
List<Permission> requiredPermissions = [
Permission.locationWhenInUse,
];
if (Platform.isAndroid && await _getAndroidSdkVersion() >= 31) {
requiredPermissions.add(Permission.bluetoothScan);
requiredPermissions.add(Permission.bluetoothConnect);
}
// 逐个请求权限
for (var permission in requiredPermissions) {
final permissionName = _getPermissionName(permission);
Logger.d(_tag, '正在请求 $permissionName 权限');
final granted = await PermissionUtil.instance.requestPermission(
// context: context,
permissionType: permission,
permissionName: permissionName,
explanationText: _getExplanationText(permission),
permanentDenialText: '您已永久拒绝$permissionName权限,请前往应用设置开启',
settingsButtonText: '去设置',
cancelButtonText: '取消',
);
if (!granted) {
Logger.w(_tag, '$permissionName 权限未授予');
return false;
}
}
return true;
} catch (e) {
Logger.e(_tag, '权限检查失败: $e');
return false;
}
}
// 获取权限的用户友好名称
String _getPermissionName(Permission permission) {
if (permission == Permission.location) return '定位';
if (permission == Permission.locationWhenInUse) 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);
}
});
}
/// 检查设备名称是否合规
bool _isValidDeviceName(String deviceName) {
return deviceName.contains(_validDeviceNamePattern);
}
/// 获取Android SDK版本
Future<int> _getAndroidSdkVersion() async {
// 简化处理,实际应该通过platform channel获取
return 31;
}
/// 更新UI
void _updateUI() {
// 根据状态更新UI文本
switch (state) {
case PairingState.init:
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);
} else {
restart();
}
}
/// 打开应用设置
void _openAppSettings() {
AppSettings.openAppSettings();
}
/// 打开蓝牙设置
void _openBluetoothSettings() {
if (Platform.isAndroid) {
AppSettings.openAppSettings(type: AppSettingsType.bluetooth);
} else {
AppSettings.openAppSettings();
}
}
/// 导航到主页
void _navigateToHome() {
Logger.i(_tag, '跳转到主页');
Get.offAllNamed(Routes.home);
}
// ==================== 公共属性 ====================
/// 当前连接的经典蓝牙设备
BluetoothDevice? get currentDevice => _currentClassicDevice;
/// 是否可以跳过配对
bool get canSkip =>
!isLoading.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;
}