Browse Source

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

weicu
lxm 1 year ago
parent
commit
9146ceca7d
  1. 7
      lib/data/models/appconfig.dart
  2. 24
      lib/data/models/appconfig_model.dart
  3. 16
      lib/data/models/appconfig_model.g.dart
  4. 5
      lib/data/models/user_Info.dart
  5. 13
      lib/data/services/network/api.dart
  6. 4
      lib/data/services/network/nw_api.dart
  7. 14
      lib/modules/devices/controllers/devices_controller.dart
  8. 6
      lib/modules/devices/views/devices_view.dart
  9. 5
      lib/modules/login/controllers/login_controller.dart
  10. 65
      lib/modules/meeting/controllers/meeting_connect_controller.dart
  11. 26
      lib/modules/meeting/controllers/meeting_controller.dart
  12. 36
      lib/modules/meeting/views/meeting_connect_view.dart
  13. 3
      lib/modules/meeting/views/meeting_view.dart
  14. 2
      lib/modules/ota_upgrade/controllers/ota_upgrade_controller.dart
  15. 27
      lib/modules/pairing/controllers/pairing_controller.dart
  16. 14
      local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/AgentService.kt
  17. 144
      local_plugins/chat_api/android/src/main/kotlin/com/yunqiinnovation/chat_api/SystemFunctionHandler.kt
  18. 102
      local_plugins/open_ai_service/android/src/main/kotlin/com/yunqiinnovation/open_ai_service/SystemFunctionHandler.kt

7
lib/data/models/appconfig.dart

@ -4,7 +4,6 @@ import 'package:deep_voice/data/models/appconfig_model.dart';
class AppConfig { class AppConfig {
static late UserGetAppConfigResp config; static late UserGetAppConfigResp config;
// 可选:提供初始化方法 // 可选:提供初始化方法
static void initialize(UserGetAppConfigResp resp) { static void initialize(UserGetAppConfigResp resp) {
config = resp; config = resp;
@ -25,4 +24,10 @@ class AppConfig {
final foundAgent = config.agents.firstWhere((agent) => agent.id == agentId); final foundAgent = config.agents.firstWhere((agent) => agent.id == agentId);
return foundAgent.systemPrompt; return foundAgent.systemPrompt;
} }
static List<String> allproductNames() {
List<String> names =
config.products.map((user) => user.devicename).toList();
return names;
}
} }

24
lib/data/models/appconfig_model.dart

@ -7,8 +7,13 @@ class UserGetAppConfigResp {
final Map<String, String> env; final Map<String, String> env;
final List<DBAgent> agents; final List<DBAgent> agents;
final Map<String, DBMCPServer> mcps; final Map<String, DBMCPServer> mcps;
final List<DBProduct> products;
UserGetAppConfigResp( UserGetAppConfigResp(
{required this.env, required this.agents, required this.mcps}); {required this.env,
required this.agents,
required this.mcps,
required this.products});
factory UserGetAppConfigResp.fromJson(Map<String, dynamic> json) => factory UserGetAppConfigResp.fromJson(Map<String, dynamic> json) =>
_$UserGetAppConfigRespFromJson(json); _$UserGetAppConfigRespFromJson(json);
@ -63,3 +68,20 @@ class DBMCPServer {
_$DBMCPServerFromJson(json); _$DBMCPServerFromJson(json);
Map<String, dynamic> toJson() => _$DBMCPServerToJson(this); Map<String, dynamic> toJson() => _$DBMCPServerToJson(this);
} }
@JsonSerializable()
class DBProduct {
final String id;
final int factoryid;
final String devicename;
DBProduct({
required this.id,
required this.factoryid,
required this.devicename,
});
factory DBProduct.fromJson(Map<String, dynamic> json) =>
_$DBProductFromJson(json);
Map<String, dynamic> toJson() => _$DBProductToJson(this);
}

16
lib/data/models/appconfig_model.g.dart

@ -16,6 +16,9 @@ UserGetAppConfigResp _$UserGetAppConfigRespFromJson(
mcps: (json['mcps'] as Map<String, dynamic>).map( mcps: (json['mcps'] as Map<String, dynamic>).map(
(k, e) => MapEntry(k, DBMCPServer.fromJson(e as Map<String, dynamic>)), (k, e) => MapEntry(k, DBMCPServer.fromJson(e as Map<String, dynamic>)),
), ),
products: (json['products'] as List<dynamic>)
.map((e) => DBProduct.fromJson(e as Map<String, dynamic>))
.toList(),
); );
Map<String, dynamic> _$UserGetAppConfigRespToJson( Map<String, dynamic> _$UserGetAppConfigRespToJson(
@ -24,6 +27,7 @@ Map<String, dynamic> _$UserGetAppConfigRespToJson(
'env': instance.env, 'env': instance.env,
'agents': instance.agents, 'agents': instance.agents,
'mcps': instance.mcps, 'mcps': instance.mcps,
'products': instance.products,
}; };
DBAgent _$DBAgentFromJson(Map<String, dynamic> json) => DBAgent( DBAgent _$DBAgentFromJson(Map<String, dynamic> json) => DBAgent(
@ -66,3 +70,15 @@ Map<String, dynamic> _$DBMCPServerToJson(DBMCPServer instance) =>
'url': instance.url, 'url': instance.url,
'tools': instance.tools, 'tools': instance.tools,
}; };
DBProduct _$DBProductFromJson(Map<String, dynamic> json) => DBProduct(
id: json['id'] as String,
factoryid: (json['factoryid'] as num).toInt(),
devicename: json['devicename'] as String,
);
Map<String, dynamic> _$DBProductToJson(DBProduct instance) => <String, dynamic>{
'id': instance.id,
'factoryid': instance.factoryid,
'devicename': instance.devicename,
};

5
lib/data/models/user_Info.dart

@ -23,6 +23,7 @@ class User {
int viplv = 0; //添加会员等级字段,0表示普通用户 int viplv = 0; //添加会员等级字段,0表示普通用户
String earphonename; String earphonename;
String earphonemac; String earphonemac;
String earphonelicense;
String earphoneauthcode; String earphoneauthcode;
// final Int64 lastbettime; // final Int64 lastbettime;
// final Int64 createtime; // final Int64 createtime;
@ -44,6 +45,7 @@ class User {
required this.viplv, // 添加默认值 required this.viplv, // 添加默认值
required this.earphonename, required this.earphonename,
required this.earphonemac, required this.earphonemac,
required this.earphonelicense,
required this.earphoneauthcode, required this.earphoneauthcode,
// required this.lastbettime, // required this.lastbettime,
// required this.createtime, // required this.createtime,
@ -74,6 +76,7 @@ class User {
viplv: (userData?['viplv'] as int?) ?? 0, // 提供默认值 viplv: (userData?['viplv'] as int?) ?? 0, // 提供默认值
earphonename: (userData?['earphonename'] as String?) ?? '', earphonename: (userData?['earphonename'] as String?) ?? '',
earphonemac: (userData?['earphonemac'] as String?) ?? '', earphonemac: (userData?['earphonemac'] as String?) ?? '',
earphonelicense: (userData?['earphonelicense'] as String?) ?? '',
earphoneauthcode: (userData?['earphoneauthcode'] as String?) ?? '', earphoneauthcode: (userData?['earphoneauthcode'] as String?) ?? '',
// lastbettime: _parseTimestamp(userData['lastbettime']), // lastbettime: _parseTimestamp(userData['lastbettime']),
// createtime: _parseTimestamp(userData['createtime']), // createtime: _parseTimestamp(userData['createtime']),
@ -102,6 +105,7 @@ class User {
viplv: (userData?['viplv'] as int?) ?? 0, // 提供默认值 viplv: (userData?['viplv'] as int?) ?? 0, // 提供默认值
earphonename: (userData?['earphonename'] as String?) ?? '', earphonename: (userData?['earphonename'] as String?) ?? '',
earphonemac: (userData?['earphonemac'] as String?) ?? '', earphonemac: (userData?['earphonemac'] as String?) ?? '',
earphonelicense: (userData?['earphonelicense'] as String?) ?? '',
earphoneauthcode: (userData?['earphoneauthcode'] as String?) ?? '', earphoneauthcode: (userData?['earphoneauthcode'] as String?) ?? '',
// lastbettime: _parseTimestamp(userData['lastbettime']), // lastbettime: _parseTimestamp(userData['lastbettime']),
// createtime: _parseTimestamp(userData['createtime']), // createtime: _parseTimestamp(userData['createtime']),
@ -128,6 +132,7 @@ class User {
'earphonename': earphonename, 'earphonename': earphonename,
'earphonemac': earphonemac, 'earphonemac': earphonemac,
"earphonelicense": earphonelicense,
'earphoneauthcode': earphoneauthcode, 'earphoneauthcode': earphoneauthcode,
// 'lastbettime': lastbettime, // 'lastbettime': lastbettime,

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

@ -74,10 +74,19 @@ class Api {
} }
//app获取设备信息 //app获取设备信息
static getequipmentinfo([params]) { static getproduct([params]) {
return DioManager().request( return DioManager().request(
NWMethod.post, NWMethod.post,
'/api/home/user_getequipmentinfo', '/api/home/user_getproduct',
params: params,
);
}
//app获取设备信息
static getproducts([params]) {
return DioManager().request(
NWMethod.post,
'/api/home/user_getproducts',
params: params, params: params,
); );
} }

4
lib/data/services/network/nw_api.dart

@ -1,5 +1,5 @@
class NWApi { class NWApi {
// static const baseApi = "https://www.deapsound.com"; // static const baseApi = "https://www.deapsound.com";
//static const baseApi = "https://api.deapsound.com"; static const baseApi = "https://api.deapsound.com";
static const baseApi = "https://api.ideapsound.com"; // static const baseApi = "https://api.ideapsound.com";
} }

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

@ -13,12 +13,14 @@ import 'package:get/get_state_manager/src/simple/get_controllers.dart';
class DevicesController extends GetxController { class DevicesController extends GetxController {
late final BleManager _bleManager; late final BleManager _bleManager;
//耳机绑定 //耳机绑定
final isearphone = false.obs; final earphonestate = 0.obs;
final earphoneName = "".obs; final earphoneName = "".obs;
final earphoneMac = "".obs; final earphoneMac = "".obs;
final earphoneLicense = "".obs;
final earphoneCode = "".obs; final earphoneCode = "".obs;
//录音笔 //录音笔
final voicepenstate = 0.obs;
final isvoicepen = false.obs; final isvoicepen = false.obs;
final voicepenName = "".obs; final voicepenName = "".obs;
final voicepenMac = "".obs; final voicepenMac = "".obs;
@ -29,9 +31,13 @@ class DevicesController extends GetxController {
_bleManager = Get.find<BleManager>(); _bleManager = Get.find<BleManager>();
earphoneName.value = User.instance.earphonename; earphoneName.value = User.instance.earphonename;
earphoneMac.value = User.instance.earphonemac; earphoneMac.value = User.instance.earphonemac;
earphoneLicense.value = User.instance.earphonelicense;
earphoneCode.value = User.instance.earphoneauthcode; earphoneCode.value = User.instance.earphoneauthcode;
if (earphoneCode.value != "") { if (earphoneCode.value != "") {
isearphone.value = true; earphonestate.value = 1;
}
if (earphoneLicense.value != "") {
earphonestate.value = 2;
} }
} }
@ -48,8 +54,8 @@ class DevicesController extends GetxController {
if (response != null) { if (response != null) {
User.instance.earphonename = ""; User.instance.earphonename = "";
User.instance.earphonemac = ""; User.instance.earphonemac = "";
User.instance.earphoneauthcode = ""; User.instance.earphonelicense = "";
isearphone.value = false; earphonestate.value = 0;
_bleManager.clearPairedDevices(); _bleManager.clearPairedDevices();
_bleManager.disconnect(); _bleManager.disconnect();
Get.toNamed(Routes.pairing); Get.toNamed(Routes.pairing);

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

@ -93,10 +93,10 @@ class DevicesView extends GetView<DevicesController> {
() => () =>
// 耳机列表 // 耳机列表
_buildSimpleNavigationSetting( _buildSimpleNavigationSetting(
title: controller.isearphone.value title: controller.earphonestate.value != 0
? controller.earphoneName.value ? controller.earphoneName.value
: "未绑定", : "未绑定",
subtitle: controller.isearphone.value subtitle: controller.earphonestate.value != 0
? controller.earphoneMac.value ? controller.earphoneMac.value
: "还未绑定设备", : "还未绑定设备",
icon: Icons.headphones, icon: Icons.headphones,
@ -110,7 +110,7 @@ class DevicesView extends GetView<DevicesController> {
titleColor: isDarkMode ? Colors.white : null, titleColor: isDarkMode ? Colors.white : null,
subtitleColor: isDarkMode ? Colors.white70 : null, subtitleColor: isDarkMode ? Colors.white70 : null,
isDarkMode: isDarkMode, isDarkMode: isDarkMode,
showbutt: controller.isearphone.value, showbutt: controller.earphonestate == 2,
onPressed: () { onPressed: () {
controller.unbindearphone(); controller.unbindearphone();
}, },

5
lib/modules/login/controllers/login_controller.dart

@ -11,7 +11,6 @@ import 'weixinLogin.dart';
import '../../../data/services/network/api.dart'; import '../../../data/services/network/api.dart';
import '../../../../data/models/user_Info.dart'; import '../../../../data/models/user_Info.dart';
import '../../common/webview_page.dart'; import '../../common/webview_page.dart';
//import 'package:shared_preferences/shared_preferences.dart';
import 'package:get_storage/get_storage.dart'; import 'package:get_storage/get_storage.dart';
import 'package:device_info_plus/device_info_plus.dart'; import 'package:device_info_plus/device_info_plus.dart';
import 'dart:io'; import 'dart:io';
@ -914,10 +913,6 @@ class LoginController extends GetxController {
print('getAppConfig:进来了'); print('getAppConfig:进来了');
// 根据API设计调整 // 根据API设计调整
final resp = UserGetAppConfigResp.fromJson(response); final resp = UserGetAppConfigResp.fromJson(response);
// 存储用户信息(示例)
// _storage.write('ENV', resp.env);
// _storage.write('AGENTS', resp.agents);
// _storage.write('MCPS', resp.mcps);
AppConfig.initialize(resp); AppConfig.initialize(resp);
return true; return true;
} catch (e) { } catch (e) {

65
lib/modules/meeting/controllers/meeting_connect_controller.dart

@ -1,15 +1,19 @@
import 'dart:async'; import 'dart:async';
import 'dart:io';
import 'package:flutter_blue_plus/flutter_blue_plus.dart'; import 'package:flutter_blue_plus/flutter_blue_plus.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:path_provider/path_provider.dart';
import 'package:wifi_iot/wifi_iot.dart'; import 'package:wifi_iot/wifi_iot.dart';
import '../../../core/utils/ftpconnect/ftpconnect.dart'; import '../../../core/utils/ftpconnect/ftpconnect.dart';
import 'meeting_controller.dart';
class MeetingConnectController extends GetxController { class MeetingConnectController extends GetxController {
final _meetingController = Get.find<MeetingController>();
RxString stateStr = '搜索中...'.obs; RxString stateStr = '搜索中...'.obs;
RxBool isRescan = false.obs; RxBool isRescan = false.obs;
RxBool isImportedFiles = false.obs;
RxString importedFilesStr = ''.obs; RxString importedFilesStr = ''.obs;
late StreamSubscription<List<ScanResult>> _scanResultsSubscription; late StreamSubscription<List<ScanResult>> _scanResultsSubscription;
@ -30,6 +34,11 @@ class MeetingConnectController extends GetxController {
FTPConnect? _ftpConnect; FTPConnect? _ftpConnect;
RxBool isDownload = false.obs;
RxDouble downloadProgress = 0.0.obs;
RxInt totalReceived = 0.obs;
RxInt fileSize = 0.obs;
@override @override
void onInit() { void onInit() {
super.onInit(); super.onInit();
@ -141,10 +150,10 @@ class MeetingConnectController extends GetxController {
switch (command) { switch (command) {
case 0x0d: // 是否有导入文件 case 0x0d: // 是否有导入文件
if (value[3] == 0x01) { if (value[3] == 0x01) {
isImportedFiles.value = true; stateStr.value = '正在开启Wi-Fi...';
importedFilesStr.value = '检查到有新文件,是否导入'; importedFilesStr.value = '检查到有新文件,正在准备导入';
sendData([0x5a, 0x02, 0x87, 0x01]);
} else { } else {
isImportedFiles.value = false;
importedFilesStr.value = '没有新文件需要导入'; importedFilesStr.value = '没有新文件需要导入';
} }
break; break;
@ -208,10 +217,52 @@ class MeetingConnectController extends GetxController {
); );
await _ftpConnect?.connect(); await _ftpConnect?.connect();
_ftpConnect?.listCommand = ListCommand.LIST; _ftpConnect?.listCommand = ListCommand.LIST;
List<FTPEntry>? _listDirectory = List<FTPEntry>? listDirectory = await _ftpConnect?.listDirectoryContent();
await _ftpConnect?.listDirectoryContent(); if (listDirectory != null && listDirectory.isNotEmpty) {
print(_listDirectory); stateStr.value = '正在下载文件...';
isDownload.value = true;
int totalSize = listDirectory.fold(
0,
(sum, entry) => sum + (entry.size ?? 0),
);
for (var i = 0; i < listDirectory.length; i++) {
importedFilesStr.value =
'共${listDirectory.length}个文件,正在下载第${i + 1}个文件';
int previousSize = i > 0 ? listDirectory[i - 1].size ?? 0 : 0;
FTPEntry element = listDirectory[i];
await _downloadFile(totalSize, previousSize, element);
await _ftpConnect?.deleteFile(element.name);
}
stateStr.value = '下载完成';
importedFilesStr.value = '下载完成,共${listDirectory.length}个文件,2秒后自动返回';
_disconnect();
Future.delayed(const Duration(seconds: 2), () {
Get.back();
});
}
}
}
Future<void> _downloadFile(
int totalSize, int previousSize, FTPEntry entry) async {
final directory = await getApplicationDocumentsDirectory();
final directoryDir = Directory('${directory.path}/RecordingPen');
if (!await directoryDir.exists()) {
await directoryDir.create(recursive: true);
} }
final String filePath = '${directory.path}/RecordingPen/${entry.name}';
final File localFile = File(filePath);
await _ftpConnect?.downloadFileWithRetry(
entry.name,
localFile,
pRetryCount: 3,
onProgress: (double progress, int received, int size) {
downloadProgress.value = (previousSize + received) / totalSize;
totalReceived.value = received;
fileSize.value = size;
},
);
await _meetingController.insertMeetingAudio(entry.name, filePath, '');
} }
String _decodePassword(List<int> data) { String _decodePassword(List<int> data) {

26
lib/modules/meeting/controllers/meeting_controller.dart

@ -190,13 +190,11 @@ class MeetingController extends GetxController {
int addUp = 0; int addUp = 0;
final appDir = await appDocDir; final appDir = await appDocDir;
// 定义源目录列表
final sourceDirs = [ final sourceDirs = [
Directory("${appDir.path}/MeetingAudio/"), Directory("${appDir.path}/MeetingAudio/"),
Directory("${appDir.path}/DestAudio/"), Directory("${appDir.path}/DestAudio/"),
]; ];
// 创建目标目录(如果不存在)
final destDir = Directory("${appDir.path}/LocalAudio/"); final destDir = Directory("${appDir.path}/LocalAudio/");
if (!await destDir.exists()) { if (!await destDir.exists()) {
await destDir.create(recursive: true); await destDir.create(recursive: true);
@ -212,8 +210,6 @@ class MeetingController extends GetxController {
} }
} }
// 此时 allFiles 包含 MeetingAudio 和 DestAudio 目录下所有的文件
for (final fileEntity in allFiles) { for (final fileEntity in allFiles) {
try { try {
final file = File(fileEntity.path); final file = File(fileEntity.path);
@ -334,9 +330,21 @@ class MeetingController extends GetxController {
} }
} }
await _audioPlayer.setAudioSource(AudioSource.file(filePath!));
var audiourl = var audiourl =
await UploadOss.upload(filepath: filePath, rootDir: 'ExternalAudio'); await UploadOss.upload(filepath: filePath, rootDir: 'ExternalAudio');
final meetingId = await insertMeetingAudio(fileName, filePath, audiourl);
if (isParamProvided) {
Get.toNamed(Routes.meetingDetails, arguments: {
'id': meetingId,
});
}
EasyLoading.dismiss();
}
Future insertMeetingAudio(
String fileName, String filePath, String audiourl) async {
await _audioPlayer.setAudioSource(AudioSource.file(filePath));
int seconds = _audioPlayer.duration?.inSeconds ?? 0; int seconds = _audioPlayer.duration?.inSeconds ?? 0;
int creationTime = DateTime.now().millisecondsSinceEpoch; int creationTime = DateTime.now().millisecondsSinceEpoch;
Map<String, Object?> data = { Map<String, Object?> data = {
@ -357,13 +365,7 @@ class MeetingController extends GetxController {
}); });
addQuantity++; addQuantity++;
} }
return meetingId;
if (isParamProvided) {
Get.toNamed(Routes.meetingDetails, arguments: {
'id': meetingId,
});
}
EasyLoading.dismiss();
} }
Future<String> _saveFile(PlatformFile file) async { Future<String> _saveFile(PlatformFile file) async {

36
lib/modules/meeting/views/meeting_connect_view.dart

@ -76,19 +76,29 @@ class MeetingConnectView extends GetView<MeetingConnectController> {
), ),
), ),
), ),
Obx( Obx(() => controller.isDownload.value
() => controller.isImportedFiles.value ? Column(
? Padding( children: [
padding: EdgeInsets.symmetric(vertical: 10.w), Container(
child: _button('导入文件', Icons.file_download, onTap: () { width: 300.w,
controller.stateStr.value = '正在开启Wi-Fi...'; height: 3.w,
controller.importedFilesStr.value = ''; margin: EdgeInsets.symmetric(vertical: 10.w),
controller.isImportedFiles.value = false; child: LinearProgressIndicator(
controller.sendData([0x5a, 0x02, 0x87, 0x01]); value: controller.downloadProgress.value,
}), valueColor:
) const AlwaysStoppedAnimation<Color>(Colors.red),
: const SizedBox(), ),
), ),
Text(
'${controller.totalReceived.value} / ${controller.fileSize.value} bytes',
style: TextStyle(
fontSize: 14.sp,
color: isDarkMode ? Colors.white70 : Colors.black54,
),
),
],
)
: const SizedBox()),
], ],
), ),
); );

3
lib/modules/meeting/views/meeting_view.dart

@ -423,6 +423,7 @@ class MeetingView extends GetView<MeetingController> {
PreferredSizeWidget _buildAppBar(BuildContext context, bool isDarkMode) { PreferredSizeWidget _buildAppBar(BuildContext context, bool isDarkMode) {
return AppBar( return AppBar(
backgroundColor: isDarkMode ? Colors.black : Colors.white, backgroundColor: isDarkMode ? Colors.black : Colors.white,
surfaceTintColor: Colors.transparent,
elevation: 0.5, elevation: 0.5,
leading: IconButton( leading: IconButton(
icon: Icon( icon: Icon(
@ -459,7 +460,7 @@ class MeetingView extends GetView<MeetingController> {
() => ListView.builder( () => ListView.builder(
controller: controller.listController, controller: controller.listController,
itemCount: controller.dataList.length, itemCount: controller.dataList.length,
padding: EdgeInsets.symmetric(horizontal: 12.w, vertical: 5.w), padding: EdgeInsets.fromLTRB(12.w, 5.w, 12.w, 90.w),
itemBuilder: (BuildContext context, int index) { itemBuilder: (BuildContext context, int index) {
MeetingModel data = MeetingModel.fromJson(controller.dataList[index]); MeetingModel data = MeetingModel.fromJson(controller.dataList[index]);
return _item(context, isDarkMode, data); return _item(context, isDarkMode, data);

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

@ -188,7 +188,7 @@ class OtaUpgradeController extends GetxController {
jlota.init(); jlota.init();
// API请求 // API请求
final response = await Api.getequipmentinfo({ final response = await Api.getproduct({
"id": deviceInfo.firmwareProductId, "id": deviceInfo.firmwareProductId,
}); });

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

@ -431,9 +431,12 @@ class PairingController extends GetxController {
try { try {
Logger.i(_tag, '开始验证设备: ${device.name} (${device.address})'); Logger.i(_tag, '开始验证设备: ${device.name} (${device.address})');
final result = await authbindingDevice(device); final result = await authbindingDevice(device);
if (result == 2) { if (result == 10) {
//验证失败 进入手动授权 //验证失败 进入手动授权
await connectble(device); await connectble(device);
} else if (result == 9) {
Logger.i(_tag, 'BLE已连接,直接完成配对');
_transition(PairingState.completed);
} else if (result == 1) { } else if (result == 1) {
//跳转绑定界面 //跳转绑定界面
//验证失败 进入手动授权 //验证失败 进入手动授权
@ -557,15 +560,22 @@ class PairingController extends GetxController {
//自动绑定设备 0 未合作商 1 合作商 2,已绑定 //自动绑定设备 0 未合作商 1 合作商 2,已绑定
Future<int> authbindingDevice(BluetoothDevice device) async { Future<int> authbindingDevice(BluetoothDevice device) async {
if (User.instance.earphonemac == device.address) { if (User.instance.earphonelicense != "" &&
User.instance.earphonemac == device.address) {
// 已经绑定 // 已经绑定
return 2; return 10;
}
if (User.instance.earphoneauthcode != "" &&
User.instance.earphonemac == device.address) {
// 已经绑定
return 9;
} }
// 检查设备名称是否包含 DeepSound 或 DeapSound // 检查设备名称是否包含 DeepSound 或 DeapSound
final deviceName = device.name; final deviceName = device.name;
print('获取到 deviceName: $deviceName');
if (deviceName.contains('DeepSound') || deviceName.contains('DeapSound')) { final names = AppConfig.allproductNames();
print('获取到 deviceName: $deviceName names:$names');
if (names.contains(deviceName)) {
await _bleManager.startScan(deviceName); await _bleManager.startScan(deviceName);
await Future.delayed(const Duration(seconds: 1)); await Future.delayed(const Duration(seconds: 1));
// 尝试最多读取 3 次设备信息,每次间隔 1 秒 // 尝试最多读取 3 次设备信息,每次间隔 1 秒
@ -610,7 +620,7 @@ class PairingController extends GetxController {
Logger.i(_tag, '绑定设备成功'); Logger.i(_tag, '绑定设备成功');
User.instance.earphonemac = currentDevice?.address ?? ''; User.instance.earphonemac = currentDevice?.address ?? '';
User.instance.earphonename = currentDevice?.name ?? ''; User.instance.earphonename = currentDevice?.name ?? '';
User.instance.earphoneauthcode = deviceCode.value; User.instance.earphonelicense = deviceCode.value;
_transition(PairingState.verifying); _transition(PairingState.verifying);
_verifyDevice(_currentClassicDevice!); _verifyDevice(_currentClassicDevice!);
return; return;
@ -642,8 +652,9 @@ class PairingController extends GetxController {
void onAutoCodeInputTap() async { void onAutoCodeInputTap() async {
// 调用BLE管理器的自动绑定方法 // 调用BLE管理器的自动绑定方法
final response = await Api.bindauthcode({ final response = await Api.bindauthcode({
"code": codeInputController.text.trim(),
"earphonname": currentDevice?.name,
"earphonemac": currentDevice?.address, "earphonemac": currentDevice?.address,
"earphoneauthcode": codeInputController.text.trim(),
}); });
Logger.i(_tag, '手动授权结果 ${response}'); Logger.i(_tag, '手动授权结果 ${response}');
if (response != null) { if (response != null) {

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

@ -734,6 +734,7 @@ object AgentService : CoroutineScope {
) { ) {
Log.d(TAG, "内部方法:通用的ChatAPI处理逻辑") Log.d(TAG, "内部方法:通用的ChatAPI处理逻辑")
// 如果有正在进行的AI流式输出,先停止它 // 如果有正在进行的AI流式输出,先停止它
audioPlayer?.stopAudio()
stopAiStream() stopAiStream()
stopTts() stopTts()
// 记录新的AI任务 // 记录新的AI任务
@ -748,6 +749,7 @@ object AgentService : CoroutineScope {
val responseBuilder = StringBuilder() val responseBuilder = StringBuilder()
var aiMetadata:String = "" var aiMetadata:String = ""
var userMetadata = JSONObject() var userMetadata = JSONObject()
var reply = false; //是否回复
var broadcast = true //是否播发 var broadcast = true //是否播发
// 如果有图片,添加图片信息到元数据 // 如果有图片,添加图片信息到元数据
if (hasImage) { if (hasImage) {
@ -814,10 +816,10 @@ object AgentService : CoroutineScope {
return return
} }
responseBuilder.append(token) responseBuilder.append(token)
if (speakResponse && broadcast) { if (speakResponse && reply && broadcast) {
ttsService?.speakStream(token) ttsService?.speakStream(token)
} }
if (broadcast) { if (reply && token.isNotEmpty()) {
// 发送流式回复token // 发送流式回复token
sendEvent("assistant_token", mapOf("token" to token)) sendEvent("assistant_token", mapOf("token" to token))
} }
@ -829,7 +831,7 @@ object AgentService : CoroutineScope {
override fun onComplete(sessionid:String) { override fun onComplete(sessionid:String) {
try { try {
// 视情况决定是否朗读回复 // 视情况决定是否朗读回复
if (speakResponse && broadcast && sessionid == currsessionId){ if (speakResponse && reply && broadcast && sessionid == currsessionId){
ttsService?.flushStream() ttsService?.flushStream()
audioPlayer?.stopAudio() audioPlayer?.stopAudio()
} }
@ -842,7 +844,10 @@ object AgentService : CoroutineScope {
if (hasImage) { if (hasImage) {
responseData["hasImage"] = true responseData["hasImage"] = true
} }
sendEvent("assistant_response", responseData) if (reply){
sendEvent("assistant_response", responseData)
}
if (response.isNotEmpty()) { if (response.isNotEmpty()) {
// 添加AI回复到历史记录 // 添加AI回复到历史记录
addToHistoryMessages(createAssistantMessage(response)) addToHistoryMessages(createAssistantMessage(response))
@ -903,6 +908,7 @@ object AgentService : CoroutineScope {
val resultmap = autoHandleFcunCallResult(name, functionCallResult); val resultmap = autoHandleFcunCallResult(name, functionCallResult);
aiMetadata = resultmap["meta"] as String; aiMetadata = resultmap["meta"] as String;
broadcast = resultmap["broadcast"] as Boolean; broadcast = resultmap["broadcast"] as Boolean;
reply = !(resultmap["notreply"] as Boolean);
sendEvent( sendEvent(
"function_call_result", mapOf( "function_call_result", mapOf(
"function_call" to functionCall.toString(), "function_call" to functionCall.toString(),

144
local_plugins/chat_api/android/src/main/kotlin/com/yunqiinnovation/chat_api/SystemFunctionHandler.kt

@ -8,6 +8,7 @@ import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.location.LocationManager import android.location.LocationManager
import android.media.AudioManager
import android.net.Uri import android.net.Uri
import android.os.Build import android.os.Build
import android.provider.AlarmClock import android.provider.AlarmClock
@ -232,6 +233,42 @@ class SystemFunctionHandler(private val context: Context? = null) {
), ),
handler = ExitNavigationHandler(context) handler = ExitNavigationHandler(context)
) )
// 增加音量
client.registerLocalFunction(
name = "add_volume",
description = "增大音量",
parameters = mapOf(
"type" to "object",
"properties" to emptyMap<String, Any>(),
"required" to emptyList<String>()
),
handler = AddVolumeHandler(context)
)
// 增加音量
client.registerLocalFunction(
name = "reduce_volume",
description = "降低音量",
parameters = mapOf(
"type" to "object",
"properties" to emptyMap<String, Any>(),
"required" to emptyList<String>()
),
handler = ReduceVolumeHandler(context)
)
// 设置静音
client.registerLocalFunction(
name = "mute_volume",
description = "设置静音",
parameters = mapOf(
"type" to "object",
"properties" to emptyMap<String, Any>(),
"required" to emptyList<String>()
),
handler = MuteVolumeHandler(context)
)
} }
/** /**
@ -1320,4 +1357,109 @@ private class OpenRecorderHandler(private val context: Context?) : FunctionHandl
}.toString() }.toString()
} }
} }
} }
/**
* 添加音量处理类
*/
private class AddVolumeHandler(private val context: Context?) : FunctionHandler {
//设置音量
fun setVolume(context: Context, streamType: Int, volume: Int) {
val audioManager = context.getSystemService(Context.AUDIO_SERVICE) as AudioManager
val maxVolume = audioManager.getStreamMaxVolume(streamType)
val safeVolume = volume.coerceIn(0, maxVolume)
audioManager.setStreamVolume(streamType, safeVolume, AudioManager.FLAG_SHOW_UI)
}
fun getCurrentVolume(context: Context, streamType: Int): Int {
val audioManager = context.getSystemService(Context.AUDIO_SERVICE) as AudioManager
return audioManager.getStreamVolume(streamType)
}
fun getMaxVolume(context: Context, streamType: Int): Int {
val audioManager = context.getSystemService(Context.AUDIO_SERVICE) as AudioManager
return audioManager.getStreamMaxVolume(streamType)
}
//增加音量/减少音量
fun adjustVolume(context: Context, direction: Int) {
val audioManager = context.getSystemService(Context.AUDIO_SERVICE) as AudioManager
audioManager.adjustStreamVolume(AudioManager.STREAM_MUSIC, direction, AudioManager.FLAG_SHOW_UI)
}
override suspend fun handle(arguments: Map<String, Any>): String {
if (context == null) {
return JSONObject().apply {
put("error", JSONObject().apply {
put("code", "context_error")
put("message", "上下文未初始化")
})
}.toString()
}
// 发送广播通知退出交互
val result = adjustVolume(context, AudioManager.ADJUST_RAISE)
return JSONObject().apply {
put("result", "增加音量成功!")
put("broadcast", false)
}.toString()
}
}
/**
* 添加音量处理类
*/
private class ReduceVolumeHandler(private val context: Context?) : FunctionHandler {
//增加音量/减少音量
fun adjustVolume(context: Context, direction: Int) {
val audioManager = context.getSystemService(Context.AUDIO_SERVICE) as AudioManager
audioManager.adjustStreamVolume(AudioManager.STREAM_MUSIC, direction, AudioManager.FLAG_SHOW_UI)
}
override suspend fun handle(arguments: Map<String, Any>): String {
if (context == null) {
return JSONObject().apply {
put("error", JSONObject().apply {
put("code", "context_error")
put("message", "上下文未初始化")
})
}.toString()
}
// 发送广播通知退出交互
adjustVolume(context, AudioManager.ADJUST_LOWER)
return JSONObject().apply {
put("result", "降低音量成功!")
put("broadcast", false)
}.toString()
}
}
/**
* 添加音量处理类
*/
private class MuteVolumeHandler(private val context: Context?) : FunctionHandler {
//设置音量
fun setVolume(context: Context, streamType: Int, volume: Int) {
val audioManager = context.getSystemService(Context.AUDIO_SERVICE) as AudioManager
val maxVolume = audioManager.getStreamMaxVolume(streamType)
val safeVolume = volume.coerceIn(0, maxVolume)
audioManager.setStreamVolume(streamType, safeVolume, AudioManager.FLAG_SHOW_UI)
}
override suspend fun handle(arguments: Map<String, Any>): String {
if (context == null) {
return JSONObject().apply {
put("error", JSONObject().apply {
put("code", "context_error")
put("message", "上下文未初始化")
})
}.toString()
}
// 发送广播通知退出交互
setVolume(context, AudioManager.STREAM_MUSIC, 0)
return JSONObject().apply {
put("result", "设置静音成功!")
put("broadcast", false)
}.toString()
}
}

102
local_plugins/open_ai_service/android/src/main/kotlin/com/yunqiinnovation/open_ai_service/SystemFunctionHandler.kt

@ -30,6 +30,7 @@ import androidx.core.content.ContextCompat
import android.app.ActivityManager import android.app.ActivityManager
import android.app.Application import android.app.Application
import android.content.ComponentName import android.content.ComponentName
import android.media.AudioManager
import android.os.Process import android.os.Process
/** /**
* 系统功能处理器 - 负责调用系统API执行各种功能以及注册系统功能 * 系统功能处理器 - 负责调用系统API执行各种功能以及注册系统功能
@ -78,6 +79,13 @@ class SystemFunctionHandler(private val context: Context) {
// 注册关闭导航服务功能 // 注册关闭导航服务功能
registerStopNavigationFunction(client) registerStopNavigationFunction(client)
//增加音量
registerAddVolumeFunction(client)
//减少音量
registerReduceVolumeFunction(client)
//静音
registerMuteVolumeFunction(client)
Log.d(TAG, "所有系统功能注册完成") Log.d(TAG, "所有系统功能注册完成")
} catch (e: Exception) { } catch (e: Exception) {
Log.e(TAG, "注册系统功能失败: ${e.message}", e) Log.e(TAG, "注册系统功能失败: ${e.message}", e)
@ -438,6 +446,78 @@ class SystemFunctionHandler(private val context: Context) {
) )
Log.d(TAG, "已注册获取当前时间功能") Log.d(TAG, "已注册获取当前时间功能")
} }
/**
* 注册添加音量函数
*/
private fun registerAddVolumeFunction(client: MCPClient) {
client.registerLocalFunction(
"add_volume",
"增大音量",
JSONObject("""
{
"type": "object",
"properties": {},
"required": []
}
"""),
object : FunctionHandler {
override suspend fun handle(arguments: Map<String, Any>): String {
val result = adjustVolume(context, AudioManager.ADJUST_RAISE)
return "{\"result\": \"增加音量成功!\"}"
}
}
)
Log.d(TAG, "已注册音量调节功能")
}
/**
* 注册减少音量函数
*/
private fun registerReduceVolumeFunction(client: MCPClient) {
client.registerLocalFunction(
"reduce_volume",
"减少音量",
JSONObject("""
{
"type": "object",
"properties": {},
"required": []
}
"""),
object : FunctionHandler {
override suspend fun handle(arguments: Map<String, Any>): String {
val result = adjustVolume(context, AudioManager.ADJUST_LOWER)
return "{\"result\": \"减少音量成功!\"}"
}
}
)
Log.d(TAG, "已注册音量调节功能")
}
/**
* 注册静音函数
*/
private fun registerMuteVolumeFunction(client: MCPClient) {
client.registerLocalFunction(
"mute_volume",
"静音设置",
JSONObject("""
{
"type": "object",
"properties": {},
"required": []
}
"""),
object : FunctionHandler {
override suspend fun handle(arguments: Map<String, Any>): String {
val result = setVolume(context, AudioManager.STREAM_MUSIC, 0)
return "{\"result\": \"减少音量成功!\"}"
}
}
)
Log.d(TAG, "已注册音量调节功能")
}
/** /**
* 控制媒体播放 * 控制媒体播放
* @param action 操作类型:"play", "pause", "previous", "next" * @param action 操作类型:"play", "pause", "previous", "next"
@ -1241,6 +1321,28 @@ class SystemFunctionHandler(private val context: Context) {
} }
} }
//设置音量
fun setVolume(context: Context, streamType: Int, volume: Int) {
val audioManager = context.getSystemService(Context.AUDIO_SERVICE) as AudioManager
val maxVolume = audioManager.getStreamMaxVolume(streamType)
val safeVolume = volume.coerceIn(0, maxVolume)
audioManager.setStreamVolume(streamType, safeVolume, AudioManager.FLAG_SHOW_UI)
}
fun getCurrentVolume(context: Context, streamType: Int): Int {
val audioManager = context.getSystemService(Context.AUDIO_SERVICE) as AudioManager
return audioManager.getStreamVolume(streamType)
}
fun getMaxVolume(context: Context, streamType: Int): Int {
val audioManager = context.getSystemService(Context.AUDIO_SERVICE) as AudioManager
return audioManager.getStreamMaxVolume(streamType)
}
//增加音量/减少音量
fun adjustVolume(context: Context, direction: Int) {
val audioManager = context.getSystemService(Context.AUDIO_SERVICE) as AudioManager
audioManager.adjustStreamVolume(AudioManager.STREAM_MUSIC, direction, AudioManager.FLAG_SHOW_UI)
}
/** /**
* 操作结果数据类 * 操作结果数据类
*/ */

Loading…
Cancel
Save