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