Files
open-im-sdk-flutter-m/lib/src/manager/im_manager.dart
gem 830ebf6b93 Merge commit '0a175bb8b1eb8d4a40c66107ca77a2ff33212bf9' into pre_merge
# Conflicts:
#	.idea/libraries/Dart_SDK.xml
#	android/build.gradle
#	android/gradle/wrapper/gradle-wrapper.properties
#	android/src/main/AndroidManifest.xml
#	example/android/app/build.gradle
#	example/android/build.gradle
#	example/android/settings.gradle
#	example/lib/main.dart
#	example/pubspec.lock
#	ios/flutter_openim_sdk.podspec
#	lib/src/manager/im_conversation_manager.dart
#	lib/src/manager/im_friendship_manager.dart
#	lib/src/models/notification_info.dart
2025-12-10 18:13:02 +08:00

738 lines
27 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import 'dart:convert';
import 'dart:developer';
import 'dart:io';
import 'package:flutter/services.dart';
import 'package:flutter_openim_sdk/flutter_openim_sdk.dart';
import 'package:flutter_openim_sdk/src/logger.dart';
class IMManager {
MethodChannel _channel;
late ConversationManager conversationManager;
late FriendshipManager friendshipManager;
late MessageManager messageManager;
late GroupManager groupManager;
late UserManager userManager;
late ChannelManager channelManager;
late OnConnectListener _connectListener;
OnListenerForService? _listenerForService;
OnUploadFileListener? _uploadFileListener;
OnUploadLogsListener? _uploadLogsListener;
late String userID;
late UserInfo userInfo;
bool isLogined = false;
String? token;
IMManager(this._channel) {
conversationManager = ConversationManager(_channel);
friendshipManager = FriendshipManager(_channel);
messageManager = MessageManager(_channel);
groupManager = GroupManager(_channel);
channelManager = ChannelManager(_channel);
userManager = UserManager(_channel);
_addNativeCallback(_channel);
}
void _addNativeCallback(MethodChannel _channel) {
_channel.setMethodCallHandler((call) {
try {
Logger.print('Flutter : $call');
if (call.method == ListenerType.connectListener) {
String type = call.arguments['type'];
switch (type) {
case 'onConnectFailed':
int? errCode = call.arguments['errCode'];
String? errMsg = call.arguments['errMsg'];
_connectListener.connectFailed(errCode, errMsg);
break;
case 'onConnecting':
_connectListener.connecting();
break;
case 'onConnectSuccess':
_connectListener.connectSuccess();
break;
case 'onKickedOffline':
_connectListener.kickedOffline();
break;
case 'onUserTokenExpired':
_connectListener.userTokenExpired();
break;
case 'onUserTokenInvalid':
_connectListener.userTokenInvalid();
break;
}
} else if (call.method == ListenerType.userListener) {
String type = call.arguments['type'];
dynamic data = call.arguments['data'];
switch (type) {
case 'onSelfInfoUpdated':
userInfo = Utils.toObj(data, (map) => UserInfo.fromJson(map));
userManager.listener.selfInfoUpdated(userInfo);
break;
case 'onUserStatusChanged':
final status =
Utils.toObj(data, (map) => UserStatusInfo.fromJson(map));
userManager.listener.userStatusChanged(status);
break;
}
} else if (call.method == ListenerType.groupListener) {
String type = call.arguments['type'];
dynamic data = call.arguments['data'];
switch (type) {
case 'onGroupApplicationAccepted':
final i = Utils.toObj(
data, (map) => GroupApplicationInfo.fromJson(map));
groupManager.listener.groupApplicationAccepted(i);
break;
case 'onGroupApplicationAdded':
final i = Utils.toObj(
data, (map) => GroupApplicationInfo.fromJson(map));
groupManager.listener.groupApplicationAdded(i);
break;
case 'onGroupApplicationDeleted':
final i = Utils.toObj(
data, (map) => GroupApplicationInfo.fromJson(map));
groupManager.listener.groupApplicationDeleted(i);
break;
case 'onGroupApplicationRejected':
final i = Utils.toObj(
data, (map) => GroupApplicationInfo.fromJson(map));
groupManager.listener.groupApplicationRejected(i);
break;
case 'onGroupDismissed':
final i = Utils.toObj(data, (map) => GroupInfo.fromJson(map));
groupManager.listener.groupDismissed(i);
break;
case 'onGroupInfoChanged':
final i = Utils.toObj(data, (map) => GroupInfo.fromJson(map));
groupManager.listener.groupInfoChanged(i);
break;
case 'onGroupMemberAdded':
final i =
Utils.toObj(data, (map) => GroupMembersInfo.fromJson(map));
groupManager.listener.groupMemberAdded(i);
break;
case 'onGroupMemberDeleted':
final i =
Utils.toObj(data, (map) => GroupMembersInfo.fromJson(map));
groupManager.listener.groupMemberDeleted(i);
break;
case 'onGroupMemberInfoChanged':
final i =
Utils.toObj(data, (map) => GroupMembersInfo.fromJson(map));
groupManager.listener.groupMemberInfoChanged(i);
break;
case 'onJoinedGroupAdded':
final i = Utils.toObj(data, (map) => GroupInfo.fromJson(map));
groupManager.listener.joinedGroupAdded(i);
break;
case 'onJoinedGroupDeleted':
final i = Utils.toObj(data, (map) => GroupInfo.fromJson(map));
groupManager.listener.joinedGroupDeleted(i);
break;
}
} else if (call.method == ListenerType.channelListener) {
String type = call.arguments['type'];
dynamic data = call.arguments['data'];
switch (type) {
case 'onChannelDismissed':
final i = Utils.toObj(data, (map) => ChannelInfo.fromJson(map));
channelManager.listener.channelDismissed(i);
break;
case 'onChannelInfoChanged':
final i = Utils.toObj(data, (map) => ChannelInfo.fromJson(map));
channelManager.listener.channelInfoChanged(i);
break;
case 'onChannelMemberAdded':
final i =
Utils.toObj(data, (map) => ChannelMembersInfo.fromJson(map));
channelManager.listener.channelMemberAdded(i);
break;
case 'onChannelMemberDeleted':
final i =
Utils.toObj(data, (map) => ChannelMembersInfo.fromJson(map));
channelManager.listener.channelMemberDeleted(i);
break;
case 'onChannelMemberInfoChanged':
final i =
Utils.toObj(data, (map) => ChannelMembersInfo.fromJson(map));
channelManager.listener.channelMemberInfoChanged(i);
break;
case 'onJoinedChannelAdded':
final i = Utils.toObj(data, (map) => ChannelInfo.fromJson(map));
channelManager.listener.joinedChannelAdded(i);
break;
case 'onJoinedChannelDeleted':
final i = Utils.toObj(data, (map) => ChannelInfo.fromJson(map));
channelManager.listener.joinedChannelDeleted(i);
break;
}
} else if (call.method == ListenerType.advancedMsgListener) {
var type = call.arguments['type'];
// var id = call.arguments['data']['id'];
switch (type) {
case 'onMsgDeleted':
var value = call.arguments['data']['message'];
final msg = Utils.toObj(value, (map) => Message.fromJson(map));
messageManager.msgListener.msgDeleted(msg);
break;
case 'onNewRecvMessageRevoked':
var value = call.arguments['data']['messageRevoked'];
var info = Utils.toObj(value, (map) => RevokedInfo.fromJson(map));
messageManager.msgListener.newRecvMessageRevoked(info);
break;
case 'onNewRecvMessageEdited':
var value = call.arguments['data']['messageEdited'];
var info = Utils.toObj(value, (map) => EditedInfo.fromJson(map));
messageManager.msgListener.newRecvMessageEdited(info);
break;
case 'onRecvC2CReadReceipt':
var value = call.arguments['data']['msgReceiptList'];
var list =
Utils.toList(value, (map) => ReadReceiptInfo.fromJson(map));
messageManager.msgListener.recvC2CReadReceipt(list);
break;
case 'onRecvNewMessage':
var value = call.arguments['data']['message'];
final msg = Utils.toObj(value, (map) => Message.fromJson(map));
messageManager.msgListener.recvNewMessage(msg);
break;
case 'onRecvOfflineNewMessage':
var value = call.arguments['data']['message'];
final msg = Utils.toObj(value, (map) => Message.fromJson(map));
messageManager.msgListener.recvOfflineNewMessage(msg);
break;
case 'onRecvOnlineOnlyMessage':
var value = call.arguments['data']['message'];
final msg = Utils.toObj(value, (map) => Message.fromJson(map));
messageManager.msgListener.recvOnlineOnlyMessage(msg);
break;
}
} else if (call.method == ListenerType.msgSendProgressListener) {
String type = call.arguments['type'];
dynamic data = call.arguments['data'];
String msgID = data['clientMsgID'] ?? '';
int progress = data['progress'] ?? 100;
switch (type) {
case 'onProgress':
messageManager.msgSendProgressListener?.progress(
msgID,
progress,
);
break;
}
} else if (call.method == ListenerType.conversationListener) {
String type = call.arguments['type'];
dynamic data = call.arguments['data'];
switch (type) {
case 'onSyncServerStart':
print('dart onSyncServerStart: $data');
conversationManager.listener.syncServerStart(data);
break;
case 'onSyncServerProgress':
conversationManager.listener.syncServerProgress(data);
break;
case 'onSyncServerFinish':
conversationManager.listener.syncServerFinish(data);
break;
case 'onSyncServerFailed':
conversationManager.listener.syncServerFailed(data);
break;
case 'onNewConversation':
var list =
Utils.toList(data, (map) => ConversationInfo.fromJson(map));
conversationManager.listener.newConversation(list);
break;
case 'onConversationChanged':
var list =
Utils.toList(data, (map) => ConversationInfo.fromJson(map));
conversationManager.listener.conversationChanged(list);
break;
case 'onTotalUnreadMessageCountChanged':
conversationManager.listener
.totalUnreadMessageCountChanged(data ?? 0);
break;
case 'onConversationUserInputStatusChanged':
final i = Utils.toObj(
data, (map) => InputStatusChangedData.fromJson(map));
conversationManager.listener
.conversationUserInputStatusChanged(i);
break;
}
} else if (call.method == ListenerType.friendListener) {
String type = call.arguments['type'];
dynamic data = call.arguments['data'];
switch (type) {
case 'onBlackAdded':
final u = Utils.toObj(data, (map) => BlacklistInfo.fromJson(map));
friendshipManager.listener.blackAdded(u);
break;
case 'onBlackDeleted':
final u = Utils.toObj(data, (map) => BlacklistInfo.fromJson(map));
friendshipManager.listener.blackDeleted(u);
break;
case 'onFriendAdded':
final u = Utils.toObj(data, (map) => FriendInfo.fromJson(map));
friendshipManager.listener.friendAdded(u);
break;
case 'onFriendApplicationAccepted':
final u = Utils.toObj(
data, (map) => FriendApplicationInfo.fromJson(map));
friendshipManager.listener.friendApplicationAccepted(u);
break;
case 'onFriendApplicationAdded':
final u = Utils.toObj(
data, (map) => FriendApplicationInfo.fromJson(map));
friendshipManager.listener.friendApplicationAdded(u);
break;
case 'onFriendApplicationDeleted':
final u = Utils.toObj(
data, (map) => FriendApplicationInfo.fromJson(map));
friendshipManager.listener.friendApplicationDeleted(u);
break;
case 'onFriendApplicationRejected':
final u = Utils.toObj(
data, (map) => FriendApplicationInfo.fromJson(map));
friendshipManager.listener.friendApplicationRejected(u);
break;
case 'onFriendDeleted':
final u = Utils.toObj(data, (map) => FriendInfo.fromJson(map));
friendshipManager.listener.friendDeleted(u);
break;
case 'onFriendInfoChanged':
final u = Utils.toObj(data, (map) => FriendInfo.fromJson(map));
friendshipManager.listener.friendInfoChanged(u);
break;
}
} else if (call.method == ListenerType.customBusinessListener) {
String type = call.arguments['type'];
String data = call.arguments['data'];
switch (type) {
case 'onRecvCustomBusinessMessage':
messageManager.customBusinessListener
?.recvCustomBusinessMessage(data);
break;
}
} else if (call.method == ListenerType.listenerForService) {
String type = call.arguments['type'];
String data = call.arguments['data'];
switch (type) {
case 'onFriendApplicationAccepted':
final u = Utils.toObj(
data, (map) => FriendApplicationInfo.fromJson(map));
_listenerForService?.friendApplicationAccepted(u);
break;
case 'onFriendApplicationAdded':
final u = Utils.toObj(
data, (map) => FriendApplicationInfo.fromJson(map));
_listenerForService?.friendApplicationAdded(u);
break;
case 'onGroupApplicationAccepted':
final i = Utils.toObj(
data, (map) => GroupApplicationInfo.fromJson(map));
_listenerForService?.groupApplicationAccepted(i);
break;
case 'onGroupApplicationAdded':
final i = Utils.toObj(
data, (map) => GroupApplicationInfo.fromJson(map));
_listenerForService?.groupApplicationAdded(i);
break;
case 'onRecvNewMessage':
final msg = Utils.toObj(data, (map) => Message.fromJson(map));
_listenerForService?.recvNewMessage(msg);
break;
}
} else if (call.method == ListenerType.uploadLogsListener) {
String type = call.arguments['type'];
dynamic data = call.arguments['data'];
switch (type) {
case 'onProgress':
int size = data['size'];
int current = data['current'];
_uploadLogsListener?.onProgress(current, size);
}
} else if (call.method == ListenerType.uploadFileListener) {
String type = call.arguments['type'];
dynamic data = call.arguments['data'];
switch (type) {
case 'complete':
String id = data['id'];
int size = data['size'];
String url = data['url'];
int type = data['type'];
_uploadFileListener?.complete(id, size, url, type);
break;
case 'hashPartComplete':
String id = data['id'];
String partHash = data['partHash'];
String fileHash = data['fileHash'];
_uploadFileListener?.hashPartComplete(id, partHash, fileHash);
break;
case 'hashPartProgress':
String id = data['id'];
int index = data['index'];
int size = data['size'];
String partHash = data['partHash'];
_uploadFileListener?.hashPartProgress(id, index, size, partHash);
break;
case 'open':
String id = data['id'];
int size = data['size'];
_uploadFileListener?.open(id, size);
break;
case 'partSize':
String id = data['id'];
int partSize = data['partSize'];
int num = data['num'];
_uploadFileListener?.partSize(id, partSize, num);
break;
case 'uploadProgress':
String id = data['id'];
int fileSize = data['fileSize'];
int streamSize = data['streamSize'];
int storageSize = data['storageSize'];
_uploadFileListener?.uploadProgress(
id, fileSize, streamSize, storageSize);
break;
case 'uploadID':
String id = data['id'];
String uploadID = data['uploadID'];
_uploadFileListener?.uploadID(id, uploadID);
break;
case 'uploadPartComplete':
String id = data['id'];
int index = data['index'];
int partSize = data['partSize'];
String partHash = data['partHash'];
_uploadFileListener?.uploadPartComplete(
id, index, partSize, partHash);
break;
}
}
} catch (error, stackTrace) {
Logger.print(
"回调失败了。${call.method} ${call.arguments['type']} ${call.arguments['data']} $error $stackTrace");
}
return Future.value(null);
});
}
Future<bool?> init(
InitConfig config,
OnConnectListener listener, {
String? operationID,
}) {
_connectListener = listener;
config.logFilePath ??= config.dataDir;
return _channel.invokeMethod(
'initSDK',
_buildParam(
{
...config.toMap(),
"operationID": Utils.checkOperationID(operationID),
},
),
);
}
/// Initialize the SDK
/// [platform] Platform ID [IMPlatform]
/// [apiAddr] SDK API address
/// [wsAddr] SDK WebSocket address
/// [dataDir] SDK database storage directory
/// [logLevel] Log level, 1: no printing
/// [enabledEncryption] true: encryption
/// [enabledCompression] true: compression
Future<dynamic> initSDK({
required int platformID,
required String apiAddr,
required String wsAddr,
required String dataDir,
required OnConnectListener listener,
int logLevel = 6,
bool isNeedEncryption = false,
bool isCompression = false,
bool isLogStandardOutput = true,
String? logFilePath,
String? operationID,
}) {
_connectListener = listener;
return _channel.invokeMethod(
'initSDK',
_buildParam(
{
"platformID": platformID,
"apiAddr": apiAddr,
"wsAddr": wsAddr,
"dataDir": dataDir,
"logLevel": logLevel,
"isCompression": isCompression,
'isNeedEncryption': isNeedEncryption,
"isLogStandardOutput": isLogStandardOutput,
"logFilePath": logFilePath,
'systemType': 'flutter',
"operationID": Utils.checkOperationID(operationID),
},
),
);
}
/// Deinitialize the SDK
void unInitSDK() {
_channel.invokeMethod('unInitSDK', _buildParam({}));
}
/// Login
/// [userID] User ID
/// [token] Login token obtained from the business server
/// [defaultValue] Default value to use if login fails
Future<UserInfo> login({
required String userID,
required String token,
String? operationID,
Future<UserInfo> Function()? defaultValue,
bool checkLoginStatus = true,
}) async {
int? status;
if (checkLoginStatus) {
// 1: logout 2: logging 3: logged
status = await getLoginStatus();
}
if (status != LoginStatus.logging && status != LoginStatus.logged) {
await _channel.invokeMethod(
'login',
_buildParam({
'userID': userID,
'token': token,
'operationID': Utils.checkOperationID(operationID),
}),
);
}
this.isLogined = true;
this.userID = userID;
this.token = token;
try {
return this.userInfo = await userManager.getSelfUserInfo();
} catch (error, stackTrace) {
log('login e: $error s: $stackTrace');
if (null != defaultValue) {
return this.userInfo = await (defaultValue.call());
}
return Future.error(error, stackTrace);
}
// return uInfo;
}
/// Logout
Future<dynamic> logout({String? operationID}) async {
var value = await _channel.invokeMethod(
'logout',
_buildParam({
'operationID': Utils.checkOperationID(operationID),
}),
);
this.isLogined = false;
this.token = null;
return value;
}
/// Get login status
/// 1: logout 2: logging 3: logged
Future<int?> getLoginStatus({
String? operationID,
}) =>
_channel.invokeMethod<int>(
'getLoginStatus',
_buildParam({
'operationID': Utils.checkOperationID(operationID),
}),
);
/// Get the current logged-in user ID
Future<String> getLoginUserID() async => userID;
/// Get the current logged-in user information
Future<UserInfo> getLoginUserInfo() async => userInfo;
/// [id] Same as [OnUploadFileListener] ID, to distinguish which file callback it is
Future uploadFile({
required String id,
required String filePath,
required String fileName,
String? contentType,
String? cause,
String? operationID,
}) =>
_channel.invokeMethod(
'uploadFile',
_buildParam({
'id': id,
'filePath': filePath,
'name': fileName,
'contentType': contentType,
'cause': cause,
'operationID': Utils.checkOperationID(operationID),
}),
);
/// Update the Firebase client registration token
/// [fcmToken] Firebase token
Future updateFcmToken({
required String fcmToken,
required int expireTime,
String? operationID,
}) =>
_channel.invokeMethod(
'updateFcmToken',
_buildParam({
'fcmToken': fcmToken,
'expireTime': expireTime,
'operationID': Utils.checkOperationID(operationID),
}));
/// Upload logs
Future uploadLogs({
String? ex,
int line = 0,
String? operationID,
}) =>
_channel.invokeMethod(
'uploadLogs',
_buildParam({
'ex': ex,
'line': line,
'operationID': Utils.checkOperationID(operationID),
}));
Future logs({
int logLevel = 5,
String? file,
int line = 0,
String? msgs,
String? err,
List<dynamic>? keyAndValues,
String? operationID,
}) =>
_channel.invokeMethod(
'logs',
_buildParam({
'line': line,
'logLevel': logLevel,
'file': file,
'msgs': msgs,
'err': err,
if (keyAndValues != null) 'keyAndValue': jsonEncode(keyAndValues),
'operationID': Utils.checkOperationID(operationID),
}));
void setUploadLogsListener(OnUploadLogsListener listener) {
_uploadLogsListener = listener;
}
void setUploadFileListener(OnUploadFileListener listener) {
_uploadFileListener = listener;
}
///
Future setListenerForService(OnListenerForService listener) {
if (Platform.isAndroid) {
this._listenerForService = listener;
return _channel.invokeMethod('setListenerForService', _buildParam({}));
} else {
throw UnsupportedError("only supprot android platform");
}
}
//sdk多了这几个方法增加一下
Future setNotificationVisibilityRule(
{required int notificationType,
required int visibilityType,
String? operationID}) =>
_channel.invokeMethod(
'setNotificationVisibilityRule',
_buildParam({
'notificationType': notificationType,
'visibilityType': visibilityType,
'operationID': Utils.checkOperationID(operationID),
}),
);
Future setNotificationVisibilityRules(
{required String rulesJSON, String? operationID}) =>
_channel.invokeMethod(
'setNotificationVisibilityRules',
_buildParam({
'rulesJSON': rulesJSON,
'operationID': Utils.checkOperationID(operationID),
}),
);
Future getNotificationVisibilityRule(
{required int notificationType, String? operationID}) =>
_channel.invokeMethod<int>(
'getNotificationVisibilityRule',
_buildParam({
'notificationType': notificationType,
'operationID': Utils.checkOperationID(operationID),
}),
);
Future<Map<int, int>> getNotificationVisibilityRules(
{String? operationID}) async {
var result = await _channel.invokeMethod<Map<int, int>>(
'getNotificationVisibilityRules',
_buildParam({
'operationID': Utils.checkOperationID(operationID),
}),
);
return result ?? <int, int>{};
}
Future enableNotificationVisibilityRule(
{required int notificationType, String? operationID}) =>
_channel.invokeMethod(
'enableNotificationVisibilityRule',
_buildParam({
'notificationType': notificationType,
'operationID': Utils.checkOperationID(operationID),
}),
);
Future disableNotificationVisibilityRule(
{required int notificationType, String? operationID}) =>
_channel.invokeMethod(
'disableNotificationVisibilityRule',
_buildParam({
'notificationType': notificationType,
'operationID': Utils.checkOperationID(operationID),
}),
);
Future deleteNotificationVisibilityRule(
{required int notificationType, String? operationID}) =>
_channel.invokeMethod(
'deleteNotificationVisibilityRule',
_buildParam({
'notificationType': notificationType,
'operationID': Utils.checkOperationID(operationID),
}),
);
Future resetNotificationVisibilityRules({String? operationID}) =>
_channel.invokeMethod(
'resetNotificationVisibilityRules',
_buildParam({
'operationID': Utils.checkOperationID(operationID),
}),
);
MethodChannel get channel => _channel;
static Map _buildParam(Map<String, dynamic> param) {
param["ManagerName"] = "imManager";
param = Utils.cleanMap(param);
return param;
}
}