aboutsummaryrefslogtreecommitdiff
path: root/src/org/jivesoftware/smackx/ChatStateManager.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/org/jivesoftware/smackx/ChatStateManager.java')
-rw-r--r--src/org/jivesoftware/smackx/ChatStateManager.java200
1 files changed, 200 insertions, 0 deletions
diff --git a/src/org/jivesoftware/smackx/ChatStateManager.java b/src/org/jivesoftware/smackx/ChatStateManager.java
new file mode 100644
index 0000000..d452a9f
--- /dev/null
+++ b/src/org/jivesoftware/smackx/ChatStateManager.java
@@ -0,0 +1,200 @@
+/**
+ * $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);
+ }
+ }
+}