You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

409 lines
11 KiB

import 'dart:async';
import 'dart:convert';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:get/get.dart';
import 'package:im_flutter_sdk/im_flutter_sdk.dart';
import '../../im/im_manager.dart';
import 'chat_controller.dart';
/// 通话类型
enum CallType {
voice, // 语音通话
video, // 视频通话
}
/// 通话状态
enum CallStatus {
calling, // 通话中
missed, // 未接听
cancelled, // 已取消
rejected, // 已拒绝
connected, // 已接通(暂时用不到,但保留用于未来扩展)
}
/// 通话管理器,单例模式,统一管理通话逻辑
class CallManager extends GetxController {
static CallManager? _instance;
static CallManager get instance {
_instance ??= Get.put(CallManager());
return _instance!;
}
// 当前正在进行的通话
final Rx<CallSession?> currentCall = Rx<CallSession?>(null);
// 通话计时器(用于记录通话时长)
Timer? _callTimer;
int _callDurationSeconds = 0;
final RxInt callDurationSeconds = RxInt(0);
CallManager() {
print('📞 [CallManager] 通话管理器已初始化');
}
/// 发起通话
/// [targetUserId] 目标用户ID
/// [callType] 通话类型:语音或视频
/// [chatController] 聊天控制器,用于发送通话消息
Future<bool> initiateCall({
required String targetUserId,
required CallType callType,
ChatController? chatController,
}) async {
try {
if (currentCall.value != null) {
SmartDialog.showToast('已有通话正在进行中');
return false;
}
print('📞 [CallManager] 发起${callType == CallType.video ? "视频" : "语音"}通话,目标用户: $targetUserId');
// 创建通话会话
final session = CallSession(
targetUserId: targetUserId,
callType: callType,
status: CallStatus.calling,
isInitiator: true,
startTime: DateTime.now(),
);
currentCall.value = session;
// 发送通话消息(发起)
final callTypeStr = callType == CallType.video ? 'video' : 'voice';
await _sendCallMessage(
targetUserId: targetUserId,
callType: callTypeStr,
callStatus: 'missed', // 初始状态为未接听,等待对方响应
chatController: chatController,
);
// TODO: 这里可以集成实际的通话SDK,发起真正的通话
// 例如:await RTCManager.instance.startCall(targetUserId, callType);
return true;
} catch (e) {
print('❌ [CallManager] 发起通话失败: $e');
SmartDialog.showToast('发起通话失败: $e');
currentCall.value = null;
return false;
}
}
/// 接听通话
/// [message] 通话消息
/// [chatController] 聊天控制器,用于更新通话消息
Future<bool> acceptCall({
required EMMessage message,
ChatController? chatController,
}) async {
try {
final callInfo = _parseCallInfo(message);
if (callInfo == null) {
return false;
}
final targetUserId = message.from ?? '';
final callTypeStr = callInfo['callType'] as String?;
final callType = callTypeStr == 'video' ? CallType.video : CallType.voice;
print('📞 [CallManager] 接听${callType == CallType.video ? "视频" : "语音"}通话');
// 创建通话会话
final session = CallSession(
targetUserId: targetUserId,
callType: callType,
status: CallStatus.calling,
isInitiator: false,
startTime: DateTime.now(),
);
currentCall.value = session;
// 开始计时
_startCallTimer();
// 更新通话消息状态为通话中
await _updateCallMessageStatus(
message: message,
callStatus: 'calling',
callDuration: 0,
chatController: chatController,
);
// TODO: 这里可以集成实际的通话SDK,接听通话
// 例如:await RTCManager.instance.acceptCall(targetUserId, callType);
return true;
} catch (e) {
print('❌ [CallManager] 接听通话失败: $e');
SmartDialog.showToast('接听通话失败: $e');
return false;
}
}
/// 拒绝通话
/// [message] 通话消息
/// [chatController] 聊天控制器,用于更新通话消息
Future<bool> rejectCall({
required EMMessage message,
ChatController? chatController,
}) async {
try {
print('📞 [CallManager] 拒绝通话');
// 更新通话消息状态为已拒绝
await _updateCallMessageStatus(
message: message,
callStatus: 'rejected',
chatController: chatController,
);
// 清理通话会话
currentCall.value = null;
// TODO: 这里可以集成实际的通话SDK,拒绝通话
// 例如:await RTCManager.instance.rejectCall(message.from ?? '');
return true;
} catch (e) {
print('❌ [CallManager] 拒绝通话失败: $e');
SmartDialog.showToast('拒绝通话失败: $e');
return false;
}
}
/// 取消通话
/// [message] 通话消息(可选,如果是发起方取消)
/// [chatController] 聊天控制器,用于更新通话消息
Future<bool> cancelCall({
EMMessage? message,
ChatController? chatController,
}) async {
try {
print('📞 [CallManager] 取消通话');
// 如果有消息,更新通话消息状态为已取消
if (message != null) {
await _updateCallMessageStatus(
message: message,
callStatus: 'cancelled',
chatController: chatController,
);
}
// 停止计时
_stopCallTimer();
// 清理通话会话
currentCall.value = null;
// TODO: 这里可以集成实际的通话SDK,取消通话
// 例如:await RTCManager.instance.cancelCall();
return true;
} catch (e) {
print('❌ [CallManager] 取消通话失败: $e');
SmartDialog.showToast('取消通话失败: $e');
return false;
}
}
/// 结束通话(通话完成)
/// [callDuration] 通话时长(秒)
/// [chatController] 聊天控制器,用于更新通话消息
Future<bool> endCall({
required int callDuration,
EMMessage? message,
ChatController? chatController,
}) async {
try {
print('📞 [CallManager] 结束通话,时长: ${callDuration}');
// 停止计时
_stopCallTimer();
// 如果有消息,更新通话消息状态为通话中(显示时长)
if (message != null) {
await _updateCallMessageStatus(
message: message,
callStatus: 'calling',
callDuration: callDuration,
chatController: chatController,
);
}
// 清理通话会话
currentCall.value = null;
// TODO: 这里可以集成实际的通话SDK,结束通话
// 例如:await RTCManager.instance.endCall();
return true;
} catch (e) {
print('❌ [CallManager] 结束通话失败: $e');
SmartDialog.showToast('结束通话失败: $e');
return false;
}
}
/// 开始通话计时
void _startCallTimer() {
_callDurationSeconds = 0;
callDurationSeconds.value = 0;
_callTimer?.cancel();
_callTimer = Timer.periodic(Duration(seconds: 1), (timer) {
_callDurationSeconds++;
callDurationSeconds.value = _callDurationSeconds;
});
}
/// 停止通话计时
void _stopCallTimer() {
_callTimer?.cancel();
_callTimer = null;
_callDurationSeconds = 0;
callDurationSeconds.value = 0;
}
/// 发送通话消息
Future<bool> _sendCallMessage({
required String targetUserId,
required String callType,
required String callStatus,
int? callDuration,
ChatController? chatController,
}) async {
try {
// 如果提供了 chatController,使用它发送消息
if (chatController != null) {
return await chatController.sendCallMessage(
callType: callType,
callStatus: callStatus,
callDuration: callDuration,
);
}
// 否则直接通过 IMManager 发送自定义消息
final callParams = <String, String>{
'callType': callType,
'callStatus': callStatus,
};
if (callDuration != null) {
callParams['callDuration'] = callDuration.toString();
}
final message = await IMManager.instance.sendCustomMessage(
targetUserId,
'call',
callParams,
);
return message != null;
} catch (e) {
print('❌ [CallManager] 发送通话消息失败: $e');
return false;
}
}
/// 更新通话消息状态
Future<bool> _updateCallMessageStatus({
required EMMessage message,
required String callStatus,
int? callDuration,
ChatController? chatController,
}) async {
try {
// 解析现有通话信息
final callInfo = _parseCallInfo(message);
if (callInfo == null) {
return false;
}
final callType = callInfo['callType'] as String? ?? 'voice';
final targetUserId = message.from ?? message.to ?? '';
// 发送更新的通话消息
return await _sendCallMessage(
targetUserId: targetUserId,
callType: callType,
callStatus: callStatus,
callDuration: callDuration,
chatController: chatController,
);
} catch (e) {
print('❌ [CallManager] 更新通话消息状态失败: $e');
return false;
}
}
/// 从消息中解析通话信息(支持新格式的自定义消息和旧格式的文本消息)
Map<String, dynamic>? _parseCallInfo(EMMessage message) {
try {
// 新格式:自定义消息
if (message.body.type == MessageType.CUSTOM) {
final customBody = message.body as EMCustomMessageBody;
if (customBody.event == 'call' && customBody.params != null) {
final params = customBody.params!;
return {
'callType': params['callType'] ?? 'voice',
'callStatus': params['callStatus'] ?? 'missed',
'callDuration': params['callDuration'] != null
? int.tryParse(params['callDuration']!)
: null,
};
}
}
// 旧格式:文本消息
if (message.body.type == MessageType.TXT) {
final textBody = message.body as EMTextMessageBody;
final content = textBody.content;
if (content != null && content.startsWith('[CALL:]')) {
final jsonStr = content.substring(7); // 移除 '[CALL:]' 前缀
return jsonDecode(jsonStr) as Map<String, dynamic>;
}
}
} catch (e) {
print('解析通话信息失败: $e');
}
return null;
}
/// 检查是否有正在进行的通话
bool get isInCall => currentCall.value != null;
/// 获取当前通话时长(秒)
int get currentCallDuration => callDurationSeconds.value;
@override
void onClose() {
_stopCallTimer();
currentCall.value = null;
super.onClose();
}
}
/// 通话会话信息
class CallSession {
final String targetUserId;
final CallType callType;
final CallStatus status;
final bool isInitiator; // 是否是发起方
final DateTime startTime;
DateTime? endTime;
CallSession({
required this.targetUserId,
required this.callType,
required this.status,
required this.isInitiator,
required this.startTime,
this.endTime,
});
/// 获取通话时长(秒)
int get duration {
final end = endTime ?? DateTime.now();
return end.difference(startTime).inSeconds;
}
}