|
|
|
@ -34,6 +34,7 @@ class BleCompanionManager(private val context: Context) { |
|
|
|
private const val PREF_NAME = "ble_companion_prefs" |
|
|
|
private const val KEY_ASSOCIATION_IDS = "association_ids" |
|
|
|
private const val KEY_PAIRED_MAC = "paired_mac" |
|
|
|
private const val KEY_PAIRED_UUID = "paired_uuid" |
|
|
|
} |
|
|
|
|
|
|
|
// 会话保存 |
|
|
|
@ -43,7 +44,21 @@ class BleCompanionManager(private val context: Context) { |
|
|
|
|
|
|
|
// 用于验证的临时MAC地址 |
|
|
|
private var pendingMacAddress: String? = null |
|
|
|
|
|
|
|
// 创建PendingIntent |
|
|
|
// 创建PendingIntent |
|
|
|
val scanIntent = Intent(BleConst.ACTION_SCAN_RESULT) |
|
|
|
.setClass(context, BleWakeReceiver::class.java) |
|
|
|
|
|
|
|
val pendingIntent = PendingIntent.getBroadcast( |
|
|
|
context, |
|
|
|
0, |
|
|
|
scanIntent, |
|
|
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { |
|
|
|
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE |
|
|
|
} else { |
|
|
|
PendingIntent.FLAG_UPDATE_CURRENT |
|
|
|
} |
|
|
|
) |
|
|
|
/** |
|
|
|
* 获取CompanionDeviceManager服务(使用Activity上下文) |
|
|
|
*/ |
|
|
|
@ -63,313 +78,344 @@ class BleCompanionManager(private val context: Context) { |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* 获取已关联的设备MAC地址 |
|
|
|
* 获取已连接的设备MAC地址 |
|
|
|
*/ |
|
|
|
fun getPairedMacAddress(): String? { |
|
|
|
return sharedPreferences.getString(KEY_PAIRED_MAC, null) |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* 保存已关联的设备MAC地址 |
|
|
|
*/ |
|
|
|
fun savePairedMacAddress(macAddress: String) { |
|
|
|
sharedPreferences.edit().putString(KEY_PAIRED_MAC, macAddress).apply() |
|
|
|
Log.i(TAG, "已保存配对设备MAC: $macAddress") |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* 清除已关联的设备MAC地址 |
|
|
|
/** |
|
|
|
* 保存连接的设备UUID |
|
|
|
*/ |
|
|
|
fun clearPairedMacAddress() { |
|
|
|
sharedPreferences.edit().remove(KEY_PAIRED_MAC).apply() |
|
|
|
Log.i(TAG, "已清除配对设备MAC") |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* 检查是否有已关联的设备 |
|
|
|
*/ |
|
|
|
@RequiresApi(Build.VERSION_CODES.O) |
|
|
|
fun hasAssociations(): Boolean { |
|
|
|
val associationIds = getAssociationIds() |
|
|
|
return associationIds.isNotEmpty() |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* 获取所有关联ID |
|
|
|
* 保存连接的设备UUID列表 |
|
|
|
* 将UUID列表转换为JSON字符串进行存储 |
|
|
|
*/ |
|
|
|
@RequiresApi(Build.VERSION_CODES.O) |
|
|
|
fun getAssociationIds(): List<String> { |
|
|
|
// 使用默认上下文获取CompanionDeviceManager |
|
|
|
val companionDeviceManager = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { |
|
|
|
context.getSystemService(Context.COMPANION_DEVICE_SERVICE) as? CompanionDeviceManager |
|
|
|
} else { |
|
|
|
null |
|
|
|
fun savePairedUuid(uuids: List<String>) { |
|
|
|
if (uuids.isNotEmpty()) { |
|
|
|
// 将列表转换为JSON字符串格式进行存储 |
|
|
|
val uuidsJson = uuids.joinToString(",") { "\"$it\"" } |
|
|
|
val jsonString = "[$uuidsJson]" |
|
|
|
sharedPreferences.edit().putString(KEY_PAIRED_UUID, jsonString).apply() |
|
|
|
Log.i(TAG, "已保存连接设备UUID列表: $uuids") |
|
|
|
} |
|
|
|
return getAssociationIds(companionDeviceManager) |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
* 获取所有关联ID(使用指定的CompanionDeviceManager) |
|
|
|
* 获取已连接的设备UUID列表 |
|
|
|
* 从JSON字符串解析为UUID列表 |
|
|
|
*/ |
|
|
|
@RequiresApi(Build.VERSION_CODES.O) |
|
|
|
private fun getAssociationIds(companionDeviceManager: CompanionDeviceManager?): List<String> { |
|
|
|
if (companionDeviceManager == null) { |
|
|
|
return emptyList() |
|
|
|
} |
|
|
|
|
|
|
|
// 注意: allAssociations 仅在API 33 (Android 13)及以上版本可用 |
|
|
|
// 在早期版本中,使用associations (返回的是String数组) |
|
|
|
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { |
|
|
|
fun getPairedUuid(): List<String> { |
|
|
|
val jsonString = sharedPreferences.getString(KEY_PAIRED_UUID, null) |
|
|
|
return if (!jsonString.isNullOrEmpty()) { |
|
|
|
try { |
|
|
|
// 由于AssociationInfo类在API 30+引入,使用反射获取更安全 |
|
|
|
val method = CompanionDeviceManager::class.java.getMethod("getAllAssociations") |
|
|
|
@Suppress("UNCHECKED_CAST") |
|
|
|
val associations = method.invoke(companionDeviceManager) as? List<Any> |
|
|
|
associations?.map { association -> |
|
|
|
// 尝试获取AssociationInfo.id |
|
|
|
val idField = association.javaClass.getField("id") |
|
|
|
idField.get(association).toString() |
|
|
|
} ?: emptyList() |
|
|
|
// 简单的JSON解析,移除方括号和引号,然后分割 |
|
|
|
jsonString.trim('[', ']') |
|
|
|
.split(",") |
|
|
|
.map { it.trim().trim('"') } |
|
|
|
.filter { it.isNotEmpty() } |
|
|
|
} catch (e: Exception) { |
|
|
|
Log.e(TAG, "获取关联ID失败: ${e.message}", e) |
|
|
|
Log.e(TAG, "解析UUID列表失败: ${e.message}") |
|
|
|
emptyList() |
|
|
|
} |
|
|
|
} else { |
|
|
|
// API 26-32 使用 associations 方法 |
|
|
|
companionDeviceManager.associations?.toList() ?: emptyList() |
|
|
|
emptyList() |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
* 保存连接的设备MAC地址 |
|
|
|
*/ |
|
|
|
fun savePairedMacAddress(macAddress: String) { |
|
|
|
sharedPreferences.edit().putString(KEY_PAIRED_MAC, macAddress).apply() |
|
|
|
Log.i(TAG, "已保存连接设备MAC: $macAddress") |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* 开始设备关联流程 |
|
|
|
* |
|
|
|
* @param activity 当前Activity,用于启动系统关联对话框 |
|
|
|
* @param deviceNamePattern 设备名称正则表达式,例如"MyHeadset.*" |
|
|
|
* @param serviceUuid 可选的服务UUID |
|
|
|
* @param macAddress 可选的MAC地址 |
|
|
|
* 清除已连接的设备MAC地址 |
|
|
|
*/ |
|
|
|
@RequiresApi(Build.VERSION_CODES.O) |
|
|
|
fun startAssociation( |
|
|
|
activity: Activity, |
|
|
|
deviceNamePattern: String? = null, |
|
|
|
serviceUuid: String? = null, |
|
|
|
macAddress: String? = null |
|
|
|
): Boolean { |
|
|
|
// 使用Activity上下文获取CompanionDeviceManager |
|
|
|
val companionDeviceManager = getCompanionDeviceManager(activity) |
|
|
|
if (companionDeviceManager == null) { |
|
|
|
Log.e(TAG, "设备不支持CompanionDeviceManager") |
|
|
|
return false |
|
|
|
} |
|
|
|
fun clearPairedMacAddress() { |
|
|
|
sharedPreferences.edit().remove(KEY_PAIRED_MAC).apply() |
|
|
|
Log.i(TAG, "已清除连接设备MAC") |
|
|
|
} |
|
|
|
|
|
|
|
// /** |
|
|
|
// * 检查是否有已关联的设备 |
|
|
|
// */ |
|
|
|
// @RequiresApi(Build.VERSION_CODES.O) |
|
|
|
// fun hasAssociations(): Boolean { |
|
|
|
// val associationIds = getAssociationIds() |
|
|
|
// return associationIds.isNotEmpty() |
|
|
|
// } |
|
|
|
|
|
|
|
// /** |
|
|
|
// * 获取所有关联ID |
|
|
|
// */ |
|
|
|
// @RequiresApi(Build.VERSION_CODES.O) |
|
|
|
// fun getAssociationIds(): List<String> { |
|
|
|
// // 使用默认上下文获取CompanionDeviceManager |
|
|
|
// val companionDeviceManager = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { |
|
|
|
// context.getSystemService(Context.COMPANION_DEVICE_SERVICE) as? CompanionDeviceManager |
|
|
|
// } else { |
|
|
|
// null |
|
|
|
// } |
|
|
|
// return getAssociationIds(companionDeviceManager) |
|
|
|
// } |
|
|
|
|
|
|
|
// /** |
|
|
|
// * 获取所有关联ID(使用指定的CompanionDeviceManager) |
|
|
|
// */ |
|
|
|
// @RequiresApi(Build.VERSION_CODES.O) |
|
|
|
// private fun getAssociationIds(companionDeviceManager: CompanionDeviceManager?): List<String> { |
|
|
|
// if (companionDeviceManager == null) { |
|
|
|
// return emptyList() |
|
|
|
// } |
|
|
|
|
|
|
|
try { |
|
|
|
// 构建设备过滤器 |
|
|
|
val deviceFilterBuilder = BluetoothDeviceFilter.Builder() |
|
|
|
// // 注意: allAssociations 仅在API 33 (Android 13)及以上版本可用 |
|
|
|
// // 在早期版本中,使用associations (返回的是String数组) |
|
|
|
// return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { |
|
|
|
// try { |
|
|
|
// // 由于AssociationInfo类在API 30+引入,使用反射获取更安全 |
|
|
|
// val method = CompanionDeviceManager::class.java.getMethod("getAllAssociations") |
|
|
|
// @Suppress("UNCHECKED_CAST") |
|
|
|
// val associations = method.invoke(companionDeviceManager) as? List<Any> |
|
|
|
// associations?.map { association -> |
|
|
|
// // 尝试获取AssociationInfo.id |
|
|
|
// val idField = association.javaClass.getField("id") |
|
|
|
// idField.get(association).toString() |
|
|
|
// } ?: emptyList() |
|
|
|
// } catch (e: Exception) { |
|
|
|
// Log.e(TAG, "获取关联ID失败: ${e.message}", e) |
|
|
|
// emptyList() |
|
|
|
// } |
|
|
|
// } else { |
|
|
|
// // API 26-32 使用 associations 方法 |
|
|
|
// companionDeviceManager.associations?.toList() ?: emptyList() |
|
|
|
// } |
|
|
|
// } |
|
|
|
|
|
|
|
// /** |
|
|
|
// * 开始设备关联流程 |
|
|
|
// * |
|
|
|
// * @param activity 当前Activity,用于启动系统关联对话框 |
|
|
|
// * @param deviceNamePattern 设备名称正则表达式,例如"MyHeadset.*" |
|
|
|
// * @param serviceUuid 可选的服务UUID |
|
|
|
// * @param macAddress 可选的MAC地址 |
|
|
|
// */ |
|
|
|
// @RequiresApi(Build.VERSION_CODES.O) |
|
|
|
// fun startAssociation( |
|
|
|
// activity: Activity, |
|
|
|
// deviceNamePattern: String? = null, |
|
|
|
// serviceUuid: String? = null, |
|
|
|
// macAddress: String? = null |
|
|
|
// ): Boolean { |
|
|
|
// // 使用Activity上下文获取CompanionDeviceManager |
|
|
|
// val companionDeviceManager = getCompanionDeviceManager(activity) |
|
|
|
// if (companionDeviceManager == null) { |
|
|
|
// Log.e(TAG, "设备不支持CompanionDeviceManager") |
|
|
|
// return false |
|
|
|
// } |
|
|
|
|
|
|
|
// try { |
|
|
|
// // 构建设备过滤器 |
|
|
|
// val deviceFilterBuilder = BluetoothDeviceFilter.Builder() |
|
|
|
|
|
|
|
// 添加设备名称 - 严格匹配模式 |
|
|
|
deviceNamePattern?.let { |
|
|
|
// 使用"^设备名称$"格式创建精确匹配的正则表达式 |
|
|
|
val exactNamePattern = Pattern.compile("^$it$") |
|
|
|
deviceFilterBuilder.setNamePattern(exactNamePattern) |
|
|
|
Log.i(TAG, "设置严格名称匹配: $it") |
|
|
|
} |
|
|
|
// // 添加设备名称 - 严格匹配模式 |
|
|
|
// deviceNamePattern?.let { |
|
|
|
// // 使用"^设备名称$"格式创建精确匹配的正则表达式 |
|
|
|
// val exactNamePattern = Pattern.compile("^$it$") |
|
|
|
// deviceFilterBuilder.setNamePattern(exactNamePattern) |
|
|
|
// Log.i(TAG, "设置严格名称匹配: $it") |
|
|
|
// } |
|
|
|
|
|
|
|
// 添加服务UUID |
|
|
|
serviceUuid?.let { |
|
|
|
try { |
|
|
|
val uuid = ParcelUuid(java.util.UUID.fromString(it)) |
|
|
|
deviceFilterBuilder.addServiceUuid(uuid, null) |
|
|
|
Log.i(TAG, "设置服务UUID匹配: $it") |
|
|
|
} catch (e: Exception) { |
|
|
|
Log.e(TAG, "设置服务UUID失败: ${e.message}", e) |
|
|
|
} |
|
|
|
} |
|
|
|
// // 添加服务UUID |
|
|
|
// serviceUuid?.let { |
|
|
|
// try { |
|
|
|
// val uuid = ParcelUuid(java.util.UUID.fromString(it)) |
|
|
|
// deviceFilterBuilder.addServiceUuid(uuid, null) |
|
|
|
// Log.i(TAG, "设置服务UUID匹配: $it") |
|
|
|
// } catch (e: Exception) { |
|
|
|
// Log.e(TAG, "设置服务UUID失败: ${e.message}", e) |
|
|
|
// } |
|
|
|
// } |
|
|
|
|
|
|
|
// 添加MAC地址 - 严格匹配 |
|
|
|
macAddress?.let { |
|
|
|
try { |
|
|
|
// 检查是否是有效的MAC地址格式 |
|
|
|
if (!isValidMacAddress(it)) { |
|
|
|
Log.e(TAG, "无效的MAC地址格式: $it") |
|
|
|
return false |
|
|
|
} |
|
|
|
// // 添加MAC地址 - 严格匹配 |
|
|
|
// macAddress?.let { |
|
|
|
// try { |
|
|
|
// // 检查是否是有效的MAC地址格式 |
|
|
|
// if (!isValidMacAddress(it)) { |
|
|
|
// Log.e(TAG, "无效的MAC地址格式: $it") |
|
|
|
// return false |
|
|
|
// } |
|
|
|
|
|
|
|
// 存储MAC地址到临时变量,成功匹配后再保存 |
|
|
|
pendingMacAddress = it |
|
|
|
// // 存储MAC地址到临时变量,成功匹配后再保存 |
|
|
|
// pendingMacAddress = it |
|
|
|
|
|
|
|
// 设置MAC地址严格匹配 |
|
|
|
deviceFilterBuilder.setAddress(it) |
|
|
|
Log.i(TAG, "设置MAC地址严格匹配: $it") |
|
|
|
} catch (e: Exception) { |
|
|
|
Log.e(TAG, "设置MAC地址失败: ${e.message}", e) |
|
|
|
return false |
|
|
|
} |
|
|
|
} ?: run { |
|
|
|
// 如果没有提供MAC地址也没有提供设备名称,则返回错误 |
|
|
|
if (deviceNamePattern == null && serviceUuid == null) { |
|
|
|
Log.e(TAG, "严格匹配模式下必须提供MAC地址、设备名称或服务UUID之一") |
|
|
|
return false |
|
|
|
} |
|
|
|
} |
|
|
|
// // 设置MAC地址严格匹配 |
|
|
|
// deviceFilterBuilder.setAddress(it) |
|
|
|
// Log.i(TAG, "设置MAC地址严格匹配: $it") |
|
|
|
// } catch (e: Exception) { |
|
|
|
// Log.e(TAG, "设置MAC地址失败: ${e.message}", e) |
|
|
|
// return false |
|
|
|
// } |
|
|
|
// } ?: run { |
|
|
|
// // 如果没有提供MAC地址也没有提供设备名称,则返回错误 |
|
|
|
// if (deviceNamePattern == null && serviceUuid == null) { |
|
|
|
// Log.e(TAG, "严格匹配模式下必须提供MAC地址、设备名称或服务UUID之一") |
|
|
|
// return false |
|
|
|
// } |
|
|
|
// } |
|
|
|
|
|
|
|
// 创建请求 |
|
|
|
val associationRequest = AssociationRequest.Builder() |
|
|
|
.addDeviceFilter(deviceFilterBuilder.build()) |
|
|
|
.setSingleDevice(true) // 找到一个即停止 |
|
|
|
.build() |
|
|
|
// // 创建请求 |
|
|
|
// val associationRequest = AssociationRequest.Builder() |
|
|
|
// .addDeviceFilter(deviceFilterBuilder.build()) |
|
|
|
// .setSingleDevice(true) // 找到一个即停止 |
|
|
|
// .build() |
|
|
|
|
|
|
|
// 发起关联请求 |
|
|
|
companionDeviceManager.associate( |
|
|
|
associationRequest, |
|
|
|
object : CompanionDeviceManager.Callback() { |
|
|
|
override fun onDeviceFound(intentSender: IntentSender) { |
|
|
|
try { |
|
|
|
activity.startIntentSenderForResult( |
|
|
|
intentSender, |
|
|
|
REQUEST_CODE_ASSOCIATE, |
|
|
|
null, |
|
|
|
0, |
|
|
|
0, |
|
|
|
0 |
|
|
|
) |
|
|
|
Log.i(TAG, "已启动设备选择对话框") |
|
|
|
} catch (e: Exception) { |
|
|
|
Log.e(TAG, "启动设备选择对话框失败: ${e.message}", e) |
|
|
|
} |
|
|
|
} |
|
|
|
// // 发起关联请求 |
|
|
|
// companionDeviceManager.associate( |
|
|
|
// associationRequest, |
|
|
|
// object : CompanionDeviceManager.Callback() { |
|
|
|
// override fun onDeviceFound(intentSender: IntentSender) { |
|
|
|
// try { |
|
|
|
// activity.startIntentSenderForResult( |
|
|
|
// intentSender, |
|
|
|
// REQUEST_CODE_ASSOCIATE, |
|
|
|
// null, |
|
|
|
// 0, |
|
|
|
// 0, |
|
|
|
// 0 |
|
|
|
// ) |
|
|
|
// Log.i(TAG, "已启动设备选择对话框") |
|
|
|
// } catch (e: Exception) { |
|
|
|
// Log.e(TAG, "启动设备选择对话框失败: ${e.message}", e) |
|
|
|
// } |
|
|
|
// } |
|
|
|
|
|
|
|
// API 26 (Android 8) 使用 onDeviceFound |
|
|
|
// API 28+ (Android 9+) 使用 onAssociationPending |
|
|
|
@RequiresApi(Build.VERSION_CODES.P) |
|
|
|
override fun onAssociationPending(intentSender: IntentSender) { |
|
|
|
try { |
|
|
|
activity.startIntentSenderForResult( |
|
|
|
intentSender, |
|
|
|
REQUEST_CODE_ASSOCIATE, |
|
|
|
null, |
|
|
|
0, |
|
|
|
0, |
|
|
|
0 |
|
|
|
) |
|
|
|
Log.i(TAG, "已启动设备选择对话框") |
|
|
|
} catch (e: Exception) { |
|
|
|
Log.e(TAG, "启动设备选择对话框失败: ${e.message}", e) |
|
|
|
} |
|
|
|
} |
|
|
|
// // API 26 (Android 8) 使用 onDeviceFound |
|
|
|
// // API 28+ (Android 9+) 使用 onAssociationPending |
|
|
|
// @RequiresApi(Build.VERSION_CODES.P) |
|
|
|
// override fun onAssociationPending(intentSender: IntentSender) { |
|
|
|
// try { |
|
|
|
// activity.startIntentSenderForResult( |
|
|
|
// intentSender, |
|
|
|
// REQUEST_CODE_ASSOCIATE, |
|
|
|
// null, |
|
|
|
// 0, |
|
|
|
// 0, |
|
|
|
// 0 |
|
|
|
// ) |
|
|
|
// Log.i(TAG, "已启动设备选择对话框") |
|
|
|
// } catch (e: Exception) { |
|
|
|
// Log.e(TAG, "启动设备选择对话框失败: ${e.message}", e) |
|
|
|
// } |
|
|
|
// } |
|
|
|
|
|
|
|
// API 33+ (Android 13+) 使用 onAssociationCreated |
|
|
|
@RequiresApi(Build.VERSION_CODES.TIRAMISU) |
|
|
|
override fun onAssociationCreated(associationInfo: AssociationInfo) { |
|
|
|
Log.i(TAG, "设备关联已创建: ${associationInfo.displayName}, ID: ${associationInfo.id}") |
|
|
|
// 保存关联ID |
|
|
|
try { |
|
|
|
val macAddressMethod = associationInfo.javaClass.getMethod("getDeviceMacAddress") |
|
|
|
val mac = macAddressMethod.invoke(associationInfo) as? String |
|
|
|
mac?.let { savePairedMacAddress(it) } |
|
|
|
} catch (e: Exception) { |
|
|
|
Log.e(TAG, "获取设备MAC地址失败: ${e.message}", e) |
|
|
|
} |
|
|
|
} |
|
|
|
// // API 33+ (Android 13+) 使用 onAssociationCreated |
|
|
|
// @RequiresApi(Build.VERSION_CODES.TIRAMISU) |
|
|
|
// override fun onAssociationCreated(associationInfo: AssociationInfo) { |
|
|
|
// Log.i(TAG, "设备关联已创建: ${associationInfo.displayName}, ID: ${associationInfo.id}") |
|
|
|
// // 保存关联ID |
|
|
|
// try { |
|
|
|
// val macAddressMethod = associationInfo.javaClass.getMethod("getDeviceMacAddress") |
|
|
|
// val mac = macAddressMethod.invoke(associationInfo) as? String |
|
|
|
// mac?.let { savePairedMacAddress(it) } |
|
|
|
// } catch (e: Exception) { |
|
|
|
// Log.e(TAG, "获取设备MAC地址失败: ${e.message}", e) |
|
|
|
// } |
|
|
|
// } |
|
|
|
|
|
|
|
override fun onFailure(error: CharSequence?) { |
|
|
|
Log.e(TAG, "设备关联失败: $error") |
|
|
|
} |
|
|
|
}, |
|
|
|
null |
|
|
|
) |
|
|
|
// override fun onFailure(error: CharSequence?) { |
|
|
|
// Log.e(TAG, "设备关联失败: $error") |
|
|
|
// } |
|
|
|
// }, |
|
|
|
// null |
|
|
|
// ) |
|
|
|
|
|
|
|
return true |
|
|
|
} catch (e: Exception) { |
|
|
|
Log.e(TAG, "启动设备关联失败: ${e.message}", e) |
|
|
|
return false |
|
|
|
} |
|
|
|
} |
|
|
|
// return true |
|
|
|
// } catch (e: Exception) { |
|
|
|
// Log.e(TAG, "启动设备关联失败: ${e.message}", e) |
|
|
|
// return false |
|
|
|
// } |
|
|
|
// } |
|
|
|
|
|
|
|
/** |
|
|
|
* 处理关联结果 |
|
|
|
* |
|
|
|
* @param requestCode 请求码 |
|
|
|
* @param resultCode 结果码 |
|
|
|
* @param data Intent数据 |
|
|
|
* @return 是否处理了关联结果 |
|
|
|
*/ |
|
|
|
@RequiresApi(Build.VERSION_CODES.O) |
|
|
|
fun handleAssociationResult(requestCode: Int, resultCode: Int, data: Intent?): Boolean { |
|
|
|
if (requestCode != REQUEST_CODE_ASSOCIATE) { |
|
|
|
return false |
|
|
|
} |
|
|
|
// /** |
|
|
|
// * 处理关联结果 |
|
|
|
// * |
|
|
|
// * @param requestCode 请求码 |
|
|
|
// * @param resultCode 结果码 |
|
|
|
// * @param data Intent数据 |
|
|
|
// * @return 是否处理了关联结果 |
|
|
|
// */ |
|
|
|
// @RequiresApi(Build.VERSION_CODES.O) |
|
|
|
// fun handleAssociationResult(requestCode: Int, resultCode: Int, data: Intent?): Boolean { |
|
|
|
// if (requestCode != REQUEST_CODE_ASSOCIATE) { |
|
|
|
// return false |
|
|
|
// } |
|
|
|
|
|
|
|
Log.i(TAG, "收到关联结果: resultCode=$resultCode") |
|
|
|
// Log.i(TAG, "收到关联结果: resultCode=$resultCode") |
|
|
|
|
|
|
|
if (resultCode == Activity.RESULT_OK && data != null) { |
|
|
|
// 获取选中的蓝牙设备 |
|
|
|
val bluetoothDevice: BluetoothDevice? = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { |
|
|
|
data.getParcelableExtra(CompanionDeviceManager.EXTRA_DEVICE, BluetoothDevice::class.java) |
|
|
|
} else { |
|
|
|
@Suppress("DEPRECATION") |
|
|
|
data.getParcelableExtra(CompanionDeviceManager.EXTRA_DEVICE) |
|
|
|
} |
|
|
|
// if (resultCode == Activity.RESULT_OK && data != null) { |
|
|
|
// // 获取选中的蓝牙设备 |
|
|
|
// val bluetoothDevice: BluetoothDevice? = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { |
|
|
|
// data.getParcelableExtra(CompanionDeviceManager.EXTRA_DEVICE, BluetoothDevice::class.java) |
|
|
|
// } else { |
|
|
|
// @Suppress("DEPRECATION") |
|
|
|
// data.getParcelableExtra(CompanionDeviceManager.EXTRA_DEVICE) |
|
|
|
// } |
|
|
|
|
|
|
|
// 获取关联ID (Android 13+) |
|
|
|
val associationId = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { |
|
|
|
try { |
|
|
|
// 由于EXTRA_ASSOCIATION_ID常量仅在API 33+可用,直接使用已知的常量字符串值 |
|
|
|
// 不再尝试使用反射获取字段 |
|
|
|
val extraAssociationIdKey = "android.companion.extra.ASSOCIATION_ID" |
|
|
|
data.getStringExtra(extraAssociationIdKey) |
|
|
|
} catch (e: Exception) { |
|
|
|
Log.e(TAG, "获取关联ID失败: ${e.message}", e) |
|
|
|
null |
|
|
|
} |
|
|
|
} else { |
|
|
|
null |
|
|
|
} |
|
|
|
// // 获取关联ID (Android 13+) |
|
|
|
// val associationId = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { |
|
|
|
// try { |
|
|
|
// // 由于EXTRA_ASSOCIATION_ID常量仅在API 33+可用,直接使用已知的常量字符串值 |
|
|
|
// // 不再尝试使用反射获取字段 |
|
|
|
// val extraAssociationIdKey = "android.companion.extra.ASSOCIATION_ID" |
|
|
|
// data.getStringExtra(extraAssociationIdKey) |
|
|
|
// } catch (e: Exception) { |
|
|
|
// Log.e(TAG, "获取关联ID失败: ${e.message}", e) |
|
|
|
// null |
|
|
|
// } |
|
|
|
// } else { |
|
|
|
// null |
|
|
|
// } |
|
|
|
|
|
|
|
// 获取并验证MAC地址 |
|
|
|
if (bluetoothDevice != null) { |
|
|
|
val deviceMac = bluetoothDevice.address |
|
|
|
val deviceName = bluetoothDevice.name ?: "未知设备" |
|
|
|
// bluetoothDevice?.let { device -> |
|
|
|
// val deviceMac = device.address |
|
|
|
// Log.i(TAG, "设备关联成功: ${device.name} ($deviceMac)") |
|
|
|
|
|
|
|
// 验证MAC地址格式 |
|
|
|
if (!isValidMacAddress(deviceMac)) { |
|
|
|
Log.e(TAG, "设备 '$deviceName' 返回的MAC地址格式无效: $deviceMac") |
|
|
|
return false |
|
|
|
} |
|
|
|
|
|
|
|
// 检查是否与预期的MAC地址匹配 |
|
|
|
if (pendingMacAddress != null && pendingMacAddress != deviceMac) { |
|
|
|
Log.w(TAG, "选中的设备MAC地址 ($deviceMac) 与预期的MAC地址 ($pendingMacAddress) 不匹配") |
|
|
|
// 如果要严格匹配MAC,可以在这里返回false |
|
|
|
return false |
|
|
|
} |
|
|
|
// // 验证MAC地址是否匹配预期 |
|
|
|
// pendingMacAddress?.let { expectedMac -> |
|
|
|
// if (deviceMac.equals(expectedMac, ignoreCase = true)) { |
|
|
|
// Log.i(TAG, "MAC地址验证成功: $deviceMac") |
|
|
|
// } else { |
|
|
|
// Log.w(TAG, "MAC地址不匹配: 预期=$expectedMac, 实际=$deviceMac") |
|
|
|
// } |
|
|
|
// } |
|
|
|
|
|
|
|
// 匹配成功后,保存新的MAC地址 |
|
|
|
savePairedMacAddress(deviceMac) |
|
|
|
Log.i(TAG, "设备关联成功: $deviceName ($deviceMac), 关联ID: $associationId") |
|
|
|
// // 保存设备MAC地址 |
|
|
|
// savePairedMacAddress(deviceMac) |
|
|
|
|
|
|
|
// 清除临时MAC地址 |
|
|
|
pendingMacAddress = null |
|
|
|
// // 保存关联ID (如果可用) |
|
|
|
// associationId?.let { id -> |
|
|
|
// Log.i(TAG, "保存关联ID: $id") |
|
|
|
// // 这里可以保存关联ID到SharedPreferences或其他存储 |
|
|
|
// } |
|
|
|
|
|
|
|
// 🎯 立即发起BLE连接 |
|
|
|
// try { |
|
|
|
// Log.i(TAG, "配对成功,立即发起BLE连接: $deviceMac") |
|
|
|
// BleService.connect(deviceMac) |
|
|
|
// } catch (e: Exception) { |
|
|
|
// Log.e(TAG, "配对成功后发起BLE连接失败: ${e.message}", e) |
|
|
|
// } |
|
|
|
// // 清除临时MAC地址 |
|
|
|
// pendingMacAddress = null |
|
|
|
|
|
|
|
return true |
|
|
|
} else { |
|
|
|
Log.e(TAG, "无法获取关联的蓝牙设备信息") |
|
|
|
} |
|
|
|
} else { |
|
|
|
Log.w(TAG, "设备关联被取消或失败: resultCode=$resultCode") |
|
|
|
// 清除临时MAC地址 |
|
|
|
pendingMacAddress = null |
|
|
|
} |
|
|
|
|
|
|
|
return false |
|
|
|
} |
|
|
|
// return true |
|
|
|
// } ?: run { |
|
|
|
// Log.e(TAG, "无法获取蓝牙设备信息") |
|
|
|
// return false |
|
|
|
// } |
|
|
|
// } else { |
|
|
|
// Log.w(TAG, "设备关联被取消或失败") |
|
|
|
// // 清除临时MAC地址 |
|
|
|
// pendingMacAddress = null |
|
|
|
// return false |
|
|
|
// } |
|
|
|
// } |
|
|
|
|
|
|
|
/** |
|
|
|
* 注册后台扫描 |
|
|
|
@ -382,12 +428,14 @@ class BleCompanionManager(private val context: Context) { |
|
|
|
) { |
|
|
|
try { |
|
|
|
Log.d(TAG, "添加后台扫描过滤器UUID: $uuids, macAddress: $macAddress") |
|
|
|
val mac = getPairedMacAddress() |
|
|
|
if (mac.isNullOrEmpty()) { |
|
|
|
//val mac = getPairedMacAddress() |
|
|
|
|
|
|
|
if (macAddress.isNullOrEmpty()||uuids.isNullOrEmpty()) { |
|
|
|
Log.e(TAG, "注册后台扫描失败: 没有配对的设备MAC地址") |
|
|
|
return |
|
|
|
} |
|
|
|
|
|
|
|
savePairedMacAddress(macAddress) |
|
|
|
savePairedUuid(uuids) |
|
|
|
val bluetoothManager = context.getSystemService(Context.BLUETOOTH_SERVICE) as? BluetoothManager |
|
|
|
val bluetoothAdapter = bluetoothManager?.adapter |
|
|
|
val bluetoothLeScanner = bluetoothAdapter?.bluetoothLeScanner |
|
|
|
@ -440,24 +488,24 @@ class BleCompanionManager(private val context: Context) { |
|
|
|
.setScanMode(ScanSettings.SCAN_MODE_LOW_POWER) |
|
|
|
.build() |
|
|
|
|
|
|
|
// 创建PendingIntent |
|
|
|
val scanIntent = Intent(BleConst.ACTION_SCAN_RESULT) |
|
|
|
.setClass(context, BleWakeReceiver::class.java) |
|
|
|
// // 创建PendingIntent |
|
|
|
// val scanIntent = Intent(BleConst.ACTION_SCAN_RESULT) |
|
|
|
// .setClass(context, BleWakeReceiver::class.java) |
|
|
|
|
|
|
|
val pendingIntent = PendingIntent.getBroadcast( |
|
|
|
context, |
|
|
|
0, |
|
|
|
scanIntent, |
|
|
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { |
|
|
|
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE |
|
|
|
} else { |
|
|
|
PendingIntent.FLAG_UPDATE_CURRENT |
|
|
|
} |
|
|
|
) |
|
|
|
// val pendingIntent = PendingIntent.getBroadcast( |
|
|
|
// context, |
|
|
|
// 0, |
|
|
|
// scanIntent, |
|
|
|
// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { |
|
|
|
// PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE |
|
|
|
// } else { |
|
|
|
// PendingIntent.FLAG_UPDATE_CURRENT |
|
|
|
// } |
|
|
|
// ) |
|
|
|
|
|
|
|
// 启动后台扫描 |
|
|
|
bluetoothLeScanner.startScan(filters, settings, pendingIntent) |
|
|
|
Log.i(TAG, "已注册后台扫描,目标设备MAC: $mac") |
|
|
|
Log.i(TAG, "已注册后台扫描,目标设备MAC: $macAddress") |
|
|
|
} catch (e: Exception) { |
|
|
|
Log.e(TAG, "注册后台扫描失败: ${e.message}", e) |
|
|
|
} |
|
|
|
@ -477,20 +525,20 @@ class BleCompanionManager(private val context: Context) { |
|
|
|
return |
|
|
|
} |
|
|
|
|
|
|
|
// 创建PendingIntent |
|
|
|
val scanIntent = Intent(BleConst.ACTION_SCAN_RESULT) |
|
|
|
.setClass(context, BleWakeReceiver::class.java) |
|
|
|
// // 创建PendingIntent |
|
|
|
// val scanIntent = Intent(BleConst.ACTION_SCAN_RESULT) |
|
|
|
// .setClass(context, BleWakeReceiver::class.java) |
|
|
|
|
|
|
|
val pendingIntent = PendingIntent.getBroadcast( |
|
|
|
context, |
|
|
|
0, |
|
|
|
scanIntent, |
|
|
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { |
|
|
|
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE |
|
|
|
} else { |
|
|
|
PendingIntent.FLAG_UPDATE_CURRENT |
|
|
|
} |
|
|
|
) |
|
|
|
// val pendingIntent = PendingIntent.getBroadcast( |
|
|
|
// context, |
|
|
|
// 0, |
|
|
|
// scanIntent, |
|
|
|
// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { |
|
|
|
// PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE |
|
|
|
// } else { |
|
|
|
// PendingIntent.FLAG_UPDATE_CURRENT |
|
|
|
// } |
|
|
|
// ) |
|
|
|
|
|
|
|
// 停止后台扫描 |
|
|
|
bluetoothLeScanner.stopScan(pendingIntent) |
|
|
|
@ -500,42 +548,42 @@ class BleCompanionManager(private val context: Context) { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* 清除所有关联 |
|
|
|
*/ |
|
|
|
@RequiresApi(Build.VERSION_CODES.O) |
|
|
|
fun clearAllAssociations() { |
|
|
|
try { |
|
|
|
// 获取CompanionDeviceManager |
|
|
|
val companionDeviceManager = context.getSystemService(Context.COMPANION_DEVICE_SERVICE) as? CompanionDeviceManager |
|
|
|
if (companionDeviceManager == null) { |
|
|
|
Log.e(TAG, "无法获取CompanionDeviceManager") |
|
|
|
return |
|
|
|
} |
|
|
|
// /** |
|
|
|
// * 清除所有关联 |
|
|
|
// */ |
|
|
|
// @RequiresApi(Build.VERSION_CODES.O) |
|
|
|
// fun clearAllAssociations() { |
|
|
|
// try { |
|
|
|
// // 获取CompanionDeviceManager |
|
|
|
// val companionDeviceManager = context.getSystemService(Context.COMPANION_DEVICE_SERVICE) as? CompanionDeviceManager |
|
|
|
// if (companionDeviceManager == null) { |
|
|
|
// Log.e(TAG, "无法获取CompanionDeviceManager") |
|
|
|
// return |
|
|
|
// } |
|
|
|
|
|
|
|
// 清除本地保存的MAC地址 |
|
|
|
clearPairedMacAddress() |
|
|
|
// // 清除本地保存的MAC地址 |
|
|
|
// clearPairedMacAddress() |
|
|
|
|
|
|
|
// 获取所有关联ID |
|
|
|
val associationIds = getAssociationIds(companionDeviceManager) |
|
|
|
// // 获取所有关联ID |
|
|
|
// val associationIds = getAssociationIds(companionDeviceManager) |
|
|
|
|
|
|
|
// 遍历解除关联 |
|
|
|
for (id in associationIds) { |
|
|
|
try { |
|
|
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { |
|
|
|
companionDeviceManager.disassociate(id) |
|
|
|
} else { |
|
|
|
companionDeviceManager.disassociate(id) |
|
|
|
} |
|
|
|
Log.i(TAG, "已解除设备关联: $id") |
|
|
|
} catch (e: Exception) { |
|
|
|
Log.e(TAG, "解除设备关联失败: $id, ${e.message}", e) |
|
|
|
} |
|
|
|
} |
|
|
|
} catch (e: Exception) { |
|
|
|
Log.e(TAG, "清除所有关联失败: ${e.message}", e) |
|
|
|
} |
|
|
|
} |
|
|
|
// // 遍历解除关联 |
|
|
|
// for (id in associationIds) { |
|
|
|
// try { |
|
|
|
// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { |
|
|
|
// companionDeviceManager.disassociate(id) |
|
|
|
// } else { |
|
|
|
// companionDeviceManager.disassociate(id) |
|
|
|
// } |
|
|
|
// Log.i(TAG, "已解除设备关联: $id") |
|
|
|
// } catch (e: Exception) { |
|
|
|
// Log.e(TAG, "解除设备关联失败: $id, ${e.message}", e) |
|
|
|
// } |
|
|
|
// } |
|
|
|
// } catch (e: Exception) { |
|
|
|
// Log.e(TAG, "清除所有关联失败: ${e.message}", e) |
|
|
|
// } |
|
|
|
// } |
|
|
|
|
|
|
|
/** |
|
|
|
* 验证是否是有效的MAC地址格式 |
|
|
|
|