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 currentCall = Rx(null); // 通话计时器(用于记录通话时长) Timer? _callTimer; int _callDurationSeconds = 0; final RxInt callDurationSeconds = RxInt(0); CallManager() { print('📞 [CallManager] 通话管理器已初始化'); } /// 发起通话 /// [targetUserId] 目标用户ID /// [callType] 通话类型:语音或视频 /// [chatController] 聊天控制器,用于发送通话消息 Future 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 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 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 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 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 _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 = { '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 _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? _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; } } } 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; } }