Add Chat State Notifications support for group chat

This commit is contained in:
Eng ChonMeng 2019-11-27 19:40:04 +01:00 committed by Paul Schaub
parent 8831961afe
commit 467a0821a0
1 changed files with 60 additions and 16 deletions

View File

@ -33,18 +33,23 @@ import org.jivesoftware.smack.StanzaListener;
import org.jivesoftware.smack.XMPPConnection; import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.chat2.Chat; import org.jivesoftware.smack.chat2.Chat;
import org.jivesoftware.smack.chat2.ChatManager; import org.jivesoftware.smack.chat2.ChatManager;
import org.jivesoftware.smack.chat2.OutgoingChatMessageListener;
import org.jivesoftware.smack.filter.AndFilter; import org.jivesoftware.smack.filter.AndFilter;
import org.jivesoftware.smack.filter.FromTypeFilter;
import org.jivesoftware.smack.filter.MessageTypeFilter; import org.jivesoftware.smack.filter.MessageTypeFilter;
import org.jivesoftware.smack.filter.MessageWithBodiesFilter;
import org.jivesoftware.smack.filter.NotFilter; import org.jivesoftware.smack.filter.NotFilter;
import org.jivesoftware.smack.filter.OrFilter;
import org.jivesoftware.smack.filter.StanzaExtensionFilter; import org.jivesoftware.smack.filter.StanzaExtensionFilter;
import org.jivesoftware.smack.filter.StanzaFilter; import org.jivesoftware.smack.filter.StanzaFilter;
import org.jivesoftware.smack.filter.StanzaTypeFilter;
import org.jivesoftware.smack.filter.ToTypeFilter;
import org.jivesoftware.smack.packet.ExtensionElement; import org.jivesoftware.smack.packet.ExtensionElement;
import org.jivesoftware.smack.packet.Message; import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.Stanza; import org.jivesoftware.smack.packet.Stanza;
import org.jivesoftware.smackx.chatstates.packet.ChatStateExtension; import org.jivesoftware.smackx.chatstates.packet.ChatStateExtension;
import org.jivesoftware.smackx.disco.ServiceDiscoveryManager; 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.EntityBareJid;
import org.jxmpp.jid.EntityFullJid; import org.jxmpp.jid.EntityFullJid;
@ -60,6 +65,7 @@ import org.jxmpp.jid.EntityFullJid;
* *
* @author Alexander Wenckus * @author Alexander Wenckus
* @author Paul Schaub * @author Paul Schaub
* @author Eng ChongMeng
* @see org.jivesoftware.smackx.chatstates.ChatState * @see org.jivesoftware.smackx.chatstates.ChatState
* @see org.jivesoftware.smackx.chatstates.packet.ChatStateExtension * @see org.jivesoftware.smackx.chatstates.packet.ChatStateExtension
*/ */
@ -72,9 +78,18 @@ public final class ChatStateManager extends Manager {
private static final Map<XMPPConnection, ChatStateManager> INSTANCES = new WeakHashMap<>(); private static final Map<XMPPConnection, ChatStateManager> INSTANCES = new WeakHashMap<>();
private static final StanzaFilter filter = new NotFilter(new StanzaExtensionFilter(NAMESPACE)); private static final StanzaFilter filter = new NotFilter(new StanzaExtensionFilter(NAMESPACE));
private static final StanzaFilter INCOMING_MESSAGE_FILTER = private static final StanzaFilter INCOMING_CHAT_STATE_FILTER =
new AndFilter(MessageTypeFilter.NORMAL_OR_CHAT, FromTypeFilter.ENTITY_FULL_JID); new AndFilter(StanzaTypeFilter.MESSAGE, new StanzaExtensionFilter(NAMESPACE)
private static final StanzaFilter INCOMING_CHAT_STATE_FILTER = new AndFilter(INCOMING_MESSAGE_FILTER, 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 * Registered ChatStateListeners
@ -82,9 +97,9 @@ public final class ChatStateManager extends Manager {
private final Set<ChatStateListener> chatStateListeners = new HashSet<>(); 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<>(); private final AsyncButOrdered<Chat> asyncButOrdered = new AsyncButOrdered<>();
@ -113,24 +128,32 @@ public final class ChatStateManager extends Manager {
private ChatStateManager(XMPPConnection connection) { private ChatStateManager(XMPPConnection connection) {
super(connection); super(connection);
ChatManager chatManager = ChatManager.getInstanceFor(connection); ChatManager chatManager = ChatManager.getInstanceFor(connection);
chatManager.addOutgoingListener(new OutgoingChatMessageListener() { MultiUserChatManager multiUserChatManager = MultiUserChatManager.getInstanceFor(connection);
connection.addStanzaInterceptor(new StanzaListener() {
@Override @Override
public void newOutgoingMessage(EntityBareJid to, Message message, Chat chat) { public void processStanza(Stanza stanza) throws NotConnectedException, InterruptedException {
if (chat == null) { Message message = (Message) stanza;
return;
}
// if message already has a chatStateExtension, then do nothing, // if message already has a chatStateExtension, then do nothing,
if (!filter.accept(message)) { if (!filter.accept(message)) {
return; 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. // otherwise add a chatState extension if necessary.
if (updateChatState(chat, ChatState.active)) { if (updateChatState(chat, ChatState.active)) {
message.addExtension(new ChatStateExtension(ChatState.active)); message.addExtension(new ChatStateExtension(ChatState.active));
} }
} }
}); }, OUTGOING_MESSAGE_FILTER);
connection.addSyncStanzaListener(new StanzaListener() { connection.addSyncStanzaListener(new StanzaListener() {
@Override @Override
@ -199,7 +222,6 @@ public final class ChatStateManager extends Manager {
} }
} }
/** /**
* Sets the current state of the provided chat. This method will send an empty bodied Message * 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 * stanza with the state attached as a {@link org.jivesoftware.smack.packet.ExtensionElement}, if
@ -224,6 +246,29 @@ public final class ChatStateManager extends Manager {
chat.send(message); 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 @Override
public boolean equals(Object o) { public boolean equals(Object o) {
@ -241,7 +286,7 @@ public final class ChatStateManager extends Manager {
return connection().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); ChatState lastChatState = chatStates.get(chat);
if (lastChatState != newState) { if (lastChatState != newState) {
chatStates.put(chat, newState); chatStates.put(chat, newState);
@ -249,5 +294,4 @@ public final class ChatStateManager extends Manager {
} }
return false; return false;
} }
} }