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 {
static late UserGetAppConfigResp config;
// 可选:提供初始化方法
static void initialize(UserGetAppConfigResp resp) {
config = resp;
@ -25,4 +24,10 @@ class AppConfig {
final foundAgent = config.agents.firstWhere((agent) => agent.id == agentId);
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 List<DBAgent> agents;
final Map<String, DBMCPServer> mcps;
final List<DBProduct> products;
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) =>
_$UserGetAppConfigRespFromJson(json);
@ -63,3 +68,20 @@ class DBMCPServer {
_$DBMCPServerFromJson(json);
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(
(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(
@ -24,6 +27,7 @@ Map<String, dynamic> _$UserGetAppConfigRespToJson(
'env': instance.env,
'agents': instance.agents,
'mcps': instance.mcps,
'products': instance.products,
};
DBAgent _$DBAgentFromJson(Map<String, dynamic> json) => DBAgent(
@ -66,3 +70,15 @@ Map<String, dynamic> _$DBMCPServerToJson(DBMCPServer instance) =>
'url': instance.url,
'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表示普通用户
String earphonename;
String earphonemac;
String earphonelicense;
String earphoneauthcode;
// final Int64 lastbettime;
// final Int64 createtime;
@ -44,6 +45,7 @@ class User {
required this.viplv, // 添加默认值
required this.earphonename,
required this.earphonemac,
required this.earphonelicense,
required this.earphoneauthcode,
// required this.lastbettime,
// required this.createtime,
@ -74,6 +76,7 @@ class User {
viplv: (userData?['viplv'] as int?) ?? 0, // 提供默认值
earphonename: (userData?['earphonename'] as String?) ?? '',
earphonemac: (userData?['earphonemac'] as String?) ?? '',
earphonelicense: (userData?['earphonelicense'] as String?) ?? '',
earphoneauthcode: (userData?['earphoneauthcode'] as String?) ?? '',
// lastbettime: _parseTimestamp(userData['lastbettime']),
// createtime: _parseTimestamp(userData['createtime']),
@ -102,6 +105,7 @@ class User {
viplv: (userData?['viplv'] as int?) ?? 0, // 提供默认值
earphonename: (userData?['earphonename'] as String?) ?? '',
earphonemac: (userData?['earphonemac'] as String?) ?? '',
earphonelicense: (userData?['earphonelicense'] as String?) ?? '',
earphoneauthcode: (userData?['earphoneauthcode'] as String?) ?? '',
// lastbettime: _parseTimestamp(userData['lastbettime']),
// createtime: _parseTimestamp(userData['createtime']),
@ -128,6 +132,7 @@ class User {
'earphonename': earphonename,
'earphonemac': earphonemac,
"earphonelicense": earphonelicense,
'earphoneauthcode': earphoneauthcode,
// 'lastbettime': lastbettime,

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

@ -74,10 +74,19 @@ class Api {
}
//app获取设备信息
static getequipmentinfo([params]) {
static getproduct([params]) {
return DioManager().request(
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,
);
}

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

@ -1,5 +1,5 @@
class NWApi {
// static const baseApi = "https://www.deapsound.com";
//static const baseApi = "https://api.deapsound.com";
static const baseApi = "https://api.ideapsound.com";
static const baseApi = "https://api.deapsound.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 {
late final BleManager _bleManager;
//耳机绑定
final isearphone = false.obs;
final earphonestate = 0.obs;
final earphoneName = "".obs;
final earphoneMac = "".obs;
final earphoneLicense = "".obs;
final earphoneCode = "".obs;
//录音笔
final voicepenstate = 0.obs;
final isvoicepen = false.obs;
final voicepenName = "".obs;
final voicepenMac = "".obs;
@ -29,9 +31,13 @@ class DevicesController extends GetxController {
_bleManager = Get.find<BleManager>();
earphoneName.value = User.instance.earphonename;
earphoneMac.value = User.instance.earphonemac;
earphoneLicense.value = User.instance.earphonelicense;
earphoneCode.value = User.instance.earphoneauthcode;
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) {
User.instance.earphonename = "";
User.instance.earphonemac = "";
User.instance.earphoneauthcode = "";
isearphone.value = false;
User.instance.earphonelicense = "";
earphonestate.value = 0;
_bleManager.clearPairedDevices();
_bleManager.disconnect();
Get.toNamed(Routes.pairing);

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

@ -93,10 +93,10 @@ class DevicesView extends GetView<DevicesController> {
() =>
// 耳机列表
_buildSimpleNavigationSetting(
title: controller.isearphone.value
title: controller.earphonestate.value != 0
? controller.earphoneName.value
: "未绑定",
subtitle: controller.isearphone.value
subtitle: controller.earphonestate.value != 0
? controller.earphoneMac.value
: "还未绑定设备",
icon: Icons.headphones,
@ -110,7 +110,7 @@ class DevicesView extends GetView<DevicesController> {
titleColor: isDarkMode ? Colors.white : null,
subtitleColor: isDarkMode ? Colors.white70 : null,
isDarkMode: isDarkMode,
showbutt: controller.isearphone.value,
showbutt: controller.earphonestate == 2,
onPressed: () {
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/models/user_Info.dart';
import '../../common/webview_page.dart';
//import 'package:shared_preferences/shared_preferences.dart';
import 'package:get_storage/get_storage.dart';
import 'package:device_info_plus/device_info_plus.dart';
import 'dart:io';
@ -914,10 +913,6 @@ class LoginController extends GetxController {
print('getAppConfig:进来了');
// 根据API设计调整
final resp = UserGetAppConfigResp.fromJson(response);
// 存储用户信息(示例)
// _storage.write('ENV', resp.env);
// _storage.write('AGENTS', resp.agents);
// _storage.write('MCPS', resp.mcps);
AppConfig.initialize(resp);
return true;
} catch (e) {

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

@ -1,15 +1,19 @@
import 'dart:async';
import 'dart:io';
import 'package:flutter_blue_plus/flutter_blue_plus.dart';
import 'package:get/get.dart';
import 'package:path_provider/path_provider.dart';
import 'package:wifi_iot/wifi_iot.dart';
import '../../../core/utils/ftpconnect/ftpconnect.dart';
import 'meeting_controller.dart';
class MeetingConnectController extends GetxController {
final _meetingController = Get.find<MeetingController>();
RxString stateStr = '搜索中...'.obs;
RxBool isRescan = false.obs;
RxBool isImportedFiles = false.obs;
RxString importedFilesStr = ''.obs;
late StreamSubscription<List<ScanResult>> _scanResultsSubscription;
@ -30,6 +34,11 @@ class MeetingConnectController extends GetxController {
FTPConnect? _ftpConnect;
RxBool isDownload = false.obs;
RxDouble downloadProgress = 0.0.obs;
RxInt totalReceived = 0.obs;
RxInt fileSize = 0.obs;
@override
void onInit() {
super.onInit();
@ -141,10 +150,10 @@ class MeetingConnectController extends GetxController {
switch (command) {
case 0x0d: // 是否有导入文件
if (value[3] == 0x01) {
isImportedFiles.value = true;
importedFilesStr.value = '检查到有新文件,是否导入';
stateStr.value = '正在开启Wi-Fi...';
importedFilesStr.value = '检查到有新文件,正在准备导入';
sendData([0x5a, 0x02, 0x87, 0x01]);
} else {
isImportedFiles.value = false;
importedFilesStr.value = '没有新文件需要导入';
}
break;
@ -208,10 +217,52 @@ class MeetingConnectController extends GetxController {
);
await _ftpConnect?.connect();
_ftpConnect?.listCommand = ListCommand.LIST;
List<FTPEntry>? _listDirectory =
await _ftpConnect?.listDirectoryContent();
print(_listDirectory);
List<FTPEntry>? listDirectory = await _ftpConnect?.listDirectoryContent();
if (listDirectory != null && listDirectory.isNotEmpty) {
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) {

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

@ -190,13 +190,11 @@ class MeetingController extends GetxController {
int addUp = 0;
final appDir = await appDocDir;
// 定义源目录列表
final sourceDirs = [
Directory("${appDir.path}/MeetingAudio/"),
Directory("${appDir.path}/DestAudio/"),
];
// 创建目标目录(如果不存在)
final destDir = Directory("${appDir.path}/LocalAudio/");
if (!await destDir.exists()) {
await destDir.create(recursive: true);
@ -212,8 +210,6 @@ class MeetingController extends GetxController {
}
}
// 此时 allFiles 包含 MeetingAudio 和 DestAudio 目录下所有的文件
for (final fileEntity in allFiles) {
try {
final file = File(fileEntity.path);
@ -334,9 +330,21 @@ class MeetingController extends GetxController {
}
}
await _audioPlayer.setAudioSource(AudioSource.file(filePath!));
var audiourl =
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 creationTime = DateTime.now().millisecondsSinceEpoch;
Map<String, Object?> data = {
@ -357,13 +365,7 @@ class MeetingController extends GetxController {
});
addQuantity++;
}
if (isParamProvided) {
Get.toNamed(Routes.meetingDetails, arguments: {
'id': meetingId,
});
}
EasyLoading.dismiss();
return meetingId;
}
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(
() => controller.isImportedFiles.value
? Padding(
padding: EdgeInsets.symmetric(vertical: 10.w),
child: _button('导入文件', Icons.file_download, onTap: () {
controller.stateStr.value = '正在开启Wi-Fi...';
controller.importedFilesStr.value = '';
controller.isImportedFiles.value = false;
controller.sendData([0x5a, 0x02, 0x87, 0x01]);
}),
)
: const SizedBox(),
),
Obx(() => controller.isDownload.value
? Column(
children: [
Container(
width: 300.w,
height: 3.w,
margin: EdgeInsets.symmetric(vertical: 10.w),
child: LinearProgressIndicator(
value: controller.downloadProgress.value,
valueColor:
const AlwaysStoppedAnimation<Color>(Colors.red),
),
),
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) {
return AppBar(
backgroundColor: isDarkMode ? Colors.black : Colors.white,
surfaceTintColor: Colors.transparent,
elevation: 0.5,
leading: IconButton(
icon: Icon(
@ -459,7 +460,7 @@ class MeetingView extends GetView<MeetingController> {
() => ListView.builder(
controller: controller.listController,
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) {
MeetingModel data = MeetingModel.fromJson(controller.dataList[index]);
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();
// API请求
final response = await Api.getequipmentinfo({
final response = await Api.getproduct({
"id": deviceInfo.firmwareProductId,
});

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

@ -431,9 +431,12 @@ class PairingController extends GetxController {
try {
Logger.i(_tag, '开始验证设备: ${device.name} (${device.address})');
final result = await authbindingDevice(device);
if (result == 2) {
if (result == 10) {
//验证失败 进入手动授权
await connectble(device);
} else if (result == 9) {
Logger.i(_tag, 'BLE已连接,直接完成配对');
_transition(PairingState.completed);
} else if (result == 1) {
//跳转绑定界面
//验证失败 进入手动授权
@ -557,15 +560,22 @@ class PairingController extends GetxController {
//自动绑定设备 0 未合作商 1 合作商 2,已绑定
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
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 Future.delayed(const Duration(seconds: 1));
// 尝试最多读取 3 次设备信息,每次间隔 1 秒
@ -610,7 +620,7 @@ class PairingController extends GetxController {
Logger.i(_tag, '绑定设备成功');
User.instance.earphonemac = currentDevice?.address ?? '';
User.instance.earphonename = currentDevice?.name ?? '';
User.instance.earphoneauthcode = deviceCode.value;
User.instance.earphonelicense = deviceCode.value;
_transition(PairingState.verifying);
_verifyDevice(_currentClassicDevice!);
return;
@ -642,8 +652,9 @@ class PairingController extends GetxController {
void onAutoCodeInputTap() async {
// 调用BLE管理器的自动绑定方法
final response = await Api.bindauthcode({
"code": codeInputController.text.trim(),
"earphonname": currentDevice?.name,
"earphonemac": currentDevice?.address,
"earphoneauthcode": codeInputController.text.trim(),
});
Logger.i(_tag, '手动授权结果 ${response}');
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处理逻辑")
// 如果有正在进行的AI流式输出,先停止它
audioPlayer?.stopAudio()
stopAiStream()
stopTts()
// 记录新的AI任务
@ -748,6 +749,7 @@ object AgentService : CoroutineScope {
val responseBuilder = StringBuilder()
var aiMetadata:String = ""
var userMetadata = JSONObject()
var reply = false; //是否回复
var broadcast = true //是否播发
// 如果有图片,添加图片信息到元数据
if (hasImage) {
@ -814,10 +816,10 @@ object AgentService : CoroutineScope {
return
}
responseBuilder.append(token)
if (speakResponse && broadcast) {
if (speakResponse && reply && broadcast) {
ttsService?.speakStream(token)
}
if (broadcast) {
if (reply && token.isNotEmpty()) {
// 发送流式回复token
sendEvent("assistant_token", mapOf("token" to token))
}
@ -829,7 +831,7 @@ object AgentService : CoroutineScope {
override fun onComplete(sessionid:String) {
try {
// 视情况决定是否朗读回复
if (speakResponse && broadcast && sessionid == currsessionId){
if (speakResponse && reply && broadcast && sessionid == currsessionId){
ttsService?.flushStream()
audioPlayer?.stopAudio()
}
@ -842,7 +844,10 @@ object AgentService : CoroutineScope {
if (hasImage) {
responseData["hasImage"] = true
}
sendEvent("assistant_response", responseData)
if (reply){
sendEvent("assistant_response", responseData)
}
if (response.isNotEmpty()) {
// 添加AI回复到历史记录
addToHistoryMessages(createAssistantMessage(response))
@ -903,6 +908,7 @@ object AgentService : CoroutineScope {
val resultmap = autoHandleFcunCallResult(name, functionCallResult);
aiMetadata = resultmap["meta"] as String;
broadcast = resultmap["broadcast"] as Boolean;
reply = !(resultmap["notreply"] as Boolean);
sendEvent(
"function_call_result", mapOf(
"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.pm.PackageManager
import android.location.LocationManager
import android.media.AudioManager
import android.net.Uri
import android.os.Build
import android.provider.AlarmClock
@ -232,6 +233,42 @@ class SystemFunctionHandler(private val context: Context? = null) {
),
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()
}
}
}
}
/**
* 添加音量处理类
*/
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.Application
import android.content.ComponentName
import android.media.AudioManager
import android.os.Process
/**
* 系统功能处理器 - 负责调用系统API执行各种功能以及注册系统功能
@ -78,6 +79,13 @@ class SystemFunctionHandler(private val context: Context) {
// 注册关闭导航服务功能
registerStopNavigationFunction(client)
//增加音量
registerAddVolumeFunction(client)
//减少音量
registerReduceVolumeFunction(client)
//静音
registerMuteVolumeFunction(client)
Log.d(TAG, "所有系统功能注册完成")
} catch (e: Exception) {
Log.e(TAG, "注册系统功能失败: ${e.message}", e)
@ -438,6 +446,78 @@ class SystemFunctionHandler(private val context: Context) {
)
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"
@ -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