1
0
Fork 0
mirror of https://github.com/vanitasvitae/Smack.git synced 2024-11-26 14:02:06 +01:00

Improve ChatMarkersManager

* Created filters to be used with incoming and outgoing message stanzas
* Added a list of ChatMarkersListener and its add and remove methods.
* Added a stanza listener for outgoing messages.
* Added a stanza listener for incoming messages.
* Added discover feature for XEP-0333.
* Added methods to inform a message was: received, displayed or ack
* Added javadoc autor tag.
This commit is contained in:
Miguel Hincapie 2018-09-06 10:18:48 -05:00 committed by Florian Schmaus
parent 33d01bff34
commit 547138b325
8 changed files with 381 additions and 10 deletions

View file

@ -35,6 +35,7 @@ public final class MessageTypeFilter extends FlexibleStanzaTypeFilter<Message> {
public static final StanzaFilter HEADLINE = new MessageTypeFilter(Type.headline); public static final StanzaFilter HEADLINE = new MessageTypeFilter(Type.headline);
public static final StanzaFilter ERROR = new MessageTypeFilter(Type.error); public static final StanzaFilter ERROR = new MessageTypeFilter(Type.error);
public static final StanzaFilter NORMAL_OR_CHAT = new OrFilter(NORMAL, CHAT); public static final StanzaFilter NORMAL_OR_CHAT = new OrFilter(NORMAL, CHAT);
public static final StanzaFilter NORMAL_OR_CHAT_OR_GROUPCHAT = new OrFilter(NORMAL_OR_CHAT, GROUPCHAT);
public static final StanzaFilter NORMAL_OR_CHAT_OR_HEADLINE = new OrFilter(NORMAL_OR_CHAT, public static final StanzaFilter NORMAL_OR_CHAT_OR_HEADLINE = new OrFilter(NORMAL_OR_CHAT,
HEADLINE); HEADLINE);

View file

@ -0,0 +1,38 @@
/**
*
* Copyright 2018 Miguel Hincapie.
*
* 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.chat_markers;
import org.jivesoftware.smack.chat2.Chat;
import org.jivesoftware.smack.packet.Message;
/**
* Chat Markers Manager class (XEP-0333).
*
* @author Miguel Hincapie
* @see <a href="http://xmpp.org/extensions/xep-0333.html">XEP-0333: Chat
* Markers</a>
*/
public interface ChatMarkersListener {
/**
* Called in ChatMarkersManager when a new message with a markable tag arrives.
*
* @param chatMarkersState the current state of the message.
* @param message the new incoming message with a markable XML tag.
* @param chat associated to the message. This element can be <tt>NULL</tt>.
*/
void newChatMarkerMessage(ChatMarkersState chatMarkersState, Message message, Chat chat);
}

View file

@ -17,24 +17,45 @@
package org.jivesoftware.smackx.chat_markers; package org.jivesoftware.smackx.chat_markers;
import java.util.Map; import java.util.Map;
import java.util.Set;
import java.util.WeakHashMap; import java.util.WeakHashMap;
import java.util.concurrent.CopyOnWriteArraySet;
import org.jivesoftware.smack.AsyncButOrdered;
import org.jivesoftware.smack.ConnectionCreationListener; import org.jivesoftware.smack.ConnectionCreationListener;
import org.jivesoftware.smack.Manager; import org.jivesoftware.smack.Manager;
import org.jivesoftware.smack.SmackException;
import org.jivesoftware.smack.SmackException.NoResponseException; import org.jivesoftware.smack.SmackException.NoResponseException;
import org.jivesoftware.smack.SmackException.NotConnectedException; import org.jivesoftware.smack.SmackException.NotConnectedException;
import org.jivesoftware.smack.StanzaListener;
import org.jivesoftware.smack.XMPPConnection; import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPConnectionRegistry; import org.jivesoftware.smack.XMPPConnectionRegistry;
import org.jivesoftware.smack.XMPPException.XMPPErrorException; import org.jivesoftware.smack.XMPPException.XMPPErrorException;
import org.jivesoftware.smack.chat2.Chat;
import org.jivesoftware.smack.chat2.ChatManager;
import org.jivesoftware.smack.filter.AndFilter;
import org.jivesoftware.smack.filter.MessageTypeFilter;
import org.jivesoftware.smack.filter.MessageWithBodiesFilter;
import org.jivesoftware.smack.filter.NotFilter;
import org.jivesoftware.smack.filter.PossibleFromTypeFilter;
import org.jivesoftware.smack.filter.StanzaExtensionFilter;
import org.jivesoftware.smack.filter.StanzaFilter;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.Stanza;
import org.jivesoftware.smackx.chat_markers.element.ChatMarkersElements; import org.jivesoftware.smackx.chat_markers.element.ChatMarkersElements;
import org.jivesoftware.smackx.chat_markers.filter.ChatMarkersFilter;
import org.jivesoftware.smackx.chat_markers.filter.EligibleForChatMarker;
import org.jivesoftware.smackx.disco.ServiceDiscoveryManager; import org.jivesoftware.smackx.disco.ServiceDiscoveryManager;
import org.jxmpp.jid.EntityBareJid;
import org.jxmpp.jid.EntityFullJid;
/** /**
* Chat Markers Manager class (XEP-0333). * Chat Markers Manager class (XEP-0333).
* *
* @see <a href="http://xmpp.org/extensions/xep-0333.html">XEP-0333: Chat * @see <a href="http://xmpp.org/extensions/xep-0333.html">XEP-0333: Chat
* Markers</a> * Markers</a>
* @author Miguel Hincapie
* @author Fernando Ramirez * @author Fernando Ramirez
* *
*/ */
@ -51,10 +72,30 @@ public final class ChatMarkersManager extends Manager {
private static final Map<XMPPConnection, ChatMarkersManager> INSTANCES = new WeakHashMap<>(); private static final Map<XMPPConnection, ChatMarkersManager> INSTANCES = new WeakHashMap<>();
// @FORMATTER:OFF
private static final StanzaFilter INCOMING_MESSAGE_FILTER = new AndFilter(
MessageTypeFilter.NORMAL_OR_CHAT_OR_GROUPCHAT,
new StanzaExtensionFilter(ChatMarkersElements.NAMESPACE),
PossibleFromTypeFilter.ENTITY_BARE_JID,
EligibleForChatMarker.INSTANCE
);
private static final StanzaFilter OUTGOING_MESSAGE_FILTER = new AndFilter(
MessageTypeFilter.NORMAL_OR_CHAT_OR_GROUPCHAT,
MessageWithBodiesFilter.INSTANCE,
new NotFilter(ChatMarkersFilter.INSTANCE),
EligibleForChatMarker.INSTANCE
);
// @FORMATTER:ON
private final Set<ChatMarkersListener> incomingListeners = new CopyOnWriteArraySet<>();
private final AsyncButOrdered<Chat> asyncButOrdered = new AsyncButOrdered<>();
/** /**
* Get the singleton instance of ChatMarkersManager. * Get the singleton instance of ChatMarkersManager.
* *
* @param connection * @param connection the connection used to get the ChatMarkersManager instance.
* @return the instance of ChatMarkersManager * @return the instance of ChatMarkersManager
*/ */
public static synchronized ChatMarkersManager getInstanceFor(XMPPConnection connection) { public static synchronized ChatMarkersManager getInstanceFor(XMPPConnection connection) {
@ -70,16 +111,66 @@ public final class ChatMarkersManager extends Manager {
private ChatMarkersManager(XMPPConnection connection) { private ChatMarkersManager(XMPPConnection connection) {
super(connection); super(connection);
connection.addStanzaInterceptor(new StanzaListener() {
@Override
public void processStanza(Stanza packet)
throws
NotConnectedException,
InterruptedException,
SmackException.NotLoggedInException {
Message message = (Message) packet;
// add a markable extension
message.addExtension(new ChatMarkersElements.MarkableExtension());
}
}, OUTGOING_MESSAGE_FILTER);
connection.addSyncStanzaListener(new StanzaListener() {
@Override
public void processStanza(Stanza packet)
throws
NotConnectedException,
InterruptedException,
SmackException.NotLoggedInException {
final Message message = (Message) packet;
EntityFullJid fullFrom = message.getFrom().asEntityFullJidIfPossible();
EntityBareJid bareFrom = fullFrom.asEntityBareJid();
final Chat chat = ChatManager.getInstanceFor(connection()).chatWith(bareFrom);
asyncButOrdered.performAsyncButOrdered(chat, new Runnable() {
@Override
public void run() {
for (ChatMarkersListener listener : incomingListeners) {
if (ChatMarkersElements.MarkableExtension.from(message) != null) {
listener.newChatMarkerMessage(ChatMarkersState.markable, message, chat);
}
else if (ChatMarkersElements.ReceivedExtension.from(message) != null) {
listener.newChatMarkerMessage(ChatMarkersState.received, message, chat);
}
else if (ChatMarkersElements.DisplayedExtension.from(message) != null) {
listener.newChatMarkerMessage(ChatMarkersState.displayed, message, chat);
}
else if (ChatMarkersElements.AcknowledgedExtension.from(message) != null) {
listener.newChatMarkerMessage(ChatMarkersState.acknowledged, message, chat);
}
}
}
});
}
}, INCOMING_MESSAGE_FILTER);
ServiceDiscoveryManager.getInstanceFor(connection).addFeature(ChatMarkersElements.NAMESPACE);
} }
/** /**
* Returns true if Chat Markers is supported by the server. * Returns true if Chat Markers is supported by the server.
* *
* @return true if Chat Markers is supported by the server. * @return true if Chat Markers is supported by the server.
* @throws NotConnectedException * @throws NotConnectedException if the connection is not connected.
* @throws XMPPErrorException * @throws XMPPErrorException in case an error response was received.
* @throws NoResponseException * @throws NoResponseException if no response was received.
* @throws InterruptedException * @throws InterruptedException if the connection is interrupted.
*/ */
public boolean isSupportedByServer() public boolean isSupportedByServer()
throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
@ -87,4 +178,64 @@ public final class ChatMarkersManager extends Manager {
.serverSupportsFeature(ChatMarkersElements.NAMESPACE); .serverSupportsFeature(ChatMarkersElements.NAMESPACE);
} }
/**
* Register a ChatMarkersListener. That listener will be informed about new
* incoming markable messages.
*
* @param listener ChatMarkersListener
* @return true, if the listener was not registered before
*/
public boolean addIncomingChatMarkerMessageListener(ChatMarkersListener listener) {
return incomingListeners.add(listener);
}
/**
* Unregister a ChatMarkersListener.
*
* @param listener ChatMarkersListener
* @return true, if the listener was registered before
*/
public boolean removeIncomingChatMarkerMessageListener(ChatMarkersListener listener) {
return incomingListeners.remove(listener);
}
public void markMessageAsReceived(Message message)
throws
NotConnectedException,
InterruptedException,
IllegalArgumentException {
if (message == null) {
throw new IllegalArgumentException("To and From needed");
}
message.addExtension(new ChatMarkersElements.ReceivedExtension(message.getStanzaId()));
sendChatMarkerMessage(message);
}
public void markMessageAsDisplayed(Message message)
throws
NotConnectedException,
InterruptedException,
IllegalArgumentException {
if (message == null) {
throw new IllegalArgumentException("To and From needed");
}
message.addExtension(new ChatMarkersElements.DisplayedExtension(message.getStanzaId()));
sendChatMarkerMessage(message);
}
public void markMessageAsAcknowledged(Message message)
throws
NotConnectedException,
InterruptedException,
IllegalArgumentException {
if (message == null) {
throw new IllegalArgumentException("To and From needed");
}
message.addExtension(new ChatMarkersElements.AcknowledgedExtension(message.getStanzaId()));
sendChatMarkerMessage(message);
}
private void sendChatMarkerMessage(Message message) throws NotConnectedException, InterruptedException {
connection().sendStanza(message);
}
} }

View file

@ -0,0 +1,45 @@
/**
*
* Copyright © 2018 Miguel Hincapie
*
* 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.chat_markers;
/**
* Chat Markers elements (XEP-0333).
*
* @author Miguel Hincapie
* @see <a href="http://xmpp.org/extensions/xep-0333.html">XEP-0333: Chat
* Markers</a>
*/
public enum ChatMarkersState {
/**
* Indicates that a message can be marked with a Chat Marker and is therefore
* a "markable message".
*/
markable,
/**
* The message has been received by a client.
*/
received,
/**
* The message has been displayed to a user in a active chat and not in a system notification.
*/
displayed,
/**
* The message has been acknowledged by some user interaction e.g. pressing an
* acknowledgement button.
*/
acknowledged
}

View file

@ -19,6 +19,7 @@ package org.jivesoftware.smackx.chat_markers.element;
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.util.XmlStringBuilder; import org.jivesoftware.smack.util.XmlStringBuilder;
import org.jivesoftware.smackx.chat_markers.ChatMarkersState;
/** /**
* Chat Markers elements (XEP-0333). * Chat Markers elements (XEP-0333).
@ -45,7 +46,7 @@ public class ChatMarkersElements {
/** /**
* markable element. * markable element.
*/ */
public static final String ELEMENT = "markable"; public static final String ELEMENT = ChatMarkersState.markable.toString();
public MarkableExtension() { public MarkableExtension() {
} }
@ -85,7 +86,7 @@ public class ChatMarkersElements {
/** /**
* received element. * received element.
*/ */
public static final String ELEMENT = "received"; public static final String ELEMENT = ChatMarkersState.received.toString();
private final String id; private final String id;
@ -138,7 +139,7 @@ public class ChatMarkersElements {
/** /**
* displayed element. * displayed element.
*/ */
public static final String ELEMENT = "displayed"; public static final String ELEMENT = ChatMarkersState.displayed.toString();
private final String id; private final String id;
@ -191,7 +192,7 @@ public class ChatMarkersElements {
/** /**
* acknowledged element. * acknowledged element.
*/ */
public static final String ELEMENT = "acknowledged"; public static final String ELEMENT = ChatMarkersState.acknowledged.toString();
private final String id; private final String id;

View file

@ -0,0 +1,37 @@
/**
*
* Copyright 2018 Miguel Hincapie
*
* 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.chat_markers.filter;
import org.jivesoftware.smack.filter.StanzaExtensionFilter;
import org.jivesoftware.smack.filter.StanzaFilter;
import org.jivesoftware.smackx.chat_markers.element.ChatMarkersElements;
/**
* Chat Markers Manager class (XEP-0333).
*
* @author HINCM008 6/08/2018.
* @see <a href="http://xmpp.org/extensions/xep-0333.html">XEP-0333: Chat
* Markers</a>
*/
public final class ChatMarkersFilter extends StanzaExtensionFilter {
public static final StanzaFilter INSTANCE = new ChatMarkersFilter(ChatMarkersElements.NAMESPACE);
private ChatMarkersFilter(String namespace) {
super(namespace);
}
}

View file

@ -0,0 +1,74 @@
/**
*
* Copyright 2018 Miguel Hincapie.
*
* 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.chat_markers.filter;
import org.jivesoftware.smack.filter.StanzaExtensionFilter;
import org.jivesoftware.smack.packet.ExtensionElement;
import org.jivesoftware.smack.packet.Stanza;
import org.jivesoftware.smack.util.StringUtils;
import org.jivesoftware.smackx.chatstates.ChatState;
import org.jivesoftware.smackx.chatstates.ChatStateManager;
/**
* Chat Markers Manager class (XEP-0333).
*
* @author Miguel Hincapie
* @see <a href="http://xmpp.org/extensions/xep-0333.html">XEP-0333: Chat
* Markers</a>
*/
public final class EligibleForChatMarker extends StanzaExtensionFilter {
public static final EligibleForChatMarker INSTANCE = new EligibleForChatMarker(ChatStateManager.NAMESPACE);
private EligibleForChatMarker(String namespace) {
super(namespace);
}
/**
* From XEP-0333, Protocol Format: The Chat Marker MUST have an 'id' which is the 'id' of the
* message being marked.<br>
* In order to make Chat Markers works together with XEP-0085 as it said in
* 8.5 Interaction with Chat States, only messages with <tt>active</tt> chat
* state are accepted.
*
* @param message to be analyzed.
* @return true if the message contains a stanza Id.
* @see <a href="http://xmpp.org/extensions/xep-0333.html">XEP-0333: Chat Markers</a>
*/
@Override
public boolean accept(Stanza message) {
if (StringUtils.isNullOrEmpty(message.getStanzaId())) {
return false;
}
if (super.accept(message)) {
ExtensionElement extension = message.getExtension(ChatStateManager.NAMESPACE);
String chatStateElementName = extension.getElementName();
ChatState state;
try {
state = ChatState.valueOf(chatStateElementName);
return (state == ChatState.active);
}
catch (Exception ex) {
return false;
}
}
return true;
}
}

View file

@ -0,0 +1,24 @@
/**
*
* Copyright 2018 Miguel Hincapie
*
* 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.
*/
/**
* Chat Markers elements (XEP-0333).
*
* @see <a href="http://xmpp.org/extensions/xep-0333.html">XEP-0333: Chat
* Markers</a>
*
*/
package org.jivesoftware.smackx.chat_markers.filter;