FileDocCategorySizeDatePackage
ImpsChatSessionManager.javaAPI DocAndroid 1.5 API9085Wed May 06 22:42:46 BST 2009com.android.im.imps

ImpsChatSessionManager.java

/*
 * 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;
    }
}