Browse Source

feat(通话): 添加AI语音通话功能

- 在AgentController中添加电话模式状态管理
- 实现开始/结束电话模式逻辑,包括权限检查和语音模式切换
- 在AgentView中添加电话模式UI,包括覆盖层、状态显示和控制按钮
- 调整"+选项"高度以容纳电话按钮
weicu
lxm 1 year ago
parent
commit
c1aac0ece5
  1. 51
      lib/modules/agent/controllers/agent_controller.dart
  2. 260
      lib/modules/agent/views/agent_view.dart

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

@ -78,6 +78,9 @@ class AgentController extends GetxController {
// "+"号选项展开状态
final isPlusOptionsExpanded = false.obs; // 是否展开"+"号选项
// 打电话模式状态
final isPhoneCallMode = false.obs; // 是否处于打电话模式
// 键盘状态
final isKeyboardVisible = false.obs; // 键盘是否可见
@ -1024,6 +1027,54 @@ class AgentController extends GetxController {
useImagePrompt('请翻译这张图片中的文字');
}
// 开始打电话模式
void startPhoneCall() async {
Logger.i(TAG, '🔥 开始打电话模式');
// 收缩"+"选项
collapsePlusOptions();
// 进入打电话模式
isPhoneCallMode.value = true;
// 如果当前是文本模式,切换到语音模式
if (isTextInputMode.value) {
// 检查麦克风权限
final hasPermission = await PermissionUtil.instance.requestPermission(
permissionType: Permission.microphone,
permissionName: '麦克风',
explanationText: '打电话功能需要访问麦克风进行语音通话。',
permanentDenialText: '您已永久拒绝麦克风权限,请前往系统设置开启权限。',
settingsButtonText: '去设置',
cancelButtonText: '取消',
);
if (!hasPermission) {
// 权限未获取,退出打电话模式
isPhoneCallMode.value = false;
return;
}
// 切换到语音模式并开始语音输入
isTextInputMode.value = false;
await startVoiceInput();
}
}
// 结束打电话模式
void endPhoneCall() async {
Logger.i(TAG, '🔥 结束打电话模式');
// 退出打电话模式
isPhoneCallMode.value = false;
// 停止语音输入
await stopVoiceInput();
// 切换回文本模式
isTextInputMode.value = true;
}
// 获取音乐播放状态
Future<void> musicStatus() async {
try {

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

@ -183,10 +183,13 @@ class _AgentViewState extends State<AgentView> with WidgetsBindingObserver {
),
],
),
body: Column(
body: Stack(
children: [
// 聊天内容
Expanded(
// 主要聊天界面
Column(
children: [
// 聊天内容
Expanded(
child: Obx(
() => Align(
alignment: Alignment.topCenter,
@ -644,7 +647,7 @@ class _AgentViewState extends State<AgentView> with WidgetsBindingObserver {
return AnimatedContainer(
duration: const Duration(milliseconds: 300),
curve: Curves.easeInOut,
height: isExpanded ? 100.h : 0, // 调整高度以适应按钮尺寸
height: isExpanded ? 120.h : 0, // 调整高度以适应按钮尺寸
child: isExpanded
? Container(
margin: EdgeInsets.only(top: 8.h),
@ -685,14 +688,14 @@ class _AgentViewState extends State<AgentView> with WidgetsBindingObserver {
// },
// isDarkMode: isDarkMode,
// ),
// 通话按钮(预留位置)
// 通话按钮
_buildPlusOptionButton(
context: context,
icon: Icons.phone,
label: '打电话',
onTap: () {
//controller.collapsePlusOptions();
// TODO: 实现通话功能
// 启动打电话模式
controller.startPhoneCall();
},
isDarkMode: isDarkMode,
),
@ -707,6 +710,16 @@ class _AgentViewState extends State<AgentView> with WidgetsBindingObserver {
],
),
),
],
),
// 打电话模式覆盖层
Obx(() {
if (controller.isPhoneCallMode.value) {
return _buildPhoneCallOverlay(context, isDarkMode);
}
return const SizedBox.shrink();
}),
],
),
);
@ -808,6 +821,239 @@ class _AgentViewState extends State<AgentView> with WidgetsBindingObserver {
),
);
}
// 构建打电话模式覆盖层
Widget _buildPhoneCallOverlay(BuildContext context, bool isDarkMode) {
return Container(
color: isDarkMode ? Colors.black : Colors.white,
child: SafeArea(
child: Column(
children: [
// 顶部标题栏
_buildPhoneCallHeader(isDarkMode),
// 中间音频可视化区域
Expanded(
child: _buildPhoneCallVisualization(isDarkMode),
),
// 底部控制按钮
_buildPhoneCallControls(isDarkMode),
],
),
),
);
}
// 构建打电话模式标题栏
Widget _buildPhoneCallHeader(bool isDarkMode) {
return Container(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
child: Row(
children: [
// 返回按钮
GestureDetector(
onTap: () => controller.endPhoneCall(),
child: Container(
width: 40,
height: 40,
decoration: BoxDecoration(
color: isDarkMode ? Colors.grey[800] : Colors.grey[200],
borderRadius: BorderRadius.circular(20),
),
child: Icon(
Icons.arrow_back,
color: isDarkMode ? Colors.white : Colors.black,
size: 20,
),
),
),
const SizedBox(width: 16),
// 标题
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'AI语音通话',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.w600,
color: isDarkMode ? Colors.white : Colors.black,
),
),
Obx(() {
final status = controller.isListening.value
? '正在通话中...'
: controller.isSpeaking.value
? 'AI正在回复...'
: '等待开始...';
return Text(
status,
style: TextStyle(
fontSize: 14,
color: isDarkMode ? Colors.grey[400] : Colors.grey[600],
),
);
}),
],
),
),
],
),
);
}
// 构建打电话模式音频可视化
Widget _buildPhoneCallVisualization(bool isDarkMode) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// 音频可视化圆圈
Obx(() {
final isActive = controller.isListening.value || controller.isSpeaking.value;
return AnimatedContainer(
duration: const Duration(milliseconds: 300),
width: isActive ? 200 : 160,
height: isActive ? 200 : 160,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: isDarkMode
? Colors.blue[900]?.withOpacity(0.3)
: Colors.blue[100],
border: Border.all(
color: Colors.blue,
width: isActive ? 3 : 2,
),
),
child: Icon(
Icons.phone,
size: isActive ? 80 : 60,
color: Colors.blue,
),
);
}),
const SizedBox(height: 32),
// 状态文字
Obx(() {
String statusText = '点击开始通话';
if (controller.isListening.value) {
statusText = '正在听您说话...';
} else if (controller.isSpeaking.value) {
statusText = 'AI正在回复...';
} else if (controller.isProcessing.value) {
statusText = '正在处理...';
}
return Text(
statusText,
style: TextStyle(
fontSize: 16,
color: isDarkMode ? Colors.grey[300] : Colors.grey[700],
fontWeight: FontWeight.w500,
),
);
}),
],
),
);
}
// 构建打电话模式控制按钮
Widget _buildPhoneCallControls(bool isDarkMode) {
return Container(
padding: const EdgeInsets.symmetric(horizontal: 32, vertical: 24),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
// 静音按钮
Obx(() => _buildPhoneControlButton(
icon: controller.isTtsEnabled.value ? Icons.volume_up : Icons.volume_off,
onTap: () => controller.toggleTts(),
isDarkMode: isDarkMode,
isActive: controller.isTtsEnabled.value,
)),
// 主要通话按钮
Obx(() {
final isActive = controller.isListening.value;
return GestureDetector(
onTap: () {
if (isActive) {
controller.stopVoiceInput();
} else {
controller.startVoiceInput();
}
},
child: Container(
width: 80,
height: 80,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: isActive ? Colors.red : Colors.green,
boxShadow: [
BoxShadow(
color: (isActive ? Colors.red : Colors.green).withOpacity(0.3),
blurRadius: 12,
spreadRadius: 2,
),
],
),
child: Icon(
isActive ? Icons.call_end : Icons.call,
color: Colors.white,
size: 36,
),
),
);
}),
// 结束通话按钮
_buildPhoneControlButton(
icon: Icons.call_end,
onTap: () => controller.endPhoneCall(),
isDarkMode: isDarkMode,
isActive: false,
backgroundColor: Colors.red,
),
],
),
);
}
// 构建打电话控制按钮
Widget _buildPhoneControlButton({
required IconData icon,
required VoidCallback onTap,
required bool isDarkMode,
required bool isActive,
Color? backgroundColor,
}) {
return GestureDetector(
onTap: onTap,
child: Container(
width: 56,
height: 56,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: backgroundColor ?? (isDarkMode ? Colors.grey[800] : Colors.grey[200]),
border: isActive ? Border.all(color: Colors.blue, width: 2) : null,
),
child: Icon(
icon,
color: backgroundColor != null
? Colors.white
: (isDarkMode ? Colors.white : Colors.black),
size: 24,
),
),
);
}
}

Loading…
Cancel
Save