From 8b308f3b51b06c174eedd3a7726e9994f787050c Mon Sep 17 00:00:00 2001 From: fdp <1286779656@qq.com> Date: Fri, 12 Sep 2025 14:28:02 +0800 Subject: [PATCH 1/5] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E9=A2=91=E7=B9=81?= =?UTF-8?q?=E5=88=87=E6=8D=A2=E7=BF=BB=E8=AF=91=E7=B1=BB=E5=9E=8B=EF=BC=8C?= =?UTF-8?q?=E6=B2=A1=E6=9C=89=E9=87=8A=E6=94=BE=E4=B8=8A=E6=AC=A1=E8=B5=84?= =?UTF-8?q?=E6=BA=90=E5=AF=BC=E8=87=B4=E5=A5=94=E6=BA=83=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/modules/translation/views/translation_view.dart | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/modules/translation/views/translation_view.dart b/lib/modules/translation/views/translation_view.dart index 3c1176909..b1f91a87c 100644 --- a/lib/modules/translation/views/translation_view.dart +++ b/lib/modules/translation/views/translation_view.dart @@ -1073,6 +1073,7 @@ class TranslationView extends GetView { Colors.blue, onTap: () async { Get.back(); + await controller.stopAll(); await controller.changeTranslationMode('simultaneous'); await controller.startRecognition(); }, @@ -1084,6 +1085,7 @@ class TranslationView extends GetView { Colors.green, onTap: () async { Get.back(); + await controller.stopAll(); await controller.changeTranslationMode('faceToFace'); await controller.startRecognition(); }, @@ -1096,6 +1098,7 @@ class TranslationView extends GetView { onTap: controller.bleManager.isConnected ? () async { Get.back(); + await controller.stopAll(); await controller.changeTranslationMode('audioVideo'); await controller.startRecognition(); } @@ -1109,6 +1112,7 @@ class TranslationView extends GetView { onTap: controller.bleManager.isConnected ? () async { Get.back(); + await controller.stopAll(); await controller.changeTranslationMode('call'); await controller.startRecognition(); } From 1ace0cfff8319f37be1b81ec7a8c62f312c103c0 Mon Sep 17 00:00:00 2001 From: liwei1dao Date: Fri, 12 Sep 2025 16:09:21 +0800 Subject: [PATCH 2/5] =?UTF-8?q?=E4=BC=9A=E9=80=80=E5=8F=B0=E5=90=8E?= =?UTF-8?q?=E5=8F=B0=E6=B3=A8=E5=86=8C=E6=9C=8D=E5=8A=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pairing/controllers/pairing_controller.dart | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/modules/pairing/controllers/pairing_controller.dart b/lib/modules/pairing/controllers/pairing_controller.dart index 99602941d..3551167a3 100644 --- a/lib/modules/pairing/controllers/pairing_controller.dart +++ b/lib/modules/pairing/controllers/pairing_controller.dart @@ -708,13 +708,13 @@ class PairingController extends GetxController with WidgetsBindingObserver { // 注册后台扫描以开始BLE配对 if (_verifiedDeviceAddress != null) { - await _bleManager.connect(); - // final success = await _bleManager.registerBackgroundScan( - // macAddress: _verifiedDeviceAddress); - // if (!success && _currentState.value == PairingState.waitingBle) { - // _setError( - // ErrorType.bleTimeout, 'blePairingStartupFailed'.tr); //BLE配对启动失败 - // } + // await _bleManager.connect(); + final success = await _bleManager.registerBackgroundScan( + macAddress: _verifiedDeviceAddress); + if (!success && _currentState.value == PairingState.waitingBle) { + _setError( + ErrorType.bleTimeout, 'blePairingStartupFailed'.tr); //BLE配对启动失败 + } } } catch (e) { Logger.e(_tag, 'BLE连接失败: $e'); @@ -1037,7 +1037,7 @@ class PairingController extends GetxController with WidgetsBindingObserver { try { Logger.i(_tag, '启动AgentService服务'); await _bleManager.startAgentService(); - _bleManager.registerBackgroundScan(macAddress: _verifiedDeviceAddress); + // _bleManager.registerBackgroundScan(macAddress: _verifiedDeviceAddress); } catch (e) { Logger.e(_tag, '启动AgentService失败: $e'); } From 8081e5ba0ab2248ecd64ba0036d4aef16adc8fee Mon Sep 17 00:00:00 2001 From: fdp <1286779656@qq.com> Date: Fri, 12 Sep 2025 16:36:04 +0800 Subject: [PATCH 3/5] add. --- .../azure_speech/AzureAsrHelper.kt | 81 ++++++++++++++++++- .../azure_speech/tools/SimpleAudioReceiver.kt | 13 +-- .../ble_service/BleCompanionManager.kt | 2 +- 3 files changed, 83 insertions(+), 13 deletions(-) diff --git a/local_plugins/azure_speech/android/src/main/kotlin/com/yunqiinnovation/azure_speech/AzureAsrHelper.kt b/local_plugins/azure_speech/android/src/main/kotlin/com/yunqiinnovation/azure_speech/AzureAsrHelper.kt index 08bf94688..8fc25365a 100644 --- a/local_plugins/azure_speech/android/src/main/kotlin/com/yunqiinnovation/azure_speech/AzureAsrHelper.kt +++ b/local_plugins/azure_speech/android/src/main/kotlin/com/yunqiinnovation/azure_speech/AzureAsrHelper.kt @@ -12,7 +12,7 @@ import android.os.Looper import java.util.concurrent.TimeUnit import com.yunqiinnovation.azure_speech.tools.RecordFile import com.microsoft.cognitiveservices.speech.audio.AudioConfig - +import com.yunqiinnovation.azure_speech.tools.AudioRecordingForegroundService /** @@ -31,6 +31,8 @@ class AzureAsrHelper(private val context: Context) { private var audioConfig: AudioConfig? = null private var continuousCallback: ContinuousRecognizeCallback? = null + // 添加前台服务管理标志 + private var isForegroundServiceRunning = false // 配置参数 private var currentLanguage = "zh-CN" private var supportedLanguages = arrayOf("zh-CN") @@ -247,6 +249,20 @@ class AzureAsrHelper(private val context: Context) { if (audioStream.isContinuousRecognitionActive) { return true } + + // 启动前台服务(在开始音频录制前) + if (!isForegroundServiceRunning && audioSourceType == AudioSourceType.MICROPHONE) { + try { + AudioRecordingForegroundService.startService(context) + isForegroundServiceRunning = true + Log.d(tag, "前台服务已启动") + } catch (e: Exception) { + Log.e(tag, "启动前台服务失败: ${e.message}") + continuousCallback?.onError(1003, "启动前台服务失败: ${e.message}") + return false + } + } + if (!isRecognizerValid()) { Log.d(tag, "重新启动连续识别") val callback = continuousCallback @@ -393,6 +409,18 @@ class AzureAsrHelper(private val context: Context) { recognizer?.close() recognizer = null audioStream.isContinuousRecognitionActive = false + + // 停止前台服务 + if (isForegroundServiceRunning) { + try { + AudioRecordingForegroundService.stopService(context) + isForegroundServiceRunning = false + Log.d(tag, "前台服务已停止") + } catch (e: Exception) { + Log.e(tag, "停止前台服务失败: ${e.message}") + } + } + return true } catch (e: Exception) { // 强制重置状态 @@ -404,6 +432,17 @@ class AzureAsrHelper(private val context: Context) { recognizer?.close() recognizer = null audioStream.isContinuousRecognitionActive = false + + // 确保前台服务被停止 + if (isForegroundServiceRunning) { + try { + AudioRecordingForegroundService.stopService(context) + isForegroundServiceRunning = false + } catch (e2: Exception) { + Log.e(tag, "强制停止前台服务失败: ${e2.message}") + } + } + return false } } @@ -442,6 +481,18 @@ class AzureAsrHelper(private val context: Context) { // 释放音频配置 audioConfig?.close() audioConfig = null + + // 确保前台服务被停止 + if (isForegroundServiceRunning) { + try { + AudioRecordingForegroundService.stopService(context) + isForegroundServiceRunning = false + Log.d(tag, "dispose时前台服务已停止") + } catch (e: Exception) { + Log.e(tag, "dispose时停止前台服务失败: ${e.message}") + } + } + // 确保状态被重置 audioStream.isContinuousRecognitionActive = false @@ -451,6 +502,7 @@ class AzureAsrHelper(private val context: Context) { audioConfig = null recognizer = null speechConfig = null + isForegroundServiceRunning = false } } @@ -479,11 +531,23 @@ class AzureAsrHelper(private val context: Context) { */ fun enableRecord(audioSourceType: AudioSourceType = AudioSourceType.MICROPHONE,filePath: String, audioDataCallback: SimpleAudioReceiver.AudioDataCallback? = null) { Log.i(tag, "开启录音:") - this.audioSourceType = audioSourceType + this.audioSourceType = audioSourceType if(audioSourceType == AudioSourceType.EXTERNAL){ - Log.i(tag, "外部音频源不在这里录音") + Log.i(tag, "外部音频源不在这里录音") return } + + // 启动前台服务(在开始录音前) + if (!isForegroundServiceRunning) { + try { + AudioRecordingForegroundService.startService(context) + isForegroundServiceRunning = true + Log.d(tag, "录音前台服务已启动") + } catch (e: Exception) { + Log.e(tag, "启动录音前台服务失败: ${e.message}") + } + } + // Initialize if not already done if (!audioStream.isInitialized()) { audioStream.initAudioRecord() @@ -574,6 +638,17 @@ class AzureAsrHelper(private val context: Context) { if (!audioStream.isContinuousRecognitionActive) { audioStream.stopMicrophoneCapture() + + // 停止录音时也停止前台服务(如果没有其他音频任务) + if (isForegroundServiceRunning) { + try { + AudioRecordingForegroundService.stopService(context) + isForegroundServiceRunning = false + Log.d(tag, "录音前台服务已停止") + } catch (e: Exception) { + Log.e(tag, "停止录音前台服务失败: ${e.message}") + } + } } audioStream.isRecord = false diff --git a/local_plugins/azure_speech/android/src/main/kotlin/com/yunqiinnovation/azure_speech/tools/SimpleAudioReceiver.kt b/local_plugins/azure_speech/android/src/main/kotlin/com/yunqiinnovation/azure_speech/tools/SimpleAudioReceiver.kt index 5dddebd68..cc72aa389 100644 --- a/local_plugins/azure_speech/android/src/main/kotlin/com/yunqiinnovation/azure_speech/tools/SimpleAudioReceiver.kt +++ b/local_plugins/azure_speech/android/src/main/kotlin/com/yunqiinnovation/azure_speech/tools/SimpleAudioReceiver.kt @@ -17,7 +17,7 @@ import android.media.AudioFocusRequest import android.media.AudioAttributes import com.yunqiinnovation.azure_speech.tools.RecordFile import android.os.Build // 新增:用于获取设备信息 -import com.yunqiinnovation.azure_speech.tools.AudioRecordingForegroundService + /** * 简单音频接收器类,用于处理音频录制和流传输 @@ -219,8 +219,6 @@ fun startAudioRecord(audioSourceType: AudioSourceType, callback: AudioDataCallba pushAudioStream?.close() pushAudioStream = null isWriting.set(false) - // 停止前台服务 - AudioRecordingForegroundService.stopService(context) throw e // 重新抛出异常让调用者处理 } } @@ -296,8 +294,7 @@ fun startAudioRecord(audioSourceType: AudioSourceType, callback: AudioDataCallba */ private fun runMicrophoneCapture() { try { - // 启动前台服务 - AudioRecordingForegroundService.startService(context) + Log.d(TAG, "前台服务已启动,开始音频录制") // 根据手机品牌决定是否设置通话音频模式 if (isHonorDevice()) { @@ -410,8 +407,7 @@ fun startAudioRecord(audioSourceType: AudioSourceType, callback: AudioDataCallba try { writeQueue.clear() isWriting.set(false) - // 停止前台服务 - AudioRecordingForegroundService.stopService(context) + // 停止录音 @@ -431,8 +427,7 @@ fun startAudioRecord(audioSourceType: AudioSourceType, callback: AudioDataCallba try { isRunning.set(false) isWriting.set(false) - // 停止前台服务 - AudioRecordingForegroundService.stopService(context) + Log.d(TAG, "前台服务已停止,音频资源已释放") // 中断并等待捕获线程结束 writeThread?.interrupt() diff --git a/local_plugins/ble_service/android/src/main/kotlin/com/yunqiinnovation/ble_service/BleCompanionManager.kt b/local_plugins/ble_service/android/src/main/kotlin/com/yunqiinnovation/ble_service/BleCompanionManager.kt index 5694090f6..5e73b6aaf 100644 --- a/local_plugins/ble_service/android/src/main/kotlin/com/yunqiinnovation/ble_service/BleCompanionManager.kt +++ b/local_plugins/ble_service/android/src/main/kotlin/com/yunqiinnovation/ble_service/BleCompanionManager.kt @@ -396,7 +396,7 @@ class BleCompanionManager(private val context: Context) { val filter = ScanFilter.Builder() .setDeviceAddress(mac) // .setServiceUuid(ParcelUuid(java.util.UUID.fromString("0000FEAA-0000-1000-8000-00805F9B34FB"))) - .setManufacturerData(0xEFAB, byteArrayOf(0xA0.toByte(), 0x01)) + //.setManufacturerData(0xEFAB, byteArrayOf(0xA0.toByte(), 0x01)) .build() // 扫描设置 From dc06c83ce5f2eb4c31b2b984a5d3721c9d3b0b3e Mon Sep 17 00:00:00 2001 From: liwei1dao Date: Fri, 12 Sep 2025 17:16:03 +0800 Subject: [PATCH 4/5] =?UTF-8?q?=E4=B8=8A=E4=BC=A0=E7=99=BE=E5=BA=A6mcp?= =?UTF-8?q?=E4=BB=A3=E7=A0=81=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../chat_api/CustomSseClientTransport.kt | 78 +++++++++++++++---- .../chat_api/SystemFunctionHandler.kt | 1 + 2 files changed, 63 insertions(+), 16 deletions(-) diff --git a/local_plugins/chat_api/android/src/main/kotlin/com/yunqiinnovation/chat_api/CustomSseClientTransport.kt b/local_plugins/chat_api/android/src/main/kotlin/com/yunqiinnovation/chat_api/CustomSseClientTransport.kt index a08ed5472..d13fbc37a 100644 --- a/local_plugins/chat_api/android/src/main/kotlin/com/yunqiinnovation/chat_api/CustomSseClientTransport.kt +++ b/local_plugins/chat_api/android/src/main/kotlin/com/yunqiinnovation/chat_api/CustomSseClientTransport.kt @@ -86,7 +86,7 @@ class CustomSseClientTransport( // 路径部分 val path = urlObj.path - + Log.e(TAG, "$serviceidString 解析成功: hostUrl:$hostUrl,path:$path,params:$params") Triple(hostUrl, path, params) } catch (e: Exception) { Log.e(TAG, "$serviceidString 解析URL失败: $url, ${e.message}") @@ -101,9 +101,10 @@ class CustomSseClientTransport( job = scope.launch(CoroutineName("CustomSseMcpClientTransport.collect#${hashCode()}")) { try { session.incoming.collect { event -> + Log.e(TAG, "$serviceidString event:${event.event}-${event.data}") when (event.event) { "error" -> { - Log.e(TAG, "$serviceidString SSE错误: ${event.data}") +// Log.e(TAG, "$serviceidString SSE错误: ${event.data}") isConnected.set(false) val exception = Exception("$serviceidString SSE Error: ${event.data}") _onError(exception) @@ -113,7 +114,7 @@ class CustomSseClientTransport( "open" -> { // SSE连接已打开 - Log.d(TAG, "$serviceidString SSE连接已打开") +// Log.d(TAG, "$serviceidString SSE连接已打开") isConnected.set(true) } "ping" -> { @@ -122,29 +123,73 @@ class CustomSseClientTransport( "endpoint" -> { try { val eventData = event.data ?: "" - + // 构建完整的端点URL - val fullEndpoint = if (eventData.contains(hostPart)) { - eventData - } else if (eventData.startsWith("/")) { - "$hostPart$eventData" - } else { - eventData + val fullEndpoint = when { + // 如果eventData已经包含完整的URL(包含协议) + eventData.startsWith("http://") || eventData.startsWith("https://") -> { + eventData + } + // 如果eventData包含hostPart但没有协议 + eventData.contains(hostPart.substringAfter("://")) -> { + if (eventData.startsWith("://")) { + "${hostPart.substringBefore("://")}$eventData" + } else { + "https://$eventData" + } + } + // 如果eventData以/开头,说明是路径 + eventData.startsWith("/") -> { + "$hostPart$eventData" + } + // 如果eventData不为空但不包含完整信息,尝试构建完整URL + eventData.isNotEmpty() -> { + if (hostPart.isNotEmpty()) { + // 如果eventData看起来像是路径片段,添加/前缀 + val path = if (!eventData.startsWith("/")) "/" + eventData else eventData + "$hostPart$path" + } else { + // 如果没有hostPart,尝试添加默认协议 + if (!eventData.contains("://")) { + "https://$eventData" + } else { + eventData + } + } + } + // 如果eventData为空,使用hostPart + else -> hostPart + } + + // 验证URL格式 + val validatedEndpoint = try { + val url = java.net.URL(fullEndpoint) + fullEndpoint + } catch (e: Exception) { + Log.w(TAG, "$serviceidString 端点URL格式可能有问题: $fullEndpoint, 尝试修复") + // 尝试修复URL + when { + !fullEndpoint.startsWith("http://") && !fullEndpoint.startsWith("https://") -> { + "https://$fullEndpoint" + } + else -> fullEndpoint + } } // 添加查询参数 val endpointWithParams = if (queryParams.isNotEmpty()) { - if (fullEndpoint.contains("?")) { + if (validatedEndpoint.contains("?")) { val queryString = queryParams.entries.joinToString("&") { "${it.key}=${it.value}" } - "$fullEndpoint&$queryString" + "$validatedEndpoint&$queryString" } else { val queryString = queryParams.entries.joinToString("&") { "${it.key}=${it.value}" } - "$fullEndpoint?$queryString" + "$validatedEndpoint?$queryString" } } else { - fullEndpoint + validatedEndpoint } + Log.d(TAG, "$serviceidString 处理endpoint事件: $endpointWithParams") endpoint.complete(endpointWithParams) } catch (e: Exception) { Log.e(TAG, "$serviceidString 处理endpoint事件失败: ${e.message}", e) @@ -174,7 +219,7 @@ class CustomSseClientTransport( } } } catch (e: CancellationException) { - Log.d(TAG, "$serviceidString SSE事件收集被取消") + Log.e(TAG, "$serviceidString SSE事件收集被取消 : ${e.message}", e) throw e } catch (e: Exception) { Log.e(TAG, "$serviceidString SSE连接异常断开: ${e.message}", e) @@ -219,11 +264,12 @@ class CustomSseClientTransport( * 启动传输层 */ override suspend fun start() { + if (!initialized.compareAndSet(false, true)) { Log.e(TAG, "$serviceidString 传输层已经启动,不能重复启动") error("CustomSseClientTransport already started!") } - + Log.d(TAG, "CustomSseClientTransport $serviceidString 启动") // 解析URL if (urlString != null) { val urlInfo = parseUrl(urlString) diff --git a/local_plugins/chat_api/android/src/main/kotlin/com/yunqiinnovation/chat_api/SystemFunctionHandler.kt b/local_plugins/chat_api/android/src/main/kotlin/com/yunqiinnovation/chat_api/SystemFunctionHandler.kt index af83a46d0..e82e77bd2 100644 --- a/local_plugins/chat_api/android/src/main/kotlin/com/yunqiinnovation/chat_api/SystemFunctionHandler.kt +++ b/local_plugins/chat_api/android/src/main/kotlin/com/yunqiinnovation/chat_api/SystemFunctionHandler.kt @@ -1352,6 +1352,7 @@ private class UpdateUserPortraitHandler(private val context: Context?) : Functio context.sendBroadcast(intent) // 发送广播通知退出交互 return JSONObject().apply { + put("nocard", true) put("result", "更新成功!") }.toString() } From 713b43379c21e705a9ccc865dfbcb8da3e2656b6 Mon Sep 17 00:00:00 2001 From: fdp <1286779656@qq.com> Date: Fri, 12 Sep 2025 17:30:46 +0800 Subject: [PATCH 5/5] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E8=87=AA=E5=8A=A8?= =?UTF-8?q?=E4=BC=9A=E8=BF=9E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/data/services/bluetooth_manager.dart | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/lib/data/services/bluetooth_manager.dart b/lib/data/services/bluetooth_manager.dart index 654cfa603..4c4f58ffd 100644 --- a/lib/data/services/bluetooth_manager.dart +++ b/lib/data/services/bluetooth_manager.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'package:get/get.dart'; import 'package:classic_bluetooth/classic_bluetooth.dart'; import '../../core/utils/logger.dart'; +import '../../routes/app_routes.dart'; // 重新导出蓝牙设备类型,让外部可以通过BluetoothManager访问 export 'package:classic_bluetooth/classic_bluetooth.dart' show BluetoothDevice; @@ -58,6 +59,7 @@ class BluetoothManager extends GetxService { try { _bluetoothSubscription = ClassicBluetooth.events.listen( (event) { + print("_setupBluetoothListener$event"); if (event.type == 'stateChanged' && event.state != null) { _bluetoothState.value = event.state!; Logger.info('蓝牙状态变更: ${event.state}'); @@ -70,9 +72,15 @@ class BluetoothManager extends GetxService { // 直接从事件中获取设备信息 final newDevice = event.device!; - // 更新当前设备 currentDeviceRx.value = newDevice; + // 检查当前是否在OTA升级界面,如果是则不跳转 + if (!_isInOtaUpgradePage()) { + Get.toNamed(Routes.pairing, arguments: newDevice); + } else { + Logger.info('当前在OTA升级界面,跳过配对页面跳转'); + } + // 正常模式:更新当前设备并触发监听器 Logger.info('经典蓝牙设备已连接: ${newDevice.name} (${newDevice.address})'); } else if (event.type == 'deviceDisconnected' && event.device != null) { @@ -138,4 +146,16 @@ class BluetoothManager extends GetxService { return false; } } + + /// 检查当前是否在OTA升级界面 + /// 返回true表示当前在OTA界面,不应该跳转到配对页面 + bool _isInOtaUpgradePage() { + try { + final currentRoute = Get.currentRoute; + return currentRoute == Routes.OTA_UPGRADE; + } catch (e) { + Logger.error('检查当前路由失败: ${e.toString()}'); + return false; + } + } }