Browse Source

上传设备解绑逻辑代码

weicu
liwei1dao 1 year ago
parent
commit
fa8c8261d0
  1. 2
      lib/core/themes/language/lang/en_us.dart
  2. 2
      lib/core/themes/language/lang/zh_cn.dart
  3. 6
      lib/data/models/user_Info.dart
  4. 9
      lib/data/services/network/api.dart
  5. 11
      lib/modules/devices/bindings/devices_binding.dart
  6. 61
      lib/modules/devices/controllers/devices_controller.dart
  7. 290
      lib/modules/devices/views/devices_view.dart
  8. 149
      lib/modules/pairing/controllers/pairing_controller.dart
  9. 47
      lib/modules/pairing/views/pairing_view.dart
  10. 20
      lib/modules/pairing/views/pairing_view_new.dart
  11. 8
      lib/modules/settings/views/settings_view.dart
  12. 7
      lib/routes/app_pages.dart
  13. 3
      lib/routes/app_routes.dart
  14. 2
      local_plugins/ble_service/android/src/main/kotlin/com/yunqiinnovation/ble_service/BleService.kt
  15. 4
      local_plugins/ble_service/android/src/main/kotlin/com/yunqiinnovation/ble_service/BleServicePlugin.kt

2
lib/core/themes/language/lang/en_us.dart

@ -90,4 +90,6 @@ const Map<String, String> enUS = {
"goToBind": "Unbind", "goToBind": "Unbind",
"scanQrCode": "Scan the QR code", "scanQrCode": "Scan the QR code",
"endRecording": "Do you want to end the recording?", "endRecording": "Do you want to end the recording?",
"earphone": "Earphone",
"voicerecorder": "VoiceRecorder"
}; };

2
lib/core/themes/language/lang/zh_cn.dart

@ -88,4 +88,6 @@ const Map<String, String> zhCN = {
"goToBind": "去绑定", "goToBind": "去绑定",
"scanQrCode": "扫描二维码", "scanQrCode": "扫描二维码",
"endRecording": "你想结束录音吗?", "endRecording": "你想结束录音吗?",
"earphone": "耳机",
"voicerecorder": "录音笔"
}; };

6
lib/data/models/user_Info.dart

@ -21,6 +21,7 @@ class User {
String phonemac; String phonemac;
String password; String password;
int viplv = 0; //添加会员等级字段,0表示普通用户 int viplv = 0; //添加会员等级字段,0表示普通用户
String earphonename;
String earphonemac; String earphonemac;
String earphoneauthcode; String earphoneauthcode;
// final Int64 lastbettime; // final Int64 lastbettime;
@ -41,6 +42,7 @@ class User {
required this.phonemac, required this.phonemac,
required this.password, required this.password,
required this.viplv, // 添加默认值 required this.viplv, // 添加默认值
required this.earphonename,
required this.earphonemac, required this.earphonemac,
required this.earphoneauthcode, required this.earphoneauthcode,
// required this.lastbettime, // required this.lastbettime,
@ -70,6 +72,7 @@ class User {
phonemac: (userData?['phonemac'] as String?) ?? '', phonemac: (userData?['phonemac'] as String?) ?? '',
password: (userData?['password'] as String?) ?? '', password: (userData?['password'] as String?) ?? '',
viplv: (userData?['viplv'] as int?) ?? 0, // 提供默认值 viplv: (userData?['viplv'] as int?) ?? 0, // 提供默认值
earphonename: (userData?['earphonename'] as String?) ?? '',
earphonemac: (userData?['earphonemac'] as String?) ?? '', earphonemac: (userData?['earphonemac'] as String?) ?? '',
earphoneauthcode: (userData?['earphoneauthcode'] as String?) ?? '', earphoneauthcode: (userData?['earphoneauthcode'] as String?) ?? '',
// lastbettime: _parseTimestamp(userData['lastbettime']), // lastbettime: _parseTimestamp(userData['lastbettime']),
@ -97,6 +100,7 @@ class User {
password: (userData?['password'] as String?) ?? '', password: (userData?['password'] as String?) ?? '',
viplv: (userData?['viplv'] as int?) ?? 0, // 提供默认值 viplv: (userData?['viplv'] as int?) ?? 0, // 提供默认值
earphonename: (userData?['earphonename'] as String?) ?? '',
earphonemac: (userData?['earphonemac'] as String?) ?? '', earphonemac: (userData?['earphonemac'] as String?) ?? '',
earphoneauthcode: (userData?['earphoneauthcode'] as String?) ?? '', earphoneauthcode: (userData?['earphoneauthcode'] as String?) ?? '',
// lastbettime: _parseTimestamp(userData['lastbettime']), // lastbettime: _parseTimestamp(userData['lastbettime']),
@ -121,6 +125,8 @@ class User {
'phonemac': phonemac, 'phonemac': phonemac,
'password': password, 'password': password,
'viplv': viplv, 'viplv': viplv,
'earphonename': earphonename,
'earphonemac': earphonemac, 'earphonemac': earphonemac,
'earphoneauthcode': earphoneauthcode, 'earphoneauthcode': earphoneauthcode,

9
lib/data/services/network/api.dart

@ -136,6 +136,15 @@ class Api {
); );
} }
//解绑定设备
static unbindearphone([params]) {
return DioManager().request(
NWMethod.post,
'/api/home/user_unbindearphone',
params: params,
);
}
//绑定设备 //绑定设备
static bindauthcode([params]) { static bindauthcode([params]) {
return DioManager().request( return DioManager().request(

11
lib/modules/devices/bindings/devices_binding.dart

@ -0,0 +1,11 @@
import 'package:deep_voice/modules/devices/controllers/devices_controller.dart';
import 'package:get/get.dart';
class DevicesBinding extends Bindings {
@override
void dependencies() {
Get.lazyPut<DevicesController>(
() => DevicesController(),
);
}
}

61
lib/modules/devices/controllers/devices_controller.dart

@ -0,0 +1,61 @@
import 'package:deep_voice/core/utils/logger.dart';
import 'package:deep_voice/data/models/user_Info.dart';
import 'package:deep_voice/data/services/ble_manager.dart';
import 'package:deep_voice/data/services/network/api.dart';
import 'package:deep_voice/routes/app_routes.dart';
import 'package:get/get.dart';
import 'package:get/get_rx/get_rx.dart';
import 'package:get/get_state_manager/src/simple/get_controllers.dart';
/*
设备管理
*/
class DevicesController extends GetxController {
late final BleManager _bleManager;
//耳机绑定
final isearphone = false.obs;
final earphoneName = "".obs;
final earphoneMac = "".obs;
final earphoneCode = "".obs;
//录音笔
final isvoicepen = false.obs;
final voicepenName = "".obs;
final voicepenMac = "".obs;
@override
void onInit() {
super.onInit();
_bleManager = Get.find<BleManager>();
earphoneName.value = User.instance.earphonename;
earphoneMac.value = User.instance.earphonemac;
earphoneCode.value = User.instance.earphoneauthcode;
if (earphoneCode.value != "") {
isearphone.value = true;
}
}
@override
void onClose() {
super.onClose();
}
void unbindearphone() async {
final response = await Api.unbindearphone({
"code": earphoneCode.value,
"earphonname": earphoneMac.value,
});
if (response != null) {
User.instance.earphonename = "";
User.instance.earphonemac = "";
User.instance.earphoneauthcode = "";
isearphone.value = false;
_bleManager.clearPairedDevices();
_bleManager.disconnect();
Get.toNamed(Routes.pairing);
return;
} else {
return;
}
}
}

290
lib/modules/devices/views/devices_view.dart

@ -0,0 +1,290 @@
import 'dart:ui';
import 'package:deep_voice/modules/devices/controllers/devices_controller.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart';
class DevicesView extends GetView<DevicesController> {
const DevicesView({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
// 直接使用 Theme.of(context) 获取当前主题
final isDarkMode = Theme.of(context).brightness == Brightness.dark;
return Scaffold(
backgroundColor: isDarkMode ? Colors.black : Colors.grey[50],
appBar: AppBar(
automaticallyImplyLeading: false,
centerTitle: true,
elevation: 0,
backgroundColor: isDarkMode ? Colors.black : Colors.white,
leading: IconButton(
icon: Icon(
Icons.arrow_back_ios,
size: 20.sp,
color: isDarkMode ? Colors.white : Colors.black,
),
onPressed: () {
Get.back(); // 正常返回
},
),
title: Text(
'设备管理'.tr,
style: TextStyle(
fontSize: 20.sp,
color: isDarkMode ? Colors.white : Colors.black,
),
),
),
body: ListView(
padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 16.h),
children: [
_buildEarphoneCard(isDarkMode),
SizedBox(height: 20.h),
_buildVoiceRecorderCard(isDarkMode),
SizedBox(height: 20.h),
],
),
);
}
// 耳机
Widget _buildEarphoneCard(bool isDarkMode) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: EdgeInsets.only(left: 4.w, bottom: 8.h),
child: Text(
'earphone'.tr, // AI设置
style: TextStyle(
fontSize: 12.sp,
fontWeight: FontWeight.w500,
color: isDarkMode ? Colors.blue[400] : Colors.blue[600],
letterSpacing: 1,
),
),
),
Container(
decoration: BoxDecoration(
color: isDarkMode ? Colors.white.withOpacity(0.05) : Colors.white,
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),
),
],
),
child: ClipRRect(
borderRadius: BorderRadius.circular(16.r),
child: BackdropFilter(
filter: isDarkMode
? ImageFilter.blur(sigmaX: 5, sigmaY: 5)
: ImageFilter.blur(sigmaX: 0, sigmaY: 0),
child: Column(
children: [
Obx(
() =>
// 耳机列表
_buildSimpleNavigationSetting(
title: controller.isearphone.value
? controller.earphoneName.value
: "未绑定",
subtitle: controller.isearphone.value
? controller.earphoneMac.value
: "还未绑定设备",
icon: Icons.headphones,
iconBgColor: isDarkMode
? const Color.fromARGB(255, 255, 255, 255)!
.withOpacity(0.3)
: const Color.fromARGB(255, 0, 0, 0)!,
iconColor: isDarkMode
? const Color.fromARGB(255, 0, 0, 0)!
: const Color.fromARGB(255, 255, 255, 255)!,
titleColor: isDarkMode ? Colors.white : null,
subtitleColor: isDarkMode ? Colors.white70 : null,
isDarkMode: isDarkMode,
showbutt: controller.isearphone.value,
onPressed: () {
controller.unbindearphone();
},
),
)
],
),
),
),
),
],
);
}
// 录音笔
Widget _buildVoiceRecorderCard(bool isDarkMode) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: EdgeInsets.only(left: 4.w, bottom: 8.h),
child: Text(
'voicerecorder'.tr, //AI设置
style: TextStyle(
fontSize: 12.sp,
fontWeight: FontWeight.w500,
color: isDarkMode ? Colors.blue[400] : Colors.blue[600],
letterSpacing: 1,
),
),
),
Container(
decoration: BoxDecoration(
color: isDarkMode ? Colors.white.withOpacity(0.05) : Colors.white,
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),
),
],
),
child: ClipRRect(
borderRadius: BorderRadius.circular(16.r),
child: BackdropFilter(
filter: isDarkMode
? ImageFilter.blur(sigmaX: 5, sigmaY: 5)
: ImageFilter.blur(sigmaX: 0, sigmaY: 0),
child: Column(
children: [
// 实时对话入口
Obx(
() => _buildSimpleNavigationSetting(
title: '未绑定',
subtitle: '还未绑定设备',
icon: Icons.edit,
iconBgColor: isDarkMode
? const Color.fromARGB(255, 255, 255, 255)!
.withOpacity(0.3)
: const Color.fromARGB(255, 0, 0, 0)!,
iconColor: isDarkMode
? const Color.fromARGB(255, 0, 0, 0)!
: const Color.fromARGB(255, 255, 255, 255)!,
titleColor: isDarkMode ? Colors.white : null,
subtitleColor: isDarkMode ? Colors.white70 : null,
isDarkMode: isDarkMode,
showbutt: controller.isvoicepen.value,
onPressed: () {},
),
)
],
),
),
),
),
],
);
}
// 简单导航设置项
Widget _buildSimpleNavigationSetting({
required String title,
String? subtitle,
required IconData icon,
required Color iconBgColor,
required Color iconColor,
Color? titleColor,
Color? subtitleColor,
required bool isDarkMode,
required bool showbutt,
required VoidCallback onPressed,
}) {
return InkWell(
child: Padding(
padding: EdgeInsets.all(16.w),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Row(
children: [
Container(
width: 32.w,
height: 32.w,
decoration: BoxDecoration(
color: iconBgColor,
borderRadius: BorderRadius.circular(16.r),
),
child: Icon(
icon,
size: 16.w,
color: iconColor,
),
),
SizedBox(width: 12.w),
Flexible(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: TextStyle(
fontSize: 16.sp,
fontWeight: FontWeight.w500,
color: titleColor ??
(isDarkMode ? Colors.white : Colors.black87),
),
overflow: TextOverflow.ellipsis,
),
if (subtitle != null) SizedBox(height: 2.h),
if (subtitle != null)
Text(
subtitle,
style: TextStyle(
fontSize: 12.sp,
color: subtitleColor ??
(isDarkMode
? Colors.white70
: Colors.black54),
),
overflow: TextOverflow.ellipsis,
),
],
),
),
],
),
),
if (showbutt)
Container(
width: 74.w,
height: 40.w,
child: ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: Colors.blue,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
padding: EdgeInsets.symmetric(horizontal: 2, vertical: 2),
),
onPressed: onPressed,
child: const Text(
'解绑',
style: TextStyle(
color: Colors.white,
),
),
)),
],
),
),
);
}
}

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

@ -2,6 +2,7 @@ import 'dart:convert';
import 'dart:typed_data'; import 'dart:typed_data';
import 'package:app_settings/app_settings.dart'; import 'package:app_settings/app_settings.dart';
import 'package:deep_voice/data/models/appconfig.dart';
import 'package:deep_voice/data/models/user_Info.dart'; import 'package:deep_voice/data/models/user_Info.dart';
import 'package:deep_voice/data/services/network/api.dart'; import 'package:deep_voice/data/services/network/api.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@ -21,6 +22,7 @@ enum PairingState {
checking, // 检查中(蓝牙、权限等) checking, // 检查中(蓝牙、权限等)
waitingClassic, // 等待经典蓝牙连接 waitingClassic, // 等待经典蓝牙连接
verifying, // 验证设备 verifying, // 验证设备
binding, //确认绑定设备
manualbinding, //手动绑定设备 manualbinding, //手动绑定设备
associating, // 正在进行BLE设备关联 associating, // 正在进行BLE设备关联
waitingBle, //等待BLE连接 waitingBle, //等待BLE连接
@ -61,6 +63,8 @@ class PairingController extends GetxController {
final isLoading = false.obs; final isLoading = false.obs;
final showConnectButton = false.obs; final showConnectButton = false.obs;
final showRetryButton = false.obs; final showRetryButton = false.obs;
final showBindButton = false.obs;
final showRetryChectButton = false.obs;
final showAutoCodeInput = false.obs; final showAutoCodeInput = false.obs;
final showSkipButton = false.obs; final showSkipButton = false.obs;
// 增加临时跳过绑定按钮状态 // 增加临时跳过绑定按钮状态
@ -68,6 +72,7 @@ class PairingController extends GetxController {
final connectButtonText = '连接设备'.obs; final connectButtonText = '连接设备'.obs;
final retryButtonText = '重试'.obs; final retryButtonText = '重试'.obs;
final deviceCode = ''.obs;
// ========== 监听器管理 ========== // ========== 监听器管理 ==========
final List<Worker> _workers = []; final List<Worker> _workers = [];
@ -251,7 +256,8 @@ class PairingController extends GetxController {
showSkipBindingButton.value = false; // 重置临时跳过绑定按钮 showSkipBindingButton.value = false; // 重置临时跳过绑定按钮
showSkipButton.value = false; showSkipButton.value = false;
showAutoCodeInput.value = false; showAutoCodeInput.value = false;
showBindButton.value = false;
showRetryChectButton.value = false;
switch (state) { switch (state) {
case PairingState.idle: case PairingState.idle:
statusText.value = '准备开始配对'; statusText.value = '准备开始配对';
@ -266,6 +272,7 @@ class PairingController extends GetxController {
statusText.value = '请确保耳机已开启并进入配对模式'; statusText.value = '请确保耳机已开启并进入配对模式';
showConnectButton.value = true; showConnectButton.value = true;
showSkipButton.value = true; showSkipButton.value = true;
connectButtonText.value = '前往蓝牙设置'; connectButtonText.value = '前往蓝牙设置';
break; break;
@ -273,7 +280,12 @@ class PairingController extends GetxController {
statusText.value = '正在验证设备...'; statusText.value = '正在验证设备...';
isLoading.value = true; isLoading.value = true;
break; break;
case PairingState.binding:
statusText.value = '确认绑定';
isLoading.value = true;
showBindButton.value = true;
showRetryChectButton.value = true;
break;
case PairingState.associating: case PairingState.associating:
statusText.value = '正在关联设备...'; statusText.value = '正在关联设备...';
isLoading.value = true; isLoading.value = true;
@ -418,39 +430,27 @@ class PairingController extends GetxController {
Future<void> _verifyDevice(BluetoothDevice device) async { Future<void> _verifyDevice(BluetoothDevice device) async {
try { try {
Logger.i(_tag, '开始验证设备: ${device.name} (${device.address})'); Logger.i(_tag, '开始验证设备: ${device.name} (${device.address})');
// TODO: 实现API验证逻辑 final result = await authbindingDevice(device);
// 暂时模拟验证通过 if (result == 2) {
// await Future.delayed(const Duration(seconds: 2));; //验证失败 进入手动授权
if (await authbindingDevice(device)) { await connectble(device);
Logger.i(_tag, '设备验证通过'); } else if (result == 1) {
_verifiedDeviceAddress = device.address; //跳转绑定界面
// 检查是否已有BLE关联 //验证失败 进入手动授权
final pairedMacAddress = await _bleManager.getPairedMacAddress(); if (currentDevice != null) {
Logger.i(_tag, '已关联的MAC地址: $pairedMacAddress'); _transition(PairingState.binding);
if (pairedMacAddress != null &&
pairedMacAddress.toUpperCase() == device.address.toUpperCase()) {
// 已有关联,检查BLE连接状态
Logger.i(_tag, '设备已关联,检查BLE连接状态');
final currentBleState = await _bleManager.getConnectionState();
Logger.i(_tag, '验证时BLE状态: $currentBleState');
if (currentBleState == 2) {
// BleConnectionState.connected
Logger.i(_tag, 'BLE已连接,直接完成配对');
_transition(PairingState.completed);
} else {
Logger.i(_tag, 'BLE未连接,等待BLE连接');
_transition(PairingState.waitingBle);
}
} else { } else {
// 没有关联或关联的是其他设备,需要进行关联 _transition(PairingState.checking);
Logger.i(_tag, '需要进行BLE设备关联'); _checkCurrentStatus();
_transition(PairingState.associating);
} }
} else { } else {
//验证失败 进入手动授权 final authcode_open = AppConfig.env("APP_AUTHCODE_OPEN") ?? "";
_transition(PairingState.manualbinding); if (authcode_open == "true") {
//验证失败 进入手动授权
_transition(PairingState.manualbinding);
} else {
_setError(ErrorType.deviceNotMatch, '请连接 DeepSound 设备');
}
} }
} catch (e) { } catch (e) {
Logger.e(_tag, '设备验证异常: $e'); Logger.e(_tag, '设备验证异常: $e');
@ -458,6 +458,37 @@ class PairingController extends GetxController {
} }
} }
//连接ble
Future<void> connectble(BluetoothDevice device) async {
Logger.i(_tag, '设备验证通过');
_verifiedDeviceAddress = device.address;
// 检查是否已有BLE关联
final pairedMacAddress = await _bleManager.getPairedMacAddress();
Logger.i(_tag, '已关联的MAC地址: $pairedMacAddress');
if (pairedMacAddress != null &&
pairedMacAddress.toUpperCase() == device.address.toUpperCase()) {
// 已有关联,检查BLE连接状态
Logger.i(_tag, '设备已关联,检查BLE连接状态');
final currentBleState = await _bleManager.getConnectionState();
Logger.i(_tag, '验证时BLE状态: $currentBleState');
if (currentBleState == 2) {
// BleConnectionState.connected
Logger.i(_tag, 'BLE已连接,直接完成配对');
_transition(PairingState.completed);
} else {
Logger.i(_tag, 'BLE未连接,等待BLE连接');
_transition(PairingState.waitingBle);
}
} else {
// 没有关联或关联的是其他设备,需要进行关联
Logger.i(_tag, '需要进行BLE设备关联');
_transition(PairingState.associating);
}
return;
}
/// 开始设备关联 /// 开始设备关联
void _startDeviceAssociation() async { void _startDeviceAssociation() async {
try { try {
@ -524,18 +555,19 @@ class PairingController extends GetxController {
} }
} }
//自动绑定设备 //自动绑定设备 0 未合作商 1 合作商 2,已绑定
Future<bool> authbindingDevice(BluetoothDevice device) async { Future<int> authbindingDevice(BluetoothDevice device) async {
if (User.instance.earphonemac == device.address) { if (User.instance.earphonemac == device.address) {
// 已经绑定 // 已经绑定
return true; return 2;
} }
// 检查设备名称是否包含 DeepSound 或 DeapSound // 检查设备名称是否包含 DeepSound 或 DeapSound
final deviceName = device.name ?? ''; final deviceName = device.name;
print('获取到 deviceName: $deviceName'); print('获取到 deviceName: $deviceName');
if (deviceName.contains('DeepSound') || deviceName.contains('DeapSound')) { if (deviceName.contains('DeepSound') || deviceName.contains('DeapSound')) {
await _bleManager.startScan(); await _bleManager.startScan();
await Future.delayed(const Duration(seconds: 1));
// 尝试最多读取 3 次设备信息,每次间隔 1 秒 // 尝试最多读取 3 次设备信息,每次间隔 1 秒
Map<String, String>? deviceinfo; Map<String, String>? deviceinfo;
for (int i = 0; i < 3; i++) { for (int i = 0; i < 3; i++) {
@ -553,27 +585,38 @@ class PairingController extends GetxController {
print('获取到 code: $code'); print('获取到 code: $code');
// 安全检查长度 // 安全检查长度
if (code != "") { if (code != "") {
final response = await Api.bindearphone({ deviceCode.value = code;
"code": code, return 1;
"earphonemac": device.address,
});
Logger.i(_tag, '手动授权结果 ${response}');
if (response != null) {
Logger.i(_tag, '设备自动绑定成功');
User.instance.earphonemac = device.address ?? '';
return true;
} else {
Logger.e(_tag, '手动绑定失败!');
return false;
}
} else { } else {
return false; return 0;
} }
} }
return false; return 0;
// 调用BLE管理器的自动绑定方法 // 调用BLE管理器的自动绑定方法
} else { } else {
return false; return 0;
}
}
//确认绑定设备
void confirmBindDevice() async {
final response = await Api.bindearphone({
"code": deviceCode.value,
"earphonname": currentDevice?.name,
"earphonemac": currentDevice?.address,
});
Logger.i(_tag, '手动授权结果 ${response}');
if (response != null) {
Logger.i(_tag, '绑定设备成功');
User.instance.earphonemac = currentDevice?.address ?? '';
User.instance.earphonename = currentDevice?.name ?? '';
User.instance.earphoneauthcode = deviceCode.value;
_transition(PairingState.verifying);
_verifyDevice(_currentClassicDevice!);
return;
} else {
Logger.e(_tag, '绑定设备失败!');
return;
} }
} }
@ -605,6 +648,8 @@ class PairingController extends GetxController {
Logger.i(_tag, '手动授权结果 ${response}'); Logger.i(_tag, '手动授权结果 ${response}');
if (response != null) { if (response != null) {
User.instance.earphonemac = currentDevice?.address ?? ''; User.instance.earphonemac = currentDevice?.address ?? '';
User.instance.earphonename = currentDevice?.name ?? '';
User.instance.earphoneauthcode = codeInputController.text.trim();
_transition(PairingState.verifying); _transition(PairingState.verifying);
_verifyDevice(_currentClassicDevice!); _verifyDevice(_currentClassicDevice!);
} else { } else {

47
lib/modules/pairing/views/pairing_view.dart

@ -290,6 +290,7 @@ class PairingView extends GetView<PairingController> {
// 当前连接设备(有设备时才显示) // 当前连接设备(有设备时才显示)
if (device != null && if (device != null &&
(state == PairingState.verifying || (state == PairingState.verifying ||
state == PairingState.binding ||
state == PairingState.waitingBle || state == PairingState.waitingBle ||
state == PairingState.completed)) ...[ state == PairingState.completed)) ...[
SizedBox(height: 24.h), SizedBox(height: 24.h),
@ -311,6 +312,8 @@ class PairingView extends GetView<PairingController> {
return '请先通过系统蓝牙连接DeapSound设备'; return '请先通过系统蓝牙连接DeapSound设备';
case PairingState.verifying: case PairingState.verifying:
return '正在验证设备...'; return '正在验证设备...';
case PairingState.binding:
return '设备绑定确认...';
case PairingState.associating: case PairingState.associating:
return '正在建立设备关联...'; return '正在建立设备关联...';
case PairingState.waitingBle: case PairingState.waitingBle:
@ -434,6 +437,8 @@ class PairingView extends GetView<PairingController> {
return Obx(() { return Obx(() {
final showConnectButton = controller.showConnectButton.value; final showConnectButton = controller.showConnectButton.value;
final showRetryButton = controller.showRetryButton.value; final showRetryButton = controller.showRetryButton.value;
final showBindButton = controller.showBindButton.value;
final showRetryChectButton = controller.showRetryChectButton.value;
final showSkipButton = controller.showSkipButton.value; final showSkipButton = controller.showSkipButton.value;
final showAutoCodeInput = controller.showAutoCodeInput.value; final showAutoCodeInput = controller.showAutoCodeInput.value;
final showSkipBindingButton = controller.showSkipBindingButton.value; final showSkipBindingButton = controller.showSkipBindingButton.value;
@ -470,7 +475,28 @@ class PairingView extends GetView<PairingController> {
], ],
), ),
), ),
if (showBindButton)
ElevatedButton(
onPressed: () => controller.confirmBindDevice(),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.blue,
foregroundColor: Colors.white,
padding: EdgeInsets.symmetric(vertical: 16.h),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16.r),
),
elevation: 0,
minimumSize: Size(double.infinity, 56.h),
),
child: Text(
"绑定",
style: TextStyle(
fontSize: 16.sp,
fontWeight: FontWeight.w600,
),
),
),
SizedBox(height: 12.h),
// 显示重试按钮 // 显示重试按钮
if (showRetryButton) if (showRetryButton)
ElevatedButton( ElevatedButton(
@ -578,6 +604,25 @@ class PairingView extends GetView<PairingController> {
), ),
), ),
], ],
//重新检测
if (showRetryChectButton) ...[
SizedBox(height: 12.h),
TextButton(
onPressed: () => controller.onRetryTap(),
style: TextButton.styleFrom(
foregroundColor: Color(0xFF0066CC),
padding: EdgeInsets.symmetric(vertical: 12.h),
minimumSize: Size(double.infinity, 48.h),
),
child: Text(
'重新扫描'.tr, // 稍后再说
style: TextStyle(
fontSize: 16.sp,
fontWeight: FontWeight.w500,
),
),
),
],
], ],
), ),
); );

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

@ -152,7 +152,10 @@ class PairingView extends GetView<PairingController> {
iconData = Icons.verified_user; iconData = Icons.verified_user;
iconColor = Colors.orange; iconColor = Colors.orange;
break; break;
case PairingState.binding:
iconData = Icons.blind;
iconColor = Colors.blue;
break;
case PairingState.associating: case PairingState.associating:
iconData = Icons.link; iconData = Icons.link;
iconColor = Colors.blue; iconColor = Colors.blue;
@ -195,6 +198,7 @@ class PairingView extends GetView<PairingController> {
/// 构建按钮 /// 构建按钮
Widget _buildButtons(bool isDarkMode) { Widget _buildButtons(bool isDarkMode) {
final showConnectButton = controller.showConnectButton.value; final showConnectButton = controller.showConnectButton.value;
final showBindButton = controller.showBindButton.value;
final showRetryButton = controller.showRetryButton.value; final showRetryButton = controller.showRetryButton.value;
final showAutoCodeInput = controller.showAutoCodeInput.value; final showAutoCodeInput = controller.showAutoCodeInput.value;
final showSkipButton = controller.showSkipButton.value; final showSkipButton = controller.showSkipButton.value;
@ -209,6 +213,19 @@ class PairingView extends GetView<PairingController> {
isDarkMode, isDarkMode,
), ),
// 绑定按钮
if (showBindButton)
TextButton(
onPressed: controller.confirmBindDevice,
child: Text(
'绑定',
style: TextStyle(
fontSize: 14.sp,
color: isDarkMode ? Colors.grey[400] : Colors.grey[600],
),
),
),
if (showRetryButton) if (showRetryButton)
_buildPrimaryButton( _buildPrimaryButton(
'重试', '重试',
@ -217,6 +234,7 @@ class PairingView extends GetView<PairingController> {
), ),
SizedBox(height: 12.h), SizedBox(height: 12.h),
if (showAutoCodeInput) if (showAutoCodeInput)
// 自动输入代码按钮 // 自动输入代码按钮
SizedBox( SizedBox(

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

@ -505,8 +505,8 @@ class SettingsView extends GetView<SettingsController> {
// 清除配对设备 // 清除配对设备
_buildSimpleNavigationSetting( _buildSimpleNavigationSetting(
title: '清除配对设备', title: '设备管理',
subtitle: '清除所有BLE配对设备记录', subtitle: '设备管理列表',
icon: Icons.bluetooth_disabled, icon: Icons.bluetooth_disabled,
iconBgColor: isDarkMode iconBgColor: isDarkMode
? Colors.red[900]!.withOpacity(0.3) ? Colors.red[900]!.withOpacity(0.3)
@ -514,9 +514,7 @@ class SettingsView extends GetView<SettingsController> {
iconColor: isDarkMode ? Colors.red[300]! : Colors.red[600]!, iconColor: isDarkMode ? Colors.red[300]! : Colors.red[600]!,
titleColor: isDarkMode ? Colors.white : null, titleColor: isDarkMode ? Colors.white : null,
subtitleColor: isDarkMode ? Colors.white70 : null, subtitleColor: isDarkMode ? Colors.white70 : null,
onTap: () { onTap: () => Get.toNamed(Routes.devices),
controller.clearBleDevices();
},
isDarkMode: isDarkMode, isDarkMode: isDarkMode,
), ),
Divider( Divider(

7
lib/routes/app_pages.dart

@ -1,3 +1,5 @@
import 'package:deep_voice/modules/devices/bindings/devices_binding.dart';
import 'package:deep_voice/modules/devices/views/devices_view.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import '../modules/feedback/bindings/feedback_bindings.dart'; import '../modules/feedback/bindings/feedback_bindings.dart';
import '../modules/feedback/views/feedback_view.dart'; import '../modules/feedback/views/feedback_view.dart';
@ -233,5 +235,10 @@ abstract class AppPages {
transition: Transition.rightToLeft, transition: Transition.rightToLeft,
transitionDuration: Duration(milliseconds: 250), transitionDuration: Duration(milliseconds: 250),
), ),
GetPage(
name: Routes.devices,
page: () => const DevicesView(),
binding: DevicesBinding(),
),
]; ];
} }

3
lib/routes/app_routes.dart

@ -52,4 +52,7 @@ abstract class Routes {
// 实时语音对话 // 实时语音对话
static const realtime = '/realtime'; static const realtime = '/realtime';
// 设备管理
static const devices = '/devices';
} }

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

@ -231,6 +231,7 @@ object BleService {
return true return true
} catch (e: Exception) { } catch (e: Exception) {
Log.e(TAG, "启动扫描失败: ${e.message}", e) Log.e(TAG, "启动扫描失败: ${e.message}", e)
isScanning = false
return false return false
} }
} }
@ -255,6 +256,7 @@ object BleService {
return true return true
} catch (e: Exception) { } catch (e: Exception) {
Log.e(TAG, "停止扫描失败: ${e.message}", e) Log.e(TAG, "停止扫描失败: ${e.message}", e)
isScanning = false
return false return false
} }
} }

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

@ -304,6 +304,10 @@ class BleServicePlugin : FlutterPlugin, MethodCallHandler, ActivityAware,
Log.d(TAG, "getDeviceInfo $info") Log.d(TAG, "getDeviceInfo $info")
result.success(info) result.success(info)
} }
"disconnect" -> {
BleService.disconnect();
result.success(null)
}
else -> { else -> {
result.notImplemented() result.notImplemented()
} }

Loading…
Cancel
Save