From 3f90a2870ce7ea7c45c5557d4a1c78b59bbc97c0 Mon Sep 17 00:00:00 2001 From: fdp <1286779656@qq.com> Date: Mon, 21 Apr 2025 19:06:17 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D1.ble=E8=BF=9E=E6=8E=A5?= =?UTF-8?q?=E5=BC=82=E5=B8=B8=E9=97=AE=E9=A2=98=202.=E7=AC=AC=E4=BA=8C?= =?UTF-8?q?=E6=AC=A1=E8=BF=9B=E5=85=A5=E6=89=AB=E6=8F=8F=E5=BC=82=E5=B8=B8?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../deepsound/BleBroadcastReceiver.kt | 6 +- .../deepsound/BleWakeService.kt | 4 +- .../deepsound/VoiceInteractionService.kt | 4 +- .../azure_speech/AzureAsrHelper.kt | 4 + local_plugins/jieli/android/build.gradle.kts | 1 + .../com/yunqiinnovation/jieli/BleService.kt | 287 +++++++++++------- local_plugins/jieli/lib/jieli.dart | 80 ++--- 7 files changed, 240 insertions(+), 146 deletions(-) diff --git a/android/app/src/main/kotlin/com/yunqiinnovation/deepsound/BleBroadcastReceiver.kt b/android/app/src/main/kotlin/com/yunqiinnovation/deepsound/BleBroadcastReceiver.kt index 15ad7a27c..131ba74eb 100644 --- a/android/app/src/main/kotlin/com/yunqiinnovation/deepsound/BleBroadcastReceiver.kt +++ b/android/app/src/main/kotlin/com/yunqiinnovation/deepsound/BleBroadcastReceiver.kt @@ -22,13 +22,13 @@ class BleBroadcastReceiver : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { FileLogger.init(context) - FileLogger.d(TAG, "接收到广播: ${intent.action}") + //FileLogger.d(TAG, "接收到广播: ${intent.action}") if (intent.action == ACTION_BLE_SCAN_RESULT) { - FileLogger.d(TAG, "收到系统BLE扫描唤醒事件") + //FileLogger.d(TAG, "收到系统BLE扫描唤醒事件") // 打印日志,表明唤醒成功 - FileLogger.d(TAG, "应用被蓝牙BLE广播唤醒成功") + //FileLogger.d(TAG, "应用被蓝牙BLE广播唤醒成功") // 启动BLE唤醒服务,该服务将执行后续操作 val serviceIntent = Intent(context, BleWakeService::class.java) diff --git a/android/app/src/main/kotlin/com/yunqiinnovation/deepsound/BleWakeService.kt b/android/app/src/main/kotlin/com/yunqiinnovation/deepsound/BleWakeService.kt index cc88226d7..89a1494a2 100644 --- a/android/app/src/main/kotlin/com/yunqiinnovation/deepsound/BleWakeService.kt +++ b/android/app/src/main/kotlin/com/yunqiinnovation/deepsound/BleWakeService.kt @@ -78,11 +78,11 @@ class BleWakeService : Service() { } override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { - FileLogger.d(TAG, "收到onStartCommand: intent=$intent") + // FileLogger.d(TAG, "收到onStartCommand: intent=$intent") // 如果是PendingIntent触发(BLE广播唤醒),打印日志 if (intent?.action == "android.bluetooth.adapter.action.REQUEST_ENABLE") { - FileLogger.d(TAG, "收到BLE广播唤醒事件,应用被唤醒") + //FileLogger.d(TAG, "收到BLE广播唤醒事件,应用被唤醒") // 在这里可以执行唤醒后的动作,如启动语音交互服务 val serviceIntent = Intent(this, VoiceInteractionService::class.java) diff --git a/android/app/src/main/kotlin/com/yunqiinnovation/deepsound/VoiceInteractionService.kt b/android/app/src/main/kotlin/com/yunqiinnovation/deepsound/VoiceInteractionService.kt index b93a79170..130bea448 100644 --- a/android/app/src/main/kotlin/com/yunqiinnovation/deepsound/VoiceInteractionService.kt +++ b/android/app/src/main/kotlin/com/yunqiinnovation/deepsound/VoiceInteractionService.kt @@ -481,7 +481,7 @@ class VoiceInteractionService : Service() { * 当 Service 被启动时调用 */ override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { - FileLogger.d(TAG, "onStartCommand: intent=$intent, action=${intent?.action}") + //FileLogger.d(TAG, "onStartCommand: intent=$intent, action=${intent?.action}") intent?.let {receivedIntent -> when(receivedIntent.action) { @@ -497,7 +497,7 @@ class VoiceInteractionService : Service() { } else -> { - FileLogger.d(TAG, "onStartCommand收到未知Intent: action=${receivedIntent.action}") + //FileLogger.d(TAG, "onStartCommand收到未知Intent: action=${receivedIntent.action}") } } } 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 1dd7cf624..62ad5685c 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 @@ -34,6 +34,10 @@ class AzureAsrHelper(private val context: Context) { private var customAudioProcessor: CustomAudioProcessor? = null private var pushStream: PushAudioInputStream? = null private var audioConfig: AudioConfig? = null + // 公共方法:允许外部写入音频数据 + fun pushAudioData(data: ByteArray) { + pushStream?.write(data) // 使用 SDK 的正确方法(如 push) + } // 初始化SDK并创建recognizer fun initialize(subscriptionKey: String, region: String, diff --git a/local_plugins/jieli/android/build.gradle.kts b/local_plugins/jieli/android/build.gradle.kts index 70b01c20e..de08666ad 100644 --- a/local_plugins/jieli/android/build.gradle.kts +++ b/local_plugins/jieli/android/build.gradle.kts @@ -52,4 +52,5 @@ dependencies { implementation("androidx.lifecycle:lifecycle-livedata-ktx:2.4.0") implementation("androidx.lifecycle:lifecycle-service:2.4.0") implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.2") + implementation(project(":azure_speech")) } \ No newline at end of file diff --git a/local_plugins/jieli/android/src/main/kotlin/com/yunqiinnovation/jieli/BleService.kt b/local_plugins/jieli/android/src/main/kotlin/com/yunqiinnovation/jieli/BleService.kt index 2ca921e24..445f78a4a 100644 --- a/local_plugins/jieli/android/src/main/kotlin/com/yunqiinnovation/jieli/BleService.kt +++ b/local_plugins/jieli/android/src/main/kotlin/com/yunqiinnovation/jieli/BleService.kt @@ -10,35 +10,43 @@ import android.util.Log import androidx.lifecycle.LifecycleService import androidx.lifecycle.MutableLiveData import kotlinx.coroutines.* +import java.io.File +import java.io.FileOutputStream +import java.text.SimpleDateFormat import java.util.* - +import com.yunqiinnovation.azure_speech.AzureAsrHelper +import com.yunqiinnovation.azure_speech.AzureTtsHelper /** * BLE服务类,提供核心蓝牙功能 */ class BleService(private val context: Context) { companion object { private const val TAG = "JieliBleService" - + // 服务和特征UUID - 根据协议规范重新定义 // private const val AUDIO_SERVICE_UUID = "0000faa0-0000-1000-8000-00805f9b34fb" // 主服务UUID - private const val AUDIO_SERVICE_UUID = "0000fd7b-0000-1000-8000-00805f9b34fb" // 主服务UUID - private const val NOTIFY_CHAR_UUID = "0000faa2-0000-1000-8000-00805f9b34fb" // 用于接收数据的特征(notify) - private const val WRITE_CHAR_UUID = "0000faa1-0000-1000-8000-00805f9b34fb" // 用于发送命令的特征(write without response) - private const val CLIENT_CHAR_CONFIG_UUID = "00002902-0000-1000-8000-00805f9b34fb" // 标准的客户端特征配置描述符 + private const val AUDIO_SERVICE_UUID = "0000abc0-0000-1111-2222-123456789abc" // 主服务UUID + private const val NOTIFY_CHAR_UUID = + "0000abc2-0000-1111-2222-123456789abc" // 用于接收数据的特征(notify) + private const val WRITE_CHAR_UUID = + "0000abc1-0000-1111-2222-123456789abc" // 用于发送命令的特征(write without response) + private const val CLIENT_CHAR_CONFIG_UUID = + "00002902-0000-1000-8000-00805f9b34fb" // 标准的客户端特征配置描述符 + // 连接状态常量 const val STATE_DISCONNECTED = 0 const val STATE_CONNECTING = 1 const val STATE_CONNECTED = 2 const val STATE_DISCONNECTING = 3 const val STATE_ERROR = 4 - + // 扫描结果更新间隔(毫秒) private const val SCAN_RESULT_UPDATE_INTERVAL = 500L - + // 命令类型常量 const val COMMAND_VOLUME = 1 } - + /** * 回调接口 */ @@ -49,33 +57,41 @@ class BleService(private val context: Context) { fun onWakeupSignalReceived() fun onCommandResult(commandType: Int, success: Boolean) } - + + // region 文件相关 + private var outputFile: File? = null + private var fos: FileOutputStream? = null + + // Azure服务 + private var azureAsrHelper: AzureAsrHelper? = null + private var azureTtsHelper: AzureTtsHelper? = null // 蓝牙管理器和适配器 - private val bluetoothManager = context.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager + private val bluetoothManager = + context.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager private val bluetoothAdapter: BluetoothAdapter? = bluetoothManager.adapter private val bluetoothLeScanner: BluetoothLeScanner? = bluetoothAdapter?.bluetoothLeScanner - + // GATT连接和特征 private var bluetoothGatt: BluetoothGatt? = null private var notifyChar: BluetoothGattCharacteristic? = null private var writeChar: BluetoothGattCharacteristic? = null - + // 扫描相关 private val scanHandler = Handler(Looper.getMainLooper()) private val scanResults = ArrayList() private var isScanning = false private var isPendingScanResultUpdate = false - private val scanResultUpdateRunnable = Runnable { + private val scanResultUpdateRunnable = Runnable { notifyScanResults() isPendingScanResultUpdate = false } - + // 连接状态LiveData val connectionState = MutableLiveData(STATE_DISCONNECTED) - + // 统一回调接口 var callback: Callback? = null - + /** * 开始扫描BLE设备 */ @@ -83,32 +99,35 @@ class BleService(private val context: Context) { if (bluetoothLeScanner == null || isScanning) { return false } - + Log.i(TAG, "开始扫描BLE设备...") scanResults.clear() - + val serviceUuid = UUID.fromString(AUDIO_SERVICE_UUID) val scanFilter = ScanFilter.Builder() // .setServiceUuid(ParcelUuid(serviceUuid)) .build() val scanFilters = listOf(scanFilter) - + val scanSettings = ScanSettings.Builder() .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY) .build() - + bluetoothLeScanner.startScan(scanFilters, scanSettings, scanCallback) isScanning = true - + // 设置超时停止扫描 scanHandler.postDelayed({ - Log.d(TAG, "定时器触发,准备停止扫描 [主线程:${Looper.myLooper() == Looper.getMainLooper()}]") + Log.d( + TAG, + "定时器触发,准备停止扫描 [主线程:${Looper.myLooper() == Looper.getMainLooper()}]" + ) stopScan() }, scanTimeout) - + return true } - + /** * 停止扫描 */ @@ -118,17 +137,17 @@ class BleService(private val context: Context) { if (bluetoothLeScanner == null || !isScanning) { return false } - + bluetoothLeScanner.stopScan(scanCallback) scanHandler.removeCallbacksAndMessages(null) isScanning = false - + // 最后一次发送扫描结果 notifyScanResults() - + return true } - + /** * 扫描回调 */ @@ -142,8 +161,8 @@ class BleService(private val context: Context) { // if (!deviceName.contains("sanag") && !deviceName.contains("Ola Friend")) { // return // } - Log.d(TAG, "============发现设备: $deviceName [$deviceAddress], RSSI: $rssi") - + //Log.d(TAG, "============发现设备: $deviceName [$deviceAddress], RSSI: $rssi") + // 打印所有发现的UUID以便调试 val serviceUuids = scanRecord?.serviceUuids if (serviceUuids != null) { @@ -151,37 +170,41 @@ class BleService(private val context: Context) { Log.d(TAG, "设备UUID: ${uuid.uuid}") } } - + // 检查制造商数据 for (i in 0 until 65535) { val manufacturerData = scanRecord?.getManufacturerSpecificData(i) if (manufacturerData != null) { - Log.d(TAG, "发现厂商ID: $i, 数据长度: ${manufacturerData.size}") + // Log.d(TAG, "发现厂商ID: $i, 数据长度: ${manufacturerData.size}") // 这里可以根据已知的厂商ID和格式解析数据 } } // 避免重复添加 + val existingIndex = scanResults.indexOfFirst { it.device.address == deviceAddress } + if (existingIndex >= 0) { + scanResults[existingIndex] = result } else { + Log.d(TAG, "扫描回调A, scanResults[existingIndex]ADD") scanResults.add(result) } - + // 如果没有待处理的更新,安排一个 if (!isPendingScanResultUpdate) { isPendingScanResultUpdate = true scanHandler.postDelayed(scanResultUpdateRunnable, SCAN_RESULT_UPDATE_INTERVAL) } } - + override fun onScanFailed(errorCode: Int) { Log.e(TAG, "扫描失败,错误码: $errorCode") isScanning = false } } - + /** * 将扫描结果通知回调 */ @@ -196,7 +219,7 @@ class BleService(private val context: Context) { callback?.onScanResult(devicesJson) } - + /** * 连接到指定设备 */ @@ -206,24 +229,25 @@ class BleService(private val context: Context) { updateConnectionState(STATE_ERROR) return } - + // 先断开现有连接 bluetoothGatt?.close() bluetoothGatt = null - + try { val device = bluetoothAdapter.getRemoteDevice(deviceAddress) updateConnectionState(STATE_CONNECTING) - + // 连接GATT服务器 Log.i(TAG, "正在连接设备: ${device.name ?: "未知设备"} [$deviceAddress]") - bluetoothGatt = device.connectGatt(context, false, gattCallback) + bluetoothGatt = + device.connectGatt(context, false, gattCallback, BluetoothDevice.TRANSPORT_LE) } catch (e: Exception) { Log.e(TAG, "连接设备时发生错误: ${e.message}") updateConnectionState(STATE_ERROR) } } - + /** * 断开连接 */ @@ -231,7 +255,7 @@ class BleService(private val context: Context) { bluetoothGatt?.disconnect() updateConnectionState(STATE_DISCONNECTING) } - + /** * 更新连接状态并通知回调 */ @@ -239,22 +263,24 @@ class BleService(private val context: Context) { connectionState.postValue(state) callback?.onConnectionStateChanged(state) } - + /** * GATT回调 */ private val gattCallback = object : BluetoothGattCallback() { + + override fun onConnectionStateChange(gatt: BluetoothGatt, status: Int, newState: Int) { if (newState == BluetoothProfile.STATE_CONNECTED && status == BluetoothGatt.GATT_SUCCESS) { Log.i(TAG, "设备已连接,开始发现服务...") updateConnectionState(STATE_CONNECTED) - + // 开始搜索服务 gatt.discoverServices() } else if (newState == BluetoothProfile.STATE_DISCONNECTED) { Log.i(TAG, "设备已断开连接") updateConnectionState(STATE_DISCONNECTED) - + // 清理资源 gatt.close() bluetoothGatt = null @@ -263,33 +289,37 @@ class BleService(private val context: Context) { } else if (status != BluetoothGatt.GATT_SUCCESS) { Log.e(TAG, "连接失败,status=$status") updateConnectionState(STATE_ERROR) - + gatt.close() bluetoothGatt = null } } - + override fun onServicesDiscovered(gatt: BluetoothGatt, status: Int) { if (status == BluetoothGatt.GATT_SUCCESS) { Log.i(TAG, "服务发现成功") - + // 查找目标服务 val audioService = gatt.getService(UUID.fromString(AUDIO_SERVICE_UUID)) if (audioService != null) { - Log.i(TAG, "找到音频服务") - + Log.i(TAG, "找到音频服务,开始遍历特征...") + audioService.characteristics.forEach { char -> + Log.i(TAG, "特征UUID: ${char.uuid}") + } + Log.i(TAG, "找到音频服务$") + // 获取特征 notifyChar = audioService.getCharacteristic(UUID.fromString(NOTIFY_CHAR_UUID)) writeChar = audioService.getCharacteristic(UUID.fromString(WRITE_CHAR_UUID)) - if (notifyChar != null && writeChar != null) { Log.i(TAG, "找到所需特征") - + // 设置特征写入类型为无响应模式 writeChar?.writeType = BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE - + // 注册特征通知 setupNotifications(gatt) + } else { Log.e(TAG, "未找到所需特征") updateConnectionState(STATE_ERROR) @@ -303,43 +333,66 @@ class BleService(private val context: Context) { updateConnectionState(STATE_ERROR) } } - - override fun onCharacteristicChanged(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic) { + // 添加扩展函数(在文件顶部声明) + fun List.toByteArray(): ByteArray { + return ByteArray(this.size) { this[it] } + } + + override fun onCharacteristicChanged( + gatt: BluetoothGatt, + characteristic: BluetoothGattCharacteristic + ) { val characteristicUuid = characteristic.uuid.toString() Log.d(TAG, "收到特征变化通知: $characteristicUuid") - + when (characteristicUuid) { // NOTIFY_CHAR_UUID -> { // // 收到音频数据 // val audioData = characteristic.value // Log.d(TAG, "收到音频数据帧,大小: ${audioData.size} 字节") - + // callback?.onAudioDataReceived(audioData) // } NOTIFY_CHAR_UUID -> { // 处理控制消息 val value = characteristic.value Log.i(TAG, "收到控制通知: ${value.contentToString()}") - + + // 判断是否收到音频数据 + if (value.isNotEmpty() && (value[0].toInt() and 0xFF == 0x4b || value[0].toInt() and 0xFF == 0x48)) { + // 处理设备返回的协议数据 + fos?.write(value) + // 方法1:手动转换 +val byteArray = ByteArray(value.size) { index -> value[index] } + +// 或者直接使用现有 + azureAsrHelper?.pushAudioData(byteArray) + } // 判断是否是协议格式响应 if (value.isNotEmpty() && value[0].toInt() and 0xFF == 0xA5) { // 处理设备返回的协议数据 processResponseData(value) - } + } // 假设0x01表示唤醒信号 else if (value.isNotEmpty() && value[0].toInt() == 0x01) { Log.i(TAG, "收到唤醒信号") - + callback?.onWakeupSignalReceived() - + // 收到唤醒后自动发送开始录音命令 sendStartRecordingCommand() + + } } } } - - override fun onCharacteristicWrite(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic, status: Int) { + + override fun onCharacteristicWrite( + gatt: BluetoothGatt, + characteristic: BluetoothGattCharacteristic, + status: Int + ) { if (characteristic.uuid.toString() == WRITE_CHAR_UUID) { if (status == BluetoothGatt.GATT_SUCCESS) { Log.i(TAG, "命令发送成功: ${characteristic.value.contentToString()}") @@ -348,8 +401,12 @@ class BleService(private val context: Context) { } } } - - override fun onDescriptorWrite(gatt: BluetoothGatt, descriptor: BluetoothGattDescriptor, status: Int) { + + override fun onDescriptorWrite( + gatt: BluetoothGatt, + descriptor: BluetoothGattDescriptor, + status: Int + ) { if (status == BluetoothGatt.GATT_SUCCESS) { Log.i(TAG, "描述符写入成功: ${descriptor.uuid}") Log.i(TAG, "特征通知设置成功,设备就绪") @@ -359,7 +416,7 @@ class BleService(private val context: Context) { } } } - + /** * 设置特征通知 */ @@ -367,10 +424,10 @@ class BleService(private val context: Context) { // 启用 notify 特征通知 if (notifyChar?.properties?.and(BluetoothGattCharacteristic.PROPERTY_NOTIFY) != 0) { gatt.setCharacteristicNotification(notifyChar, true) - + val descriptor = notifyChar?.getDescriptor(UUID.fromString(CLIENT_CHAR_CONFIG_UUID)) descriptor?.value = BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE - + if (descriptor != null) { gatt.writeDescriptor(descriptor) Log.i(TAG, "设置数据通知特征通知") @@ -379,10 +436,11 @@ class BleService(private val context: Context) { Log.e(TAG, "数据通知特征不支持通知") } } - + + /** * 处理设备返回的数据 - * 协议格式: + * 协议格式: * 协议头(0xA5) + 字段类型(1byte) + 数据长度(1byte) + 数据域(N byte) + 校验和(1byte) */ private fun processResponseData(data: ByteArray) { @@ -390,36 +448,36 @@ class BleService(private val context: Context) { Log.e(TAG, "返回数据格式错误,长度不足") return } - + // 判断协议头 if (data[0].toInt() and 0xFF != 0xA5) { Log.e(TAG, "返回数据协议头错误: ${data[0].toInt() and 0xFF}") return } - + val fieldType = data[1].toInt() and 0xFF val dataLen = data[2].toInt() and 0xFF - + // 验证长度 if (data.size != dataLen + 4) { // 协议头+字段类型+数据长度+数据域+校验和 Log.e(TAG, "返回数据长度不匹配: 应为${dataLen + 4},实际为${data.size}") return } - + // 计算校验和 var checksum = 0 for (i in 0 until data.size - 1) { checksum += data[i].toInt() and 0xFF } checksum = checksum and 0xFF - + val receivedChecksum = data[data.size - 1].toInt() and 0xFF - + if (checksum != receivedChecksum) { Log.e(TAG, "校验和错误: 计算值=$checksum, 接收值=$receivedChecksum") return } - + // 解析字段类型 when (fieldType) { 0x01 -> { @@ -435,7 +493,7 @@ class BleService(private val context: Context) { } } } - + /** * 发送开始录音命令 */ @@ -444,26 +502,43 @@ class BleService(private val context: Context) { Log.e(TAG, "无法发送开始录音命令,设备未连接") return false } - + try { val commandData = byteArrayOf(0x01) writeChar?.value = commandData - + // 获取外部存储目录路径 + // 生成带时间戳的文件名(格式:yyyyMMdd_HHmmss_audio.opus) + val timeStamp = SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(Date()) + val fileName = "${timeStamp}_audio.opus" + val externalDir = context.getExternalFilesDir(null) + outputFile = File(externalDir, fileName) + fos = FileOutputStream(outputFile).also { + Log.i(TAG, "音频文件创建成功:${outputFile?.absolutePath}") + } + + // val subscriptionKey = call.argument("subscriptionKey") ?: "" + // val region = call.argument("region") ?: "" + // val supportedLanguages = call.argument>("supportedLanguages") ?: listOf("zh-CN") + + // val success = azureAsrHelper.initialize(subscriptionKey, region, supportedLanguages.toTypedArray()) + // result.success(success) + + val success = bluetoothGatt?.writeCharacteristic(writeChar) ?: false - + if (success) { Log.i(TAG, "开始录音命令已发送") } else { Log.e(TAG, "开始录音命令发送失败") } - + return success } catch (e: Exception) { Log.e(TAG, "发送开始录音命令时发生异常: ${e.message}") return false } } - + /** * 发送停止录音命令 */ @@ -472,74 +547,78 @@ class BleService(private val context: Context) { Log.e(TAG, "无法发送停止录音命令,设备未连接") return false } - + try { val commandData = byteArrayOf(0x02) writeChar?.value = commandData - + val success = bluetoothGatt?.writeCharacteristic(writeChar) ?: false - + fos?.close() if (success) { Log.i(TAG, "停止录音命令已发送") } else { Log.e(TAG, "停止录音命令发送失败") } - + return success } catch (e: Exception) { Log.e(TAG, "发送停止录音命令时发生异常: ${e.message}") return false } } - + /** * 发送音量调整命令 * 协议格式: * 协议头(0xAA) + 字段类型(0x01) + 数据长度(0x01) + 音量值(0~16) + 校验和 */ fun adjustVolume(volume: Int): Boolean { + Log.e( + TAG, + "adjustVolume,bluetoothGatt=${bluetoothGatt},writeChar=${writeChar},connectionState=${connectionState.value}" + ) if (bluetoothGatt == null || writeChar == null || connectionState.value != STATE_CONNECTED) { Log.e(TAG, "无法发送音量调整命令,设备未连接") return false } - + // 限制音量范围为0-16 val safeVolume = volume.coerceIn(0, 16) - + try { // 构建命令数据 val header: Byte = 0xAA.toByte() val fieldType: Byte = 0x01 val dataLen: Byte = 0x01 val volumeData: Byte = safeVolume.toByte() - + // 计算校验和 - val checksum = ((header.toInt() and 0xFF) + - (fieldType.toInt() and 0xFF) + - (dataLen.toInt() and 0xFF) + - (volumeData.toInt() and 0xFF)) and 0xFF - + val checksum = ((header.toInt() and 0xFF) + + (fieldType.toInt() and 0xFF) + + (dataLen.toInt() and 0xFF) + + (volumeData.toInt() and 0xFF)) and 0xFF + // 创建完整命令 val commandData = byteArrayOf(header, fieldType, dataLen, volumeData, checksum.toByte()) - + // 写入特征 writeChar?.value = commandData - + val success = bluetoothGatt?.writeCharacteristic(writeChar) ?: false - + if (success) { Log.i(TAG, "音量调整命令已发送: $safeVolume") } else { Log.e(TAG, "音量调整命令发送失败") } - + return success } catch (e: Exception) { Log.e(TAG, "发送音量调整命令时发生异常: ${e.message}") return false } } - + /** * 增加音量 */ @@ -547,7 +626,7 @@ class BleService(private val context: Context) { // 假设当前音量保存在设备中,我们只需发送增加命令 return adjustVolume(16) // 发送最大音量命令 } - + /** * 减小音量 */ @@ -555,7 +634,7 @@ class BleService(private val context: Context) { // 假设当前音量保存在设备中,我们只需发送减小命令 return adjustVolume(0) // 发送最小音量命令 } - + /** * 清理资源 */ diff --git a/local_plugins/jieli/lib/jieli.dart b/local_plugins/jieli/lib/jieli.dart index fa65eca91..b92e5841c 100644 --- a/local_plugins/jieli/lib/jieli.dart +++ b/local_plugins/jieli/lib/jieli.dart @@ -51,34 +51,37 @@ class EventType { /// 杰理BLE插件主类 class Jieli { - static const MethodChannel _methodChannel = MethodChannel('com.yunqiinnovation.jieli'); - static const EventChannel _eventChannel = EventChannel('com.yunqiinnovation.jieli/events'); - + static const MethodChannel _methodChannel = + MethodChannel('com.yunqiinnovation.jieli'); + static const EventChannel _eventChannel = + EventChannel('com.yunqiinnovation.jieli/events'); + /// 扫描结果流 - static final StreamController> _scanController = + static final StreamController> _scanController = StreamController>.broadcast(); Stream> get scanResults => _scanController.stream; - + /// 连接状态变化流 - static final StreamController _connectionController = + static final StreamController _connectionController = StreamController.broadcast(); Stream get connectionState => _connectionController.stream; - + /// 音频数据流 - static final StreamController _audioController = + static final StreamController _audioController = StreamController.broadcast(); Stream get audioDataStream => _audioController.stream; - + /// 唤醒信号流 - static final StreamController _wakeupController = + static final StreamController _wakeupController = StreamController.broadcast(); Stream get wakeupStream => _wakeupController.stream; - + /// 命令结果流 - static final StreamController _commandResultController = + static final StreamController _commandResultController = StreamController.broadcast(); - Stream get commandResultStream => _commandResultController.stream; - + Stream get commandResultStream => + _commandResultController.stream; + /// 原始事件流 Stream>? _eventStream; @@ -95,22 +98,24 @@ class Jieli { /// 设置事件监听器 void _setupEventListener() { - _eventSubscription = _eventChannel.receiveBroadcastStream().listen(_handleEvent); + _eventSubscription = + _eventChannel.receiveBroadcastStream().listen(_handleEvent); } /// 处理来自原生平台的事件 void _handleEvent(dynamic event) { if (event is! Map) return; - + final eventData = Map.from(event); final String eventType = eventData['type'] as String; final dynamic data = eventData['data']; - + switch (eventType) { case EventType.scanResult: if (data is List) { final deviceList = data - .map((item) => BleDevice.fromJson(Map.from(item))) + .map( + (item) => BleDevice.fromJson(Map.from(item))) .toList(); _scanController.add(deviceList); } @@ -175,37 +180,41 @@ class Jieli { /// 发送开始录音命令 Future sendStartRecordingCommand() async { - final result = await _methodChannel.invokeMethod('sendStartRecordingCommand'); + final result = + await _methodChannel.invokeMethod('sendStartRecordingCommand'); return result ?? false; } /// 发送停止录音命令 Future sendStopRecordingCommand() async { - final result = await _methodChannel.invokeMethod('sendStopRecordingCommand'); + final result = + await _methodChannel.invokeMethod('sendStopRecordingCommand'); return result ?? false; } /// 关闭插件,释放资源 Future dispose() async { await stopScan(); - await disconnect(); - _eventSubscription?.cancel(); - _scanController.close(); - _connectionController.close(); - _audioController.close(); - _wakeupController.close(); - _commandResultController.close(); + // await disconnect(); + // _eventSubscription?.cancel(); + // _scanController.close(); + // _connectionController.close(); + // _audioController.close(); + // _wakeupController.close(); + // _commandResultController.close(); } /// 检查蓝牙是否已启用 Future isBluetoothEnabled() async { - final result = await _methodChannel.invokeMethod('isBluetoothEnabled'); + final result = + await _methodChannel.invokeMethod('isBluetoothEnabled'); return result ?? false; } /// 请求打开蓝牙 Future requestBluetoothEnable() async { - final result = await _methodChannel.invokeMethod('requestBluetoothEnable'); + final result = + await _methodChannel.invokeMethod('requestBluetoothEnable'); return result ?? false; } @@ -223,7 +232,7 @@ class Jieli { return false; } } - + /// 增加音量 Future increaseVolume() async { try { @@ -234,7 +243,7 @@ class Jieli { return false; } } - + /// 减小音量 Future decreaseVolume() async { try { @@ -261,12 +270,13 @@ class BluetoothDevice { class CommandResult { final int commandType; final bool success; - + CommandResult({ required this.commandType, required this.success, }); - + @override - String toString() => 'CommandResult{commandType: $commandType, success: $success}'; -} \ No newline at end of file + String toString() => + 'CommandResult{commandType: $commandType, success: $success}'; +}