1
0
Fork 0
mirror of https://codeberg.org/Mercury-IM/Smack synced 2024-11-26 00:02:06 +01:00

Deprecate Chat API, introduce new Chat API

Also add (From|To)TypeFilter and update/fix the documentation in a few places.
This commit is contained in:
Florian Schmaus 2017-01-11 19:35:55 +01:00
parent b0fef6ffcb
commit d47463a533
22 changed files with 612 additions and 113 deletions

View file

@ -69,6 +69,7 @@ Smack Extensions and currently supported XEPs of smack-extensions
| XMPP Over BOSH | [XEP-0206](http://xmpp.org/extensions/xep-0206.html) | Use Bidirectional-streams Over Synchronous HTTP (BOSH) to transport XMPP stanzas. | | XMPP Over BOSH | [XEP-0206](http://xmpp.org/extensions/xep-0206.html) | Use Bidirectional-streams Over Synchronous HTTP (BOSH) to transport XMPP stanzas. |
| Attention | [XEP-0224](http://xmpp.org/extensions/xep-0224.html) | Getting attention of another user. | | Attention | [XEP-0224](http://xmpp.org/extensions/xep-0224.html) | Getting attention of another user. |
| Bits of Binary | [XEP-0231](http://xmpp.org/extensions/xep-0231.html) | Including or referring to small bits of binary data in an XML stanza. | | Bits of Binary | [XEP-0231](http://xmpp.org/extensions/xep-0231.html) | Including or referring to small bits of binary data in an XML stanza. |
| Best Practices for Resource Locking | [XEP-0296](https://xmpp.org/extensions/xep-0296.html) | Specifies best practices to be followed by Jabber/XMPP clients about when to lock into, and unlock away from, resources. |
| Last Message Correction | [XEP-0308](http://xmpp.org/extensions/xep-0308.html) | Provides a method for indicating that a message is a correction of the last sent message. | | Last Message Correction | [XEP-0308](http://xmpp.org/extensions/xep-0308.html) | Provides a method for indicating that a message is a correction of the last sent message. |
| [Group Chat Invitations](invitation.md) | n/a | Send invitations to other users to join a group chat room. | | [Group Chat Invitations](invitation.md) | n/a | Send invitations to other users to join a group chat room. |
| [Jive Properties](properties.md) | n/a | TODO | | [Jive Properties](properties.md) | n/a | TODO |

View file

@ -97,8 +97,8 @@ done. The last step is to send the message as you do with any other message.
An XHTML message is like any regular message, therefore to send the message An XHTML message is like any regular message, therefore to send the message
you can follow the usual steps you do in order to send a message. For example, you can follow the usual steps you do in order to send a message. For example,
to send a message as part of a chat just use the message **#send(Message)** of to send a message as part of a chat just use the message **#sendMessage(Message)** of
_**Chat**_ or you can use the message **#send(Stanza)** of _**Chat**_ or you can use the message **#sendStanza(Stanza)** of
_**XMPPConnection**_. _**XMPPConnection**_.
**Example** **Example**
@ -142,19 +142,20 @@ XHTML bodies of any received message.
``` ```
// Create a listener for the chat and display any XHTML content // Create a listener for the chat and display any XHTML content
PacketListener packetListener = new PacketListener() { IncomingChatMessageListener listener = new IncomingChatMessageListener() {
public void processStanza(Stanza stanza) { public void newIncomingMessage(EntityBareJid from, Message message, Chat chat) {
Message message = (Message) stanza;
// Obtain the XHTML bodies of the message // Obtain the XHTML bodies of the message
List<CharSequence> bodies = XHTMLManager.getBodies(message); List<CharSequence> bodies = XHTMLManager.getBodies(message);
if (bodies != null) { if (bodies == null) {
// Display the bodies on the console return;
for (CharSequence body : bodies) {
System.out.println(body); // Display the bodies on the console
} for (CharSequence body : bodies) {
System.out.println(body);
} }
}
}; };
chat.addMessageListener(packetListener); chatManager.addListener(listener);
``` ```
Discover support for XHTML Messages Discover support for XHTML Messages

View file

@ -70,7 +70,7 @@ created, such as the ability to disable or require encryption. See
Once you've created a connection, you should login with the Once you've created a connection, you should login with the
`XMPPConnection.login()` method. Once you've logged in, you can being `XMPPConnection.login()` method. Once you've logged in, you can being
chatting with other users by creating new `Chat` or `GroupChat` chatting with other users by creating new `Chat` or `MultiUserChat`
objects. objects.
Working with the Roster Working with the Roster
@ -98,18 +98,18 @@ your presence to let people know you're unavailable and "out fishing":
// Create a new presence. Pass in false to indicate we're unavailable._ // Create a new presence. Pass in false to indicate we're unavailable._
Presence presence = new Presence(Presence.Type.unavailable); Presence presence = new Presence(Presence.Type.unavailable);
presence.setStatus("Gone fishing"); presence.setStatus("Gone fishing");
// Send the packet (assume we have an XMPPConnection instance called "con"). // Send the stanza (assume we have an XMPPConnection instance called "con").
con.sendStanza(presence); con.sendStanza(presence);
``` ```
Smack provides two ways to read incoming packets: `PacketListener`, and Smack provides two ways to read incoming packets: `StanzaListener`, and
`PacketCollector`. Both use `StanzaFilter` instances to determine which `StanzaCollector`. Both use `StanzaFilter` instances to determine which
packets should be processed. A packet listener is used for event style stanzas should be processed. A stanza listener is used for event style
programming, while a packet collector has a result queue of packets that you programming, while a stanza collector has a result queue of packets that you
can do polling and blocking operations on. So, a packet listener is useful can do polling and blocking operations on. So, a stanza listener is useful
when you want to take some action whenever a packet happens to come in, while when you want to take some action whenever a stanza happens to come in, while
a packet collector is useful when you want to wait for a specific packet to a stanza collector is useful when you want to wait for a specific packet to
arrive. Packet collectors and listeners can be created using an Connection arrive. Stanza collectors and listeners can be created using an Connection
instance. instance.
Copyright (C) Jive Software 2002-2008 Copyright (C) Jive Software 2002-2008

View file

@ -6,7 +6,7 @@ Messaging using Chats
Sending messages back and forth is at the core of instant messaging. Although Sending messages back and forth is at the core of instant messaging. Although
individual messages can be sent and received as packets, it's generally easier individual messages can be sent and received as packets, it's generally easier
to treat the string of messages as a chat using the to treat the string of messages as a chat using the
`org.jivesoftware.smack.Chat` class. `org.jivesoftware.smack.chat2.Chat` class.
Chat Chat
---- ----
@ -17,80 +17,44 @@ and then send them a text message:
``` ```
// Assume we've created an XMPPConnection name "connection"._ // Assume we've created an XMPPConnection name "connection"._
ChatManager chatmanager = ChatManager.getInstanceFor(connection); ChatManager chatManager = ChatManager.getInstanceFor(connection);
Chat newChat = chatmanager.createChat("jsmith@jivesoftware.com", new MessageListener() { chatManager.addListener(new IncomingChatMessageListener() {
public void processMessage(Chat chat, Message message) { @Override
System.out.println("Received message: " + message); void newIncomingMessage(EntityBareJid from, Message message, Chat chat) {
} System.out.println("New message from " + from ": " + message.getBody());
}
}); });
EntityBareJid jid = JidCreate.entityBareFrom("jsmith@jivesoftware.com");
try { Chat chat = chatManager.chatWith(jid);
newChat.sendMessage("Howdy!"); chat.sendMessage("Howdy!");
}
catch (XMPPException e) {
System.out.println("Error Delivering block");
} }
``` ```
The `Chat.sendMessage(String)` method is a convenience method that creates a The `Chat.sendMessage(String)` method is a convenience method that creates a
Message object, sets the body using the String parameter, then sends the Message object, sets the body using the String parameter, then sends the
message. In the case that you wish to set additional values on a Message message. In the case that you wish to set additional values on a Message
before sending it, use the `Chat.createMessage()` and before sending it, use
`Chat.sendMessage(Message)` methods, as in the following code snippet: `Chat.sendMessage(Message)` method, as in the following code snippet:
``` ```
Message newMessage = new Message(); Message newMessage = new Message();
newMessage.setBody("Howdy!"); newMessage.setBody("Howdy!");
// Additional modifications to the message Stanza. // Additional modifications to the message Stanza.
JivePropertiesManager.addProperty(newMessage, "favoriteColor", "red"); JivePropertiesManager.addProperty(newMessage, "favoriteColor", "red");
newChat.sendMessage(newMessage); chat.sendMessage(newMessage);
``` ```
You'll also notice in the example above that we specified a MessageListener You'll also notice in the example above that we specified an IncomingChatMessageListener.
when creating a chat. The listener is notified any time a new message arrives The listener is notified any time a new chat message arrives.
from the other user in the chat. The following code snippet uses the listener The following code snippet uses the listener
as a parrot-bot -- it echoes back everything the other user types. as a parrot-bot -- it echoes back everything the other user types.
``` ```
// Assume a MessageListener we've setup with a chat._ // Assume a IncomingChatMessageListener we've setup with a ChatManager
public void newIncomingMessage(EntityBareJid from, Message message, Chat chat) {
public void processMessage(Chat chat, Message message) { // Send back the same text the other user sent us.
// Send back the same text the other user sent us._ chat.sendMessage(message.getBody());
chat.sendMessage(message.getBody());
} }
``` ```
Incoming Chat
-------------
When chats are prompted by another user, the setup is slightly different since
you are receiving a chat message first. Instead of explicitly creating a chat
to send messages, you need to register to handle newly created Chat instances
when the ChatManager creates them. The ChatManager will already find a
matching chat (by thread id) and if none exists, then it will create a new one
that does match. To get this new chat, you have to register to be notified
when it happens. You can register a message listener to receive all future
messages as part of this handler.
```
// Assume we've created an XMPPConnection name "connection"._
ChatManager chatManager = ChatManager.getInstanceFor(connection);
chatManager.addChatListener(
new ChatManagerListener() {
@Override
public void chatCreated(Chat chat, boolean createdLocally)
{
if (!createdLocally)
chat.addMessageListener(new MyNewMessageListener());;
}
});
```
In addition to thread based chat messages, there are some clients that do not
send a thread id as part of the chat. To handle this scenario, Smack will
attempt match the incoming messages to the best fit existing chat, based on
the JID. It will attempt to find a chat with the same full JID, failing that,
it will try the base JID. If no existing chat to the user can found, then a
new one is created.
Copyright (C) Jive Software 2002-2008 Copyright (C) Jive Software 2002-2008

View file

@ -0,0 +1,62 @@
/**
*
* Copyright 2017 Florian Schmaus.
*
* 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.smack.filter;
import org.jivesoftware.smack.packet.Stanza;
import org.jxmpp.jid.Jid;
public abstract class AbstractJidTypeFilter implements StanzaFilter {
protected enum JidType {
entityFull,
entityBare,
domainFull,
domainBare,
;
}
private final JidType jidType;
protected AbstractJidTypeFilter(JidType jidType) {
this.jidType = jidType;
}
protected abstract Jid getJidToInspect(Stanza stanza);
@Override
public final boolean accept(Stanza stanza) {
final Jid jid = stanza.getFrom();
if (jid == null) {
return false;
}
switch (jidType) {
case entityFull:
return jid.isEntityFullJid();
case entityBare:
return jid.isEntityBareJid();
case domainFull:
return jid.isDomainFullJid();
case domainBare:
return jid.isDomainBareJid();
default:
throw new AssertionError();
}
}
}

View file

@ -0,0 +1,38 @@
/**
*
* Copyright 2017 Florian Schmaus.
*
* 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.smack.filter;
import org.jivesoftware.smack.packet.Stanza;
import org.jxmpp.jid.Jid;
public final class FromTypeFilter extends AbstractJidTypeFilter {
public static final FromTypeFilter ENTITY_FULL_JID = new FromTypeFilter(JidType.entityFull);
public static final FromTypeFilter ENTITY_BARE_JID = new FromTypeFilter(JidType.entityBare);
public static final FromTypeFilter DOMAIN_FULL_JID = new FromTypeFilter(JidType.domainFull);
public static final FromTypeFilter DOMAIN_BARE_JID = new FromTypeFilter(JidType.domainBare);
private FromTypeFilter(JidType jidType) {
super(jidType);
}
@Override
protected Jid getJidToInspect(Stanza stanza) {
return stanza.getFrom();
}
}

View file

@ -0,0 +1,40 @@
/**
*
* Copyright 2017 Florian Schmaus.
*
* 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.smack.filter;
import org.jivesoftware.smack.packet.Stanza;
import org.jxmpp.jid.Jid;
public final class ToTypeFilter extends AbstractJidTypeFilter {
public static final ToTypeFilter ENTITY_FULL_JID = new ToTypeFilter(JidType.entityFull);
public static final ToTypeFilter ENTITY_BARE_JID = new ToTypeFilter(JidType.entityBare);
public static final ToTypeFilter DOMAIN_FULL_JID = new ToTypeFilter(JidType.domainFull);
public static final ToTypeFilter DOMAIN_BARE_JID = new ToTypeFilter(JidType.domainBare);
public static final StanzaFilter ENTITY_FULL_OR_BARE_JID = new OrFilter(ENTITY_FULL_JID, ENTITY_BARE_JID);
private ToTypeFilter(JidType jidType) {
super(jidType);
}
@Override
protected Jid getJidToInspect(Stanza stanza) {
return stanza.getTo();
}
}

View file

@ -28,8 +28,6 @@ import org.jivesoftware.smack.SmackException.NotConnectedException;
import org.jivesoftware.smack.StanzaListener; import org.jivesoftware.smack.StanzaListener;
import org.jivesoftware.smack.XMPPConnection; import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPException.XMPPErrorException; import org.jivesoftware.smack.XMPPException.XMPPErrorException;
import org.jivesoftware.smack.chat.Chat;
import org.jivesoftware.smack.chat.ChatManager;
import org.jivesoftware.smack.chat.ChatMessageListener; import org.jivesoftware.smack.chat.ChatMessageListener;
import org.jivesoftware.smack.filter.AndFilter; import org.jivesoftware.smack.filter.AndFilter;
import org.jivesoftware.smack.filter.FromMatchesFilter; import org.jivesoftware.smack.filter.FromMatchesFilter;
@ -146,8 +144,10 @@ public class MultiUserChatLight {
* for the newly created chat. * for the newly created chat.
* @return new Chat for sending private messages to a given room occupant. * @return new Chat for sending private messages to a given room occupant.
*/ */
public Chat createPrivateChat(EntityJid occupant, ChatMessageListener listener) { @Deprecated
return ChatManager.getInstanceFor(connection).createChat(occupant, listener); // Do not re-use Chat API, which was designed for XMPP-IM 1:1 chats and not MUClight private chats.
public org.jivesoftware.smack.chat.Chat createPrivateChat(EntityJid occupant, ChatMessageListener listener) {
return org.jivesoftware.smack.chat.ChatManager.getInstanceFor(connection).createChat(occupant, listener);
} }
/** /**

View file

@ -0,0 +1,73 @@
/**
*
* Copyright 2017 Florian Schmaus.
*
* 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.smack.chat2;
import org.jivesoftware.smack.Manager;
import org.jivesoftware.smack.SmackException.NotConnectedException;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.Presence;
import org.jxmpp.jid.EntityBareJid;
import org.jxmpp.jid.EntityFullJid;
import org.jxmpp.jid.Jid;
public final class Chat extends Manager {
private final EntityBareJid jid;
volatile EntityFullJid lockedResource;
Presence lastPresenceOfLockedResource;
Chat(final XMPPConnection connection, EntityBareJid jid) {
super(connection);
this.jid = jid;
}
public void send(CharSequence message) throws NotConnectedException, InterruptedException {
Message stanza = new Message();
stanza.setBody(message);
send(stanza);
}
public void send(Message message) throws NotConnectedException, InterruptedException {
switch (message.getType()) {
case normal:
case chat:
break;
default:
throw new IllegalArgumentException("Message must be of type 'normal' or 'chat'");
}
Jid to = lockedResource;
if (to == null) {
to = jid;
}
message.setTo(to);
connection().sendStanza(message);
}
public EntityBareJid getXmppAddressOfChatPartner() {
return jid;
}
void unlockResource() {
lockedResource = null;
lastPresenceOfLockedResource = null;
}
}

View file

@ -0,0 +1,237 @@
/**
*
* Copyright 2017 Florian Schmaus.
*
* 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.smack.chat2;
import java.util.Map;
import java.util.Set;
import java.util.WeakHashMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet;
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.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.OrFilter;
import org.jivesoftware.smack.filter.StanzaExtensionFilter;
import org.jivesoftware.smack.filter.StanzaFilter;
import org.jivesoftware.smack.filter.ToTypeFilter;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.Presence;
import org.jivesoftware.smack.packet.Stanza;
import org.jivesoftware.smack.roster.AbstractRosterListener;
import org.jivesoftware.smack.roster.Roster;
import org.jivesoftware.smackx.xhtmlim.packet.XHTMLExtension;
import org.jxmpp.jid.EntityBareJid;
import org.jxmpp.jid.EntityFullJid;
import org.jxmpp.jid.Jid;
/**
* A chat manager for 1:1 XMPP instant messaging chats.
* <p>
* This manager and the according {@link Chat} API implement "Resource Locking" (XEP-0296). Support for Carbon Copies
* (XEP-0280) will be added once the XEP has progressed from experimental.
* </p>
*
* @see <a href="https://xmpp.org/extensions/xep-0296.html">XEP-0296: Best Practices for Resource Locking</a>
*/
public final class ChatManager extends Manager {
private static final Map<XMPPConnection, ChatManager> INSTANCES = new WeakHashMap<>();
public static synchronized ChatManager getInstanceFor(XMPPConnection connection) {
ChatManager chatManager = INSTANCES.get(connection);
if (chatManager == null) {
chatManager = new ChatManager(connection);
INSTANCES.put(connection, chatManager);
}
return chatManager;
}
// @FORMATTER:OFF
private static final StanzaFilter MESSAGE_FILTER = new AndFilter(
MessageTypeFilter.NORMAL_OR_CHAT,
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
);
private static final StanzaFilter INCOMING_MESSAGE_FILTER = new AndFilter(
MESSAGE_FILTER,
FromTypeFilter.ENTITY_FULL_JID
);
// @FORMATTER:ON
private final Map<EntityBareJid, Chat> chats = new ConcurrentHashMap<>();
private final Set<IncomingChatMessageListener> incomingListeners = new CopyOnWriteArraySet<>();
private final Set<OutgoingChatMessageListener> outgoingListeners = new CopyOnWriteArraySet<>();
private boolean xhtmlIm;
private ChatManager(final XMPPConnection connection) {
super(connection);
connection.addSyncStanzaListener(new StanzaListener() {
@Override
public void processStanza(Stanza stanza) {
Message message = (Message) stanza;
if (!shouldAcceptMessage(message)) {
return;
}
final Jid from = message.getFrom();
final EntityFullJid fullFrom = from.asEntityFullJidOrThrow();
final EntityBareJid bareFrom = fullFrom.asEntityBareJid();
final Chat chat = chatWith(bareFrom);
chat.lockedResource = fullFrom;
for (IncomingChatMessageListener listener : incomingListeners) {
listener.newIncomingMessage(bareFrom, message, chat);
}
}
}, INCOMING_MESSAGE_FILTER);
connection.addPacketInterceptor(new StanzaListener() {
@Override
public void processStanza(Stanza stanza) throws NotConnectedException, InterruptedException {
Message message = (Message) stanza;
if (!shouldAcceptMessage(message)) {
return;
}
final EntityBareJid to = message.getTo().asEntityBareJidOrThrow();
final Chat chat = chatWith(to);
for (OutgoingChatMessageListener listener : outgoingListeners) {
listener.newOutgoingMessage(to, message, chat);
}
}
}, OUTGOING_MESSAGE_FILTER);
Roster roster = Roster.getInstanceFor(connection);
roster.addRosterListener(new AbstractRosterListener() {
@Override
public void presenceChanged(Presence presence) {
final Jid from = presence.getFrom();
final EntityBareJid bareFrom = from.asEntityBareJidIfPossible();
if (bareFrom == null) {
return;
}
final Chat chat = chats.get(bareFrom);
if (chat == null) {
return;
}
if (chat.lockedResource == null) {
// According to XEP-0296, no action is required for resource locking upon receiving a presence if no
// resource is currently locked.
return;
}
final EntityFullJid fullFrom = from.asEntityFullJidIfPossible();
if (!chat.lockedResource.equals(fullFrom)) {
return;
}
if (chat.lastPresenceOfLockedResource == null) {
// We have no last known presence from the locked resource.
chat.lastPresenceOfLockedResource = presence;
return;
}
if (chat.lastPresenceOfLockedResource.getMode() != presence.getMode()
|| chat.lastPresenceOfLockedResource.getType() != presence.getType()) {
chat.unlockResource();
}
}
});
}
private boolean shouldAcceptMessage(Message message) {
if (!message.getBodies().isEmpty()) {
return true;
}
// Message has no XMPP-IM bodies, abort here if xhtmlIm is not enabled.
if (!xhtmlIm) {
return false;
}
XHTMLExtension xhtmlExtension = XHTMLExtension.from(message);
if (xhtmlExtension == null) {
// Message has no XHTML-IM extension, abort.
return false;
}
return true;
}
public boolean addListener(IncomingChatMessageListener listener) {
return incomingListeners.add(listener);
}
public boolean removeListener(IncomingChatMessageListener listener) {
return incomingListeners.remove(listener);
}
public boolean addListener(OutgoingChatMessageListener listener) {
return outgoingListeners.add(listener);
}
public boolean removeOutoingLIstener(OutgoingChatMessageListener listener) {
return outgoingListeners.remove(listener);
}
/**
* Start a new or retrieve the existing chat with <code>jid</code>.
*
* @param jid the XMPP address of the other entity to chat with.
* @return the Chat API for the given XMPP address.
*/
public Chat chatWith(EntityBareJid jid) {
Chat chat = chats.get(jid);
if (chat == null) {
synchronized (chats) {
// Double-checked locking.
chat = chats.get(jid);
if (chat != null) {
return chat;
}
chat = new Chat(connection(), jid);
chats.put(jid, chat);
}
}
return chat;
}
/**
* Also notify about messages containing XHTML-IM.
*
* @param xhtmlIm
*/
public void setXhmtlImEnabled(boolean xhtmlIm) {
this.xhtmlIm = xhtmlIm;
}
}

View file

@ -0,0 +1,26 @@
/**
*
* Copyright 2017 Florian Schmaus.
*
* 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.smack.chat2;
import org.jivesoftware.smack.packet.Message;
import org.jxmpp.jid.EntityBareJid;
public interface IncomingChatMessageListener {
void newIncomingMessage(EntityBareJid from, Message message, Chat chat);
}

View file

@ -0,0 +1,26 @@
/**
*
* Copyright 2017 Florian Schmaus.
*
* 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.smack.chat2;
import org.jivesoftware.smack.packet.Message;
import org.jxmpp.jid.EntityBareJid;
public interface OutgoingChatMessageListener {
void newOutgoingMessage(EntityBareJid to, Message message, Chat chat);
}

View file

@ -0,0 +1,21 @@
/**
*
* Copyright 2017 Florian Schmaus
*
* 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.
*/
/**
* Smack's new improved API for 1:1 chats.
*/
package org.jivesoftware.smack.chat2;

View file

@ -17,7 +17,6 @@
package org.jivesoftware.smackx.chatstates; package org.jivesoftware.smackx.chatstates;
import org.jivesoftware.smack.chat.Chat;
import org.jivesoftware.smack.chat.ChatMessageListener; import org.jivesoftware.smack.chat.ChatMessageListener;
import org.jivesoftware.smack.packet.Message; import org.jivesoftware.smack.packet.Message;
@ -35,5 +34,7 @@ public interface ChatStateListener extends ChatMessageListener {
* @param state the new state of the participant. * @param state the new state of the participant.
* @param message the message carrying the chat state. * @param message the message carrying the chat state.
*/ */
void stateChanged(Chat chat, ChatState state, Message message); // TODO Migrate to new chat2 API on Smack 4.3.
@SuppressWarnings("deprecation")
void stateChanged(org.jivesoftware.smack.chat.Chat chat, ChatState state, Message message);
} }

View file

@ -24,8 +24,6 @@ import org.jivesoftware.smack.MessageListener;
import org.jivesoftware.smack.SmackException.NotConnectedException; import org.jivesoftware.smack.SmackException.NotConnectedException;
import org.jivesoftware.smack.XMPPConnection; import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.Manager; import org.jivesoftware.smack.Manager;
import org.jivesoftware.smack.chat.Chat;
import org.jivesoftware.smack.chat.ChatManager;
import org.jivesoftware.smack.chat.ChatManagerListener; import org.jivesoftware.smack.chat.ChatManagerListener;
import org.jivesoftware.smack.chat.ChatMessageListener; import org.jivesoftware.smack.chat.ChatMessageListener;
import org.jivesoftware.smack.filter.NotFilter; import org.jivesoftware.smack.filter.NotFilter;
@ -49,6 +47,8 @@ import org.jivesoftware.smackx.disco.ServiceDiscoveryManager;
* @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
*/ */
// TODO Migrate to new chat2 API on Smack 4.3.
@SuppressWarnings("deprecation")
public final class ChatStateManager extends Manager { public final class ChatStateManager extends Manager {
public static final String NAMESPACE = "http://jabber.org/protocol/chatstates"; public static final String NAMESPACE = "http://jabber.org/protocol/chatstates";
@ -79,13 +79,13 @@ public final class ChatStateManager extends Manager {
/** /**
* Maps chat to last chat state. * Maps chat to last chat state.
*/ */
private final Map<Chat, ChatState> chatStates = new WeakHashMap<Chat, ChatState>(); private final Map<org.jivesoftware.smack.chat.Chat, ChatState> chatStates = new WeakHashMap<>();
private final ChatManager chatManager; private final org.jivesoftware.smack.chat.ChatManager chatManager;
private ChatStateManager(XMPPConnection connection) { private ChatStateManager(XMPPConnection connection) {
super(connection); super(connection);
chatManager = ChatManager.getInstanceFor(connection); chatManager = org.jivesoftware.smack.chat.ChatManager.getInstanceFor(connection);
chatManager.addOutgoingMessageInterceptor(outgoingInterceptor, filter); chatManager.addOutgoingMessageInterceptor(outgoingInterceptor, filter);
chatManager.addChatListener(incomingInterceptor); chatManager.addChatListener(incomingInterceptor);
@ -104,7 +104,7 @@ public final class ChatStateManager extends Manager {
* @throws NotConnectedException * @throws NotConnectedException
* @throws InterruptedException * @throws InterruptedException
*/ */
public void setCurrentState(ChatState newState, Chat chat) throws NotConnectedException, InterruptedException { public void setCurrentState(ChatState newState, org.jivesoftware.smack.chat.Chat chat) throws NotConnectedException, InterruptedException {
if(chat == null || newState == null) { if(chat == null || newState == null) {
throw new IllegalArgumentException("Arguments cannot be null."); throw new IllegalArgumentException("Arguments cannot be null.");
} }
@ -133,7 +133,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(org.jivesoftware.smack.chat.Chat 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);
@ -142,7 +142,7 @@ public final class ChatStateManager extends Manager {
return false; return false;
} }
private static void fireNewChatState(Chat chat, ChatState state, Message message) { private static void fireNewChatState(org.jivesoftware.smack.chat.Chat chat, ChatState state, Message message) {
for (ChatMessageListener listener : chat.getListeners()) { for (ChatMessageListener listener : chat.getListeners()) {
if (listener instanceof ChatStateListener) { if (listener instanceof ChatStateListener) {
((ChatStateListener) listener).stateChanged(chat, state, message); ((ChatStateListener) listener).stateChanged(chat, state, message);
@ -154,7 +154,7 @@ public final class ChatStateManager extends Manager {
@Override @Override
public void processMessage(Message message) { public void processMessage(Message message) {
Chat chat = chatManager.getThreadChat(message.getThread()); org.jivesoftware.smack.chat.Chat chat = chatManager.getThreadChat(message.getThread());
if (chat == null) { if (chat == null) {
return; return;
} }
@ -166,11 +166,11 @@ public final class ChatStateManager extends Manager {
private class IncomingMessageInterceptor implements ChatManagerListener, ChatMessageListener { private class IncomingMessageInterceptor implements ChatManagerListener, ChatMessageListener {
public void chatCreated(final Chat chat, boolean createdLocally) { public void chatCreated(final org.jivesoftware.smack.chat.Chat chat, boolean createdLocally) {
chat.addMessageListener(this); chat.addMessageListener(this);
} }
public void processMessage(Chat chat, Message message) { public void processMessage(org.jivesoftware.smack.chat.Chat chat, Message message) {
ExtensionElement extension = message.getExtension(NAMESPACE); ExtensionElement extension = message.getExtension(NAMESPACE);
if (extension == null) { if (extension == null) {
return; return;

View file

@ -37,8 +37,6 @@ import org.jivesoftware.smack.SmackException.NotConnectedException;
import org.jivesoftware.smack.XMPPConnection; import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPException; import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.XMPPException.XMPPErrorException; import org.jivesoftware.smack.XMPPException.XMPPErrorException;
import org.jivesoftware.smack.chat.Chat;
import org.jivesoftware.smack.chat.ChatManager;
import org.jivesoftware.smack.chat.ChatMessageListener; import org.jivesoftware.smack.chat.ChatMessageListener;
import org.jivesoftware.smack.filter.AndFilter; import org.jivesoftware.smack.filter.AndFilter;
import org.jivesoftware.smack.filter.FromMatchesFilter; import org.jivesoftware.smack.filter.FromMatchesFilter;
@ -1847,8 +1845,11 @@ public class MultiUserChat {
* created chat. * created chat.
* @return new Chat for sending private messages to a given room occupant. * @return new Chat for sending private messages to a given room occupant.
*/ */
public Chat createPrivateChat(EntityFullJid occupant, ChatMessageListener listener) { // TODO This should be made new not using chat.Chat. Private MUC chats are different from XMPP-IM 1:1 chats in to many ways.
return ChatManager.getInstanceFor(connection).createChat(occupant, listener); // API sketch: PrivateMucChat createPrivateChat(Resourcepart nick)
@SuppressWarnings("deprecation")
public org.jivesoftware.smack.chat.Chat createPrivateChat(EntityFullJid occupant, ChatMessageListener listener) {
return org.jivesoftware.smack.chat.ChatManager.getInstanceFor(connection).createChat(occupant, listener);
} }
/** /**

View file

@ -36,7 +36,9 @@ import java.util.concurrent.CopyOnWriteArraySet;
* sender. * sender.
* *
* @author Matt Tucker * @author Matt Tucker
* @deprecated use <code>org.jivesoftware.smack.chat2.Chat</code> from <code>smack-extensions</code> instead.
*/ */
@Deprecated
public class Chat { public class Chat {
private ChatManager chatManager; private ChatManager chatManager;

View file

@ -52,7 +52,9 @@ import org.jxmpp.jid.EntityJid;
* made aware of new chats, register a listener by calling {@link #addChatListener(ChatManagerListener)}. * made aware of new chats, register a listener by calling {@link #addChatListener(ChatManagerListener)}.
* *
* @author Alexander Wenckus * @author Alexander Wenckus
* @deprecated use <code>org.jivesoftware.smack.chat2.ChatManager</code> from <code>smack-extensions</code> instead.
*/ */
@Deprecated
public final class ChatManager extends Manager{ public final class ChatManager extends Manager{
private static final Logger LOGGER = Logger.getLogger(ChatManager.class.getName()); private static final Logger LOGGER = Logger.getLogger(ChatManager.class.getName());

View file

@ -30,5 +30,6 @@ public interface ChatManagerListener {
* @param chat the chat that was created. * @param chat the chat that was created.
* @param createdLocally true if the chat was created by the local user and false if it wasn't. * @param createdLocally true if the chat was created by the local user and false if it wasn't.
*/ */
@SuppressWarnings("deprecation")
void chatCreated(Chat chat, boolean createdLocally); void chatCreated(Chat chat, boolean createdLocally);
} }

View file

@ -20,5 +20,6 @@ package org.jivesoftware.smack.chat;
import org.jivesoftware.smack.packet.Message; import org.jivesoftware.smack.packet.Message;
public interface ChatMessageListener { public interface ChatMessageListener {
@SuppressWarnings("deprecation")
void processMessage(Chat chat, Message message); void processMessage(Chat chat, Message message);
} }

View file

@ -24,7 +24,6 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail; import static org.junit.Assert.fail;
import org.jivesoftware.smack.DummyConnection; import org.jivesoftware.smack.DummyConnection;
import org.jivesoftware.smack.chat.ChatManager.MatchMode;
import org.jivesoftware.smack.packet.Message; import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.Message.Type; import org.jivesoftware.smack.packet.Message.Type;
import org.jivesoftware.smack.packet.Stanza; import org.jivesoftware.smack.packet.Stanza;
@ -35,6 +34,7 @@ import org.junit.Test;
import org.jxmpp.jid.Jid; import org.jxmpp.jid.Jid;
import org.jxmpp.jid.JidTestUtil; import org.jxmpp.jid.JidTestUtil;
@SuppressWarnings("deprecation")
public class ChatConnectionTest { public class ChatConnectionTest {
private DummyConnection dc; private DummyConnection dc;
@ -46,7 +46,7 @@ public class ChatConnectionTest {
public void setUp() throws Exception { public void setUp() throws Exception {
// Defaults // Defaults
ChatManager.setDefaultIsNormalIncluded(true); ChatManager.setDefaultIsNormalIncluded(true);
ChatManager.setDefaultMatchMode(MatchMode.BARE_JID); ChatManager.setDefaultMatchMode(ChatManager.MatchMode.BARE_JID);
dc = DummyConnection.newConnectedDummyConnection(); dc = DummyConnection.newConnectedDummyConnection();
cm = ChatManager.getInstanceFor(dc); cm = ChatManager.getInstanceFor(dc);
@ -77,14 +77,14 @@ public class ChatConnectionTest {
@Test @Test
public void validateDefaultSetMatchModeNone() { public void validateDefaultSetMatchModeNone() {
ChatManager.setDefaultMatchMode(MatchMode.NONE); ChatManager.setDefaultMatchMode(ChatManager.MatchMode.NONE);
assertEquals(MatchMode.NONE, ChatManager.getInstanceFor(new DummyConnection()).getMatchMode()); assertEquals(ChatManager.MatchMode.NONE, ChatManager.getInstanceFor(new DummyConnection()).getMatchMode());
} }
@Test @Test
public void validateDefaultSetMatchModeEntityBareJid() { public void validateDefaultSetMatchModeEntityBareJid() {
ChatManager.setDefaultMatchMode(MatchMode.BARE_JID); ChatManager.setDefaultMatchMode(ChatManager.MatchMode.BARE_JID);
assertEquals(MatchMode.BARE_JID, ChatManager.getInstanceFor(new DummyConnection()).getMatchMode()); assertEquals(ChatManager.MatchMode.BARE_JID, ChatManager.getInstanceFor(new DummyConnection()).getMatchMode());
} }
@Test @Test
@ -138,7 +138,7 @@ public class ChatConnectionTest {
// No thread behaviour // No thread behaviour
@Test @Test
public void chatMatchedOnJIDWhenNoThreadBareMode() { public void chatMatchedOnJIDWhenNoThreadBareMode() {
// MatchMode.BARE_JID is the default, so setting required. // ChatManager.MatchMode.BARE_JID is the default, so setting required.
TestMessageListener msgListener = new TestMessageListener(); TestMessageListener msgListener = new TestMessageListener();
TestChatManagerListener listener = new TestChatManagerListener(msgListener); TestChatManagerListener listener = new TestChatManagerListener(msgListener);
cm.addChatListener(listener); cm.addChatListener(listener);
@ -162,7 +162,7 @@ public class ChatConnectionTest {
public void chatMatchedOnJIDWhenNoThreadJidMode() { public void chatMatchedOnJIDWhenNoThreadJidMode() {
TestMessageListener msgListener = new TestMessageListener(); TestMessageListener msgListener = new TestMessageListener();
TestChatManagerListener listener = new TestChatManagerListener(msgListener); TestChatManagerListener listener = new TestChatManagerListener(msgListener);
cm.setMatchMode(MatchMode.SUPPLIED_JID); cm.setMatchMode(ChatManager.MatchMode.SUPPLIED_JID);
cm.addChatListener(listener); cm.addChatListener(listener);
Stanza incomingChat = createChatPacket(null, true); Stanza incomingChat = createChatPacket(null, true);
processServerMessage(incomingChat); processServerMessage(incomingChat);
@ -188,7 +188,7 @@ public class ChatConnectionTest {
public void chatMatchedOnJIDWhenNoThreadNoneMode() { public void chatMatchedOnJIDWhenNoThreadNoneMode() {
TestMessageListener msgListener = new TestMessageListener(); TestMessageListener msgListener = new TestMessageListener();
TestChatManagerListener listener = new TestChatManagerListener(msgListener); TestChatManagerListener listener = new TestChatManagerListener(msgListener);
cm.setMatchMode(MatchMode.NONE); cm.setMatchMode(ChatManager.MatchMode.NONE);
cm.addChatListener(listener); cm.addChatListener(listener);
Stanza incomingChat = createChatPacket(null, true); Stanza incomingChat = createChatPacket(null, true);
processServerMessage(incomingChat); processServerMessage(incomingChat);

View file

@ -31,8 +31,6 @@ import org.igniterealtime.smack.inttest.AbstractSmackIntegrationTest;
import org.igniterealtime.smack.inttest.SmackIntegrationTest; import org.igniterealtime.smack.inttest.SmackIntegrationTest;
import org.igniterealtime.smack.inttest.SmackIntegrationTestEnvironment; import org.igniterealtime.smack.inttest.SmackIntegrationTestEnvironment;
import org.jivesoftware.smack.SmackException.NotConnectedException; import org.jivesoftware.smack.SmackException.NotConnectedException;
import org.jivesoftware.smack.chat.Chat;
import org.jivesoftware.smack.chat.ChatManager;
import org.jivesoftware.smack.chat.ChatManagerListener; import org.jivesoftware.smack.chat.ChatManagerListener;
import org.jivesoftware.smack.filter.ThreadFilter; import org.jivesoftware.smack.filter.ThreadFilter;
import org.jivesoftware.smack.packet.Message; import org.jivesoftware.smack.packet.Message;
@ -46,12 +44,14 @@ import org.jxmpp.stringprep.XmppStringprepException;
*/ */
public class ChatTest extends AbstractSmackIntegrationTest { public class ChatTest extends AbstractSmackIntegrationTest {
private final ChatManager chatManagerOne; @SuppressWarnings("deprecation")
private final org.jivesoftware.smack.chat.ChatManager chatManagerOne;
private boolean invoked; private boolean invoked;
@SuppressWarnings("deprecation")
public ChatTest(SmackIntegrationTestEnvironment environment) { public ChatTest(SmackIntegrationTestEnvironment environment) {
super(environment); super(environment);
chatManagerOne = ChatManager.getInstanceFor(conOne); chatManagerOne = org.jivesoftware.smack.chat.ChatManager.getInstanceFor(conOne);
} }
@BeforeClass @BeforeClass
@ -64,9 +64,10 @@ public class ChatTest extends AbstractSmackIntegrationTest {
JivePropertiesManager.setJavaObjectEnabled(false); JivePropertiesManager.setJavaObjectEnabled(false);
} }
@SuppressWarnings("deprecation")
@SmackIntegrationTest @SmackIntegrationTest
public void testProperties() throws XmppStringprepException, NotConnectedException, Exception { public void testProperties() throws XmppStringprepException, NotConnectedException, Exception {
Chat newChat = chatManagerOne.createChat(conTwo.getUser()); org.jivesoftware.smack.chat.Chat newChat = chatManagerOne.createChat(conTwo.getUser());
StanzaCollector collector = conTwo.createStanzaCollector(new ThreadFilter(newChat.getThreadID())); StanzaCollector collector = conTwo.createStanzaCollector(new ThreadFilter(newChat.getThreadID()));
Message msg = new Message(); Message msg = new Message();
@ -112,12 +113,13 @@ public class ChatTest extends AbstractSmackIntegrationTest {
getProperty(msg2, "birthdate")); getProperty(msg2, "birthdate"));
} }
@SuppressWarnings("deprecation")
@SmackIntegrationTest @SmackIntegrationTest
public void chatManagerTest() { public void chatManagerTest() {
ChatManagerListener listener = new ChatManagerListener() { ChatManagerListener listener = new ChatManagerListener() {
@Override @Override
public void chatCreated(Chat chat, boolean createdLocally) { public void chatCreated(org.jivesoftware.smack.chat.Chat chat, boolean createdLocally) {
invoked = true; invoked = true;
} }