/** 伪推送; app退出之后就不会收到推送的信息 通知栏新消息通知 a.每个session 只显示一条 b.每个msg 信息都显示 配置依赖与 configure */ public class IMNotificationManager extends IMManager { private Logger logger = Logger.getLogger(IMNotificationManager.class); private static IMNotificationManager inst = new IMNotificationManager(); public static IMNotificationManager instance() { return inst; } private ConfigurationSp configurationSp; private IMNotificationManager() {} @Override public void doOnStart() { cancelAllNotifications(); } public void onLoginSuccess() { int loginId = IMLoginManager.instance().getLoginId(); configurationSp = ConfigurationSp.instance(ctx, loginId); if (!EventBus.getDefault().isRegistered(inst)) { EventBus.getDefault().register(inst); } } public void reset() { EventBus.getDefault().unregister(this); cancelAllNotifications(); } public void onEventMainThread(UnreadEvent event) { switch (event.event) { case UNREAD_MSG_RECEIVED: UnreadEntity unreadEntity = event.entity; handleMsgRecv(unreadEntity); break; } } // 屏蔽群,相关的通知全部删除 public void onEventMainThread(GroupEvent event) { GroupEntity gEntity = event.getGroupEntity(); if (event.getEvent() == GroupEvent.Event.SHIELD_GROUP_OK) { if (gEntity == null) { return; } cancelSessionNotifications(gEntity.getSessionKey()); } } private void handleMsgRecv(UnreadEntity entity) { logger.d("notification#recv unhandled message"); int peerId = entity.getPeerId(); int sessionType = entity.getSessionType(); logger.d("notification#msg no one handled, peerId:%d, sessionType:%d", peerId, sessionType); // 判断是否设定了免打扰 if (entity.isForbidden()) { logger.d("notification#GROUP_STATUS_SHIELD"); return; } // PC端是否登陆 取消 【暂时先关闭】 // if(IMLoginManager.instance().isPcOnline()){ // logger.d("notification#isPcOnline"); // return; // } // 全局开关 boolean globallyOnOff = configurationSp.getCfg( SysConstant.SETTING_GLOBAL, ConfigurationSp.CfgDimension.NOTIFICATION); if (globallyOnOff) { logger.d("notification#shouldGloballyShowNotification is false, return"); return; } // 单独的设置 boolean singleOnOff = configurationSp.getCfg(entity.getSessionKey(), ConfigurationSp.CfgDimension.NOTIFICATION); if (singleOnOff) { logger.d("notification#shouldShowNotificationBySession is false, return"); return; } // if the message is a multi login message which send from another terminal,not need notificate // to status bar // 判断是否是自己的消息 if (IMLoginManager.instance().getLoginId() != peerId) { showNotification(entity); } } public void cancelAllNotifications() { logger.d("notification#cancelAllNotifications"); if (null == ctx) { return; } NotificationManager notifyMgr = (NotificationManager) ctx.getSystemService(Context.NOTIFICATION_SERVICE); if (notifyMgr == null) { return; } notifyMgr.cancelAll(); } /** * 在通知栏中删除特定回话的状态 * * @param sessionKey */ public void cancelSessionNotifications(String sessionKey) { logger.d("notification#cancelSessionNotifications"); NotificationManager notifyMgr = (NotificationManager) ctx.getSystemService(Context.NOTIFICATION_SERVICE); if (null == notifyMgr) { return; } int notificationId = getSessionNotificationId(sessionKey); notifyMgr.cancel(notificationId); } private void showNotification(final UnreadEntity unreadEntity) { // todo eric need to set the exact size of the big icon // 服务端有些特定的支持 尺寸是不是要调整一下 todo 100*100 下面的就可以不要了 ImageSize targetSize = new ImageSize(80, 80); int peerId = unreadEntity.getPeerId(); int sessionType = unreadEntity.getSessionType(); String avatarUrl = ""; String title = ""; String content = unreadEntity.getLatestMsgData(); String unit = ctx.getString(R.string.msg_cnt_unit); int totalUnread = unreadEntity.getUnReadCnt(); if (unreadEntity.getSessionType() == DBConstant.SESSION_TYPE_SINGLE) { UserEntity contact = IMContactManager.instance().findContact(peerId); if (contact != null) { title = contact.getMainName(); avatarUrl = contact.getAvatar(); } else { title = "User_" + peerId; avatarUrl = ""; } } else { GroupEntity group = IMGroupManager.instance().findGroup(peerId); if (group != null) { title = group.getMainName(); avatarUrl = group.getAvatar(); } else { title = "Group_" + peerId; avatarUrl = ""; } } // 获取头像 avatarUrl = IMUIHelper.getRealAvatarUrl(avatarUrl); final String ticker = String.format("[%d%s]%s: %s", totalUnread, unit, title, content); final int notificationId = getSessionNotificationId(unreadEntity.getSessionKey()); final Intent intent = new Intent(ctx, MessageActivity.class); intent.putExtra(IntentConstant.KEY_SESSION_KEY, unreadEntity.getSessionKey()); logger.d("notification#notification avatarUrl:%s", avatarUrl); final String finalTitle = title; ImageLoader.getInstance() .loadImage( avatarUrl, targetSize, null, new SimpleImageLoadingListener() { @Override public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) { logger.d("notification#icon onLoadComplete"); // holder.image.setImageBitmap(loadedImage); showInNotificationBar(finalTitle, ticker, loadedImage, notificationId, intent); } @Override public void onLoadingFailed(String imageUri, View view, FailReason failReason) { logger.d("notification#icon onLoadFailed"); // 服务器支持的格式有哪些 // todo eric default avatar is too small, need big size(128 * 128) Bitmap defaultBitmap = BitmapFactory.decodeResource( ctx.getResources(), IMUIHelper.getDefaultAvatarResId(unreadEntity.getSessionType())); showInNotificationBar(finalTitle, ticker, defaultBitmap, notificationId, intent); } }); } private void showInNotificationBar( String title, String ticker, Bitmap iconBitmap, int notificationId, Intent intent) { logger.d("notification#showInNotificationBar title:%s ticker:%s", title, ticker); NotificationManager notifyMgr = (NotificationManager) ctx.getSystemService(Context.NOTIFICATION_SERVICE); if (notifyMgr == null) { return; } Builder builder = new NotificationCompat.Builder(ctx); builder.setContentTitle(title); builder.setContentText(ticker); builder.setSmallIcon(R.drawable.tt_small_icon); builder.setTicker(ticker); builder.setWhen(System.currentTimeMillis()); builder.setAutoCancel(true); // this is the content near the right bottom side // builder.setContentInfo("content info"); if (configurationSp.getCfg( SysConstant.SETTING_GLOBAL, ConfigurationSp.CfgDimension.VIBRATION)) { // delay 0ms, vibrate 200ms, delay 250ms, vibrate 200ms long[] vibrate = {0, 200, 250, 200}; builder.setVibrate(vibrate); } else { logger.d("notification#setting is not using vibration"); } // sound if (configurationSp.getCfg(SysConstant.SETTING_GLOBAL, ConfigurationSp.CfgDimension.SOUND)) { builder.setDefaults(Notification.DEFAULT_SOUND); } else { logger.d("notification#setting is not using sound"); } if (iconBitmap != null) { logger.d("notification#fetch icon from network ok"); builder.setLargeIcon(iconBitmap); } else { // do nothint ? } // if MessageActivity is in the background, the system would bring it to // the front intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); PendingIntent pendingIntent = PendingIntent.getActivity(ctx, notificationId, intent, PendingIntent.FLAG_UPDATE_CURRENT); builder.setContentIntent(pendingIntent); Notification notification = builder.build(); notifyMgr.notify(notificationId, notification); } // come from // http://www.partow.net/programming/hashfunctions/index.html#BKDRHashFunction private long hashBKDR(String str) { long seed = 131; // 31 131 1313 13131 131313 etc.. long hash = 0; for (int i = 0; i < str.length(); i++) { hash = (hash * seed) + str.charAt(i); } return hash; } /* End Of BKDR Hash Function */ public int getSessionNotificationId(String sessionKey) { logger.d("notification#getSessionNotificationId sessionTag:%s", sessionKey); int hashedNotificationId = (int) hashBKDR(sessionKey); logger.d("notification#hashedNotificationId:%d", hashedNotificationId); return hashedNotificationId; } }
public class IMGroupManager extends IMManager { private Logger logger = Logger.getLogger(IMGroupManager.class); private static IMGroupManager inst = new IMGroupManager(); public static IMGroupManager instance() { return inst; } // 依赖的服务管理 private IMSocketManager imSocketManager = IMSocketManager.instance(); private IMLoginManager imLoginManager = IMLoginManager.instance(); private DBInterface dbInterface = DBInterface.instance(); // todo Pinyin的处理 // 正式群,临时群都会有的,存在竞争 如果不同时请求的话 private Map<Integer, GroupEntity> groupMap = new ConcurrentHashMap<>(); // 群组状态 private boolean isGroupReady = false; @Override public void doOnStart() { groupMap.clear(); } public void onNormalLoginOk() { onLocalLoginOk(); onLocalNetOk(); } /** 1. 加载本地信息 2. 请求正规群信息 , 与本地进行对比 3. version groupId 请求 */ public void onLocalLoginOk() { logger.i("group#loadFromDb"); if (!EventBus.getDefault().isRegistered(inst)) { EventBus.getDefault().registerSticky(inst); } // 加载本地group List<GroupEntity> localGroupInfoList = dbInterface.loadAllGroup(); for (GroupEntity groupInfo : localGroupInfoList) { groupMap.put(groupInfo.getPeerId(), groupInfo); } triggerEvent(new GroupEvent(GroupEvent.Event.GROUP_INFO_OK)); } public void onLocalNetOk() { reqGetNormalGroupList(); } @Override public void reset() { isGroupReady = false; groupMap.clear(); EventBus.getDefault().unregister(inst); } public void onEvent(SessionEvent event) { switch (event) { case RECENT_SESSION_LIST_UPDATE: // groupMap 本地已经加载完毕之后才触发 loadSessionGroupInfo(); break; } } /** * 实现自身的事件驱动 * * @param event */ public synchronized void triggerEvent(GroupEvent event) { switch (event.getEvent()) { case GROUP_INFO_OK: isGroupReady = true; break; case GROUP_INFO_UPDATED: isGroupReady = true; break; } EventBus.getDefault().postSticky(event); } /** ---------------事件驱动end------------------------------ */ /** 1. 加载本地信息 2. 从session中获取 群组信息,从本地中获取这些群组的version信息 3. 合并上述的merge结果, version groupId 请求 */ private void loadSessionGroupInfo() { logger.i("group#loadSessionGroupInfo"); List<SessionEntity> sessionInfoList = IMSessionManager.instance().getRecentSessionList(); List<IMBaseDefine.GroupVersionInfo> needReqList = new ArrayList<>(); for (SessionEntity sessionInfo : sessionInfoList) { int version = 0; if (sessionInfo.getPeerType() == DBConstant.SESSION_TYPE_GROUP /** 群组 */ ) { if (groupMap.containsKey(sessionInfo.getPeerId())) { version = groupMap.get(sessionInfo.getPeerId()).getVersion(); } IMBaseDefine.GroupVersionInfo versionInfo = IMBaseDefine.GroupVersionInfo.newBuilder() .setVersion(version) .setGroupId(sessionInfo.getPeerId()) .build(); needReqList.add(versionInfo); } } // 事件触发的时候需要注意 if (needReqList.size() > 0) { reqGetGroupDetailInfo(needReqList); return; } } /** 联系人页面正式群的请求 todo 正式群与临时群逻辑上的分开的,但是底层应该是想通的 */ private void reqGetNormalGroupList() { logger.i("group#reqGetNormalGroupList"); int loginId = imLoginManager.getLoginId(); IMGroup.IMNormalGroupListReq normalGroupListReq = IMGroup.IMNormalGroupListReq.newBuilder().setUserId(loginId).build(); int sid = IMBaseDefine.ServiceID.SID_GROUP_VALUE; int cid = IMBaseDefine.GroupCmdID.CID_GROUP_NORMAL_LIST_REQUEST_VALUE; imSocketManager.sendRequest(normalGroupListReq, sid, cid); logger.i("group#send packet to server"); } public void onRepNormalGroupList(IMGroup.IMNormalGroupListRsp normalGroupListRsp) { logger.i("group#onRepNormalGroupList"); int groupSize = normalGroupListRsp.getGroupVersionListCount(); logger.i("group#onRepNormalGroupList cnt:%d", groupSize); List<IMBaseDefine.GroupVersionInfo> versionInfoList = normalGroupListRsp.getGroupVersionListList(); /** 对比DB中的version字段 */ // 这块对比的可以抽离出来 List<IMBaseDefine.GroupVersionInfo> needInfoList = new ArrayList<>(); for (IMBaseDefine.GroupVersionInfo groupVersionInfo : versionInfoList) { int groupId = groupVersionInfo.getGroupId(); int version = groupVersionInfo.getVersion(); if (groupMap.containsKey(groupId) && groupMap.get(groupId).getVersion() == version) { continue; } IMBaseDefine.GroupVersionInfo versionInfo = IMBaseDefine.GroupVersionInfo.newBuilder().setVersion(0).setGroupId(groupId).build(); needInfoList.add(versionInfo); } // 事件触发的时候需要注意 todo if (needInfoList.size() > 0) { reqGetGroupDetailInfo(needInfoList); } } public void reqGroupDetailInfo(int groupId) { IMBaseDefine.GroupVersionInfo groupVersionInfo = IMBaseDefine.GroupVersionInfo.newBuilder().setGroupId(groupId).setVersion(0).build(); ArrayList<IMBaseDefine.GroupVersionInfo> list = new ArrayList<>(); list.add(groupVersionInfo); reqGetGroupDetailInfo(list); } /** 请求群组的详细信息 */ public void reqGetGroupDetailInfo(List<IMBaseDefine.GroupVersionInfo> versionInfoList) { logger.i("group#reqGetGroupDetailInfo"); if (versionInfoList == null || versionInfoList.size() <= 0) { logger.e("group#reqGetGroupDetailInfo# please check your params,cause by empty/null"); return; } int loginId = imLoginManager.getLoginId(); IMGroup.IMGroupInfoListReq groupInfoListReq = IMGroup.IMGroupInfoListReq.newBuilder() .setUserId(loginId) .addAllGroupVersionList(versionInfoList) .build(); int sid = IMBaseDefine.ServiceID.SID_GROUP_VALUE; int cid = IMBaseDefine.GroupCmdID.CID_GROUP_INFO_REQUEST_VALUE; imSocketManager.sendRequest(groupInfoListReq, sid, cid); } public void onRepGroupDetailInfo(IMGroup.IMGroupInfoListRsp groupInfoListRsp) { logger.i("group#onRepGroupDetailInfo"); int groupSize = groupInfoListRsp.getGroupInfoListCount(); int userId = groupInfoListRsp.getUserId(); int loginId = imLoginManager.getLoginId(); logger.i("group#onRepGroupDetailInfo cnt:%d", groupSize); if (groupSize <= 0 || userId != loginId) { logger.i("group#onRepGroupDetailInfo size empty or userid[%d]≠ loginId[%d]", userId, loginId); return; } ArrayList<GroupEntity> needDb = new ArrayList<>(); for (IMBaseDefine.GroupInfo groupInfo : groupInfoListRsp.getGroupInfoListList()) { // 群组的详细信息 // 保存在DB中 // GroupManager 中的变量 GroupEntity groupEntity = ProtoBuf2JavaBean.getGroupEntity(groupInfo); groupMap.put(groupEntity.getPeerId(), groupEntity); needDb.add(groupEntity); } dbInterface.batchInsertOrUpdateGroup(needDb); triggerEvent(new GroupEvent(GroupEvent.Event.GROUP_INFO_UPDATED)); } /** 创建群 默认是创建临时群,且客户端只能创建临时群 */ public void reqCreateTempGroup(String groupName, Set<Integer> memberList) { logger.i("group#reqCreateTempGroup, tempGroupName = %s", groupName); int loginId = imLoginManager.getLoginId(); IMGroup.IMGroupCreateReq groupCreateReq = IMGroup.IMGroupCreateReq.newBuilder() .setUserId(loginId) .setGroupType(IMBaseDefine.GroupType.GROUP_TYPE_TMP) .setGroupName(groupName) .setGroupAvatar("") // todo 群头像 现在是四宫格 .addAllMemberIdList(memberList) .build(); int sid = IMBaseDefine.ServiceID.SID_GROUP_VALUE; int cid = IMBaseDefine.GroupCmdID.CID_GROUP_CREATE_REQUEST_VALUE; imSocketManager.sendRequest( groupCreateReq, sid, cid, new Packetlistener() { @Override public void onSuccess(Object response) { try { IMGroup.IMGroupCreateRsp groupCreateRsp = IMGroup.IMGroupCreateRsp.parseFrom((CodedInputStream) response); IMGroupManager.instance().onReqCreateTempGroup(groupCreateRsp); } catch (IOException e) { logger.e("reqCreateTempGroup parse error"); triggerEvent(new GroupEvent(GroupEvent.Event.CREATE_GROUP_FAIL)); } } @Override public void onFaild() { triggerEvent(new GroupEvent(GroupEvent.Event.CREATE_GROUP_FAIL)); } @Override public void onTimeout() { triggerEvent(new GroupEvent(GroupEvent.Event.CREATE_GROUP_TIMEOUT)); } }); } public void onReqCreateTempGroup(IMGroup.IMGroupCreateRsp groupCreateRsp) { logger.d("group#onReqCreateTempGroup"); int resultCode = groupCreateRsp.getResultCode(); if (0 != resultCode) { logger.e("group#createGroup failed"); triggerEvent(new GroupEvent(GroupEvent.Event.CREATE_GROUP_FAIL)); return; } GroupEntity groupEntity = ProtoBuf2JavaBean.getGroupEntity(groupCreateRsp); // 更新DB 更新map groupMap.put(groupEntity.getPeerId(), groupEntity); IMSessionManager.instance().updateSession(groupEntity); dbInterface.insertOrUpdateGroup(groupEntity); triggerEvent(new GroupEvent(GroupEvent.Event.CREATE_GROUP_OK, groupEntity)); // 接收到之后修改UI } /** 删除群成员 REMOVE_CHANGE_MEMBER_TYPE 可能会触发头像的修改 */ public void reqRemoveGroupMember(int groupId, Set<Integer> removeMemberlist) { reqChangeGroupMember( groupId, IMBaseDefine.GroupModifyType.GROUP_MODIFY_TYPE_DEL, removeMemberlist); } /** 新增群成员 ADD_CHANGE_MEMBER_TYPE 可能会触发头像的修改 */ public void reqAddGroupMember(int groupId, Set<Integer> addMemberlist) { reqChangeGroupMember( groupId, IMBaseDefine.GroupModifyType.GROUP_MODIFY_TYPE_ADD, addMemberlist); } private void reqChangeGroupMember( int groupId, IMBaseDefine.GroupModifyType groupModifyType, Set<Integer> changeMemberlist) { logger.i("group#reqChangeGroupMember, changeGroupMemberType = %s", groupModifyType.toString()); final int loginId = imLoginManager.getLoginId(); IMGroup.IMGroupChangeMemberReq groupChangeMemberReq = IMGroup.IMGroupChangeMemberReq.newBuilder() .setUserId(loginId) .setChangeType(groupModifyType) .addAllMemberIdList(changeMemberlist) .setGroupId(groupId) .build(); int sid = IMBaseDefine.ServiceID.SID_GROUP_VALUE; int cid = IMBaseDefine.GroupCmdID.CID_GROUP_CHANGE_MEMBER_REQUEST_VALUE; imSocketManager.sendRequest( groupChangeMemberReq, sid, cid, new Packetlistener() { @Override public void onSuccess(Object response) { try { IMGroup.IMGroupChangeMemberRsp groupChangeMemberRsp = IMGroup.IMGroupChangeMemberRsp.parseFrom((CodedInputStream) response); IMGroupManager.instance().onReqChangeGroupMember(groupChangeMemberRsp); } catch (IOException e) { logger.e("reqChangeGroupMember parse error!"); triggerEvent(new GroupEvent(GroupEvent.Event.CHANGE_GROUP_MEMBER_FAIL)); } } @Override public void onFaild() { triggerEvent(new GroupEvent(GroupEvent.Event.CHANGE_GROUP_MEMBER_FAIL)); } @Override public void onTimeout() { triggerEvent(new GroupEvent(GroupEvent.Event.CHANGE_GROUP_MEMBER_TIMEOUT)); } }); } public void onReqChangeGroupMember(IMGroup.IMGroupChangeMemberRsp groupChangeMemberRsp) { int resultCode = groupChangeMemberRsp.getResultCode(); if (0 != resultCode) { triggerEvent(new GroupEvent(GroupEvent.Event.CHANGE_GROUP_MEMBER_FAIL)); return; } int groupId = groupChangeMemberRsp.getGroupId(); List<Integer> changeUserIdList = groupChangeMemberRsp.getChgUserIdListList(); IMBaseDefine.GroupModifyType groupModifyType = groupChangeMemberRsp.getChangeType(); GroupEntity groupEntityRet = groupMap.get(groupId); groupEntityRet.setlistGroupMemberIds(groupChangeMemberRsp.getCurUserIdListList()); groupMap.put(groupId, groupEntityRet); dbInterface.insertOrUpdateGroup(groupEntityRet); GroupEvent groupEvent = new GroupEvent(GroupEvent.Event.CHANGE_GROUP_MEMBER_SUCCESS); groupEvent.setChangeList(changeUserIdList); groupEvent.setChangeType(ProtoBuf2JavaBean.getGroupChangeType(groupModifyType)); groupEvent.setGroupEntity(groupEntityRet); triggerEvent(groupEvent); } /** 屏蔽群消息 IMGroupShieldReq 备注:应为屏蔽之后大部分操作依旧需要客户端做 */ public void reqShieldGroup(final int groupId, final int shieldType) { final GroupEntity entity = groupMap.get(groupId); if (entity == null) { logger.i("GroupEntity do not exist!"); return; } final int loginId = IMLoginManager.instance().getLoginId(); IMGroup.IMGroupShieldReq shieldReq = IMGroup.IMGroupShieldReq.newBuilder() .setShieldStatus(shieldType) .setGroupId(groupId) .setUserId(loginId) .build(); int sid = IMBaseDefine.ServiceID.SID_GROUP_VALUE; int cid = IMBaseDefine.GroupCmdID.CID_GROUP_SHIELD_GROUP_REQUEST_VALUE; imSocketManager.sendRequest( shieldReq, sid, cid, new Packetlistener() { @Override public void onSuccess(Object response) { try { IMGroup.IMGroupShieldRsp groupShieldRsp = IMGroup.IMGroupShieldRsp.parseFrom((CodedInputStream) response); int resCode = groupShieldRsp.getResultCode(); if (resCode != 0) { triggerEvent(new GroupEvent(GroupEvent.Event.SHIELD_GROUP_FAIL)); return; } if (groupShieldRsp.getGroupId() != groupId || groupShieldRsp.getUserId() != loginId) { return; } // 更新DB状态 entity.setStatus(shieldType); dbInterface.insertOrUpdateGroup(entity); // 更改未读计数状态 boolean isFor = shieldType == DBConstant.GROUP_STATUS_SHIELD; IMUnreadMsgManager.instance() .setForbidden( EntityChangeEngine.getSessionKey(groupId, DBConstant.SESSION_TYPE_GROUP), isFor); triggerEvent(new GroupEvent(GroupEvent.Event.SHIELD_GROUP_OK, entity)); } catch (IOException e) { logger.e("reqChangeGroupMember parse error!"); triggerEvent(new GroupEvent(GroupEvent.Event.SHIELD_GROUP_FAIL)); } } @Override public void onFaild() { triggerEvent(new GroupEvent(GroupEvent.Event.SHIELD_GROUP_FAIL)); } @Override public void onTimeout() { triggerEvent(new GroupEvent(GroupEvent.Event.SHIELD_GROUP_TIMEOUT)); } }); } /** 收到群成员发生变更消息 服务端主动发出 DB */ public void receiveGroupChangeMemberNotify(IMGroup.IMGroupChangeMemberNotify notify) { int groupId = notify.getGroupId(); int changeType = ProtoBuf2JavaBean.getGroupChangeType(notify.getChangeType()); List<Integer> changeList = notify.getChgUserIdListList(); List<Integer> curMemberList = notify.getCurUserIdListList(); if (groupMap.containsKey(groupId)) { GroupEntity entity = groupMap.get(groupId); entity.setlistGroupMemberIds(curMemberList); dbInterface.insertOrUpdateGroup(entity); groupMap.put(groupId, entity); GroupEvent groupEvent = new GroupEvent(GroupEvent.Event.CHANGE_GROUP_MEMBER_SUCCESS); groupEvent.setChangeList(changeList); groupEvent.setChangeType(changeType); groupEvent.setGroupEntity(entity); triggerEvent(groupEvent); } else { // todo 没有就暂时不管了,只要聊过天都会显示在回话里面 } } public List<GroupEntity> getNormalGroupList() { List<GroupEntity> normalGroupList = new ArrayList<>(); for (Entry<Integer, GroupEntity> entry : groupMap.entrySet()) { GroupEntity group = entry.getValue(); if (group == null) { continue; } if (group.getGroupType() == DBConstant.GROUP_TYPE_NORMAL) { normalGroupList.add(group); } } return normalGroupList; } // 该方法只有正式群 // todo eric efficiency public List<GroupEntity> getNormalGroupSortedList() { List<GroupEntity> groupList = getNormalGroupList(); Collections.sort( groupList, new Comparator<GroupEntity>() { @Override public int compare(GroupEntity entity1, GroupEntity entity2) { if (entity1.getPinyinElement().pinyin == null) { PinYin.getPinYin(entity1.getMainName(), entity1.getPinyinElement()); } if (entity2.getPinyinElement().pinyin == null) { PinYin.getPinYin(entity2.getMainName(), entity2.getPinyinElement()); } return entity1 .getPinyinElement() .pinyin .compareToIgnoreCase(entity2.getPinyinElement().pinyin); } }); return groupList; } public GroupEntity findGroup(int groupId) { logger.d("group#findGroup groupId:%s", groupId); if (groupMap.containsKey(groupId)) { return groupMap.get(groupId); } return null; } public List<GroupEntity> getSearchAllGroupList(String key) { List<GroupEntity> searchList = new ArrayList<>(); for (Map.Entry<Integer, GroupEntity> entry : groupMap.entrySet()) { GroupEntity groupEntity = entry.getValue(); if (IMUIHelper.handleGroupSearch(key, groupEntity)) { searchList.add(groupEntity); } } return searchList; } public List<UserEntity> getGroupMembers(int groupId) { logger.d("group#getGroupMembers groupId:%s", groupId); GroupEntity group = findGroup(groupId); if (group == null) { logger.e("group#no such group id:%s", groupId); return null; } Set<Integer> userList = group.getlistGroupMemberIds(); ArrayList<UserEntity> memberList = new ArrayList<UserEntity>(); for (Integer id : userList) { UserEntity contact = IMContactManager.instance().findContact(id); if (contact == null) { logger.e("group#no such contact id:%s", id); continue; } memberList.add(contact); } return memberList; } /** ------set/get 的定义 */ public Map<Integer, GroupEntity> getGroupMap() { return groupMap; } public boolean isGroupReady() { return isGroupReady; } }
/** 未读消息相关的处理,归属于messageEvent中 可以理解为MessageManager的又一次拆分 为session提供未读支持。 DB 中不保存 */ public class IMUnreadMsgManager extends IMManager { private Logger logger = Logger.getLogger(IMUnreadMsgManager.class); private static IMUnreadMsgManager inst = new IMUnreadMsgManager(); public static IMUnreadMsgManager instance() { return inst; } private IMSocketManager imSocketManager = IMSocketManager.instance(); private IMLoginManager loginManager = IMLoginManager.instance(); /** key=> sessionKey */ private ConcurrentHashMap<String, UnreadEntity> unreadMsgMap = new ConcurrentHashMap<>(); private int totalUnreadCount = 0; private boolean unreadListReady = false; @Override public void doOnStart() {} // 未读消息控制器,本地是不存状态的 public void onNormalLoginOk() { unreadMsgMap.clear(); reqUnreadMsgContactList(); } public void onLocalNetOk() { unreadMsgMap.clear(); reqUnreadMsgContactList(); } @Override public void reset() { unreadListReady = false; unreadMsgMap.clear(); } /** * 继承该方法实现自身的事件驱动 * * @param event */ public synchronized void triggerEvent(UnreadEvent event) { switch (event.event) { case UNREAD_MSG_LIST_OK: unreadListReady = true; break; } EventBus.getDefault().post(event); } /** -------------------------------分割线---------------------------------- */ /** 请求未读消息列表 */ private void reqUnreadMsgContactList() { logger.i("unread#1reqUnreadMsgContactList"); int loginId = IMLoginManager.instance().getLoginId(); IMMessage.IMUnreadMsgCntReq unreadMsgCntReq = IMMessage.IMUnreadMsgCntReq.newBuilder().setUserId(loginId).build(); int sid = IMBaseDefine.ServiceID.SID_MSG_VALUE; int cid = IMBaseDefine.MessageCmdID.CID_MSG_UNREAD_CNT_REQUEST_VALUE; imSocketManager.sendRequest(unreadMsgCntReq, sid, cid); } public void onRepUnreadMsgContactList(IMMessage.IMUnreadMsgCntRsp unreadMsgCntRsp) { logger.i("unread#2onRepUnreadMsgContactList"); totalUnreadCount = unreadMsgCntRsp.getTotalCnt(); List<IMBaseDefine.UnreadInfo> unreadInfoList = unreadMsgCntRsp.getUnreadinfoListList(); logger.i( "unread#unreadMsgCnt:%d, unreadMsgInfoCnt:%d", unreadInfoList.size(), totalUnreadCount); for (IMBaseDefine.UnreadInfo unreadInfo : unreadInfoList) { UnreadEntity unreadEntity = ProtoBuf2JavaBean.getUnreadEntity(unreadInfo); // 屏蔽的设定 addIsForbidden(unreadEntity); unreadMsgMap.put(unreadEntity.getSessionKey(), unreadEntity); } triggerEvent(new UnreadEvent(UnreadEvent.Event.UNREAD_MSG_LIST_OK)); } /** * 回话是否已经被设定为屏蔽 * * @param unreadEntity */ private void addIsForbidden(UnreadEntity unreadEntity) { if (unreadEntity.getSessionType() == DBConstant.SESSION_TYPE_GROUP) { GroupEntity groupEntity = IMGroupManager.instance().findGroup(unreadEntity.getPeerId()); if (groupEntity != null && groupEntity.getStatus() == DBConstant.GROUP_STATUS_SHIELD) { unreadEntity.setForbidden(true); } } } /** 设定未读回话为屏蔽回话 仅限于群组 todo */ public void setForbidden(String sessionKey, boolean isFor) { UnreadEntity unreadEntity = unreadMsgMap.get(sessionKey); if (unreadEntity != null) { unreadEntity.setForbidden(isFor); } } public void add(MessageEntity msg) { // 更新session list中的msg信息 // 更新未读消息计数 if (msg == null) { logger.d("unread#unreadMgr#add msg is null!"); return; } // isFirst场景:出现一条未读消息,出现小红点,需要触发 [免打扰的情况下] boolean isFirst = false; logger.d("unread#unreadMgr#add unread msg:%s", msg); UnreadEntity unreadEntity; int loginId = IMLoginManager.instance().getLoginId(); String sessionKey = msg.getSessionKey(); boolean isSend = msg.isSend(loginId); if (isSend) { IMNotificationManager.instance().cancelSessionNotifications(sessionKey); return; } if (unreadMsgMap.containsKey(sessionKey)) { unreadEntity = unreadMsgMap.get(sessionKey); // 判断最后一条msgId是否相同 if (unreadEntity.getLaststMsgId() == msg.getMsgId()) { return; } unreadEntity.setUnReadCnt(unreadEntity.getUnReadCnt() + 1); } else { isFirst = true; unreadEntity = new UnreadEntity(); unreadEntity.setUnReadCnt(1); unreadEntity.setPeerId(msg.getPeerId(isSend)); unreadEntity.setSessionType(msg.getSessionType()); unreadEntity.buildSessionKey(); } unreadEntity.setLatestMsgData(msg.getMessageDisplay()); unreadEntity.setLaststMsgId(msg.getMsgId()); addIsForbidden(unreadEntity); /** 放入manager 状态中 */ unreadMsgMap.put(unreadEntity.getSessionKey(), unreadEntity); /** 没有被屏蔽才会发送广播 */ if (!unreadEntity.isForbidden() || isFirst) { UnreadEvent unreadEvent = new UnreadEvent(); unreadEvent.event = UnreadEvent.Event.UNREAD_MSG_RECEIVED; unreadEvent.entity = unreadEntity; triggerEvent(unreadEvent); } } public void ackReadMsg(MessageEntity entity) { logger.d("chat#ackReadMsg -> msg:%s", entity); int loginId = loginManager.getLoginId(); IMBaseDefine.SessionType sessionType = Java2ProtoBuf.getProtoSessionType(entity.getSessionType()); IMMessage.IMMsgDataReadAck readAck = IMMessage.IMMsgDataReadAck.newBuilder() .setMsgId(entity.getMsgId()) .setSessionId(entity.getPeerId(false)) .setSessionType(sessionType) .setUserId(loginId) .build(); int sid = IMBaseDefine.ServiceID.SID_MSG_VALUE; int cid = IMBaseDefine.MessageCmdID.CID_MSG_READ_ACK_VALUE; imSocketManager.sendRequest(readAck, sid, cid); } public void ackReadMsg(UnreadEntity unreadEntity) { logger.d("chat#ackReadMsg -> msg:%s", unreadEntity); int loginId = loginManager.getLoginId(); IMBaseDefine.SessionType sessionType = Java2ProtoBuf.getProtoSessionType(unreadEntity.getSessionType()); IMMessage.IMMsgDataReadAck readAck = IMMessage.IMMsgDataReadAck.newBuilder() .setMsgId(unreadEntity.getLaststMsgId()) .setSessionId(unreadEntity.getPeerId()) .setSessionType(sessionType) .setUserId(loginId) .build(); int sid = IMBaseDefine.ServiceID.SID_MSG_VALUE; int cid = IMBaseDefine.MessageCmdID.CID_MSG_READ_ACK_VALUE; imSocketManager.sendRequest(readAck, sid, cid); } /** * 服务端主动发送已读通知 * * @param readNotify */ public void onNotifyRead(IMMessage.IMMsgDataReadNotify readNotify) { logger.d("chat#onNotifyRead"); // 发送此信令的用户id int trigerId = readNotify.getUserId(); int loginId = IMLoginManager.instance().getLoginId(); if (trigerId != loginId) { logger.i("onNotifyRead# trigerId:%s,loginId:%s not Equal", trigerId, loginId); return; } // 现在的逻辑是msgId之后的 全部都是已读的 // 不做复杂判断了,简单处理 int msgId = readNotify.getMsgId(); int peerId = readNotify.getSessionId(); int sessionType = ProtoBuf2JavaBean.getJavaSessionType(readNotify.getSessionType()); String sessionKey = EntityChangeEngine.getSessionKey(peerId, sessionType); // 通知栏也要去除掉 NotificationManager notifyMgr = (NotificationManager) ctx.getSystemService(Context.NOTIFICATION_SERVICE); if (notifyMgr == null) { return; } int notificationId = IMNotificationManager.instance().getSessionNotificationId(sessionKey); notifyMgr.cancel(notificationId); UnreadEntity unreadSession = findUnread(sessionKey); if (unreadSession != null && unreadSession.getLaststMsgId() <= msgId) { // 清空会话session logger.d("chat#onNotifyRead# unreadSession onLoginOut"); readUnreadSession(sessionKey); } } /** * 备注: 先获取最后一条消息 1. 清除回话内的未读计数 2. 发送最后一条msgId的已读确认 * * @param sessionKey */ public void readUnreadSession(String sessionKey) { logger.d("unread#readUnreadSession# sessionKey:%s", sessionKey); if (unreadMsgMap.containsKey(sessionKey)) { UnreadEntity entity = unreadMsgMap.remove(sessionKey); ackReadMsg(entity); triggerEvent(new UnreadEvent(UnreadEvent.Event.SESSION_READED_UNREAD_MSG)); } } public UnreadEntity findUnread(String sessionKey) { logger.d("unread#findUnread# buddyId:%s", sessionKey); if (TextUtils.isEmpty(sessionKey) || unreadMsgMap.size() <= 0) { logger.i("unread#findUnread# no unread info"); return null; } if (unreadMsgMap.containsKey(sessionKey)) { return unreadMsgMap.get(sessionKey); } return null; } /** ----------------实体set/get------------------------------- */ public ConcurrentHashMap<String, UnreadEntity> getUnreadMsgMap() { return unreadMsgMap; } public int getTotalUnreadCount() { int count = 0; for (UnreadEntity entity : unreadMsgMap.values()) { if (!entity.isForbidden()) { count = count + entity.getUnReadCnt(); } } return count; } public boolean isUnreadListReady() { return unreadListReady; } }