Presences.java

  1package eu.siacs.conversations.entities;
  2
  3import android.util.Pair;
  4import com.google.common.base.Strings;
  5import com.google.common.collect.Iterables;
  6import eu.siacs.conversations.xmpp.Jid;
  7import eu.siacs.conversations.xmpp.manager.DiscoManager;
  8import im.conversations.android.xmpp.model.disco.info.Identity;
  9import im.conversations.android.xmpp.model.stanza.Presence;
 10import java.util.ArrayList;
 11import java.util.HashMap;
 12import java.util.HashSet;
 13import java.util.List;
 14import java.util.Map;
 15import java.util.Set;
 16
 17public class Presences {
 18    private final HashMap<String, Presence> presences = new HashMap<>();
 19    private final Contact contact;
 20
 21    public Presences(final Contact contact) {
 22        this.contact = contact;
 23    }
 24
 25    private static String nameWithoutVersion(String name) {
 26        String[] parts = name.split(" ");
 27        if (parts.length > 1 && Character.isDigit(parts[parts.length - 1].charAt(0))) {
 28            StringBuilder output = new StringBuilder();
 29            for (int i = 0; i < parts.length - 1; ++i) {
 30                if (output.length() != 0) {
 31                    output.append(' ');
 32                }
 33                output.append(parts[i]);
 34            }
 35            return output.toString();
 36        } else {
 37            return name;
 38        }
 39    }
 40
 41    public List<Presence> getPresences() {
 42        synchronized (this.presences) {
 43            return new ArrayList<>(this.presences.values());
 44        }
 45    }
 46
 47    public Map<String, Presence> getPresencesMap() {
 48        synchronized (this.presences) {
 49            return new HashMap<>(this.presences);
 50        }
 51    }
 52
 53    public Presence get(String resource) {
 54        synchronized (this.presences) {
 55            return this.presences.get(resource);
 56        }
 57    }
 58
 59    public void updatePresence(String resource, Presence presence) {
 60        synchronized (this.presences) {
 61            this.presences.put(resource, presence);
 62        }
 63    }
 64
 65    public void removePresence(String resource) {
 66        synchronized (this.presences) {
 67            this.presences.remove(resource);
 68        }
 69    }
 70
 71    public void clearPresences() {
 72        synchronized (this.presences) {
 73            this.presences.clear();
 74        }
 75    }
 76
 77    public Presence.Availability getShownStatus() {
 78        Presence.Availability highestAvailability = Presence.Availability.OFFLINE;
 79        synchronized (this.presences) {
 80            for (final Presence p : presences.values()) {
 81                final var availability = p.getAvailability();
 82                if (availability == Presence.Availability.DND) {
 83                    return availability;
 84                } else if (availability.compareTo(highestAvailability) < 0) {
 85                    highestAvailability = availability;
 86                }
 87            }
 88        }
 89        return highestAvailability;
 90    }
 91
 92    public int size() {
 93        synchronized (this.presences) {
 94            return presences.size();
 95        }
 96    }
 97
 98    public boolean isEmpty() {
 99        synchronized (this.presences) {
100            return this.presences.isEmpty();
101        }
102    }
103
104    public String[] toResourceArray() {
105        synchronized (this.presences) {
106            final String[] presencesArray = new String[presences.size()];
107            presences.keySet().toArray(presencesArray);
108            return presencesArray;
109        }
110    }
111
112    public List<PresenceTemplate> asTemplates() {
113        synchronized (this.presences) {
114            ArrayList<PresenceTemplate> templates = new ArrayList<>(presences.size());
115            for (Presence presence : this.presences.values()) {
116                String message = Strings.nullToEmpty(presence.getStatus()).trim();
117                if (Strings.isNullOrEmpty(message)) {
118                    continue;
119                }
120                templates.add(new PresenceTemplate(presence.getAvailability(), message));
121            }
122            return templates;
123        }
124    }
125
126    public boolean has(String presence) {
127        synchronized (this.presences) {
128            return presences.containsKey(presence);
129        }
130    }
131
132    public Set<String> getStatusMessages() {
133        Set<String> messages = new HashSet<>();
134        synchronized (this.presences) {
135            for (Presence presence : this.presences.values()) {
136                String message = Strings.nullToEmpty(presence.getStatus()).trim();
137                if (Strings.isNullOrEmpty(message)) {
138                    continue;
139                }
140                messages.add(message);
141            }
142        }
143        return messages;
144    }
145
146    public Set<Jid> getFullJids() {
147        final Set<Jid> jids = new HashSet<>();
148        synchronized (this.presences) {
149            for (var resource : this.presences.keySet()) {
150                final var jid = Strings.isNullOrEmpty(resource) ? contact.getJid().asBareJid() : contact.getJid().withResource(resource);
151                jids.add(jid);
152            }
153        }
154        jids.add(contact.getJid().asBareJid());
155        return jids;
156    }
157
158    public boolean allOrNonSupport(String namespace) {
159        final var connection = this.contact.getAccount().getXmppConnection();
160        if (connection == null) {
161            return true;
162        }
163        synchronized (this.presences) {
164            for (var resource : this.presences.keySet()) {
165                final var disco =
166                        connection
167                                .getManager(DiscoManager.class)
168                                .get(
169                                        Strings.isNullOrEmpty(resource)
170                                                ? contact.getJid().asBareJid()
171                                                : contact.getJid().withResource(resource));
172                if (disco == null || !disco.getFeatureStrings().contains(namespace)) {
173                    return false;
174                }
175            }
176        }
177        return true;
178    }
179
180    public boolean anySupport(final String namespace) {
181        final var connection = this.contact.getAccount().getXmppConnection();
182        if (connection == null) {
183            return false;
184        }
185        final var jids = getFullJids();
186        if (jids.size() == 0) {
187            return true;
188        }
189        for (final var jid : jids) {
190            final var disco = connection.getManager(DiscoManager.class).get(jid);
191            if (disco != null && disco.hasFeature(namespace)) {
192                return true;
193            }
194        }
195        return false;
196    }
197
198    public String firstWhichSupport(final String namespace) {
199        final var connection = this.contact.getAccount().getXmppConnection();
200        if (connection == null) {
201            return null;
202        }
203        for (final var jid : getFullJids()) {
204            final var disco = connection.getManager(DiscoManager.class).get(jid);
205            if (disco != null && disco.hasFeature(namespace)) {
206                return jid.getResource() == null ? "" : jid.getResource();
207            }
208        }
209        return null;
210    }
211
212    public boolean anyIdentity(final String category, final String type) {
213        final var connection = this.contact.getAccount().getXmppConnection();
214        if (connection == null) {
215            return false;
216        }
217        for (final var jid : getFullJids()) {
218            final var disco = connection.getManager(DiscoManager.class).get(jid);
219            if (disco != null && disco.hasIdentityWithCategoryAndType(category, type)) {
220                return true;
221            }
222        }
223        return false;
224    }
225
226    public Pair<Map<String, String>, Map<String, String>> toTypeAndNameMap() {
227        Map<String, String> typeMap = new HashMap<>();
228        Map<String, String> nameMap = new HashMap<>();
229        final var connection = this.contact.getAccount().getXmppConnection();
230        if (connection == null) {
231            return new Pair<>(typeMap, nameMap);
232        }
233        synchronized (this.presences) {
234            for (final String resource : this.presences.keySet()) {
235                final var serviceDiscoveryResult =
236                        connection
237                                .getManager(DiscoManager.class)
238                                .get(
239                                        Strings.isNullOrEmpty(resource)
240                                                ? contact.getJid().asBareJid()
241                                                : contact.getJid().withResource(resource));
242                if (serviceDiscoveryResult != null
243                        && !serviceDiscoveryResult.getIdentities().isEmpty()) {
244                    final Identity identity =
245                            Iterables.getFirst(serviceDiscoveryResult.getIdentities(), null);
246                    String type = identity.getType();
247                    String name = identity.getIdentityName();
248                    if (type != null) {
249                        typeMap.put(resource, type);
250                    }
251                    if (name != null) {
252                        nameMap.put(resource, nameWithoutVersion(name));
253                    }
254                }
255            }
256        }
257        return new Pair<>(typeMap, nameMap);
258    }
259}