package com.mogujie.tt.imlib;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import android.content.BroadcastReceiver;
import android.content.Intent;
import android.os.Handler;
import android.os.Message;
import com.mogujie.tt.config.HandlerConstant;
import com.mogujie.tt.config.ProtocolConstant;
import com.mogujie.tt.config.SysConstant;
import com.mogujie.tt.entity.MessageInfo;
import com.mogujie.tt.imlib.db.IMDbManager;
import com.mogujie.tt.imlib.network.SocketThread;
import com.mogujie.tt.imlib.proto.AckGroupUnreadMsgPacket;
import com.mogujie.tt.imlib.proto.AckUnreadMsgPacket;
import com.mogujie.tt.imlib.proto.ContactEntity;
import com.mogujie.tt.imlib.proto.GroupEntity;
import com.mogujie.tt.imlib.proto.GroupUnreadMsgPacket;
import com.mogujie.tt.imlib.proto.MessageEntity;
import com.mogujie.tt.imlib.proto.MessageNotifyPacket;
import com.mogujie.tt.imlib.proto.MessagePacket;
import com.mogujie.tt.imlib.proto.UnreadMsgPacket;
import com.mogujie.tt.imlib.proto.UnreadMsgPacket.PacketResponse;
import com.mogujie.tt.imlib.utils.IMContactHelper;
import com.mogujie.tt.imlib.utils.IMUIHelper;
import com.mogujie.tt.log.Logger;
import com.mogujie.tt.packet.base.DataBuffer;
import com.mogujie.tt.task.TaskManager;
import com.mogujie.tt.task.biz.UploadImageTask;
import com.mogujie.tt.ui.utils.IMServiceHelper;
import com.mogujie.tt.ui.utils.IMServiceHelper.OnIMServiceListner;
public class IMMessageManager extends IMManager implements OnIMServiceListner {
private static IMMessageManager inst;
private Logger logger = Logger.getLogger(IMMessageManager.class);
public static IMMessageManager instance() {
synchronized (IMMessageManager.class) {
if (inst == null) {
inst = new IMMessageManager();
}
return inst;
}
}
private int seqNo = 1;
private List<MessageInfo> noSessionEntityMsgList = new ArrayList<MessageInfo>();
private IMServiceHelper imServiceHelper = new IMServiceHelper();
private static Handler mainHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case HandlerConstant.HANDLER_IMAGE_UPLOAD_FAILD :
IMMessageManager.instance().onUploadImageFaild(msg.obj);
break;
case HandlerConstant.HANDLER_IMAGE_UPLOAD_SUCESS :
IMMessageManager.instance().onImageUploadFinish(msg.obj);
break;
default :
break;
}
super.handleMessage(msg);
}
};
public void onUploadImageFaild(Object obj) {
logger.d("pic#onUploadImageFaild");
if (obj == null)
return;
MessageInfo messageInfo = (MessageInfo) obj;
logger.d("pic#msg:%s", messageInfo);
IMUnAckMsgManager.instance().handleTimeoutUnAckMsg(messageInfo.msgId);
broadcastMsgStatus(messageInfo, SysConstant.MESSAGE_STATE_FINISH_FAILED);
}
public void onImageUploadFinish(Object obj) {
logger.d("pic#onImageUploadFinish");
if (obj == null)
return;
MessageInfo messageInfo = (MessageInfo) obj;
logger.d("pic#msg:%s", messageInfo);
String imageUrl = messageInfo.getUrl();
logger.d("pic#imageUrl:%s", imageUrl);
String realImageURL = "";
try {
realImageURL = URLDecoder.decode(imageUrl, "utf-8");
logger.d("pic#realImageUrl:%s", realImageURL);
} catch (UnsupportedEncodingException e) {
logger.e(e.toString());
}
MessageInfo msgInfo = IMUnAckMsgManager.instance().get(messageInfo.msgId);
if (msgInfo == null) {
logger.e("pic#no such msgInfo");
}
msgInfo.setUrl(realImageURL);
msgInfo.setMsgLoadState(SysConstant.MESSAGE_STATE_LOADDING);
msgInfo.setMsgContent(SysConstant.MESSAGE_IMAGE_LINK_START
+ realImageURL + SysConstant.MESSAGE_IMAGE_LINK_END);
broadcastMsgStatus(msgInfo, SysConstant.MESSAGE_STATE_LOADDING);
logger.d("pic#send pic message, msg:%s", msgInfo);
sendText(msgInfo.getTargetId(), msgInfo.getMsgContent(), msgInfo.sessionType, msgInfo);
}
private void broadcastMsgStatus(MessageInfo msg, int status) {
logger.d("chat#pic#broadcastMsgStatus msg:%s, status:%d", msg, status);
Intent intent = new Intent(IMActions.ACTION_MSG_STATUS);
IMUIHelper.setSessionInIntent(intent, msg.sessionId, msg.sessionType);
intent.putExtra(SysConstant.STATUS_KEY, status);
if (ctx != null) {
ctx.sendBroadcast(intent);
logger.d("chat#pic#broadcast ok");
}
}
private IMMessageManager() {
}
public void register() {
logger.d("chat#regisgter");
List<String> actions = new ArrayList<String>();
actions.add(IMActions.ACTION_GROUP_READY);
imServiceHelper.registerActions(ctx, actions, IMServiceHelper.INTENT_NO_PRIORITY, this);
}
public void sendText(String sessionId, String text, int sessionType,
MessageInfo msgInfo) {
logger.i("chat#text#sendText -> peerId:%s, text:%s", sessionId, text);
fillMessageCommonInfo(msgInfo, sessionId, getTextMsgType(sessionType), sessionType);
msgInfo.msgData = text.getBytes(Charset.forName("utf8"));
msgInfo.msgLen = msgInfo.msgData.length;
sendMessage(msgInfo);
}
public void sendVoice(String sessionId, byte[] voiceData, int sessionType,
MessageInfo msgInfo) {
logger.i("chat#audio#sendVoice -> sessionId:%s, voidDataLen:%d", sessionId, voiceData.length);
fillMessageCommonInfo(msgInfo, sessionId, getAudioMsgType(sessionType), sessionType);
// todo eric utf8?
msgInfo.msgData = voiceData;
msgInfo.msgLen = voiceData.length;
sendMessage(msgInfo);
}
public void sendImages(String sessionId, int sessionType,
List<MessageInfo> msgList) {
for (MessageInfo msg : msgList) {
logger.d("chat#pic#sendImage sessionId:%s, msg:%s", sessionId, msg);
//image message would wrapped as a text message after uploading image to server ok
fillMessageCommonInfo(msg, sessionId, getTextMsgType(sessionType), sessionType);
}
//todo eric get rid of TaskManager
UploadImageTask upTask = new UploadImageTask(mainHandler, sessionType, SysConstant.UPLOAD_IMAGE_URL_PREFIX, "", msgList);
TaskManager.getInstance().trigger(upTask);
}
private void fillMessageCommonInfo(MessageInfo msg, String sessionId,
byte msgType, int sessionType) {
msg.seqNo = seqNo++;
msg.fromId = IMLoginManager.instance().getLoginId();
msg.toId = sessionId;
msg.type = msgType;
msg.generateMsgIdIfEmpty();
msg.generateSessionId(true);
msg.generateSessionType(sessionType);
logger.d("chat#msg.fromid" + msg.fromId);
// todo eric, use the server time
msg.createTime = (int) (System.currentTimeMillis() / 1000);
// it looks no one use attach param now
msg.attach = "";
}
private static byte getTextMsgType(int sessionType) {
byte msgType = ProtocolConstant.MSG_TYPE_P2P_TEXT;
if (sessionType == IMSession.SESSION_GROUP
|| sessionType == IMSession.SESSION_TEMP_GROUP) {
msgType = ProtocolConstant.MSG_TYPE_GROUP_TEXT;
}
return msgType;
}
private static byte getAudioMsgType(int sessionType) {
byte msgType = ProtocolConstant.MSG_TYPE_P2P_AUDIO;
if (sessionType == IMSession.SESSION_GROUP
|| sessionType == IMSession.SESSION_TEMP_GROUP) {
msgType = ProtocolConstant.MSG_TYPE_GROUP_AUDIO;
}
return msgType;
}
public void resendMessage(MessageInfo msgInfo) {
logger.d("chat#resend#resendMessage msgInfo:%s", msgInfo);
if (msgInfo == null) {
return;
}
msgInfo.setResend(true);
if (msgInfo.isTextType()) {
logger.d("chat#resend#this is a text type message");
sendText(msgInfo.toId, msgInfo.getMsgContent(), msgInfo.sessionType, msgInfo);
} else if (msgInfo.isAudioType()) {
logger.d("chat#resend#this is an audio type message");
sendVoice(msgInfo.toId, msgInfo.getAudioContent(), msgInfo.sessionType, msgInfo);
} else if (msgInfo.isImage()) {
logger.d("chat#pic#resend#this is a picture type message");
List<MessageInfo> msgList = new ArrayList<MessageInfo>();
msgList.add(msgInfo);
sendImages(msgInfo.sessionId, msgInfo.sessionType, msgList);
}
if (ctx != null) {
Intent intent = new Intent(IMActions.ACTION_MSG_RESENT);
IMUIHelper.setSessionInIntent(intent, msgInfo.sessionId, msgInfo.sessionType);
ctx.sendBroadcast(intent);
}
}
private void sendMessage(MessageInfo msgInfo) {
logger.i("chat#sendMessage, msg:%s", msgInfo);
SocketThread channel = IMLoginManager.instance().getMsgServerChannel();
if (channel == null) {
logger.e("contact#channel is null");
return;
}
// todo eric, this should be put in ui layer
msgInfo.setMsgLoadState(SysConstant.MESSAGE_STATE_LOADDING);
IMUnAckMsgManager.instance().add(msgInfo);
IMRecentSessionManager.instance().update(msgInfo);
IMRecentSessionManager.instance().broadcast();
channel.sendPacket(new MessagePacket(msgInfo));
}
private boolean broadcastMessage(String sessionId, int sessionType,
String action, boolean ordered) {
// todo eric use local broadcast
if (ctx != null) {
Intent intent = new Intent(action);
IMUIHelper.setSessionInIntent(intent, sessionId, sessionType);
if (ordered) {
ctx.sendOrderedBroadcast(intent, null);
} else {
ctx.sendBroadcast(intent);
}
return true;
}
return false;
}
private boolean broadcastMessageAck(MessageEntity msg, String action) {
// todo eric use local broadcast
if (ctx != null) {
Intent intent = new Intent(action);
IMUIHelper.setSessionInIntent(intent, msg.sessionId, msg.sessionType);
intent.putExtra(SysConstant.MSG_ID_KEY, msg.msgId);
ctx.sendBroadcast(intent);
return true;
}
return false;
}
public void onMessageAck(DataBuffer buffer) {
logger.i("chat#onMessageAck");
MessagePacket packet = new MessagePacket();
packet.decode(buffer);
// todo eric adjust eclipse formatting detail, let .getResponse on the
// same line(more characters on 1 line?)
MessagePacket.PacketResponse resp = (MessagePacket.PacketResponse) packet.getResponse();
logger.d("chat#get msg ack:%s", resp.msgAck);
MessageInfo msg = IMUnAckMsgManager.instance().remove(resp.msgAck.seqNo);
msg.setMsgLoadState(SysConstant.MESSAGE_STATE_FINISH_SUCCESSED);
IMDbManager.instance(ctx).updateMessageStatus(msg);
if (broadcastMessageAck(msg, IMActions.ACTION_MSG_ACK)) {
logger.d("chat#broadcast receiving ack msg");
}
}
private MessageInfo decodeMessageInfo(DataBuffer buffer) {
MessageNotifyPacket packet = new MessageNotifyPacket();
packet.decode(buffer);
logger.d("decode recv message ok");
// todo eric unify the getResponse to get notify, but low priority, 'cuz
// we're gonna make the tool to automatically
// generate the packet
MessageNotifyPacket.packetNotify notify = (MessageNotifyPacket.packetNotify) packet.getResponse();
MessageEntity msg = notify.msg;
logger.d("chat#msg:%s", msg);
return new MessageInfo(msg);
}
private Object findMsgSessionEntity(MessageInfo msg) {
logger.d("chat#findMsgSessionEntity msg:%s", msg);
ContactEntity contactEntity = IMContactManager.instance().findContact(msg.sessionId);
if (contactEntity != null) {
logger.d("chat#this is a contact msg");
return contactEntity;
}
GroupEntity groupEntity = IMGroupManager.instance().findGroup(msg.sessionId);
if (groupEntity != null) {
logger.d("chat#this is a group msg");
return groupEntity;
}
IMGroupManager.instance().reqGetTempGroupList();
return null;
}
public void onRecvMessage(DataBuffer buffer) {
logger.i("chat#onRecvMessage");
MessageInfo msgInfo = decodeMessageInfo(buffer);
if (msgInfo == null) {
logger.e("chat#decodeMessageInfo failed");
return;
}
ackMsg(msgInfo);
Object msgSessionEntity = findMsgSessionEntity(msgInfo);
if (msgSessionEntity != null) {
logger.d("chat#found msgSessionEntity");
handleRecvMsg(msgInfo);
} else {
logger.d("chat#msgSessionEntity not found for msg:%s", msgInfo);
noSessionEntityMsgList.add(msgInfo);
}
}
private void handleRecvMsg(MessageInfo msgInfo) {
logger.d("chat#handleRecvMsg");
List<MessageInfo> msgInfoList = new ArrayList<MessageInfo>();
msgInfoList.add(msgInfo);
handleUnreadMsg(msgInfoList);
IMRecentSessionManager.instance().broadcast();
}
private void setMsgSessionType(MessageInfo msgInfo) {
ContactEntity contact = IMContactManager.instance().findContact(msgInfo.sessionId);
if (contact != null) {
msgInfo.sessionType = IMSession.SESSION_P2P;
return;
}
GroupEntity group = IMGroupManager.instance().findGroup(msgInfo.sessionId);
if (group != null) {
msgInfo.sessionType = group.type;
return;
}
logger.e("chat#unkown msg session type, could be temp group type");
msgInfo.sessionType = IMSession.SESSION_TEMP_GROUP;
}
//msgInfoList should belong to the same session
private void handleUnreadMsg(List<MessageInfo> msgInfoList) {
logger.d("chat#handleUnreadMsg");
String sessionId = "";
int sessionType = -1;
for (MessageInfo msgInfo : msgInfoList) {
if (sessionId.isEmpty()) {
sessionId = msgInfo.sessionId;
sessionType = msgInfo.sessionType;
}
List<MessageInfo> splitMessageList = IMContactHelper.splitMessage(msgInfo);
for (MessageInfo msg : splitMessageList) {
setMsgSessionType(msg);
IMUnreadMsgManager.instance().add(msg);
IMRecentSessionManager.instance().update(msg);
}
}
if (sessionId.isEmpty()) {
logger.e("chat#handleUnreadMsg sessionid is empty");
return;
}
if (broadcastMessage(sessionId, sessionType, IMActions.ACTION_MSG_RECV, true)) {
logger.d("chat#broadcast receiving new msg");
}
}
private void ackMsg(MessageEntity msg) {
logger.d("chat#sendMessageAck -> fromId:%s, seqNo:%d", msg.fromId, seqNo);
SocketThread channel = IMLoginManager.instance().getMsgServerChannel();
if (channel == null) {
logger.e("contact#channel is null");
return;
}
channel.sendPacket(new MessageNotifyPacket(msg));
logger.i("chat#send packet to server");
}
public void onRepUnreadMsg(DataBuffer buffer) {
logger.i("unread#onRepUnreadMsg");
UnreadMsgPacket packet = new UnreadMsgPacket();
packet.decode(buffer);
UnreadMsgPacket.PacketResponse response = (PacketResponse) packet.getResponse();
handleUnreadMsgList(response.entityList);
}
public void onRepGroupUnreadMsg(DataBuffer buffer) {
logger.i("unread#onRepGroupUnreadMsg");
GroupUnreadMsgPacket packet = new GroupUnreadMsgPacket();
packet.decode(buffer);
GroupUnreadMsgPacket.PacketResponse response = (GroupUnreadMsgPacket.PacketResponse) packet.getResponse();
handleUnreadMsgList(response.entityList);
}
private void handleUnreadMsgList(List<MessageEntity> msgEntitiyList) {
List<MessageInfo> msgInfoList = new ArrayList<MessageInfo>();
for (MessageEntity msgEntity : msgEntitiyList) {
MessageInfo msgInfo = new MessageInfo(msgEntity);
msgInfoList.add(msgInfo);
}
//todo eric, why the order is reversed
Collections.reverse(msgInfoList);
handleUnreadMsg(msgInfoList);
IMRecentSessionManager.instance().broadcast();
}
public List<MessageInfo> ReadUnreadMsgList(String sessionId, int sessionType) {
logger.d("unread#ReadUnreadMsgList sessionid:%s, sessionType:%d", sessionId, sessionType);
List<MessageInfo> msgList = IMUnreadMsgManager.instance().popUnreadMsgList(sessionId);
if (msgList == null || msgList.isEmpty()) {
logger.d("unread#no unread msgs");
return msgList;
}
for (MessageInfo msgInfo : msgList) {
msgInfo.generateSessionType(sessionType);
IMDbManager.instance(ctx).saveMsg(msgInfo, false);
}
if (sessionType == IMSession.SESSION_P2P) {
ackUnreadMsgs(sessionId);
} else {
ackGroupUnreadMsgs(sessionId);
}
return msgList;
}
public void ackUnreadMsgs(String contactId) {
if (contactId == null) {
return;
}
logger.d("chat#ackUnreadMsgs contactId:%s", contactId);
SocketThread channel = IMLoginManager.instance().getMsgServerChannel();
if (channel == null) {
logger.e("contact#channel is null");
return;
}
channel.sendPacket(new AckUnreadMsgPacket(contactId));
logger.i("chat#send packet to server");
}
public void ackGroupUnreadMsgs(String groupId) {
if (groupId == null) {
return;
}
logger.d("chat#ackUnreadMsgs groupId:%s", groupId);
SocketThread channel = IMLoginManager.instance().getMsgServerChannel();
if (channel == null) {
logger.e("contact#channel is null");
return;
}
channel.sendPacket(new AckGroupUnreadMsgPacket(groupId));
logger.i("chat#send packet to server");
}
@Override
public void onAction(String action, Intent intent,
BroadcastReceiver broadcastReceiver) {
// TODO Auto-generated method stub
if (action.equals(IMActions.ACTION_GROUP_READY)) {
logger.d("chat#on action group ready");
for (MessageInfo msg : noSessionEntityMsgList) {
handleRecvMsg(msg);
}
}
}
@Override
public void onIMServiceConnected() {
// TODO Auto-generated method stub
}
}