Browse Source

feat(语音输入): 实现按住说话功能并优化语音输入模式切换

- 新增按住说话状态和模式控制变量
- 修改语音输入模式切换逻辑以支持按住说话模式
- 添加开始/结束按住说话的方法
- 重构语音输入UI为长按手势操作
- 优化语音输入状态下的UI反馈
weicu
lxm 1 year ago
parent
commit
53bfadf3d7
  1. 66
      lib/modules/agent/controllers/agent_controller.dart
  2. 139
      lib/modules/agent/views/agent_view.dart

66
lib/modules/agent/controllers/agent_controller.dart

@ -81,6 +81,12 @@ class AgentController extends GetxController {
// 打电话模式状态 // 打电话模式状态
final isPhoneCallMode = false.obs; // 是否处于打电话模式 final isPhoneCallMode = false.obs; // 是否处于打电话模式
// 按住说话状态
final isPushToTalkActive = false.obs; // 是否正在按住说话
// 按住说话模式状态 - 用于区分是否处于按住说话模式(即使当前没有按住)
final isPushToTalkMode = false.obs; // 是否处于按住说话模式
// 键盘状态 // 键盘状态
final isKeyboardVisible = false.obs; // 键盘是否可见 final isKeyboardVisible = false.obs; // 键盘是否可见
@ -340,7 +346,11 @@ class AgentController extends GetxController {
break; break;
case AgentServiceEventType.recognitionStopped: case AgentServiceEventType.recognitionStopped:
isListening.value = false; isListening.value = false;
isTextInputMode.value = true; // 只有在非按住说话模式且非打电话模式下才自动切换到文字模式
// 按住说话模式下应该保持在语音模式,等待下次按住
if (!isPushToTalkMode.value && !isPhoneCallMode.value) {
isTextInputMode.value = true;
}
// isTextInputMode.refresh(); // isTextInputMode.refresh();
Logger.i(TAG, '识别停止'); Logger.i(TAG, '识别停止');
break; break;
@ -765,7 +775,7 @@ class AgentController extends GetxController {
} }
} }
// 切换输入模式 // 切换输入模式(改造为按住说话模式)
void toggleInputMode() async { void toggleInputMode() async {
// 如果当前在图片输入模式,先清除 // 如果当前在图片输入模式,先清除
if (isImageInputActive.value) { if (isImageInputActive.value) {
@ -780,7 +790,7 @@ class AgentController extends GetxController {
// context: Get.context!, // context: Get.context!,
permissionType: Permission.microphone, permissionType: Permission.microphone,
permissionName: '麦克风', permissionName: '麦克风',
explanationText: '语音识别与翻译功能需要访问麦克风。', explanationText: '语音识别功能需要访问麦克风。',
permanentDenialText: '您已永久拒绝麦克风权限,请前往系统设置开启权限。', permanentDenialText: '您已永久拒绝麦克风权限,请前往系统设置开启权限。',
settingsButtonText: '去设置', settingsButtonText: '去设置',
cancelButtonText: '取消', cancelButtonText: '取消',
@ -792,13 +802,15 @@ class AgentController extends GetxController {
isTextInputMode.toggle(); isTextInputMode.toggle();
// 切换到语音模式时,直接开始语音输入 // 切换到语音模式时,启用按住说话模式
if (!isTextInputMode.value) { // 切换到文本模式时,禁用按住说话模式并停止任何正在进行的语音输入
startVoiceInput(); if (isTextInputMode.value) {
} // 切换到文本模式
// 切换到文本模式时,直接停止语音输入 isPushToTalkMode.value = false;
else {
stopVoiceInput(); stopVoiceInput();
} else {
// 切换到语音模式,启用按住说话模式
isPushToTalkMode.value = true;
} }
} }
@ -1057,6 +1069,8 @@ class AgentController extends GetxController {
// 切换到语音模式并开始语音输入 // 切换到语音模式并开始语音输入
isTextInputMode.value = false; isTextInputMode.value = false;
// 打电话模式不使用按住说话模式,而是持续语音输入
isPushToTalkMode.value = false;
await startVoiceInput(); await startVoiceInput();
} }
} }
@ -1071,8 +1085,40 @@ class AgentController extends GetxController {
// 停止语音输入 // 停止语音输入
await stopVoiceInput(); await stopVoiceInput();
// 切换回文本模式 // 切换回文本模式,并禁用按住说话模式
isTextInputMode.value = true; isTextInputMode.value = true;
isPushToTalkMode.value = false;
}
// 开始按住说话
void startPushToTalk() async {
Logger.i(TAG, '🎤 开始按住说话');
// 只有在语音模式且按住说话模式下才能按住说话
if (isTextInputMode.value || !isPushToTalkMode.value) return;
// 如果已经在按住说话,不重复开始
if (isPushToTalkActive.value) return;
// 设置按住说话状态
isPushToTalkActive.value = true;
// 开始语音识别
await startVoiceInput();
}
// 结束按住说话
void endPushToTalk() async {
Logger.i(TAG, '🎤 结束按住说话');
// 如果不在按住说话状态,直接返回
if (!isPushToTalkActive.value) return;
// 清除按住说话状态(但保持按住说话模式)
isPushToTalkActive.value = false;
// 停止语音识别
await stopVoiceInput();
} }
// 获取音乐播放状态 // 获取音乐播放状态

139
lib/modules/agent/views/agent_view.dart

@ -457,75 +457,84 @@ class _AgentViewState extends State<AgentView> with WidgetsBindingObserver {
), ),
); );
} else { } else {
// 语音输入模式 - 始终显示波形图 // 语音输入模式 - 按住说话模式
return GestureDetector( return GestureDetector(
onTap: () { onLongPressStart: (_) {
if (controller.isListening.value) { // 开始按住说话
controller.stopVoiceInput(); controller.startPushToTalk();
} else {
controller.startVoiceInput();
}
}, },
child: Container( onLongPressEnd: (_) {
height: 50, // 结束按住说话
padding: const EdgeInsets.symmetric( controller.endPushToTalk();
horizontal: 16), },
decoration: BoxDecoration( onLongPressCancel: () {
color: isDarkMode // 取消按住说话
? Colors.grey[800] controller.endPushToTalk();
: Colors.grey[200], },
borderRadius: BorderRadius.circular(22), child: Obx(() {
border: Border.all( final isPushToTalkActive = controller.isPushToTalkActive.value;
color: isDarkMode final isListening = controller.isListening.value;
? Colors.grey[800]!
: Colors.grey.withOpacity(0.1), return Container(
width: 1.0, height: 50,
padding: const EdgeInsets.symmetric(
horizontal: 16),
decoration: BoxDecoration(
color: isPushToTalkActive
? (isDarkMode ? Colors.blue[900] : Colors.blue[100])
: (isDarkMode ? Colors.grey[800] : Colors.grey[200]),
borderRadius: BorderRadius.circular(22),
border: Border.all(
color: isPushToTalkActive
? Colors.blue
: (isDarkMode
? Colors.grey[800]!
: Colors.grey.withOpacity(0.1)),
width: isPushToTalkActive ? 2.0 : 1.0,
),
), ),
), child: Row(
child: Row( children: [
children: [ // 语音波形图 - 只在按住时显示
// 语音波形图 - 始终显示 if (isPushToTalkActive) ...[
Expanded( const AgentSoundBarsWidget(),
child: Row( const Spacer(),
mainAxisAlignment: ],
MainAxisAlignment.start,
children: [ // 状态文字和图标
const AgentSoundBarsWidget(), Expanded(
const Spacer(), child: Row(
// "正在聆听"文字 mainAxisAlignment: isPushToTalkActive
Obx(() { ? MainAxisAlignment.end
final isListening = controller : MainAxisAlignment.center,
.isListening.value; children: [
return Row( Icon(
children: [ isPushToTalkActive ? Icons.mic : Icons.mic_none,
Icon( size: 16,
Icons.mic, color: isPushToTalkActive
size: 14, ? Colors.blue
color: isListening : Colors.grey[500],
? primaryColor ),
: Colors.grey[500], const SizedBox(width: 6),
), Text(
const SizedBox(width: 4), isPushToTalkActive
Text( ? '正在录音...'
'正在聆听', : '按住说话',
style: TextStyle( style: TextStyle(
fontSize: 13, fontSize: 14,
fontWeight: fontWeight: FontWeight.w500,
FontWeight.w500, color: isPushToTalkActive
color: isListening ? Colors.blue
? primaryColor : Colors.grey[600],
: Colors.grey[500], ),
), ),
), ],
], ),
);
}),
],
), ),
), ],
], ),
), );
), }),
); );
} }
}), }),

Loading…
Cancel
Save