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.
450 lines
13 KiB
450 lines
13 KiB
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;
|
|
|
|
// 存储活跃的 ChatController 实例,key 为 userId
|
|
final Map<String, ChatController> _activeChatControllers = {};
|
|
|
|
IMManager._internal() {
|
|
print('IMManager instance created');
|
|
}
|
|
|
|
/// 初始化IM SDK
|
|
Future<bool> 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 {
|
|
// 消息监听器
|
|
|
|
// 连接监听器
|
|
EMClient.getInstance.addConnectionEventHandler(
|
|
'',
|
|
EMConnectionEventHandler(
|
|
onConnected: () {
|
|
print('Connected to IM server');
|
|
},
|
|
onDisconnected: () {
|
|
print('Disconnected from IM server:');
|
|
},
|
|
onTokenDidExpire: () {
|
|
print('IM token about to expire');
|
|
},
|
|
onUserKickedByOtherDevice: () {
|
|
print('User kicked out of IM server');
|
|
},
|
|
),
|
|
);
|
|
EMClient.getInstance.chatManager.addEventHandler(
|
|
// EMChatEventHandler 对应的 key。
|
|
"",
|
|
EMChatEventHandler(
|
|
onMessagesReceived: (messages) {
|
|
// 收到新消息时,更新会话列表
|
|
_refreshConversationList();
|
|
|
|
// 通知对应的 ChatController 更新消息列表
|
|
_notifyChatControllers(messages);
|
|
|
|
for (var msg in messages) {
|
|
switch (msg.body.type) {
|
|
case MessageType.TXT:
|
|
{}
|
|
break;
|
|
case MessageType.IMAGE:
|
|
{}
|
|
break;
|
|
case MessageType.VIDEO:
|
|
{}
|
|
break;
|
|
case MessageType.LOCATION:
|
|
{}
|
|
break;
|
|
case MessageType.VOICE:
|
|
{}
|
|
break;
|
|
case MessageType.FILE:
|
|
{}
|
|
break;
|
|
case MessageType.CUSTOM:
|
|
{}
|
|
break;
|
|
case MessageType.COMBINE:
|
|
{}
|
|
break;
|
|
case MessageType.CMD:
|
|
{
|
|
// 当前回调中不会有 CMD 类型消息,CMD 类型消息通过 `EMChatEventHandler#onCmdMessagesReceived` 回调接收
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
},
|
|
),
|
|
);
|
|
} catch (e) {
|
|
print('Failed to register listeners: $e');
|
|
}
|
|
}
|
|
|
|
/// 登录IM服务
|
|
Future<bool> 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<bool> logout() async {
|
|
try {
|
|
await EMClient.getInstance.logout();
|
|
print('IM logout successful');
|
|
return true;
|
|
} catch (e) {
|
|
print('IM logout failed: $e');
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/// 发送文本消息
|
|
Future<EMMessage?> 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<EMMessage?> 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);
|
|
print('Image message sent successfully');
|
|
return message;
|
|
} catch (e) {
|
|
print('Failed to send image message: $e');
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/// 发送图片消息
|
|
Future<EMMessage?> 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<EMMessage?> 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<List<EMConversation>> getConversations() async {
|
|
return EMClient.getInstance.chatManager.loadAllConversations();
|
|
}
|
|
|
|
/// 获取好有列表
|
|
Future<Map<String, EMUserInfo>> getContacts(String userId) async {
|
|
return await EMClient.getInstance.userInfoManager.fetchUserInfoById([
|
|
userId,
|
|
]);
|
|
}
|
|
|
|
/// 获取指定会话的消息记录
|
|
Future<List<EMMessage?>> getMessages(
|
|
String conversationId, {
|
|
int pageSize = 20,
|
|
String? startMsgId,
|
|
}) async {
|
|
EMConversationType convType = EMConversationType.Chat;
|
|
EMCursorResult<EMMessage?> cursor = await EMClient.getInstance.chatManager
|
|
.fetchHistoryMessagesByOption(
|
|
conversationId,
|
|
convType,
|
|
pageSize: pageSize,
|
|
);
|
|
return cursor.data;
|
|
}
|
|
|
|
/// 获取用户信息
|
|
Future<EMUserInfo?> 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<EMMessage> 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<ConversationController>()) {
|
|
final conversationController = Get.find<ConversationController>();
|
|
conversationController.refreshConversations();
|
|
}
|
|
} catch (e) {
|
|
// ConversationController 可能未注册,忽略错误
|
|
if (Get.isLogEnable) {
|
|
Get.log('刷新会话列表失败: $e');
|
|
}
|
|
}
|
|
}
|
|
|
|
/// 添加用户到黑名单
|
|
Future<void> addToBlacklist(String userId) async {
|
|
try {
|
|
await EMClient.getInstance.contactManager.addUserToBlockList(userId);
|
|
print('已将用户 $userId 添加到黑名单');
|
|
} catch (e) {
|
|
print('添加黑名单失败: $e');
|
|
rethrow;
|
|
}
|
|
}
|
|
|
|
/// 从黑名单移除用户
|
|
Future<void> removeFromBlacklist(String userId) async {
|
|
try {
|
|
await EMClient.getInstance.contactManager.removeUserFromBlockList(userId);
|
|
print('已将用户 $userId 从黑名单移除');
|
|
} catch (e) {
|
|
print('移除黑名单失败: $e');
|
|
rethrow;
|
|
}
|
|
}
|
|
|
|
/// 获取黑名单列表
|
|
Future<List<String>> getBlacklist() async {
|
|
try {
|
|
final list = await EMClient.getInstance.contactManager.getBlockListFromServer();
|
|
print('获取黑名单列表成功,共 ${list.length} 个用户');
|
|
return list;
|
|
} catch (e) {
|
|
print('获取黑名单列表失败: $e');
|
|
return [];
|
|
}
|
|
}
|
|
|
|
/// 清理资源
|
|
void dispose() {
|
|
try {
|
|
EMClient.getInstance.removeConnectionEventHandler("");
|
|
} catch (e) {
|
|
print('Failed to dispose resources: $e');
|
|
}
|
|
}
|
|
}
|
|
|
|
// 导出单例实例
|
|
final IMManager imManager = IMManager();
|