diff --git a/.cursorrules b/.cursorrules
index a81ae52e0..ce77e46f4 100644
--- a/.cursorrules
+++ b/.cursorrules
@@ -1,14 +1,34 @@
-1. 请严格遵循 Effective Dart 的风格指南来编写代码
-2. 不要主动运行程序, 如flutter run
-3. 不要改动android, iOS项目的配置信息,包括SDK, NDK, JAVA版本等
-4. 不要使用print, 而是使用logger, 尽可能避免打印日志,仅在出错时记录日志
-5. 尽可能使用GetX提供的各种功能, 而不是使用原生的StatefulWidget
-6. 不要增加重试逻辑,出现失败直接返回错误
-7. 尽可能避免使用try catch
-8. 请使用较小的代码量改动,最安全的方法进行代码编写和优化
-9. 请尽可能使用中文回复问题
-10. 重要:不要过度设计!!
-11. 本项目使用 Kotlin DSL 进行开发
+# 代码简明规范
+## Dart / Flutter
+ 1. 风格统一
+ • 严格遵循 Effective Dart 格式与命名惯例。
+ 2. 日志与调试
+ • 禁止 print;仅在异常场景调用 logger.e(...)。
+ 3. 业务逻辑
+ • 默认使用 GetX(状态、路由、依赖注入等),避免手写 StatefulWidget。
+ 4. 异常处理
+ • 不写重试逻辑,失败即返回错误。
+ • 谨慎使用 try-catch;优先通过类型系统与空安全防御。
+ 5. 代码演进
+ • 任何改动都应 “小步、安全”,杜绝一次性大改或过度设计。
+
+## 原生层(Android / iOS)
+ 1. 配置不可改
+ • 禁止修改 SDK、NDK、JDK 与 iOS 构建配置。
+ 2. Kotlin DSL
+ • 原生实现一律采用 Kotlin + 协程,遵守 Kotlin 最佳实践。
+ • 不写 Java 代码。
+
+## 通用要求
+ 1. 不自动执行
+ • 生成脚本/代码不得包含 flutter run 等执行指令。
+ 2. 并发优先级
+ • 并发场景首选 Kotlin 协程;仅在 Flutter 端按需使用 Dart Future / Stream。
+ 3. 语言
+ • 与用户交流、注释及文档一律使用中文。
+
+宗旨:保持极简,先跑通业务,再考虑优化。
+
# 项目架构概述
DeepSound是一个基于Flutter和GetX构建的多平台语音应用,具有语音合成、语音识别、语音聊天等功能。
diff --git a/android/app/build.gradle.kts b/android/app/build.gradle.kts
index be42f1334..9853fa6a0 100644
--- a/android/app/build.gradle.kts
+++ b/android/app/build.gradle.kts
@@ -67,7 +67,7 @@ android {
applicationId = "com.yunqiinnovation.deepsound"
// You can update the following values to match your application needs.
// For more information, see: https://flutter.dev/to/review-gradle-config.
- minSdk = 24 // Updated to meet azure_speech_recognition_null_safety requirements
+ minSdk = 26 // 更新为26以满足ble_service库的要求
targetSdk = 34 // 设置为 Android 13 (API 33),以匹配 FOREGROUND_SERVICE_MEDIA_PLAYBACK 权限的要求
versionCode = flutter.versionCode
versionName = flutter.versionName
@@ -126,10 +126,10 @@ dependencies {
// 添加本地插件模块依赖
implementation(project(":azure_speech"))
implementation(project(":open_ai_service"))
- implementation(project(":volcano_speech"))
implementation(project(":chat_storage")) // 添加新创建的SQLite聊天存储插件
implementation(project(":jieli")) // 添加新创建的SQLite聊天存储插件
implementation(project(":spotify_sdk"))
+ implementation(project(":deepvoice_utils"))
// 添加OkHttp依赖
implementation("com.squareup.okhttp3:okhttp:4.9.3")
diff --git a/android/app/src/debug/AndroidManifest.xml b/android/app/src/debug/AndroidManifest.xml
deleted file mode 100644
index 399f6981d..000000000
--- a/android/app/src/debug/AndroidManifest.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
-
diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
index 8b3012b08..941f7665f 100644
--- a/android/app/src/main/AndroidManifest.xml
+++ b/android/app/src/main/AndroidManifest.xml
@@ -7,12 +7,15 @@
+
+
+
@@ -75,6 +78,8 @@
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
= Build.VERSION_CODES.O) {
- context.startForegroundService(serviceIntent)
- } else {
- context.startService(serviceIntent)
- }
- }
- }
-}
\ No newline at end of file
diff --git a/android/app/src/main/kotlin/com/yunqiinnovation/deepsound/BleWakeService.kt b/android/app/src/main/kotlin/com/yunqiinnovation/deepsound/BleWakeService.kt
deleted file mode 100644
index 89a1494a2..000000000
--- a/android/app/src/main/kotlin/com/yunqiinnovation/deepsound/BleWakeService.kt
+++ /dev/null
@@ -1,335 +0,0 @@
-package com.yunqiinnovation.deepsound
-
-import android.app.Notification
-import android.app.NotificationChannel
-import android.app.NotificationManager
-import android.app.PendingIntent
-import android.app.Service
-import android.bluetooth.BluetoothAdapter
-import android.bluetooth.BluetoothManager
-import android.bluetooth.le.BluetoothLeScanner
-import android.bluetooth.le.ScanCallback
-import android.bluetooth.le.ScanFilter
-import android.bluetooth.le.ScanResult
-import android.bluetooth.le.ScanSettings
-import android.content.Context
-import android.content.Intent
-import android.os.Binder
-import android.os.Build
-import android.os.IBinder
-import android.os.ParcelUuid
-import androidx.core.app.NotificationCompat
-import com.yunqiinnovation.deepsound.core.utils.FileLogger
-import java.util.UUID
-
-/**
- * BLE唤醒服务,用于接收蓝牙BLE广播唤醒应用
- * 可以作为前台服务运行,以提高可靠性
- */
-class BleWakeService : Service() {
-
- companion object {
- private const val TAG = "BleWakeService"
- private const val NOTIFICATION_ID = 1002
- private const val CHANNEL_ID = "ble_wake_channel"
-
- // 定义唤醒用的特定UUID,应与耳机约定一致
- // 此UUID仅作示例,实际使用时应根据耳机厂商提供的UUID进行修改
- val OLA_FRIEND_UUID = UUID.fromString("0000fd7b-0000-1000-8000-00805f9b34fb")
-
- // 服务启动标志
- private var isRunning = false
-
- // 获取服务运行状态
- fun isServiceRunning(): Boolean = isRunning
- }
-
- private val binder = LocalBinder()
- private var bluetoothAdapter: BluetoothAdapter? = null
- private var bluetoothLeScanner: BluetoothLeScanner? = null
- private var isScanning = false
-
- /**
- * 本地绑定器
- */
- inner class LocalBinder : Binder() {
- fun getService(): BleWakeService = this@BleWakeService
- }
-
- override fun onCreate() {
- super.onCreate()
- FileLogger.d(TAG, "BLE唤醒服务正在创建")
-
- // 初始化蓝牙
- val bluetoothManager = getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager
- bluetoothAdapter = bluetoothManager.adapter
- bluetoothLeScanner = bluetoothAdapter?.bluetoothLeScanner
-
- // 标记服务状态
- isRunning = true
-
- // 启动前台服务
- startForeground(NOTIFICATION_ID, createNotification())
-
- // 开始扫描
- // startScanning()
-
- FileLogger.d(TAG, "BLE唤醒服务已启动")
- }
-
- override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
- // FileLogger.d(TAG, "收到onStartCommand: intent=$intent")
-
- // 如果是PendingIntent触发(BLE广播唤醒),打印日志
- if (intent?.action == "android.bluetooth.adapter.action.REQUEST_ENABLE") {
- //FileLogger.d(TAG, "收到BLE广播唤醒事件,应用被唤醒")
-
- // 在这里可以执行唤醒后的动作,如启动语音交互服务
- val serviceIntent = Intent(this, VoiceInteractionService::class.java)
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
- startForegroundService(serviceIntent)
- } else {
- startService(serviceIntent)
- }
- }
-
- return START_STICKY
- }
-
- override fun onBind(intent: Intent?): IBinder {
- return binder
- }
-
- override fun onDestroy() {
- super.onDestroy()
- FileLogger.d(TAG, "BLE唤醒服务即将销毁")
-
- // 停止扫描
- stopScanning()
-
- // 标记服务状态
- isRunning = false
- }
-
- /**
- * 开始扫描BLE广播
- */
- fun startScanning() {
- FileLogger.d(TAG, "开始扫描BLE广播")
-
- if (bluetoothLeScanner == null) {
- FileLogger.e(TAG, "蓝牙扫描器不可用")
- return
- }
-
- if (isScanning) {
- FileLogger.d(TAG, "已在扫描中,忽略")
- return
- }
-
- // 构建扫描过滤器,匹配特定的UUID
- val scanFilter = ScanFilter.Builder()
- .setServiceUuid(ParcelUuid(OLA_FRIEND_UUID))
- .build()
-
- // 设置扫描参数
- val scanSettings = ScanSettings.Builder()
- .setScanMode(ScanSettings.SCAN_MODE_LOW_POWER) // 低功耗模式
- .build()
-
- try {
- FileLogger.d(TAG, "开始BLE扫描, 扫描过滤器: $OLA_FRIEND_UUID")
- // 开始扫描
- bluetoothLeScanner?.startScan(listOf(scanFilter), scanSettings, scanCallback)
- isScanning = true
- FileLogger.d(TAG, "BLE扫描已启动")
- } catch (e: Exception) {
- FileLogger.e(TAG, "开始BLE扫描失败: ${e.message}")
- }
- }
-
- /**
- * 停止扫描BLE广播
- */
- fun stopScanning() {
- FileLogger.d(TAG, "停止扫描BLE广播")
-
- if (bluetoothLeScanner == null || !isScanning) {
- FileLogger.d(TAG, "没有正在进行的扫描")
- return
- }
-
- try {
- // 停止扫描
- bluetoothLeScanner?.stopScan(scanCallback)
- isScanning = false
- FileLogger.d(TAG, "BLE扫描已停止")
- } catch (e: Exception) {
- FileLogger.e(TAG, "停止BLE扫描失败: ${e.message}")
- }
- }
-
- /**
- * 扫描回调
- */
- private val scanCallback = object : ScanCallback() {
- override fun onScanResult(callbackType: Int, result: ScanResult) {
- FileLogger.d(TAG, "收到扫描结果: ${result.device.address}")
-
- // 检查是否是目标设备和唤醒广播
- val serviceUuids = result.scanRecord?.serviceUuids
- if (serviceUuids != null && serviceUuids.any { it.uuid == OLA_FRIEND_UUID }) {
- FileLogger.d(TAG, "检测到Ola Friend耳机唤醒广播")
-
-
-
- // 从广播包中获取额外数据
- val manufacturerData = result.scanRecord?.getManufacturerSpecificData(0xFFFF)
- if (manufacturerData != null && manufacturerData.size > 1) {
- // 假设第一个字节为命令类型,如0x01表示语音唤醒
- val commandType = manufacturerData[0].toInt() and 0xFF
- FileLogger.d(TAG, "收到命令类型: $commandType")
-
- if (commandType == 0x01) { // 语音唤醒命令
- FileLogger.d(TAG, "耳机语音唤醒命令,启动语音交互")
-
- // 启动语音交互服务
- val serviceIntent = Intent(applicationContext, VoiceInteractionService::class.java)
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
- startForegroundService(serviceIntent)
- } else {
- startService(serviceIntent)
- }
- }
- }
- }
- }
-
- override fun onScanFailed(errorCode: Int) {
- FileLogger.e(TAG, "BLE扫描失败,错误代码: $errorCode")
- isScanning = false
- }
- }
-
- /**
- * 创建通知
- */
- private fun createNotification(): Notification {
- val manager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
-
- // Android 8.0+ 需要创建通知渠道
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
- val channel = NotificationChannel(
- CHANNEL_ID,
- "蓝牙唤醒服务",
- NotificationManager.IMPORTANCE_LOW
- )
- manager.createNotificationChannel(channel)
- }
-
- // 点击通知打开MainActivity
- val intent = Intent(this, MainActivity::class.java).apply {
- flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP
- }
- val pendingIntent = PendingIntent.getActivity(
- this,
- 0,
- intent,
- PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
- )
-
- return NotificationCompat.Builder(this, CHANNEL_ID)
- .setContentTitle("蓝牙唤醒服务")
- .setContentText("正在监听耳机唤醒信号")
- .setSmallIcon(R.mipmap.ic_launcher)
- .setContentIntent(pendingIntent)
- .build()
- }
-
- /**
- * 为后台持久扫描注册PendingIntent
- * 这种方式即使应用被销毁后仍然可被唤醒
- */
- fun registerBackgroundScan() {
- FileLogger.d(TAG, "注册后台持久扫描")
-
- if (bluetoothLeScanner == null) {
- FileLogger.e(TAG, "蓝牙扫描器不可用")
- return
- }
-
- // 构建扫描过滤器,匹配特定的UUID
- val scanFilter = ScanFilter.Builder()
- // .setServiceUuid(ParcelUuid(OLA_FRIEND_UUID))
- .build()
-
- // 设置扫描参数
- val scanSettings = ScanSettings.Builder()
- .setScanMode(ScanSettings.SCAN_MODE_LOW_POWER) // 低功耗模式
- .build()
-
- // 创建PendingIntent
- val intent = Intent(this, BleBroadcastReceiver::class.java)
- intent.action = BleBroadcastReceiver.ACTION_BLE_SCAN_RESULT
-
- val pendingIntent = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
- PendingIntent.getBroadcast(
- this, 0, intent,
- PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE
- )
- } else {
- PendingIntent.getBroadcast(
- this, 0, intent,
- PendingIntent.FLAG_UPDATE_CURRENT
- )
- }
-
- try {
- // 启动带PendingIntent的扫描
- bluetoothLeScanner?.startScan(
- listOf(scanFilter),
- scanSettings,
- pendingIntent
- )
- FileLogger.d(TAG, "后台持久扫描已注册")
- } catch (e: Exception) {
- FileLogger.e(TAG, "注册后台持久扫描失败: ${e.message}")
- }
- }
-
- /**
- * 注销后台持久扫描
- */
- fun unregisterBackgroundScan() {
- FileLogger.d(TAG, "注销后台持久扫描")
-
- if (bluetoothLeScanner == null) {
- FileLogger.e(TAG, "蓝牙扫描器不可用")
- return
- }
-
- // 创建与注册时相同的PendingIntent
- val intent = Intent(this, BleBroadcastReceiver::class.java)
- intent.action = BleBroadcastReceiver.ACTION_BLE_SCAN_RESULT
-
- val pendingIntent = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
- PendingIntent.getBroadcast(
- this, 0, intent,
- PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE
- )
- } else {
- PendingIntent.getBroadcast(
- this, 0, intent,
- PendingIntent.FLAG_UPDATE_CURRENT
- )
- }
-
- try {
- // 停止带PendingIntent的扫描
- bluetoothLeScanner?.stopScan(pendingIntent)
- FileLogger.d(TAG, "后台持久扫描已注销")
- } catch (e: Exception) {
- FileLogger.e(TAG, "注销后台持久扫描失败: ${e.message}")
- }
- }
-}
\ No newline at end of file
diff --git a/android/app/src/main/kotlin/com/yunqiinnovation/deepsound/ClassicBluetoothHelper.kt b/android/app/src/main/kotlin/com/yunqiinnovation/deepsound/ClassicBluetoothHelper.kt
deleted file mode 100644
index 423f4b8e7..000000000
--- a/android/app/src/main/kotlin/com/yunqiinnovation/deepsound/ClassicBluetoothHelper.kt
+++ /dev/null
@@ -1,242 +0,0 @@
-package com.yunqiinnovation.deepsound
-
-import android.bluetooth.BluetoothAdapter
-import android.bluetooth.BluetoothProfile
-import android.content.Context
-import android.content.pm.PackageManager
-import android.os.Build
-import android.util.Log
-import android.Manifest
-import androidx.core.content.ContextCompat
-import java.util.concurrent.atomic.AtomicBoolean
-import java.util.concurrent.CountDownLatch
-import java.util.concurrent.TimeUnit
-
-/**
- * 经典蓝牙辅助类,用于获取已连接的蓝牙设备信息
- */
-class ClassicBluetoothHelper(private val context: Context) {
-
- private val TAG = "ClassicBluetoothHelper"
-
- // 缓存蓝牙代理对象
- private var a2dpProxy: BluetoothProfile? = null
- private var headsetProxy: BluetoothProfile? = null
-
- /**
- * 获取已连接的A2DP设备(音频输出设备)
- * @param callback 回调接口,返回设备列表或错误信息
- */
- fun getConnectedA2dpDevices(callback: BluetoothDevicesCallback) {
- getConnectedBluetoothDevices(BluetoothProfile.A2DP, callback)
- }
-
- /**
- * 获取已连接的蓝牙耳机设备
- * @param callback 回调接口,返回设备列表或错误信息
- */
- fun getConnectedHeadsetDevices(callback: BluetoothDevicesCallback) {
- getConnectedBluetoothDevices(BluetoothProfile.HEADSET, callback)
- }
-
- /**
- * 检查是否有蓝牙连接权限
- * @return 是否有权限
- */
- private fun hasBluetoothConnectPermission(): Boolean {
- return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
- ContextCompat.checkSelfPermission(
- context,
- Manifest.permission.BLUETOOTH_CONNECT
- ) == PackageManager.PERMISSION_GRANTED
- } else {
- true
- }
- }
-
- /**
- * 检查蓝牙是否启用
- * @return 蓝牙是否启用
- */
- fun isBluetoothEnabled(): Boolean {
- try {
- val bluetoothAdapter = BluetoothAdapter.getDefaultAdapter()
- if (bluetoothAdapter == null) {
- Log.w(TAG, "设备不支持蓝牙")
- return false
- }
-
- return bluetoothAdapter.isEnabled
- } catch (e: Exception) {
- Log.e(TAG, "检查蓝牙状态失败: ${e.message}")
- return false
- }
- }
-
- /**
- * 获取指定配置文件的已连接蓝牙设备
- * @param profile 蓝牙配置文件类型,如 BluetoothProfile.A2DP 或 BluetoothProfile.HEADSET
- * @param callback 回调接口,返回设备列表或错误信息
- */
- private fun getConnectedBluetoothDevices(profile: Int, callback: BluetoothDevicesCallback) {
- val bluetoothAdapter = BluetoothAdapter.getDefaultAdapter()
- if (bluetoothAdapter == null) {
- Log.e(TAG, "没有找到蓝牙适配器")
- callback.onError("NO_ADAPTER", "没有找到蓝牙适配器")
- return
- }
-
- // 检查蓝牙权限
- if (!hasBluetoothConnectPermission()) {
- Log.e(TAG, "缺少BLUETOOTH_CONNECT权限")
- callback.onError("PERMISSION_DENIED", "缺少蓝牙连接权限,请在应用设置中授予该权限")
- return
- }
-
- try {
- // 检查是否已经有缓存的代理对象
- val cachedProxy = when (profile) {
- BluetoothProfile.A2DP -> a2dpProxy
- BluetoothProfile.HEADSET -> headsetProxy
- else -> null
- }
-
- if (cachedProxy != null) {
- // 使用已缓存的代理对象
- processDeviceList(cachedProxy, profile, callback)
- return
- }
-
- // 使用原子布尔值确保回调只被调用一次
- val callbackCalled = AtomicBoolean(false)
-
- // 使用CountDownLatch等待服务连接
- val latch = CountDownLatch(1)
-
- // 通过 getProfileProxy 获取指定 Profile (A2DP/HEADSET) 的连接状态
- bluetoothAdapter.getProfileProxy(context,
- object : BluetoothProfile.ServiceListener {
- override fun onServiceConnected(profileId: Int, proxy: BluetoothProfile) {
- Log.d(TAG, "蓝牙配置文件服务已连接: $profileId")
- if (profileId == profile) {
- // 缓存代理对象
- when (profileId) {
- BluetoothProfile.A2DP -> a2dpProxy = proxy
- BluetoothProfile.HEADSET -> headsetProxy = proxy
- }
-
- try {
- processDeviceList(proxy, profileId, callback)
- latch.countDown()
- } catch (e: Exception) {
- Log.e(TAG, "处理设备信息时出错: ${e.message}")
-
- // 确保回调只被调用一次
- if (callbackCalled.compareAndSet(false, true)) {
- callback.onError("DEVICE_ERROR", "处理设备信息时出错: ${e.message}")
- }
- latch.countDown()
- }
- }
- }
-
- override fun onServiceDisconnected(profileId: Int) {
- // Profile 断开连接
- Log.e(TAG, "蓝牙配置文件服务断开连接: $profileId")
-
- // 清除对应的缓存代理
- when (profileId) {
- BluetoothProfile.A2DP -> a2dpProxy = null
- BluetoothProfile.HEADSET -> headsetProxy = null
- }
-
- // 确保回调只被调用一次,但不在这里调用错误回调
- // 避免服务断开时也返回错误导致重复回调
- if (callbackCalled.compareAndSet(false, true) && !latch.await(0, TimeUnit.MILLISECONDS)) {
- callback.onError("SERVICE_DISCONNECTED", "蓝牙配置文件服务断开连接")
- latch.countDown()
- }
- }
- }, profile
- )
-
- // 设置超时处理
- Thread {
- try {
- // 最多等待3秒
- if (!latch.await(3, TimeUnit.SECONDS)) {
- Log.e(TAG, "获取蓝牙设备超时")
- if (callbackCalled.compareAndSet(false, true)) {
- callback.onError("TIMEOUT", "获取蓝牙设备超时")
- }
- }
- } catch (e: InterruptedException) {
- Log.e(TAG, "等待蓝牙服务被中断: ${e.message}")
- if (callbackCalled.compareAndSet(false, true)) {
- callback.onError("INTERRUPTED", "等待蓝牙服务被中断: ${e.message}")
- }
- }
- }.start()
-
- } catch (e: Exception) {
- Log.e(TAG, "获取蓝牙设备失败: ${e.message}")
- callback.onError("BLUETOOTH_ERROR", "获取蓝牙设备失败: ${e.message}")
- }
- }
-
- /**
- * 处理设备列表
- */
- private fun processDeviceList(proxy: BluetoothProfile, profileId: Int, callback: BluetoothDevicesCallback) {
- // 获取已连接设备
- val connectedDevices = proxy.connectedDevices
-
- // 转换为设备信息列表
- val devicesList = connectedDevices.map { device ->
- mapOf(
- "name" to (device.name ?: "未知设备"),
- "address" to device.address
- )
- }
-
- Log.d(TAG, "获取到的设备列表:${devicesList.size}个设备")
-
- // 返回设备列表(不关闭代理)
- callback.onSuccess(devicesList)
- }
-
- /**
- * 释放资源
- */
- fun dispose() {
- val bluetoothAdapter = BluetoothAdapter.getDefaultAdapter() ?: return
-
- // 释放A2DP代理
- a2dpProxy?.let {
- try {
- bluetoothAdapter.closeProfileProxy(BluetoothProfile.A2DP, it)
- } catch (e: Exception) {
- Log.e(TAG, "关闭A2DP代理失败: ${e.message}")
- }
- a2dpProxy = null
- }
-
- // 释放HEADSET代理
- headsetProxy?.let {
- try {
- bluetoothAdapter.closeProfileProxy(BluetoothProfile.HEADSET, it)
- } catch (e: Exception) {
- Log.e(TAG, "关闭HEADSET代理失败: ${e.message}")
- }
- headsetProxy = null
- }
- }
-
- /**
- * 蓝牙设备回调接口
- */
- interface BluetoothDevicesCallback {
- fun onSuccess(devices: List