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		final ArrayList<String> fingerprints = new ArrayList<String>();
270		try {
271			if (this.keys.has("otr_fingerprints")) {
272				final JSONArray prints = this.keys.getJSONArray("otr_fingerprints");
273				for (int i = 0; i < prints.length(); ++i) {
274					final String print = prints.isNull(i) ? null : prints.getString(i);
275					if (print != null && !print.isEmpty()) {
276						fingerprints.add(prints.getString(i));
277					}
278				}
279			}
280		} catch (final JSONException ignored) {
281
282		}
283		return fingerprints;
284	}
285
286	public boolean addOtrFingerprint(String print) {
287		if (getOtrFingerprints().contains(print)) {
288			return false;
289		}
290		try {
291			JSONArray fingerprints;
292			if (!this.keys.has("otr_fingerprints")) {
293				fingerprints = new JSONArray();
294
295			} else {
296				fingerprints = this.keys.getJSONArray("otr_fingerprints");
297			}
298			fingerprints.put(print);
299			this.keys.put("otr_fingerprints", fingerprints);
300			return true;
301		} catch (final JSONException ignored) {
302			return false;
303		}
304	}
305
306	public long getPgpKeyId() {
307		if (this.keys.has("pgp_keyid")) {
308			try {
309				return this.keys.getLong("pgp_keyid");
310			} catch (JSONException e) {
311				return 0;
312			}
313		} else {
314			return 0;
315		}
316	}
317
318	public void setPgpKeyId(long keyId) {
319		try {
320			this.keys.put("pgp_keyid", keyId);
321		} catch (final JSONException ignored) {
322
323		}
324	}
325
326	public void setOption(int option) {
327		this.subscription |= 1 << option;
328	}
329
330	public void resetOption(int option) {
331		this.subscription &= ~(1 << option);
332	}
333
334	public boolean getOption(int option) {
335		return ((this.subscription & (1 << option)) != 0);
336	}
337
338	public boolean showInRoster() {
339		return (this.getOption(Contact.Options.IN_ROSTER) && (!this
340				.getOption(Contact.Options.DIRTY_DELETE)))
341				|| (this.getOption(Contact.Options.DIRTY_PUSH));
342	}
343
344	public void parseSubscriptionFromElement(Element item) {
345		String ask = item.getAttribute("ask");
346		String subscription = item.getAttribute("subscription");
347
348		if (subscription != null) {
349			switch (subscription) {
350				case "to":
351					this.resetOption(Options.FROM);
352					this.setOption(Options.TO);
353					break;
354				case "from":
355					this.resetOption(Options.TO);
356					this.setOption(Options.FROM);
357					this.resetOption(Options.PREEMPTIVE_GRANT);
358					break;
359				case "both":
360					this.setOption(Options.TO);
361					this.setOption(Options.FROM);
362					this.resetOption(Options.PREEMPTIVE_GRANT);
363					break;
364				case "none":
365					this.resetOption(Options.FROM);
366					this.resetOption(Options.TO);
367					break;
368			}
369		}
370
371		// do NOT override asking if pending push request
372		if (!this.getOption(Contact.Options.DIRTY_PUSH)) {
373			if ((ask != null) && (ask.equals("subscribe"))) {
374				this.setOption(Contact.Options.ASKING);
375			} else {
376				this.resetOption(Contact.Options.ASKING);
377			}
378		}
379	}
380
381	public void parseGroupsFromElement(Element item) {
382		this.groups = new JSONArray();
383		for (Element element : item.getChildren()) {
384			if (element.getName().equals("group") && element.getContent() != null) {
385				this.groups.put(element.getContent());
386			}
387		}
388	}
389
390	public Element asElement() {
391		final Element item = new Element("item");
392		item.setAttribute("jid", this.jid.toString());
393		if (this.serverName != null) {
394			item.setAttribute("name", this.serverName);
395		}
396		for (String group : getGroups()) {
397			item.addChild("group").setContent(group);
398		}
399		return item;
400	}
401
402	@Override
403	public int compareTo(final ListItem another) {
404		return this.getDisplayName().compareToIgnoreCase(
405				another.getDisplayName());
406	}
407
408	public Jid getServer() {
409		return getJid().toDomainJid();
410	}
411
412	public boolean setAvatar(String filename) {
413		if (this.avatar != null && this.avatar.equals(filename)) {
414			return false;
415		} else {
416			this.avatar = filename;
417			return true;
418		}
419	}
420
421	public String getAvatar() {
422		return this.avatar;
423	}
424
425	public boolean deleteOtrFingerprint(String fingerprint) {
426		boolean success = false;
427		try {
428			if (this.keys.has("otr_fingerprints")) {
429				JSONArray newPrints = new JSONArray();
430				JSONArray oldPrints = this.keys
431						.getJSONArray("otr_fingerprints");
432				for (int i = 0; i < oldPrints.length(); ++i) {
433					if (!oldPrints.getString(i).equals(fingerprint)) {
434						newPrints.put(oldPrints.getString(i));
435					} else {
436						success = true;
437					}
438				}
439				this.keys.put("otr_fingerprints", newPrints);
440			}
441			return success;
442		} catch (JSONException e) {
443			return false;
444		}
445	}
446
447	public boolean trusted() {
448		return getOption(Options.FROM) && getOption(Options.TO);
449	}
450
451	public String getShareableUri() {
452		if (getOtrFingerprints().size() >= 1) {
453			String otr = getOtrFingerprints().get(0);
454			return "xmpp:" + getJid().toBareJid().toString() + "?otr-fingerprint=" + otr.replace(" ", "");
455		} else {
456			return "xmpp:" + getJid().toBareJid().toString();
457		}
458	}
459
460	public static class Lastseen {
461		public long time;
462		public String presence;
463
464		public Lastseen() {
465			time = 0;
466			presence = null;
467		}
468
469		public Lastseen(final String presence, final long time) {
470			this.time = time;
471			this.presence = presence;
472		}
473	}
474
475	public class Options {
476		public static final int TO = 0;
477		public static final int FROM = 1;
478		public static final int ASKING = 2;
479		public static final int PREEMPTIVE_GRANT = 3;
480		public static final int IN_ROSTER = 4;
481		public static final int PENDING_SUBSCRIPTION_REQUEST = 5;
482		public static final int DIRTY_PUSH = 6;
483		public static final int DIRTY_DELETE = 7;
484	}
485}