/* * Copyright (C) 2007-2008 Esmertec AG. * Copyright (C) 2007-2008 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.im.imps; import java.util.ArrayList; import java.util.Date; import android.text.format.Time; import android.util.TimeFormatException; import com.android.im.engine.Address; import com.android.im.engine.ChatSession; import com.android.im.engine.ChatSessionManager; import com.android.im.engine.ImEntity; import com.android.im.engine.ImErrorInfo; import com.android.im.engine.Message; /** * The implementation of ChatSessionManager with Wireless Village IMPS protocol. */ public class ImpsChatSessionManager extends ChatSessionManager implements ServerTransactionListener { private ImpsConnection mConnection; private ImpsTransactionManager mTransactionManager; private ArrayList<Message> mMessageQueue; private boolean mStartNotifying; ImpsChatSessionManager(ImpsConnection connection) { mConnection = connection; mMessageQueue = new ArrayList<Message>(); mTransactionManager = connection.getTransactionManager(); mTransactionManager.setTransactionListener(ImpsTags.NewMessage, this); mTransactionManager.setTransactionListener(ImpsTags.DeliveryReport_Request, this); } @Override protected void sendMessageAsync(final ChatSession ses, final Message message) { // force to send from the currently logged user. message.setFrom(mConnection.getSession().getLoginUserAddress()); if(message.getDateTime() == null) { message.setDateTime(new Date()); } Primitive primitive = createSendMessagePrimitive(message); AsyncTransaction tx = new AsyncTransaction(mTransactionManager) { @Override public void onResponseOk(Primitive response) { } @Override public void onResponseError(ImpsErrorInfo error) { ses.onSendMessageError(message, error); } }; tx.sendRequest(primitive); } public void notifyServerTransaction(ServerTransaction tx) { Primitive primitive = tx.getRequest(); if (ImpsTags.NewMessage.equals(primitive.getType())) { Message msg = extractMessage(primitive); // send response to the server. Primitive response = new Primitive(ImpsTags.MessageDelivered); response.addElement(ImpsTags.MessageID, msg.getID()); tx.sendResponse(response); synchronized(mMessageQueue) { if(mStartNotifying){ processMessage(msg); } else { mMessageQueue.add(msg); } } } else if(ImpsTags.DeliveryReport_Request.equals(primitive.getType())) { tx.sendStatusResponse(ImpsConstants.SUCCESS_CODE); // We only notify the user when an error occurs. ImErrorInfo error = ImpsUtils.checkResultError(primitive); if(error != null) { PrimitiveElement msgInfo = primitive.getElement(ImpsTags.MessageInfo); String msgId = msgInfo.getChildContents(ImpsTags.MessageID); PrimitiveElement recipent = msgInfo.getChild(ImpsTags.Recipient); ImpsAddress recipentAddress = ImpsAddress.fromPrimitiveElement( recipent.getFirstChild()); ChatSession session = findSession(recipentAddress); if(session != null) { session.onSendMessageError(msgId, error); } else { ImpsLog.log("Session has closed when received delivery error: " + error); } } } } public void start() { synchronized (mMessageQueue) { mStartNotifying = true; for (Message message : mMessageQueue) { processMessage(message); } mMessageQueue.clear(); } } /** * Extracts a message from a NewMessage primitive. * * @param primitive * the NewMessage primitive. * @return an instance of message. */ private Message extractMessage(Primitive primitive) { String msgBody = primitive.getElementContents(ImpsTags.ContentData); if (msgBody == null) { msgBody = ""; } Message msg = new Message(msgBody); PrimitiveElement msgInfo = primitive.getElement(ImpsTags.MessageInfo); String id = msgInfo.getChildContents(ImpsTags.MessageID); msg.setID(id); PrimitiveElement sender = msgInfo.getChild(ImpsTags.Sender); msg.setFrom(ImpsAddress.fromPrimitiveElement(sender.getFirstChild())); PrimitiveElement recipent = msgInfo.getChild(ImpsTags.Recipient); if (recipent != null && recipent.getFirstChild() != null) { msg.setTo(ImpsAddress.fromPrimitiveElement(recipent.getFirstChild())); } else { msg.setTo(mConnection.getLoginUser().getAddress()); } String dateTime = msgInfo.getChildContents(ImpsTags.DateTime); if (dateTime != null) { try { Time t = new Time(); t.parse(dateTime); msg.setDateTime(new Date(t.toMillis(false /* use isDst */))); } catch (TimeFormatException e) { msg.setDateTime(new Date()); } } else { msg.setDateTime(new Date()); } return msg; } /** * Creates a SendMessage-Request primitive to send message. * * @param message the message to send. * @return the SendMessage-Request primitive. */ private Primitive createSendMessagePrimitive(Message message) { Primitive primitive = new Primitive(ImpsTags.SendMessage_Request); primitive.addElement(ImpsTags.DeliveryReport, mConnection.getConfig().needDeliveryReport()); PrimitiveElement msgInfo = primitive.addElement(ImpsTags.MessageInfo); PrimitiveElement recipient = msgInfo.addChild(ImpsTags.Recipient); recipient.addChild(((ImpsAddress)message.getTo()).toPrimitiveElement()); PrimitiveElement sender = msgInfo.addChild(ImpsTags.Sender); sender.addChild(((ImpsAddress)message.getFrom()).toPrimitiveElement()); // XXX: ContentType is optional and by default is "text/plain". // However without this the OZ IMPS server wouldn't reply to our // SendMessage requests and just let the HTTP connection times out. msgInfo.addChild(ImpsTags.ContentType, "text/plain"); // optional // Calendar calendar = Calendar.getInstance(); // calendar.setTime(message.getDateTime()); // msgInfo.addChild(ImpsTags.DateTime, DateUtils.writeDateTime(calendar)); String msgBody = message.getBody(); msgInfo.addChild(ImpsTags.ContentSize, Integer.toString(msgBody.length())); primitive.addElement(ImpsTags.ContentData, msgBody); return primitive; } /** * Processes an incoming message. Called by the sub protocol implementation * when an incoming message arrived. * * @param msg the incoming message. */ void processMessage(Message msg) { ImpsAddress from = (ImpsAddress) msg.getFrom(); ImpsAddress to = (ImpsAddress) msg.getTo(); ImpsAddress address = (to instanceof ImpsGroupAddress) ? to : from; synchronized (this) { ChatSession ses = findSession(address); if (ses == null) { ImEntity participant = address.getEntity(mConnection); if (participant != null) { ses = createChatSession(address.getEntity(mConnection)); } else { ImpsLog.log("Message from unknown sender"); return; } } ses.onReceiveMessage(msg); } } /** * Finds the ChatSession which the message belongs to. * * @param msg the message. * @return the ChatSession or <code>null</code> if the session not exists. */ private ChatSession findSession(Address address) { for(ChatSession session : mSessions) { ImEntity participant = session.getParticipant(); if(participant.getAddress().equals(address)) { return session; } } return null; } }