diff --git a/lib/modules/agent/controllers/agent_controller.dart b/lib/modules/agent/controllers/agent_controller.dart index 93960f0d8..f1a51375c 100644 --- a/lib/modules/agent/controllers/agent_controller.dart +++ b/lib/modules/agent/controllers/agent_controller.dart @@ -4,7 +4,6 @@ import 'dart:io'; import 'package:amap_service/amap_service.dart'; import 'package:deep_voice/data/services/music_manager.dart'; import 'package:deep_voice/data/services/navigation_manager.dart'; -import 'package:deep_voice/routes/app_routes.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:agent_service/agent_service.dart'; @@ -12,32 +11,11 @@ import 'package:chat_storage/chat_storage.dart'; import 'package:image_picker/image_picker.dart'; import 'package:get_storage/get_storage.dart'; import 'package:permission_handler/permission_handler.dart'; -import 'package:app_settings/app_settings.dart'; import 'package:deep_voice/core/utils/logger.dart'; import 'package:path_provider/path_provider.dart'; import 'package:photo_view/photo_view.dart'; import '../../../core/utils/permission_util.dart'; - -class Message { - final bool isUser; //标识信息是否来自用户还是AI - final String text; //信息文本内容 - final DateTime timestamp; // 消息时间戳 - final bool isRecognizing; // 是否为语音识别中的临时消息 - final bool hasImage; // 是否包含图片 - final String? imagePath; // 图片路径 - final Map? meta; //卡片数据 - - Message({ - // 构造函数 - required this.isUser, - required this.text, - DateTime? timestamp, - this.isRecognizing = false, - this.hasImage = false, - this.imagePath, - this.meta, - }) : this.timestamp = timestamp ?? DateTime.now(); -} +import '../../../data/models/message_model.dart'; class AgentController extends GetxController { static AgentController get to => Get.find(); //单例访问模式 @@ -214,7 +192,7 @@ class AgentController extends GetxController { } } - // 添加欢迎消息 + // 添加初始欢迎消息 void _addWelcomeMessage() { messages.add(Message( isUser: false, @@ -418,7 +396,7 @@ class AgentController extends GetxController { break; case AgentServiceEventType.functionCallResult: - final responseId = event.data['responseId'] ?? ''; + final responseId = event.data['responseId'] ?? ''; // final result = event.data['result']; final metaStr = event.data['meta'] ?? ''; // final functionCall = event.data['function_call'] ?? ''; @@ -521,8 +499,10 @@ class AgentController extends GetxController { } // 发送消息(包括所有支持的消息类型) - Future sendTextMessage() async { - // 如果图片输入是活跃的,使用选中的图片和文本框的内容 + //场景1:用户在文本输入框中输入文本,点击发送按钮,调用sendMessage()方法。 + //场景2:当用户选择图片后在文本框输入内容并发送时,调用sendMessage()转发给sendImageMessage()。 + Future sendMessage() async { + // 1. 处理图片+文本的混合输入 if (isImageInputActive.value && selectedImagePath.value != null) { final text = textController.text.trim(); final imagePath = selectedImagePath.value!; // 使用!强制断言非空 @@ -533,6 +513,7 @@ class AgentController extends GetxController { return; } + // 2. 处理纯文本消息 final text = textController.text.trim(); if (text.isEmpty) return; @@ -541,7 +522,7 @@ class AgentController extends GetxController { textController.clear(); // 使用reverse:true时通常不需要手动滚动 - + // 3. 调用AI服务处理 try { isProcessing.value = true; // 标记为新的AI回复 @@ -636,36 +617,6 @@ class AgentController extends GetxController { } } - // 保存消息到持久化存储 - // Future _saveMessageToStorage(Message message) async { - // try { - // // 准备元数据,包含图片信息 - // Map metadata = {}; - // if (message.meta != null) { - // metadata = Map.from(message.meta!); - // } - - // // 添加图片相关信息到元数据 - // if (message.hasImage) { - // metadata['hasImage'] = true; - // metadata['imagePath'] = message.imagePath; - // } - - // // 保存消息 - // await ChatStorage.saveMessage( - // sessionId: sessionId, - // sender: message.isUser ? 'user' : 'assistant', - // message: message.text, - // metadata: metadata.isNotEmpty ? jsonEncode(metadata) : null, - // //timestamp: message.timestamp.toIso8601String(), - // ); - - // Logger.i(TAG, '消息已保存到持久化存储'); - // } catch (e) { - // Logger.e(TAG, '保存消息到持久化存储失败: $e'); - // } - // } - // 切换输入模式 void toggleInputMode() async { // 如果当前在图片输入模式,先清除 @@ -703,156 +654,6 @@ class AgentController extends GetxController { } } -// 检查麦克风权限,返回true表示已授权,false表示未授权 - Future _checkMicrophonePermission() async { - try { - // 1.先获取当前麦克风权限状态 - final micStatus = await Permission.microphone.status; - - // 2.如果麦克风权限已授予,返回true - if (micStatus.isGranted) { - return true; - } - - // 3.创建待请求权限列表(这里只请求麦克风权限) - List permissionsToRequest = []; - permissionsToRequest.add(Permission.microphone); - - // 4.弹出权限说明对话框(首次拒绝/还没授权时展示) - if (micStatus.isPermanentlyDenied) { - _showPermissionDialog(true); - } else { - _showPermissionDialog(false); - } - - // 5.请求权限,并获取返回的权限状态集合 - final statuses = await permissionsToRequest.request(); - - // 6.用于记录被拒绝的权限(便于后续判断) - Permission? deniedPermission; - - // 7.遍历权限状态集合,检查是否有任何权限被拒绝 - statuses.forEach((permission, status) { - if (!status.isGranted) { - deniedPermission = permission; - Logger.e(TAG, '权限 ${permission.toString()} 未被授予: $status'); - } - }); - - // // 8. 如果是“永久拒绝”(用户勾选了“不再询问”) - // if (micStatus.isPermanentlyDenied) { - // _showPermissionDialog(true); - // return false; - // } - - // // 9. 普通的拒绝情况,再次弹出说明对话框 - // if (deniedPermission != null) { - // _showPermissionDialog(false); - // } - - // 10. 如果权限未被授予,返回 false - return false; - } catch (e) { - Logger.e(TAG, '检查麦克风权限失败: $e'); - return false; - } - } - - // 显示权限提示对话框 - void _showPermissionDialog(bool isPermanent) { - Get.generalDialog( - barrierDismissible: false, - barrierLabel: '权限提示', - transitionBuilder: (context, anim1, anim2, child) { - return SlideTransition( - position: Tween( - begin: Offset(0, -1), // 从上方滑入 - end: Offset(0, 0), - ).animate(CurvedAnimation(parent: anim1, curve: Curves.easeOut)), - child: child, - ); - }, - pageBuilder: (context, anim1, anim2) { - return SafeArea( - child: Align( - alignment: Alignment.topCenter, // 关键:设置为顶部对齐 - child: Padding( - padding: const EdgeInsets.only(top: 80, left: 24, right: 24), - child: Material( - borderRadius: BorderRadius.circular(12), - color: Colors.white, - child: Padding( - padding: const EdgeInsets.all(16), - child: ConstrainedBox( - constraints: BoxConstraints(maxWidth: 400), - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const Text('需要麦克风权限', - style: TextStyle( - fontSize: 18, fontWeight: FontWeight.bold)), - const SizedBox(height: 12), - Text(isPermanent - ? '语音功能需要麦克风权限。请前往设置页面手动开启权限。' - : '语音功能需要麦克风权限。请允许使用麦克风权限以继续使用语音功能。'), - const SizedBox(height: 16), - const Text('权限使用说明:', - style: TextStyle(fontWeight: FontWeight.bold)), - const SizedBox(height: 8), - const Text('• 麦克风权限:用于录制您的语音,实现语音翻译、语音助手对话等核心功能'), - const SizedBox(height: 8), - const Text('• 我们仅在您主动使用语音功能时访问麦克风'), - const SizedBox(height: 8), - const Text('• 您可以随时在系统设置中关闭权限'), - const SizedBox(height: 20), - Row( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - TextButton( - onPressed: () => Get.back(), - child: const Text('取消'), - ), - const SizedBox(width: 8), - TextButton( - onPressed: () { - Get.back(); - if (isPermanent) { - AppSettings.openAppSettings(); - } else { - Permission.microphone.request(); - } - }, - child: Text(isPermanent ? '前往设置' : '授权'), - ), - ], - ), - ], - ), - ), - ), - ), - ), - ), - ); - }, - ); - } - - // 获取Android SDK版本 - Future _getAndroidSdkVersion() async { - if (Platform.isAndroid) { - try { - // 这里我们简单返回一个较高的值,确保请求所有可能需要的权限 - // 在实际应用中,你可能需要使用platform channel获取真实的SDK版本 - return 31; // 对应Android 12 - } catch (e) { - Logger.e(TAG, '获取Android SDK版本失败: $e'); - } - } - return 0; - } - // 开始语音输入 Future startVoiceInput() async { if (isListening.value) return; diff --git a/lib/modules/agent/controllers/report_controller.dart b/lib/modules/agent/controllers/report_controller.dart index 0bae0106a..816d8277d 100644 --- a/lib/modules/agent/controllers/report_controller.dart +++ b/lib/modules/agent/controllers/report_controller.dart @@ -1,7 +1,8 @@ import 'package:get/get.dart'; -import 'agent_controller.dart'; // 确保导入Message模型 +//import 'agent_controller.dart'; // 确保导入Message模型 import 'package:flutter/material.dart'; import '../../../data/services/network/api.dart'; +import '../../../data/models/message_model.dart'; class ReportController extends GetxController { // 当前举报的消息 diff --git a/lib/modules/agent/views/agent_view.dart b/lib/modules/agent/views/agent_view.dart index 15044c63c..6ba38309e 100644 --- a/lib/modules/agent/views/agent_view.dart +++ b/lib/modules/agent/views/agent_view.dart @@ -8,6 +8,7 @@ import 'package:flutter/services.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import '../../../core/theme/app_colors.dart'; import 'package:haptic_feedback/haptic_feedback.dart'; +import '../../../data/models/message_model.dart'; class AgentView extends StatelessWidget { const AgentView({Key? key}) : super(key: key); @@ -384,12 +385,12 @@ class AgentView extends StatelessWidget { vertical: isImageActive ? 12 : 0), ), onSubmitted: (_) => - controller.sendTextMessage(), + controller.sendMessage(), ); }), ), GestureDetector( - onTap: () => controller.sendTextMessage(), + onTap: () => controller.sendMessage(), child: Container( padding: const EdgeInsets.all(6), child: Icon(