Browse Source

feat(translation): 添加左滑手势导航至历史记录页面

refactor(translation): 移除火山AI摘要生成相关代码
fix(pairing): 修正华为设备权限检查逻辑
weicu
lxm 1 year ago
parent
commit
d4fedde95e
  1. 10
      lib/modules/pairing/controllers/pairing_controller.dart
  2. 162
      lib/modules/translation/controllers/translation_detail_controller.dart
  3. 352
      lib/modules/translation/views/translation_detail_view.dart
  4. 45
      lib/modules/translation/views/translation_view.dart

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

@ -576,9 +576,15 @@ class PairingController extends GetxController with WidgetsBindingObserver {
/// 检查权限
Future<bool> _checkPermissions() async {
//如果是华为设备,且已经检查过,直接返回false
//如果是华为设备,且已经检查过,直接返回权限状态
if (isHuaweiDevice && isHuaweiHasChecked) {
return false;
final bluetoothScanStatus = await Permission.bluetoothScan.status;
final bluetoothConnectStatus = await Permission.bluetoothConnect.status;
final locationStatus = await Permission.locationWhenInUse.status;
return bluetoothScanStatus.isGranted &&
bluetoothConnectStatus.isGranted &&
locationStatus.isGranted;
}
// Android权限检查

162
lib/modules/translation/controllers/translation_detail_controller.dart

@ -1,6 +1,6 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../../../data/services/volcano_ai_service.dart';
// import '../../../data/services/volcano_ai_service.dart';// 注释掉火山AI服务导入
import '../controllers/translation_controller.dart';
import 'translation_audiohistory_controller.dart';
import '../models/translation_models.dart'; // 添加这行导入
@ -12,7 +12,7 @@ import '../../../data/services/translation_usage_service.dart';
//详情页管理
class TranslationDetailController extends GetxController {
// 依赖注入
final VolcanoAIService _aiService = Get.find<VolcanoAIService>();
//final VolcanoAIService _aiService = Get.find<VolcanoAIService>();
final GetStorage _storage = GetStorage();
late final TranslationUsageService _usageService;
@ -124,89 +124,89 @@ class TranslationDetailController extends GetxController {
sessionItems.map((e) => e.translatedText).join('\n\n');
// 使用延长的超时时间重试提取摘要
Future<String> retryExtractSummaryWithExtendedTimeout(String text) async {
return _extractSummaryInternal(text, _extendedTimeoutDuration);
}
// Future<String> retryExtractSummaryWithExtendedTimeout(String text) async {
// return _extractSummaryInternal(text, _extendedTimeoutDuration);
// }
// 提取摘要
Future<String> extractSummary(String text) async {
return _extractSummaryInternal(text, _defaultTimeoutDuration);
}
// Future<String> extractSummary(String text) async {
// return _extractSummaryInternal(text, _defaultTimeoutDuration);
// }
// 内部摘要提取方法,避免代码重复
Future<String> _extractSummaryInternal(
String text, int timeoutDuration) async {
if (text.isEmpty) {
return '无法生成摘要:文本为空';
}
isGeneratingSummary.value = true;
try {
// 构建提示词
final messages = [
{'role': 'user', 'content': '请为以下文本生成一个简洁的摘要,突出重点内容:\n\n$text'},
];
// 系统提示词
const systemPrompt = '''
你是一个专业的文本摘要助手,擅长从会议记录、对话和文本中提取关键信息并生成简洁的摘要。
请按照以下格式生成摘要:
1. 首先用1-2句话对整体内容进行简要概括
2. 然后使用项目符号列表(•)列出关键点或重要信息
3. 如果是对话内容,请特别标注出讨论的结论或达成的共识
请遵循以下原则:
1. 保留原文的核心信息和关键点
2. 使用简洁、清晰的语言
3. 保持客观,不添加个人观点
4. 摘要总长度应该是原文的1/3或更短
5. 确保列表中的每个要点都简明扼要,突出重点
''';
String result;
try {
// 使用超时限制
result = await _aiService
.sendMessage(
messages: messages,
systemPrompt: systemPrompt,
)
.timeout(Duration(seconds: timeoutDuration), onTimeout: () {
throw TimeoutException('请求超时', Duration(seconds: timeoutDuration));
});
} on TimeoutException catch (_) {
final timeoutMessage = timeoutDuration == _defaultTimeoutDuration
? '生成摘要超时,请稍后重试'
: '生成摘要超时,请检查网络连接后重试';
summaryResult.value = timeoutMessage;
return summaryResult.value;
}
// 更新结果并保存
if (result.isNotEmpty) {
summaryResult.value = result;
saveSummary(result);
// 记录AI摘要API调用统计
try {
_usageService.recordAiSummaryApiCall();
} catch (e) {
Logger.error('记录AI摘要统计失败: $e');
}
}
return result;
} catch (e) {
final errorMessage = '生成摘要失败: ${e.toString()}';
Logger.error('TranslationDetailController', errorMessage);
summaryResult.value = errorMessage;
return errorMessage;
} finally {
isGeneratingSummary.value = false;
}
}
// Future<String> _extractSummaryInternal(
// String text, int timeoutDuration) async {
// if (text.isEmpty) {
// return '无法生成摘要:文本为空';
// }
// isGeneratingSummary.value = true;
// try {
// // 构建提示词
// final messages = [
// {'role': 'user', 'content': '请为以下文本生成一个简洁的摘要,突出重点内容:\n\n$text'},
// ];
// // 系统提示词
// const systemPrompt = '''
// 你是一个专业的文本摘要助手,擅长从会议记录、对话和文本中提取关键信息并生成简洁的摘要。
// 请按照以下格式生成摘要:
// 1. 首先用1-2句话对整体内容进行简要概括
// 2. 然后使用项目符号列表(•)列出关键点或重要信息
// 3. 如果是对话内容,请特别标注出讨论的结论或达成的共识
// 请遵循以下原则:
// 1. 保留原文的核心信息和关键点
// 2. 使用简洁、清晰的语言
// 3. 保持客观,不添加个人观点
// 4. 摘要总长度应该是原文的1/3或更短
// 5. 确保列表中的每个要点都简明扼要,突出重点
// ''';
// String result;
// try {
// // 使用超时限制
// result = await _aiService
// .sendMessage(
// messages: messages,
// systemPrompt: systemPrompt,
// )
// .timeout(Duration(seconds: timeoutDuration), onTimeout: () {
// throw TimeoutException('请求超时', Duration(seconds: timeoutDuration));
// });
// } on TimeoutException catch (_) {
// final timeoutMessage = timeoutDuration == _defaultTimeoutDuration
// ? '生成摘要超时,请稍后重试'
// : '生成摘要超时,请检查网络连接后重试';
// summaryResult.value = timeoutMessage;
// return summaryResult.value;
// }
// // 更新结果并保存
// if (result.isNotEmpty) {
// summaryResult.value = result;
// saveSummary(result);
// // 记录AI摘要API调用统计
// try {
// _usageService.recordAiSummaryApiCall();
// } catch (e) {
// Logger.error('记录AI摘要统计失败: $e');
// }
// }
// return result;
// } catch (e) {
// final errorMessage = '生成摘要失败: ${e.toString()}';
// Logger.error('TranslationDetailController', errorMessage);
// summaryResult.value = errorMessage;
// return errorMessage;
// } finally {
// isGeneratingSummary.value = false;
// }
// }
// 删除单条翻译记录
Future<void> deleteTranslationItem(TranslationItem item) async {

352
lib/modules/translation/views/translation_detail_view.dart

@ -142,9 +142,9 @@ class TranslationDetailView extends GetView<TranslationDetailController> {
if (controller.selectedTabIndex.value == 0)
..._buildOriginalRecords(isDarkMode)
else
_buildSummaryWidget(isDarkMode),
//_buildSummaryWidget(isDarkMode),
SizedBox(height: 24.h),
SizedBox(height: 24.h),
],
);
}),
@ -323,23 +323,23 @@ class TranslationDetailView extends GetView<TranslationDetailController> {
}
// 构建总结视图
Widget _buildSummaryWidget(bool isDarkMode) {
final String combinedSourceText = _getCombinedSourceText();
// Widget _buildSummaryWidget(bool isDarkMode) {
// final String combinedSourceText = _getCombinedSourceText();
return Column(
children: [
// 摘要内容容器
_buildSummaryContainer(isDarkMode),
// return Column(
// children: [
// // 摘要内容容器
// _buildSummaryContainer(isDarkMode),
// 提取摘要按钮
Obx(() => _buildExtractSummaryButton(combinedSourceText, isDarkMode)),
// // 提取摘要按钮
// Obx(() => _buildExtractSummaryButton(combinedSourceText, isDarkMode)),
// 重新生成按钮
Obx(() =>
_buildRegenerateSummaryButton(combinedSourceText, isDarkMode)),
],
);
}
// // 重新生成按钮
// Obx(() =>
// _buildRegenerateSummaryButton(combinedSourceText, isDarkMode)),
// ],
// );
// }
// 构建摘要容器
Widget _buildSummaryContainer(bool isDarkMode) {
@ -484,163 +484,163 @@ class TranslationDetailView extends GetView<TranslationDetailController> {
}
// 构建提取摘要按钮
Widget _buildExtractSummaryButton(
String combinedSourceText, bool isDarkMode) {
final bool isDisabled = controller.isGeneratingSummary.value ||
controller.summaryResult.value.isNotEmpty;
return Container(
width: double.infinity,
margin: EdgeInsets.only(bottom: 16.h),
child: ElevatedButton(
onPressed:
isDisabled ? null : () => _extractSummary(combinedSourceText),
style: ElevatedButton.styleFrom(
backgroundColor: isDarkMode ? AppColors.primary : Colors.blue,
foregroundColor: Colors.white,
padding: EdgeInsets.symmetric(vertical: 12.h),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16.r),
),
elevation: 0,
disabledBackgroundColor: controller.isGeneratingSummary.value
? isDarkMode
? AppColors.primary.withAlpha(128)
: Colors.blue.withAlpha(128)
: Colors.grey.withAlpha(128),
),
child: controller.isGeneratingSummary.value
? _buildGeneratingIndicator(isDarkMode)
: Text(
controller.summaryResult.value.isEmpty
? 'extractSummary'.tr
: 'summaryGenerated'.tr, // 提取摘要 : 已生成摘要
style: TextStyle(
fontSize: 16.sp,
fontWeight: FontWeight.w600,
),
),
),
);
}
// Widget _buildExtractSummaryButton(
// String combinedSourceText, bool isDarkMode) {
// final bool isDisabled = controller.isGeneratingSummary.value ||
// controller.summaryResult.value.isNotEmpty;
// return Container(
// width: double.infinity,
// margin: EdgeInsets.only(bottom: 16.h),
// child: ElevatedButton(
// onPressed:
// isDisabled ? null : () => _extractSummary(combinedSourceText),
// style: ElevatedButton.styleFrom(
// backgroundColor: isDarkMode ? AppColors.primary : Colors.blue,
// foregroundColor: Colors.white,
// padding: EdgeInsets.symmetric(vertical: 12.h),
// shape: RoundedRectangleBorder(
// borderRadius: BorderRadius.circular(16.r),
// ),
// elevation: 0,
// disabledBackgroundColor: controller.isGeneratingSummary.value
// ? isDarkMode
// ? AppColors.primary.withAlpha(128)
// : Colors.blue.withAlpha(128)
// : Colors.grey.withAlpha(128),
// ),
// child: controller.isGeneratingSummary.value
// ? _buildGeneratingIndicator(isDarkMode)
// : Text(
// controller.summaryResult.value.isEmpty
// ? 'extractSummary'.tr
// : 'summaryGenerated'.tr, // 提取摘要 : 已生成摘要
// style: TextStyle(
// fontSize: 16.sp,
// fontWeight: FontWeight.w600,
// ),
// ),
// ),
// );
// }
// 构建生成中指示器
Widget _buildGeneratingIndicator(bool isDarkMode) {
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SizedBox(
width: 20.w,
height: 20.h,
child: CircularProgressIndicator(
color: Colors.white,
strokeWidth: 2.0,
),
),
SizedBox(width: 12.w),
Text(
'generating'.tr, // 生成中...
style: TextStyle(
fontSize: 16.sp,
fontWeight: FontWeight.w600,
),
),
],
);
}
// Widget _buildGeneratingIndicator(bool isDarkMode) {
// return Row(
// mainAxisAlignment: MainAxisAlignment.center,
// children: [
// SizedBox(
// width: 20.w,
// height: 20.h,
// child: CircularProgressIndicator(
// color: Colors.white,
// strokeWidth: 2.0,
// ),
// ),
// SizedBox(width: 12.w),
// Text(
// 'generating'.tr, // 生成中...
// style: TextStyle(
// fontSize: 16.sp,
// fontWeight: FontWeight.w600,
// ),
// ),
// ],
// );
// }
// 构建重新生成按钮
Widget _buildRegenerateSummaryButton(
String combinedSourceText, bool isDarkMode) {
if (controller.summaryResult.value.isEmpty ||
controller.isGeneratingSummary.value) {
return SizedBox.shrink();
}
return Container(
width: double.infinity,
margin: EdgeInsets.only(bottom: 16.h),
child: OutlinedButton(
onPressed: () => _extractSummary(combinedSourceText),
style: OutlinedButton.styleFrom(
foregroundColor: isDarkMode ? AppColors.primary : Colors.blue,
side: BorderSide(color: isDarkMode ? AppColors.primary : Colors.blue),
padding: EdgeInsets.symmetric(vertical: 12.h),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16.r),
),
),
child: Text(
'regenerate'.tr, // 重新生成
style: TextStyle(
fontSize: 16.sp,
fontWeight: FontWeight.w600,
),
),
),
);
}
// Widget _buildRegenerateSummaryButton(
// String combinedSourceText, bool isDarkMode) {
// if (controller.summaryResult.value.isEmpty ||
// controller.isGeneratingSummary.value) {
// return SizedBox.shrink();
// }
// return Container(
// width: double.infinity,
// margin: EdgeInsets.only(bottom: 16.h),
// child: OutlinedButton(
// onPressed: () => _extractSummary(combinedSourceText),
// style: OutlinedButton.styleFrom(
// foregroundColor: isDarkMode ? AppColors.primary : Colors.blue,
// side: BorderSide(color: isDarkMode ? AppColors.primary : Colors.blue),
// padding: EdgeInsets.symmetric(vertical: 12.h),
// shape: RoundedRectangleBorder(
// borderRadius: BorderRadius.circular(16.r),
// ),
// ),
// child: Text(
// 'regenerate'.tr, // 重新生成
// style: TextStyle(
// fontSize: 16.sp,
// fontWeight: FontWeight.w600,
// ),
// ),
// ),
// );
// }
// 提取摘要方法
void _extractSummary(String text) async {
final result = await controller.extractSummary(text);
// void _extractSummary(String text) async {
// final result = await controller.extractSummary(text);
// 检查是否超时,如果超时则显示重试选项
if (result.contains('超时')) {
_showTimeoutDialog(text);
}
}
// // 检查是否超时,如果超时则显示重试选项
// if (result.contains('超时')) {
// _showTimeoutDialog(text);
// }
// }
// 显示超时对话框
void _showTimeoutDialog(String text) {
Get.dialog(
AlertDialog(
title: Text(
'requestTimeout'.tr, // 请求超时
style: TextStyle(
fontSize: 18.sp,
fontWeight: FontWeight.bold,
),
),
content: Text(
'summaryTimeoutMessage'.tr, // 生成摘要请求超时,可能是网络问题或服务器繁忙。
style: TextStyle(
fontSize: 16.sp,
),
),
actions: [
TextButton(
onPressed: () => Get.back(),
child: Text(
'cancel'.tr, // 取消
style: TextStyle(
fontSize: 16.sp,
color: Colors.grey[700],
),
),
),
TextButton(
onPressed: () {
Get.back();
// 使用延长的超时时间重试
controller.retryExtractSummaryWithExtendedTimeout(text);
},
child: Text(
'retry'.tr, // 重试
style: TextStyle(
fontSize: 16.sp,
color: Colors.blue,
fontWeight: FontWeight.bold,
),
),
),
],
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12.r),
),
),
);
}
// void _showTimeoutDialog(String text) {
// Get.dialog(
// AlertDialog(
// title: Text(
// 'requestTimeout'.tr, // 请求超时
// style: TextStyle(
// fontSize: 18.sp,
// fontWeight: FontWeight.bold,
// ),
// ),
// content: Text(
// 'summaryTimeoutMessage'.tr, // 生成摘要请求超时,可能是网络问题或服务器繁忙。
// style: TextStyle(
// fontSize: 16.sp,
// ),
// ),
// actions: [
// TextButton(
// onPressed: () => Get.back(),
// child: Text(
// 'cancel'.tr, // 取消
// style: TextStyle(
// fontSize: 16.sp,
// color: Colors.grey[700],
// ),
// ),
// ),
// TextButton(
// onPressed: () {
// Get.back();
// // 使用延长的超时时间重试
// controller.retryExtractSummaryWithExtendedTimeout(text);
// },
// child: Text(
// 'retry'.tr, // 重试
// style: TextStyle(
// fontSize: 16.sp,
// color: Colors.blue,
// fontWeight: FontWeight.bold,
// ),
// ),
// ),
// ],
// shape: RoundedRectangleBorder(
// borderRadius: BorderRadius.circular(12.r),
// ),
// ),
// );
// }
// 显示删除单条记录确认对话框
void _showDeleteItemConfirmDialog(TranslationItem item) {
@ -750,18 +750,18 @@ class TranslationDetailView extends GetView<TranslationDetailController> {
'${'originalText'.tr}:\n$sourceText\n\n${'translatedText'.tr}:\n$translatedText'; // 原文:译文:
} else {
// 分享摘要
if (controller.summaryResult.value.isNotEmpty) {
shareText =
'${'sessionSummary'.tr}:\n${controller.summaryResult.value}'; // 会话摘要:
} else {
Get.snackbar(
'cannotShare'.tr, // 无法分享
'generateSummaryFirst'.tr, // 请先生成摘要
snackPosition: SnackPosition.BOTTOM,
duration: Duration(seconds: 2),
);
return;
}
// if (controller.summaryResult.value.isNotEmpty) {
// shareText =
// '${'sessionSummary'.tr}:\n${controller.summaryResult.value}'; // 会话摘要:
// } else {
// Get.snackbar(
// 'cannotShare'.tr, // 无法分享
// 'generateSummaryFirst'.tr, // 请先生成摘要
// snackPosition: SnackPosition.BOTTOM,
// duration: Duration(seconds: 2),
// );
// return;
// }
}
// 调用系统分享

45
lib/modules/translation/views/translation_view.dart

@ -343,16 +343,31 @@ class TranslationView extends GetView<TranslationController> {
}
Widget _buildBody(bool isDarkMode) {
return Column(
children: [
Expanded(
child: _buildTranslationContent(isDarkMode),
),
_buildControlBar(isDarkMode),
],
return GestureDetector(
onPanUpdate: (details) {
// 检测左滑手势
if (details.delta.dx < -10) {
// 左滑距离超过10像素时触发
_handleSwipeLeft();
}
},
child: Column(
children: [
Expanded(
child: _buildTranslationContent(isDarkMode),
),
_buildControlBar(isDarkMode),
],
),
);
}
// 处理左滑手势
void _handleSwipeLeft() async {
await controller.stopAll();
Get.toNamed(Routes.translationHistory);
}
Widget _buildTranslationContent(bool isDarkMode) {
return Obx(() {
// 其他模式使用原有的列表布局
@ -969,14 +984,14 @@ class TranslationView extends GetView<TranslationController> {
controller.deleteTranslationItem(item.sessionId, item.timestamp);
// 显示删除成功提示
Get.snackbar(
'success'.tr, // deleteSuccess
'已删除该翻译记录', // deleteTranslationSuccess
snackPosition: SnackPosition.BOTTOM,
backgroundColor: Get.isDarkMode ? Colors.grey[800] : Colors.grey[200],
colorText: Get.isDarkMode ? Colors.white : Colors.black,
duration: const Duration(milliseconds: 800),
);
// Get.snackbar(
// 'success'.tr, // deleteSuccess
// '已删除该翻译记录', // deleteTranslationSuccess
// snackPosition: SnackPosition.BOTTOM,
// backgroundColor: Get.isDarkMode ? Colors.grey[800] : Colors.grey[200],
// colorText: Get.isDarkMode ? Colors.white : Colors.black,
// duration: const Duration(milliseconds: 800),
// );
} catch (e) {
// 显示删除失败提示
Get.snackbar(

Loading…
Cancel
Save