|
|
@ -23,6 +23,7 @@ import androidx.core.content.ContextCompat |
|
|
import java.util.concurrent.ConcurrentHashMap |
|
|
import java.util.concurrent.ConcurrentHashMap |
|
|
import android.content.pm.ServiceInfo |
|
|
import android.content.pm.ServiceInfo |
|
|
import java.util.concurrent.CopyOnWriteArrayList |
|
|
import java.util.concurrent.CopyOnWriteArrayList |
|
|
|
|
|
import com.yunqiinnovation.jl_opus.JlOpusPlugin |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
* BLE服务类:提供蓝牙低功耗设备的扫描、连接和通信功能 |
|
|
* BLE服务类:提供蓝牙低功耗设备的扫描、连接和通信功能 |
|
|
@ -85,6 +86,8 @@ object BleService { |
|
|
// 回调集合 - 使用线程安全的集合存储所有回调 |
|
|
// 回调集合 - 使用线程安全的集合存储所有回调 |
|
|
private val callbacks = CopyOnWriteArrayList<Callback>() |
|
|
private val callbacks = CopyOnWriteArrayList<Callback>() |
|
|
|
|
|
|
|
|
|
|
|
// 获取插件实例 |
|
|
|
|
|
private val opusPlugin = JlOpusPlugin() |
|
|
|
|
|
|
|
|
// 初始化状态 |
|
|
// 初始化状态 |
|
|
private var isInitialized = false |
|
|
private var isInitialized = false |
|
|
@ -101,7 +104,8 @@ object BleService { |
|
|
this.context = appContext.applicationContext |
|
|
this.context = appContext.applicationContext |
|
|
|
|
|
|
|
|
// 初始化蓝牙管理器和适配器 |
|
|
// 初始化蓝牙管理器和适配器 |
|
|
bluetoothManager = context.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager |
|
|
bluetoothManager = |
|
|
|
|
|
context.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager |
|
|
bluetoothAdapter = bluetoothManager.adapter |
|
|
bluetoothAdapter = bluetoothManager.adapter |
|
|
bluetoothLeScanner = bluetoothAdapter?.bluetoothLeScanner |
|
|
bluetoothLeScanner = bluetoothAdapter?.bluetoothLeScanner |
|
|
|
|
|
|
|
|
@ -242,7 +246,8 @@ object BleService { |
|
|
// 直接上报每个扫描结果 |
|
|
// 直接上报每个扫描结果 |
|
|
try { |
|
|
try { |
|
|
// 将单个扫描结果转换为需要的格式 |
|
|
// 将单个扫描结果转换为需要的格式 |
|
|
val uuids = result.scanRecord?.serviceUuids?.map { it.uuid.toString() } ?: emptyList() |
|
|
val uuids = |
|
|
|
|
|
result.scanRecord?.serviceUuids?.map { it.uuid.toString() } ?: emptyList() |
|
|
val deviceMap = mapOf( |
|
|
val deviceMap = mapOf( |
|
|
"id" to dev.address, |
|
|
"id" to dev.address, |
|
|
"name" to (dev.name ?: "未知"), |
|
|
"name" to (dev.name ?: "未知"), |
|
|
@ -273,10 +278,13 @@ object BleService { |
|
|
private fun printDeviceInfo(result: ScanResult) { |
|
|
private fun printDeviceInfo(result: ScanResult) { |
|
|
val device = result.device |
|
|
val device = result.device |
|
|
val scanRecord = result.scanRecord |
|
|
val scanRecord = result.scanRecord |
|
|
val serviceUuids = scanRecord?.serviceUuids?.joinToString(", ") { it.uuid.toString() } ?: "无" |
|
|
val serviceUuids = |
|
|
|
|
|
scanRecord?.serviceUuids?.joinToString(", ") { it.uuid.toString() } ?: "无" |
|
|
|
|
|
|
|
|
FileLogger.d(TAG, "扫描到设备: 名称=${device.name ?: "未知"}, 地址=${device.address}, " + |
|
|
FileLogger.d( |
|
|
"RSSI=${result.rssi}, 服务UUID=[$serviceUuids]") |
|
|
TAG, "扫描到设备: 名称=${device.name ?: "未知"}, 地址=${device.address}, " + |
|
|
|
|
|
"RSSI=${result.rssi}, 服务UUID=[$serviceUuids]" |
|
|
|
|
|
) |
|
|
|
|
|
|
|
|
// 打印广播数据的详细信息 |
|
|
// 打印广播数据的详细信息 |
|
|
val scanData = scanRecord?.bytes |
|
|
val scanData = scanRecord?.bytes |
|
|
@ -372,10 +380,12 @@ object BleService { |
|
|
updateConnectionState(BleConst.STATE_CONNECTED) |
|
|
updateConnectionState(BleConst.STATE_CONNECTED) |
|
|
g.discoverServices() |
|
|
g.discoverServices() |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
newState == BluetoothProfile.STATE_DISCONNECTED -> { |
|
|
newState == BluetoothProfile.STATE_DISCONNECTED -> { |
|
|
updateConnectionState(BleConst.STATE_DISCONNECTED) |
|
|
updateConnectionState(BleConst.STATE_DISCONNECTED) |
|
|
disconnectGatt() |
|
|
disconnectGatt() |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
else -> { |
|
|
else -> { |
|
|
updateConnectionState(BleConst.STATE_ERROR) |
|
|
updateConnectionState(BleConst.STATE_ERROR) |
|
|
disconnectGatt() |
|
|
disconnectGatt() |
|
|
@ -391,6 +401,7 @@ object BleService { |
|
|
|
|
|
|
|
|
// 获取主服务特征 |
|
|
// 获取主服务特征 |
|
|
val primarySvc = g.getService(BleConst.PRIMARY_SERVICE_UUID) |
|
|
val primarySvc = g.getService(BleConst.PRIMARY_SERVICE_UUID) |
|
|
|
|
|
|
|
|
notifyChar = primarySvc?.getCharacteristic(BleConst.NOTIFY_CHAR_UUID) |
|
|
notifyChar = primarySvc?.getCharacteristic(BleConst.NOTIFY_CHAR_UUID) |
|
|
writeChar = primarySvc?.getCharacteristic(BleConst.WRITE_CHAR_UUID) |
|
|
writeChar = primarySvc?.getCharacteristic(BleConst.WRITE_CHAR_UUID) |
|
|
|
|
|
|
|
|
@ -405,10 +416,7 @@ object BleService { |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
writeChar?.writeType = BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE |
|
|
writeChar?.writeType = BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE |
|
|
|
|
|
//要先设置音频服务的通知,否则接收不到 |
|
|
// 设置主服务的通知 |
|
|
|
|
|
setupNotifications(g, notifyChar) |
|
|
|
|
|
|
|
|
|
|
|
// 设置音频服务的通知(如果存在) |
|
|
// 设置音频服务的通知(如果存在) |
|
|
if (audioChar != null) { |
|
|
if (audioChar != null) { |
|
|
setupNotifications(g, audioChar) |
|
|
setupNotifications(g, audioChar) |
|
|
@ -416,11 +424,20 @@ object BleService { |
|
|
} else { |
|
|
} else { |
|
|
FileLogger.w(TAG, "音频服务特征未找到") |
|
|
FileLogger.w(TAG, "音频服务特征未找到") |
|
|
} |
|
|
} |
|
|
|
|
|
// 设置主服务的通知 |
|
|
|
|
|
setupNotifications(g, notifyChar) |
|
|
|
|
|
//开启解码 |
|
|
|
|
|
opusPlugin?.startOpusDecodeStream(false, 1, 16000, 40, object : |
|
|
|
|
|
JlOpusPlugin.CallbackOpus { |
|
|
|
|
|
override fun onAudioDataReceivedOpus(pcmData: ByteArray) { |
|
|
|
|
|
// 回调给上层 |
|
|
|
|
|
notifyAudioDataReceived(pcmData) |
|
|
|
|
|
} |
|
|
|
|
|
}) |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
override fun onCharacteristicChanged(g: BluetoothGatt, c: BluetoothGattCharacteristic) { |
|
|
override fun onCharacteristicChanged(g: BluetoothGatt, c: BluetoothGattCharacteristic) { |
|
|
FileLogger.d(TAG, "收到特征数据: ${c.uuid}, 数据=${c.value?.joinToString(", ") { "0x${(it.toInt() and 0xFF).toString(16)}" }}") |
|
|
|
|
|
val data = c.value ?: return |
|
|
val data = c.value ?: return |
|
|
|
|
|
|
|
|
// 根据特征UUID区分处理 |
|
|
// 根据特征UUID区分处理 |
|
|
@ -443,13 +460,23 @@ object BleService { |
|
|
} |
|
|
} |
|
|
// 其他未知数据 |
|
|
// 其他未知数据 |
|
|
else -> { |
|
|
else -> { |
|
|
FileLogger.d(TAG, "收到未知数据: ${data.joinToString(", ") { "0x%02X".format(it) }}") |
|
|
FileLogger.d( |
|
|
|
|
|
TAG, |
|
|
|
|
|
"收到未知数据: ${data.joinToString(", ") { "0x%02X".format(it) }}" |
|
|
|
|
|
) |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
// 其他未知特征 |
|
|
// 其他未知特征 |
|
|
else -> { |
|
|
else -> { |
|
|
FileLogger.d(TAG, "收到未知特征数据: ${c.uuid}, 数据=${data.joinToString(", ") { "0x%02X".format(it) }}") |
|
|
FileLogger.d( |
|
|
|
|
|
TAG, |
|
|
|
|
|
"收到未知特征数据: ${c.uuid}, 数据=${ |
|
|
|
|
|
data.joinToString(", ") { |
|
|
|
|
|
"0x%02X".format(it) |
|
|
|
|
|
} |
|
|
|
|
|
}" |
|
|
|
|
|
) |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
@ -457,7 +484,10 @@ object BleService { |
|
|
override fun onCharacteristicWrite( |
|
|
override fun onCharacteristicWrite( |
|
|
g: BluetoothGatt, c: BluetoothGattCharacteristic, status: Int |
|
|
g: BluetoothGatt, c: BluetoothGattCharacteristic, status: Int |
|
|
) { |
|
|
) { |
|
|
FileLogger.i(TAG, "写入 ${if (status == BluetoothGatt.GATT_SUCCESS) "成功" else "失败"}") |
|
|
FileLogger.i( |
|
|
|
|
|
TAG, |
|
|
|
|
|
"写入 ${if (status == BluetoothGatt.GATT_SUCCESS) "成功" else "失败"}" |
|
|
|
|
|
) |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
override fun onDescriptorWrite( |
|
|
override fun onDescriptorWrite( |
|
|
@ -489,11 +519,9 @@ object BleService { |
|
|
*/ |
|
|
*/ |
|
|
private fun processAudioData(data: ByteArray) { |
|
|
private fun processAudioData(data: ByteArray) { |
|
|
try { |
|
|
try { |
|
|
// 记录日志 |
|
|
|
|
|
FileLogger.d(TAG, "收到音频数据: ${data.size}字节") |
|
|
|
|
|
|
|
|
|
|
|
// 回调给上层 |
|
|
opusPlugin?.handleDecodeRequest(data) |
|
|
notifyAudioDataReceived(data) |
|
|
|
|
|
} catch (e: Exception) { |
|
|
} catch (e: Exception) { |
|
|
FileLogger.e(TAG, "处理音频数据异常: ${e.message}", e) |
|
|
FileLogger.e(TAG, "处理音频数据异常: ${e.message}", e) |
|
|
} |
|
|
} |
|
|
@ -503,7 +531,10 @@ object BleService { |
|
|
* 处理设备响应数据 - 帧头为0xBB |
|
|
* 处理设备响应数据 - 帧头为0xBB |
|
|
*/ |
|
|
*/ |
|
|
private fun processDeviceResponse(data: ByteArray) { |
|
|
private fun processDeviceResponse(data: ByteArray) { |
|
|
FileLogger.d(TAG, "收到设备响应: ${data.joinToString(", ") { "0x${(it.toInt() and 0xFF).toString(16)}" }}") |
|
|
FileLogger.d( |
|
|
|
|
|
TAG, |
|
|
|
|
|
"收到设备响应: ${data.joinToString(", ") { "0x${(it.toInt() and 0xFF).toString(16)}" }}" |
|
|
|
|
|
) |
|
|
if (data.size < 3) { |
|
|
if (data.size < 3) { |
|
|
FileLogger.e(TAG, "设备响应数据格式错误:数据长度过短") |
|
|
FileLogger.e(TAG, "设备响应数据格式错误:数据长度过短") |
|
|
return |
|
|
return |
|
|
@ -530,7 +561,7 @@ object BleService { |
|
|
|
|
|
|
|
|
// 检查数据长度 |
|
|
// 检查数据长度 |
|
|
if (data.size != length + 4) { // 帧头 + 命令 + 长度 + 数据 + CRC |
|
|
if (data.size != length + 4) { // 帧头 + 命令 + 长度 + 数据 + CRC |
|
|
FileLogger.e(TAG, "设备响应数据长度不匹配: 期望=${length+4}, 实际=${data.size}") |
|
|
FileLogger.e(TAG, "设备响应数据长度不匹配: 期望=${length + 4}, 实际=${data.size}") |
|
|
return |
|
|
return |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
@ -540,7 +571,10 @@ object BleService { |
|
|
val receivedCrc = data[data.size - 1].toInt() and 0xFF |
|
|
val receivedCrc = data[data.size - 1].toInt() and 0xFF |
|
|
|
|
|
|
|
|
if (calculatedCrc.toInt() and 0xFF != receivedCrc) { |
|
|
if (calculatedCrc.toInt() and 0xFF != receivedCrc) { |
|
|
FileLogger.e(TAG, "设备响应CRC校验失败: 计算=${calculatedCrc.toInt() and 0xFF}, 接收=${receivedCrc}") |
|
|
FileLogger.e( |
|
|
|
|
|
TAG, |
|
|
|
|
|
"设备响应CRC校验失败: 计算=${calculatedCrc.toInt() and 0xFF}, 接收=${receivedCrc}" |
|
|
|
|
|
) |
|
|
return |
|
|
return |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
@ -550,7 +584,16 @@ object BleService { |
|
|
System.arraycopy(data, 3, responseData, 0, length) |
|
|
System.arraycopy(data, 3, responseData, 0, length) |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
FileLogger.d(TAG, "收到设备响应: CMD=0x${cmdType.toString(16)}, 数据=${responseData.joinToString(", ") { "0x${(it.toInt() and 0xFF).toString(16)}" }}") |
|
|
FileLogger.d( |
|
|
|
|
|
TAG, |
|
|
|
|
|
"收到设备响应: CMD=0x${cmdType.toString(16)}, 数据=${ |
|
|
|
|
|
responseData.joinToString(", ") { |
|
|
|
|
|
"0x${ |
|
|
|
|
|
(it.toInt() and 0xFF).toString(16) |
|
|
|
|
|
}" |
|
|
|
|
|
} |
|
|
|
|
|
}" |
|
|
|
|
|
) |
|
|
|
|
|
|
|
|
// 根据命令类型处理不同的响应 |
|
|
// 根据命令类型处理不同的响应 |
|
|
when (cmdType) { |
|
|
when (cmdType) { |
|
|
@ -563,13 +606,15 @@ object BleService { |
|
|
|
|
|
|
|
|
if (responseData.isNotEmpty()) { |
|
|
if (responseData.isNotEmpty()) { |
|
|
// 转换为字符串前,确认是否是有效的ASCII字符 |
|
|
// 转换为字符串前,确认是否是有效的ASCII字符 |
|
|
val version = responseData.joinToString(".") { (it.toInt() and 0xFF).toString() } |
|
|
val version = |
|
|
|
|
|
responseData.joinToString(".") { (it.toInt() and 0xFF).toString() } |
|
|
resultMap["version"] = version |
|
|
resultMap["version"] = version |
|
|
FileLogger.i(TAG, "接收到目标版本号: $version") |
|
|
FileLogger.i(TAG, "接收到目标版本号: $version") |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
notifyDeviceInfoReceived(cmdType, resultMap) |
|
|
notifyDeviceInfoReceived(cmdType, resultMap) |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
BleConst.CMD_GET_PRODUCT_ID -> { |
|
|
BleConst.CMD_GET_PRODUCT_ID -> { |
|
|
val resultMap = mutableMapOf<String, Any>( |
|
|
val resultMap = mutableMapOf<String, Any>( |
|
|
"success" to true, |
|
|
"success" to true, |
|
|
@ -578,13 +623,16 @@ object BleService { |
|
|
) |
|
|
) |
|
|
|
|
|
|
|
|
if (responseData.isNotEmpty() && responseData.size >= 2) { |
|
|
if (responseData.isNotEmpty() && responseData.size >= 2) { |
|
|
val productId = "0x${(responseData[0].toInt() and 0xFF).toString(16)}${(responseData[1].toInt() and 0xFF).toString(16)}" |
|
|
val productId = "0x${(responseData[0].toInt() and 0xFF).toString(16)}${ |
|
|
|
|
|
(responseData[1].toInt() and 0xFF).toString(16) |
|
|
|
|
|
}" |
|
|
resultMap["productId"] = productId |
|
|
resultMap["productId"] = productId |
|
|
FileLogger.i(TAG, "接收到产品ID: $productId") |
|
|
FileLogger.i(TAG, "接收到产品ID: $productId") |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
notifyDeviceInfoReceived(cmdType, resultMap) |
|
|
notifyDeviceInfoReceived(cmdType, resultMap) |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
BleConst.CMD_GET_COLOR_ID -> { |
|
|
BleConst.CMD_GET_COLOR_ID -> { |
|
|
val resultMap = mutableMapOf<String, Any>( |
|
|
val resultMap = mutableMapOf<String, Any>( |
|
|
"success" to true, |
|
|
"success" to true, |
|
|
@ -600,6 +648,7 @@ object BleService { |
|
|
|
|
|
|
|
|
notifyDeviceInfoReceived(cmdType, resultMap) |
|
|
notifyDeviceInfoReceived(cmdType, resultMap) |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
BleConst.CMD_GET_BATTERY_INFO -> { |
|
|
BleConst.CMD_GET_BATTERY_INFO -> { |
|
|
val resultMap = mutableMapOf<String, Any>( |
|
|
val resultMap = mutableMapOf<String, Any>( |
|
|
"success" to true, |
|
|
"success" to true, |
|
|
@ -612,11 +661,15 @@ object BleService { |
|
|
val leftEarBattery = responseData[0].toInt() and 0x7F |
|
|
val leftEarBattery = responseData[0].toInt() and 0x7F |
|
|
val leftEarCharging = (responseData[0].toInt() and 0x80) != 0 |
|
|
val leftEarCharging = (responseData[0].toInt() and 0x80) != 0 |
|
|
|
|
|
|
|
|
val rightEarBattery = if (responseData.size > 1) responseData[1].toInt() and 0x7F else 0 |
|
|
val rightEarBattery = |
|
|
val rightEarCharging = if (responseData.size > 1) (responseData[1].toInt() and 0x80) != 0 else false |
|
|
if (responseData.size > 1) responseData[1].toInt() and 0x7F else 0 |
|
|
|
|
|
val rightEarCharging = |
|
|
|
|
|
if (responseData.size > 1) (responseData[1].toInt() and 0x80) != 0 else false |
|
|
|
|
|
|
|
|
val caseBattery = if (responseData.size > 2) responseData[2].toInt() and 0x7F else 0 |
|
|
val caseBattery = |
|
|
val caseCharging = if (responseData.size > 2) (responseData[2].toInt() and 0x80) != 0 else false |
|
|
if (responseData.size > 2) responseData[2].toInt() and 0x7F else 0 |
|
|
|
|
|
val caseCharging = |
|
|
|
|
|
if (responseData.size > 2) (responseData[2].toInt() and 0x80) != 0 else false |
|
|
|
|
|
|
|
|
// 添加到结果Map |
|
|
// 添加到结果Map |
|
|
resultMap["leftEarBattery"] = leftEarBattery |
|
|
resultMap["leftEarBattery"] = leftEarBattery |
|
|
@ -627,14 +680,17 @@ object BleService { |
|
|
resultMap["caseCharging"] = caseCharging |
|
|
resultMap["caseCharging"] = caseCharging |
|
|
resultMap["batteryLevel"] = leftEarBattery // 整体电量用左耳表示 |
|
|
resultMap["batteryLevel"] = leftEarBattery // 整体电量用左耳表示 |
|
|
|
|
|
|
|
|
FileLogger.i(TAG, "接收到电量信息: 左耳=${leftEarBattery}% " + |
|
|
FileLogger.i( |
|
|
"(${if(leftEarCharging) "充电中" else "未充电"}), 右耳=${rightEarBattery}% " + |
|
|
TAG, "接收到电量信息: 左耳=${leftEarBattery}% " + |
|
|
"(${if(rightEarCharging) "充电中" else "未充电"}), 充电仓=${caseBattery}% " + |
|
|
"(${if (leftEarCharging) "充电中" else "未充电"}), 右耳=${rightEarBattery}% " + |
|
|
"(${if(caseCharging) "充电中" else "未充电"})") |
|
|
"(${if (rightEarCharging) "充电中" else "未充电"}), 充电仓=${caseBattery}% " + |
|
|
|
|
|
"(${if (caseCharging) "充电中" else "未充电"})" |
|
|
|
|
|
) |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
notifyDeviceInfoReceived(cmdType, resultMap) |
|
|
notifyDeviceInfoReceived(cmdType, resultMap) |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
BleConst.CMD_CONTROL_CODEC -> { |
|
|
BleConst.CMD_CONTROL_CODEC -> { |
|
|
val resultMap = mutableMapOf<String, Any>( |
|
|
val resultMap = mutableMapOf<String, Any>( |
|
|
"success" to true, |
|
|
"success" to true, |
|
|
@ -673,6 +729,7 @@ object BleService { |
|
|
|
|
|
|
|
|
notifyDeviceInfoReceived(cmdType, resultMap) |
|
|
notifyDeviceInfoReceived(cmdType, resultMap) |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
else -> { |
|
|
else -> { |
|
|
// 对于未知命令,返回原始数据 |
|
|
// 对于未知命令,返回原始数据 |
|
|
val resultMap = mapOf( |
|
|
val resultMap = mapOf( |
|
|
@ -691,7 +748,10 @@ object BleService { |
|
|
* 处理设备主动上报数据 - 帧头为0xCC |
|
|
* 处理设备主动上报数据 - 帧头为0xCC |
|
|
*/ |
|
|
*/ |
|
|
private fun processDeviceNotification(data: ByteArray) { |
|
|
private fun processDeviceNotification(data: ByteArray) { |
|
|
FileLogger.d(TAG, "收到设备主动上报: ${data.joinToString(", ") { "0x${(it.toInt() and 0xFF).toString(16)}" }}") |
|
|
FileLogger.d( |
|
|
|
|
|
TAG, |
|
|
|
|
|
"收到设备主动上报: ${data.joinToString(", ") { "0x${(it.toInt() and 0xFF).toString(16)}" }}" |
|
|
|
|
|
) |
|
|
if (data.size < 3) { |
|
|
if (data.size < 3) { |
|
|
FileLogger.e(TAG, "设备主动上报数据格式错误:数据长度过短") |
|
|
FileLogger.e(TAG, "设备主动上报数据格式错误:数据长度过短") |
|
|
return |
|
|
return |
|
|
@ -703,7 +763,7 @@ object BleService { |
|
|
|
|
|
|
|
|
// 检查数据长度 |
|
|
// 检查数据长度 |
|
|
if (data.size != length + 4) { // 帧头 + 类型 + 长度 + 数据 + CRC |
|
|
if (data.size != length + 4) { // 帧头 + 类型 + 长度 + 数据 + CRC |
|
|
FileLogger.e(TAG, "设备主动上报数据长度不匹配: 期望=${length+4}, 实际=${data.size}") |
|
|
FileLogger.e(TAG, "设备主动上报数据长度不匹配: 期望=${length + 4}, 实际=${data.size}") |
|
|
return |
|
|
return |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
@ -713,7 +773,10 @@ object BleService { |
|
|
val receivedCrc = data[data.size - 1].toInt() and 0xFF |
|
|
val receivedCrc = data[data.size - 1].toInt() and 0xFF |
|
|
|
|
|
|
|
|
if (calculatedCrc.toInt() and 0xFF != receivedCrc) { |
|
|
if (calculatedCrc.toInt() and 0xFF != receivedCrc) { |
|
|
FileLogger.e(TAG, "设备主动上报CRC校验失败: 计算=${calculatedCrc.toInt() and 0xFF}, 接收=${receivedCrc}") |
|
|
FileLogger.e( |
|
|
|
|
|
TAG, |
|
|
|
|
|
"设备主动上报CRC校验失败: 计算=${calculatedCrc.toInt() and 0xFF}, 接收=${receivedCrc}" |
|
|
|
|
|
) |
|
|
return |
|
|
return |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
@ -723,7 +786,16 @@ object BleService { |
|
|
System.arraycopy(data, 3, notifyData, 0, length) |
|
|
System.arraycopy(data, 3, notifyData, 0, length) |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
FileLogger.d(TAG, "收到设备主动上报: 类型=0x${infoType.toString(16)}, 数据=${notifyData.joinToString(", ") { "0x${(it.toInt() and 0xFF).toString(16)}" }}") |
|
|
FileLogger.d( |
|
|
|
|
|
TAG, |
|
|
|
|
|
"收到设备主动上报: 类型=0x${infoType.toString(16)}, 数据=${ |
|
|
|
|
|
notifyData.joinToString(", ") { |
|
|
|
|
|
"0x${ |
|
|
|
|
|
(it.toInt() and 0xFF).toString(16) |
|
|
|
|
|
}" |
|
|
|
|
|
} |
|
|
|
|
|
}" |
|
|
|
|
|
) |
|
|
|
|
|
|
|
|
// 根据上报类型处理 |
|
|
// 根据上报类型处理 |
|
|
when (infoType) { |
|
|
when (infoType) { |
|
|
@ -740,11 +812,14 @@ object BleService { |
|
|
val leftEarBattery = notifyData[0].toInt() and 0x7F |
|
|
val leftEarBattery = notifyData[0].toInt() and 0x7F |
|
|
val leftEarCharging = (notifyData[0].toInt() and 0x80) != 0 |
|
|
val leftEarCharging = (notifyData[0].toInt() and 0x80) != 0 |
|
|
|
|
|
|
|
|
val rightEarBattery = if (notifyData.size > 1) notifyData[1].toInt() and 0x7F else 0 |
|
|
val rightEarBattery = |
|
|
val rightEarCharging = if (notifyData.size > 1) (notifyData[1].toInt() and 0x80) != 0 else false |
|
|
if (notifyData.size > 1) notifyData[1].toInt() and 0x7F else 0 |
|
|
|
|
|
val rightEarCharging = |
|
|
|
|
|
if (notifyData.size > 1) (notifyData[1].toInt() and 0x80) != 0 else false |
|
|
|
|
|
|
|
|
val caseBattery = if (notifyData.size > 2) notifyData[2].toInt() and 0x7F else 0 |
|
|
val caseBattery = if (notifyData.size > 2) notifyData[2].toInt() and 0x7F else 0 |
|
|
val caseCharging = if (notifyData.size > 2) (notifyData[2].toInt() and 0x80) != 0 else false |
|
|
val caseCharging = |
|
|
|
|
|
if (notifyData.size > 2) (notifyData[2].toInt() and 0x80) != 0 else false |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 添加到结果Map |
|
|
// 添加到结果Map |
|
|
@ -756,14 +831,17 @@ object BleService { |
|
|
resultMap["caseCharging"] = caseCharging |
|
|
resultMap["caseCharging"] = caseCharging |
|
|
resultMap["batteryLevel"] = leftEarBattery // 整体电量用左耳表示 |
|
|
resultMap["batteryLevel"] = leftEarBattery // 整体电量用左耳表示 |
|
|
|
|
|
|
|
|
FileLogger.i(TAG, "收到主动上报电量信息: 左耳=${leftEarBattery}% " + |
|
|
FileLogger.i( |
|
|
"(${if(leftEarCharging) "充电中" else "未充电"}), 右耳=${rightEarBattery}% " + |
|
|
TAG, "收到主动上报电量信息: 左耳=${leftEarBattery}% " + |
|
|
"(${if(rightEarCharging) "充电中" else "未充电"}), 充电仓=${caseBattery}% " + |
|
|
"(${if (leftEarCharging) "充电中" else "未充电"}), 右耳=${rightEarBattery}% " + |
|
|
"(${if(caseCharging) "充电中" else "未充电"})") |
|
|
"(${if (rightEarCharging) "充电中" else "未充电"}), 充电仓=${caseBattery}% " + |
|
|
|
|
|
"(${if (caseCharging) "充电中" else "未充电"})" |
|
|
|
|
|
) |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
notifyDeviceInfoReceived(infoType, resultMap) |
|
|
notifyDeviceInfoReceived(infoType, resultMap) |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
BleConst.CMD_WAKE_UP -> { |
|
|
BleConst.CMD_WAKE_UP -> { |
|
|
FileLogger.i(TAG, "收到唤醒指令") |
|
|
FileLogger.i(TAG, "收到唤醒指令") |
|
|
// 接收到唤醒信号,打开编码器 |
|
|
// 接收到唤醒信号,打开编码器 |
|
|
@ -827,7 +905,10 @@ object BleService { |
|
|
*/ |
|
|
*/ |
|
|
fun closeCodec(): Boolean { |
|
|
fun closeCodec(): Boolean { |
|
|
FileLogger.i(TAG, "关闭编解码...") |
|
|
FileLogger.i(TAG, "关闭编解码...") |
|
|
return sendCommand(BleConst.CMD_CONTROL_CODEC.toByte(), byteArrayOf(BleConst.CODEC_CONTROL_CLOSE.toByte())) |
|
|
return sendCommand( |
|
|
|
|
|
BleConst.CMD_CONTROL_CODEC.toByte(), |
|
|
|
|
|
byteArrayOf(BleConst.CODEC_CONTROL_CLOSE.toByte()) |
|
|
|
|
|
) |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
@ -835,7 +916,10 @@ object BleService { |
|
|
*/ |
|
|
*/ |
|
|
fun openDecoder(): Boolean { |
|
|
fun openDecoder(): Boolean { |
|
|
FileLogger.i(TAG, "打开解码...") |
|
|
FileLogger.i(TAG, "打开解码...") |
|
|
return sendCommand(BleConst.CMD_CONTROL_CODEC.toByte(), byteArrayOf(BleConst.CODEC_CONTROL_DECODE_ON.toByte())) |
|
|
return sendCommand( |
|
|
|
|
|
BleConst.CMD_CONTROL_CODEC.toByte(), |
|
|
|
|
|
byteArrayOf(BleConst.CODEC_CONTROL_DECODE_ON.toByte()) |
|
|
|
|
|
) |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
@ -851,7 +935,13 @@ object BleService { |
|
|
*/ |
|
|
*/ |
|
|
fun openEncoder(): Boolean { |
|
|
fun openEncoder(): Boolean { |
|
|
FileLogger.i(TAG, "打开编码...") |
|
|
FileLogger.i(TAG, "打开编码...") |
|
|
return sendCommand(BleConst.CMD_CONTROL_CODEC.toByte(), byteArrayOf(BleConst.CODEC_CONTROL_ENCODE_ON.toByte())) |
|
|
return sendCommand( |
|
|
|
|
|
BleConst.CMD_CONTROL_CODEC.toByte(), |
|
|
|
|
|
byteArrayOf( |
|
|
|
|
|
BleConst.CODEC_CONTROL_ENCODE_ON.toByte(), |
|
|
|
|
|
BleConst.AUDIO_CHANNEL_RIGHT.toByte() |
|
|
|
|
|
) |
|
|
|
|
|
) |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
@ -870,10 +960,12 @@ object BleService { |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// 高级控制:设置命令类型、长度和两个参数 |
|
|
// 高级控制:设置命令类型、长度和两个参数 |
|
|
return sendCommand(BleConst.CMD_CONTROL_CODEC.toByte(), byteArrayOf( |
|
|
return sendCommand( |
|
|
0xA1.toByte(), // 状态码 - 使用解码状态 |
|
|
BleConst.CMD_CONTROL_CODEC.toByte(), byteArrayOf( |
|
|
channelMode.toByte() // 声道模式 |
|
|
0xA1.toByte(), // 状态码 - 使用解码状态 |
|
|
)) |
|
|
channelMode.toByte() // 声道模式 |
|
|
|
|
|
) |
|
|
|
|
|
) |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
@ -1046,9 +1138,12 @@ object BleService { |
|
|
cmdPacket[3 + cmdLength] = crc |
|
|
cmdPacket[3 + cmdLength] = crc |
|
|
|
|
|
|
|
|
// 记录命令详情 |
|
|
// 记录命令详情 |
|
|
FileLogger.d(TAG, "发送命令: CMD=0x${(cmdType.toInt() and 0xFF).toString(16)}, 长度=$cmdLength, " + |
|
|
FileLogger.d( |
|
|
"数据=${cmdData.joinToString(", ") { "0x${(it.toInt() and 0xFF).toString(16)}" }}, " + |
|
|
TAG, |
|
|
"CRC=0x${(crc.toInt() and 0xFF).toString(16)}") |
|
|
"发送命令: CMD=0x${(cmdType.toInt() and 0xFF).toString(16)}, 长度=$cmdLength, " + |
|
|
|
|
|
"数据=${cmdData.joinToString(", ") { "0x${(it.toInt() and 0xFF).toString(16)}" }}, " + |
|
|
|
|
|
"CRC=0x${(crc.toInt() and 0xFF).toString(16)}" |
|
|
|
|
|
) |
|
|
|
|
|
|
|
|
// 发送数据 |
|
|
// 发送数据 |
|
|
writeChar!!.value = cmdPacket |
|
|
writeChar!!.value = cmdPacket |
|
|
|