Browse Source

Merge branch 'new_dev' of https://github.com/deepcloud2048/deep_voice into new_dev

weicu
lxm 1 year ago
parent
commit
ec33438d96
  1. 106
      lib/data/services/ble_manager.dart
  2. 13
      lib/data/services/bluetooth_manager.dart
  3. 244
      lib/modules/home/controllers/home_controller.dart
  4. 223
      lib/modules/home/views/home_view.dart
  5. 1158
      lib/modules/pairing/controllers/pairing_controller.dart
  6. 192
      lib/modules/pairing/views/pairing_view.dart
  7. 258
      lib/modules/pairing/views/pairing_view_new.dart
  8. 4
      local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/AgentService.kt
  9. 2
      local_plugins/ble_service/android/src/main/kotlin/com/yunqiinnovation/ble_service/BleCompanionManager.kt
  10. 2
      test.json

106
lib/data/services/ble_manager.dart

@ -6,6 +6,7 @@ import 'package:deep_voice/core/utils/logger.dart';
import 'package:get_storage/get_storage.dart';
import 'dart:convert';
import 'package:agent_service/agent_service.dart';
import 'bluetooth_manager.dart';
// 重新导出BLE连接状态常量,让外部可以通过BleManager访问
export 'package:ble_service/ble_service.dart' show BleConnectionState;
@ -107,6 +108,10 @@ class BleManager extends GetxService {
final BleService _bleService = BleService();
// 经典蓝牙实例
// 当前连接的设备信息
String? _currentDeviceAddress;
String? _currentDeviceName;
// BLE连接状态
final _connectionState = BleConnectionState.disconnected.obs;
int get connectionState => _connectionState.value;
@ -179,19 +184,8 @@ class BleManager extends GetxService {
state == BleConnectionState.error) {
Logger.info('BLE设备已断开或发生错误');
_isConnected.value = false;
// 更新设备信息
_updateDeviceInfoIfNeeded(
batteryLevel: 0,
leftEarBattery: 0,
rightEarBattery: 0,
caseBattery: 0,
leftEarCharging: false,
rightEarCharging: false,
caseCharging: false,
firmwareVersion: '',
firmwareProductId: '',
firmwareProductColor: '',
);
// 断开连接时不清除设备信息,保留最后的电量信息
// 只有在需要完全清除设备时才重置信息
}
}
@ -408,11 +402,14 @@ class BleManager extends GetxService {
else if (deviceInfo.value == null &&
_isConnected.value &&
(firmwareVersion != null || batteryLevel != null)) {
// 提示尝试创建设备信息但缺少必要信息
Logger.warning('尝试更新设备信息,但设备基本信息不存在');
// 使用保存的设备信息创建新的DeviceInfo对象
String deviceAddress = _currentDeviceAddress ?? "";
String deviceName = _currentDeviceName ?? "";
Logger.info('创建新的设备信息对象: address=$deviceAddress, name=$deviceName');
deviceInfo.value = DeviceInfo(
address: "", // 应确保从连接信息中获取真实值
name: "",
address: deviceAddress,
name: deviceName,
batteryLevel: batteryLevel ?? 0,
leftEarBattery: leftEarBattery ?? 0,
rightEarBattery: rightEarBattery ?? 0,
@ -427,35 +424,59 @@ class BleManager extends GetxService {
}
}
/// 清除设备信息(用于完全断开连接或切换设备时)
void clearDeviceInfo() {
Logger.info('清除设备信息');
deviceInfo.value = null;
_currentDeviceAddress = null;
_currentDeviceName = null;
}
/// 连接成功后获取设备信息
Future<void> fetchDeviceInfo() async {
Logger.info('开始获取BLE设备详细信息');
try {
// 获取电池信息
final batteryResult = await _bleService.getBatteryInfo();
if (batteryResult) {
Logger.info('电池信息获取指令已发送');
} else {
// 确保设备信息对象存在
if (deviceInfo.value == null && _isConnected.value) {
// 创建初始设备信息对象
String deviceAddress = _currentDeviceAddress ?? "";
String deviceName = _currentDeviceName ?? "";
Logger.info('初始化设备信息对象: address=$deviceAddress, name=$deviceName');
deviceInfo.value = DeviceInfo(
address: deviceAddress,
name: deviceName,
batteryLevel: 0,
leftEarBattery: 0,
rightEarBattery: 0,
caseBattery: 0,
leftEarCharging: false,
rightEarCharging: false,
caseCharging: false,
firmwareVersion: '',
firmwareProductId: '',
firmwareProductColor: '',
);
}
// 并行获取所有设备信息,不使用延迟
final results = await Future.wait([
_bleService.getBatteryInfo(),
_bleService.getDeviceVersion(),
_bleService.getProductId(),
]);
Logger.info('设备信息获取结果: 电池=${results[0]}, 版本=${results[1]}, 产品ID=${results[2]}');
if (!results[0]) {
Logger.warning('电池信息获取指令发送失败');
}
// 添加延迟
await Future.delayed(const Duration(seconds: 1));
// 获取设备版本信息
final versionResult = await _bleService.getDeviceVersion();
if (versionResult) {
Logger.info('版本信息获取指令已发送');
} else {
if (!results[1]) {
Logger.warning('版本信息获取指令发送失败');
}
// 添加延迟
await Future.delayed(const Duration(seconds: 1));
// 获取产品id
final productIdResult = await _bleService.getProductId();
if (productIdResult) {
Logger.info('产品id获取指令已发送');
} else {
Logger.warning('产品id获取指令发送失败');
if (!results[2]) {
Logger.warning('产品ID获取指令发送失败');
}
} catch (e) {
Logger.error('获取BLE设备信息失败: ${e.toString()}');
@ -579,6 +600,8 @@ class BleManager extends GetxService {
Logger.info('BLE服务停止成功');
_isConnected.value = false;
_connectionState.value = BleConnectionState.disconnected;
// 停止服务时清除设备信息
clearDeviceInfo();
} else {
Logger.error('BLE服务停止失败');
}
@ -759,6 +782,15 @@ class BleManager extends GetxService {
/// 注册后台扫描
Future<bool> registerBackgroundScan({String? macAddress}) async {
try {
// 保存设备地址信息
if (macAddress != null) {
_currentDeviceAddress = macAddress;
// 从蓝牙管理器获取设备名称
final bluetoothManager = Get.find<BluetoothManager>();
_currentDeviceName = bluetoothManager.currentDevice?.name ?? "";
Logger.info('保存设备信息: address=$_currentDeviceAddress, name=$_currentDeviceName');
}
return await _bleService.registerBackgroundScan(macAddress: macAddress);
} catch (e) {
Logger.error('注册后台扫描失败: ${e.toString()}');

13
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<BluetoothDevice?> currentDeviceRx = Rx<BluetoothDevice?>(null);
@ -28,9 +29,19 @@ class BluetoothManager extends GetxService {
super.onInit();
Logger.info('蓝牙服务初始化');
// 设置蓝牙事件监听
_setupBluetoothListener();
// 主动检查当前已连接的设备和蓝牙状态
Future.delayed(Duration.zero, () async {
Logger.info('检查初始蓝牙连接状态');
// 先检查蓝牙是否开启
await isBluetoothEnabled();
// 然后刷新设备连接
await refreshDeviceConnection();
});
}
/// 释放资源

244
lib/modules/home/controllers/home_controller.dart

@ -6,32 +6,26 @@ import 'package:flutter/material.dart';
import '../../../data/services/bluetooth_manager.dart';
import '../../../data/services/ble_manager.dart';
/// 连接状态枚举
enum ConnectionStatus {
disconnected, // 未连接
connecting, // 连接中
connected, // 已连接
failed // 连接失败
disconnected,
connecting,
connected,
failed
}
/// 主页控制器,管理设备连接状态和信息展示
class HomeController extends GetxController {
static const String _tag = 'HomeCtrl';
static HomeController get to => Get.find();
// 连接状态相关
final _connectionStatus = ConnectionStatus.disconnected.obs;
final _connectionErrorMsg = ''.obs;
final _bluetoothState = 'unknown'.obs;
// Getter方法
ConnectionStatus get connectionStatus => _connectionStatus.value;
String get connectionErrorMsg => _connectionErrorMsg.value;
String get bluetoothState => _bluetoothState.value;
final _isFullyConnected = false.obs;
String? _lastConnectedDeviceName;
bool get isFullyConnected => _isFullyConnected.value;
RxBool get isFullyConnectedRx => _isFullyConnected;
BluetoothDevice? get currentDevice => _bluetoothManager.currentDevice;
Rx<DeviceInfo?> get deviceInfo => _bleManager.deviceInfo;
bool get isConnected => currentDevice != null;
// 设备详细信息的getter,从deviceInfo获取数据
final leftEarBattery = 0.obs;
final rightEarBattery = 0.obs;
final caseBattery = 0.obs;
@ -39,15 +33,6 @@ class HomeController extends GetxController {
final rightEarCharging = false.obs;
final caseCharging = false.obs;
final firmwareVersion = ''.obs;
// int get leftEarBattery => deviceInfo?.leftEarBattery ?? 0;
// int get rightEarBattery => deviceInfo?.rightEarBattery ?? 0;
// int get caseBattery => deviceInfo?.caseBattery ?? 0;
// bool get leftEarCharging => deviceInfo?.leftEarCharging ?? false;
// bool get rightEarCharging => deviceInfo?.rightEarCharging ?? false;
// bool get caseCharging => deviceInfo?.caseCharging ?? false;
// String get firmwareVersion => deviceInfo?.firmwareVersion ?? '';
/// 当有新的deviceInfo时更新所有Observable变量
void updateDeviceInfo(DeviceInfo? newDeviceInfo) {
leftEarBattery.value = newDeviceInfo?.leftEarBattery ?? 0;
rightEarBattery.value = newDeviceInfo?.rightEarBattery ?? 0;
@ -58,12 +43,10 @@ class HomeController extends GetxController {
firmwareVersion.value = newDeviceInfo?.firmwareVersion ?? '';
}
// 监听和订阅
Worker? _deviceInfoWorker;
Worker? _bluetoothStateWorker;
Worker? _deviceWorker;
Worker? _classicBluetoothWorker;
Worker? _bleConnectionWorker;
// 蓝牙服务
final _bluetoothManager = Get.find<BluetoothManager>();
final _bleManager = Get.find<BleManager>();
@override
@ -75,175 +58,120 @@ class HomeController extends GetxController {
@override
void onClose() {
_deviceInfoWorker?.dispose();
_bluetoothStateWorker?.dispose();
_deviceWorker?.dispose();
_classicBluetoothWorker?.dispose();
_bleConnectionWorker?.dispose();
super.onClose();
}
/// 使用缓存的蓝牙设备信息初始化
void _initWithCachedInfo() {
// 使用蓝牙服务中的当前状态
_bluetoothState.value = _bluetoothManager.bluetoothState;
// 根据当前设备状态设置连接状态
if (currentDevice != null) {
_connectionStatus.value = ConnectionStatus.connected;
_connectionErrorMsg.value = '';
updateDeviceInfo(_bleManager.deviceInfo.value);
Logger.info('主页加载缓存设备信息: ${currentDevice!.name}');
} else {
_connectionStatus.value = ConnectionStatus.disconnected;
if (_bluetoothState.value == 'off') {
_connectionErrorMsg.value = '蓝牙未开启';
} else {
_connectionErrorMsg.value = '未连接到设备';
}
}
// 设置监听器监听蓝牙服务状态变化
_setupStateListeners();
// 在初始化时自动触发一次刷新设备连接状态
refreshDeviceConnection();
_checkConnectionStatus();
}
/// 设置状态监听器
void _setupStateListeners() {
// 设备信息监听 - 设备信息更新时自动触发UI更新
_deviceInfoWorker = ever(_bleManager.deviceInfo, (DeviceInfo? info) {
Logger.info('设备信息监听');
if (info != null) {
Logger.info('主页更新设备信息: ${info.toString()}');
updateDeviceInfo(info);
} else {
Logger.info('主页清空设备信息');
}
update();
});
// 蓝牙状态监听
_bluetoothStateWorker =
ever(_bluetoothManager.bluetoothState.obs, (String state) {
_bluetoothState.value = state;
// 如果蓝牙关闭,更新错误信息
if (state == 'off') {
_connectionErrorMsg.value = '蓝牙未开启';
_connectionStatus.value = ConnectionStatus.disconnected;
}
update();
});
// 设备连接状态监听 - 直接监听设备变化
_deviceWorker =
ever(_bluetoothManager.currentDeviceRx, (BluetoothDevice? device) {
_classicBluetoothWorker = ever(_bluetoothManager.currentDeviceRx, (BluetoothDevice? device) {
if (device != null) {
// 设备已连接
if (_connectionStatus.value != ConnectionStatus.connected) {
_connectionStatus.value = ConnectionStatus.connected;
_connectionErrorMsg.value = '';
Logger.info('主页检测到设备已连接: ${device.name}');
}
} else if (_connectionStatus.value == ConnectionStatus.connected) {
// 设备已断开
_connectionStatus.value = ConnectionStatus.disconnected;
_connectionErrorMsg.value = '设备已断开连接';
Logger.info('主页检测到设备已断开连接');
_lastConnectedDeviceName = device.name;
}
update();
_checkConnectionStatus();
});
_bleConnectionWorker = ever(_bleManager.connectionStateRx, (int state) {
_checkConnectionStatus();
});
}
/// 刷新设备连接状态
/// 注意:此方法不会主动刷新设备,只更新UI状态
Future<void> refreshDeviceConnection() async {
// 更新当前状态以反映UI变化
bool hasDevice = currentDevice != null;
if (hasDevice) {
_connectionStatus.value = ConnectionStatus.connected;
_connectionErrorMsg.value = '';
await _bleManager.fetchDeviceInfo();
Logger.info('使用缓存设备信息: ${currentDevice!.name}');
} else {
if (_bluetoothState.value == 'off') {
_connectionErrorMsg.value = '蓝牙未开启';
} else {
_connectionErrorMsg.value = '未连接到设备';
}
_connectionStatus.value = ConnectionStatus.disconnected;
}
// 刷新UI
update();
await _checkConnectionStatus();
}
// 导航和功能方法
/// 重新扫描设备
void rescanDevices() {
Get.toNamed(Routes.pairing);
}
/// 打开翻译功能
void openTranslation() {
Get.toNamed(Routes.translation);
}
/// 展示AI功能
void exploreAIFeatures() {
Get.toNamed(Routes.agent);
}
Future<void> _checkConnectionStatus() async {
try {
final currentBleState = await _bleManager.getConnectionState();
bool hasClassicBluetooth = currentDevice != null;
bool hasBleConnection = currentBleState == 2;
bool wasConnected = _isFullyConnected.value;
bool isNowConnected = hasClassicBluetooth && hasBleConnection;
if (isNowConnected && currentDevice == null) {
isNowConnected = false;
}
if (_isFullyConnected.value != isNowConnected) {
_isFullyConnected.value = isNowConnected;
_isFullyConnected.refresh();
update();
if (!wasConnected && isNowConnected) {
await _bleManager.fetchDeviceInfo();
}
}
update();
} catch (e) {
Logger.e(_tag, '检查连接状态失败: $e');
if (_isFullyConnected.value) {
_isFullyConnected.value = false;
_isFullyConnected.refresh();
update();
}
}
}
// UI辅助方法
/// 获取连接状态对应的颜色
Color getConnectionStatusColor() {
switch (_connectionStatus.value) {
case ConnectionStatus.connected:
return Colors.green;
case ConnectionStatus.connecting:
return Colors.amber;
case ConnectionStatus.failed:
return Colors.red;
case ConnectionStatus.disconnected:
default:
return Colors.grey;
}
return _isFullyConnected.value ? Colors.green : Colors.grey;
}
/// 获取连接状态对应的文本
String getConnectionStatusText() {
if (bluetoothState == 'off') {
return 'bluetoothOff'.tr;
}
switch (_connectionStatus.value) {
case ConnectionStatus.connected:
return 'connected'.tr;
case ConnectionStatus.connecting:
return 'connecting'.tr;
case ConnectionStatus.failed:
return 'connectionFailure'.tr;
case ConnectionStatus.disconnected:
default:
return 'unconnected'.tr;
}
return _isFullyConnected.value ? 'connected'.tr : 'unconnected'.tr;
}
/// 获取设备名称
bool get shouldShowDeviceInfo => currentDevice != null;
String getDeviceName() {
return currentDevice?.name ?? 'unknownEquipment'.tr;
if (_isFullyConnected.value && currentDevice == null) {
_checkConnectionStatus();
}
final deviceName = currentDevice?.name;
if (deviceName != null && deviceName.isNotEmpty) {
return deviceName;
}
final bleDeviceName = deviceInfo.value?.name;
if (bleDeviceName != null && bleDeviceName.isNotEmpty) {
return bleDeviceName;
}
if (!_isFullyConnected.value && _lastConnectedDeviceName != null && _lastConnectedDeviceName!.isNotEmpty) {
return _lastConnectedDeviceName!;
}
return 'unknownEquipment'.tr;
}
/// 获取信号强度(模拟数据)
String getSignalStrength() {
if (!isConnected) {
if (!_isFullyConnected.value) {
return 'zeroSignal'.tr;
}
@ -258,12 +186,10 @@ class HomeController extends GetxController {
}
}
/// 获取设备ID
String getDeviceId() {
return deviceInfo.value?.address ?? currentDevice?.address ?? 'unknown'.tr;
}
/// 获取设备电量文本
String getBatteryText() {
return deviceInfo.value?.batteryLevel != null &&
deviceInfo.value!.batteryLevel > 0
@ -271,9 +197,8 @@ class HomeController extends GetxController {
: 'unknown'.tr;
}
/// 获取耳机左右电量文本
String getEarbudsText() {
if (deviceInfo == null ||
if (deviceInfo.value == null ||
(deviceInfo.value!.leftEarBattery <= 0 &&
deviceInfo.value!.rightEarBattery <= 0 ||
deviceInfo.value!.caseBattery > 100)) {
@ -295,7 +220,6 @@ class HomeController extends GetxController {
return '左耳: $leftStatus / 右耳: $rightStatus';
}
/// 获取充电仓电量文本
String getCaseText() {
if (deviceInfo.value == null ||
deviceInfo.value!.caseBattery <= 0 ||
@ -311,9 +235,8 @@ class HomeController extends GetxController {
return '充电仓: $caseStatus';
}
/// 获取充电状态文本
String getChargingText() {
if (deviceInfo == null) {
if (deviceInfo.value == null) {
return 'unknown'.tr;
}
@ -324,7 +247,6 @@ class HomeController extends GetxController {
return isCharging ? 'charging'.tr : 'notCharging'.tr;
}
/// 获取设备版本文本
String getVersionText() {
return deviceInfo.value?.firmwareVersion.isNotEmpty == true
? deviceInfo.value!.firmwareVersion

223
lib/modules/home/views/home_view.dart

@ -5,8 +5,6 @@ import '../controllers/home_controller.dart';
import '../../../routes/app_pages.dart';
import '../../../routes/app_routes.dart';
import '../../../core/widgets/common_bottom_nav.dart';
// 导入枚举
import '../controllers/home_controller.dart' show ConnectionStatus;
// 呼吸灯动画组件
class AnimatedPulsing extends StatefulWidget {
@ -205,8 +203,12 @@ class HomeView extends GetView<HomeController> {
// 设备状态卡片
Widget _buildDeviceStatusCard(bool isDarkMode) {
return Container(
decoration: BoxDecoration(
return Obx(() {
// 使用连接状态作为key,强制在状态改变时重建widget
final isConnected = controller.isFullyConnectedRx.value;
return Container(
key: ValueKey('device_status_$isConnected'),
decoration: BoxDecoration(
color: isDarkMode ? const Color(0xFF1A1A1A) : Colors.white,
borderRadius: BorderRadius.circular(16.r),
boxShadow: [
@ -226,18 +228,9 @@ class HomeView extends GetView<HomeController> {
// 设备信息
SizedBox(height: 12.h),
Obx(() {
// 如果连接失败且有错误信息,显示错误提示
if (controller.connectionStatus == ConnectionStatus.failed &&
controller.connectionErrorMsg.isNotEmpty) {
return _buildConnectionErrorSection(isDarkMode); //错误提示
}
// 如果正在连接中,显示连接中提示
if (controller.connectionStatus == ConnectionStatus.connecting) {
return _buildConnectingIndicator(); //连接中提示
}
return controller.isConnected
// 移除连接失败和连接中的状态判断,直接根据是否完全连接来判断
// 使用响应式访问确保UI更新
return controller.isFullyConnectedRx.value
? _buildConnectedDeviceInfo(isDarkMode) //已连接设备信息
: _buildDisconnectedInfo(isDarkMode); //未连接提示
}),
@ -245,7 +238,8 @@ class HomeView extends GetView<HomeController> {
// 连接按钮(仅在未连接时显示)
SizedBox(height: 12.h),
Obx(() {
if (controller.connectionStatus == ConnectionStatus.disconnected) {
// 使用响应式访问确保UI更新
if (!controller.isFullyConnectedRx.value) {
return _buildConnectButton();
} else {
return const SizedBox();
@ -253,7 +247,8 @@ class HomeView extends GetView<HomeController> {
}),
],
),
);
);
});
}
// 设备状态头部
@ -332,95 +327,7 @@ class HomeView extends GetView<HomeController> {
}
// 连接错误部分
Widget _buildConnectionErrorSection(bool isDarkMode) {
return Column(
children: [
Container(
padding: EdgeInsets.all(12.w),
decoration: BoxDecoration(
color:
isDarkMode ? Colors.red[900]!.withOpacity(0.2) : Colors.red[50],
borderRadius: BorderRadius.circular(12.r),
border: Border.all(
color: isDarkMode ? Colors.red[800]! : Colors.red[200]!,
),
),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Icon(
Icons.error_outline,
color: Colors.red,
size: 18.sp,
),
SizedBox(width: 8.w),
Expanded(
child: Text(
controller.connectionErrorMsg,
style: TextStyle(
fontSize: 12.sp,
height: 1.4,
color: isDarkMode ? Colors.red[300] : Colors.red[800],
),
),
),
],
),
),
SizedBox(height: 12.h),
// 连接按钮
ElevatedButton(
onPressed: controller.rescanDevices,
style: ElevatedButton.styleFrom(
backgroundColor: Colors.blue,
foregroundColor: Colors.white,
minimumSize: Size(double.infinity, 40.h),
elevation: 2,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10.r),
),
),
child: Text(
'reconnect'.tr, // 重新连接
style: TextStyle(
fontSize: 14.sp,
fontWeight: FontWeight.w500,
),
),
),
],
);
}
// 连接中指示器
Widget _buildConnectingIndicator() {
return Container(
padding: EdgeInsets.symmetric(vertical: 20.h),
child: Center(
child: Column(
children: [
SizedBox(
width: 30.w,
height: 30.w,
child: CircularProgressIndicator(
strokeWidth: 2.5.w,
valueColor: AlwaysStoppedAnimation<Color>(Colors.blue),
),
),
SizedBox(height: 12.h),
Text(
'connectingDevice'.tr,
style: TextStyle(
fontSize: 13.sp,
color: Colors.blue,
fontWeight: FontWeight.w500,
),
),
],
),
),
);
}
// 已连接设备信息(新增设备名称 + 信号强度 + 电池信息)
Widget _buildConnectedDeviceInfo(bool isDarkMode) {
@ -608,110 +515,6 @@ class HomeView extends GetView<HomeController> {
}
// 设备详情卡片
Widget _buildDeviceDetailsCard(bool isDarkMode) {
return Container(
padding: EdgeInsets.all(12.w),
decoration: BoxDecoration(
color: isDarkMode ? Colors.grey[900] : Colors.grey[50],
borderRadius: BorderRadius.circular(12.r),
border: Border.all(
color: isDarkMode ? Colors.grey[800]! : Colors.grey[200]!,
width: 1,
),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 设备ID
_buildInfoRow(
icon: Icons.perm_device_info,
label: 'deviceId'.tr,
value: controller.getDeviceId(),
isDarkMode: isDarkMode,
),
SizedBox(height: 8.h),
// 固件版本
_buildInfoRow(
icon: Icons.info_outline,
label: 'firmwareVersion'.tr,
value: controller.firmwareVersion.value,
isDarkMode: isDarkMode,
),
// 连接状态标签
// SizedBox(height: 10.h),
// Container(
// padding: EdgeInsets.symmetric(horizontal: 8.w, vertical: 4.h),
// decoration: BoxDecoration(
// color: controller.getConnectionStatusColor().withOpacity(0.1),
// borderRadius: BorderRadius.circular(6.r),
// border: Border.all(
// color: controller.getConnectionStatusColor().withOpacity(0.3),
// width: 1,
// ),
// ),
// child: Row(
// mainAxisSize: MainAxisSize.min,
// children: [
// Icon(
// Icons.check_circle_outline,
// size: 12.sp,
// color: controller.getConnectionStatusColor(),
// ),
// SizedBox(width: 4.w),
// Text(
// controller.getConnectionStatusText(),
// style: TextStyle(
// fontSize: 11.sp,
// color: controller.getConnectionStatusColor(),
// fontWeight: FontWeight.w500,
// ),
// ),
// ],
// ),
// ),
],
),
);
}
// 信息行
Widget _buildInfoRow({
required IconData icon,
required String label,
required String value,
required bool isDarkMode,
}) {
return Row(
children: [
Icon(
icon,
size: 14.sp,
color: isDarkMode ? Colors.grey[400] : Colors.grey[600],
),
SizedBox(width: 6.w),
Text(
'$label: ',
style: TextStyle(
fontSize: 11.sp,
color: isDarkMode ? Colors.grey[400] : Colors.grey[600],
),
),
Expanded(
child: Text(
value.isEmpty ? 'unknown'.tr : value,
style: TextStyle(
fontSize: 11.sp,
fontWeight: FontWeight.w500,
color: isDarkMode ? Colors.grey[300] : Colors.grey[700],
),
overflow: TextOverflow.ellipsis,
),
),
],
);
}
// 未连接信息
Widget _buildDisconnectedInfo(bool isDarkMode) {

1158
lib/modules/pairing/controllers/pairing_controller.dart

File diff suppressed because it is too large

192
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<PairingController> {
const PairingView({Key? key}) : super(key: key);
@ -97,28 +95,25 @@ class PairingView extends GetView<PairingController> {
// 设备动画 - 根据状态显示不同的动画效果
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,24 @@ class PairingView extends GetView<PairingController> {
// 根据状态获取图标
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.associating:
return Icons.link;
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;
case ErrorType.associationFailed:
return Icons.link_off;
default:
return Icons.error_outline;
}
default:
return Icons.headphones;
}
@ -258,8 +258,8 @@ class PairingView extends GetView<PairingController> {
// 状态信息
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 +289,9 @@ class PairingView extends GetView<PairingController> {
// 当前连接设备(有设备时才显示)
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 +302,43 @@ class PairingView extends GetView<PairingController> {
// 根据状态获取副标题
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.associating:
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.associationFailed:
return '设备关联失败,请重试';
case ErrorType.bleTimeout:
return '扫描超时,请确保设备在附近';
case ErrorType.networkError:
return '网络错误,请检查网络连接';
default:
return '配对失败,请重试';
}
default:
return '';
}
@ -411,23 +416,32 @@ class PairingView extends GetView<PairingController> {
);
}
// 获取连接按钮图标
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 +455,10 @@ class PairingView extends GetView<PairingController> {
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 +468,12 @@ class PairingView extends GetView<PairingController> {
),
),
// 原有的状态按钮(错误处理等)
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 +483,7 @@ class PairingView extends GetView<PairingController> {
minimumSize: Size(double.infinity, 56.h),
),
child: Text(
actionButtonText,
controller.retryButtonText.value,
style: TextStyle(
fontSize: 16.sp,
fontWeight: FontWeight.w600,
@ -478,11 +491,11 @@ class PairingView extends GetView<PairingController> {
),
),
// 跳过按钮和重置按钮
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 +511,10 @@ class PairingView extends GetView<PairingController> {
),
],
// 添加重置按钮(在错误状态下)
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);
}
}
}

258
lib/modules/pairing/views/pairing_view_new.dart

@ -0,0 +1,258 @@
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<PairingController> {
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<Color>(
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.associating:
iconData = Icons.link;
iconColor = Colors.blue;
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,
),
),
),
);
}
}

4
local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/AgentService.kt

@ -612,7 +612,7 @@ object AgentService : CoroutineScope {
val responseBuilder = StringBuilder()
var aiMetadata:String = ""
var userMetadata = JSONObject()
var broadcast = false //是否播发
var broadcast = true //是否播发
// 如果有图片,添加图片信息到元数据
if (hasImage) {
userMetadata.put("hasImage", true)
@ -631,7 +631,7 @@ object AgentService : CoroutineScope {
)
val _systemPrompt = Utils.fillTemplate(systemPrompt,location)
messagesWithSystemPrompt.put(createSystemMessage(_systemPrompt))
Log.d(TAG, "liwei--------- 系统提示词: ${_systemPrompt}")
// Log.d(TAG, "liwei--------- 系统提示词: ${_systemPrompt}")
}
// 再添加历史消息

2
local_plugins/ble_service/android/src/main/kotlin/com/yunqiinnovation/ble_service/BleCompanionManager.kt

@ -391,7 +391,7 @@ class BleCompanionManager(private val context: Context) {
Log.e(TAG, "注册后台扫描失败: 无法获取蓝牙扫描器")
return
}
unregisterBackgroundScan()
// 构建过滤器
val filter = ScanFilter.Builder()
.setDeviceAddress(mac)

2
test.json

@ -15,7 +15,7 @@ adb shell am broadcast \
adb shell dumpsys package com.yunqiinnovation.deepsound | grep receiver
adb logcat -v time | grep -i BleForegroundService
adb logcat -v time | grep -Ei "BleWakeReceiver|BleMediaService"

Loading…
Cancel
Save