Browse Source

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

newdev_shunjiawei
wolfplus 1 year ago
parent
commit
eecfef18ae
  1. 86
      lib/modules/agent/views/message_bubble.dart
  2. 1
      lib/modules/home/bindings/home_binding.dart
  3. 382
      lib/modules/home/views/home_view.dart
  4. 8
      lib/modules/ota_upgrade/controllers/ota_upgrade_controller.dart
  5. 377
      lib/modules/ota_upgrade/views/ota_upgrade_view.dart
  6. 23
      lib/modules/profile/controllers/profile_controller.dart
  7. 309
      lib/modules/profile/views/profile_view.dart
  8. 365
      lib/modules/settings/views/settings_view.dart
  9. 14
      lib/routes/app_pages.dart
  10. 61
      lib/routes/app_routes.dart
  11. 2
      local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/BleAgent.kt
  12. 1
      local_plugins/azure_speech/android/build.gradle.kts
  13. 58
      local_plugins/azure_speech/android/src/main/kotlin/com/yunqiinnovation/azure_speech/AzureSpeechPlugin.kt
  14. 1
      local_plugins/ble_service/android/build.gradle.kts
  15. 8
      local_plugins/ble_service/android/src/main/kotlin/com/yunqiinnovation/ble_service/BleService.kt
  16. 4
      local_plugins/ble_service/android/src/main/kotlin/com/yunqiinnovation/ble_service/BleServicePlugin.kt

86
lib/modules/agent/views/message_bubble.dart

@ -217,7 +217,7 @@ class MessageBubble extends GetView<AgentController> {
final rawList = mate!['card_weather']['dayweather'] as List; final rawList = mate!['card_weather']['dayweather'] as List;
final daylist = final daylist =
rawList.map((e) => Map<String, dynamic>.from(e)).toList(); rawList.map((e) => Map<String, dynamic>.from(e)).toList();
final now = daylist[0]; final now = mate!['card_weather']['day'] as Map<String, dynamic>;
//天气 //天气
return _buildWeatherContent( return _buildWeatherContent(
context, context,
@ -677,7 +677,7 @@ class MessageBubble extends GetView<AgentController> {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text( Text(
'当前天气情况:', '查询天气情况:',
style: TextStyle( style: TextStyle(
color: Colors.black87, color: Colors.black87,
fontSize: 14, fontSize: 14,
@ -714,7 +714,7 @@ class MessageBubble extends GetView<AgentController> {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text( Text(
city, "${city} ${now["date"]}",
style: TextStyle( style: TextStyle(
fontSize: 16, fontSize: 16,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
@ -846,46 +846,46 @@ class MessageBubble extends GetView<AgentController> {
], ],
), ),
), ),
const SizedBox(height: 10), // const SizedBox(height: 10),
Row( // Row(
mainAxisSize: MainAxisSize.min, // mainAxisSize: MainAxisSize.min,
children: [ // children: [
Expanded( // Expanded(
child: OutlinedButton.icon( // child: OutlinedButton.icon(
onPressed: () {}, // onPressed: () {},
icon: Icon(Icons.location_on, size: 16), // icon: Icon(Icons.location_on, size: 16),
label: Text('更多地区'), // label: Text('更多地区'),
style: OutlinedButton.styleFrom( // style: OutlinedButton.styleFrom(
foregroundColor: Theme.of(context).primaryColor, // foregroundColor: Theme.of(context).primaryColor,
side: BorderSide( // side: BorderSide(
color: Theme.of(context).primaryColor.withOpacity(0.5)), // color: Theme.of(context).primaryColor.withOpacity(0.5)),
padding: EdgeInsets.symmetric(horizontal: 12, vertical: 4), // padding: EdgeInsets.symmetric(horizontal: 12, vertical: 4),
shape: RoundedRectangleBorder( // shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16), // borderRadius: BorderRadius.circular(16),
), // ),
textStyle: TextStyle(fontSize: 12), // textStyle: TextStyle(fontSize: 12),
), // ),
), // ),
), // ),
SizedBox(width: 8), // SizedBox(width: 8),
Expanded( // Expanded(
child: OutlinedButton.icon( // child: OutlinedButton.icon(
onPressed: () {}, // onPressed: () {},
icon: Icon(Icons.calendar_today, size: 16), // icon: Icon(Icons.calendar_today, size: 16),
label: Text('15天预报'), // label: Text('15天预报'),
style: OutlinedButton.styleFrom( // style: OutlinedButton.styleFrom(
foregroundColor: Colors.grey[700], // foregroundColor: Colors.grey[700],
side: BorderSide(color: Colors.grey[400]!), // side: BorderSide(color: Colors.grey[400]!),
padding: EdgeInsets.symmetric(horizontal: 12, vertical: 4), // padding: EdgeInsets.symmetric(horizontal: 12, vertical: 4),
shape: RoundedRectangleBorder( // shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16), // borderRadius: BorderRadius.circular(16),
), // ),
textStyle: TextStyle(fontSize: 12), // textStyle: TextStyle(fontSize: 12),
), // ),
), // ),
), // ),
], // ],
), // ),
], ],
); );
} }

1
lib/modules/home/bindings/home_binding.dart

@ -1,6 +1,5 @@
import '../../explore/controllers/explore_controller.dart'; import '../../explore/controllers/explore_controller.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import '../../explore/controllers/explore_controller.dart';
import '../controllers/home_controller.dart'; import '../controllers/home_controller.dart';
class HomeBinding extends Bindings { class HomeBinding extends Bindings {

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

@ -1,17 +1,17 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'dart:ui';
import '../controllers/home_controller.dart'; import '../controllers/home_controller.dart';
import '../../../routes/app_pages.dart'; import '../../../routes/app_pages.dart';
import '../../../routes/app_routes.dart'; import '../../../routes/app_routes.dart';
import '../../../core/widgets/common_bottom_nav.dart'; import '../../../core/widgets/common_bottom_nav.dart';
// 导入枚举 // 导入枚举
import '../controllers/home_controller.dart' show ConnectionStatus; import '../controllers/home_controller.dart' show ConnectionStatus;
// 呼吸光动画组件 // 呼吸灯动画组件
class AnimatedPulsing extends StatefulWidget { class AnimatedPulsing extends StatefulWidget {
const AnimatedPulsing({Key? key}) : super(key: key);
@override @override
_AnimatedPulsingState createState() => _AnimatedPulsingState(); _AnimatedPulsingState createState() => _AnimatedPulsingState();
} }
@ -25,14 +25,11 @@ class _AnimatedPulsingState extends State<AnimatedPulsing>
void initState() { void initState() {
super.initState(); super.initState();
_controller = AnimationController( _controller = AnimationController(
duration: Duration(seconds: 2), duration: const Duration(seconds: 2),
vsync: this, vsync: this,
)..repeat(reverse: true); )..repeat(reverse: true);
_animation = Tween<double>(begin: 0.5, end: 0.8).animate( _animation = Tween<double>(begin: 0.5, end: 0.8).animate(
CurvedAnimation( CurvedAnimation(parent: _controller, curve: Curves.easeInOut),
parent: _controller,
curve: Curves.easeInOut,
),
); );
} }
@ -49,36 +46,36 @@ class _AnimatedPulsingState extends State<AnimatedPulsing>
builder: (context, child) { builder: (context, child) {
return Opacity( return Opacity(
opacity: _animation.value, opacity: _animation.value,
child: Container( child: child,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(16.r),
gradient: RadialGradient(
colors: [
Colors.white.withOpacity(0.3),
Colors.transparent,
],
center: Alignment.topRight,
radius: 0.8,
stops: [0.0, 1.0],
),
),
),
); );
}, },
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(16.r),
gradient: RadialGradient(
colors: [
Colors.white.withOpacity(0.3),
Colors.transparent,
],
center: Alignment.topRight,
radius: 0.8,
stops: const [0.0, 1.0],
),
),
),
); );
} }
} }
//首页视图 // 首页视图
class HomeView extends GetView<HomeController> { class HomeView extends GetView<HomeController> {
const HomeView({Key? key}) : super(key: key); const HomeView({Key? key}) : super(key: key);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final bool isDarkMode = Theme.of(context).brightness == Brightness.dark; final bool isDarkMode = Theme.of(context).brightness == Brightness.dark;
return Scaffold( return Scaffold(
backgroundColor: isDarkMode ? Colors.black : Color(0xFFF9FAFC), backgroundColor: isDarkMode ? Colors.black : const Color(0xFFF9FAFC),
body: SafeArea( body: SafeArea(
child: SingleChildScrollView( child: SingleChildScrollView(
padding: EdgeInsets.all(16.w), padding: EdgeInsets.all(16.w),
@ -210,7 +207,7 @@ class HomeView extends GetView<HomeController> {
Widget _buildDeviceStatusCard(bool isDarkMode) { Widget _buildDeviceStatusCard(bool isDarkMode) {
return Container( return Container(
decoration: BoxDecoration( decoration: BoxDecoration(
color: isDarkMode ? Color(0xFF1A1A1A) : Colors.white, color: isDarkMode ? const Color(0xFF1A1A1A) : Colors.white,
borderRadius: BorderRadius.circular(16.r), borderRadius: BorderRadius.circular(16.r),
boxShadow: [ boxShadow: [
BoxShadow( BoxShadow(
@ -229,20 +226,22 @@ class HomeView extends GetView<HomeController> {
// 设备信息 // 设备信息
SizedBox(height: 12.h), SizedBox(height: 12.h),
Obx(() { Obx(() {
print('连接状态: ${controller.connectionStatus}'); // 添加调试打印
print('错误信息: ${controller.connectionErrorMsg}'); // 添加调试打印
// 如果连接失败且有错误信息,显示错误提示 // 如果连接失败且有错误信息,显示错误提示
if (controller.connectionStatus == ConnectionStatus.failed && if (controller.connectionStatus == ConnectionStatus.failed &&
controller.connectionErrorMsg.isNotEmpty) { controller.connectionErrorMsg.isNotEmpty) {
return _buildConnectionErrorSection(isDarkMode); return _buildConnectionErrorSection(isDarkMode); //错误提示
} }
// 如果正在连接中,显示连接中提示 // 如果正在连接中,显示连接中提示
if (controller.connectionStatus == ConnectionStatus.connecting) { if (controller.connectionStatus == ConnectionStatus.connecting) {
return _buildConnectingIndicator(); return _buildConnectingIndicator(); //连接中提示
} }
return controller.isConnected return controller.isConnected
? _buildConnectedDeviceInfo(isDarkMode) ? _buildConnectedDeviceInfo(isDarkMode) //已连接设备信息
: _buildDisconnectedInfo(isDarkMode); : _buildDisconnectedInfo(isDarkMode); //未连接提示
}), }),
// 连接按钮(仅在未连接时显示) // 连接按钮(仅在未连接时显示)
@ -285,6 +284,7 @@ class HomeView extends GetView<HomeController> {
Obx(() => Row( Obx(() => Row(
children: [ children: [
Container( Container(
//状态指示灯
width: 8.w, width: 8.w,
height: 8.w, height: 8.w,
decoration: BoxDecoration( decoration: BoxDecoration(
@ -344,7 +344,8 @@ class HomeView extends GetView<HomeController> {
isDarkMode ? Colors.red[900]!.withOpacity(0.2) : Colors.red[50], isDarkMode ? Colors.red[900]!.withOpacity(0.2) : Colors.red[50],
borderRadius: BorderRadius.circular(12.r), borderRadius: BorderRadius.circular(12.r),
border: Border.all( border: Border.all(
color: isDarkMode ? Colors.red[800]! : Colors.red[200]!), color: isDarkMode ? Colors.red[800]! : Colors.red[200]!,
),
), ),
child: Row( child: Row(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
@ -410,7 +411,7 @@ class HomeView extends GetView<HomeController> {
), ),
SizedBox(height: 12.h), SizedBox(height: 12.h),
Text( Text(
'connectingDevice'.tr, // 正在连接设备... 'connectingDevice'.tr,
style: TextStyle( style: TextStyle(
fontSize: 13.sp, fontSize: 13.sp,
color: Colors.blue, color: Colors.blue,
@ -423,13 +424,45 @@ class HomeView extends GetView<HomeController> {
); );
} }
// 已连接设备信息 // 已连接设备信息(新增设备名称 + 信号强度 + 电池信息)
Widget _buildConnectedDeviceInfo(bool isDarkMode) { Widget _buildConnectedDeviceInfo(bool isDarkMode) {
return Column( return Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
// 电池状态网格布局 Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Text(
'${'connectedTo'.tr}:${controller.getDeviceName()}',
style: TextStyle(
fontSize: 13.sp,
fontWeight: FontWeight.w500,
color: isDarkMode ? Colors.grey[300] : Colors.grey[700],
),
overflow: TextOverflow.ellipsis,
),
),
Container(
padding: EdgeInsets.symmetric(horizontal: 6.w, vertical: 2.h),
decoration: BoxDecoration(
color: Colors.blue.withOpacity(0.1),
borderRadius: BorderRadius.circular(4.r),
),
child: Text(
'${'signal'.tr}: ${controller.getSignalStrength()}',
style: TextStyle(
fontSize: 11.sp,
color: Colors.blue[700],
),
),
),
],
),
SizedBox(height: 12.h),
_buildBatteryInfoGrid(isDarkMode), _buildBatteryInfoGrid(isDarkMode),
SizedBox(height: 16.h),
_buildDeviceDetailsCard(isDarkMode),
], ],
); );
} }
@ -441,53 +474,66 @@ class HomeView extends GetView<HomeController> {
final leftBattery = controller.leftEarBattery; final leftBattery = controller.leftEarBattery;
final rightBattery = controller.rightEarBattery; final rightBattery = controller.rightEarBattery;
final caseBattery = controller.caseBattery; final caseBattery = controller.caseBattery;
// 获取充电状态 // 获取充电状态
final leftEarCharging = controller.leftEarCharging; final leftEarCharging = controller.leftEarCharging;
final rightEarCharging = controller.rightEarCharging; final rightEarCharging = controller.rightEarCharging;
final caseCharging = controller.caseCharging; final caseCharging = controller.caseCharging;
return GridView.count( // 使用更紧凑的布局
crossAxisCount: 3, return Container(
shrinkWrap: true, padding: EdgeInsets.symmetric(vertical: 8.h, horizontal: 4.w),
physics: NeverScrollableScrollPhysics(), decoration: BoxDecoration(
crossAxisSpacing: 12.w, color: isDarkMode ? Colors.grey[900] : Colors.grey[50],
childAspectRatio: 0.85, borderRadius: BorderRadius.circular(12.r),
children: [ ),
// 左耳机 child: Row(
_buildBatteryGridItem( mainAxisAlignment: MainAxisAlignment.spaceEvenly,
icon: Icons.headphones, children: [
label: '左耳机', // 左耳机
batteryLevel: leftBattery.value, _buildCompactBatteryItem(
isCharging: leftEarCharging.value, label: '左',
isDarkMode: isDarkMode, batteryLevel: leftBattery.value,
), isCharging: leftEarCharging.value,
isDarkMode: isDarkMode,
),
// 右耳机 // 分隔线
_buildBatteryGridItem( Container(
icon: Icons.headphones, height: 30.h,
label: '右耳机', width: 1,
batteryLevel: rightBattery.value, color: isDarkMode ? Colors.grey[800] : Colors.grey[300],
isCharging: rightEarCharging.value, ),
isDarkMode: isDarkMode,
),
// 充电盒 // 右耳机
_buildBatteryGridItem( _buildCompactBatteryItem(
icon: Icons.charging_station, label: '右',
label: '充电盒', batteryLevel: rightBattery.value,
batteryLevel: caseBattery.value, isCharging: rightEarCharging.value,
isCharging: caseCharging.value, isDarkMode: isDarkMode,
isDarkMode: isDarkMode, ),
),
], // 分隔线
Container(
height: 30.h,
width: 1,
color: isDarkMode ? Colors.grey[800] : Colors.grey[300],
),
// 充电盒
_buildCompactBatteryItem(
label: '充电盒',
batteryLevel: caseBattery.value,
isCharging: caseCharging.value,
isDarkMode: isDarkMode,
),
],
),
); );
}); });
} }
// 单个电池网格项 // 紧凑型电池项
Widget _buildBatteryGridItem({ Widget _buildCompactBatteryItem({
required IconData icon,
required String label, required String label,
required int batteryLevel, required int batteryLevel,
required bool isDarkMode, required bool isDarkMode,
@ -496,7 +542,6 @@ class HomeView extends GetView<HomeController> {
// 获取电池图标和颜色 // 获取电池图标和颜色
IconData batteryIcon; IconData batteryIcon;
Color batteryColor; Color batteryColor;
if (batteryLevel > 75) { if (batteryLevel > 75) {
batteryIcon = Icons.battery_full; batteryIcon = Icons.battery_full;
batteryColor = Colors.green[600]!; batteryColor = Colors.green[600]!;
@ -505,7 +550,7 @@ class HomeView extends GetView<HomeController> {
batteryColor = Colors.green[600]!; batteryColor = Colors.green[600]!;
} else if (batteryLevel > 25) { } else if (batteryLevel > 25) {
batteryIcon = Icons.battery_4_bar; batteryIcon = Icons.battery_4_bar;
batteryColor = Colors.yellow[600]!; batteryColor = Colors.orange[600]!;
} else if (batteryLevel > 10) { } else if (batteryLevel > 10) {
batteryIcon = Icons.battery_3_bar; batteryIcon = Icons.battery_3_bar;
batteryColor = Colors.orange[600]!; batteryColor = Colors.orange[600]!;
@ -514,80 +559,52 @@ class HomeView extends GetView<HomeController> {
batteryColor = Colors.red[600]!; batteryColor = Colors.red[600]!;
} }
return Container( return Expanded(
decoration: BoxDecoration( child: Padding(
color: isDarkMode ? Colors.grey[900] : Colors.grey[100], padding: EdgeInsets.symmetric(vertical: 6.h),
borderRadius: BorderRadius.circular(12.r), child: Column(
boxShadow: [ mainAxisSize: MainAxisSize.min,
BoxShadow( children: [
color: isDarkMode // 标签
? Colors.black.withOpacity(0.1) Text(
: Colors.grey.withOpacity(0.1), label,
blurRadius: 4, style: TextStyle(
spreadRadius: 0, fontSize: 11.sp,
offset: Offset(0, 2), color: isDarkMode ? Colors.grey[400] : Colors.grey[500],
), fontWeight: FontWeight.normal,
], ),
), textAlign: TextAlign.center,
padding: EdgeInsets.all(10.w),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// 设备图标
Container(
padding: EdgeInsets.all(8.w),
decoration: BoxDecoration(
shape: BoxShape.circle,
),
child: Icon(
icon,
size: 24.sp,
color: isDarkMode ? Colors.grey[300] : Colors.grey[800],
),
),
SizedBox(height: 6.h),
// 标签
Text(
label,
style: TextStyle(
fontSize: 12.sp,
color: isDarkMode ? Colors.grey[400] : Colors.grey[500],
fontWeight: FontWeight.normal,
), ),
textAlign: TextAlign.center, SizedBox(height: 4.h),
),
SizedBox(height: 6.h),
// 电池指示器 // 电池指示器
Row( Row(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
Icon( Icon(
batteryIcon, batteryIcon,
size: 14.sp, size: 12.sp,
color: batteryColor,
),
SizedBox(width: 4.w),
Text(
'$batteryLevel%',
style: TextStyle(
fontSize: 12.sp,
fontWeight: FontWeight.w500,
color: batteryColor, color: batteryColor,
), ),
),
if (isCharging) ...[
SizedBox(width: 2.w), SizedBox(width: 2.w),
Icon( Text(
Icons.bolt, '$batteryLevel%',
size: 12.sp, style: TextStyle(
color: Colors.green[600], fontSize: 11.sp,
fontWeight: FontWeight.w500,
color: batteryColor,
),
), ),
if (isCharging)
Icon(
Icons.bolt,
size: 10.sp,
color: Colors.green[600],
),
], ],
], ),
), ],
], ),
), ),
); );
} }
@ -625,37 +642,37 @@ class HomeView extends GetView<HomeController> {
), ),
// 连接状态标签 // 连接状态标签
SizedBox(height: 10.h), // SizedBox(height: 10.h),
Container( // Container(
padding: EdgeInsets.symmetric(horizontal: 8.w, vertical: 4.h), // padding: EdgeInsets.symmetric(horizontal: 8.w, vertical: 4.h),
decoration: BoxDecoration( // decoration: BoxDecoration(
color: controller.getConnectionStatusColor().withOpacity(0.1), // color: controller.getConnectionStatusColor().withOpacity(0.1),
borderRadius: BorderRadius.circular(6.r), // borderRadius: BorderRadius.circular(6.r),
border: Border.all( // border: Border.all(
color: controller.getConnectionStatusColor().withOpacity(0.3), // color: controller.getConnectionStatusColor().withOpacity(0.3),
width: 1, // width: 1,
), // ),
), // ),
child: Row( // child: Row(
mainAxisSize: MainAxisSize.min, // mainAxisSize: MainAxisSize.min,
children: [ // children: [
Icon( // Icon(
Icons.check_circle_outline, // Icons.check_circle_outline,
size: 12.sp, // size: 12.sp,
color: controller.getConnectionStatusColor(), // color: controller.getConnectionStatusColor(),
), // ),
SizedBox(width: 4.w), // SizedBox(width: 4.w),
Text( // Text(
controller.getConnectionStatusText(), // controller.getConnectionStatusText(),
style: TextStyle( // style: TextStyle(
fontSize: 11.sp, // fontSize: 11.sp,
color: controller.getConnectionStatusColor(), // color: controller.getConnectionStatusColor(),
fontWeight: FontWeight.w500, // fontWeight: FontWeight.w500,
), // ),
), // ),
], // ],
), // ),
), // ),
], ],
), ),
); );
@ -701,33 +718,34 @@ class HomeView extends GetView<HomeController> {
// 未连接信息 // 未连接信息
Widget _buildDisconnectedInfo(bool isDarkMode) { Widget _buildDisconnectedInfo(bool isDarkMode) {
return Container( return Container(
padding: EdgeInsets.symmetric(vertical: 16.h, horizontal: 12.w), padding: EdgeInsets.symmetric(vertical: 10.h, horizontal: 12.w),
alignment: Alignment.center, alignment: Alignment.center,
decoration: BoxDecoration( decoration: BoxDecoration(
color: isDarkMode ? Colors.grey[900] : Colors.grey[100], color: isDarkMode ? Colors.grey[900] : Colors.grey[100],
borderRadius: BorderRadius.circular(12.r), borderRadius: BorderRadius.circular(10.r),
border: Border.all( border: Border.all(
color: isDarkMode ? Colors.grey[800]! : Colors.grey[300]!, color: isDarkMode ? Colors.grey[800]! : Colors.grey[300]!,
width: 1, width: 1,
), ),
), ),
child: Column( child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
Icon( Icon(
Icons.bluetooth_disabled, Icons.bluetooth_disabled,
size: 32.sp, size: 14.sp,
color: isDarkMode ? Colors.grey[600] : Colors.grey[500], color: isDarkMode ? Colors.grey[400] : Colors.grey[700],
), ),
SizedBox(height: 12.h), SizedBox(height: 12.h),
Text( Text(
'disconnectedDevice'.tr, // 未连接设备,点击下方按钮连接 'disconnectedDevice'.tr, // 未连接设备,点击下方按钮连接
style: TextStyle( style: TextStyle(
fontSize: 13.sp, fontSize: 12.sp,
color: isDarkMode ? Colors.grey[400] : Colors.grey[700], color: isDarkMode ? Colors.grey[400] : Colors.grey[700],
height: 1.4, // height: 1.4,
fontWeight: FontWeight.w500, // fontWeight: FontWeight.w500,
), ),
textAlign: TextAlign.center, //textAlign: TextAlign.center,
), ),
], ],
), ),
@ -750,7 +768,7 @@ class HomeView extends GetView<HomeController> {
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
Icon(Icons.bluetooth_searching, size: 18.sp), Icon(Icons.bluetooth_searching, size: 16.sp),
SizedBox(width: 8.w), SizedBox(width: 8.w),
Text( Text(
'connectDevice'.tr, // 连接设备 'connectDevice'.tr, // 连接设备
@ -832,8 +850,8 @@ class HomeView extends GetView<HomeController> {
begin: Alignment.topLeft, begin: Alignment.topLeft,
end: Alignment.bottomRight, end: Alignment.bottomRight,
colors: [ colors: [
Color(0xFF4CAF50).withOpacity(0.3), const Color(0xFF4CAF50).withOpacity(0.3),
Color(0xFF009688).withOpacity(0.3), const Color(0xFF009688).withOpacity(0.3),
], ],
), ),
onTap: controller.openTranslation, onTap: controller.openTranslation,

8
lib/modules/ota_upgrade/controllers/ota_upgrade_controller.dart

@ -73,10 +73,8 @@ class OtaUpgradeController extends GetxController {
} }
// 检查固件信息有效性 // 检查固件信息有效性
if (deviceInfo.firmwareVersion.isEmpty if (deviceInfo.firmwareVersion.isEmpty ||
//|| deviceInfo.firmwareProductId.isEmpty) {
// deviceInfo.firmwareProductId.isEmpty
) {
throw const FormatException('设备固件信息不完整'); throw const FormatException('设备固件信息不完整');
} }
@ -85,7 +83,7 @@ class OtaUpgradeController extends GetxController {
// API请求 // API请求
final response = await Api.getequipmentinfo({ final response = await Api.getequipmentinfo({
"id": "a2", "id": deviceInfo.firmwareProductId,
}); });
// 处理API响应 // 处理API响应

377
lib/modules/ota_upgrade/views/ota_upgrade_view.dart

@ -1,129 +1,326 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import '../controllers/ota_upgrade_controller.dart'; import '../controllers/ota_upgrade_controller.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
class OtaUpgradeView extends GetView<OtaUpgradeController> { class OtaUpgradeView extends GetView<OtaUpgradeController> {
const OtaUpgradeView({Key? key}) : super(key: key); const OtaUpgradeView({Key? key}) : super(key: key);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final isDarkMode = Theme.of(context).brightness == Brightness.dark;
return Scaffold( return Scaffold(
body: Center( backgroundColor: isDarkMode ? Colors.black : Colors.grey[100],
child: ConstrainedBox( appBar: AppBar(
constraints: BoxConstraints(maxWidth: 600), // 限制最大宽度 automaticallyImplyLeading: false,
child: SingleChildScrollView( centerTitle: true,
child: Padding( elevation: 0,
padding: EdgeInsets.all(20), backgroundColor: isDarkMode ? Colors.black : Colors.white,
child: Column( leading: IconButton(
mainAxisSize: MainAxisSize.min, icon: Icon(
children: [ Icons.arrow_back_ios,
// 设备图标 size: 20.sp,
_buildDeviceIcon(), color: isDarkMode ? Colors.white : Colors.black,
SizedBox(height: 30), ),
onPressed: () => Get.back(),
),
title: Text(
'固件升级'.tr,
style: TextStyle(
fontSize: 20.sp,
color: isDarkMode ? Colors.white : Colors.black,
),
),
),
body: SingleChildScrollView(
child: Padding(
padding: EdgeInsets.all(20),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
// 设备信息卡片
Card(
elevation: 2,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
),
color: isDarkMode ? Colors.grey[900] : Colors.white,
child: Padding(
padding: EdgeInsets.all(20),
child: Column(
children: [
_buildDeviceIcon(isDarkMode),
SizedBox(height: 20),
_buildVersionInfo(isDarkMode),
],
),
),
),
SizedBox(height: 20),
// 版本信息 // 更新说明
_buildVersionInfo(), _buildUpdateNotes(isDarkMode),
SizedBox(height: 40), SizedBox(height: 20),
// 状态显示区域 // 状态显示区域
Obx(() { Obx(() {
if (controller.isFetchingDeviceInfo.value) { if (controller.isFetchingDeviceInfo.value) {
return _buildLoadingIndicator(); return _buildLoadingIndicator(isDarkMode);
} }
if (controller.errorMessage.value.isNotEmpty) { if (controller.errorMessage.value.isNotEmpty) {
return _buildErrorInfo(); return _buildErrorInfo(isDarkMode);
} }
return _buildUpdateButton(); return _buildUpdateButton(isDarkMode);
}), }),
], ],
),
),
), ),
), ),
), ),
); );
} }
// 组件化构建方法 // 设备图标
Widget _buildDeviceIcon() => CircleAvatar( Widget _buildDeviceIcon(bool isDarkMode) => Image.asset(
radius: 60, isDarkMode
backgroundColor: Colors.blue[100], ? 'assets/images/headphone_dark.png'
child: Icon(Icons.phone_iphone, size: 60, color: Colors.blue), : 'assets/images/headphone_light.png',
height: 180.h,
fit: BoxFit.contain,
); );
Widget _buildVersionInfo() => Column( Widget _buildVersionInfo(bool isDarkMode) => Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text('当前版本', style: TextStyle(fontSize: 18, color: Colors.grey)), Row(
SizedBox(height: 8), crossAxisAlignment: CrossAxisAlignment.center, // 改为center对齐
Text('v${controller.currentVersion}', children: [
style: TextStyle( Text('当前固件版本:'.tr,
fontSize: 24, style: TextStyle(
fontWeight: FontWeight.bold, fontSize: 18,
color: Colors.blue)), color: isDarkMode ? Colors.grey[300] : Colors.black)),
SizedBox(width: 8), // 改为width
Obx(() => Text('v${controller.currentVersion}',
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.normal,
color: isDarkMode ? Colors.blue[300] : Colors.blue))),
],
),
SizedBox(height: 8), // 改为height
Row(
crossAxisAlignment: CrossAxisAlignment.center, // 改为center对齐
children: [
Text('最新固件版本:'.tr,
style: TextStyle(
fontSize: 18,
color: isDarkMode ? Colors.grey[300] : Colors.black)),
SizedBox(width: 8), // 改为width
Text('v${controller.latestVersion}',
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.normal,
color: isDarkMode ? Colors.green[300] : Colors.green)),
],
),
], ],
); );
Widget _buildLoadingIndicator() => Column( // 添加固件更新说明
Widget _buildUpdateNotes(bool isDarkMode) =>
Obx(() => controller.isUpdateAvailable.value
? Card(
elevation: 2,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
),
color: isDarkMode ? Colors.grey[900] : Colors.white,
child: Padding(
padding: EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Icon(
Icons.new_releases,
color: isDarkMode ? Colors.amber[300] : Colors.amber,
size: 20,
),
SizedBox(width: 8),
Text(
'更新内容'.tr,
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: isDarkMode ? Colors.white : Colors.black,
),
),
],
),
Divider(),
SizedBox(height: 8),
// 这里可以从controller中获取更新说明,如果有的话
Text(
'1. 优化音质表现\n2. 提升连接稳定性\n3. 修复已知问题'.tr,
style: TextStyle(
fontSize: 14,
color: isDarkMode ? Colors.grey[300] : Colors.grey[800],
),
),
],
),
),
)
: SizedBox.shrink());
Widget _buildLoadingIndicator(bool isDarkMode) => Column(
children: [ children: [
CircularProgressIndicator(), CircularProgressIndicator(
valueColor: AlwaysStoppedAnimation(
isDarkMode ? Colors.blue[300] : Colors.blue),
),
SizedBox(height: 20), SizedBox(height: 20),
Text('正在连接设备...', style: TextStyle(color: Colors.grey)) Text('正在连接设备...',
style:
TextStyle(color: isDarkMode ? Colors.grey[300] : Colors.grey))
], ],
); );
Widget _buildErrorInfo() => Column( Widget _buildErrorInfo(bool isDarkMode) => Container(
children: [ padding: EdgeInsets.all(16),
Icon(Icons.error_outline, color: Colors.red, size: 40), decoration: BoxDecoration(
SizedBox(height: 10), color:
Text(controller.errorMessage.value, isDarkMode ? Colors.red[900]!.withOpacity(0.2) : Colors.red[50],
style: TextStyle(color: Colors.red)), borderRadius: BorderRadius.circular(12),
SizedBox(height: 20), border: Border.all(
ElevatedButton( color: isDarkMode ? Colors.red[700]! : Colors.red[200]!,
onPressed: controller.retryConnection, width: 1,
child: Text('重试连接'), ),
) ),
], child: Column(
children: [
Icon(Icons.error_outline, color: Colors.red, size: 40),
SizedBox(height: 10),
Text(controller.errorMessage.value,
textAlign: TextAlign.center,
style: TextStyle(
color: isDarkMode ? Colors.red[300] : Colors.red,
fontSize: 16)),
SizedBox(height: 20),
ElevatedButton.icon(
icon: Icon(Icons.refresh),
label: Text('重试连接'.tr),
style: ElevatedButton.styleFrom(
backgroundColor: isDarkMode ? Colors.blue[700] : Colors.blue,
foregroundColor: Colors.white,
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 10),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16.r),
),
),
onPressed: controller.retryConnection,
)
],
),
); );
Widget _buildUpdateButton() { Widget _buildUpdateButton(bool isDarkMode) {
return controller.isUpdating.value return controller.isUpdating.value
? _buildProgressIndicator() ? _buildProgressIndicator(isDarkMode)
: ElevatedButton( : Container(
style: ElevatedButton.styleFrom( width: double.infinity,
backgroundColor: controller.isUpdateAvailable.value padding: EdgeInsets.symmetric(vertical: 10),
? Colors.blue child: Column(
: Colors.grey, children: [
padding: EdgeInsets.symmetric(horizontal: 40, vertical: 15), ElevatedButton.icon(
shape: RoundedRectangleBorder( icon: Icon(
borderRadius: BorderRadius.circular(30), controller.isUpdateAvailable.value
), ? Icons.system_update
), : Icons.check_circle,
onPressed: controller.isUpdateAvailable.value ),
? controller.startUpdate label: Text(
: null, controller.isUpdateAvailable.value ? '立即更新' : '已是最新版本',
child: Text( style: TextStyle(fontSize: 16),
controller.isUpdateAvailable.value ? '立即更新' : '已是最新版本', ),
style: TextStyle(fontSize: 16, color: Colors.white), style: ElevatedButton.styleFrom(
backgroundColor: controller.isUpdateAvailable.value
? (isDarkMode ? Colors.blue[700] : Colors.blue)
: (isDarkMode ? Colors.grey[800] : Colors.grey),
foregroundColor: Colors.white,
padding: EdgeInsets.symmetric(horizontal: 40, vertical: 15),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(30),
),
minimumSize: Size(240, 50),
),
onPressed: controller.isUpdateAvailable.value
? controller.startUpdate
: null,
),
if (controller.isUpdateAvailable.value) ...[
SizedBox(height: 8),
Text(
'建议连接WiFi并保持充电状态'.tr,
style: TextStyle(
fontSize: 14,
color: isDarkMode ? Colors.grey[400] : Colors.grey[600],
),
),
],
],
), ),
); );
} }
Widget _buildProgressIndicator() => Column( Widget _buildProgressIndicator(bool isDarkMode) => Container(
children: [ padding: EdgeInsets.all(20),
SizedBox( decoration: BoxDecoration(
width: 200, color:
child: LinearProgressIndicator( isDarkMode ? Colors.blue[900]!.withOpacity(0.2) : Colors.blue[50],
value: controller.downloadProgress.value, borderRadius: BorderRadius.circular(16),
backgroundColor: Colors.grey[200], ),
valueColor: AlwaysStoppedAnimation(Colors.blue), child: Column(
minHeight: 12, children: [
borderRadius: BorderRadius.circular(10), Text('正在下载更新...'.tr,
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: isDarkMode ? Colors.blue[300] : Colors.blue[700])),
SizedBox(height: 16),
SizedBox(
width: 240,
child: LinearProgressIndicator(
value: controller.downloadProgress.value,
backgroundColor:
isDarkMode ? Colors.grey[800] : Colors.grey[200],
valueColor: AlwaysStoppedAnimation(
isDarkMode ? Colors.blue[300] : Colors.blue),
minHeight: 12,
borderRadius: BorderRadius.circular(10),
),
), ),
), SizedBox(height: 16),
SizedBox(height: 20), Row(
Text( mainAxisSize: MainAxisSize.min,
'${(controller.downloadProgress.value * 100).toStringAsFixed(1)}%', children: [
style: TextStyle(color: Colors.grey)) Icon(
], Icons.downloading,
color: isDarkMode ? Colors.blue[300] : Colors.blue,
size: 20,
),
SizedBox(width: 8),
Text(
'${(controller.downloadProgress.value * 100).toStringAsFixed(1)}%',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color:
isDarkMode ? Colors.blue[300] : Colors.blue[700])),
],
)
],
),
); );
} }

23
lib/modules/profile/controllers/profile_controller.dart

@ -1,7 +1,6 @@
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:get_storage/get_storage.dart'; import 'package:get_storage/get_storage.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
//import 'package:http/http.dart';
import 'package:image_picker/image_picker.dart'; import 'package:image_picker/image_picker.dart';
import 'package:logger/logger.dart'; import 'package:logger/logger.dart';
import '../../../data/models/user_Info.dart'; import '../../../data/models/user_Info.dart';
@ -19,26 +18,17 @@ import '../../../modules/login/controllers/facebookLogin.dart';
class ProfileController extends GetxController { class ProfileController extends GetxController {
static ProfileController get to => Get.find(); static ProfileController get to => Get.find();
// final _userName = ''.obs;
// final _email = ''.obs;
// final _avatar = ''.obs;
// final _isEditing = false.obs;
// final _storage = GetStorage();
//用户信息 //用户信息
static const String userKey = 'user_info'; //用户信息存储键 static const String userKey = 'user_info'; //用户信息存储键
final _storage = GetStorage(); //本地存储 final _storage = GetStorage(); //本地存储
final ImagePicker _picker = ImagePicker(); //图片选择器 final ImagePicker _picker = ImagePicker(); //图片选择器
final Rx<User> userInfo = User.instance.obs; // 响应式用户信息对象,使用单例模式确保全局唯一性 final Rx<User> userInfo = User.instance.obs; // 响应式用户信息对象,使用单例模式确保全局唯一性
final RxString localAvatar = ''.obs; // 本地头像路径,便于响应式更新UI final RxString localAvatar = ''.obs; // 本地头像路径,便于响应式更新UI
final RxBool hasEdits = false.obs; // 是否有编辑
// 使用响应式变量 // 使用响应式变量
final currentLanguage = 'zh'.obs; final currentLanguage = 'zh'.obs;
// String get userName => _userName.value;
// String get email => _email.value;
// String get avatar => _avatar.value;
// bool get isEditing => _isEditing.value;
@override @override
void onInit() { void onInit() {
super.onInit(); super.onInit();
@ -46,16 +36,6 @@ class ProfileController extends GetxController {
loadLanguagePreference(); loadLanguagePreference();
} }
// void loadUserData() {
// try {
// _userName.value = _storage.read('userName') ?? 'User Name';
// _email.value = _storage.read('userEmail') ?? 'user@example.com';
// _avatar.value = _storage.read('userAvatar') ?? '';
// } catch (e) {
// Get.snackbar('Error', 'Failed to load user data');
// }
// }
// 加载用户语言偏好设置 // 加载用户语言偏好设置
void loadLanguagePreference() { void loadLanguagePreference() {
try { try {
@ -332,6 +312,7 @@ class ProfileController extends GetxController {
// if (Get.isRegistered<SettingsController>()) { // if (Get.isRegistered<SettingsController>()) {
// Get.find<SettingsController>().loadUserInfo(); // Get.find<SettingsController>().loadUserInfo();
// } // }
hasEdits.value = true; //表示有编辑
} }
} }

309
lib/modules/profile/views/profile_view.dart

@ -9,130 +9,130 @@ import 'dart:ui';
import '../../common/webview_page.dart'; import '../../common/webview_page.dart';
// ProfileView: 用户设置页面 // ProfileView: 用户设置页面
class ProfileView extends GetView<ProfileController> { // class ProfileView extends GetView<ProfileController> {
const ProfileView({Key? key}) : super(key: key); // const ProfileView({Key? key}) : super(key: key);
Widget _buildMenuItem({ // Widget _buildMenuItem({
required String title, // required String title,
required IconData icon, // required IconData icon,
String? subtitle, // String? subtitle,
required VoidCallback onTap, // required VoidCallback onTap,
}) { // }) {
return ListTile( // return ListTile(
leading: Icon(icon, size: 24.sp, color: Colors.grey[700]), // leading: Icon(icon, size: 24.sp, color: Colors.grey[700]),
title: Text( // title: Text(
title, // title,
style: TextStyle( // style: TextStyle(
fontSize: 16.sp, // fontSize: 16.sp,
fontWeight: FontWeight.w500, // fontWeight: FontWeight.w500,
), // ),
), // ),
subtitle: subtitle != null // subtitle: subtitle != null
? Text( // ? Text(
subtitle, // subtitle,
style: TextStyle( // style: TextStyle(
fontSize: 14.sp, // fontSize: 14.sp,
color: Colors.grey[600], // color: Colors.grey[600],
), // ),
) // )
: null, // : null,
trailing: Icon( // trailing: Icon(
Icons.chevron_right, // Icons.chevron_right,
size: 24.sp, // size: 24.sp,
color: Colors.grey[400], // color: Colors.grey[400],
), // ),
onTap: onTap, // onTap: onTap,
); // );
} // }
@override // @override
Widget build(BuildContext context) { // Widget build(BuildContext context) {
return Scaffold( // return Scaffold(
backgroundColor: Colors.white, // backgroundColor: Colors.white,
appBar: AppBar( // appBar: AppBar(
backgroundColor: Colors.white, // backgroundColor: Colors.white,
elevation: 0, // elevation: 0,
title: Text( // title: Text(
'设置'.tr, // '设置'.tr,
style: TextStyle( // style: TextStyle(
fontSize: 17.sp, // fontSize: 17.sp,
fontWeight: FontWeight.w600, // fontWeight: FontWeight.w600,
color: Colors.black, // color: Colors.black,
), // ),
), // ),
centerTitle: true, // centerTitle: true,
), // ),
body: ListView( // body: ListView(
children: [ // children: [
SizedBox(height: 12.h), // SizedBox(height: 12.h),
const Divider(), // const Divider(),
_buildMenuItem( // _buildMenuItem(
title: '关于'.tr, // title: '关于'.tr,
icon: Icons.info_outline, // icon: Icons.info_outline,
onTap: () { // onTap: () {
// TODO: 实现关于页面 // // TODO: 实现关于页面
}, // },
), // ),
const Divider(), // const Divider(),
_buildMenuItem( // _buildMenuItem(
title: '火山语音合成测试'.tr, // title: '火山语音合成测试'.tr,
icon: Icons.record_voice_over, // icon: Icons.record_voice_over,
subtitle: '测试火山语音TTS功能', // subtitle: '测试火山语音TTS功能',
onTap: () { // onTap: () {
Get.toNamed(Routes.TTS_TEST); // Get.toNamed(Routes.TTS_TEST);
}, // },
), // ),
const Divider(), // const Divider(),
_buildMenuItem( // _buildMenuItem(
title: '火山语音识别测试'.tr, // title: '火山语音识别测试'.tr,
icon: Icons.mic_external_on, // icon: Icons.mic_external_on,
subtitle: '测试火山语音ASR功能', // subtitle: '测试火山语音ASR功能',
onTap: () { // onTap: () {
Get.toNamed(Routes.VOLCANO_ASR_TEST); // Get.toNamed(Routes.VOLCANO_ASR_TEST);
}, // },
), // ),
const Divider(), // const Divider(),
_buildMenuItem( // _buildMenuItem(
title: 'Azure语音识别测试'.tr, // title: 'Azure语音识别测试'.tr,
icon: Icons.mic, // icon: Icons.mic,
subtitle: '测试Azure语音ASR功能', // subtitle: '测试Azure语音ASR功能',
onTap: () { // onTap: () {
Get.toNamed(Routes.AZURE_ASR_TEST); // Get.toNamed(Routes.AZURE_ASR_TEST);
}, // },
), // ),
const Divider(), // const Divider(),
_buildMenuItem( // _buildMenuItem(
title: 'Azure语音合成测试'.tr, // title: 'Azure语音合成测试'.tr,
icon: Icons.record_voice_over, // icon: Icons.record_voice_over,
subtitle: '测试Azure语音TTS功能', // subtitle: '测试Azure语音TTS功能',
onTap: () { // onTap: () {
Get.toNamed(Routes.AZURE_TTS_TEST); // Get.toNamed(Routes.AZURE_TTS_TEST);
}, // },
), // ),
const Divider(), // const Divider(),
_buildMenuItem( // _buildMenuItem(
title: '本地语音合成测试'.tr, // title: '本地语音合成测试'.tr,
icon: Icons.speaker_phone, // icon: Icons.speaker_phone,
subtitle: '测试Flutter TTS本地语音合成功能', // subtitle: '测试Flutter TTS本地语音合成功能',
onTap: () { // onTap: () {
Get.toNamed(Routes.FLUTTER_TTS_TEST); // Get.toNamed(Routes.FLUTTER_TTS_TEST);
}, // },
), // ),
const Divider(), // const Divider(),
_buildMenuItem( // _buildMenuItem(
title: '本地语音识别测试'.tr, // title: '本地语音识别测试'.tr,
icon: Icons.mic_none, // icon: Icons.mic_none,
subtitle: '测试Flutter本地语音识别功能', // subtitle: '测试Flutter本地语音识别功能',
onTap: () { // onTap: () {
Get.toNamed(Routes.FLUTTER_ASR_TEST); // Get.toNamed(Routes.FLUTTER_ASR_TEST);
}, // },
), // ),
], // ],
), // ),
bottomNavigationBar: const CommonBottomNav(currentIndex: 2), // bottomNavigationBar: const CommonBottomNav(currentIndex: 2),
); // );
} // }
} // }
// EditProfileView: 编辑个人资料页面 // EditProfileView: 编辑个人资料页面
class EditProfileView extends GetView<ProfileController> { class EditProfileView extends GetView<ProfileController> {
@ -146,19 +146,19 @@ class EditProfileView extends GetView<ProfileController> {
return Scaffold( return Scaffold(
resizeToAvoidBottomInset: false, // 添加这一行,防止键盘挤压内容 resizeToAvoidBottomInset: false, // 添加这一行,防止键盘挤压内容
appBar: AppBar( appBar: AppBar(
automaticallyImplyLeading: false, automaticallyImplyLeading: false,
centerTitle: true, centerTitle: true,
elevation: 0, elevation: 0,
backgroundColor: isDarkMode ? Colors.black : Colors.white, backgroundColor: isDarkMode ? Colors.black : Colors.white,
leading: IconButton( leading: IconButton(
icon: Icon( icon: Icon(
Icons.arrow_back_ios, Icons.arrow_back_ios,
size: 20.sp, size: 20.sp,
color: isDarkMode? Colors.white : Colors.black, color: isDarkMode ? Colors.white : Colors.black,
),
onPressed: () => Get.back(),
), ),
onPressed: () => Get.back(),
),
// title: Text( // title: Text(
// 'editProfile'.tr, // 'editProfile'.tr,
// style: TextStyle( // style: TextStyle(
@ -168,22 +168,25 @@ class EditProfileView extends GetView<ProfileController> {
// ), // ),
// ), // ),
actions: [ actions: [
TextButton( Obx(() => Visibility(
onPressed: () { visible: controller.hasEdits.value,
// 保存用户名 child: TextButton(
controller.updateUserNameAndAvatar(nameController.text); onPressed: () {
// 返回上一级页面 // 保存用户名
Get.back(); controller.updateUserNameAndAvatar(nameController.text);
}, // 返回上一级页面
child: Text( Get.back();
'save'.tr, },
style: TextStyle( child: Text(
fontSize: 16.sp, 'save'.tr,
color: isDarkMode ? Colors.blue[300] : Colors.blue, style: TextStyle(
fontWeight: FontWeight.w500, fontSize: 16.sp,
), color: isDarkMode ? Colors.blue[300] : Colors.blue,
), fontWeight: FontWeight.w500,
), ),
),
),
))
], ],
), ),
body: Padding( body: Padding(
@ -472,6 +475,12 @@ class EditProfileView extends GetView<ProfileController> {
onPressed: () { onPressed: () {
// 确认时,将对话框输入的值更新回主 nameController // 确认时,将对话框输入的值更新回主 nameController
nameController.text = dialogNameController.text; nameController.text = dialogNameController.text;
controller.userInfo.update((user) {
if (user != null) {
user.name = dialogNameController.text;
}
});
controller.hasEdits.value = true; // 标记已编辑
Get.back(); // 关闭对话框 Get.back(); // 关闭对话框
// 注意:实际的保存操作由 AppBar 中的保存按钮触发 // 注意:实际的保存操作由 AppBar 中的保存按钮触发
}, },

365
lib/modules/settings/views/settings_view.dart

@ -62,156 +62,155 @@ class SettingsView extends GetView<SettingsController> {
return GestureDetector( return GestureDetector(
onTap: () => Get.toNamed(Routes.editProfile), onTap: () => Get.toNamed(Routes.editProfile),
child: Container( child: Container(
padding: EdgeInsets.all(16.w), padding: EdgeInsets.all(16.w),
decoration: BoxDecoration( decoration: BoxDecoration(
gradient: LinearGradient( gradient: LinearGradient(
colors: isDarkMode colors: isDarkMode
? [ ? [
const Color(0xFF1A1A1A).withOpacity(0.7), const Color(0xFF1A1A1A).withOpacity(0.7),
const Color(0xFF2A2A2A).withOpacity(0.7) const Color(0xFF2A2A2A).withOpacity(0.7)
] ]
: [const Color(0xFFE3F2FD), const Color(0xFFF3E5F5)], : [const Color(0xFFE3F2FD), const Color(0xFFF3E5F5)],
begin: Alignment.topLeft, begin: Alignment.topLeft,
end: Alignment.bottomRight, end: Alignment.bottomRight,
),
borderRadius: BorderRadius.circular(16.r),
boxShadow: [
BoxShadow(
color: isDarkMode
? Colors.black.withOpacity(0.2)
: Colors.black.withOpacity(0.05),
blurRadius: 10,
offset: const Offset(0, 2),
), ),
], borderRadius: BorderRadius.circular(16.r),
), boxShadow: [
child: ClipRRect( BoxShadow(
//裁剪圆角矩形 color: isDarkMode
borderRadius: BorderRadius.circular(16.r), ? Colors.black.withOpacity(0.2)
child: BackdropFilter( : Colors.black.withOpacity(0.05),
filter: isDarkMode blurRadius: 10,
? ImageFilter.blur(sigmaX: 10, sigmaY: 10) offset: const Offset(0, 2),
: ImageFilter.blur(sigmaX: 0, sigmaY: 0), ),
child: Row( ],
children: [ ),
Obx(() => GestureDetector( child: ClipRRect(
//onTap: ()=>controller.updateUserAvatar(), //裁剪圆角矩形
child: Container( borderRadius: BorderRadius.circular(16.r),
//用户头像部分 child: BackdropFilter(
width: 60.w, filter: isDarkMode
height: 60.w, ? ImageFilter.blur(sigmaX: 10, sigmaY: 10)
decoration: BoxDecoration( : ImageFilter.blur(sigmaX: 0, sigmaY: 0),
borderRadius: BorderRadius.circular(30.r), child: Row(
boxShadow: [ children: [
BoxShadow( Obx(() => GestureDetector(
color: Colors.black.withOpacity(0.1), //onTap: ()=>controller.updateUserAvatar(),
blurRadius: 8, child: Container(
offset: const Offset(0, 2), //用户头像部分
), width: 60.w,
], height: 60.w,
), decoration: BoxDecoration(
child: ClipRRect(
// 裁剪圆形头像
borderRadius: BorderRadius.circular(30.r), borderRadius: BorderRadius.circular(30.r),
child: controller.userInfo.value.avatar.isEmpty boxShadow: [
? BoxShadow(
Container( color: Colors.black.withOpacity(0.1),
decoration: BoxDecoration( blurRadius: 8,
color: Colors.blue[600], offset: const Offset(0, 2),
borderRadius: BorderRadius.circular(30.r), ),
), ],
child: Center( ),
child: Text( child: ClipRRect(
controller // 裁剪圆形头像
.userInfo.value.name.isNotEmpty borderRadius: BorderRadius.circular(30.r),
? controller.userInfo.value.name[0] child: controller.userInfo.value.avatar.isEmpty
.toUpperCase() ? Container(
: '?', decoration: BoxDecoration(
style: TextStyle( color: Colors.blue[600],
color: Colors.white, borderRadius: BorderRadius.circular(30.r),
fontSize: 24.sp,
fontWeight: FontWeight.bold,
),
), ),
), child: Center(
) child: Text(
: Image.network( controller
controller.userInfo.value.avatar, .userInfo.value.name.isNotEmpty
fit: BoxFit.cover, ? controller.userInfo.value.name[0]
errorBuilder: (context, error, stackTrace) { .toUpperCase()
// 当图片加载失败时显示用户名首字母 : '?',
return Container( style: TextStyle(
decoration: BoxDecoration( color: Colors.white,
color: Colors.blue[600], fontSize: 24.sp,
borderRadius: fontWeight: FontWeight.bold,
BorderRadius.circular(30.r), ),
), ),
child: Center( ),
child: Text( )
controller.userInfo.value.name : Image.network(
.isNotEmpty controller.userInfo.value.avatar,
? controller fit: BoxFit.cover,
.userInfo.value.name[0] errorBuilder: (context, error, stackTrace) {
.toUpperCase() // 当图片加载失败时显示用户名首字母
: '?', return Container(
style: TextStyle( decoration: BoxDecoration(
color: Colors.white, color: Colors.blue[600],
fontSize: 24.sp, borderRadius:
fontWeight: FontWeight.bold, BorderRadius.circular(30.r),
),
child: Center(
child: Text(
controller.userInfo.value.name
.isNotEmpty
? controller
.userInfo.value.name[0]
.toUpperCase()
: '?',
style: TextStyle(
color: Colors.white,
fontSize: 24.sp,
fontWeight: FontWeight.bold,
),
), ),
), ),
), );
); },
}, )),
)),
),
)),
SizedBox(width: 16.w),
Expanded(
// 占据剩余空间
child: Obx(
() => Column(
crossAxisAlignment: CrossAxisAlignment.start, // 左对齐
children: [
Text(
//用户名
controller.userInfo.value.name,
style: TextStyle(
fontSize: 18.sp,
fontWeight: FontWeight.w600,
color: isDarkMode ? Colors.white : Colors.black87,
),
overflow: TextOverflow.ellipsis,
), ),
SizedBox(height: 4.h), )),
Row( SizedBox(width: 16.w),
children: [ Expanded(
Container( // 占据剩余空间
padding: EdgeInsets.symmetric( child: Obx(
horizontal: 8.w, vertical: 2.h), () => Column(
decoration: BoxDecoration( crossAxisAlignment: CrossAxisAlignment.start, // 左对齐
color: Colors.blue[600], children: [
borderRadius: BorderRadius.circular(10.r), Text(
), //用户名
child: Obx(() { controller.userInfo.value.name,
final membershipMap = { style: TextStyle(
0: 'freeUser', //'免费用户' fontSize: 18.sp,
1: 'standardUser', //'标准用户' fontWeight: FontWeight.w600,
2: 'proUser', //'专业用户' color: isDarkMode ? Colors.white : Colors.black87,
};
final membershipString = membershipMap[
controller
.userInfo.value.membershipLevel] ??
'Unknown';
return Text(
membershipString,
style: TextStyle(
fontSize: 10.sp,
color: Colors.white,
),
);
}),
), ),
overflow: TextOverflow.ellipsis,
),
SizedBox(height: 4.h),
Row(
children: [
Container(
padding: EdgeInsets.symmetric(
horizontal: 8.w, vertical: 2.h),
decoration: BoxDecoration(
color: Colors.blue[600],
borderRadius: BorderRadius.circular(10.r),
),
child: Obx(() {
final membershipMap = {
0: 'freeUser', //'免费用户'
1: 'standardUser', //'标准用户'
2: 'proUser', //'专业用户'
};
final membershipString = membershipMap[
controller
.userInfo.value.membershipLevel] ??
'Unknown';
return Text(
membershipString,
style: TextStyle(
fontSize: 10.sp,
color: Colors.white,
),
);
}),
),
// SizedBox(width: 8.w), // SizedBox(width: 8.w),
// Flexible( // Flexible(
// child: Text( // child: Text(
@ -225,46 +224,46 @@ class SettingsView extends GetView<SettingsController> {
// overflow: TextOverflow.ellipsis, // overflow: TextOverflow.ellipsis,
// ), // ),
// ), // ),
], ],
), ),
SizedBox(height: 4.h), SizedBox(height: 4.h),
Text( Text(
'ID:${controller.userInfo.value.uid}', 'ID:${controller.userInfo.value.uid}',
style: TextStyle( style: TextStyle(
fontSize: 12.sp, fontSize: 12.sp,
color: isDarkMode ? Colors.white70 : Colors.black54, color: isDarkMode ? Colors.white70 : Colors.black54,
),
), ),
), ],
], ),
), ),
), ),
), Container(
Container( // 编辑按钮
// 编辑按钮 width: 32.w,
width: 32.w, height: 32.w,
height: 32.w, decoration: BoxDecoration(
decoration: BoxDecoration( color: isDarkMode
color: isDarkMode ? Colors.white.withOpacity(0.1)
? Colors.white.withOpacity(0.1) : Colors.white.withOpacity(0.7),
: Colors.white.withOpacity(0.7), borderRadius: BorderRadius.circular(16.r),
borderRadius: BorderRadius.circular(16.r), boxShadow: [
boxShadow: [ BoxShadow(
BoxShadow( color: Colors.black.withOpacity(0.05),
color: Colors.black.withOpacity(0.05), blurRadius: 4,
blurRadius: 4, offset: const Offset(0, 1),
offset: const Offset(0, 1), ),
), ],
],
),
child: InkWell(
onTap: () => Get.toNamed(Routes.editProfile),
child: Icon(
Icons.edit,
size: 16.w,
color: isDarkMode ? Colors.white70 : Colors.grey[600],
), ),
)), child: InkWell(
], onTap: () => Get.toNamed(Routes.editProfile),
child: Icon(
Icons.edit,
size: 16.w,
color: isDarkMode ? Colors.white70 : Colors.grey[600],
),
)),
],
), ),
), ),
), ),
@ -572,7 +571,7 @@ class SettingsView extends GetView<SettingsController> {
_buildSimpleNavigationSetting( _buildSimpleNavigationSetting(
title: 'ota升级', title: 'ota升级',
subtitle: '测试ble耳机固件版本升级', subtitle: '测试ble耳机固件版本升级',
icon: Icons.record_voice_over, icon: Icons.system_update_alt,
iconBgColor: isDarkMode iconBgColor: isDarkMode
? Colors.orange[900]!.withOpacity(0.3) ? Colors.orange[900]!.withOpacity(0.3)
: Colors.orange[100]!, : Colors.orange[100]!,
@ -581,11 +580,11 @@ class SettingsView extends GetView<SettingsController> {
onTap: () => Get.toNamed(Routes.OTA_UPGRADE), onTap: () => Get.toNamed(Routes.OTA_UPGRADE),
isDarkMode: isDarkMode, isDarkMode: isDarkMode,
), ),
Divider( // Divider(
height: 1, // height: 1,
color: isDarkMode // color: isDarkMode
? Colors.white.withOpacity(0.1) // ? Colors.white.withOpacity(0.1)
: Colors.grey[200]), // : Colors.grey[200]),
// 退出登录 // 退出登录
// _buildSimpleNavigationSetting( // _buildSimpleNavigationSetting(

14
lib/routes/app_pages.dart

@ -103,13 +103,13 @@ abstract class AppPages {
page: () => const OtaUpgradeView(), page: () => const OtaUpgradeView(),
binding: OtaUpgradeBinding(), binding: OtaUpgradeBinding(),
), ),
GetPage( // GetPage(
name: Routes.profile, // name: Routes.profile,
page: () => const ProfileView(), // page: () => const ProfileView(),
binding: ProfileBinding(), // binding: ProfileBinding(),
transition: Transition.rightToLeft, // transition: Transition.rightToLeft,
transitionDuration: Duration(milliseconds: 250), // transitionDuration: Duration(milliseconds: 250),
), // ),
GetPage( GetPage(
name: Routes.editProfile, name: Routes.editProfile,
page: () => const EditProfileView(), page: () => const EditProfileView(),

61
lib/routes/app_routes.dart

@ -1,35 +1,48 @@
abstract class Routes { abstract class Routes {
// 认证相关
static const splash = '/splash'; static const splash = '/splash';
static const login = '/login'; static const login = '/login';
static const pairing = '/pairing'; static const pairing = '/pairing';
// 主功能模块
static const initial = '/home'; static const initial = '/home';
static const home = '/home'; static const home = '/home';
static const explore = '/explore'; static const explore = '/explore';
static const chat = '/chat'; static const chat = '/chat';
static const OTA_UPGRADE = '/ota_upgrade';
static const recording = '/recording';
static const profile = '/profile'; static const profile = '/profile';
static const meeting = '/meeting';
static const meetingRecord = '/meetingRecord';
static const meetingDetails = '/meetingDetails';
static const meetingEdit = '/meetingEdit';
static const timbre = '/timbre';
static const settings = '/settings';
static const speechDemo = '/speech_demo';
static const TTS_TEST = '/tts_test';
static const ASR_TEST = '/asr_test';
static const AZURE_ASR_TEST = '/azure_asr_test';
static const AZURE_TTS_TEST = '/azure_tts_test';
static const VOLCANO_ASR_TEST = '/volcano_asr_test';
static const translation = '/translation';
static const translationHistory = '/translation_history';
static const FLUTTER_TTS_TEST = '/flutter_tts_test';
static const FLUTTER_ASR_TEST = '/flutter_asr_test';
static const editProfile = '/edit_profile'; static const editProfile = '/edit_profile';
static const feedback = '/feedback';
static const jieliTest = '/jieli_test'; // 设备管理
static const opusTest = '/opus_test'; static const OTA_UPGRADE = '/ota_upgrade'; // OTA升级
static const bleTest = '/ble_test'; static const timbre = '/timbre'; // 音色设置
static const speechTest = '/speech_test'; static const settings = '/settings'; // 系统设置
static const agent = '/agent'; static const recording = '/recording'; // 录音功能
// 会议相关
static const meeting = '/meeting';
static const meetingRecord = '/meeting_record'; // 会议记录
static const meetingDetails = '/meeting_details'; // 会议详情
static const meetingEdit = '/meeting_edit'; // 会议编辑
// 语言功能
static const translation = '/translation'; // 实时翻译
static const translationHistory = '/translation_history'; // 翻译历史
static const speechDemo = '/speech_demo'; // 语音演示
// 测试路由(生产环境建议移除)
static const ttsTest = '/tts_test'; // 文字转语音测试
static const asrTest = '/asr_test'; // 语音识别测试
static const azureAsrTest = '/azure_asr_test'; // Azure语音识别
static const azureTtsTest = '/azure_tts_test'; // Azure文字转语音
static const volcanoAsrTest = '/volcano_asr_test'; // 火山引擎语音识别
static const flutterTtsTest = '/flutter_tts_test'; // Flutter TTS测试
static const flutterAsrTest = '/flutter_asr_test'; // Flutter ASR测试
static const jieliTest = '/jieli_test'; // 杰理芯片测试
static const opusTest = '/opus_test'; // Opus编码测试
static const bleTest = '/ble_test'; // 蓝牙测试
static const speechTest = '/speech_test'; // 综合语音测试
// 支持服务
static const feedback = '/feedback'; // 用户反馈
static const agent = '/agent'; // 在线客服
} }

2
local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/BleAgent.kt

@ -128,7 +128,7 @@ object BleAgent : BleService.Callback, AgentServiceListener {
} }
override fun onAudioDataReceived(data: ByteArray) { override fun onAudioDataReceived(data: ByteArray) {
// FileLogger.d(TAG, "onAudioDataReceived, data: ${data.size}") FileLogger.d(TAG, "onAudioDataReceived, data: ${data.size}")
AgentService.pushAudioData(data) AgentService.pushAudioData(data)
// 可选:处理音频数据 // 可选:处理音频数据
} }

1
local_plugins/azure_speech/android/build.gradle.kts

@ -53,4 +53,5 @@ tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile> {
dependencies { dependencies {
// 添加Microsoft语音SDK // 添加Microsoft语音SDK
implementation("com.microsoft.cognitiveservices.speech:client-sdk:1.30.0") implementation("com.microsoft.cognitiveservices.speech:client-sdk:1.30.0")
implementation(project(":ble_service"))
} }

58
local_plugins/azure_speech/android/src/main/kotlin/com/yunqiinnovation/azure_speech/AzureSpeechPlugin.kt

@ -14,9 +14,10 @@ import io.flutter.plugin.common.EventChannel
import kotlinx.coroutines.* import kotlinx.coroutines.*
import kotlin.coroutines.CoroutineContext import kotlin.coroutines.CoroutineContext
import com.microsoft.cognitiveservices.speech.audio.* import com.microsoft.cognitiveservices.speech.audio.*
import com.yunqiinnovation.ble_service.BleService
import com.yunqiinnovation.ble_service.BleConst
/** AzureSpeechPlugin */ /** AzureSpeechPlugin */
class AzureSpeechPlugin: FlutterPlugin, CoroutineScope { class AzureSpeechPlugin: FlutterPlugin,BleService.Callback, CoroutineScope {
private val tag = "AzureSpeechPlugin" private val tag = "AzureSpeechPlugin"
private lateinit var context: Context private lateinit var context: Context
private val mainHandler = Handler(Looper.getMainLooper()) private val mainHandler = Handler(Looper.getMainLooper())
@ -37,7 +38,10 @@ class AzureSpeechPlugin: FlutterPlugin, CoroutineScope {
private lateinit var ttsEventChannel: EventChannel private lateinit var ttsEventChannel: EventChannel
private var ttsEventSink: EventChannel.EventSink? = null private var ttsEventSink: EventChannel.EventSink? = null
private lateinit var azureTtsHelper: AzureTtsHelper private lateinit var azureTtsHelper: AzureTtsHelper
// 是否已注册BLE回调
private var isRegisteredToBle = false
// ASR 事件发送方法 // ASR 事件发送方法
private fun sendAsrEvent(event: Map<String, Any>) { private fun sendAsrEvent(event: Map<String, Any>) {
if (asrEventSink == null) { if (asrEventSink == null) {
@ -110,6 +114,19 @@ class AzureSpeechPlugin: FlutterPlugin, CoroutineScope {
// 初始化Azure语音服务 // 初始化Azure语音服务
azureTtsHelper = AzureTtsHelper(context) azureTtsHelper = AzureTtsHelper(context)
azureAsrHelper = AzureAsrHelper(context) azureAsrHelper = AzureAsrHelper(context)
// 2. 初始化BleService并注册回调
if (BleService.initialize(context)) {
if (BleService.addCallback(this)) {
isRegisteredToBle = true
FileLogger.d(tag, "已注册BLE回调")
} else {
FileLogger.w(tag, "BLE回调注册失败")
}
} else {
FileLogger.e(tag, "BLE服务初始化失败")
}
} }
// ASR方法处理器 // ASR方法处理器
@ -359,7 +376,12 @@ class AzureSpeechPlugin: FlutterPlugin, CoroutineScope {
try { try {
// 取消所有协程 // 取消所有协程
job.cancel() job.cancel()
// 取消BLE回调注册
if (isRegisteredToBle) {
BleService.removeCallback(this)
isRegisteredToBle = false
}
// 释放资源 // 释放资源
azureTtsHelper.dispose() azureTtsHelper.dispose()
azureAsrHelper.dispose() azureAsrHelper.dispose()
@ -367,4 +389,34 @@ class AzureSpeechPlugin: FlutterPlugin, CoroutineScope {
FileLogger.e(tag, "释放资源失败: ${e.message}") FileLogger.e(tag, "释放资源失败: ${e.message}")
} }
} }
override fun onScanResult(devices: List<Map<String, Any>>) {
}
override fun onConnectionStateChanged(state: Int) {
// 可选:处理连接状态变化
if (state == BleConst.STATE_CONNECTED) {
FileLogger.d(tag, "Ble设备已连接,准备好语音交互")
} else if (state == BleConst.STATE_DISCONNECTED) {
FileLogger.d(tag, "Ble设备已断开,停止语音服务")
// 设备断开连接,停止语音服务
// stopListening()
}
}
override fun onAudioDataReceived(data: ByteArray) {
FileLogger.d(tag, "AzureSpeechPlugin onAudioDataReceived, data: ${data.size}")
azureAsrHelper.pushAudioData(data)
}
override fun onWakeupSignalReceived() {
}
override fun onDeviceInfoReceived(infoType: Int, infoData: Map<String, Any>) {
}
} }

1
local_plugins/ble_service/android/build.gradle.kts

@ -31,5 +31,4 @@ dependencies {
// 使用从本地Maven仓库引用的AAR依赖 // 使用从本地Maven仓库引用的AAR依赖
implementation("com.jieli.audio:jl_audio_decode:2.0.0") implementation("com.jieli.audio:jl_audio_decode:2.0.0")
implementation(project(":deepvoice_utils")) implementation(project(":deepvoice_utils"))
implementation(project(":azure_speech"))
} }

8
local_plugins/ble_service/android/src/main/kotlin/com/yunqiinnovation/ble_service/BleService.kt

@ -661,10 +661,10 @@ object BleService {
"rawData" to responseData "rawData" to responseData
) )
if (responseData.isNotEmpty() && responseData.size >= 2) { if (responseData.isNotEmpty()) {
val productId = "0x${(responseData[0].toInt() and 0xFF).toString(16)}${ // 转换为字符串前,确认是否是有效的ASCII字符
(responseData[1].toInt() and 0xFF).toString(16) val productId =
}" responseData.joinToString(".") { (it.toInt() and 0xFF).toString() }
resultMap["productId"] = productId resultMap["productId"] = productId
FileLogger.i(TAG, "接收到产品ID: $productId") FileLogger.i(TAG, "接收到产品ID: $productId")
} }

4
local_plugins/ble_service/android/src/main/kotlin/com/yunqiinnovation/ble_service/BleServicePlugin.kt

@ -189,6 +189,10 @@ class BleServicePlugin : FlutterPlugin, MethodCallHandler, ActivityAware,
"getDeviceVersion" -> { "getDeviceVersion" -> {
val success = BleService.getDeviceVersion() val success = BleService.getDeviceVersion()
result.success(success) result.success(success)
}
"getProductId" -> {
val success = BleService.getProductId()
result.success(success)
} }
"getBatteryInfo" -> { "getBatteryInfo" -> {
val success = BleService.getBatteryInfo() val success = BleService.getBatteryInfo()

Loading…
Cancel
Save