--- /home/cmeng/source/smack/Smack-4.4.0-alpha3-20190916/smack-extensions/src/main/java/org/jivesoftware/smackx/chatstates/ChatStateManager.java	2019-09-18 15:59:57.000000000 
+++ /home/cmeng/workspace/android/atalk-android/aTalk/src/main/java/org/jivesoftware/smackx/chatstates/ChatStateManager.java	2019-11-27 17:53:33.000000000 
@@ -30,26 +30,31 @@
 import org.jivesoftware.smack.Manager;
 import org.jivesoftware.smack.SmackException.NotConnectedException;
 import org.jivesoftware.smack.StanzaListener;
 import org.jivesoftware.smack.XMPPConnection;
 import org.jivesoftware.smack.chat2.Chat;
 import org.jivesoftware.smack.chat2.ChatManager;
-import org.jivesoftware.smack.chat2.OutgoingChatMessageListener;
 import org.jivesoftware.smack.filter.AndFilter;
-import org.jivesoftware.smack.filter.FromTypeFilter;
 import org.jivesoftware.smack.filter.MessageTypeFilter;
+import org.jivesoftware.smack.filter.MessageWithBodiesFilter;
 import org.jivesoftware.smack.filter.NotFilter;
+import org.jivesoftware.smack.filter.OrFilter;
 import org.jivesoftware.smack.filter.StanzaExtensionFilter;
 import org.jivesoftware.smack.filter.StanzaFilter;
+import org.jivesoftware.smack.filter.StanzaTypeFilter;
 import org.jivesoftware.smack.packet.ExtensionElement;
+import org.jivesoftware.smack.filter.ToTypeFilter;
 import org.jivesoftware.smack.packet.Message;
 import org.jivesoftware.smack.packet.Stanza;
 
 import org.jivesoftware.smackx.chatstates.packet.ChatStateExtension;
 import org.jivesoftware.smackx.disco.ServiceDiscoveryManager;
 
+import org.jivesoftware.smackx.muc.MultiUserChat;
+import org.jivesoftware.smackx.muc.MultiUserChatManager;
+import org.jivesoftware.smackx.xhtmlim.packet.XHTMLExtension;
 import org.jxmpp.jid.EntityBareJid;
 import org.jxmpp.jid.EntityFullJid;
 
 /**
  * Handles chat state for all chats on a particular XMPPConnection. This class manages both the
  * stanza extensions and the disco response necessary for compliance with
@@ -70,25 +75,34 @@
 
     public static final String NAMESPACE = "http://jabber.org/protocol/chatstates";
 
     private static final Map<XMPPConnection, ChatStateManager> INSTANCES = new WeakHashMap<>();
 
     private static final StanzaFilter filter = new NotFilter(new StanzaExtensionFilter(NAMESPACE));
-    private static final StanzaFilter INCOMING_MESSAGE_FILTER =
-            new AndFilter(MessageTypeFilter.NORMAL_OR_CHAT, FromTypeFilter.ENTITY_FULL_JID);
-    private static final StanzaFilter INCOMING_CHAT_STATE_FILTER = new AndFilter(INCOMING_MESSAGE_FILTER, new StanzaExtensionFilter(NAMESPACE));
+    private static final StanzaFilter INCOMING_CHAT_STATE_FILTER =
+            new AndFilter(StanzaTypeFilter.MESSAGE, new StanzaExtensionFilter(NAMESPACE)
+    );
+
+    private static final StanzaFilter MESSAGE_FILTER = new AndFilter(
+            new OrFilter(MessageTypeFilter.NORMAL_OR_CHAT, MessageTypeFilter.GROUPCHAT),
+            new OrFilter(MessageWithBodiesFilter.INSTANCE, new StanzaExtensionFilter(XHTMLExtension.ELEMENT, XHTMLExtension.NAMESPACE))
+    );
 
+    private static final StanzaFilter OUTGOING_MESSAGE_FILTER =
+            new AndFilter( MESSAGE_FILTER, ToTypeFilter.ENTITY_FULL_OR_BARE_JID
+    );
+
     /**
      * Registered ChatStateListeners
      */
     private final Set<ChatStateListener> chatStateListeners = new HashSet<>();
 
     /**
-     * Maps chat to last chat state.
+     * Maps chat/mucChat to last chat state.
      */
-    private final Map<Chat, ChatState> chatStates = new WeakHashMap<>();
+    private final Map<Object, ChatState> chatStates = new WeakHashMap<>();
 
     private final AsyncButOrdered<Chat> asyncButOrdered = new AsyncButOrdered<>();
 
     /**
      * Returns the ChatStateManager related to the XMPPConnection and it will create one if it does
      * not yet exist.
@@ -111,30 +125,38 @@
      *
      * @param connection xmpp connection
      */
     private ChatStateManager(XMPPConnection connection) {
         super(connection);
         ChatManager chatManager = ChatManager.getInstanceFor(connection);
-        chatManager.addOutgoingListener(new OutgoingChatMessageListener() {
+        MultiUserChatManager multiUserChatManager = MultiUserChatManager.getInstanceFor(connection);
+
+        connection.addStanzaInterceptor(new StanzaListener() {
             @Override
-            public void newOutgoingMessage(EntityBareJid to, Message message, Chat chat) {
-                if (chat == null) {
-                    return;
-                }
+            public void processStanza(Stanza stanza) throws NotConnectedException, InterruptedException {
+                Message message = (Message) stanza;
 
                 // if message already has a chatStateExtension, then do nothing,
                 if (!filter.accept(message)) {
                     return;
                 }
 
+                Object chat;
+                EntityBareJid entityBareJid = message.getTo().asEntityBareJidIfPossible();
+                if (message.getType() == Message.Type.groupchat) {
+                    chat = multiUserChatManager.getMultiUserChat(entityBareJid);
+                } else {
+                    chat = chatManager.chatWith(entityBareJid);
+                }
+
                 // otherwise add a chatState extension if necessary.
                 if (updateChatState(chat, ChatState.active)) {
                     message.addExtension(new ChatStateExtension(ChatState.active));
                 }
             }
-        });
+        }, OUTGOING_MESSAGE_FILTER);
 
         connection.addSyncStanzaListener(new StanzaListener() {
             @Override
             public void processStanza(Stanza stanza) {
                 final Message message = (Message) stanza;
 
@@ -197,13 +219,12 @@
     public boolean removeChatStateListener(ChatStateListener listener) {
         synchronized (chatStateListeners) {
             return chatStateListeners.remove(listener);
         }
     }
 
-
     /**
      * Sets the current state of the provided chat. This method will send an empty bodied Message
      * stanza with the state attached as a {@link org.jivesoftware.smack.packet.ExtensionElement}, if
      * and only if the new chat state is different than the last state.
      *
      * @param newState the new state of the chat
@@ -222,12 +243,35 @@
         ChatStateExtension extension = new ChatStateExtension(newState);
         message.addExtension(extension);
 
         chat.send(message);
     }
 
+    /**
+     * Sets the current state of the provided mucchat. This method will send an empty bodied Message
+     * stanza with the state attached as a {@link org.jivesoftware.smack.packet.ExtensionElement}, if
+     * and only if the new chat state is different than the last state.
+     *
+     * @param newState  the new state of the chat
+     * @param mucChat the MultiUserChat instance
+     * @throws NotConnectedException if the XMPP connection is not connected.
+     * @throws InterruptedException if the calling thread was interrupted.
+     */
+    public void setCurrentState(ChatState newState, MultiUserChat mucChat) throws NotConnectedException, InterruptedException {
+        if (mucChat == null || newState == null) {
+            throw new IllegalArgumentException("Arguments cannot be null.");
+        }
+        if (!updateChatState(mucChat, newState)) {
+            return;
+        }
+        Message message = new Message();
+        ChatStateExtension extension = new ChatStateExtension(newState);
+        message.addExtension(extension);
+
+        mucChat.sendMessage(message);
+    }
 
     @Override
     public boolean equals(Object o) {
         if (this == o) return true;
         if (o == null || getClass() != o.getClass()) return false;
 
@@ -239,16 +283,15 @@
 
     @Override
     public int hashCode() {
         return connection().hashCode();
     }
 
-    private synchronized boolean updateChatState(Chat chat, ChatState newState) {
+    private synchronized boolean updateChatState(Object chat, ChatState newState) {
         ChatState lastChatState = chatStates.get(chat);
         if (lastChatState != newState) {
             chatStates.put(chat, newState);
             return true;
         }
         return false;
     }
-
 }
