Browse Source

ble重构优化

newdev_shunjiawei
fdp 1 year ago
parent
commit
9f099dbea3
  1. 32
      lib/modules/pairing/controllers/pairing_controller.dart
  2. 240
      local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/BleMediaService.kt
  3. 704
      local_plugins/ble_service/android/src/main/kotlin/com/yunqiinnovation/ble_service/BleCompanionManager.kt
  4. 74
      local_plugins/ble_service/android/src/main/kotlin/com/yunqiinnovation/ble_service/BleCompanionService.kt
  5. 1
      local_plugins/ble_service/android/src/main/kotlin/com/yunqiinnovation/ble_service/BleService.kt
  6. 54
      local_plugins/ble_service/android/src/main/kotlin/com/yunqiinnovation/ble_service/BleServicePlugin.kt
  7. 12
      local_plugins/ble_service/android/src/main/kotlin/com/yunqiinnovation/ble_service/BootReceiver.kt

32
lib/modules/pairing/controllers/pairing_controller.dart

@ -659,22 +659,22 @@ class PairingController extends GetxController with WidgetsBindingObserver {
return;
}
Logger.i(_tag, '开始BLE设备关联: $_verifiedDeviceName:$_verifiedDeviceAddress');
// 调用BLE管理器的关联方法
final success = await _bleManager.startDeviceAssociation(
deviceNamePattern: _verifiedDeviceName!,
macAddress: _verifiedDeviceAddress!,
);
if (success) {
Logger.i(_tag, 'BLE设备关联成功');
// 关联成功后,转到等待BLE连接状态
_transition(PairingState.waitingBle);
} else {
_setError(ErrorType.associationFailed,
'bleDeviceAssociationFailed'.tr); //BLE设备关联失败
}
// Logger.i(_tag, '开始BLE设备关联: $_verifiedDeviceName:$_verifiedDeviceAddress');
// // 调用BLE管理器的关联方法
// final success = await _bleManager.startDeviceAssociation(
// deviceNamePattern: _verifiedDeviceName!,
// macAddress: _verifiedDeviceAddress!,
// );
// if (success) {
// Logger.i(_tag, 'BLE设备关联成功');
// 关联成功后,转到等待BLE连接状态
_transition(PairingState.waitingBle);
// } else {
// _setError(ErrorType.associationFailed,
// 'bleDeviceAssociationFailed'.tr); //BLE设备关联失败
// }
} catch (e) {
Logger.e(_tag, 'BLE设备关联异常: $e');
_setError(ErrorType.associationFailed,

240
local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/BleMediaService.kt

@ -8,8 +8,10 @@ import android.app.Service
import android.content.Context
import android.content.Intent
import android.content.SharedPreferences
import android.content.res.AssetManager
import android.os.Build
import android.os.IBinder
import android.os.LocaleList
import androidx.core.app.NotificationCompat
import android.util.Log
import kotlinx.coroutines.CoroutineScope
@ -21,6 +23,9 @@ import kotlin.coroutines.CoroutineContext
import kotlinx.coroutines.Dispatchers
import com.yunqiinnovation.ble_service.BleConst
import com.yunqiinnovation.ble_service.BleService
import java.io.IOException
import java.util.Locale
import org.json.JSONObject
/**
* BLE代理服务
@ -28,6 +33,7 @@ import com.yunqiinnovation.ble_service.BleService
* 1. 语音代理初始化和管理
* 2. BLE设备连接管理
* 3. 后台扫描结果处理
* 4. 自主配置获取和管理
*/
class BleMediaService : Service(), CoroutineScope {
@ -48,12 +54,12 @@ class BleMediaService : Service(), CoroutineScope {
private const val KEY_MCP_SERVER = "mcpServer"
private const val KEY_VOLCANO_APP_ID = "volcanoAppId"
private const val KEY_VOLCANO_TOKEN = "volcanoToken"
private const val KEY_SERVER_URL = "serverurl"
private const val KEY_USER_TOKEN = "usertoken"
private const val KEY_SUPPORTED_LANGUAGES = "supportedLanguages"
private const val KEY_MUSICSERVICE_TYPE = "musicServiceType"
private const val APP_NAVIGATION_MODE = "navigationMode"
// 用于跟踪服务是否正在运行
private val isRunning = AtomicBoolean(false)
@ -72,9 +78,6 @@ class BleMediaService : Service(), CoroutineScope {
// 语音代理是否已初始化
private var isChatAgentInitialized = false
// 当前启动Intent
private var currentIntent: Intent? = null
override fun onCreate() {
super.onCreate()
@ -90,9 +93,6 @@ class BleMediaService : Service(), CoroutineScope {
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
Log.i(TAG, "BleAgentService.onStartCommand: ${intent?.action}")
// 保存当前Intent,用于后续初始化
currentIntent = intent
// 初始化语音代理
if (!isChatAgentInitialized) {
initializeChatAgent()
@ -113,24 +113,14 @@ class BleMediaService : Service(), CoroutineScope {
/**
* 初始化语音交互代理
* 自主获取配置参数,不依赖Flutter层传入
*/
private fun initializeChatAgent() {
try {
// 获取SharedPreferences
val prefs = applicationContext.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE)
// 尝试从Intent中获取参数
val configFromIntent = extractConfigFromIntent(currentIntent)
// 如果Intent中有参数,则保存到SharedPreferences
if (configFromIntent.isNotEmpty()) {
Log.d(TAG, "从Intent中获取到配置参数,保存到SharedPreferences")
saveConfigToPreferences(configFromIntent, prefs)
}
Log.i(TAG, "开始自主获取配置参数...")
// 合并Intent和SharedPreferences中的参数
val config = getConfigFromPreferences(prefs)
Log.d(TAG, "当前配置参数: $config")
// 获取配置参数
val config = getServiceConfig()
// 检查必要的配置是否存在
val azureSpeechKey = config[KEY_AZURE_SPEECH_KEY]?.toString() ?: ""
@ -138,6 +128,8 @@ class BleMediaService : Service(), CoroutineScope {
if (azureSpeechKey.isEmpty() || openaiApiKey.isEmpty()) {
Log.w(TAG, "语音代理初始化失败: API密钥未配置")
Log.w(TAG, "Azure Speech Key: ${if (azureSpeechKey.isEmpty()) "未配置" else "已配置"}")
Log.w(TAG, "OpenAI API Key: ${if (openaiApiKey.isEmpty()) "未配置" else "已配置"}")
return
}
@ -148,7 +140,7 @@ class BleMediaService : Service(), CoroutineScope {
if(config.isNotEmpty()) {
// 初始化ChatAgent
Log.i(TAG, "开始初始化ChatAgent")
Log.i(TAG, "开始初始化ChatAgent,配置参数数量: ${config.size}")
val success = BleAgent.initialize(applicationContext, config)
if (success) {
isChatAgentInitialized = true
@ -163,98 +155,145 @@ class BleMediaService : Service(), CoroutineScope {
}
/**
* 从Intent中提取配置参数
* 自主获取服务配置
* 从多个源获取配置参数:SharedPreferences、Assets、系统设置等
*/
private fun extractConfigFromIntent(intent: Intent?): Map<String, Any> {
private fun getServiceConfig(): Map<String, Any> {
val config = mutableMapOf<String, Any>()
intent?.let {
// 提取可能存在的配置参数
it.getStringExtra(KEY_AZURE_SPEECH_KEY)?.let { value -> config[KEY_AZURE_SPEECH_KEY] = value }
it.getStringExtra(KEY_AZURE_SPEECH_REGION)?.let { value -> config[KEY_AZURE_SPEECH_REGION] = value }
it.getStringExtra(KEY_OPENAI_API_KEY)?.let { value -> config[KEY_OPENAI_API_KEY] = value }
it.getStringExtra(KEY_OPENAI_BASE_URL)?.let { value -> config[KEY_OPENAI_BASE_URL] = value }
it.getStringExtra(KEY_OPENAI_MODEL)?.let { value -> config[KEY_OPENAI_MODEL] = value }
it.getStringExtra(KEY_OPENAI_VISION_MODEL)?.let { value -> config[KEY_OPENAI_VISION_MODEL] = value }
it.getStringExtra(KEY_SYSTEM_PROMPT)?.let { value -> config[KEY_SYSTEM_PROMPT] = value }
it.getStringExtra(KEY_MCP_SERVER)?.let { value -> config[KEY_MCP_SERVER] = value }
it.getStringExtra(KEY_VOLCANO_APP_ID)?.let { value -> config[KEY_VOLCANO_APP_ID] = value }
it.getStringExtra(KEY_VOLCANO_TOKEN)?.let { value -> config[KEY_VOLCANO_TOKEN] = value }
it.getStringExtra(KEY_SERVER_URL)?.let { value -> config[KEY_SERVER_URL] = value }
it.getStringExtra(KEY_USER_TOKEN)?.let { value -> config[KEY_USER_TOKEN] = value }
it.getStringExtra(KEY_MUSICSERVICE_TYPE)?.let { value -> config[KEY_MUSICSERVICE_TYPE] = value }
it.getStringExtra(APP_NAVIGATION_MODE)?.let { value -> config[APP_NAVIGATION_MODE] = value }
// 提取 supportedLanguages 参数
it.getStringArrayListExtra(KEY_SUPPORTED_LANGUAGES)?.let { languages ->
if (languages.isNotEmpty()) {
config[KEY_SUPPORTED_LANGUAGES] = languages
}
try {
// 1. 从SharedPreferences获取基础配置
val prefs = getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE)
// 添加字符串类型的配置参数(使用默认值)
config[KEY_AZURE_SPEECH_KEY] = getConfigString(prefs, KEY_AZURE_SPEECH_KEY, "")
config[KEY_AZURE_SPEECH_REGION] = getConfigString(prefs, KEY_AZURE_SPEECH_REGION, "eastasia")
config[KEY_OPENAI_API_KEY] = getConfigString(prefs, KEY_OPENAI_API_KEY, "")
config[KEY_OPENAI_BASE_URL] = getConfigString(prefs, KEY_OPENAI_BASE_URL, "https://api.openai.com/v1")
config[KEY_OPENAI_MODEL] = getConfigString(prefs, KEY_OPENAI_MODEL, "gpt-3.5-turbo")
config[KEY_OPENAI_VISION_MODEL] = getConfigString(prefs, KEY_OPENAI_VISION_MODEL, "gpt-4-vision-preview")
config[KEY_SYSTEM_PROMPT] = getConfigString(prefs, KEY_SYSTEM_PROMPT, "")
config[KEY_VOLCANO_APP_ID] = getConfigString(prefs, KEY_VOLCANO_APP_ID, "2316081933")
config[KEY_VOLCANO_TOKEN] = getConfigString(prefs, KEY_VOLCANO_TOKEN, "mcJTh6OelBqU_L78lgGd77Nyrc_-5-En")
config[KEY_SERVER_URL] = getConfigString(prefs, KEY_SERVER_URL, "")
config[KEY_USER_TOKEN] = getConfigString(prefs, KEY_USER_TOKEN, "")
config[KEY_MUSICSERVICE_TYPE] = getConfigString(prefs, KEY_MUSICSERVICE_TYPE, "AUTO")
config[APP_NAVIGATION_MODE] = getConfigString(prefs, APP_NAVIGATION_MODE, "external_auto")
// 2. 从Assets获取MCP配置
val mcpConfig = loadMcpConfigFromAssets()
if (mcpConfig.isNotEmpty()) {
config[KEY_MCP_SERVER] = mcpConfig
Log.d(TAG, "成功从Assets加载MCP配置")
} else {
Log.w(TAG, "未能从Assets加载MCP配置,使用空配置")
config[KEY_MCP_SERVER] = "{\"mcpServers\":{}}"
}
// 3. 获取系统语言配置
val supportedLanguages = getSystemSupportedLanguages()
config[KEY_SUPPORTED_LANGUAGES] = supportedLanguages
Log.d(TAG, "系统支持的语言: ${supportedLanguages.joinToString(", ")}")
Log.d(TAG, "配置获取完成,参数数量: ${config.size}")
} catch (e: Exception) {
Log.e(TAG, "获取服务配置时发生异常", e)
}
Log.i(TAG, "从Intent中提取到配置参数: $config")
return config
}
/**
* 将配置参数保存到SharedPreferences
* 从Assets目录加载MCP配置文件
*/
private fun saveConfigToPreferences(config: Map<String, Any>, prefs: SharedPreferences) {
prefs.edit().apply {
config.forEach { (key, value) ->
when (value) {
is String -> {
if (value.isNotEmpty()) {
putString(key, value)
}
}
is List<*> -> {
// 处理 supportedLanguages 列表
if (key == KEY_SUPPORTED_LANGUAGES) {
val languageList = value.filterIsInstance<String>()
if (languageList.isNotEmpty()) {
// 将语言列表转换为逗号分隔的字符串保存
putString(key, languageList.joinToString(","))
}
}
}
}
}
apply()
private fun loadMcpConfigFromAssets(): String {
return try {
val assetManager: AssetManager = assets
val inputStream = assetManager.open(".mcp.json")
val size = inputStream.available()
val buffer = ByteArray(size)
inputStream.read(buffer)
inputStream.close()
val jsonString = String(buffer, Charsets.UTF_8)
Log.d(TAG, "成功从Assets加载.mcp.json文件")
jsonString
} catch (e: IOException) {
Log.w(TAG, "无法从Assets加载.mcp.json文件: ${e.message}")
// 返回默认的空MCP配置
"{\"mcpServers\":{}}"
}
}
/**
* 从SharedPreferences获取配置
* 获取系统支持的语言列表
* 根据系统当前语言设置动态配置语音识别语言
*/
private fun getConfigFromPreferences(prefs: SharedPreferences): Map<String, Any> {
val config = mutableMapOf<String, Any>()
// 添加字符串类型的配置参数
config[KEY_AZURE_SPEECH_KEY] = getConfigString(prefs, KEY_AZURE_SPEECH_KEY, "")
config[KEY_AZURE_SPEECH_REGION] = getConfigString(prefs, KEY_AZURE_SPEECH_REGION, "eastasia")
config[KEY_OPENAI_API_KEY] = getConfigString(prefs, KEY_OPENAI_API_KEY, "")
config[KEY_OPENAI_BASE_URL] = getConfigString(prefs, KEY_OPENAI_BASE_URL, "https://api.openai.com/v1")
config[KEY_OPENAI_MODEL] = getConfigString(prefs, KEY_OPENAI_MODEL, "gpt-3.5-turbo")
config[KEY_OPENAI_VISION_MODEL] = getConfigString(prefs, KEY_OPENAI_VISION_MODEL, "gpt-4-vision-preview")
config[KEY_SYSTEM_PROMPT] = getConfigString(prefs, KEY_SYSTEM_PROMPT, "gpt-3.5-turbo")
config[KEY_MCP_SERVER] = getConfigString(prefs, KEY_MCP_SERVER, "")
config[KEY_VOLCANO_APP_ID] = getConfigString(prefs, KEY_VOLCANO_APP_ID, "")
config[KEY_VOLCANO_TOKEN] = getConfigString(prefs, KEY_VOLCANO_TOKEN, "")
config[KEY_SERVER_URL] = getConfigString(prefs, KEY_SERVER_URL, "")
config[KEY_USER_TOKEN] = getConfigString(prefs, KEY_USER_TOKEN, "")
config[KEY_MUSICSERVICE_TYPE] = getConfigString(prefs, KEY_MUSICSERVICE_TYPE, "")
config[APP_NAVIGATION_MODE] = getConfigString(prefs, APP_NAVIGATION_MODE, "")
// 添加 supportedLanguages 参数处理
val supportedLanguagesStr = getConfigString(prefs, KEY_SUPPORTED_LANGUAGES, "")
if (supportedLanguagesStr.isNotEmpty()) {
// 将逗号分隔的字符串转换回语言列表
val languageList = supportedLanguagesStr.split(",").map { it.trim() }.filter { it.isNotEmpty() }
if (languageList.isNotEmpty()) {
config[KEY_SUPPORTED_LANGUAGES] = languageList
private fun getSystemSupportedLanguages(): List<String> {
val supportedLanguages = mutableListOf<String>()
try {
// 获取系统当前语言设置
val currentLocale = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
LocaleList.getDefault().get(0)
} else {
@Suppress("DEPRECATION")
Locale.getDefault()
}
val language = currentLocale.language
val country = currentLocale.country
// 构造Azure Speech SDK需要的语言格式
when (language) {
"zh" -> {
when (country) {
"CN" -> supportedLanguages.add("zh-CN")
"TW" -> supportedLanguages.add("zh-TW")
"HK" -> supportedLanguages.add("zh-HK")
else -> supportedLanguages.add("zh-CN") // 默认简体中文
}
}
"en" -> {
when (country) {
"US" -> supportedLanguages.add("en-US")
"GB" -> supportedLanguages.add("en-GB")
"AU" -> supportedLanguages.add("en-AU")
"CA" -> supportedLanguages.add("en-CA")
else -> supportedLanguages.add("en-US") // 默认美式英语
}
}
"ja" -> supportedLanguages.add("ja-JP")
"ko" -> supportedLanguages.add("ko-KR")
"fr" -> supportedLanguages.add("fr-FR")
"de" -> supportedLanguages.add("de-DE")
"es" -> supportedLanguages.add("es-ES")
"it" -> supportedLanguages.add("it-IT")
"pt" -> supportedLanguages.add("pt-BR")
"ru" -> supportedLanguages.add("ru-RU")
else -> {
// 对于不支持的语言,添加通用格式
if (country.isNotEmpty()) {
supportedLanguages.add("$language-$country")
} else {
supportedLanguages.add("en-US") // 最终兜底
}
}
}
// 确保至少有一个语言
if (supportedLanguages.isEmpty()) {
supportedLanguages.add("en-US")
}
Log.d(TAG, "检测到系统语言: $language-$country,配置语音识别语言: ${supportedLanguages.joinToString(", ")}")
} catch (e: Exception) {
Log.e(TAG, "获取系统语言配置失败,使用默认语言: $e")
supportedLanguages.add("en-US")
}
return config
return supportedLanguages
}
/**
@ -278,6 +317,9 @@ class BleMediaService : Service(), CoroutineScope {
super.onDestroy()
}
/**
* 检查BLE连接状态并尝试连接
*/
private fun checkBleConnection() {
Log.i(TAG, "checkBleConnection收到连接请求")
@ -335,4 +377,4 @@ class BleMediaService : Service(), CoroutineScope {
return builder.build()
}
}
}

704
local_plugins/ble_service/android/src/main/kotlin/com/yunqiinnovation/ble_service/BleCompanionManager.kt

@ -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地址格式

74
local_plugins/ble_service/android/src/main/kotlin/com/yunqiinnovation/ble_service/BleCompanionService.kt

@ -1,74 +0,0 @@
package com.yunqiinnovation.ble_service
import android.companion.AssociationInfo
import android.companion.CompanionDeviceService
import android.content.Intent
import android.os.Build
import android.os.IBinder
import androidx.annotation.RequiresApi
import android.util.Log
/**
* BleCompanionService
* 用于监听配对设备的出现和消失
* 注:此服务仅在Android 11 (API 30)及以上版本可用
*/
@RequiresApi(Build.VERSION_CODES.R)
class BleCompanionService : CompanionDeviceService() {
private val TAG = "BleCompanionService"
override fun onCreate() {
super.onCreate()
Log.i(TAG, "BleCompanionService已创建")
}
override fun onDeviceAppeared(info: AssociationInfo) {
super.onDeviceAppeared(info)
Log.i(TAG, "检测到配对设备出现: ${info.displayName}, ID: ${info.id}")
// 当配对设备出现时,启动前台服务
try {
val serviceIntent = Intent().apply {
// 尝试使用BleMediaService,如果不存在则回退到BleService
val serviceClassName = try {
Log.d(TAG, "BleMediaService存在")
Class.forName("com.yunqiinnovation.agent_service.BleMediaService").name
} catch (e: ClassNotFoundException) {
Log.d(TAG, "BleMediaService不存在,回退到BleService")
BleService::class.java.name
}
setClassName(this@BleCompanionService, serviceClassName)
action = "android.intent.action.DEVICE_APPEARED"
putExtra("assocId", info.id)
putExtra("displayName", info.displayName)
// 如果设备信息中有MAC地址,也可以传递
// 使用反射获取MAC地址,因为deviceMacAddress在不同API版本可能不同或不存在
try {
val macAddressMethod = info.javaClass.getMethod("getDeviceMacAddress")
val mac = macAddressMethod.invoke(info) as? String
mac?.let { putExtra("mac", it) }
} catch (e: Exception) {
Log.e(TAG, "获取设备MAC地址失败: ${e.message}", e)
}
}
// 启动前台服务
startForegroundService(serviceIntent)
Log.i(TAG, "已启动服务")
} catch (e: Exception) {
Log.e(TAG, "启动服务失败: ${e.message}", e)
}
}
override fun onDeviceDisappeared(info: AssociationInfo) {
super.onDeviceDisappeared(info)
Log.i(TAG, "配对设备消失: ${info.displayName}, ID: ${info.id}")
// 可以选择停止服务或执行其他清理操作
}
// 注意: 不需要重写 onBindCompanionDeviceService 和 onUnbindCompanionDeviceService
// 这些方法在 Android 12+ 才添加,我们可以使用父类的默认实现
}

1
local_plugins/ble_service/android/src/main/kotlin/com/yunqiinnovation/ble_service/BleService.kt

@ -520,6 +520,7 @@ object BleService {
*/
fun connect() {
try {
Log.i(TAG, "设备连接与通信: 自动连接已配对的BLE设备")
// 创建 BleCompanionManager 实例
val companionManager = BleCompanionManager(context)

54
local_plugins/ble_service/android/src/main/kotlin/com/yunqiinnovation/ble_service/BleServicePlugin.kt

@ -123,21 +123,21 @@ class BleServicePlugin : FlutterPlugin, MethodCallHandler, ActivityAware,
// 实现ActivityResultListener接口
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?): Boolean {
// 处理设备关联结果
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val handled = bleCompanionManager.handleAssociationResult(requestCode, resultCode, data)
if (handled) {
// 返回设备关联结果
pendingAssociationResult?.success(true)
pendingAssociationResult = null
return true
} else {
pendingAssociationResult?.success(false)
pendingAssociationResult = null
return true
}
}
return false
// // 处理设备关联结果
// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
// val handled = bleCompanionManager.handleAssociationResult(requestCode, resultCode, data)
// if (handled) {
// // 返回设备关联结果
// pendingAssociationResult?.success(true)
// pendingAssociationResult = null
// return true
// } else {
// pendingAssociationResult?.success(false)
// pendingAssociationResult = null
// return true
// }
// }
return true
}
// 添加sendEvent函数
@ -247,18 +247,18 @@ class BleServicePlugin : FlutterPlugin, MethodCallHandler, ActivityAware,
return
}
// 启动设备关联
val success = bleCompanionManager.startAssociation(
currentActivity,
deviceNamePattern,
serviceUuid,
macAddress
)
// // 启动设备关联
// val success = bleCompanionManager.startAssociation(
// currentActivity,
// deviceNamePattern,
// serviceUuid,
// macAddress
// )
if (!success) {
pendingAssociationResult = null
result.error("FAILED", "启动设备关联失败", null)
}
// if (!success) {
// pendingAssociationResult = null
// result.error("FAILED", "启动设备关联失败", null)
// }
// 不要立即返回结果,等待onActivityResult回调
}
"getPairedMacAddress" -> {
@ -304,7 +304,7 @@ class BleServicePlugin : FlutterPlugin, MethodCallHandler, ActivityAware,
}
"clearAssociations" -> {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
bleCompanionManager.clearAllAssociations()
// bleCompanionManager.clearAllAssociations()
result.success(true)
} else {
result.error("UNSUPPORTED", "设备不支持Companion Device Manager", null)

12
local_plugins/ble_service/android/src/main/kotlin/com/yunqiinnovation/ble_service/BootReceiver.kt

@ -12,16 +12,22 @@ class BootReceiver : BroadcastReceiver() {
private val TAG = "BootReceiver"
override fun onReceive(ctx: Context, i: Intent) {
val serviceClass = try {
Log.i(TAG, "BleMediaService不存在")
Log.d(TAG, "BleMediaService不存在")
Class.forName("com.yunqiinnovation.agent_service.BleMediaService")
} catch (e: ClassNotFoundException) {
Log.i(TAG, "BleMediaService不存在")
Log.d(TAG, "BleMediaService不存在")
return
}
if (i.action == Intent.ACTION_BOOT_COMPLETED) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
{
Log.d(TAG, "BleMediaService")
ctx.startForegroundService(Intent(ctx, serviceClass))
else ctx.startService(Intent(ctx, serviceClass))
}
else{
Log.d(TAG, "BleMediaService")
ctx.startService(Intent(ctx, serviceClass))
}
}
}
}
Loading…
Cancel
Save