Contact.java

  1package eu.siacs.conversations.entities;
  2
  3import android.content.ContentValues;
  4import android.database.Cursor;
  5
  6import org.json.JSONArray;
  7import org.json.JSONException;
  8import org.json.JSONObject;
  9
 10import java.util.ArrayList;
 11import java.util.List;
 12
 13import eu.siacs.conversations.utils.UIHelper;
 14import eu.siacs.conversations.xml.Element;
 15import eu.siacs.conversations.xmpp.jid.InvalidJidException;
 16import eu.siacs.conversations.xmpp.jid.Jid;
 17
 18public class Contact implements ListItem {
 19	public static final String TABLENAME = "contacts";
 20
 21	public static final String SYSTEMNAME = "systemname";
 22	public static final String SERVERNAME = "servername";
 23	public static final String JID = "jid";
 24	public static final String OPTIONS = "options";
 25	public static final String SYSTEMACCOUNT = "systemaccount";
 26	public static final String PHOTOURI = "photouri";
 27	public static final String KEYS = "pgpkey";
 28	public static final String ACCOUNT = "accountUuid";
 29	public static final String AVATAR = "avatar";
 30	public static final String LAST_PRESENCE = "last_presence";
 31	public static final String LAST_TIME = "last_time";
 32	public static final String GROUPS = "groups";
 33	public Lastseen lastseen = new Lastseen();
 34	protected String accountUuid;
 35	protected String systemName;
 36	protected String serverName;
 37	protected String presenceName;
 38	protected Jid jid;
 39	protected int subscription = 0;
 40	protected String systemAccount;
 41	protected String photoUri;
 42	protected String avatar;
 43	protected JSONObject keys = new JSONObject();
 44	protected JSONArray groups = new JSONArray();
 45	protected Presences presences = new Presences();
 46	protected Account account;
 47
 48	public Contact(final String account, final String systemName, final String serverName,
 49				   final Jid jid, final int subscription, final String photoUri,
 50				   final String systemAccount, final String keys, final String avatar, final Lastseen lastseen, final String groups) {
 51		this.accountUuid = account;
 52		this.systemName = systemName;
 53		this.serverName = serverName;
 54		this.jid = jid;
 55		this.subscription = subscription;
 56		this.photoUri = photoUri;
 57		this.systemAccount = systemAccount;
 58		try {
 59			this.keys = (keys == null ? new JSONObject("") : new JSONObject(keys));
 60		} catch (JSONException e) {
 61			this.keys = new JSONObject();
 62		}
 63		this.avatar = avatar;
 64		try {
 65			this.groups = (groups == null ? new JSONArray() : new JSONArray(groups));
 66		} catch (JSONException e) {
 67			this.groups = new JSONArray();
 68		}
 69		this.lastseen = lastseen;
 70	}
 71
 72	public Contact(final Jid jid) {
 73		this.jid = jid;
 74	}
 75
 76	public static Contact fromCursor(final Cursor cursor) {
 77		final Lastseen lastseen = new Lastseen(
 78				cursor.getString(cursor.getColumnIndex(LAST_PRESENCE)),
 79				cursor.getLong(cursor.getColumnIndex(LAST_TIME)));
 80		final Jid jid;
 81		try {
 82			jid = Jid.fromString(cursor.getString(cursor.getColumnIndex(JID)));
 83		} catch (final InvalidJidException e) {
 84			// TODO: Borked DB... handle this somehow?
 85			return null;
 86		}
 87		return new Contact(cursor.getString(cursor.getColumnIndex(ACCOUNT)),
 88				cursor.getString(cursor.getColumnIndex(SYSTEMNAME)),
 89				cursor.getString(cursor.getColumnIndex(SERVERNAME)),
 90				jid,
 91				cursor.getInt(cursor.getColumnIndex(OPTIONS)),
 92				cursor.getString(cursor.getColumnIndex(PHOTOURI)),
 93				cursor.getString(cursor.getColumnIndex(SYSTEMACCOUNT)),
 94				cursor.getString(cursor.getColumnIndex(KEYS)),
 95				cursor.getString(cursor.getColumnIndex(AVATAR)),
 96				lastseen,
 97				cursor.getString(cursor.getColumnIndex(GROUPS)));
 98	}
 99
100	public String getDisplayName() {
101		if (this.systemName != null) {
102			return this.systemName;
103		} else if (this.serverName != null) {
104			return this.serverName;
105		} else if (this.presenceName != null) {
106			return this.presenceName;
107		} else if (jid.hasLocalpart()) {
108			return jid.getLocalpart();
109		} else {
110			return jid.getDomainpart();
111		}
112	}
113
114	public String getProfilePhoto() {
115		return this.photoUri;
116	}
117
118	public Jid getJid() {
119		return jid;
120	}
121
122	@Override
123	public List<Tag> getTags() {
124		ArrayList<Tag> tags = new ArrayList<Tag>();
125		for (String group : getGroups()) {
126			tags.add(new Tag(group, UIHelper.getColorForName(group)));
127		}
128		int status = getMostAvailableStatus();
129		switch (getMostAvailableStatus()) {
130			case Presences.CHAT:
131			case Presences.ONLINE:
132				tags.add(new Tag("online", 0xff259b24));
133				break;
134			case Presences.AWAY:
135				tags.add(new Tag("away", 0xffff9800));
136				break;
137			case Presences.XA:
138				tags.add(new Tag("not available", 0xffe51c23));
139				break;
140			case Presences.DND:
141				tags.add(new Tag("dnd", 0xffe51c23));
142				break;
143		}
144		return tags;
145	}
146
147	public boolean match(String needle) {
148		if (needle == null) {
149			return true;
150		}
151		needle = needle.toLowerCase();
152		String[] parts = needle.split("\\s+");
153		if (parts.length > 1) {
154			for(int i = 0; i < parts.length; ++i) {
155				if (!match(parts[i])) {
156					return false;
157				}
158			}
159			return true;
160		} else {
161			return jid.toString().contains(needle) || getDisplayName().toLowerCase().contains(needle) || matchInTag(needle);
162		}
163	}
164
165	private boolean matchInTag(String needle) {
166		for (Tag tag : getTags()) {
167			if (tag.getName().toLowerCase().contains(needle)) {
168				return true;
169			}
170		}
171		return false;
172	}
173
174	public ContentValues getContentValues() {
175		ContentValues values = new ContentValues();
176		values.put(ACCOUNT, accountUuid);
177		values.put(SYSTEMNAME, systemName);
178		values.put(SERVERNAME, serverName);
179		values.put(JID, jid.toString());
180		values.put(OPTIONS, subscription);
181		values.put(SYSTEMACCOUNT, systemAccount);
182		values.put(PHOTOURI, photoUri);
183		values.put(KEYS, keys.toString());
184		values.put(AVATAR, avatar);
185		values.put(LAST_PRESENCE, lastseen.presence);
186		values.put(LAST_TIME, lastseen.time);
187		values.put(GROUPS, groups.toString());
188		return values;
189	}
190
191	public int getSubscription() {
192		return this.subscription;
193	}
194
195	public Account getAccount() {
196		return this.account;
197	}
198
199	public void setAccount(Account account) {
200		this.account = account;
201		this.accountUuid = account.getUuid();
202	}
203
204	public Presences getPresences() {
205		return this.presences;
206	}
207
208	public void setPresences(Presences pres) {
209		this.presences = pres;
210	}
211
212	public void updatePresence(String resource, int status) {
213		this.presences.updatePresence(resource, status);
214	}
215
216	public void removePresence(String resource) {
217		this.presences.removePresence(resource);
218	}
219
220	public void clearPresences() {
221		this.presences.clearPresences();
222		this.resetOption(Options.PENDING_SUBSCRIPTION_REQUEST);
223	}
224
225	public int getMostAvailableStatus() {
226		return this.presences.getMostAvailableStatus();
227	}
228
229	public void setPhotoUri(String uri) {
230		this.photoUri = uri;
231	}
232
233	public void setServerName(String serverName) {
234		this.serverName = serverName;
235	}
236
237	public void setSystemName(String systemName) {
238		this.systemName = systemName;
239	}
240
241	public void setPresenceName(String presenceName) {
242		this.presenceName = presenceName;
243	}
244
245	public String getSystemAccount() {
246		return systemAccount;
247	}
248
249	public void setSystemAccount(String account) {
250		this.systemAccount = account;
251	}
252
253	public List<String> getGroups() {
254		ArrayList<String> groups = new ArrayList<String>();
255		for (int i = 0; i < this.groups.length(); ++i) {
256			try {
257				groups.add(this.groups.getString(i));
258			} catch (final JSONException ignored) {
259			}
260		}
261		return groups;
262	}
263
264	public ArrayList<String> getOtrFingerprints() {
265		ArrayList<String> fingerprints = new ArrayList<String>();
266		try {
267			if (this.keys.has("otr_fingerprints")) {
268				JSONArray prints = this.keys
269						.getJSONArray("otr_fingerprints");
270				for (int i = 0; i < prints.length(); ++i) {
271					fingerprints.add(prints.getString(i));
272				}
273			}
274		} catch (final JSONException ignored) {
275
276		}
277		return fingerprints;
278	}
279
280	public boolean addOtrFingerprint(String print) {
281		if (getOtrFingerprints().contains(print)) {
282			return false;
283		}
284		try {
285			JSONArray fingerprints;
286			if (!this.keys.has("otr_fingerprints")) {
287				fingerprints = new JSONArray();
288
289			} else {
290				fingerprints = this.keys.getJSONArray("otr_fingerprints");
291			}
292			fingerprints.put(print);
293			this.keys.put("otr_fingerprints", fingerprints);
294			return true;
295		} catch (final JSONException ignored) {
296			return false;
297		}
298	}
299
300	public long getPgpKeyId() {
301		if (this.keys.has("pgp_keyid")) {
302			try {
303				return this.keys.getLong("pgp_keyid");
304			} catch (JSONException e) {
305				return 0;
306			}
307		} else {
308			return 0;
309		}
310	}
311
312	public void setPgpKeyId(long keyId) {
313		try {
314			this.keys.put("pgp_keyid", keyId);
315		} catch (final JSONException ignored) {
316
317		}
318	}
319
320	public void setOption(int option) {
321		this.subscription |= 1 << option;
322	}
323
324	public void resetOption(int option) {
325		this.subscription &= ~(1 << option);
326	}
327
328	public boolean getOption(int option) {
329		return ((this.subscription & (1 << option)) != 0);
330	}
331
332	public boolean showInRoster() {
333		return (this.getOption(Contact.Options.IN_ROSTER) && (!this
334				.getOption(Contact.Options.DIRTY_DELETE)))
335				|| (this.getOption(Contact.Options.DIRTY_PUSH));
336	}
337
338	public void parseSubscriptionFromElement(Element item) {
339		String ask = item.getAttribute("ask");
340		String subscription = item.getAttribute("subscription");
341
342		if (subscription != null) {
343			switch (subscription) {
344				case "to":
345					this.resetOption(Options.FROM);
346					this.setOption(Options.TO);
347					break;
348				case "from":
349					this.resetOption(Options.TO);
350					this.setOption(Options.FROM);
351					this.resetOption(Options.PREEMPTIVE_GRANT);
352					break;
353				case "both":
354					this.setOption(Options.TO);
355					this.setOption(Options.FROM);
356					this.resetOption(Options.PREEMPTIVE_GRANT);
357					break;
358				case "none":
359					this.resetOption(Options.FROM);
360					this.resetOption(Options.TO);
361					break;
362			}
363		}
364
365		// do NOT override asking if pending push request
366		if (!this.getOption(Contact.Options.DIRTY_PUSH)) {
367			if ((ask != null) && (ask.equals("subscribe"))) {
368				this.setOption(Contact.Options.ASKING);
369			} else {
370				this.resetOption(Contact.Options.ASKING);
371			}
372		}
373	}
374
375	public void parseGroupsFromElement(Element item) {
376		this.groups = new JSONArray();
377		for (Element element : item.getChildren()) {
378			if (element.getName().equals("group") && element.getContent() != null) {
379				this.groups.put(element.getContent());
380			}
381		}
382	}
383
384	public Element asElement() {
385		final Element item = new Element("item");
386		item.setAttribute("jid", this.jid.toString());
387		if (this.serverName != null) {
388			item.setAttribute("name", this.serverName);
389		}
390		for (String group : getGroups()) {
391			item.addChild("group").setContent(group);
392		}
393		return item;
394	}
395
396	@Override
397	public int compareTo(final ListItem another) {
398		return this.getDisplayName().compareToIgnoreCase(
399				another.getDisplayName());
400	}
401
402	public Jid getServer() {
403		return getJid().toDomainJid();
404	}
405
406	public boolean setAvatar(String filename) {
407		if (this.avatar != null && this.avatar.equals(filename)) {
408			return false;
409		} else {
410			this.avatar = filename;
411			return true;
412		}
413	}
414
415	public String getAvatar() {
416		return this.avatar;
417	}
418
419	public boolean deleteOtrFingerprint(String fingerprint) {
420		boolean success = false;
421		try {
422			if (this.keys.has("otr_fingerprints")) {
423				JSONArray newPrints = new JSONArray();
424				JSONArray oldPrints = this.keys
425						.getJSONArray("otr_fingerprints");
426				for (int i = 0; i < oldPrints.length(); ++i) {
427					if (!oldPrints.getString(i).equals(fingerprint)) {
428						newPrints.put(oldPrints.getString(i));
429					} else {
430						success = true;
431					}
432				}
433				this.keys.put("otr_fingerprints", newPrints);
434			}
435			return success;
436		} catch (JSONException e) {
437			return false;
438		}
439	}
440
441	public boolean trusted() {
442		return getOption(Options.FROM) && getOption(Options.TO);
443	}
444
445	public String getShareableUri() {
446		if (getOtrFingerprints().size() >= 1) {
447			String otr = getOtrFingerprints().get(0);
448			return "xmpp:" + getJid().toBareJid().toString() + "?otr-fingerprint=" + otr.replace(" ", "");
449		} else {
450			return "xmpp:" + getJid().toBareJid().toString();
451		}
452	}
453
454	public static class Lastseen {
455		public long time;
456		public String presence;
457
458		public Lastseen() {
459			time = 0;
460			presence = null;
461		}
462
463		public Lastseen(final String presence, final long time) {
464			this.time = time;
465			this.presence = presence;
466		}
467	}
468
469	public class Options {
470		public static final int TO = 0;
471		public static final int FROM = 1;
472		public static final int ASKING = 2;
473		public static final int PREEMPTIVE_GRANT = 3;
474		public static final int IN_ROSTER = 4;
475		public static final int PENDING_SUBSCRIPTION_REQUEST = 5;
476		public static final int DIRTY_PUSH = 6;
477		public static final int DIRTY_DELETE = 7;
478	}
479}