diff --git a/lib/modules/pairing/controllers/pairing_controller.dart b/lib/modules/pairing/controllers/pairing_controller.dart index c9fbdb076..e00ee536b 100644 --- a/lib/modules/pairing/controllers/pairing_controller.dart +++ b/lib/modules/pairing/controllers/pairing_controller.dart @@ -9,8 +9,6 @@ import 'package:get/get.dart'; import 'package:permission_handler/permission_handler.dart'; import 'package:url_launcher/url_launcher.dart'; import 'package:get_storage/get_storage.dart'; -import 'package:device_info_plus/device_info_plus.dart'; - import '../../../routes/app_routes.dart'; import '../../../core/utils/logger.dart'; import '/data/models/appconfig.dart'; 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 396d3ecc2..8599c5962 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 @@ -360,7 +360,7 @@ object AgentService : CoroutineScope { restartIdleCheck() sendEvent("tts_started", mapOf("status" to "started")) // 停止气泡音,避免与TTS同时播放 - audioPlayer?.stopAudio() + // audioPlayer?.stopAudio() } TtsEventType.SYNTHESIS_COMPLETED -> { @@ -374,14 +374,14 @@ object AgentService : CoroutineScope { restartIdleCheck() sendEvent("tts_canceled", mapOf("status" to "canceled")) // TTS取消时停止气泡音 - audioPlayer?.stopAudio() + // audioPlayer?.stopAudio() } TtsEventType.PLAYBACK_STARTED -> { //开始AI播报内容 restartIdleCheck() sendEvent("playback_started", mapOf("status" to "playback_started")) // 确保气泡音已停止,避免与TTS播放重叠 - audioPlayer?.stopAudio() + // audioPlayer?.stopAudio() if(_isInterrupt.get()){ BleService.closeCodec() } @@ -1037,13 +1037,14 @@ object AgentService : CoroutineScope { return } responseBuilder.append(token) -// Log.d(TAG, "liwei--------------- AI Call onToken:$token speakResponse:$speakResponse reply:$reply broadcast:$broadcast") +// Log.d(TAG, "liwei--------------- AI Call onToken:$token speakResponse:$speakResponse reply:$reply broadcast:$broadcast") if (speakResponse && reply && broadcast && token.isNotEmpty()) { ttsService?.speakStream(token) } if (reply && token.isNotEmpty()) { // 发送流式回复token sendEvent("assistant_token", mapOf("sessionid" to sessionid,"token" to token)) + audioPlayer?.stopAudio() } } catch (e: Exception) { Log.e(TAG, "liwei--------------- AI Call onToken 异常", e) @@ -1053,7 +1054,6 @@ object AgentService : CoroutineScope { override fun onComplete(sessionid: String) { try { if (sessionid == currsessionId) { - audioPlayer?.stopAudio() } // 视情况决定是否朗读回复 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 e824eadcf..ea127ffb1 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 @@ -26,6 +26,7 @@ import com.yunqiinnovation.ble_service.BleCompanionManager import android.bluetooth.BluetoothAdapter import android.bluetooth.BluetoothDevice import android.bluetooth.BluetoothManager +import android.bluetooth.BluetoothProfile import android.content.BroadcastReceiver import android.content.IntentFilter import android.content.pm.PackageManager @@ -261,9 +262,9 @@ private var companionManager: BleCompanionManager? = null if (deviceName.equals(TARGET_DEVICE_NAME, ignoreCase = true)) { Log.i(TAG, "发现已绑定的目标设备: $deviceName") - val pairedUuid = companionManager?.getPairedUuid() - val pairedMac = companionManager?.getPairedMacAddress() - companionManager?.registerBackgroundScan(pairedMac, pairedUuid) + val pairedUuid = companionManager?.getPairedUuid() + val pairedMac = companionManager?.getPairedMacAddress() + companionManager?.registerBackgroundScan(pairedMac, pairedUuid) // 注意:这里只能检查绑定状态,无法直接判断连接状态 // 实际连接状态需要通过广播接收器监听 } @@ -677,21 +678,130 @@ private var companionManager: BleCompanionManager? = null super.onDestroy() } + /** + * 蓝牙耳机设备信息数据类 + */ + data class BluetoothHeadsetInfo( + val name: String, + val address: String + ) + private fun checkBleConnection() { Log.i(TAG, "checkBleConnection收到连接请求") try { - // 使用协程异步处理连接 - launch { - if(!BleService.isConnected()) { - BleService.connect() - } + // 首先检查经典蓝牙状态 + if (bluetoothAdapter == null) { + Log.e(TAG, "蓝牙适配器不可用,无法进行BLE连接") + return + } + + if (!bluetoothAdapter!!.isEnabled) { + Log.w(TAG, "经典蓝牙未开启,无法进行BLE连接") + return + } + + // 检查蓝牙耳机连接状态 + checkBluetoothHeadsetConnection { hasConnectedHeadset, headsetInfoList -> + if (hasConnectedHeadset && headsetInfoList.isNotEmpty()) { + Log.i(TAG, "检测到 ${headsetInfoList.size} 个蓝牙耳机已连接") + + // 查找匹配的配对设备 + val pairedMacAddress = BleService.getPairedMacAddress() + val targetHeadset = headsetInfoList.find { it.address == pairedMacAddress } + + if (targetHeadset != null) { + Log.i(TAG, "找到匹配的蓝牙耳机: ${targetHeadset.name} (${targetHeadset.address})") + + // 使用协程异步处理连接 + launch { + if (!BleService.isConnected()) { + BleService.connect() + } + } + } else { + Log.w(TAG, "未找到匹配的配对设备,已连接的设备:") + headsetInfoList.forEach { headset -> + Log.w(TAG, " - ${headset.name} (${headset.address})") + } + Log.w(TAG, "配对的MAC地址: $pairedMacAddress") + } + } else { + Log.w(TAG, "未检测到蓝牙耳机连接,跳过BLE连接") + } } + } catch (e: Exception) { Log.e(TAG, "连接设备失败: ${e.message}", e) } } + /** + * 检查蓝牙耳机连接状态 + * @param callback 回调函数,返回是否有蓝牙耳机连接以及耳机信息列表 + */ + private fun checkBluetoothHeadsetConnection(callback: (Boolean, List) -> Unit) { + try { + bluetoothAdapter?.let { adapter -> + if (ContextCompat.checkSelfPermission(this, android.Manifest.permission.BLUETOOTH_CONNECT) + != PackageManager.PERMISSION_GRANTED) { + Log.w(TAG, "缺少蓝牙连接权限,无法检查蓝牙耳机连接状态") + callback(false, emptyList()) + return + } + + // 检查HEADSET设备连接状态 + adapter.getProfileProxy(this, object : BluetoothProfile.ServiceListener { + override fun onServiceConnected(profile: Int, proxy: BluetoothProfile) { + try { + val connectedDevices = proxy.connectedDevices + val hasConnectedHeadset = connectedDevices.isNotEmpty() + + Log.i(TAG, "蓝牙耳机连接设备数量: ${connectedDevices.size}") + + if (hasConnectedHeadset) { + // 返回所有连接的蓝牙耳机设备信息 + val headsetInfoList = connectedDevices.map { device -> + BluetoothHeadsetInfo( + name = device.name ?: "Unknown Headset", + address = device.address + ) + } + + Log.d(TAG, "蓝牙耳机连接设备列表:") + headsetInfoList.forEach { info -> + Log.d(TAG, " - ${info.name} (${info.address})") + } + + adapter.closeProfileProxy(BluetoothProfile.HEADSET, proxy) + callback(true, headsetInfoList) + } else { + adapter.closeProfileProxy(BluetoothProfile.HEADSET, proxy) + callback(false, emptyList()) + } + + } catch (e: Exception) { + Log.e(TAG, "检查蓝牙耳机连接状态时发生异常", e) + adapter.closeProfileProxy(BluetoothProfile.HEADSET, proxy) + callback(false, emptyList()) + } + } + + override fun onServiceDisconnected(profile: Int) { + Log.d(TAG, "蓝牙耳机服务断开连接") + } + }, BluetoothProfile.HEADSET) + + } ?: run { + Log.e(TAG, "蓝牙适配器为空") + callback(false, emptyList()) + } + } catch (e: Exception) { + Log.e(TAG, "检查蓝牙耳机连接状态时发生异常", e) + callback(false, emptyList()) + } + } + /** * 创建前台服务通知 */ diff --git a/local_plugins/agent_service/ios/agent_service/Sources/agent_service/AgentServiceImpl.swift b/local_plugins/agent_service/ios/agent_service/Sources/agent_service/AgentServiceImpl.swift index a0561af62..d07c9b2b0 100644 --- a/local_plugins/agent_service/ios/agent_service/Sources/agent_service/AgentServiceImpl.swift +++ b/local_plugins/agent_service/ios/agent_service/Sources/agent_service/AgentServiceImpl.swift @@ -1697,6 +1697,7 @@ class ChatApiStreamCallback: StreamCallback { // 在开始流式TTS时立即停止气泡音 } if (reply && token != "") { + try agentService.audioPlayer?.stopAwaitSound() try agentService.sendEvent(name: "assistant_token", data: ["sessionid":sessionid,"token": token]) } }catch { @@ -1876,7 +1877,7 @@ extension AgentServiceImpl: TtsEventListener { isSpeaking = true restartIdleCheck() sendEvent(name: "tts_started", data: ["status": "started"]) - audioPlayer?.stopAwaitSound() + // audioPlayer?.stopAwaitSound() case .synthesisCompleted: isSpeaking = false diff --git a/local_plugins/ble_service/android/src/main/kotlin/com/yunqiinnovation/ble_service/BleConst.kt b/local_plugins/ble_service/android/src/main/kotlin/com/yunqiinnovation/ble_service/BleConst.kt index 71959ff31..52c2706d6 100644 --- a/local_plugins/ble_service/android/src/main/kotlin/com/yunqiinnovation/ble_service/BleConst.kt +++ b/local_plugins/ble_service/android/src/main/kotlin/com/yunqiinnovation/ble_service/BleConst.kt @@ -40,9 +40,9 @@ object BleConst { // ================== 连接状态 ================== const val STATE_DISCONNECTED = 0//未连接 - const val STATE_CONNECTING = 1//已断开连接 + const val STATE_CONNECTING = 1//连接中 const val STATE_CONNECTED = 2//已连接 - const val STATE_DISCONNECTING = 3//连接中 + const val STATE_DISCONNECTING = 3//已断开连接 const val STATE_ERROR = 4//连接错误 // ================== 广播厂商信息 ================== 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 2233ed330..040a87204 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 @@ -640,7 +640,12 @@ object BleService { */ fun connect(address: String) { if (connectionState.value == BleConst.STATE_CONNECTED) { - Log.w(TAG, "已经连接中,忽略连接请求") + Log.w(TAG, "已经连接,忽略连接请求") + return + } + + if (connectionState.value == BleConst.STATE_CONNECTING) { + Log.w(TAG, "连接中,忽略连接请求") return } @@ -1472,6 +1477,18 @@ object BleService { return connectionState.value == BleConst.STATE_CONNECTED } + /** + * 获取缓存设备地址 + * @return mac地址 + */ + fun getPairedMacAddress(): String? { + companionManager = BleCompanionManager.getInstance(context) + // 获取已配对的 MAC 地址 + val pairedMac = companionManager?.getPairedMacAddress() + return pairedMac + } + + /** * 获取已连接设备的地址 * @return 设备地址,未连接时返回null diff --git a/local_plugins/chat_api/android/src/main/kotlin/com/yunqiinnovation/chat_api/MusicSystemFunctionHandler.kt b/local_plugins/chat_api/android/src/main/kotlin/com/yunqiinnovation/chat_api/MusicSystemFunctionHandler.kt index cc40debdd..a241d9def 100644 --- a/local_plugins/chat_api/android/src/main/kotlin/com/yunqiinnovation/chat_api/MusicSystemFunctionHandler.kt +++ b/local_plugins/chat_api/android/src/main/kotlin/com/yunqiinnovation/chat_api/MusicSystemFunctionHandler.kt @@ -186,7 +186,7 @@ private class MusicSearchHandler(private val context: Context?) : FunctionHandle return JSONObject().apply { put("result", "已开始播放媒体") - put("notreply", true) + // put("notreply", true) put("broadcast", false) }.toString() } @@ -215,7 +215,7 @@ private class MusicSearchHandler(private val context: Context?) : FunctionHandle return JSONObject().apply { put("result", "已暂停媒体播放") - put("notreply", true) + // put("notreply", true) put("broadcast", false) }.toString() } @@ -244,7 +244,7 @@ private class MusicSearchHandler(private val context: Context?) : FunctionHandle return JSONObject().apply { put("result", "已关闭媒体播放") - put("notreply", true) + // put("notreply", true) put("broadcast", false) }.toString() } @@ -272,7 +272,7 @@ private class MusicSearchHandler(private val context: Context?) : FunctionHandle return JSONObject().apply { put("result", "已切换到上一首") - put("notreply", true) + // put("notreply", true) put("broadcast", false) }.toString() } @@ -300,7 +300,7 @@ private class MusicSearchHandler(private val context: Context?) : FunctionHandle return JSONObject().apply { put("result", "已切换到下一首") - put("notreply", true) + // put("notreply", true) put("broadcast", false) }.toString() }