mirror of
https://codeberg.org/Mercury-IM/Smack
synced 2024-11-22 14:22:05 +01:00
Migrate the ChatState package to chat2 API
Fixes SMACK-761
This commit is contained in:
parent
f42d9137b5
commit
a3d430f334
5 changed files with 210 additions and 46 deletions
|
@ -1,6 +1,6 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2003-2007 Jive Software.
|
||||
* Copyright 2003-2007 Jive Software, 2018 Paul Schaub.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -17,15 +17,16 @@
|
|||
|
||||
package org.jivesoftware.smackx.chatstates;
|
||||
|
||||
import org.jivesoftware.smack.chat.ChatMessageListener;
|
||||
import org.jivesoftware.smack.chat2.Chat;
|
||||
import org.jivesoftware.smack.packet.Message;
|
||||
|
||||
/**
|
||||
* Events for when the state of a user in a chat changes.
|
||||
*
|
||||
* @author Alexander Wenckus
|
||||
* @author Paul Schaub
|
||||
*/
|
||||
public interface ChatStateListener extends ChatMessageListener {
|
||||
public interface ChatStateListener {
|
||||
|
||||
/**
|
||||
* Fired when the state of a chat with another user changes.
|
||||
|
@ -34,7 +35,5 @@ public interface ChatStateListener extends ChatMessageListener {
|
|||
* @param state the new state of the participant.
|
||||
* @param message the message carrying the chat state.
|
||||
*/
|
||||
// TODO Migrate to new chat2 API on Smack 4.3.
|
||||
@SuppressWarnings("deprecation")
|
||||
void stateChanged(org.jivesoftware.smack.chat.Chat chat, ChatState state, Message message);
|
||||
void stateChanged(Chat chat, ChatState state, Message message);
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2003-2007 Jive Software.
|
||||
* Copyright 2003-2007 Jive Software, 2018 Paul Schaub.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -17,24 +17,33 @@
|
|||
|
||||
package org.jivesoftware.smackx.chatstates;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.WeakHashMap;
|
||||
|
||||
import org.jivesoftware.smack.Manager;
|
||||
import org.jivesoftware.smack.MessageListener;
|
||||
import org.jivesoftware.smack.SmackException.NotConnectedException;
|
||||
import org.jivesoftware.smack.StanzaListener;
|
||||
import org.jivesoftware.smack.XMPPConnection;
|
||||
import org.jivesoftware.smack.chat.ChatManagerListener;
|
||||
import org.jivesoftware.smack.chat.ChatMessageListener;
|
||||
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.NotFilter;
|
||||
import org.jivesoftware.smack.filter.StanzaExtensionFilter;
|
||||
import org.jivesoftware.smack.filter.StanzaFilter;
|
||||
import org.jivesoftware.smack.packet.ExtensionElement;
|
||||
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.jxmpp.jid.EntityBareJid;
|
||||
import org.jxmpp.jid.EntityFullJid;
|
||||
|
||||
/**
|
||||
* Handles chat state for all chats on a particular XMPPConnection. This class manages both the
|
||||
* stanza(/packet) extensions and the disco response necessary for compliance with
|
||||
|
@ -45,17 +54,39 @@ import org.jivesoftware.smackx.disco.ServiceDiscoveryManager;
|
|||
* If this does not occur you will not receive the update notifications.
|
||||
*
|
||||
* @author Alexander Wenckus
|
||||
* @author Paul Schaub
|
||||
* @see org.jivesoftware.smackx.chatstates.ChatState
|
||||
* @see org.jivesoftware.smackx.chatstates.packet.ChatStateExtension
|
||||
*/
|
||||
// TODO Migrate to new chat2 API on Smack 4.3.
|
||||
@SuppressWarnings("deprecation")
|
||||
public final class ChatStateManager extends Manager {
|
||||
|
||||
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);
|
||||
|
||||
/**
|
||||
* Message listener, that appends a ChatStateExtension to outgoing messages
|
||||
*/
|
||||
private final OutgoingMessageInterceptor outgoingInterceptor = new OutgoingMessageInterceptor();
|
||||
|
||||
/**
|
||||
* Message listener, that triggers registered ChatStateListeners for incoming chat messages with ChatStateExtensions.
|
||||
*/
|
||||
private final IncomingMessageInterceptor incomingInterceptor = new IncomingMessageInterceptor();
|
||||
|
||||
/**
|
||||
* Registered ChatStateListeners
|
||||
*/
|
||||
private final Set<ChatStateListener> chatStateListeners = new HashSet<>();
|
||||
|
||||
/**
|
||||
* Maps chat to last chat state.
|
||||
*/
|
||||
private final Map<Chat, ChatState> chatStates = new WeakHashMap<>();
|
||||
|
||||
/**
|
||||
* Returns the ChatStateManager related to the XMPPConnection and it will create one if it does
|
||||
|
@ -72,27 +103,43 @@ public final class ChatStateManager extends Manager {
|
|||
return manager;
|
||||
}
|
||||
|
||||
private final OutgoingMessageInterceptor outgoingInterceptor = new OutgoingMessageInterceptor();
|
||||
|
||||
private final IncomingMessageInterceptor incomingInterceptor = new IncomingMessageInterceptor();
|
||||
|
||||
/**
|
||||
* Maps chat to last chat state.
|
||||
* Private constructor to create a new ChatStateManager.
|
||||
* This adds ChatMessageListeners as interceptors to the connection and adds the namespace to the disco features.
|
||||
*
|
||||
* @param connection xmpp connection
|
||||
*/
|
||||
private final Map<org.jivesoftware.smack.chat.Chat, ChatState> chatStates = new WeakHashMap<>();
|
||||
|
||||
private final org.jivesoftware.smack.chat.ChatManager chatManager;
|
||||
|
||||
private ChatStateManager(XMPPConnection connection) {
|
||||
super(connection);
|
||||
chatManager = org.jivesoftware.smack.chat.ChatManager.getInstanceFor(connection);
|
||||
chatManager.addOutgoingMessageInterceptor(outgoingInterceptor, filter);
|
||||
chatManager.addChatListener(incomingInterceptor);
|
||||
ChatManager chatManager = ChatManager.getInstanceFor(connection);
|
||||
chatManager.addOutgoingListener(outgoingInterceptor);
|
||||
connection.addAsyncStanzaListener(new IncomingMessageInterceptor(),
|
||||
new AndFilter(INCOMING_MESSAGE_FILTER, new StanzaExtensionFilter(NAMESPACE)));
|
||||
|
||||
ServiceDiscoveryManager.getInstanceFor(connection).addFeature(NAMESPACE);
|
||||
INSTANCES.put(connection, this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a ChatStateListener. That listener will be informed about changed chat states.
|
||||
*
|
||||
* @param listener chatStateListener
|
||||
* @return true, if the listener was not registered before
|
||||
*/
|
||||
public boolean addChatStateListener(ChatStateListener listener) {
|
||||
return chatStateListeners.add(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregister a ChatStateListener.
|
||||
*
|
||||
* @param listener chatStateListener
|
||||
* @return true, if the listener was registered before
|
||||
*/
|
||||
public boolean removeChatStateListener(ChatStateListener listener) {
|
||||
return chatStateListeners.remove(listener);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets the current state of the provided chat. This method will send an empty bodied Message
|
||||
|
@ -101,10 +148,10 @@ public final class ChatStateManager extends Manager {
|
|||
*
|
||||
* @param newState the new state of the chat
|
||||
* @param chat the chat.
|
||||
* @throws NotConnectedException
|
||||
* @throws InterruptedException
|
||||
* @throws NotConnectedException
|
||||
* @throws InterruptedException
|
||||
*/
|
||||
public void setCurrentState(ChatState newState, org.jivesoftware.smack.chat.Chat chat) throws NotConnectedException, InterruptedException {
|
||||
public void setCurrentState(ChatState newState, Chat chat) throws NotConnectedException, InterruptedException {
|
||||
if (chat == null || newState == null) {
|
||||
throw new IllegalArgumentException("Arguments cannot be null.");
|
||||
}
|
||||
|
@ -115,7 +162,7 @@ public final class ChatStateManager extends Manager {
|
|||
ChatStateExtension extension = new ChatStateExtension(newState);
|
||||
message.addExtension(extension);
|
||||
|
||||
chat.sendMessage(message);
|
||||
chat.send(message);
|
||||
}
|
||||
|
||||
|
||||
|
@ -135,7 +182,7 @@ public final class ChatStateManager extends Manager {
|
|||
return connection().hashCode();
|
||||
}
|
||||
|
||||
private synchronized boolean updateChatState(org.jivesoftware.smack.chat.Chat chat, ChatState newState) {
|
||||
private synchronized boolean updateChatState(Chat chat, ChatState newState) {
|
||||
ChatState lastChatState = chatStates.get(chat);
|
||||
if (lastChatState != newState) {
|
||||
chatStates.put(chat, newState);
|
||||
|
@ -144,38 +191,44 @@ public final class ChatStateManager extends Manager {
|
|||
return false;
|
||||
}
|
||||
|
||||
private static void fireNewChatState(org.jivesoftware.smack.chat.Chat chat, ChatState state, Message message) {
|
||||
for (ChatMessageListener listener : chat.getListeners()) {
|
||||
if (listener instanceof ChatStateListener) {
|
||||
((ChatStateListener) listener).stateChanged(chat, state, message);
|
||||
}
|
||||
private void fireNewChatState(Chat chat, ChatState state, Message message) {
|
||||
for (ChatStateListener listener : chatStateListeners) {
|
||||
listener.stateChanged(chat, state, message);
|
||||
}
|
||||
}
|
||||
|
||||
private class OutgoingMessageInterceptor implements MessageListener {
|
||||
private class OutgoingMessageInterceptor implements OutgoingChatMessageListener {
|
||||
|
||||
@Override
|
||||
public void processMessage(Message message) {
|
||||
org.jivesoftware.smack.chat.Chat chat = chatManager.getThreadChat(message.getThread());
|
||||
public void newOutgoingMessage(EntityBareJid to, Message message, Chat chat) {
|
||||
if (chat == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// if message already has a chatStateExtension, then do nothing,
|
||||
if (!filter.accept(message)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// otherwise add a chatState extension if necessary.
|
||||
if (updateChatState(chat, ChatState.active)) {
|
||||
message.addExtension(new ChatStateExtension(ChatState.active));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class IncomingMessageInterceptor implements ChatManagerListener, ChatMessageListener {
|
||||
private class IncomingMessageInterceptor implements StanzaListener {
|
||||
|
||||
@Override
|
||||
public void chatCreated(final org.jivesoftware.smack.chat.Chat chat, boolean createdLocally) {
|
||||
chat.addMessageListener(this);
|
||||
}
|
||||
public void processStanza(Stanza packet) {
|
||||
Message message = (Message) packet;
|
||||
|
||||
@Override
|
||||
public void processMessage(org.jivesoftware.smack.chat.Chat chat, Message message) {
|
||||
EntityFullJid fullFrom = message.getFrom().asEntityFullJidIfPossible();
|
||||
EntityBareJid bareFrom = fullFrom.asEntityBareJid();
|
||||
|
||||
Chat chat = ChatManager.getInstanceFor(connection()).chatWith(bareFrom);
|
||||
ExtensionElement extension = message.getExtension(NAMESPACE);
|
||||
|
||||
if (extension == null) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2015 Florian Schmaus
|
||||
* Copyright 2015 - 2018 Florian Schmaus, Paul Schaub
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -16,6 +16,6 @@
|
|||
*/
|
||||
|
||||
/**
|
||||
* Smacks implementation of XEP-0085: Chat State Notifications.
|
||||
* Classes for Chat States (<a href="http://www.xmpp.org/extensions/xep-0085.html">XEP-0085</a>).
|
||||
*/
|
||||
package org.jivesoftware.smackx.chatstates;
|
||||
|
|
|
@ -0,0 +1,91 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2018 Paul Schaub
|
||||
*
|
||||
* 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.chatstate;
|
||||
|
||||
import org.jivesoftware.smack.chat2.Chat;
|
||||
import org.jivesoftware.smack.chat2.ChatManager;
|
||||
import org.jivesoftware.smack.packet.Message;
|
||||
import org.jivesoftware.smackx.chatstates.ChatState;
|
||||
import org.jivesoftware.smackx.chatstates.ChatStateListener;
|
||||
import org.jivesoftware.smackx.chatstates.ChatStateManager;
|
||||
|
||||
import org.igniterealtime.smack.inttest.AbstractSmackIntegrationTest;
|
||||
import org.igniterealtime.smack.inttest.SmackIntegrationTest;
|
||||
import org.igniterealtime.smack.inttest.SmackIntegrationTestEnvironment;
|
||||
import org.igniterealtime.smack.inttest.util.SimpleResultSyncPoint;
|
||||
import org.junit.After;
|
||||
|
||||
public class ChatStateIntegrationTest extends AbstractSmackIntegrationTest {
|
||||
|
||||
// Listener for composing chat state
|
||||
private final SimpleResultSyncPoint composingSyncPoint = new SimpleResultSyncPoint();
|
||||
private final ChatStateListener composingListener = new ChatStateListener() {
|
||||
@Override
|
||||
public void stateChanged(Chat chat, ChatState state, Message message) {
|
||||
if (state.equals(ChatState.composing)) {
|
||||
composingSyncPoint.signal();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Listener for active chat state
|
||||
private final SimpleResultSyncPoint activeSyncPoint = new SimpleResultSyncPoint();
|
||||
private final ChatStateListener activeListener = new ChatStateListener() {
|
||||
@Override
|
||||
public void stateChanged(Chat chat, ChatState state, Message message) {
|
||||
if (state.equals(ChatState.active)) {
|
||||
activeSyncPoint.signal();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
public ChatStateIntegrationTest(SmackIntegrationTestEnvironment environment) {
|
||||
super(environment);
|
||||
}
|
||||
|
||||
@SmackIntegrationTest
|
||||
public void testChatStateListeners() throws Exception {
|
||||
ChatStateManager manOne = ChatStateManager.getInstance(conOne);
|
||||
ChatStateManager manTwo = ChatStateManager.getInstance(conTwo);
|
||||
|
||||
// Add chatState listeners.
|
||||
manTwo.addChatStateListener(composingListener);
|
||||
manTwo.addChatStateListener(activeListener);
|
||||
|
||||
Chat chatOne = ChatManager.getInstanceFor(conOne)
|
||||
.chatWith(conTwo.getUser().asEntityBareJid());
|
||||
|
||||
// Test, if setCurrentState works and the chatState arrives
|
||||
manOne.setCurrentState(ChatState.composing, chatOne);
|
||||
composingSyncPoint.waitForResult(timeout);
|
||||
|
||||
// Test, if the OutgoingMessageInterceptor successfully adds a chatStateExtension of "active" to
|
||||
// an outgoing chat message and if it arrives at the other side.
|
||||
Chat chat = ChatManager.getInstanceFor(conOne)
|
||||
.chatWith(conTwo.getUser().asEntityBareJid());
|
||||
chat.send("Hi!");
|
||||
activeSyncPoint.waitForResult(timeout);
|
||||
}
|
||||
|
||||
@After
|
||||
public void cleanup() {
|
||||
ChatStateManager manTwo = ChatStateManager.getInstance(conTwo);
|
||||
manTwo.removeChatStateListener(composingListener);
|
||||
manTwo.removeChatStateListener(activeListener);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2018 Paul Schaub
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* TODO describe me.
|
||||
*/
|
||||
package org.jivesoftware.smackx.chatstate;
|
Loading…
Reference in a new issue