diff --git a/smack-im/src/main/java/org/jivesoftware/smack/roster/Roster.java b/smack-im/src/main/java/org/jivesoftware/smack/roster/Roster.java index 3df9b904c5..ab1e594068 100644 --- a/smack-im/src/main/java/org/jivesoftware/smack/roster/Roster.java +++ b/smack-im/src/main/java/org/jivesoftware/smack/roster/Roster.java @@ -1432,12 +1432,22 @@ private void fireRosterChangedEvent(final Collection addedEntries, final Co /** * Fires roster presence changed event to roster listeners. * + * @param bareFrom the bare JID that send the presence. + * @param ownPresence true if this is a presence from one of our available resources. * @param presence the presence change. */ - private void fireRosterPresenceEvent(final Presence presence) { + private void fireRosterPresenceEvent(BareJid bareFrom, boolean ownPresence, Presence presence) { + if (!ownPresence && !contains(bareFrom)) { + return; + } + synchronized (rosterListenersAndEntriesLock) { for (RosterListener listener : rosterListeners) { - listener.presenceChanged(presence); + if (ownPresence) { + listener.ownPresenceChanged(presence); + } else { + listener.presenceChanged(presence); + } } } } @@ -1636,28 +1646,30 @@ public void processStanza(Stanza packet) throws NotConnectedException, Interrupt } final Jid from = packet.getFrom(); + final Presence presence = (Presence) packet; + final XMPPConnection connection = connection(); + if (connection == null) { + LOGGER.finest("Connection was null while trying to handle exotic presence stanza: " + presence); + return; + } if (!isLoaded() && rosterLoadedAtLogin) { - XMPPConnection connection = connection(); - // Only log the warning, if this is not the reflected self-presence. Otherwise, // the reflected self-presence may cause a spurious warning in case the // connection got quickly shut down. See SMACK-941. - if (connection != null && from != null && !from.equals(connection.getUser())) { + if (from != null && !from.equals(connection.getUser())) { LOGGER.warning("Roster not loaded while processing " + packet); } } - final Presence presence = (Presence) packet; final BareJid key; + final boolean ownPresence; if (from != null) { + EntityFullJid myJid = connection.getUser(); + ownPresence = from.isParentOf(myJid); + key = from.asBareJid(); } else { - XMPPConnection connection = connection(); - if (connection == null) { - LOGGER.finest("Connection was null while trying to handle exotic presence stanza: " + presence); - return; - } // Assume the presence come "from the users account on the server" since no from was set (RFC 6120 ยง // 8.1.2.1 4.). Note that getUser() may return null, but should never return null in this case as where // connected. @@ -1671,6 +1683,7 @@ public void processStanza(Stanza packet) throws NotConnectedException, Interrupt } LOGGER.info("Exotic presence stanza without from received: " + presence); key = myJid.asBareJid(); + ownPresence = true; } asyncButOrdered.performAsyncButOrdered(key, new Runnable() { @@ -1704,10 +1717,10 @@ public void run() { userPresences.remove(Resourcepart.EMPTY); // Add the new presence, using the resources as a key. userPresences.put(fromResource, presence); - // If the user is in the roster, fire an event. - if (contains(key)) { - fireRosterPresenceEvent(presence); - } + + // If the user is in the roster or if its our own presence, fire an event. + fireRosterPresenceEvent(key, ownPresence, presence); + for (PresenceEventListener presenceEventListener : presenceEventListeners) { presenceEventListener.presenceAvailable(fullFrom, presence); } @@ -1727,10 +1740,9 @@ public void run() { // such as the user being on vacation. userPresences.put(fromResource, presence); } - // If the user is in the roster, fire an event. - if (contains(key)) { - fireRosterPresenceEvent(presence); - } + + // If the user is in the roster or if its our own presence, fire an event. + fireRosterPresenceEvent(key, ownPresence, presence); // Ensure that 'from' is a full JID before invoking the presence unavailable // listeners. Usually unavailable presences always have a resourcepart, i.e. are @@ -1764,10 +1776,10 @@ public void run() { // Set the new presence using the empty resource as a key. userPresences.put(Resourcepart.EMPTY, presence); - // If the user is in the roster, fire an event. - if (contains(key)) { - fireRosterPresenceEvent(presence); - } + + // If the user is in the roster or if its our own presence, fire an event. + fireRosterPresenceEvent(key, ownPresence, presence); + for (PresenceEventListener presenceEventListener : presenceEventListeners) { presenceEventListener.presenceError(from, presence); } diff --git a/smack-im/src/main/java/org/jivesoftware/smack/roster/RosterListener.java b/smack-im/src/main/java/org/jivesoftware/smack/roster/RosterListener.java index 005b703e94..a8d2e2e636 100644 --- a/smack-im/src/main/java/org/jivesoftware/smack/roster/RosterListener.java +++ b/smack-im/src/main/java/org/jivesoftware/smack/roster/RosterListener.java @@ -79,4 +79,13 @@ public interface RosterListener { * @see Roster#getPresence(org.jxmpp.jid.BareJid) */ void presenceChanged(Presence presence); + + /** + * Called when the presence of one of the own available resources is changed. + * + * @param presence the presence that changed. + * @see Roster#getPresence(org.jxmpp.jid.BareJid) + * @since 4.5 + */ + default void ownPresenceChanged(Presence presence) { }; }