Browse Source

Merge commit 'ae3e0c715b55bfc0551eecbfdc707c0c2f33ad38' into 重写ble

weicu
lxm 12 months ago
parent
commit
3f24b72420
  1. 33
      lib/data/services/ble_manager.dart
  2. 4
      local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/AgentService.kt
  3. 1
      local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/AgentServicePlugin.kt
  4. 2
      local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/BleMediaService.kt
  5. BIN
      local_plugins/ble_service/android/libs/jl_audio_decode_V2.1.0_20013-release.aar
  6. 46
      local_plugins/ble_service/android/src/main/kotlin/com/yunqiinnovation/ble_service/BleService.kt
  7. 35
      local_plugins/ble_service/android/src/main/kotlin/com/yunqiinnovation/ble_service/OpusAudioManager.kt

33
lib/data/services/ble_manager.dart

@ -6,6 +6,7 @@ import 'package:get_storage/get_storage.dart';
import 'package:get/get.dart';
import 'package:ble_service/ble_service.dart';
import 'package:agent_service/agent_service.dart';
import 'package:voitrans/data/models/appconfig_model.dart';
import '../../data/models/appconfig.dart';
import '../../core/utils/logger.dart';
@ -639,6 +640,35 @@ class BleManager extends GetxService {
}
}
String getDeviceName() {
if (deviceInfo.value != null) {
return deviceInfo.value!.name;
}
return "";
}
// 加载TTS偏好设置
Future<bool> _loadTtsInterrupe() async {
try {
if (!_storage.hasData("agent_tts_interrupe")) {
// 获取设备类型以确定检查策略
String deviceName = getDeviceName();
DBProduct? product = AppConfig.getproduct(deviceName);
if (deviceName != "" && product != null) {
// Logger.d('AgentController',
// 'liwe----------product $deviceName ${product.toJson()}');
return !product.iscontinuouschat!;
} else {
return false;
}
} else {
return _storage.read('agent_tts_interrupe') ?? false;
}
} catch (e) {
return false;
}
}
/// 启动BLE服务
Future<bool> startAgentService() async {
try {
@ -668,6 +698,8 @@ class BleManager extends GetxService {
// 从Flutter资源包中加载.mcp.json
final jsonData = AppConfig.mcpConfig();
Logger.info('启动BLE服务,musicServiceType: ${musicServiceType}');
final isTtsInterruptEnabled = await _loadTtsInterrupe();
// 根据APP语言动态配置语音识别语言
List<String> supportedLanguages = [];
try {
@ -737,6 +769,7 @@ class BleManager extends GetxService {
'supportedLanguages': supportedLanguages,
'musicServiceType': musicServiceType,
"navigationMode": navigationMode,
"isttsinterrupt": isTtsInterruptEnabled ? "true" : "false",
"callingTool": "callingTool".tr
};

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

@ -203,11 +203,15 @@ object AgentService : CoroutineScope {
serverurl = config["serverurl"]?.toString() ?: "";
usertoken = config["usertoken"]?.toString() ?: "";
callingTool = config["callingTool"]?.toString() ?: "";
// 初始化上下文和服务
this.context = appContext.applicationContext
// 从 SharedPreferences 读取 _isInterrupt 状态
loadIsInterruptFromCache()
if (config["isttsinterrupt"] != null){
_isInterrupt.set(config["isttsinterrupt"]?.toString() == "true")
}
this.chatApiService = ChatApiService(context)
this.chatStorageHelper = ChatStorageHelper(context)

1
local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/AgentServicePlugin.kt

@ -77,6 +77,7 @@ class AgentServicePlugin : FlutterPlugin, MethodCallHandler, EventChannel.Stream
config["usertoken"]?.toString()?.let { intent.putExtra("usertoken", it) }
config["musicServiceType"]?.toString()?.let { intent.putExtra("musicServiceType", it) }
config["navigationMode"]?.toString()?.let { intent.putExtra("navigationMode", it) }
config["isttsinterrupt"]?.toString()?.let { intent.putExtra("isttsinterrupt", it) }
config["callingTool"]?.toString()?.let { intent.putExtra("callingTool", it) }

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

@ -571,6 +571,8 @@ private var companionManager: BleCompanionManager? = null
it.getStringExtra(KEY_MUSICSERVICE_TYPE)?.let { value -> config[KEY_MUSICSERVICE_TYPE] = value }
it.getStringExtra(APP_NAVIGATION_MODE)?.let { value -> config[APP_NAVIGATION_MODE] = value }
it.getStringExtra(APP_CALLING_TOOL)?.let { value -> config[APP_CALLING_TOOL] = value }
it.getStringExtra("isttsinterrupt")?.let { value -> config["isttsinterrupt"] = value }
// 提取 supportedLanguages 参数
it.getStringArrayListExtra(KEY_SUPPORTED_LANGUAGES)?.let { languages ->
if (languages.isNotEmpty()) {

BIN
local_plugins/ble_service/android/libs/jl_audio_decode_V2.1.0_20013-release.aar

Binary file not shown.

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

@ -856,15 +856,15 @@ object BleService {
//要先设置音频服务的通知,否则接收不到
// 设置音频服务的通知(如果存在)
if (audioChar != null) {
setupNotifications(g, audioChar)
Log.i(TAG, "音频服务特征找到并设置通知")
setupNotifications(g, audioChar)
Log.i(TAG, "音频服务特征找到并设置通知")
} else {
Log.w(TAG, "音频服务特征未找到")
}
// 设置主服务的通知
setupNotifications(g, notifyChar)
setupNotifications(g, notifyChar)
// 在服务发现完成后,请求最大MTU
//requestMaxMtu(g)
@ -885,7 +885,7 @@ object BleService {
}
// 通知特征数据(命令和控制)
BleConst.NOTIFY_CHAR_UUID -> {
// Log.i(TAG, "收到通知特征数据(命令和控制)")
Log.i(TAG, "收到通知特征数据(命令和控制)")
// 判断数据类型
when {
// 设备响应 (0xBB开头)
@ -955,17 +955,41 @@ object BleService {
/**
* 设置特征的通知功能
* @param g BluetoothGatt实例
* @param characteristic 要设置通知的特征
*/
private fun setupNotifications(g: BluetoothGatt, characteristic: BluetoothGattCharacteristic?) {
private fun setupNotifications(g: BluetoothGatt, characteristic: BluetoothGattCharacteristic?) : Boolean{
if (characteristic == null || characteristic.properties.and(BluetoothGattCharacteristic.PROPERTY_NOTIFY) == 0) {
Log.w(TAG, "特征不支持通知功能: ${characteristic?.uuid}")
return
return false
}
// 在本地启用特征通知监听
val localResult = g.setCharacteristicNotification(characteristic, true)
if (!localResult) {
Log.e(TAG, "本地通知设置失败: ${characteristic.uuid}")
return false
}
Log.d(TAG, "本地通知设置成功: ${characteristic.uuid}")
g.setCharacteristicNotification(characteristic, true)
// 获取客户端特征配置描述符
val desc = characteristic.getDescriptor(BleConst.CLIENT_CHAR_CONFIG_UUID)
desc?.value = BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE
g.writeDescriptor(desc)
if (desc == null) {
Log.w(TAG, "未找到客户端配置描述符: ${characteristic.uuid}")
return false
}
// 设置描述符值为启用通知
desc.value = BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE
// 向远程设备写入描述符(异步操作,结果在onDescriptorWrite回调中处理)
val writeResult = g.writeDescriptor(desc)
if (writeResult) {
Log.d(TAG, "开始写入通知描述符: ${characteristic.uuid}")
} else {
Log.e(TAG, "通知描述符写入请求失败: ${characteristic.uuid}")
}
return true
}
/**

35
local_plugins/ble_service/android/src/main/kotlin/com/yunqiinnovation/ble_service/OpusAudioManager.kt

@ -84,6 +84,9 @@ class OpusAudioManager {
// Opus解码器实例
private var opusManager: OpusManager? = null
private var option: OpusOption? = null
private var opusManager1: OpusManager? = null
private var option1: OpusOption? = null
private val mainHandler = Handler(Looper.getMainLooper())
// 解码音频数据队列和处理线程
@ -124,6 +127,9 @@ class OpusAudioManager {
opusManager = OpusManager()
option = OpusOption()
opusManager1 = OpusManager()
option1 = OpusOption()
// 启动队列处理
startAudioQueueProcessing()
@ -174,6 +180,8 @@ class OpusAudioManager {
// 清理Opus相关资源
opusManager = null
option = null
opusManager1 = null
option1 = null
callback = null
isInitialized = false
@ -312,20 +320,27 @@ class OpusAudioManager {
* @return 是否成功开始编码
*/
fun startEncodeStream(): Boolean {
if (!isInitialized || opusManager == null) {
if (!isInitialized || opusManager1 == null) {
Log.e(TAG, "OpusManager未初始化,无法开始编码")
return false
}
// 如果已经在编码流,先停止
if (opusManager?.isEncodeStream == true) {
if (opusManager1?.isEncodeStream == true) {
Log.d(TAG, "Opus编码流已在运行")
return true
}
// 配置解码参数
option1?.apply {
setHasHead(false)
setChannel(1)
setSampleRate(16000)
setPacketSize(40)
}
try {
Log.d(TAG, "准备开始Opus数据流编码")
opusManager?.startEncodeStream(object : OnEncodeStreamCallback {
opusManager1?.startEncodeStream(object : OnEncodeStreamCallback {
override fun onEncodeStream(data: ByteArray?) {
Log.d(TAG, "Opus编码回调收到数据,大小: ${data?.size ?: 0} bytes")
if (data != null) {
@ -371,8 +386,8 @@ class OpusAudioManager {
* @return 是否成功停止编码
*/
fun stopEncodeStream(): Boolean {
if (opusManager?.isEncodeStream == true) {
opusManager?.stopEncodeStream()
if (opusManager1?.isEncodeStream == true) {
opusManager1?.stopEncodeStream()
stopAudioSendThread() // 停止音频发送线程
Log.i(TAG, "已停止Opus数据流编码")
callback?.onEncodeStreamStateChanged(false)
@ -389,16 +404,16 @@ class OpusAudioManager {
* @param data 音频数据字节数组,通常为PCM格式
*/
fun writeExternalAudioData(data: ByteArray) {
if (!isInitialized || opusManager == null) {
if (!isInitialized || opusManager1 == null) {
Log.e(TAG, "OpusManager未初始化")
return
}
// 如果已经在编码流中,直接写入数据
if (opusManager?.isEncodeStream == true) {
if (opusManager1?.isEncodeStream == true) {
Log.d(TAG, "正在进行Opus编码流,写入音频数据")
// 将外部音频数据写入编码流
opusManager?.writeEncodeStream(data)
opusManager1?.writeEncodeStream(data)
} else {
Log.w(TAG, "Opus编码流未启动,无法写入音频数据")
}
@ -421,7 +436,7 @@ class OpusAudioManager {
* @return 是否正在编码
*/
fun isEncoding(): Boolean {
return opusManager?.isEncodeStream == true
return opusManager1?.isEncodeStream == true
}
/**

Loading…
Cancel
Save