import 'dart:io'; import 'package:get/get.dart'; import 'package:get_storage/get_storage.dart'; import 'package:im_flutter_sdk/im_flutter_sdk.dart'; import 'package:video_thumbnail/video_thumbnail.dart'; import 'package:path_provider/path_provider.dart'; import '../controller/message/conversation_controller.dart'; import '../controller/message/chat_controller.dart'; // 完整的IM管理器实现,使用实际的SDK类型和方法 class IMManager { // 单例模式 static final IMManager _instance = IMManager._internal(); factory IMManager() => _instance; final storage = GetStorage(); // 静态getter用于instance访问 static IMManager get instance => _instance; bool _isInitialized = false; bool _listenersRegistered = false; // 监听器标识符 static const String _connectionHandlerKey = 'im_manager_connection_handler'; static const String _chatHandlerKey = 'im_manager_chat_handler'; // 存储活跃的 ChatController 实例,key 为 userId final Map _activeChatControllers = {}; IMManager._internal() { print('IMManager instance created'); } /// 初始化IM SDK Future initialize(String appKey) async { try { if (_isInitialized) { print('IM SDK already initialized'); return true; } // 创建EMOptions实例 final options = EMOptions( appKey: appKey, autoLogin: false, acceptInvitationAlways: false, ); // 初始化SDK await EMClient.getInstance.init(options); _isInitialized = true; print('IM SDK initialized successfully'); return true; } catch (e) { print('Failed to initialize IM SDK: $e'); return false; } } // 注册监听器 void _registerListeners() { try { // 防止重复注册 if (_listenersRegistered) { if (Get.isLogEnable) { Get.log('监听器已注册,跳过重复注册'); } return; } // 连接监听器 EMClient.getInstance.addConnectionEventHandler( _connectionHandlerKey, EMConnectionEventHandler( onConnected: () { if (Get.isLogEnable) { Get.log('✅ [IMManager] 已连接到IM服务器'); } }, onDisconnected: () { if (Get.isLogEnable) { Get.log('❌ [IMManager] 与IM服务器断开连接'); } // TODO: 可以在这里添加自动重连逻辑 }, onTokenDidExpire: () { if (Get.isLogEnable) { Get.log('⚠️ [IMManager] IM token即将过期'); } // TODO: 可以在这里添加自动刷新token的逻辑 }, onUserKickedByOtherDevice: () { if (Get.isLogEnable) { Get.log('⚠️ [IMManager] 用户被其他设备踢出'); } // TODO: 可以在这里添加处理逻辑,如跳转到登录页 }, ), ); // 消息监听器 EMClient.getInstance.chatManager.addEventHandler( _chatHandlerKey, EMChatEventHandler( onMessagesReceived: (messages) { if (Get.isLogEnable) { Get.log('📨 [IMManager] 收到 ${messages.length} 条新消息'); } // 收到新消息时,更新会话列表 _refreshConversationList(); // 通知对应的 ChatController 更新消息列表 _notifyChatControllers(messages); }, onCmdMessagesReceived: (cmdMessages) { if (Get.isLogEnable) { Get.log('📨 [IMManager] 收到 ${cmdMessages.length} 条CMD消息'); } // 处理CMD消息(如撤回消息等) for (var msg in cmdMessages) { _handleCmdMessage(msg); } }, ), ); _listenersRegistered = true; if (Get.isLogEnable) { Get.log('✅ [IMManager] 监听器注册成功'); } } catch (e) { if (Get.isLogEnable) { Get.log('❌ [IMManager] 注册监听器失败: $e'); } } } /// 登录IM服务 Future login(String token) async { try { if (!_isInitialized) { print('IM SDK not initialized'); return false; } var userId = storage.read('userId'); await EMClient.getInstance.logout(); await EMClient.getInstance.loginWithToken(userId, token); // 注册监听器 _registerListeners(); print('IM login successful'); return true; } catch (e) { print('IM login failed: $e'); return false; } } /// 登出IM服务 Future logout() async { try { await EMClient.getInstance.logout(); print('IM logout successful'); return true; } catch (e) { print('IM logout failed: $e'); return false; } } /// 发送文本消息 Future sendTextMessage( String content, String toChatUsername, ) async { print('Text message sent'); try { // 创建文本消息 final message = EMMessage.createTxtSendMessage( targetId: toChatUsername, content: content, ); print('Text message sent successfully'); return await EMClient.getInstance.chatManager.sendMessage(message); } catch (e) { print('Failed to send text message: $e'); return null; } } /// 发送语音消息 Future sendVoiceMessage( String filePath, String toChatUsername, int duration, ) async { try { // 创建语音消息 final message = EMMessage.createVoiceSendMessage( targetId: toChatUsername, filePath: filePath, duration: duration, ); // 发送消息 await EMClient.getInstance.chatManager.sendMessage(message); if (Get.isLogEnable) { Get.log('✅ [IMManager] 语音消息发送成功'); } return message; } catch (e) { if (Get.isLogEnable) { Get.log('❌ [IMManager] 发送语音消息失败: $e'); } return null; } } /// 发送图片消息 Future sendImageMessage( String imagePath, String toChatUsername, ) async { try { // 创建图片消息 final message = EMMessage.createImageSendMessage( targetId: toChatUsername, filePath: imagePath, sendOriginalImage: false, ); // 发送消息 await EMClient.getInstance.chatManager.sendMessage(message); print('Image message sent successfully'); return message; } catch (e) { print('Failed to send image message: $e'); return null; } } /// 发送视频消息 Future sendVideoMessage( String videoPath, String toChatUsername, int duration, ) async { try { print('🎬 [IMManager] 创建视频消息'); print('视频路径: $videoPath'); print('接收用户: $toChatUsername'); print('视频时长: $duration 秒'); // 🎯 手动生成视频缩略图 String? thumbnailPath; try { print('📸 [IMManager] 开始生成视频缩略图...'); // 获取临时目录 final tempDir = await getTemporaryDirectory(); final fileName = videoPath.split('/').last.split('.').first; final thumbFileName = '${fileName}_thumb.jpg'; thumbnailPath = '${tempDir.path}/$thumbFileName'; // 使用 video_thumbnail 生成缩略图 final uint8list = await VideoThumbnail.thumbnailFile( video: videoPath, thumbnailPath: thumbnailPath, imageFormat: ImageFormat.JPEG, maxWidth: 400, // 缩略图最大宽度 quality: 75, // 图片质量 ); if (uint8list != null && File(uint8list).existsSync()) { thumbnailPath = uint8list; print('✅ [IMManager] 缩略图生成成功: $thumbnailPath'); } else { print('⚠️ [IMManager] 缩略图生成返回null'); thumbnailPath = null; } } catch (e) { print('❌ [IMManager] 生成缩略图失败: $e'); thumbnailPath = null; } // 创建视频消息 final message = EMMessage.createVideoSendMessage( targetId: toChatUsername, filePath: videoPath, duration: duration, thumbnailLocalPath: thumbnailPath, // 🎯 指定缩略图路径 ); print('消息创建成功,消息类型: ${message.body.type}'); print('消息体是否为视频: ${message.body is EMVideoMessageBody}'); // 检查缩略图信息 if (message.body is EMVideoMessageBody) { final videoBody = message.body as EMVideoMessageBody; print('📸 [IMManager] 缩略图本地路径: ${videoBody.thumbnailLocalPath}'); print('📸 [IMManager] 缩略图远程路径: ${videoBody.thumbnailRemotePath}'); // 验证缩略图文件是否存在 if (videoBody.thumbnailLocalPath != null) { final thumbFile = File(videoBody.thumbnailLocalPath!); print('📸 [IMManager] 缩略图文件是否存在: ${thumbFile.existsSync()}'); } } // 发送消息 await EMClient.getInstance.chatManager.sendMessage(message); print('✅ [IMManager] 视频消息发送成功'); return message; } catch (e) { print('❌ [IMManager] 发送视频消息失败: $e'); return null; } } /// 获取会话列表 Future> getConversations() async { return EMClient.getInstance.chatManager.loadAllConversations(); } /// 获取用户信息(单个用户) /// 删除指定会话 Future deleteConversation( String conversationId, { bool deleteMessages = true, }) async { try { await EMClient.getInstance.chatManager.deleteConversation( conversationId, deleteMessages: deleteMessages, ); return true; } catch (e) { if (Get.isLogEnable) { Get.log('删除会话失败: $e'); } else { print('删除会话失败: $e'); } return false; } } /// 获取好有列表 Future> getContacts(String userId) async { return await EMClient.getInstance.userInfoManager.fetchUserInfoById([ userId, ]); } /// 获取指定会话的消息记录 Future> getMessages( String conversationId, { int pageSize = 20, String? startMsgId, }) async { try { EMConversationType convType = EMConversationType.Chat; // 获取会话对象 final conversation = await EMClient.getInstance.chatManager.getConversation( conversationId, type: convType, createIfNeed: false, ); if (conversation == null) { if (Get.isLogEnable) { Get.log('❌ [IMManager] 会话不存在: $conversationId'); } return []; } // 如果有startMsgId,从该消息开始加载;否则加载最新消息 if (startMsgId != null && startMsgId.isNotEmpty) { // 从指定消息ID开始加载更旧的消息 final messages = await conversation.loadMessages( startMsgId: startMsgId, loadCount: pageSize, ); return messages.map((msg) => msg as EMMessage?).toList(); } else { // 加载最新消息 final messages = await conversation.loadMessages( loadCount: pageSize, ); return messages.map((msg) => msg as EMMessage?).toList(); } } catch (e) { if (Get.isLogEnable) { Get.log('❌ [IMManager] 获取消息失败: $e'); } return []; } } /// 获取用户信息 Future getUserInfo(String userId) async { var data = await EMClient.getInstance.userInfoManager.fetchUserInfoById([ userId, ]); return data[userId]; } /// 注册 ChatController void registerChatController(ChatController controller) { _activeChatControllers[controller.userId] = controller; if (Get.isLogEnable) { Get.log('注册 ChatController: ${controller.userId}'); } } /// 注销 ChatController void unregisterChatController(String userId) { _activeChatControllers.remove(userId); if (Get.isLogEnable) { Get.log('注销 ChatController: $userId'); } } /// 通知 ChatController 更新消息列表 void _notifyChatControllers(List messages) { try { // 遍历所有收到的消息 for (var message in messages) { // 只处理接收到的消息(direction == RECEIVE) if (message.direction == MessageDirection.RECEIVE) { // 获取消息的发送者ID(from 属性) final fromId = message.from; if (fromId != null && fromId.isNotEmpty) { // 查找对应的 ChatController final controller = _activeChatControllers[fromId]; if (controller != null) { controller.addReceivedMessage(message); if (Get.isLogEnable) { Get.log('通知 ChatController 更新消息: $fromId'); } } } } } } catch (e) { if (Get.isLogEnable) { Get.log('通知 ChatController 更新消息列表失败: $e'); } } } /// 刷新会话列表 void _refreshConversationList() { try { // 尝试获取 ConversationController 并刷新会话列表 if (Get.isRegistered()) { final conversationController = Get.find(); conversationController.refreshConversations(); } } catch (e) { // ConversationController 可能未注册,忽略错误 if (Get.isLogEnable) { Get.log('刷新会话列表失败: $e'); } } } /// 添加用户到黑名单 Future addToBlacklist(String userId) async { try { await EMClient.getInstance.contactManager.addUserToBlockList(userId); print('已将用户 $userId 添加到黑名单'); } catch (e) { print('添加黑名单失败: $e'); rethrow; } } /// 从黑名单移除用户 Future removeFromBlacklist(String userId) async { try { await EMClient.getInstance.contactManager.removeUserFromBlockList(userId); print('已将用户 $userId 从黑名单移除'); } catch (e) { print('移除黑名单失败: $e'); rethrow; } } /// 获取黑名单列表 Future> getBlacklist() async { try { final list = await EMClient.getInstance.contactManager.getBlockListFromServer(); print('获取黑名单列表成功,共 ${list.length} 个用户'); return list; } catch (e) { print('获取黑名单列表失败: $e'); return []; } } /// 处理CMD消息 void _handleCmdMessage(EMMessage cmdMessage) { try { // 这里可以处理各种CMD消息,如撤回消息等 if (Get.isLogEnable) { Get.log('处理CMD消息: ${cmdMessage.msgId}'); } } catch (e) { if (Get.isLogEnable) { Get.log('处理CMD消息失败: $e'); } } } // 注意:以下方法保留用于未来扩展,当SDK支持更多回调时可以使用 // 目前环信SDK的EMChatEventHandler可能不支持这些回调,所以暂时注释掉 /* /// 通知消息已读 void _notifyMessageRead(List messages, String from, String to) { try { // 找到对应的 ChatController 并通知消息已读 final controller = _activeChatControllers[from]; if (controller != null) { // TODO: 在 ChatController 中添加更新消息已读状态的方法 if (Get.isLogEnable) { Get.log('通知消息已读: $from'); } } } catch (e) { if (Get.isLogEnable) { Get.log('通知消息已读失败: $e'); } } } /// 通知消息已送达 void _notifyMessageDelivered(List messages, String to) { try { // 找到对应的 ChatController 并通知消息已送达 for (var message in messages) { final targetId = message.to; if (targetId != null) { final controller = _activeChatControllers[targetId]; if (controller != null) { // TODO: 在 ChatController 中添加更新消息送达状态的方法 if (Get.isLogEnable) { Get.log('通知消息已送达: $targetId'); } } } } } catch (e) { if (Get.isLogEnable) { Get.log('通知消息已送达失败: $e'); } } } /// 通知消息撤回 void _notifyMessageRecalled(List messages) { try { // 通知所有相关的 ChatController 更新消息列表 for (var message in messages) { final fromId = message.from; final toId = message.to; // 通知发送方的 ChatController if (fromId != null) { final controller = _activeChatControllers[fromId]; if (controller != null) { // TODO: 在 ChatController 中添加处理消息撤回的方法 if (Get.isLogEnable) { Get.log('通知消息撤回(发送方): $fromId'); } } } // 通知接收方的 ChatController if (toId != null) { final controller = _activeChatControllers[toId]; if (controller != null) { // TODO: 在 ChatController 中添加处理消息撤回的方法 if (Get.isLogEnable) { Get.log('通知消息撤回(接收方): $toId'); } } } } // 刷新会话列表 _refreshConversationList(); } catch (e) { if (Get.isLogEnable) { Get.log('通知消息撤回失败: $e'); } } } /// 通知消息状态变更 void _notifyMessageStatusChanged(EMMessage message, MessageStatus status, EMError? error) { try { final targetId = message.to; if (targetId != null) { final controller = _activeChatControllers[targetId]; if (controller != null) { // TODO: 在 ChatController 中添加更新消息状态的方法 if (Get.isLogEnable) { Get.log('通知消息状态变更: $targetId, status=$status'); } } } } catch (e) { if (Get.isLogEnable) { Get.log('通知消息状态变更失败: $e'); } } } */ /// 撤回消息 Future recallMessage(EMMessage message) async { try { final messageId = message.msgId; if (messageId.isEmpty) { if (Get.isLogEnable) { Get.log('❌ [IMManager] 消息ID为空,无法撤回'); } return false; } await EMClient.getInstance.chatManager.recallMessage(messageId); if (Get.isLogEnable) { Get.log('✅ [IMManager] 消息撤回成功: $messageId'); } // 刷新会话列表 _refreshConversationList(); return true; } catch (e) { if (Get.isLogEnable) { Get.log('❌ [IMManager] 消息撤回失败: $e'); } return false; } } /// 删除消息 Future deleteMessage(String conversationId, String messageId, {bool deleteRemote = false}) async { try { final conversation = await EMClient.getInstance.chatManager.getConversation( conversationId, type: EMConversationType.Chat, createIfNeed: false, ); if (conversation != null) { await conversation.deleteMessage(messageId); if (Get.isLogEnable) { Get.log('✅ [IMManager] 消息删除成功: $messageId'); } // 刷新会话列表 _refreshConversationList(); return true; } return false; } catch (e) { if (Get.isLogEnable) { Get.log('❌ [IMManager] 消息删除失败: $e'); } return false; } } /// 标记消息为已读 Future markMessageAsRead(String conversationId, String messageId) async { try { final conversation = await EMClient.getInstance.chatManager.getConversation( conversationId, type: EMConversationType.Chat, createIfNeed: false, ); if (conversation != null) { await conversation.markMessageAsRead(messageId); if (Get.isLogEnable) { Get.log('✅ [IMManager] 消息标记为已读: $messageId'); } // 刷新会话列表 _refreshConversationList(); return true; } return false; } catch (e) { if (Get.isLogEnable) { Get.log('❌ [IMManager] 标记消息为已读失败: $e'); } return false; } } /// 标记会话所有消息为已读 Future markAllMessagesAsRead(String conversationId) async { try { final conversation = await EMClient.getInstance.chatManager.getConversation( conversationId, type: EMConversationType.Chat, createIfNeed: false, ); if (conversation != null) { await conversation.markAllMessagesAsRead(); if (Get.isLogEnable) { Get.log('✅ [IMManager] 会话所有消息标记为已读: $conversationId'); } // 刷新会话列表 _refreshConversationList(); return true; } return false; } catch (e) { if (Get.isLogEnable) { Get.log('❌ [IMManager] 标记会话所有消息为已读失败: $e'); } return false; } } /// 重发消息 Future resendMessage(EMMessage failedMessage) async { try { final targetId = failedMessage.to ?? failedMessage.conversationId; if (targetId == null || targetId.isEmpty) { if (Get.isLogEnable) { Get.log('❌ [IMManager] 重发消息失败:目标ID为空'); } return null; } EMMessage? newMessage; // 根据消息类型重新创建消息 switch (failedMessage.body.type) { case MessageType.TXT: final textBody = failedMessage.body as EMTextMessageBody; newMessage = EMMessage.createTxtSendMessage( targetId: targetId, content: textBody.content, ); break; case MessageType.IMAGE: final imageBody = failedMessage.body as EMImageMessageBody; final localPath = imageBody.localPath; if (localPath.isEmpty) { if (Get.isLogEnable) { Get.log('❌ [IMManager] 重发图片消息失败:本地路径为空'); } return null; } newMessage = EMMessage.createImageSendMessage( targetId: targetId, filePath: localPath, sendOriginalImage: false, ); break; case MessageType.VOICE: final voiceBody = failedMessage.body as EMVoiceMessageBody; final localPath = voiceBody.localPath; if (localPath.isEmpty) { if (Get.isLogEnable) { Get.log('❌ [IMManager] 重发语音消息失败:本地路径为空'); } return null; } newMessage = EMMessage.createVoiceSendMessage( targetId: targetId, filePath: localPath, duration: voiceBody.duration, ); break; case MessageType.VIDEO: final videoBody = failedMessage.body as EMVideoMessageBody; final localPath = videoBody.localPath; if (localPath.isEmpty) { if (Get.isLogEnable) { Get.log('❌ [IMManager] 重发视频消息失败:本地路径为空'); } return null; } newMessage = EMMessage.createVideoSendMessage( targetId: targetId, filePath: localPath, duration: videoBody.duration ?? 0, thumbnailLocalPath: videoBody.thumbnailLocalPath, ); break; default: if (Get.isLogEnable) { Get.log('❌ [IMManager] 不支持重发该类型的消息: ${failedMessage.body.type}'); } return null; } // 重新发送消息 final result = await EMClient.getInstance.chatManager.sendMessage(newMessage); if (Get.isLogEnable) { Get.log('✅ [IMManager] 消息重发成功: ${newMessage.msgId}'); } return result; } catch (e) { if (Get.isLogEnable) { Get.log('❌ [IMManager] 重发消息失败: $e'); } return null; } } /// 清理资源 void dispose() { try { if (_listenersRegistered) { EMClient.getInstance.removeConnectionEventHandler(_connectionHandlerKey); EMClient.getInstance.chatManager.removeEventHandler(_chatHandlerKey); _listenersRegistered = false; if (Get.isLogEnable) { Get.log('✅ [IMManager] 资源清理完成'); } } } catch (e) { if (Get.isLogEnable) { Get.log('❌ [IMManager] 清理资源失败: $e'); } } } } // 导出单例实例 final IMManager imManager = IMManager();