main
hrxiang 4 years ago
parent 54d0d62bd9
commit 475f350b99
  1. 1
      android/src/main/java/io/openim/flutter_openim_sdk/FlutterOpenimSdkPlugin.java
  2. 4
      android/src/main/java/io/openim/flutter_openim_sdk/listener/AdvancedMsgListenerImpl.java
  3. 4
      android/src/main/java/io/openim/flutter_openim_sdk/listener/MsgSendProgressListener.java
  4. 4
      android/src/main/java/io/openim/flutter_openim_sdk/manager/ConversationManager.java
  5. 7
      android/src/main/java/io/openim/flutter_openim_sdk/manager/MessageManager.java
  6. 49
      android/src/main/java/io/openim/flutter_openim_sdk/util/CommonUtil.java
  7. 36
      lib/enum/listener_type.dart
  8. 18
      lib/enum/message_type.dart
  9. 27
      lib/flutter_openim_sdk.dart
  10. 14
      lib/listener/advanced_msg_listener.dart
  11. 15
      lib/listener/conversation_listener.dart
  12. 19
      lib/listener/friendship_listener.dart
  13. 21
      lib/listener/int_sdk_listener.dart
  14. 3
      lib/listener/msg_send_progress_listener.dart
  15. 97
      lib/manager/im_conversation_manager.dart
  16. 115
      lib/manager/im_friendship_manager.dart
  17. 7
      lib/manager/im_group_manager.dart
  18. 376
      lib/manager/im_manager.dart
  19. 217
      lib/manager/im_message_manager.dart
  20. 7
      lib/manager/im_offline_push_manager.dart
  21. 7
      lib/manager/im_signaling_manager.dart
  22. 77
      lib/models/conversation_info.dart
  23. 337
      lib/models/message.dart
  24. 65
      lib/models/user_info.dart
  25. 9
      lib/src/openim.dart
  26. 20
      test/flutter_openim_sdk_test.dart

@ -9,7 +9,6 @@ import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import io.flutter.embedding.engine.plugins.FlutterPlugin;
import io.flutter.plugin.common.EventChannel;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugin.common.MethodChannel.MethodCallHandler;

@ -11,9 +11,9 @@ public class AdvancedMsgListenerImpl implements OnAdvancedMsgListener {
final MethodChannel channel;
final Map<String, String> values = new HashMap<>();
public AdvancedMsgListenerImpl(MethodChannel channel, String id) {
public AdvancedMsgListenerImpl(MethodChannel channel, String listenerId) {
this.channel = channel;
this.values.put("id", id);
this.values.put("id", listenerId);
}
@Override

@ -8,10 +8,10 @@ import io.flutter.plugin.common.MethodChannel;
import io.openim.flutter_openim_sdk.util.CommonUtil;
import open_im_sdk.SendMsgCallBack;
public class SendMsgProgressListener implements SendMsgCallBack {
public class MsgSendProgressListener implements SendMsgCallBack {
public SendMsgProgressListener(MethodChannel.Result result, MethodChannel channel, MethodCall call) {
public MsgSendProgressListener(MethodChannel.Result result, MethodChannel channel, MethodCall call) {
this.result = result;
this.channel = channel;
this.call = call;

@ -17,13 +17,13 @@ public class ConversationManager {
public void setConversationListener(MethodCall methodCall, MethodChannel.Result result) {
Open_im_sdk.setConversationListener(new ConversationListenerImpl(channel));
}
public void getAllConversationList(MethodCall methodCall, MethodChannel.Result result) {
Open_im_sdk.getAllConversationList(new BaseImpl(result));
}
public void getOneConversation(MethodCall methodCall, MethodChannel.Result result) {
Open_im_sdk.getOneConversation(CommonUtil.getConversationId(methodCall), new BaseImpl(result));
Open_im_sdk.getOneConversation(CommonUtil.getConversationSourceId(methodCall), CommonUtil.getConversationSessionType(methodCall), new BaseImpl(result));
}
public void getMultipleConversation(MethodCall methodCall, MethodChannel.Result result) {

@ -7,7 +7,7 @@ import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
import io.openim.flutter_openim_sdk.listener.AdvancedMsgListenerImpl;
import io.openim.flutter_openim_sdk.listener.BaseImpl;
import io.openim.flutter_openim_sdk.listener.SendMsgProgressListener;
import io.openim.flutter_openim_sdk.listener.MsgSendProgressListener;
import io.openim.flutter_openim_sdk.util.CommonUtil;
import open_im_sdk.OnAdvancedMsgListener;
import open_im_sdk.Open_im_sdk;
@ -29,7 +29,6 @@ public class MessageManager {
AdvancedMsgListenerImpl listener = new AdvancedMsgListenerImpl(channel, key);
listeners.put(methodCall.argument(KEY_ID), listener);
Open_im_sdk.addAdvancedMsgListener(listener);
System.out.println("=================add msg listener======" + listener);
}
}
@ -37,12 +36,10 @@ public class MessageManager {
String key = methodCall.argument(KEY_ID);
OnAdvancedMsgListener listener = listeners.remove(key);
Open_im_sdk.removeAdvancedMsgListener(listener);
System.out.println("=================remove msg listener======" + listener);
}
public void sendMessage(MethodCall methodCall, MethodChannel.Result result) {
SendMsgProgressListener listener = new SendMsgProgressListener(result, channel, methodCall);
System.out.println("===============sendMessage===============");
MsgSendProgressListener listener = new MsgSendProgressListener(result, channel, methodCall);
Open_im_sdk.sendMessage(listener, CommonUtil.getSendMessageContent(methodCall), CommonUtil.getSendMessageReceiver(methodCall), CommonUtil.geSendMessageGroupId(methodCall), CommonUtil.getSendMessageOnlineOnly(methodCall));
}

@ -12,9 +12,6 @@ import io.openim.flutter_openim_sdk.FlutterOpenimSdkPlugin;
public class CommonUtil {
/**
* 主线程处理器
*/
private final static Handler MAIN_HANDLER = new Handler(Looper.getMainLooper());
public static <T> T getParamValue(MethodCall methodCall, String param) {
@ -42,14 +39,7 @@ public class CommonUtil {
MAIN_HANDLER.post(runnable);
}
/**
* 运行主线程返回错误结果执行
*
* @param result 返回结果对象
* @param errorCode 错误码
* @param errorMessage 错误信息
* @param errorDetails 错误内容
*/
public static void runMainThreadReturnError(final MethodChannel.Result result, final String errorCode, final String errorMessage, final Object errorDetails) {
MAIN_HANDLER.post(new Runnable() {
@Override
@ -63,33 +53,6 @@ public class CommonUtil {
runMainThreadReturnError(result, String.valueOf(errorCode), errorMessage, errorDetails);
}
/*
public static void returnError(final MethodChannel.Result result, int i, String s) {
HashMap<String, Object> err = new HashMap<String, Object>();
err.put("code", i);
err.put("desc", s);
result.success(err);
}
public static void returnError(final MethodChannel.Result result, int i, String s, HashMap<String, Object> data) {
HashMap<String, Object> err = new HashMap<String, Object>();
err.put("code", i);
err.put("desc", s);
err.put("data", data);
result.success(err);
}
public static <T> void returnSuccess(final MethodChannel.Result result, T data) {
HashMap<String, Object> succ = new HashMap<String, Object>();
succ.put("code", 0);
succ.put("desc", "ok");
succ.put("data", data);
result.success(succ);
}
*/
public static <T> void emitEvent(MethodChannel channel, String method, String type, Long errCode, String errMsg, T data) {
runMainThread(new Runnable() {
@Override
@ -243,6 +206,14 @@ public class CommonUtil {
return getSDKJsonParam(methodCall, KEY_CONVERSATION_IDS);
}
public static String getConversationSourceId(MethodCall methodCall) {
return getParamValue(methodCall, KEY_CONVERSATION_SOURCE_ID);
}
public static int getConversationSessionType(MethodCall methodCall) {
return getParamValue(methodCall, KEY_CONVERSATION_SESSION_TYPE);
}
public static String getConversationDraft(MethodCall methodCall) {
return getParamValue(methodCall, KEY_CONVERSATION_DRAFT);
}
@ -292,6 +263,8 @@ public class CommonUtil {
final static String KEY_CONVERSATION_IDS = "conversationIDList";
final static String KEY_CONVERSATION_DRAFT = "draftText";
final static String KEY_CONVERSATION_PINNED = "isPinned";
final static String KEY_CONVERSATION_SOURCE_ID = "sourceID";
final static String KEY_CONVERSATION_SESSION_TYPE = "sessionType";
// user info
final static String KEY_USER_IDS = "uidList";
}

@ -1,36 +0,0 @@
///
///
/// {@category Enums}
///
class ListenerType {
///
///
static const String simpleMsgListener = 'simpleMsgListener';
///
///
static const String initSDKListener = 'initSDKListener';
///
///
static const String groupListener = 'groupListener';
///
///
static const String advancedMsgListener = 'advancedMsgListener';
///
///
static const String conversationListener = 'conversationListener';
///
///
static const String friendListener = 'friendListener';
///
///
static const String signalingListener = 'signalingListener';
static const String messageProgressListener =
"messageProgressListener";
}

@ -1,18 +0,0 @@
class MessageType {
///
static const text = 101;
///
static const picture = 102;
///
static const voice = 103;
///
static const video = 104;
///
static const file = 105;
static const accept_friend = 201;
}

@ -1,9 +1,18 @@
import 'package:flutter/services.dart';
import 'package:flutter_openim_sdk/manager/im_manager.dart';
class FlutterOpenimSdk {
static const MethodChannel _channel =
const MethodChannel('flutter_openim_sdk');
static IMManager iMManager = IMManager(_channel);
}
export 'src/enum/listener_type.dart';
export 'src/enum/message_type.dart';
export 'src/listener/advanced_msg_listener.dart';
export 'src/listener/conversation_listener.dart';
export 'src/listener/friendship_listener.dart';
export 'src/listener/init_sdk_listener.dart';
export 'src/listener/msg_send_progress_listener.dart';
export 'src/manager/im_conversation_manager.dart';
export 'src/manager/im_friendship_manager.dart';
export 'src/manager/im_group_manager.dart';
export 'src/manager/im_manager.dart';
export 'src/manager/im_message_manager.dart';
export 'src/manager/im_offline_push_manager.dart';
export 'src/manager/im_signaling_manager.dart';
export 'src/models/conversation_info.dart';
export 'src/models/message.dart';
export 'src/models/user_info.dart';
export 'src/openim.dart';

@ -1,14 +0,0 @@
import 'package:flutter_openim_sdk/models/message.dart';
class AdvancedMsgListener {
/// listener uniquely identifies
final String id;
AdvancedMsgListener({required this.id});
void onRecvNewMessage(Message msg) {}
void onRecvC2CReadReceipt(Message msg) {}
void onRecvMessageRevoked(Message msg) {}
}

@ -1,15 +0,0 @@
import 'package:flutter_openim_sdk/models/conversation_info.dart';
class ConversationListener {
void onConversationChanged(List<ConversationInfo> list) {}
void onNewConversation(List<ConversationInfo> list) {}
void onSyncServerFailed() {}
void onSyncServerFinish() {}
void onSyncServerStart() {}
void onTotalUnreadMessageCountChanged(int i) {}
}

@ -1,19 +0,0 @@
class FriendshipListener {
void onBlackListAdd(dynamic u) {}
void onBlackListDeleted(dynamic u) {}
void onFriendApplicationListAccept(dynamic u) {}
void onFriendApplicationListAdded(dynamic u) {}
void onFriendApplicationListDeleted(dynamic u) {}
void onFriendApplicationListReject(dynamic u) {}
void onFriendInfoChanged(dynamic u) {}
void onFriendListAdded(dynamic u) {}
void onFriendListDeleted(dynamic u) {}
}

@ -1,21 +0,0 @@
import 'package:flutter_openim_sdk/models/user_info.dart';
class InitSDKListener {
/// SDK
void onConnecting() {}
/// SDK
void onConnectSuccess() {}
/// SDK
void onConnectFailed(int? int, String? errorMsg) {}
/// 线 UI IMManager login()
void onKickedOffline() {}
/// 线 userSig IMManager login()
void onUserSigExpired() {}
///
void onSelfInfoUpdated(UserInfo info) {}
}

@ -1,3 +0,0 @@
class MsgSendProgressListener {
void onProgress(String msgID, int progress) {}
}

@ -1,97 +0,0 @@
import 'dart:convert';
import 'package:flutter/services.dart';
import 'package:flutter_openim_sdk/listener/conversation_listener.dart';
import 'package:flutter_openim_sdk/models/conversation_info.dart';
class ConversationManager {
MethodChannel _channel;
late ConversationListener conversationListener;
ConversationManager(this._channel);
void setConversationListener(ConversationListener listener) {
this.conversationListener = listener;
_channel.invokeMethod('setConversationListener', _buildParam({}));
}
Future<List<ConversationInfo>> getAllConversationList() {
return _channel
.invokeMethod('getAllConversationList', _buildParam({}))
.then((value) => _toList(value));
}
Future<ConversationInfo> getSingleConversation(
{required String conversationID}) {
return _channel
.invokeMethod(
'getOneConversation',
_buildParam({
"conversationID": conversationID,
}))
.then((value) => _toObj(value));
}
Future<List<ConversationInfo>> getMultipleConversation(
{required List<String> conversationIDList}) {
return _channel
.invokeMethod('getMultipleConversation',
_buildParam({"conversationIDList": conversationIDList}))
.then((value) => _toList(value));
}
void deleteConversation({required String conversationID}) {
_channel
.invokeMethod('deleteConversation',
_buildParam({"conversationID": conversationID}))
.then((value) => _printValue(value));
}
void setConversationDraft(
{required String conversationID, required String draftText}) {
_channel
.invokeMethod(
'setConversationDraft',
_buildParam(
{"conversationID": conversationID, "draftText": draftText}))
.then((value) => _printValue(value));
}
void pinConversation(
{required String conversationID, required bool isPinned}) {
_channel
.invokeMethod(
'pinConversation',
_buildParam(
{"conversationID": conversationID, "isPinned": isPinned}))
.then((value) => _printValue(value));
}
Future<dynamic> getTotalUnreadMsgCount() {
return _channel.invokeMethod('getTotalUnreadMsgCount', _buildParam({}));
}
///@nodoc
static Map _buildParam(Map param) {
param["ManagerName"] = "conversationManager";
return param;
}
static List<ConversationInfo> _toList(String? value) {
var list = _formatJson(value);
if (null == list) return <ConversationInfo>[];
return (list as List).map((e) => ConversationInfo.fromJson(e)).toList();
}
static ConversationInfo _toObj(String value) =>
ConversationInfo.fromJson(_formatJson(value));
///@nodoc
static dynamic _formatJson(value) {
return jsonDecode(_printValue(value));
}
static String _printValue(value) {
return value;
}
}

@ -1,115 +0,0 @@
import 'dart:convert';
import 'package:flutter/services.dart';
import 'package:flutter_openim_sdk/listener/friendship_listener.dart';
import 'package:flutter_openim_sdk/models/user_info.dart';
class FriendshipManager {
MethodChannel _channel;
late FriendshipListener friendshipListener;
FriendshipManager(this._channel);
void setFriendshipListener(FriendshipListener listener) {
this.friendshipListener = listener;
_channel.invokeMethod('setFriendListener', _buildParam({}));
}
Future<List<UserInfo>> getFriendsInfo({required List<String> uidList}) {
return _channel
.invokeMethod('getFriendsInfo', _buildParam({"uidList": uidList}))
.then((value) => _toList(value));
}
Future<dynamic> addFriend({required String uid, required String reason}) {
return _channel.invokeMethod(
'addFriend', _buildParam({"uid": uid, "reqMessage": reason}));
}
Future<List<UserInfo>> getFriendApplicationList() {
return _channel
.invokeMethod('getFriendApplicationList', _buildParam({}))
.then((value) => _toList(value));
}
Future<List<UserInfo>> getFriendList() {
return _channel
.invokeMethod('getFriendList', _buildParam({}))
.then((value) => _toList(value));
}
Future<dynamic> setFriendInfo({required UserInfo info}) {
return _channel.invokeMethod('setFriendInfo', _buildParam(info.toJson()));
}
Future<dynamic> addToBlackList({required String uid}) {
return _channel.invokeMethod('addToBlackList', _buildParam({"uid": uid}));
}
Future<List<UserInfo>> getBlackList() {
return _channel
.invokeMethod('getBlackList', _buildParam({}))
.then((value) => _toList(value));
}
Future<dynamic> deleteFromBlackList({required String uid}) {
return _channel.invokeMethod(
'deleteFromBlackList', _buildParam({"uid": uid}));
}
Future<List<UserInfo>> checkFriend(List<String> uidList) {
return _channel
.invokeMethod('checkFriend', _buildParam({'uidList': uidList}))
.then((value) => _toList(value));
}
Future<dynamic> deleteFromFriendList({required String uid}) {
return _channel.invokeMethod(
'deleteFromFriendList', _buildParam({"uid": uid}));
}
Future<dynamic> acceptFriendApplication({required String uid}) {
return _channel.invokeMethod(
'acceptFriendApplication', _buildParam({"uid": uid}));
}
Future<dynamic> refuseFriendApplication({required String uid}) {
return _channel.invokeMethod(
'refuseFriendApplication', _buildParam({"uid": uid}));
}
Future<dynamic> forceSyncFriendApplication() {
return _channel.invokeMethod('forceSyncFriendApplication', _buildParam({}));
}
Future<dynamic> forceSyncFriend() {
return _channel.invokeMethod('forceSyncFriend', _buildParam({}));
}
Future<dynamic> forceSyncBlackList() {
return _channel.invokeMethod('forceSyncBlackList', _buildParam({}));
}
///@nodoc
static Map _buildParam(Map param) {
param["ManagerName"] = "friendshipManager";
return param;
}
static List<UserInfo> _toList(String? value) {
var list = _formatJson(value);
if (null == list) return <UserInfo>[];
return (list as List).map((e) => UserInfo.fromJson(e)).toList();
}
static UserInfo _toObj(String value) => UserInfo.fromJson(_formatJson(value));
///@nodoc
static dynamic _formatJson(value) {
return jsonDecode(_printValue(value));
}
static String _printValue(value) {
return value;
}
}

@ -1,7 +0,0 @@
import 'package:flutter/services.dart';
class GroupManager{
MethodChannel _channel;
GroupManager(this._channel);
}

@ -1,376 +0,0 @@
import 'dart:convert';
import 'package:flutter/services.dart';
import 'package:flutter_openim_sdk/enum/listener_type.dart';
import 'package:flutter_openim_sdk/listener/int_sdk_listener.dart';
import 'package:flutter_openim_sdk/manager/im_conversation_manager.dart';
import 'package:flutter_openim_sdk/manager/im_friendship_manager.dart';
import 'package:flutter_openim_sdk/manager/im_group_manager.dart';
import 'package:flutter_openim_sdk/manager/im_message_manager.dart';
import 'package:flutter_openim_sdk/manager/im_offline_push_manager.dart';
import 'package:flutter_openim_sdk/manager/im_signaling_manager.dart';
import 'package:flutter_openim_sdk/models/conversation_info.dart';
import 'package:flutter_openim_sdk/models/message.dart';
import 'package:flutter_openim_sdk/models/user_info.dart';
class IMManager {
MethodChannel _channel;
late ConversationManager conversationManager;
late FriendshipManager friendshipManager;
late MessageManager messageManager;
late GroupManager groupManager;
late OfflinePushManager offlinePushManager;
late SignalingManager signalingManager;
late InitSDKListener _initSDKListener;
IMManager(this._channel) {
conversationManager = ConversationManager(_channel);
friendshipManager = FriendshipManager(_channel);
messageManager = MessageManager(_channel);
groupManager = GroupManager(_channel);
offlinePushManager = OfflinePushManager(_channel);
signalingManager = SignalingManager(_channel);
addNativeCallback(_channel);
}
void addNativeCallback(MethodChannel _channel) {
_channel.setMethodCallHandler((call) {
try {
/*if (call.method == ListenerType.simpleMsgListener) {
var args = call.arguments;
var data = args['data'];
String type = args['type'];
switch (type) {
case 'onRecvC2CCustomMessage':
break;
case 'onRecvC2CTextMessage':
break;
case 'onRecvGroupCustomMessage':
break;
case 'onRecvGroupTextMessage':
break;
}
} else */
if (call.method == ListenerType.initSDKListener) {
String type = call.arguments['type'];
dynamic data = call.arguments['data'];
switch (type) {
case 'onSelfInfoUpdated':
var u = UserInfo.fromJson(_formatJson(data));
_initSDKListener.onSelfInfoUpdated(u);
break;
case 'onConnectFailed':
int? errCode = call.arguments['errCode'];
String? errMsg = call.arguments['errMsg'];
_initSDKListener.onConnectFailed(errCode, errMsg);
break;
case 'onConnecting':
_initSDKListener.onConnecting();
break;
case 'onConnectSuccess':
_initSDKListener.onConnectSuccess();
break;
case 'onKickedOffline':
_initSDKListener.onKickedOffline();
break;
case 'onUserSigExpired':
_initSDKListener.onUserSigExpired();
break;
}
} else if (call.method == ListenerType.groupListener) {
var args = call.arguments;
String type = args['type'];
Map<String, dynamic> params = args['data'] == null
? new Map<String, dynamic>()
: new Map<String, dynamic>.from(args['data']);
String groupID = params['groupID'] == null ? '' : params['groupID'];
String opReason =
params['opReason'] == null ? '' : params['opReason'];
bool isAgreeJoin =
params['isAgreeJoin'] == null ? false : params['isAgreeJoin'];
String customData =
params['customData'] == null ? '' : params['customData'];
Map<String, String> groupAttributeMap =
params['groupAttributeMap'] == null
? new Map<String, String>()
: new Map<String, String>.from(params['groupAttributeMap']);
List<Map<String, dynamic>> memberListMap =
params['memberList'] == null
? List.empty(growable: true)
: List.from(params['memberList']);
List<Map<String, dynamic>> groupMemberChangeInfoListMap =
params['groupMemberChangeInfoList'] == null
? List.empty(growable: true)
: List.from(params['groupMemberChangeInfoList']);
List<Map<String, dynamic>> groupChangeInfoListMap =
params['groupChangeInfoList'] == null
? List.empty(growable: true)
: List.from(params['groupChangeInfoList']);
switch (type) {
case 'onMemberEnter':
break;
case 'onMemberLeave':
break;
case 'onMemberInvited':
break;
case 'onMemberKicked':
break;
case 'onMemberInfoChanged':
break;
case 'onGroupCreated':
break;
case 'onGroupDismissed':
break;
case 'onGroupRecycled':
break;
case 'onGroupInfoChanged':
break;
case 'onReceiveJoinApplication':
break;
case 'onApplicationProcessed':
break;
case 'onGrantAdministrator':
break;
case 'onRevokeAdministrator':
break;
case 'onQuitFromGroup':
break;
case 'onReceiveRESTCustomData':
break;
case 'onGroupAttributeChanged':
break;
}
} else if (call.method == ListenerType.advancedMsgListener) {
var type = call.arguments['type'];
var id = call.arguments['data']['id'];
var msg =
Message.fromJson(_formatJson(call.arguments['data']['message']));
switch (type) {
case 'onRecvNewMessage':
for (var listener in messageManager.advancedMsgListeners) {
if (listener.id == id) {
listener.onRecvNewMessage(msg);
}
}
break;
case 'onRecvMessageRevoked':
for (var listener in messageManager.advancedMsgListeners) {
if (listener.id == id) {
listener.onRecvMessageRevoked(msg);
}
}
break;
case 'onRecvC2CReadReceipt':
for (var listener in messageManager.advancedMsgListeners) {
if (listener.id == id) {
listener.onRecvC2CReadReceipt(msg);
}
}
break;
}
} else if (call.method == ListenerType.messageProgressListener) {
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?.onProgress(
msgID,
progress,
);
break;
}
} else if (call.method == ListenerType.conversationListener) {
String type = call.arguments['type'];
dynamic data = call.arguments['data'];
switch (type) {
case 'onSyncServerStart':
conversationManager.conversationListener.onSyncServerStart();
break;
case 'onSyncServerFinish':
conversationManager.conversationListener.onSyncServerFinish();
break;
case 'onSyncServerFailed':
conversationManager.conversationListener.onSyncServerFailed();
break;
case 'onNewConversation':
List<ConversationInfo> list = List.empty(growable: true);
if (null != data) {
list = (_formatJson(data) as List)
.map((e) => ConversationInfo.fromJson(e))
.toList();
}
conversationManager.conversationListener.onNewConversation(list);
break;
case 'onConversationChanged':
List<ConversationInfo> list = List.empty(growable: true);
if (null != data) {
list = (_formatJson(data) as List)
.map((e) => ConversationInfo.fromJson(e))
.toList();
}
conversationManager.conversationListener
.onConversationChanged(list);
break;
case 'onTotalUnreadMessageCountChanged':
conversationManager.conversationListener
.onTotalUnreadMessageCountChanged(data ?? 0);
break;
}
} else if (call.method == ListenerType.friendListener) {
// int errCode = call.arguments['errCode'];
// String errMsg = call.arguments['errMsg'];
String type = call.arguments['type'];
dynamic data = call.arguments['data'];
switch (type) {
case 'onBlackListAdd':
friendshipManager.friendshipListener.onBlackListAdd(data);
break;
case 'onBlackListDeleted':
friendshipManager.friendshipListener.onBlackListDeleted(data);
break;
case 'onFriendApplicationListAccept':
friendshipManager.friendshipListener
.onFriendApplicationListAccept(data);
break;
case 'onFriendApplicationListAdded':
friendshipManager.friendshipListener
.onFriendApplicationListAdded(data);
break;
case 'onFriendApplicationListDeleted':
friendshipManager.friendshipListener
.onFriendApplicationListDeleted(data);
break;
case 'onFriendApplicationListReject':
friendshipManager.friendshipListener
.onFriendApplicationListReject(data);
break;
case 'onFriendInfoChanged':
friendshipManager.friendshipListener.onFriendInfoChanged(data);
break;
case 'onFriendListAdded':
friendshipManager.friendshipListener.onFriendListAdded(data);
break;
case 'onFriendListDeleted':
friendshipManager.friendshipListener.onFriendListDeleted(data);
break;
}
}
/*else if (call.method == 'logFromSwift') {
var data = call.arguments["data"];
var msg = call.arguments["msg"];
} else if (call.method == ListenerType.signalingListener) {
var args = call.arguments;
String type = args['type'];
Map<String, dynamic> params = args['data'];
String inviteID =
params['inviteID'] == null ? '' : params['inviteID'];
String inviter = params['inviter'] == null ? '' : params['inviter'];
String groupID = params['groupID'] == null ? '' : params['groupID'];
List<String>? inviteeList = params['inviteeList'] == null
? null
: List.from(params['inviteeList']);
String data = params['data'] == null ? '' : params['data'];
String invitee = params['invitee'] == null ? '' : params['invitee'];
switch (type) {
case 'onReceiveNewInvitation':
break;
case 'onInviteeAccepted':
break;
case 'onInviteeRejected':
break;
case 'onInvitationCancelled':
break;
case 'onInvitationTimeout':
break;
}
}*/
} catch (err) {
print(
"回调失败了,数据类型异常。$err ${call.method} ${call.arguments['type']} ${call.arguments['data']}");
}
return Future.value(null);
});
}
Future<dynamic> initSDK({
required int platform,
required String ipApi,
required String ipWs,
required String dbPath,
required InitSDKListener listener,
}) {
_initSDKListener = listener;
return _channel.invokeMethod(
'initSDK',
_buildParam(
{"platform": platform, "ipApi": ipApi, "ipWs": ipWs, "dbDir": dbPath},
),
);
}
Future<dynamic> unInitSDK() {
return _channel.invokeMethod('unInitSDK', _buildParam({}));
}
Future<dynamic> login({required String uid, required String token}) {
return _channel.invokeMethod(
'login',
_buildParam({'uid': uid, 'token': token}),
);
}
Future<dynamic> logout() {
return _channel.invokeMethod('logout', _buildParam({}));
}
Future<int?> getLoginStatus() {
return _channel.invokeMethod<int>('getLoginStatus', _buildParam({}));
}
Future<String?> getLoginUid() {
return _channel.invokeMethod<String>('getLoginUid', _buildParam({}));
}
Future<String?> setSelfInfo(UserInfo info) {
return _channel.invokeMethod('setSelfInfo', _buildParam(info.toJson()));
// .then((value) => UserInfo.fromJson(value));
}
Future<List<UserInfo>> getUsersInfo(List<String> uidList) {
return _channel
.invokeMethod('getUsersInfo', _buildParam({'uidList': uidList}))
.then((value) => _toList(value));
}
Future<dynamic> forceSyncLoginUerInfo(List<String> uidList) {
return _channel.invokeMethod('forceSyncLoginUerInfo', _buildParam({}));
}
///@nodoc
static Map _buildParam(Map param) {
param["ManagerName"] = "imManager";
return param;
}
static List<UserInfo> _toList(String value) =>
(_formatJson(value) as List).map((e) => UserInfo.fromJson(e)).toList();
///@nodoc
static dynamic _formatJson(value) {
return jsonDecode(_printValue(value));
}
static String _printValue(value) {
return value;
}
}

@ -1,217 +0,0 @@
import 'dart:convert';
import 'package:flutter/services.dart';
import 'package:flutter_openim_sdk/listener/advanced_msg_listener.dart';
import 'package:flutter_openim_sdk/listener/msg_send_progress_listener.dart';
import 'package:flutter_openim_sdk/models/message.dart';
class MessageManager {
MethodChannel _channel;
List<AdvancedMsgListener> advancedMsgListeners = List.empty(growable: true);
MsgSendProgressListener? msgSendProgressListener;
MessageManager(this._channel);
void addAdvancedMsgListener(AdvancedMsgListener listener) {
advancedMsgListeners.add(listener);
_channel.invokeMethod(
'addAdvancedMsgListener', _buildParam({'id': listener.id}));
}
void removeAdvancedMsgListener(AdvancedMsgListener listener) {
advancedMsgListeners.remove(listener);
_channel.invokeMethod(
'removeAdvancedMsgListener', _buildParam({'id': listener.id}));
}
void setMsgProgressListener(MsgSendProgressListener listener) {
msgSendProgressListener = listener;
}
/*Future<Message> */
Future<dynamic> sendMessage({
required Message message,
String? receiver,
String? groupID,
required bool onlineUserOnly,
}) {
return _channel.invokeMethod(
'sendMessage',
_buildParam({
'message': message.toJson(),
'receiver': receiver ?? '',
'groupID': groupID ?? '',
'onlineUserOnly': onlineUserOnly,
}))
/*.then((value) => _toObj(value))*/;
}
Future<List<Message>> getHistoryMessageList({
String? userID,
Message? startMsg,
String? groupID,
int? count,
}) {
return _channel
.invokeMethod(
'getHistoryMessageList',
_buildParam({
'userID': userID ?? '',
'startMsg': startMsg?.toJson() /*?? {}*/,
'groupID': groupID ?? '',
'count': count ?? 10,
}))
.then((value) => _toList(value));
}
void revokeMessage({required Message message}) {
_channel.invokeMethod('revokeMessage', _buildParam(message.toJson()));
}
Future deleteMessageFromLocalStorage({required Message message}) {
return _channel.invokeMethod(
'deleteMessageFromLocalStorage', _buildParam(message.toJson()));
}
void deleteMessages({required List<Message> msgList}) {
// _channel.invokeMethod('deleteMessages', _buildParam(msgList.toJson()));
}
void insertSingleMessageToLocalStorage({
String? userID,
Message? message,
String? sender,
}) {
_channel.invokeMethod(
'insertSingleMessageToLocalStorage',
_buildParam({
"userID": userID,
"message": message?.toJson(),
"sender": sender,
}));
}
void findMessages({required List<String> messageIDList}) {
_channel.invokeMethod(
'findMessages',
_buildParam({
"messageIDList": messageIDList,
}));
}
Future<dynamic> markSingleMessageHasRead({required String userID}) {
return _channel.invokeMethod(
'markSingleMessageHasRead', _buildParam({'userID': userID}));
}
Future<Message> createTextMessage({required String text}) {
return _channel
.invokeMethod('createTextMessage', _buildParam({'text': text}))
.then((value) => _toObj(value));
}
Future<Message> createTextAtMessage({
required String text,
required List<String> atUserList,
}) {
return _channel
.invokeMethod(
'createTextAtMessage',
_buildParam({
'text': text,
'atUserList': atUserList,
}),
)
.then((value) => _toObj(value));
}
Future<Message> createImageMessage({required String imagePath}) {
return _channel
.invokeMethod(
'createImageMessage',
_buildParam({'imagePath': imagePath}),
)
.then((value) => _toObj(value));
}
Future<Message> createSoundMessage(
{required String soundPath, required int duration}) {
return _channel
.invokeMethod(
'createSoundMessage',
_buildParam({'soundPath': soundPath, "duration": duration}),
)
.then((value) => _toObj(value));
}
Future<Message> createVideoMessage({
required String videoPath,
required String videoType,
required int duration,
required String snapshotPath,
}) {
return _channel
.invokeMethod(
'createVideoMessage',
_buildParam({
'videoPath': videoPath,
'videoType': videoType,
'duration': duration,
'snapshotPath': snapshotPath,
}))
.then((value) => _toObj(value));
}
Future<Message> createFileMessage(
{required String filePath, required String fileName}) {
return _channel
.invokeMethod('createFileMessage',
_buildParam({'filePath': filePath, 'fileName': fileName}))
.then((value) => _toObj(value));
}
Future<Message> createMergerMessage(
{required List<Message> messageList,
required String title,
required List<String> summaryList}) {
return _channel
.invokeMethod(
'createMergerMessage',
_buildParam({
'messageList': messageList.map((e) => e.toJson()).toList(),
'title': title,
'summaryList': summaryList,
}))
.then((value) => _toObj(value));
}
Future<Message> createForwardMessage({required List<Message> messageList}) {
return _channel
.invokeMethod(
'createForwardMessage',
_buildParam(
{'message': messageList.map((e) => e.toJson()).toList()}))
.then((value) => _toObj(value));
}
///@nodoc
static Map _buildParam(Map param) {
print('param:$param');
param["ManagerName"] = "messageManager";
return param;
}
static List<Message> _toList(String value) =>
(_formatJson(value) as List).map((e) => Message.fromJson(e)).toList();
static Message _toObj(String value) => Message.fromJson(_formatJson(value));
///@nodoc
static dynamic _formatJson(value) {
return jsonDecode(_printValue(value));
}
static String _printValue(value) {
return value;
}
}

@ -1,7 +0,0 @@
import 'package:flutter/services.dart';
class OfflinePushManager{
MethodChannel _channel;
OfflinePushManager(this._channel);
}

@ -1,7 +0,0 @@
import 'package:flutter/services.dart';
class SignalingManager {
MethodChannel _channel;
SignalingManager(this._channel);
}

@ -1,77 +0,0 @@
import 'dart:convert';
import 'package:flutter_openim_sdk/models/message.dart';
class ConversationInfo {
String conversationID;
int? conversationType;
String? userID;
String? groupID;
String? showName;
String? faceUrl;
int? recvMsgOpt;
int? unreadCount;
Message? latestMsg;
int? latestMsgSendTime;
String? draftText;
int? draftTimestamp;
dynamic isPinned;
ConversationInfo(
{required this.conversationID,
this.conversationType,
this.userID,
this.groupID,
this.showName,
this.faceUrl,
this.recvMsgOpt,
this.unreadCount,
this.latestMsg,
this.latestMsgSendTime,
this.draftText,
this.draftTimestamp,
this.isPinned});
ConversationInfo.fromJson(Map<String, dynamic> json)
: conversationID = json['conversationID'] {
// conversationID = json['conversationID'];
conversationType = json['conversationType'];
userID = json['userID'];
groupID = json['groupID'];
showName = json['showName'];
faceUrl = json['faceUrl'];
recvMsgOpt = json['recvMsgOpt'];
unreadCount = json['unreadCount'];
// latestMsg = json['latestMsg'];
// if (null != json['latestMsg']) {
if (json['latestMsg'] is String) {
latestMsg = Message.fromJson(jsonDecode(json['latestMsg']));
} else if (json['latestMsg'] is Map) {
latestMsg = Message.fromJson(json['latestMsg']);
}
// }
latestMsgSendTime = json['latestMsgSendTime'];
draftText = json['draftText'];
draftTimestamp = json['draftTimestamp'];
isPinned = json['isPinned'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['conversationID'] = this.conversationID;
data['conversationType'] = this.conversationType;
data['userID'] = this.userID;
data['groupID'] = this.groupID;
data['showName'] = this.showName;
data['faceUrl'] = this.faceUrl;
data['recvMsgOpt'] = this.recvMsgOpt;
data['unreadCount'] = this.unreadCount;
data['latestMsg'] = this.latestMsg?.toJson();
data['latestMsgSendTime'] = this.latestMsgSendTime;
data['draftText'] = this.draftText;
data['draftTimestamp'] = this.draftTimestamp;
data['isPinned'] = this.isPinned;
return data;
}
}

@ -1,337 +0,0 @@
class Message {
String? clientMsgID;
String? serverMsgID;
int? createTime;
int? sendTime;
String? sendID;
String? recvID;
int? msgFrom;
int? contentType;
int? platformID;
List<String>? forceList;
String? senderNickName;
String? senderFaceUrl;
String? groupID;
String? content;
int? seq;
bool? isRead;
int? status;
String? remark;
dynamic ext;
int? sessionType;
PictureElem? pictureElem;
SoundElem? soundElem;
VideoElem? videoElem;
FileElem? fileElem;
Message(
{this.clientMsgID,
this.serverMsgID,
this.createTime,
this.sendTime,
this.sendID,
this.recvID,
this.msgFrom,
this.contentType,
this.platformID,
this.forceList,
this.senderNickName,
this.senderFaceUrl,
this.groupID,
this.content,
this.seq,
this.isRead,
this.status,
this.remark,
this.ext,
this.sessionType,
this.pictureElem,
this.soundElem,
this.videoElem,
this.fileElem});
Message.fromJson(Map<String, dynamic> json)
/* : clientMsgID = json['clientMsgID']*/ {
clientMsgID = json['clientMsgID'];
serverMsgID = json['serverMsgID'];
createTime = json['createTime'];
sendTime = json['sendTime'];
sendID = json['sendID'];
recvID = json['RecvID'];
msgFrom = json['msgFrom'];
contentType = json['contentType'];
platformID = json['platformID'];
forceList = json['forceList'];
senderNickName = json['senderNickName'];
senderFaceUrl = json['senderFaceUrl'];
groupID = json['groupID'];
content = json['content'];
seq = json['seq'];
isRead = json['isRead'];
status = json['status'];
remark = json['remark'];
ext = json['ext'];
sessionType = json['sessionType'];
pictureElem = json['pictureElem'] != null
? new PictureElem.fromJson(json['pictureElem'])
: null;
soundElem = json['soundElem'] != null
? new SoundElem.fromJson(json['soundElem'])
: null;
videoElem = json['videoElem'] != null
? new VideoElem.fromJson(json['videoElem'])
: null;
fileElem = json['fileElem'] != null
? new FileElem.fromJson(json['fileElem'])
: null;
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['clientMsgID'] = this.clientMsgID;
data['serverMsgID'] = this.serverMsgID;
data['createTime'] = this.createTime;
data['sendTime'] = this.sendTime;
data['sendID'] = this.sendID;
data['RecvID'] = this.recvID;
data['msgFrom'] = this.msgFrom;
data['contentType'] = this.contentType;
data['platformID'] = this.platformID;
data['forceList'] = this.forceList;
data['senderNickName'] = this.senderNickName;
data['senderFaceUrl'] = this.senderFaceUrl;
data['groupID'] = this.groupID;
data['content'] = this.content;
data['seq'] = this.seq;
data['isRead'] = this.isRead;
data['status'] = this.status;
data['remark'] = this.remark;
data['ext'] = this.ext;
data['sessionType'] = this.sessionType;
if (this.pictureElem != null) {
data['pictureElem'] = this.pictureElem?.toJson();
}
if (this.soundElem != null) {
data['soundElem'] = this.soundElem?.toJson();
}
if (this.videoElem != null) {
data['videoElem'] = this.videoElem?.toJson();
}
if (this.fileElem != null) {
data['fileElem'] = this.fileElem?.toJson();
}
return data;
}
@override
bool operator ==(Object other) {
// TODO: implement ==
if (other is Message) {
return other.id == id;
}
return super == other;
}
@override
// TODO: implement hashCode
int get hashCode => super.hashCode;
String? get id =>
clientMsgID == null || clientMsgID!.isEmpty ? serverMsgID : clientMsgID;
}
class PictureElem {
String? sourcePath;
PictureInfo? sourcePicture;
PictureInfo? bigPicture;
PictureInfo? snapshotPicture;
PictureElem(
{this.sourcePath,
this.sourcePicture,
this.bigPicture,
this.snapshotPicture});
PictureElem.fromJson(Map<String, dynamic> json) {
sourcePath = json['sourcePath'];
sourcePicture = json['sourcePicture'] != null
? new PictureInfo.fromJson(json['sourcePicture'])
: null;
bigPicture = json['bigPicture'] != null
? new PictureInfo.fromJson(json['bigPicture'])
: null;
snapshotPicture = json['snapshotPicture'] != null
? new PictureInfo.fromJson(json['snapshotPicture'])
: null;
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['sourcePath'] = this.sourcePath;
if (this.sourcePicture != null) {
data['sourcePicture'] = this.sourcePicture?.toJson();
}
if (this.bigPicture != null) {
data['bigPicture'] = this.bigPicture?.toJson();
}
if (this.snapshotPicture != null) {
data['snapshotPicture'] = this.snapshotPicture?.toJson();
}
return data;
}
}
class PictureInfo {
String? uuID;
String? type;
int? size;
int? width;
int? height;
String? url;
PictureInfo(
{this.uuID, this.type, this.size, this.width, this.height, this.url});
PictureInfo.fromJson(Map<String, dynamic> json) {
uuID = json['uuID'];
type = json['type'];
size = json['size'];
width = json['width'];
height = json['height'];
url = json['url'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['uuID'] = this.uuID;
data['type'] = this.type;
data['size'] = this.size;
data['width'] = this.width;
data['height'] = this.height;
data['url'] = this.url;
return data;
}
}
class SoundElem {
String? uuID;
String? soundPath;
String? sourceUrl;
int? dataSize;
int? duration;
SoundElem(
{this.uuID,
this.soundPath,
this.sourceUrl,
this.dataSize,
this.duration});
SoundElem.fromJson(Map<String, dynamic> json) {
uuID = json['uuID'];
soundPath = json['soundPath'];
sourceUrl = json['sourceUrl'];
dataSize = json['dataSize'];
duration = json['duration'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['uuID'] = this.uuID;
data['soundPath'] = this.soundPath;
data['sourceUrl'] = this.sourceUrl;
data['dataSize'] = this.dataSize;
data['duration'] = this.duration;
return data;
}
}
class VideoElem {
String? videoPath;
String? videoUUID;
String? videoUrl;
String? videoType;
int? videoSize;
int? duration;
String? snapshotPath;
String? snapshotUUID;
int? snapshotSize;
String? snapshotUrl;
int? snapshotWidth;
int? snapshotHeight;
VideoElem(
{this.videoPath,
this.videoUUID,
this.videoUrl,
this.videoType,
this.videoSize,
this.duration,
this.snapshotPath,
this.snapshotUUID,
this.snapshotSize,
this.snapshotUrl,
this.snapshotWidth,
this.snapshotHeight});
VideoElem.fromJson(Map<String, dynamic> json) {
videoPath = json['videoPath'];
videoUUID = json['videoUUID'];
videoUrl = json['videoUrl'];
videoType = json['videoType'];
videoSize = json['videoSize'];
duration = json['duration'];
snapshotPath = json['snapshotPath'];
snapshotUUID = json['snapshotUUID'];
snapshotSize = json['snapshotSize'];
snapshotUrl = json['snapshotUrl'];
snapshotWidth = json['snapshotWidth'];
snapshotHeight = json['snapshotHeight'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['videoPath'] = this.videoPath;
data['videoUUID'] = this.videoUUID;
data['videoUrl'] = this.videoUrl;
data['videoType'] = this.videoType;
data['videoSize'] = this.videoSize;
data['duration'] = this.duration;
data['snapshotPath'] = this.snapshotPath;
data['snapshotUUID'] = this.snapshotUUID;
data['snapshotSize'] = this.snapshotSize;
data['snapshotUrl'] = this.snapshotUrl;
data['snapshotWidth'] = this.snapshotWidth;
data['snapshotHeight'] = this.snapshotHeight;
return data;
}
}
class FileElem {
String? filePath;
String? uuID;
String? sourceUrl;
String? fileName;
int? fileSize;
FileElem(
{this.filePath, this.uuID, this.sourceUrl, this.fileName, this.fileSize});
FileElem.fromJson(Map<String, dynamic> json) {
filePath = json['filePath'];
uuID = json['uuID'];
sourceUrl = json['sourceUrl'];
fileName = json['fileName'];
fileSize = json['fileSize'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['filePath'] = this.filePath;
data['uuID'] = this.uuID;
data['sourceUrl'] = this.sourceUrl;
data['fileName'] = this.fileName;
data['fileSize'] = this.fileSize;
return data;
}
}

@ -1,65 +0,0 @@
class UserInfo {
String uid;
String? name;
String? icon;
int? gender; // 0 1 2
String? mobile;
String? birth;
String? email;
String? ex;
String? comment;
int? isInBlackList; // 0 1
String? reqMessage;
String? applyTime;
int? flag; // //01-1
UserInfo(
{required this.uid,
this.name,
this.icon,
this.gender,
this.mobile,
this.birth,
this.email,
this.ex,
this.comment,
this.isInBlackList,
this.reqMessage,
this.applyTime,
this.flag});
UserInfo.fromJson(Map<String, dynamic> json) : uid = json['uid'] {
name = json['name'];
icon = json['icon'];
gender = json['gender'];
mobile = json['mobile'];
birth = json['birth'];
email = json['email'];
ex = json['ex'];
comment = json['comment'];
isInBlackList = json['isInBlackList'];
reqMessage = json['reqMessage'];
applyTime = json['applyTime'];
flag = json['flag'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['uid'] = this.uid;
data['name'] = this.name;
data['icon'] = this.icon;
data['gender'] = this.gender;
data['mobile'] = this.mobile;
data['birth'] = this.birth;
data['email'] = this.email;
data['ex'] = this.ex;
data['comment'] = this.comment;
data['isInBlackList'] = this.isInBlackList;
data['reqMessage'] = this.reqMessage;
data['applyTime'] = this.applyTime;
data['flag'] = this.flag;
return data;
}
String get nickname => comment ?? name ?? uid;
}

@ -0,0 +1,9 @@
import 'package:flutter/services.dart';
import 'package:flutter_openim_sdk/flutter_openim_sdk.dart';
class OpenIM {
static const MethodChannel _channel =
const MethodChannel('flutter_openim_sdk');
static IMManager iMManager = IMManager(_channel);
}

@ -1,6 +1,3 @@
import 'package:flutter/services.dart';
import 'package:flutter_openim_sdk/models/message.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter_openim_sdk/flutter_openim_sdk.dart';
void main() {
@ -20,15 +17,14 @@ void main() {
test('getPlatformVersion', () async {});*/
Message m1= new Message(clientMsgID: '1',content: 'c1');
Message m2= new Message(clientMsgID: '2',content: 'c2');
Message m3= new Message(clientMsgID: '3',content: 'c3');
Message m4= new Message(clientMsgID: '3',content: 'c4');
List list = List.of([m1,m2,m3]);
Message m1 = new Message(clientMsgID: '1', content: 'c1');
Message m2 = new Message(clientMsgID: '2', content: 'c2');
Message m3 = new Message(clientMsgID: '3', content: 'c3');
Message m4 = new Message(clientMsgID: '3', content: 'c4');
List list = List.of([m1, m2, m3]);
print(list.contains(m4));
print(m1==m4);
print(m2==m4);
print(m3==m4);
print(m1 == m4);
print(m2 == m4);
print(m3 == m4);
}

Loading…
Cancel
Save