package org.limewire.facebook.service; import java.util.Map; import java.util.List; import java.util.ArrayList; import java.util.HashMap; import java.util.Collections; import org.limewire.friend.api.ChatState; import org.limewire.friend.api.FriendException; import org.limewire.friend.api.IncomingChatListener; import org.limewire.friend.api.MessageReader; import org.limewire.friend.api.MessageWriter; /** * Manages message readers ({@link org.limewire.friend.api.MessageReader}) that ChatListener * uses to handle incoming instant messages and changing chat states. * * MessageReaders get added to ChatManager either by directly being * added via {@link #addMessageReader}, or by being created via * {@link IncomingChatListener#incomingChat(MessageWriter)}. * */ class ChatManager { private final Map<String, MessageReader> readers = new HashMap<String, MessageReader>(); private final FacebookFriendConnection facebookFriendConnection; ChatManager(FacebookFriendConnection facebookFriendConnection) { this.facebookFriendConnection = facebookFriendConnection; } /** * Associates a friend ID with a MessageReader. Happens when preparing to * initiate a new chat with a friend by sending a chat message. * * @param friendId ID of friend. * @param reader {link @MessageReader} to associate it with. * @return MessageWriter interface to use for sending chat messages. */ MessageWriter addMessageReader(String friendId, MessageReader reader) { addMessageReaderAndProcess(friendId, reader); return new MessageWriterImpl(friendId); } /** * Gets from existing collection of {@link MessageReader} objects * or creates one via the friend's {@link IncomingChatListener}. * * @param friendId ID of friend * @return MessageReader message reader associated with friend. */ MessageReader getMessageReader(String friendId) { if (this.facebookFriendConnection.isLoggedIn()) { synchronized (readers) { MessageReader messageReader = readers.get(friendId); if (messageReader == null) { messageReader = new MessageReaderQueued(); readers.put(friendId, messageReader); } return messageReader; } } return null; } /** * Sets an incoming chat listener for the friend whose ID is friendId * Creates a {@link MessageReader} object wrapping the incoming chat listener. * * @param friendId ID of the friend. * @param listener {@link IncomingChatListener} used to create message readers. */ void setIncomingChatListener(String friendId, IncomingChatListener listener) { MessageReaderIncomingListenerWrapper wrapperReader = new MessageReaderIncomingListenerWrapper(friendId, listener); addMessageReaderAndProcess(friendId, wrapperReader); } /** * Stops managing chats for friendId. * * @param friendId ID of friend */ void removeChat(String friendId) { synchronized (readers) { readers.remove(friendId); } } /** * Add a MessageReader to readers map. If a previous MessageReader was in the map, * read in the queued up messages (if it is a {@link MessageReaderQueued}). * * @param friendId friend id * @param reader MessageReader */ private void addMessageReaderAndProcess(String friendId, MessageReader reader) { synchronized (readers) { MessageReader prevReader = readers.put(friendId, reader); if (prevReader instanceof MessageReaderQueued) { MessageReaderQueued msgQ = (MessageReaderQueued) prevReader; msgQ.processQueuedMessages(reader); } } } /** * {@link MessageReader} wrapper object backed by a MessageReader created * via incoming chat (using {@link IncomingChatListener#incomingChat(MessageWriter)}. * */ private class MessageReaderIncomingListenerWrapper implements MessageReader { private final MessageWriter messageWriter; private final IncomingChatListener listener; private MessageReader messagereader; public MessageReaderIncomingListenerWrapper(String friendId, IncomingChatListener listener) { this.messageWriter = new MessageWriterImpl(friendId); this.listener = listener; this.messagereader = null; } @Override public void readMessage(String message) { getMessageReader().readMessage(message); } @Override public void newChatState(ChatState chatState) { getMessageReader().newChatState(chatState); } @Override public void error(String errorMessage) { getMessageReader().error(errorMessage); } private MessageReader getMessageReader() { if (messagereader == null) { messagereader = listener.incomingChat(messageWriter); } return messagereader; } } /** * A {@link MessageReader} impl that keeps a record of * what is read in, chat states, etc. The messages will be * read in (for example, into the chat window) at a later point in time. */ private class MessageReaderQueued implements MessageReader { private final List<ChatActivity> queuedActivities = Collections.synchronizedList(new ArrayList<ChatActivity>()); @Override public void readMessage(String message) { queuedActivities.add(new MessageActivity(message)); } @Override public void newChatState(ChatState chatState) { queuedActivities.add(new ChatStateActivity(chatState.toString())); } @Override public void error(String errorMessage) { queuedActivities.add(new ErrorActivity(errorMessage)); } public void processQueuedMessages(MessageReader reader) { synchronized (queuedActivities) { for (ChatActivity msg : queuedActivities) { msg.processActivity(reader); } } } } private abstract class ChatActivity { ChatActivity(String text) { this.text = text; } final String text; abstract void processActivity(MessageReader reader); } private class MessageActivity extends ChatActivity { MessageActivity(String text) { super(text); } @Override void processActivity(MessageReader reader) { reader.readMessage(text); } } private class ChatStateActivity extends ChatActivity { ChatStateActivity(String text) { super(text); } @Override void processActivity(MessageReader reader) { reader.newChatState(ChatState.valueOf(text)); } } private class ErrorActivity extends ChatActivity { ErrorActivity(String text) { super(text); } @Override void processActivity(MessageReader reader) { reader.error(text); } } /** * {@link MessageWriter} impl that delegates to the facebook connection * to write messages or set chat states. */ private class MessageWriterImpl implements MessageWriter { private final String friendId; MessageWriterImpl(String friendId) { this.friendId = friendId; } @Override public void writeMessage(String message) throws FriendException { facebookFriendConnection.sendChatMessage(friendId, message); } @Override public void setChatState(ChatState chatState) throws FriendException { facebookFriendConnection.sendChatStateUpdate(friendId, chatState); } } }