Browse Source

005

newdev_shunjiawei
wolfplus 2 years ago
parent
commit
be49d36183
  1. 311
      android/app/src/main/kotlin/com/example/deep_voice/MainActivity.kt
  2. 925
      android/app/src/main/kotlin/com/example/deep_voice/VoiceInteractionService.kt
  3. 40
      android/app/src/main/kotlin/com/example/deep_voice/VolcanoAIService.kt
  4. 6
      lib/core/bindings/initial_binding.dart
  5. 4
      lib/data/services/azure_asr_service.dart
  6. 4
      lib/data/services/azure_tts_service.dart
  7. 3
      lib/data/services/chat_history_service.dart
  8. 178
      lib/data/services/flutter_asr_service.dart
  9. 214
      lib/data/services/voice_interaction_service.dart
  10. 5
      lib/main.dart
  11. 77
      lib/modules/chat/controllers/chat_controller.dart
  12. 82
      lib/modules/home/controllers/home_controller.dart
  13. 61
      lib/modules/home/views/home_view.dart
  14. 7
      lib/modules/translation/controllers/translation_controller.dart
  15. 5
      pubspec.yaml

311
android/app/src/main/kotlin/com/example/deep_voice/MainActivity.kt

@ -10,12 +10,17 @@ import android.util.Log
import com.ryanheise.audioservice.AudioServiceActivity import com.ryanheise.audioservice.AudioServiceActivity
import io.flutter.embedding.engine.FlutterEngine import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannel import io.flutter.plugin.common.MethodChannel
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.EventChannel import io.flutter.plugin.common.EventChannel
import io.flutter.embedding.engine.plugins.FlutterPlugin import io.flutter.embedding.engine.plugins.FlutterPlugin
import io.flutter.plugins.GeneratedPluginRegistrant import io.flutter.plugins.GeneratedPluginRegistrant
import io.flutter.embedding.android.FlutterActivity import io.flutter.embedding.android.FlutterActivity
import android.content.Context import android.content.Context
import android.content.ComponentName import android.content.ComponentName
import android.content.BroadcastReceiver
import android.content.IntentFilter
import android.os.Handler
import android.os.Looper
class MainActivity: FlutterActivity() { class MainActivity: FlutterActivity() {
@ -24,18 +29,49 @@ class MainActivity: FlutterActivity() {
private val AZURE_ASR_EVENT_CHANNEL = "com.deep_voice.azure_asr_events" private val AZURE_ASR_EVENT_CHANNEL = "com.deep_voice.azure_asr_events"
private val AZURE_TTS_CHANNEL = "com.deep_voice.azure_tts" private val AZURE_TTS_CHANNEL = "com.deep_voice.azure_tts"
private val VOICE_INTERACTION_CHANNEL = "com.deep_voice.voice_interaction" private val VOICE_INTERACTION_CHANNEL = "com.deep_voice.voice_interaction"
private val VOICE_INTERACTION_EVENT_CHANNEL = "com.deep_voice.voice_interaction_events"
private val TAG = "MainActivity" private val TAG = "MainActivity"
private lateinit var azureAsrHelper: AzureAsrHelper private lateinit var azureAsrHelper: AzureAsrHelper
private lateinit var azureTtsHelper: AzureTtsHelper private lateinit var azureTtsHelper: AzureTtsHelper
private var azureAsrEventSink: EventChannel.EventSink? = null private var azureAsrEventSink: EventChannel.EventSink? = null
private var voiceInteractionEventSink: EventChannel.EventSink? = null
// 广播接收器
private val voiceInteractionReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
Log.d(TAG, "收到广播: ${intent?.action}")
when (intent?.action) {
VoiceInteractionService.ACTION_RECOGNITION_STARTED -> {
val timestamp = intent.getLongExtra("timestamp", 0)
Log.d(TAG, "收到语音识别启动广播: timestamp=$timestamp")
sendVoiceInteractionEvent(mapOf(
"type" to "recognitionStarted",
"timestamp" to timestamp
))
}
VoiceInteractionService.ACTION_CHAT_HISTORY_UPDATED -> {
val agentId = intent.getStringExtra("agentId") ?: "default"
val userMessage = intent.getStringExtra("userMessage") ?: ""
val assistantMessage = intent.getStringExtra("assistantMessage") ?: ""
val timestamp = intent.getLongExtra("timestamp", System.currentTimeMillis())
Log.d(TAG, "收到聊天记录更新广播: agentId=$agentId, timestamp=$timestamp")
Log.d(TAG, "用户消息: ${userMessage.take(50)}...")
Log.d(TAG, "助手回复: ${assistantMessage.take(50)}...")
sendChatHistoryEvent(agentId, userMessage, assistantMessage, timestamp)
}
}
}
}
// 使用伴生对象存储配置信息,使其在整个应用中可访问 // 使用伴生对象存储配置信息,使其在整个应用中可访问
companion object { companion object {
// Azure 语音服务配置 // Azure 语音服务配置
var azureSpeechKey: String = "" var azureSpeechKey: String = "2K2VOHSuL8ZSEnbKgHSyTwo1ohj7C2gbxyVimsjpVUt6aETxVwlvJQQJ99BCACqBBLyXJ3w3AAAYACOGqCMj"
var azureSpeechRegion: String = "" var azureSpeechRegion: String = "southeastasia"
var volcanoAiApiKey: String = "" var volcanoAiApiKey: String = "0fd7c93b-b9bf-4b0b-bca6-e284491792bf"
} }
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
@ -43,16 +79,126 @@ class MainActivity: FlutterActivity() {
azureAsrHelper = AzureAsrHelper() azureAsrHelper = AzureAsrHelper()
azureTtsHelper = AzureTtsHelper() azureTtsHelper = AzureTtsHelper()
// 注册广播接收器
registerVoiceInteractionReceiver()
// 不再自动启动服务,由用户通过 Flutter 界面手动启动 // 不再自动启动服务,由用户通过 Flutter 界面手动启动
Log.d(TAG, "MainActivity 已创建,但不会自动启动 VoiceInteractionService") Log.d(TAG, "MainActivity 已创建,但不会自动启动 VoiceInteractionService")
// 确保 Flutter 引擎准备好后立即设置事件通道
flutterEngine?.let { engine ->
Log.d(TAG, "Flutter 引擎已存在,立即设置事件通道")
setupEventChannels(engine)
}
}
/**
* 注册语音交互服务的广播接收器
*/
private fun registerVoiceInteractionReceiver() {
val intentFilter = IntentFilter().apply {
addAction(VoiceInteractionService.ACTION_RECOGNITION_STARTED)
addAction(VoiceInteractionService.ACTION_CHAT_HISTORY_UPDATED)
}
registerReceiver(voiceInteractionReceiver, intentFilter)
} }
override fun configureFlutterEngine(flutterEngine: FlutterEngine) { override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine) Log.d(TAG, "开始配置 Flutter 引擎")
// 先设置事件通道,确保在插件注册之前就已准备好
Log.d(TAG, "在插件注册前设置事件通道")
setupEventChannels(flutterEngine)
// 注册所有插件 // 然后调用父类方法和注册插件
super.configureFlutterEngine(flutterEngine)
GeneratedPluginRegistrant.registerWith(flutterEngine) GeneratedPluginRegistrant.registerWith(flutterEngine)
// 设置方法通道
setupMethodChannels(flutterEngine)
Log.d(TAG, "Flutter 引擎配置完成")
}
/**
* 设置事件通道
*/
private fun setupEventChannels(flutterEngine: FlutterEngine) {
Log.d(TAG, "开始设置事件通道")
try {
// 语音交互事件通道
Log.d(TAG, "设置语音交互事件通道")
EventChannel(flutterEngine.dartExecutor.binaryMessenger, VOICE_INTERACTION_EVENT_CHANNEL).setStreamHandler(
object : EventChannel.StreamHandler {
override fun onListen(arguments: Any?, events: EventChannel.EventSink?) {
Log.d(TAG, "语音交互事件通道开始监听,arguments: $arguments")
voiceInteractionEventSink = events
// 通知 Flutter 端事件通道已准备好
try {
events?.success(mapOf("type" to "channelReady"))
Log.d(TAG, "已发送 channelReady 事件")
// 测试发送一个聊天记录事件
Handler(Looper.getMainLooper()).postDelayed({
if (voiceInteractionEventSink != null) {
Log.d(TAG, "发送测试聊天记录事件")
sendChatHistoryEvent(
"personal_assistant",
"这是一条测试用户消息",
"这是一条测试助手回复",
System.currentTimeMillis()
)
} else {
Log.e(TAG, "无法发送测试事件:事件通道未准备好")
}
}, 5000) // 5秒后发送测试事件
} catch (e: Exception) {
Log.e(TAG, "发送 channelReady 事件失败: ${e.message}", e)
}
}
override fun onCancel(arguments: Any?) {
Log.d(TAG, "语音交互事件通道取消监听,arguments: $arguments")
voiceInteractionEventSink = null
}
}
)
Log.d(TAG, "语音交互事件通道设置完成")
// Azure ASR 事件通道
Log.d(TAG, "设置 Azure ASR 事件通道")
EventChannel(flutterEngine.dartExecutor.binaryMessenger, AZURE_ASR_EVENT_CHANNEL).setStreamHandler(
object : EventChannel.StreamHandler {
override fun onListen(arguments: Any?, events: EventChannel.EventSink?) {
Log.d(TAG, "ASR事件通道开始监听,arguments: $arguments")
azureAsrEventSink = events
// 通知 Flutter 端事件通道已准备好
events?.success(mapOf("type" to "channelReady"))
Log.d(TAG, "已发送 ASR channelReady 事件")
}
override fun onCancel(arguments: Any?) {
Log.d(TAG, "ASR事件通道取消监听,arguments: $arguments")
azureAsrEventSink = null
}
}
)
Log.d(TAG, "Azure ASR 事件通道设置完成")
} catch (e: Exception) {
Log.e(TAG, "设置事件通道时发生错误: ${e.message}", e)
}
Log.d(TAG, "所有事件通道设置完成")
}
/**
* 设置方法通道
*/
private fun setupMethodChannels(flutterEngine: FlutterEngine) {
// 音频处理通道 // 音频处理通道
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, AUDIO_PROCESSING_CHANNEL).setMethodCallHandler { call, result -> MethodChannel(flutterEngine.dartExecutor.binaryMessenger, AUDIO_PROCESSING_CHANNEL).setMethodCallHandler { call, result ->
// 为所有方法返回默认值或成功状态 // 为所有方法返回默认值或成功状态
@ -195,24 +341,6 @@ class MainActivity: FlutterActivity() {
} }
} }
// Azure ASR 事件通道
EventChannel(flutterEngine.dartExecutor.binaryMessenger, AZURE_ASR_EVENT_CHANNEL).setStreamHandler(
object : EventChannel.StreamHandler {
override fun onListen(arguments: Any?, events: EventChannel.EventSink?) {
Log.d(TAG, "ASR事件通道开始监听")
azureAsrEventSink = events
// 通知 Flutter 端事件通道已准备好
events?.success(mapOf("type" to "channelReady"))
}
override fun onCancel(arguments: Any?) {
Log.d(TAG, "ASR事件通道取消监听")
azureAsrEventSink = null
}
}
)
// Azure TTS 方法通道 // Azure TTS 方法通道
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, AZURE_TTS_CHANNEL).setMethodCallHandler { call, result -> MethodChannel(flutterEngine.dartExecutor.binaryMessenger, AZURE_TTS_CHANNEL).setMethodCallHandler { call, result ->
when (call.method) { when (call.method) {
@ -286,24 +414,7 @@ class MainActivity: FlutterActivity() {
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, VOICE_INTERACTION_CHANNEL).setMethodCallHandler { call, result -> MethodChannel(flutterEngine.dartExecutor.binaryMessenger, VOICE_INTERACTION_CHANNEL).setMethodCallHandler { call, result ->
when (call.method) { when (call.method) {
"startVoiceInteractionService" -> { "startVoiceInteractionService" -> {
// 获取配置参数 startVoiceInteractionService(call, result)
val azureSpeechKey = call.argument<String>("azure_speech_key") ?: ""
val azureSpeechRegion = call.argument<String>("azure_speech_region") ?: ""
val volcanoAiApiKey = call.argument<String>("volcano_ai_api_key") ?: ""
// 保存配置到静态变量
MainActivity.azureSpeechKey = azureSpeechKey
MainActivity.azureSpeechRegion = azureSpeechRegion
MainActivity.volcanoAiApiKey = volcanoAiApiKey
// 打印日志
Log.d(TAG, "已保存配置到静态变量:")
Log.d(TAG, "azure_speech_key: ${if (azureSpeechKey.isNotEmpty()) azureSpeechKey.take(5) + "..." else "空"}")
Log.d(TAG, "azure_speech_region: $azureSpeechRegion")
Log.d(TAG, "volcano_ai_api_key: ${if (volcanoAiApiKey.isNotEmpty()) volcanoAiApiKey.take(5) + "..." else "空"}")
startVoiceInteractionService()
result.success(true)
} }
"stopVoiceInteractionService" -> { "stopVoiceInteractionService" -> {
stopVoiceInteractionService() stopVoiceInteractionService()
@ -312,6 +423,10 @@ class MainActivity: FlutterActivity() {
"isVoiceInteractionServiceRunning" -> { "isVoiceInteractionServiceRunning" -> {
result.success(VoiceInteractionService.isServiceRunning()) result.success(VoiceInteractionService.isServiceRunning())
} }
"pauseVoiceInteraction" -> {
pauseVoiceInteraction()
result.success(true)
}
else -> { else -> {
result.notImplemented() result.notImplemented()
} }
@ -347,35 +462,121 @@ class MainActivity: FlutterActivity() {
} }
/** /**
* 启动语音交互服务 * 启动语音交互服务(带配置参数)
*/ */
private fun startVoiceInteractionService() { private fun startVoiceInteractionService(call: MethodCall, result: MethodChannel.Result) {
Log.d(TAG, "准备启动语音交互服务") Log.d(TAG, "启动语音交互服务(带配置参数)")
val serviceIntent = Intent(this, VoiceInteractionService::class.java)
// 添加 ACTION_MEDIA_BUTTON 动作,确保服务能够接收媒体按钮事件 // 获取配置参数
serviceIntent.action = Intent.ACTION_MEDIA_BUTTON val key = call.argument<String>("azure_speech_key") ?: ""
val region = call.argument<String>("azure_speech_region") ?: ""
val aiKey = call.argument<String>("volcano_ai_api_key") ?: ""
// 确保明确设置组件 // 设置 Azure Speech 配置
serviceIntent.component = ComponentName(packageName, VoiceInteractionService::class.java.name) azureSpeechKey = key
azureSpeechRegion = region
volcanoAiApiKey = aiKey
// 输出配置信息 Log.d(TAG, "配置参数: key=${if (key.isNotEmpty()) key.take(5) + "..." else "空"}, region=$region, aiKey=${if (aiKey.isNotEmpty()) aiKey.take(5) + "..." else "空"}")
Log.d(TAG, "启动服务时的配置信息: subscriptionKey=${if (azureSpeechKey.isNotEmpty()) azureSpeechKey.take(5) + "..." else "空"}, serviceRegion=$azureSpeechRegion")
Log.d(TAG, "启动服务: $serviceIntent") // 创建 Intent 并启动服务
startService(serviceIntent) val serviceIntent = Intent(this, VoiceInteractionService::class.java)
Log.d(TAG, "语音交互服务启动完成")
// 在 Android O 及以上版本,需要使用 startForegroundService
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
startForegroundService(serviceIntent)
} else {
startService(serviceIntent)
}
Log.d(TAG, "语音交互服务启动命令已发送")
result.success(true)
} }
/** /**
* 停止语音交互服务 * 停止语音交互服务
*/ */
private fun stopVoiceInteractionService() { private fun stopVoiceInteractionService() {
Log.d(TAG, "停止语音交互服务")
val serviceIntent = Intent(this, VoiceInteractionService::class.java) val serviceIntent = Intent(this, VoiceInteractionService::class.java)
stopService(serviceIntent) stopService(serviceIntent)
Log.d(TAG, "语音交互服务停止命令已发送")
}
/**
* 暂停语音交互(停止TTS和语音识别,但保持服务运行)
*/
private fun pauseVoiceInteraction() {
Log.d(TAG, "发送暂停语音交互请求")
val intent = Intent(this, VoiceInteractionService::class.java).apply {
action = VoiceInteractionService.ACTION_PAUSE_VOICE_INTERACTION
}
// 在 Android O 及以上版本,需要使用 startForegroundService
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
startForegroundService(intent)
} else {
startService(intent)
}
}
/**
* 发送聊天记录事件到 Flutter
*/
private fun sendChatHistoryEvent(agentId: String, userMessage: String, assistantMessage: String, timestamp: Long) {
if (voiceInteractionEventSink == null) {
Log.e(TAG, "无法发送聊天记录事件:事件通道未准备好")
return
}
Log.d(TAG, "发送聊天记录事件到Flutter: agentId=$agentId, userMessage=${userMessage.take(20)}..., assistantMessage=${assistantMessage.take(20)}...")
runOnUiThread {
try {
val eventData = mapOf(
"type" to "chatHistory",
"agentId" to agentId,
"userMessage" to userMessage,
"assistantMessage" to assistantMessage,
"timestamp" to timestamp
)
voiceInteractionEventSink?.success(eventData)
Log.d(TAG, "聊天记录事件已发送到Flutter")
} catch (e: Exception) {
Log.e(TAG, "发送聊天记录事件到Flutter失败: ${e.message}")
}
}
}
/**
* 发送语音交互事件到 Flutter
*/
private fun sendVoiceInteractionEvent(event: Map<String, Any>) {
if (voiceInteractionEventSink == null) {
Log.e(TAG, "无法发送语音交互事件:事件通道未准备好,事件内容: $event")
return
}
Log.d(TAG, "发送语音交互事件到Flutter: $event")
runOnUiThread {
try {
voiceInteractionEventSink?.success(event)
Log.d(TAG, "语音交互事件已发送到Flutter")
} catch (e: Exception) {
Log.e(TAG, "发送语音交互事件到Flutter失败: ${e.message}", e)
}
}
} }
override fun onDestroy() { override fun onDestroy() {
// 注销广播接收器
try {
unregisterReceiver(voiceInteractionReceiver)
} catch (e: Exception) {
Log.e(TAG, "注销广播接收器失败: ${e.message}")
}
azureAsrHelper.dispose() azureAsrHelper.dispose()
azureTtsHelper.dispose() azureTtsHelper.dispose()
super.onDestroy() super.onDestroy()

925
android/app/src/main/kotlin/com/example/deep_voice/VoiceInteractionService.kt

File diff suppressed because it is too large

40
android/app/src/main/kotlin/com/example/deep_voice/VolcanoAIService.kt

@ -1,6 +1,5 @@
package com.example.deep_voice package com.example.deep_voice
import android.content.Context
import android.util.Log import android.util.Log
import okhttp3.* import okhttp3.*
import okhttp3.MediaType.Companion.toMediaTypeOrNull import okhttp3.MediaType.Companion.toMediaTypeOrNull
@ -16,7 +15,7 @@ import java.util.concurrent.TimeUnit
* *
* 参考Flutter端的VolcanoAIService实现,提供同步和异步的API调用方式 * 参考Flutter端的VolcanoAIService实现,提供同步和异步的API调用方式
*/ */
class VolcanoAIService(private val context: Context) { class VolcanoAIService() {
private val TAG = "VolcanoAIService" private val TAG = "VolcanoAIService"
private val baseUrl = "https://ark.cn-beijing.volces.com/api/v3" private val baseUrl = "https://ark.cn-beijing.volces.com/api/v3"
private val chatEndpoint = "/chat/completions" private val chatEndpoint = "/chat/completions"
@ -26,16 +25,26 @@ class VolcanoAIService(private val context: Context) {
.writeTimeout(30, TimeUnit.SECONDS) .writeTimeout(30, TimeUnit.SECONDS)
.build() .build()
private val apiKey: String private var apiKey: String = ""
get() { private var isInitialized = false
// 从 MainActivity 的静态变量获取 API Key
return MainActivity.volcanoAiApiKey
}
init { /**
if (apiKey.isEmpty()) { * 初始化火山AI服务
Log.e(TAG, "火山AI配置信息不完整,请检查环境变量") *
* @param apiKey 火山AI API密钥
* @return 初始化是否成功
*/
fun initialize(apiKey: String): Boolean {
this.apiKey = apiKey
isInitialized = apiKey.isNotEmpty()
if (!isInitialized) {
Log.e(TAG, "初始化失败:API key 不能为空")
} else {
Log.d(TAG, "火山AI服务初始化成功")
} }
return isInitialized
} }
/** /**
@ -84,6 +93,11 @@ class VolcanoAIService(private val context: Context) {
*/ */
@Throws(VolcanoAIException::class) @Throws(VolcanoAIException::class)
fun sendMessage(messages: JSONArray, systemPrompt: String): String { fun sendMessage(messages: JSONArray, systemPrompt: String): String {
// 检查是否已初始化
if (!isInitialized || apiKey.isEmpty()) {
throw VolcanoAIException("火山AI服务未初始化或API key为空,请先调用initialize方法")
}
val fullMessages = JSONArray().apply { val fullMessages = JSONArray().apply {
put(JSONObject().apply { put(JSONObject().apply {
put("role", "system") put("role", "system")
@ -150,6 +164,12 @@ class VolcanoAIService(private val context: Context) {
* @param callback 回调函数,用于接收流式输出的结果 * @param callback 回调函数,用于接收流式输出的结果
*/ */
fun sendMessageStream(messages: JSONArray, systemPrompt: String, callback: StreamCallback) { fun sendMessageStream(messages: JSONArray, systemPrompt: String, callback: StreamCallback) {
// 检查是否已初始化
if (!isInitialized || apiKey.isEmpty()) {
callback.onError(VolcanoAIException("火山AI服务未初始化或API key为空,请先调用initialize方法"))
return
}
val fullMessages = JSONArray().apply { val fullMessages = JSONArray().apply {
put(JSONObject().apply { put(JSONObject().apply {
put("role", "system") put("role", "system")

6
lib/core/bindings/initial_binding.dart

@ -4,11 +4,13 @@ import '../../data/services/azure_tts_service.dart';
import '../../data/services/chat_history_service.dart'; import '../../data/services/chat_history_service.dart';
import '../../data/services/volcano_translation_service.dart'; import '../../data/services/volcano_translation_service.dart';
import '../../data/services/voice_interaction_service.dart'; import '../../data/services/voice_interaction_service.dart';
import '../../core/utils/logger.dart';
/// 初始绑定,用于管理全局依赖 /// 初始绑定,用于管理全局依赖
class InitialBinding extends Bindings { class InitialBinding extends Bindings {
@override @override
void dependencies() { void dependencies() {
Logger.warning('InitialBinding dependencies');
// Azure 语音识别服务 // Azure 语音识别服务
Get.lazyPut<AzureAsrService>(() => AzureAsrService(), fenix: true); Get.lazyPut<AzureAsrService>(() => AzureAsrService(), fenix: true);
@ -21,7 +23,7 @@ class InitialBinding extends Bindings {
// 火山翻译服务 // 火山翻译服务
Get.lazyPut<VolcanoTranslationService>(() => VolcanoTranslationService(), fenix: true); Get.lazyPut<VolcanoTranslationService>(() => VolcanoTranslationService(), fenix: true);
// 语音交互服务 // 语音交互服务 - 使用 putAsync 确保 init 方法被调用
Get.putAsync<VoiceInteractionService>(() => VoiceInteractionService().init()); Get.putAsync<VoiceInteractionService>(() => VoiceInteractionService().init(), permanent: true);
} }
} }

4
lib/data/services/azure_asr_service.dart

@ -42,8 +42,8 @@ class AzureAsrService {
/// 从环境变量加载配置 /// 从环境变量加载配置
void _loadConfig() { void _loadConfig() {
_subscriptionKey = dotenv.env['AZURE_ASR_SUBSCRIPTION_KEY'] ?? ''; _subscriptionKey = dotenv.env['AZURE_SPEECH_KEY'] ?? '';
_serviceRegion = dotenv.env['AZURE_ASR_SERVICE_REGION'] ?? ''; _serviceRegion = dotenv.env['AZURE_SPEECH_REGION'] ?? '';
if (_subscriptionKey.isEmpty || _serviceRegion.isEmpty) { if (_subscriptionKey.isEmpty || _serviceRegion.isEmpty) {
throw Exception('未找到 Azure 语音服务配置。请在 .env 文件中设置 AZURE_SPEECH_KEY 和 AZURE_SPEECH_REGION'); throw Exception('未找到 Azure 语音服务配置。请在 .env 文件中设置 AZURE_SPEECH_KEY 和 AZURE_SPEECH_REGION');

4
lib/data/services/azure_tts_service.dart

@ -50,8 +50,8 @@ class AzureTtsService extends GetxService {
/// 从环境变量加载配置 /// 从环境变量加载配置
void _loadConfig() { void _loadConfig() {
_subscriptionKey = dotenv.env['AZURE_ASR_SUBSCRIPTION_KEY'] ?? ''; _subscriptionKey = dotenv.env['AZURE_SPEECH_KEY'] ?? '';
_serviceRegion = dotenv.env['AZURE_ASR_SERVICE_REGION'] ?? ''; _serviceRegion = dotenv.env['AZURE_SPEECH_REGION'] ?? '';
} }
/// 初始化TTS服务 /// 初始化TTS服务

3
lib/data/services/chat_history_service.dart

@ -39,6 +39,9 @@ class ChatHistoryService extends GetxService {
final jsonList = messagesToSave.map((m) => m.toJson()).toList(); final jsonList = messagesToSave.map((m) => m.toJson()).toList();
final jsonString = jsonEncode(jsonList); final jsonString = jsonEncode(jsonList);
// 保存到存储
await _storage.write(_getHistoryKey(agentId), jsonString);
Logger.info('已保存聊天记录,代理: $agentId,消息数: ${messagesToSave.length}');
} catch (e) { } catch (e) {
Logger.error('保存聊天记录失败', e); Logger.error('保存聊天记录失败', e);

178
lib/data/services/flutter_asr_service.dart

@ -1,8 +1,9 @@
import 'dart:async'; import 'dart:async';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:speech_to_text/speech_to_text.dart'; // 移除 speech_to_text 依赖
import 'package:speech_to_text/speech_recognition_result.dart'; // import 'package:speech_to_text/speech_to_text.dart';
// import 'package:speech_to_text/speech_recognition_result.dart';
import '../../core/utils/logger.dart'; import '../../core/utils/logger.dart';
/// ASR状态枚举 /// ASR状态枚举
@ -14,11 +15,25 @@ enum AsrState {
error // 错误状态 error // 错误状态
} }
/// 监听模式枚举(与 speech_to_text 兼容)
enum ListenMode {
deviceDefault,
dictation,
search,
confirmation,
}
/// 本地名称类(与 speech_to_text 兼容)
class LocaleName {
final String name;
final String localeId;
LocaleName(this.name, this.localeId);
}
/// Flutter ASR服务,负责本地语音转文本功能 /// Flutter ASR服务,负责本地语音转文本功能
/// 注意:此实现为空实现,不再依赖 speech_to_text 插件
class FlutterAsrService extends GetxService { class FlutterAsrService extends GetxService {
// 语音识别实例
late SpeechToText _speech;
// 当前ASR状态 // 当前ASR状态
final Rx<AsrState> _asrState = AsrState.notInitialized.obs; final Rx<AsrState> _asrState = AsrState.notInitialized.obs;
AsrState get asrState => _asrState.value; AsrState get asrState => _asrState.value;
@ -44,7 +59,8 @@ class FlutterAsrService extends GetxService {
bool get isAvailable => _isAvailable.value; bool get isAvailable => _isAvailable.value;
// 是否正在监听 // 是否正在监听
bool get isListening => _speech.isListening; bool _isListening = false;
bool get isListening => _isListening;
// 识别结果流 // 识别结果流
final _recognitionResultsController = StreamController<String>.broadcast(); final _recognitionResultsController = StreamController<String>.broadcast();
@ -58,37 +74,16 @@ class FlutterAsrService extends GetxService {
/// 初始化语音识别 /// 初始化语音识别
Future<void> _initSpeech() async { Future<void> _initSpeech() async {
_speech = SpeechToText();
try { try {
final available = await _speech.initialize( // 空实现,不再使用 speech_to_text
onStatus: _onStatusChanged, _isAvailable.value = false;
onError: _onErrorListener, _asrState.value = AsrState.error;
debugLogging: kDebugMode,
); // 添加一个默认语言
_locales.add(LocaleName('中文 (简体)', 'zh-CN'));
_isAvailable.value = available; _currentLocale.value = 'zh-CN';
if (available) { Logger.info('FlutterAsrService 已初始化(空实现)');
_asrState.value = AsrState.initialized;
// 获取可用的语言
final locales = await _speech.locales();
_locales.assignAll(locales);
// 默认选择系统语言,如果支持的话
final systemLocale = await _speech.systemLocale();
if (systemLocale != null) {
_currentLocale.value = systemLocale.localeId;
} else if (_locales.isNotEmpty) {
_currentLocale.value = _locales.first.localeId;
}
Logger.info('语音识别初始化成功,可用语言数量: ${_locales.length}');
} else {
_asrState.value = AsrState.error;
Logger.error('语音识别不可用');
}
} catch (e) { } catch (e) {
_asrState.value = AsrState.error; _asrState.value = AsrState.error;
_isAvailable.value = false; _isAvailable.value = false;
@ -110,68 +105,25 @@ class FlutterAsrService extends GetxService {
bool onDevice = false, bool onDevice = false,
ListenMode listenMode = ListenMode.deviceDefault, ListenMode listenMode = ListenMode.deviceDefault,
}) async { }) async {
if (!_isAvailable.value) { // 空实现,不再使用 speech_to_text
Logger.error('语音识别不可用,无法开始监听'); Logger.error('FlutterAsrService 为空实现,不支持语音识别');
return false; return false;
}
// 清空上次的识别结果
_lastWords.value = '';
_confidence.value = 0.0;
try {
final started = await _speech.listen(
onResult: _onSpeechResult,
listenFor: listenFor,
pauseFor: pauseFor,
localeId: _currentLocale.value,
listenOptions: SpeechListenOptions(
partialResults: partialResults,
onDevice: onDevice,
listenMode: listenMode,
),
);
if (started) {
_asrState.value = AsrState.listening;
Logger.info('开始语音识别监听');
} else {
Logger.error('开始语音识别监听失败');
}
return started;
} catch (e) {
Logger.error('开始语音识别监听出错: $e');
return false;
}
} }
/// 停止监听 /// 停止监听
Future<void> stopListening() async { Future<void> stopListening() async {
if (!_speech.isListening) return; // 空实现,不再使用 speech_to_text
_isListening = false;
try { _asrState.value = AsrState.notListening;
await _speech.stop();
_asrState.value = AsrState.notListening;
Logger.info('停止语音识别监听');
} catch (e) {
Logger.error('停止语音识别监听出错: $e');
}
} }
/// 取消监听 /// 取消监听
Future<void> cancelListening() async { Future<void> cancelListening() async {
if (!_speech.isListening) return; // 空实现,不再使用 speech_to_text
_isListening = false;
try { _lastWords.value = '';
await _speech.cancel(); _confidence.value = 0.0;
_lastWords.value = ''; _asrState.value = AsrState.notListening;
_confidence.value = 0.0;
_asrState.value = AsrState.notListening;
Logger.info('取消语音识别监听');
} catch (e) {
Logger.error('取消语音识别监听出错: $e');
}
} }
/// 设置当前语言 /// 设置当前语言
@ -182,54 +134,8 @@ class FlutterAsrService extends GetxService {
} }
} }
/// 语音识别结果回调
void _onSpeechResult(SpeechRecognitionResult result) {
final resultText = result.recognizedWords;
_lastWords.value = resultText;
if (result.finalResult) {
// 计算置信度 - 仅在最终结果时有效
if (result.alternates.isNotEmpty) {
_confidence.value = result.alternates.first.confidence;
}
// 发送最终结果到流
_recognitionResultsController.add(resultText);
Logger.info('语音识别最终结果: "$resultText", 置信度: ${_confidence.value}');
_asrState.value = AsrState.notListening;
} else {
// 部分结果
Logger.info('语音识别部分结果: "$resultText"');
}
}
/// 状态变化回调
void _onStatusChanged(String status) {
Logger.info('语音识别状态变化: $status');
switch (status) {
case 'listening':
_asrState.value = AsrState.listening;
break;
case 'notListening':
_asrState.value = AsrState.notListening;
break;
case 'done':
_asrState.value = AsrState.notListening;
break;
}
}
/// 错误回调
void _onErrorListener(dynamic error) {
Logger.error('语音识别错误: ${error.errorMsg}');
_asrState.value = AsrState.error;
}
@override @override
void onClose() { void onClose() {
_speech.cancel();
_recognitionResultsController.close(); _recognitionResultsController.close();
super.onClose(); super.onClose();
} }

214
lib/data/services/voice_interaction_service.dart

@ -1,6 +1,11 @@
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart'; import 'package:flutter_dotenv/flutter_dotenv.dart';
import '../services/azure_asr_service.dart';
import '../services/azure_tts_service.dart';
import '../services/chat_history_service.dart';
import '../../modules/chat/models/message_model.dart';
import '../../core/utils/logger.dart';
/// 语音交互服务,用于管理原生的 VoiceInteractionService /// 语音交互服务,用于管理原生的 VoiceInteractionService
class VoiceInteractionService extends GetxService { class VoiceInteractionService extends GetxService {
@ -10,37 +15,187 @@ class VoiceInteractionService extends GetxService {
final _isRunning = false.obs; final _isRunning = false.obs;
bool get isRunning => _isRunning.value; bool get isRunning => _isRunning.value;
// 语音识别状态
final _isRecognitionActive = false.obs;
bool get isRecognitionActive => _isRecognitionActive.value;
// 聊天记录 - 可被 ChatView 监听
final RxList<Message> personalAssistantMessages = <Message>[].obs;
// 方法通道 // 方法通道
final MethodChannel _channel = const MethodChannel('com.deep_voice.voice_interaction'); final MethodChannel _channel = const MethodChannel('com.deep_voice.voice_interaction');
// 事件通道
final EventChannel _eventChannel = const EventChannel('com.deep_voice.voice_interaction_events');
// 事件流订阅
Stream<dynamic>? _eventStream;
// 初始化标记,避免重复初始化
static bool _isInitialized = false;
/// 初始化服务 /// 初始化服务
Future<VoiceInteractionService> init() async { Future<VoiceInteractionService> init() async {
// 获取服务运行状态 // 如果已经初始化过,直接返回
await _updateServiceStatus(); if (_isInitialized) {
return this; return this;
}
try {
// 获取服务运行状态
await _updateServiceStatus();
// 设置事件监听
_setupEventChannel();
// 加载个人助手的历史记录
_loadPersonalAssistantHistory();
// 标记为已初始化
_isInitialized = true;
return this;
} catch (e) {
Logger.error('VoiceInteractionService 初始化失败', e);
return this;
}
}
/// 加载个人助手的历史记录
void _loadPersonalAssistantHistory() {
if (Get.isRegistered<ChatHistoryService>()) {
final historyService = Get.find<ChatHistoryService>();
final messages = historyService.loadHistory('personal_assistant');
if (messages.isNotEmpty) {
personalAssistantMessages.assignAll(messages);
}
}
}
/// 设置事件通道监听
void _setupEventChannel() {
try {
_eventStream = _eventChannel.receiveBroadcastStream();
_eventStream?.listen(
_handleEvent,
onError: (error) {
Logger.error('语音交互事件通道错误', error);
}
);
} catch (e) {
Logger.error('设置事件通道监听失败', e);
}
}
/// 处理来自原生端的事件
void _handleEvent(dynamic event) {
try {
if (event is! Map) {
return;
}
final type = event['type'] as String?;
if (type == null) {
return;
}
switch (type) {
case 'channelReady':
break;
case 'recognitionStarted':
_isRecognitionActive.value = true;
// 检测到后台服务的语音识别开始运行,主动停止Flutter端的语音识别和TTS
// _stopFlutterVoiceServices();
break;
case 'chatHistory':
// 处理聊天记录
_handleChatHistory(event);
break;
}
} catch (e) {
Logger.error('处理原生端事件时出错', e);
}
}
/// 处理聊天记录事件
void _handleChatHistory(Map<dynamic, dynamic> event) {
try {
final agentId = event['agentId'] as String?;
final userMessage = event['userMessage'] as String?;
final assistantMessage = event['assistantMessage'] as String?;
final timestamp = event['timestamp'] as int?;
if (agentId == null || userMessage == null || assistantMessage == null || timestamp == null) {
return;
}
// 只处理个人助手的聊天记录
if (agentId != 'personal_assistant') {
return;
}
// 添加用户消息
personalAssistantMessages.add(Message(
role: 'user',
content: userMessage,
timestamp: DateTime.fromMillisecondsSinceEpoch(timestamp),
));
// 添加助手消息
personalAssistantMessages.add(Message(
role: 'assistant',
content: assistantMessage,
timestamp: DateTime.fromMillisecondsSinceEpoch(timestamp + 1000), // 助手消息时间戳稍后
));
// 保存到历史记录
if (Get.isRegistered<ChatHistoryService>()) {
final historyService = Get.find<ChatHistoryService>();
historyService.saveHistory('personal_assistant', personalAssistantMessages);
}
} catch (e) {
Logger.error('处理聊天记录事件失败', e);
}
}
/// 停止 Flutter 端的语音服务
void _stopFlutterVoiceServices() {
// 停止 TTS
if (Get.isRegistered<AzureTtsService>()) {
Get.find<AzureTtsService>().stop();
}
// 停止 ASR
if (Get.isRegistered<AzureAsrService>()) {
Get.find<AzureAsrService>().stopContinuousRecognition();
}
} }
/// 启动语音交互服务 /// 启动语音交互服务
Future<bool> startService() async { Future<bool> startService() async {
try { try {
// 获取配置信息 // 获取 Azure 配置
final speechKey = dotenv.env['AZURE_ASR_SUBSCRIPTION_KEY'] ?? ''; final azureKey = dotenv.env['AZURE_SPEECH_KEY'] ?? '';
final speechRegion = dotenv.env['AZURE_ASR_SERVICE_REGION'] ?? ''; final azureRegion = dotenv.env['AZURE_SPEECH_REGION'] ?? '';
// 从 .env 读取火山 AI API Key // 获取火山 AI 配置
final aiApiKey = dotenv.env['VOLCANO_AI_API_KEY'] ?? ''; final volcanoApiKey = dotenv.env['VOLCANO_AI_API_KEY'] ?? '';
// 调用原生方法,传递配置参数 // 启动服务
final result = await _channel.invokeMethod<bool>('startVoiceInteractionService', { final result = await _channel.invokeMethod<bool>('startVoiceInteractionService', {
'azure_speech_key': speechKey, 'azure_speech_key': azureKey,
'azure_speech_region': speechRegion, 'azure_speech_region': azureRegion,
'volcano_ai_api_key': aiApiKey, 'volcano_ai_api_key': volcanoApiKey,
}) ?? false; }) ?? false;
await _updateServiceStatus(); // 更新状态
if (result) {
await _updateServiceStatus();
}
return result; return result;
} catch (e) { } catch (e) {
print('启动语音交互服务失败: $e'); Logger.error('启动语音交互服务失败', e);
return false; return false;
} }
} }
@ -49,22 +204,45 @@ class VoiceInteractionService extends GetxService {
Future<bool> stopService() async { Future<bool> stopService() async {
try { try {
final result = await _channel.invokeMethod<bool>('stopVoiceInteractionService') ?? false; final result = await _channel.invokeMethod<bool>('stopVoiceInteractionService') ?? false;
await _updateServiceStatus();
// 更新状态
if (result) {
await _updateServiceStatus();
}
return result;
} catch (e) {
Logger.error('停止语音交互服务失败', e);
return false;
}
}
/// 暂停语音交互
Future<bool> pauseVoiceInteraction() async {
try {
final result = await _channel.invokeMethod<bool>('pauseVoiceInteraction') ?? false;
return result; return result;
} catch (e) { } catch (e) {
print('停止语音交互服务失败: $e'); Logger.error('暂停语音交互失败', e);
return false; return false;
} }
} }
/// 更新服务状态 /// 更新服务运行状态
Future<void> _updateServiceStatus() async { Future<void> _updateServiceStatus() async {
try { try {
final status = await _channel.invokeMethod<bool>('isVoiceInteractionServiceRunning') ?? false; final status = await _channel.invokeMethod<bool>('isVoiceInteractionServiceRunning') ?? false;
_isRunning.value = status; _isRunning.value = status;
} catch (e) { } catch (e) {
print('获取语音交互服务状态失败: $e'); Logger.error('获取语音交互服务状态失败', e);
_isRunning.value = false; _isRunning.value = false;
} }
} }
@override
void onClose() {
// 清理资源
_eventStream = null;
super.onClose();
}
} }

5
lib/main.dart

@ -71,11 +71,6 @@ class MainApp extends StatelessWidget {
translations: AppTranslations(), translations: AppTranslations(),
locale: const Locale('zh', 'CN'), locale: const Locale('zh', 'CN'),
fallbackLocale: const Locale('en', 'US'), fallbackLocale: const Locale('en', 'US'),
// 在应用初始化完成后,初始化 ConfigService
onInit: () {
// 延迟执行,确保应用已完全启动
},
); );
} }
); );

77
lib/modules/chat/controllers/chat_controller.dart

@ -9,6 +9,7 @@ import '../../../data/providers/agent_provider.dart';
import '../../../data/models/agent.dart'; import '../../../data/models/agent.dart';
import '../../../core/utils/logger.dart'; import '../../../core/utils/logger.dart';
import '../../../data/services/chat_history_service.dart'; import '../../../data/services/chat_history_service.dart';
import '../../../data/services/voice_interaction_service.dart';
class ChatController extends GetxController { class ChatController extends GetxController {
// 服务 // 服务
@ -16,6 +17,7 @@ class ChatController extends GetxController {
final AzureTtsService _ttsService = Get.find<AzureTtsService>(); final AzureTtsService _ttsService = Get.find<AzureTtsService>();
final AzureAsrService _asrService = Get.find<AzureAsrService>(); final AzureAsrService _asrService = Get.find<AzureAsrService>();
final ChatHistoryService _historyService = Get.find<ChatHistoryService>(); final ChatHistoryService _historyService = Get.find<ChatHistoryService>();
final VoiceInteractionService _voiceInteractionService = Get.find<VoiceInteractionService>();
// 添加ScrollController用于控制列表滚动 // 添加ScrollController用于控制列表滚动
final ScrollController scrollController = ScrollController(); final ScrollController scrollController = ScrollController();
@ -47,6 +49,9 @@ class ChatController extends GetxController {
StreamSubscription? _aiResponseSubscription; StreamSubscription? _aiResponseSubscription;
StreamSubscription? _recognitionSubscription; StreamSubscription? _recognitionSubscription;
// 语音交互服务消息监听
Worker? _personalAssistantMessagesWorker;
// 取消标志 // 取消标志
bool _shouldCancelAiResponse = false; bool _shouldCancelAiResponse = false;
@ -55,6 +60,8 @@ class ChatController extends GetxController {
super.onInit(); super.onInit();
Logger.info('ChatController onInit'); Logger.info('ChatController onInit');
// 从路由参数获取Agent信息 // 从路由参数获取Agent信息
final Map<String, dynamic> args = Get.arguments ?? {}; final Map<String, dynamic> args = Get.arguments ?? {};
final String agentId = args['agentId'] ?? 'personal_assistant'; final String agentId = args['agentId'] ?? 'personal_assistant';
@ -69,12 +76,24 @@ class ChatController extends GetxController {
agent = foundAgent; agent = foundAgent;
// 暂停后台语音交互服务,避免与聊天页面的语音功能相互干扰
// if (Get.isRegistered<VoiceInteractionService>()) {
// await VoiceInteractionService.to.pauseVoiceInteraction();
// Logger.info('已暂停后台语音交互服务');
// }
// 设置TTS服务 // 设置TTS服务
await _ttsService.setVoice(agent.voice); await _ttsService.setVoice(agent.voice);
// 加载聊天历史 // 加载聊天历史
_loadChatHistory(); _loadChatHistory();
// 如果是个人助手,监听语音交互服务的消息更新
if (agent.id == 'personal_assistant') {
_setupPersonalAssistantMessagesListener();
}
// 如果没有历史记录,添加欢迎消息 // 如果没有历史记录,添加欢迎消息
if (messages.isEmpty && agent.welcomeMessage.isNotEmpty) { if (messages.isEmpty && agent.welcomeMessage.isNotEmpty) {
messages.add(Message( messages.add(Message(
@ -99,6 +118,37 @@ class ChatController extends GetxController {
}); });
} }
/// 设置个人助手消息监听器
void _setupPersonalAssistantMessagesListener() {
// 取消之前的监听器(如果有)
_personalAssistantMessagesWorker?.dispose();
// 设置新的监听器
_personalAssistantMessagesWorker = ever(
_voiceInteractionService.personalAssistantMessages,
(List<Message> updatedMessages) {
if (agent.id == 'personal_assistant' && updatedMessages.isNotEmpty) {
Logger.info('检测到语音交互服务的个人助手消息更新,同步到聊天视图');
// 中断当前响应
_interruptCurrentResponse("检测到语音交互服务的消息更新,中断当前响应");
// 更新消息列表
messages.assignAll(updatedMessages);
// 滚动到底部
_scrollToBottom();
}
}
);
// 初始同步
if (_voiceInteractionService.personalAssistantMessages.isNotEmpty) {
messages.assignAll(_voiceInteractionService.personalAssistantMessages);
Logger.info('初始同步语音交互服务的个人助手消息,消息数: ${_voiceInteractionService.personalAssistantMessages.length}');
}
}
@override @override
void onClose() { void onClose() {
Logger.info('ChatController onClose'); Logger.info('ChatController onClose');
@ -107,6 +157,7 @@ class ChatController extends GetxController {
scrollController.dispose(); scrollController.dispose();
_aiResponseSubscription?.cancel(); _aiResponseSubscription?.cancel();
_recognitionSubscription?.cancel(); _recognitionSubscription?.cancel();
_personalAssistantMessagesWorker?.dispose();
// 停止TTS // 停止TTS
_ttsService.stop(); _ttsService.stop();
@ -504,10 +555,31 @@ class ChatController extends GetxController {
// 删除存储的历史记录 // 删除存储的历史记录
_historyService.clearHistory(agent.id); _historyService.clearHistory(agent.id);
// 如果是个人助手,同时清空语音交互服务的消息
if (agent.id == 'personal_assistant' && Get.isRegistered<VoiceInteractionService>()) {
_voiceInteractionService.personalAssistantMessages.clear();
_voiceInteractionService.personalAssistantMessages.add(Message(
role: 'assistant',
content: agent.welcomeMessage,
timestamp: DateTime.now(),
));
}
} }
// 加载聊天历史 // 加载聊天历史
void _loadChatHistory() { void _loadChatHistory() {
// 如果是个人助手,优先使用语音交互服务中的消息
if (agent.id == 'personal_assistant' &&
Get.isRegistered<VoiceInteractionService>() &&
_voiceInteractionService.personalAssistantMessages.isNotEmpty) {
messages.clear();
messages.addAll(_voiceInteractionService.personalAssistantMessages);
Logger.info('从语音交互服务加载个人助手的历史记录,消息数: ${messages.length}');
return;
}
// 否则从历史服务加载
final history = _historyService.loadHistory(agent.id); final history = _historyService.loadHistory(agent.id);
if (history.isNotEmpty) { if (history.isNotEmpty) {
messages.clear(); messages.clear();
@ -518,6 +590,11 @@ class ChatController extends GetxController {
// 保存聊天历史 // 保存聊天历史
void _saveChatHistory() { void _saveChatHistory() {
_historyService.saveHistory(agent.id, messages); _historyService.saveHistory(agent.id, messages);
// 如果是个人助手,同时更新语音交互服务的消息
if (agent.id == 'personal_assistant' && Get.isRegistered<VoiceInteractionService>()) {
_voiceInteractionService.personalAssistantMessages.assignAll(messages);
}
} }
// 添加消息,供外部调用 // 添加消息,供外部调用

82
lib/modules/home/controllers/home_controller.dart

@ -1,7 +1,7 @@
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'dart:async'; import 'dart:async';
import 'dart:io';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import '../../../data/services/voice_interaction_service.dart';
enum ConnectStatus { enum ConnectStatus {
initial, // 初始状态,显示添加设备 initial, // 初始状态,显示添加设备
@ -18,10 +18,6 @@ class HomeController extends GetxController {
Timer? _animationTimer; Timer? _animationTimer;
bool _isDisposed = false; bool _isDisposed = false;
// 语音服务状态
final _voiceServiceRunning = false.obs;
bool get voiceServiceRunning => _voiceServiceRunning.value;
ConnectStatus get connectStatus => _connectStatus.value; ConnectStatus get connectStatus => _connectStatus.value;
String get searchDots => _searchDots.value; String get searchDots => _searchDots.value;
@ -29,9 +25,6 @@ class HomeController extends GetxController {
void onInit() { void onInit() {
super.onInit(); super.onInit();
_connectStatus.value = ConnectStatus.initial; _connectStatus.value = ConnectStatus.initial;
// 获取语音服务状态
_updateVoiceServiceStatus();
} }
@override @override
@ -41,79 +34,6 @@ class HomeController extends GetxController {
super.onClose(); super.onClose();
} }
/// 更新语音服务状态
Future<void> _updateVoiceServiceStatus() async {
if (Get.isRegistered<VoiceInteractionService>()) {
_voiceServiceRunning.value = VoiceInteractionService.to.isRunning;
} else {
_voiceServiceRunning.value = false;
}
}
/// 启动语音交互服务
Future<void> startVoiceService() async {
// 确保 ConfigService 已初始化
// 确保 VoiceInteractionService 已初始化
if (!Get.isRegistered<VoiceInteractionService>()) {
await Get.putAsync(() => VoiceInteractionService().init());
}
final result = await VoiceInteractionService.to.startService();
await _updateVoiceServiceStatus();
if (result) {
Get.snackbar(
'成功',
'语音交互服务已启动',
snackPosition: SnackPosition.TOP,
backgroundColor: Colors.green.withOpacity(0.1),
colorText: Colors.green,
duration: const Duration(seconds: 2),
);
} else {
Get.snackbar(
'错误',
'启动语音交互服务失败',
snackPosition: SnackPosition.TOP,
backgroundColor: Colors.red.withOpacity(0.1),
colorText: Colors.red,
duration: const Duration(seconds: 2),
);
}
}
/// 停止语音交互服务
Future<void> stopVoiceService() async {
if (!Get.isRegistered<VoiceInteractionService>()) {
await Get.putAsync(() => VoiceInteractionService().init());
}
final result = await VoiceInteractionService.to.stopService();
await _updateVoiceServiceStatus();
if (result) {
Get.snackbar(
'成功',
'语音交互服务已停止',
snackPosition: SnackPosition.TOP,
backgroundColor: Colors.blue.withOpacity(0.1),
colorText: Colors.blue,
duration: const Duration(seconds: 2),
);
} else {
Get.snackbar(
'错误',
'停止语音交互服务失败',
snackPosition: SnackPosition.TOP,
backgroundColor: Colors.red.withOpacity(0.1),
colorText: Colors.red,
duration: const Duration(seconds: 2),
);
}
}
void startSearchingAnimation() { void startSearchingAnimation() {
stopSearchingAnimation(); // Clear any existing timer stopSearchingAnimation(); // Clear any existing timer

61
lib/modules/home/views/home_view.dart

@ -241,65 +241,6 @@ class HomeView extends GetView<HomeController> {
}); });
} }
/// 构建语音服务控制按钮
Widget _buildVoiceServiceButton() {
return Obx(() {
final isRunning = controller.voiceServiceRunning;
return Container(
width: double.infinity,
height: 56.h,
margin: EdgeInsets.symmetric(horizontal: 20.w),
decoration: BoxDecoration(
gradient: LinearGradient(
colors: isRunning
? [const Color(0xFFE57373), const Color(0xFFEF5350)] // 红色渐变(停止)
: [const Color(0xFF66BB6A), const Color(0xFF4CAF50)], // 绿色渐变(启动)
begin: Alignment.centerLeft,
end: Alignment.centerRight,
),
borderRadius: BorderRadius.circular(28.r),
boxShadow: [
BoxShadow(
color: (isRunning ? const Color(0xFFE57373) : const Color(0xFF66BB6A)).withOpacity(0.3),
offset: const Offset(0, 4),
blurRadius: 12,
),
],
),
child: Material(
color: Colors.transparent,
child: InkWell(
onTap: isRunning ? controller.stopVoiceService : controller.startVoiceService,
borderRadius: BorderRadius.circular(28.r),
child: Center(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
isRunning ? Icons.stop : Icons.play_arrow,
color: Colors.white,
size: 24.sp,
),
SizedBox(width: 12.w),
Text(
isRunning ? '停止语音服务' : '启动语音服务',
style: TextStyle(
fontSize: 18.sp,
fontWeight: FontWeight.w600,
color: Colors.white,
letterSpacing: 0.5,
),
),
],
),
),
),
),
);
});
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
@ -331,8 +272,6 @@ class HomeView extends GetView<HomeController> {
), ),
), ),
_buildActionButton(), _buildActionButton(),
SizedBox(height: 20.h),
_buildVoiceServiceButton(),
SizedBox(height: 40.h), SizedBox(height: 40.h),
], ],
), ),

7
lib/modules/translation/controllers/translation_controller.dart

@ -7,6 +7,7 @@ import '../../../data/services/volcano_translation_service.dart';
import '../../../data/services/azure_asr_service.dart'; import '../../../data/services/azure_asr_service.dart';
import '../../../data/services/azure_tts_service.dart'; import '../../../data/services/azure_tts_service.dart';
import '../../../core/utils/logger.dart'; import '../../../core/utils/logger.dart';
import '../../../data/services/voice_interaction_service.dart';
class TranslationController extends GetxController { class TranslationController extends GetxController {
// Services // Services
@ -80,6 +81,12 @@ class TranslationController extends GetxController {
void onInit() { void onInit() {
super.onInit(); super.onInit();
// 暂停后台语音交互服务,避免与翻译页面的语音功能相互干扰
if (Get.isRegistered<VoiceInteractionService>()) {
VoiceInteractionService.to.pauseVoiceInteraction();
Logger.info('已暂停后台语音交互服务');
}
// 加载历史记录 // 加载历史记录
loadTranslationHistory(); loadTranslationHistory();

5
pubspec.yaml

@ -29,8 +29,8 @@ dependencies:
crypto: ^3.0.3 crypto: ^3.0.3
flutter_tts: ^4.2.2 flutter_tts: ^4.2.2
logger: ^2.0.2+1 logger: ^2.0.2+1
shared_preferences: ^2.2.2 # shared_preferences: ^2.2.2 # 移除,使用 get_storage 代替
# azure_speech_recognition_null_safety: ^0.9.6 # speech_to_text: ^7.0.0 # 移除,使用自定义的 ASR 服务
window_size: window_size:
git: git:
url: https://github.com/google/flutter-desktop-embedding.git url: https://github.com/google/flutter-desktop-embedding.git
@ -42,7 +42,6 @@ dependencies:
record: ^5.2.1 record: ^5.2.1
share_plus: ^10.0.0 share_plus: ^10.0.0
image_picker: ^1.1.2 image_picker: ^1.1.2
speech_to_text: ^7.0.0
audio_waveforms: ^1.3.0 audio_waveforms: ^1.3.0
dev_dependencies: dev_dependencies:

Loading…
Cancel
Save