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        return jids;
155    }
156
157    public boolean allOrNonSupport(String namespace) {
158        final var connection = this.contact.getAccount().getXmppConnection();
159        if (connection == null) {
160            return true;
161        }
162        synchronized (this.presences) {
163            for (var resource : this.presences.keySet()) {
164                final var disco =
165                        connection
166                                .getManager(DiscoManager.class)
167                                .get(
168                                        Strings.isNullOrEmpty(resource)
169                                                ? contact.getJid().asBareJid()
170                                                : contact.getJid().withResource(resource));
171                if (disco == null || !disco.getFeatureStrings().contains(namespace)) {
172                    return false;
173                }
174            }
175        }
176        return true;
177    }
178
179    public boolean anySupport(final String namespace) {
180        final var connection = this.contact.getAccount().getXmppConnection();
181        if (connection == null) {
182            return false;
183        }
184        final var jids = getFullJids();
185        if (jids.size() == 0) {
186            return true;
187        }
188        for (final var jid : jids) {
189            final var disco = connection.getManager(DiscoManager.class).get(jid);
190            if (disco != null && disco.hasFeature(namespace)) {
191                return true;
192            }
193        }
194        return false;
195    }
196
197    public String firstWhichSupport(final String namespace) {
198        final var connection = this.contact.getAccount().getXmppConnection();
199        if (connection == null) {
200            return null;
201        }
202        for (final var jid : getFullJids()) {
203            final var disco = connection.getManager(DiscoManager.class).get(jid);
204            if (disco != null && disco.hasFeature(namespace)) {
205                return jid.getResource();
206            }
207        }
208        return null;
209    }
210
211    public boolean anyIdentity(final String category, final String type) {
212        final var connection = this.contact.getAccount().getXmppConnection();
213        if (connection == null) {
214            return false;
215        }
216        for (final var jid : getFullJids()) {
217            final var disco = connection.getManager(DiscoManager.class).get(jid);
218            if (disco != null && disco.hasIdentityWithCategoryAndType(category, type)) {
219                return true;
220            }
221        }
222        return false;
223    }
224
225    public Pair<Map<String, String>, Map<String, String>> toTypeAndNameMap() {
226        Map<String, String> typeMap = new HashMap<>();
227        Map<String, String> nameMap = new HashMap<>();
228        final var connection = this.contact.getAccount().getXmppConnection();
229        if (connection == null) {
230            return new Pair<>(typeMap, nameMap);
231        }
232        synchronized (this.presences) {
233            for (final String resource : this.presences.keySet()) {
234                final var serviceDiscoveryResult =
235                        connection
236                                .getManager(DiscoManager.class)
237                                .get(
238                                        Strings.isNullOrEmpty(resource)
239                                                ? contact.getJid().asBareJid()
240                                                : contact.getJid().withResource(resource));
241                if (serviceDiscoveryResult != null
242                        && !serviceDiscoveryResult.getIdentities().isEmpty()) {
243                    final Identity identity =
244                            Iterables.getFirst(serviceDiscoveryResult.getIdentities(), null);
245                    String type = identity.getType();
246                    String name = identity.getIdentityName();
247                    if (type != null) {
248                        typeMap.put(resource, type);
249                    }
250                    if (name != null) {
251                        nameMap.put(resource, nameWithoutVersion(name));
252                    }
253                }
254            }
255        }
256        return new Pair<>(typeMap, nameMap);
257    }
258}