1package eu.siacs.conversations.entities;
2
3import android.util.Pair;
4
5import java.util.ArrayList;
6import java.util.HashMap;
7import java.util.Hashtable;
8import java.util.List;
9import java.util.Map;
10
11public class Presences {
12 private final Hashtable<String, Presence> presences = new Hashtable<>();
13
14 private static String nameWithoutVersion(String name) {
15 String[] parts = name.split(" ");
16 if (parts.length > 1 && Character.isDigit(parts[parts.length - 1].charAt(0))) {
17 StringBuilder output = new StringBuilder();
18 for (int i = 0; i < parts.length - 1; ++i) {
19 if (output.length() != 0) {
20 output.append(' ');
21 }
22 output.append(parts[i]);
23 }
24 return output.toString();
25 } else {
26 return name;
27 }
28 }
29
30 public List<Presence> getPresences() {
31 synchronized (this.presences) {
32 return new ArrayList<>(this.presences.values());
33 }
34 }
35
36 public Map<String, Presence> getPresencesMap() {
37 synchronized (this.presences) {
38 return new HashMap<>(this.presences);
39 }
40 }
41
42 public Presence get(String resource) {
43 synchronized (this.presences) {
44 return this.presences.get(resource);
45 }
46 }
47
48 public void updatePresence(String resource, Presence presence) {
49 synchronized (this.presences) {
50 this.presences.put(resource, presence);
51 }
52 }
53
54 public void removePresence(String resource) {
55 synchronized (this.presences) {
56 this.presences.remove(resource);
57 }
58 }
59
60 public void clearPresences() {
61 synchronized (this.presences) {
62 this.presences.clear();
63 }
64 }
65
66 public Presence.Status getShownStatus() {
67 Presence.Status status = Presence.Status.OFFLINE;
68 synchronized (this.presences) {
69 for (Presence p : presences.values()) {
70 if (p.getStatus() == Presence.Status.DND) {
71 return p.getStatus();
72 } else if (p.getStatus().compareTo(status) < 0) {
73 status = p.getStatus();
74 }
75 }
76 }
77 return status;
78 }
79
80 public int size() {
81 synchronized (this.presences) {
82 return presences.size();
83 }
84 }
85
86 public boolean isEmpty() {
87 synchronized (this.presences) {
88 return this.presences.isEmpty();
89 }
90 }
91
92 public String[] toResourceArray() {
93 synchronized (this.presences) {
94 final String[] presencesArray = new String[presences.size()];
95 presences.keySet().toArray(presencesArray);
96 return presencesArray;
97 }
98 }
99
100 public List<PresenceTemplate> asTemplates() {
101 synchronized (this.presences) {
102 ArrayList<PresenceTemplate> templates = new ArrayList<>(presences.size());
103 for (Presence p : presences.values()) {
104 if (p.getMessage() != null && !p.getMessage().trim().isEmpty()) {
105 templates.add(new PresenceTemplate(p.getStatus(), p.getMessage()));
106 }
107 }
108 return templates;
109 }
110 }
111
112 public boolean has(String presence) {
113 synchronized (this.presences) {
114 return presences.containsKey(presence);
115 }
116 }
117
118 public List<String> getStatusMessages() {
119 ArrayList<String> messages = new ArrayList<>();
120 synchronized (this.presences) {
121 for (Presence presence : this.presences.values()) {
122 String message = presence.getMessage() == null ? null : presence.getMessage().trim();
123 if (message != null && !message.isEmpty() && !messages.contains(message)) {
124 messages.add(message);
125 }
126 }
127 }
128 return messages;
129 }
130
131 public boolean allOrNonSupport(String namespace) {
132 synchronized (this.presences) {
133 for (Presence presence : this.presences.values()) {
134 ServiceDiscoveryResult disco = presence.getServiceDiscoveryResult();
135 if (disco == null || !disco.getFeatures().contains(namespace)) {
136 return false;
137 }
138 }
139 }
140 return true;
141 }
142
143 public boolean anySupport(final String namespace) {
144 synchronized (this.presences) {
145 if (this.presences.size() == 0) {
146 return true;
147 }
148 for (Presence presence : this.presences.values()) {
149 ServiceDiscoveryResult disco = presence.getServiceDiscoveryResult();
150 if (disco != null && disco.getFeatures().contains(namespace)) {
151 return true;
152 }
153 }
154 }
155 return false;
156 }
157
158 public String firstWhichSupport(final String namespace) {
159 synchronized (this.presences) {
160 for (Map.Entry<String, Presence> entry : this.presences.entrySet()) {
161 String resource = entry.getKey();
162 Presence presence = entry.getValue();
163 ServiceDiscoveryResult disco = presence.getServiceDiscoveryResult();
164 if (disco != null && disco.getFeatures().contains(namespace)) {
165 return resource;
166 }
167 }
168 }
169 return null;
170 }
171
172 public boolean anyIdentity(final String category, final String type) {
173 synchronized (this.presences) {
174 if (this.presences.size() == 0) {
175 // https://github.com/iNPUTmice/Conversations/issues/4230
176 return false;
177 }
178 for (Presence presence : this.presences.values()) {
179 ServiceDiscoveryResult disco = presence.getServiceDiscoveryResult();
180 if (disco != null && disco.hasIdentity(category, type)) {
181 return true;
182 }
183 }
184 }
185 return false;
186 }
187
188 public Pair<Map<String, String>, Map<String, String>> toTypeAndNameMap() {
189 Map<String, String> typeMap = new HashMap<>();
190 Map<String, String> nameMap = new HashMap<>();
191 synchronized (this.presences) {
192 for (Map.Entry<String, Presence> presenceEntry : this.presences.entrySet()) {
193 String resource = presenceEntry.getKey();
194 Presence presence = presenceEntry.getValue();
195 ServiceDiscoveryResult serviceDiscoveryResult = presence == null ? null : presence.getServiceDiscoveryResult();
196 if (serviceDiscoveryResult != null && serviceDiscoveryResult.getIdentities().size() > 0) {
197 ServiceDiscoveryResult.Identity identity = serviceDiscoveryResult.getIdentities().get(0);
198 String type = identity.getType();
199 String name = identity.getName();
200 if (type != null) {
201 typeMap.put(resource, type);
202 }
203 if (name != null) {
204 nameMap.put(resource, nameWithoutVersion(name));
205 }
206 }
207 }
208 }
209 return new Pair<>(typeMap, nameMap);
210 }
211}