Browse Source

1

newdev_shunjiawei
fdp 1 year ago
parent
commit
f9f4e665d0
  1. 240
      local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/BleMediaService.kt

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

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