| /** |
| * $RCSfile$ |
| * $Revision: 2407 $ |
| * $Date: 2004-11-02 15:37:00 -0800 (Tue, 02 Nov 2004) $ |
| * |
| * Copyright 2003-2007 Jive Software. |
| * |
| * All rights reserved. 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 org.jivesoftware.smackx; |
| |
| import org.jivesoftware.smack.*; |
| import org.jivesoftware.smack.util.collections.ReferenceMap; |
| import org.jivesoftware.smack.filter.PacketFilter; |
| import org.jivesoftware.smack.filter.NotFilter; |
| import org.jivesoftware.smack.filter.PacketExtensionFilter; |
| import org.jivesoftware.smack.packet.Message; |
| import org.jivesoftware.smack.packet.Packet; |
| import org.jivesoftware.smack.packet.PacketExtension; |
| import org.jivesoftware.smackx.packet.ChatStateExtension; |
| |
| import java.util.Map; |
| import java.util.WeakHashMap; |
| |
| /** |
| * Handles chat state for all chats on a particular Connection. This class manages both the |
| * packet extensions and the disco response neccesary for compliance with |
| * <a href="http://www.xmpp.org/extensions/xep-0085.html">XEP-0085</a>. |
| * |
| * NOTE: {@link org.jivesoftware.smackx.ChatStateManager#getInstance(org.jivesoftware.smack.Connection)} |
| * needs to be called in order for the listeners to be registered appropriately with the connection. |
| * If this does not occur you will not receive the update notifications. |
| * |
| * @author Alexander Wenckus |
| * @see org.jivesoftware.smackx.ChatState |
| * @see org.jivesoftware.smackx.packet.ChatStateExtension |
| */ |
| public class ChatStateManager { |
| |
| private static final Map<Connection, ChatStateManager> managers = |
| new WeakHashMap<Connection, ChatStateManager>(); |
| |
| private static final PacketFilter filter = new NotFilter( |
| new PacketExtensionFilter("http://jabber.org/protocol/chatstates")); |
| |
| /** |
| * Returns the ChatStateManager related to the Connection and it will create one if it does |
| * not yet exist. |
| * |
| * @param connection the connection to return the ChatStateManager |
| * @return the ChatStateManager related the the connection. |
| */ |
| public static ChatStateManager getInstance(final Connection connection) { |
| if(connection == null) { |
| return null; |
| } |
| synchronized (managers) { |
| ChatStateManager manager = managers.get(connection); |
| if (manager == null) { |
| manager = new ChatStateManager(connection); |
| manager.init(); |
| managers.put(connection, manager); |
| } |
| |
| return manager; |
| } |
| } |
| |
| private final Connection connection; |
| |
| private final OutgoingMessageInterceptor outgoingInterceptor = new OutgoingMessageInterceptor(); |
| |
| private final IncomingMessageInterceptor incomingInterceptor = new IncomingMessageInterceptor(); |
| |
| /** |
| * Maps chat to last chat state. |
| */ |
| private final Map<Chat, ChatState> chatStates = |
| new ReferenceMap<Chat, ChatState>(ReferenceMap.WEAK, ReferenceMap.HARD); |
| |
| private ChatStateManager(Connection connection) { |
| this.connection = connection; |
| } |
| |
| private void init() { |
| connection.getChatManager().addOutgoingMessageInterceptor(outgoingInterceptor, |
| filter); |
| connection.getChatManager().addChatListener(incomingInterceptor); |
| |
| ServiceDiscoveryManager.getInstanceFor(connection) |
| .addFeature("http://jabber.org/protocol/chatstates"); |
| } |
| |
| /** |
| * Sets the current state of the provided chat. This method will send an empty bodied Message |
| * packet with the state attached as a {@link org.jivesoftware.smack.packet.PacketExtension}, if |
| * and only if the new chat state is different than the last state. |
| * |
| * @param newState the new state of the chat |
| * @param chat the chat. |
| * @throws org.jivesoftware.smack.XMPPException |
| * when there is an error sending the message |
| * packet. |
| */ |
| public void setCurrentState(ChatState newState, Chat chat) throws XMPPException { |
| if(chat == null || newState == null) { |
| throw new IllegalArgumentException("Arguments cannot be null."); |
| } |
| if(!updateChatState(chat, newState)) { |
| return; |
| } |
| Message message = new Message(); |
| ChatStateExtension extension = new ChatStateExtension(newState); |
| message.addExtension(extension); |
| |
| chat.sendMessage(message); |
| } |
| |
| |
| public boolean equals(Object o) { |
| if (this == o) return true; |
| if (o == null || getClass() != o.getClass()) return false; |
| |
| ChatStateManager that = (ChatStateManager) o; |
| |
| return connection.equals(that.connection); |
| |
| } |
| |
| public int hashCode() { |
| return connection.hashCode(); |
| } |
| |
| private boolean updateChatState(Chat chat, ChatState newState) { |
| ChatState lastChatState = chatStates.get(chat); |
| if (lastChatState != newState) { |
| chatStates.put(chat, newState); |
| return true; |
| } |
| return false; |
| } |
| |
| private void fireNewChatState(Chat chat, ChatState state) { |
| for (MessageListener listener : chat.getListeners()) { |
| if (listener instanceof ChatStateListener) { |
| ((ChatStateListener) listener).stateChanged(chat, state); |
| } |
| } |
| } |
| |
| private class OutgoingMessageInterceptor implements PacketInterceptor { |
| |
| public void interceptPacket(Packet packet) { |
| Message message = (Message) packet; |
| Chat chat = connection.getChatManager().getThreadChat(message.getThread()); |
| if (chat == null) { |
| return; |
| } |
| if (updateChatState(chat, ChatState.active)) { |
| message.addExtension(new ChatStateExtension(ChatState.active)); |
| } |
| } |
| } |
| |
| private class IncomingMessageInterceptor implements ChatManagerListener, MessageListener { |
| |
| public void chatCreated(final Chat chat, boolean createdLocally) { |
| chat.addMessageListener(this); |
| } |
| |
| public void processMessage(Chat chat, Message message) { |
| PacketExtension extension |
| = message.getExtension("http://jabber.org/protocol/chatstates"); |
| if (extension == null) { |
| return; |
| } |
| |
| ChatState state; |
| try { |
| state = ChatState.valueOf(extension.getElementName()); |
| } |
| catch (Exception ex) { |
| return; |
| } |
| |
| fireNewChatState(chat, state); |
| } |
| } |
| } |