diff --git a/lib/data/services/ble_manager.dart b/lib/data/services/ble_manager.dart index ef78d9290..970cf6d5c 100644 --- a/lib/data/services/ble_manager.dart +++ b/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 _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 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 supportedLanguages = []; try { @@ -737,6 +769,7 @@ class BleManager extends GetxService { 'supportedLanguages': supportedLanguages, 'musicServiceType': musicServiceType, "navigationMode": navigationMode, + "isttsinterrupt": isTtsInterruptEnabled ? "true" : "false", "callingTool": "callingTool".tr }; diff --git a/local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/AgentService.kt b/local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/AgentService.kt index 5ef4e504f..2e241583c 100644 --- a/local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/AgentService.kt +++ b/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) diff --git a/local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/AgentServicePlugin.kt b/local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/AgentServicePlugin.kt index 735672a7c..39edf4c8d 100644 --- a/local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/AgentServicePlugin.kt +++ b/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) } diff --git a/local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/BleMediaService.kt b/local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/BleMediaService.kt index 08b079a23..e824eadcf 100644 --- a/local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/BleMediaService.kt +++ b/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()) { diff --git a/local_plugins/ble_service/android/libs/jl_audio_decode_V2.1.0_20013-release.aar b/local_plugins/ble_service/android/libs/jl_audio_decode_V2.1.0_20013-release.aar new file mode 100644 index 000000000..84091918b Binary files /dev/null and b/local_plugins/ble_service/android/libs/jl_audio_decode_V2.1.0_20013-release.aar differ diff --git a/local_plugins/ble_service/android/src/main/kotlin/com/yunqiinnovation/ble_service/BleService.kt b/local_plugins/ble_service/android/src/main/kotlin/com/yunqiinnovation/ble_service/BleService.kt index b5c9d918e..d9b133f2a 100644 --- a/local_plugins/ble_service/android/src/main/kotlin/com/yunqiinnovation/ble_service/BleService.kt +++ b/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 } /** diff --git a/local_plugins/ble_service/android/src/main/kotlin/com/yunqiinnovation/ble_service/OpusAudioManager.kt b/local_plugins/ble_service/android/src/main/kotlin/com/yunqiinnovation/ble_service/OpusAudioManager.kt index d3eb804b7..ccba13367 100644 --- a/local_plugins/ble_service/android/src/main/kotlin/com/yunqiinnovation/ble_service/OpusAudioManager.kt +++ b/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 } /**