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		return jid.toString().contains(needle) || getDisplayName().toLowerCase().contains(needle) || matchInTag(needle);
153	}
154
155	private boolean matchInTag(String needle) {
156		for (Tag tag : getTags()) {
157			if (tag.getName().toLowerCase().contains(needle)) {
158				return true;
159			}
160		}
161		return false;
162	}
163
164	public ContentValues getContentValues() {
165		ContentValues values = new ContentValues();
166		values.put(ACCOUNT, accountUuid);
167		values.put(SYSTEMNAME, systemName);
168		values.put(SERVERNAME, serverName);
169		values.put(JID, jid.toString());
170		values.put(OPTIONS, subscription);
171		values.put(SYSTEMACCOUNT, systemAccount);
172		values.put(PHOTOURI, photoUri);
173		values.put(KEYS, keys.toString());
174		values.put(AVATAR, avatar);
175		values.put(LAST_PRESENCE, lastseen.presence);
176		values.put(LAST_TIME, lastseen.time);
177		values.put(GROUPS, groups.toString());
178		return values;
179	}
180
181	public int getSubscription() {
182		return this.subscription;
183	}
184
185	public Account getAccount() {
186		return this.account;
187	}
188
189	public void setAccount(Account account) {
190		this.account = account;
191		this.accountUuid = account.getUuid();
192	}
193
194	public Presences getPresences() {
195		return this.presences;
196	}
197
198	public void setPresences(Presences pres) {
199		this.presences = pres;
200	}
201
202	public void updatePresence(String resource, int status) {
203		this.presences.updatePresence(resource, status);
204	}
205
206	public void removePresence(String resource) {
207		this.presences.removePresence(resource);
208	}
209
210	public void clearPresences() {
211		this.presences.clearPresences();
212		this.resetOption(Options.PENDING_SUBSCRIPTION_REQUEST);
213	}
214
215	public int getMostAvailableStatus() {
216		return this.presences.getMostAvailableStatus();
217	}
218
219	public void setPhotoUri(String uri) {
220		this.photoUri = uri;
221	}
222
223	public void setServerName(String serverName) {
224		this.serverName = serverName;
225	}
226
227	public void setSystemName(String systemName) {
228		this.systemName = systemName;
229	}
230
231	public void setPresenceName(String presenceName) {
232		this.presenceName = presenceName;
233	}
234
235	public String getSystemAccount() {
236		return systemAccount;
237	}
238
239	public void setSystemAccount(String account) {
240		this.systemAccount = account;
241	}
242
243	public List<String> getGroups() {
244		ArrayList<String> groups = new ArrayList<String>();
245		for (int i = 0; i < this.groups.length(); ++i) {
246			try {
247				groups.add(this.groups.getString(i));
248			} catch (final JSONException ignored) {
249			}
250		}
251		return groups;
252	}
253
254	public ArrayList<String> getOtrFingerprints() {
255		ArrayList<String> fingerprints = new ArrayList<String>();
256		try {
257			if (this.keys.has("otr_fingerprints")) {
258				JSONArray prints = this.keys
259						.getJSONArray("otr_fingerprints");
260				for (int i = 0; i < prints.length(); ++i) {
261					fingerprints.add(prints.getString(i));
262				}
263			}
264		} catch (final JSONException ignored) {
265
266		}
267		return fingerprints;
268	}
269
270	public boolean addOtrFingerprint(String print) {
271		if (getOtrFingerprints().contains(print)) {
272			return false;
273		}
274		try {
275			JSONArray fingerprints;
276			if (!this.keys.has("otr_fingerprints")) {
277				fingerprints = new JSONArray();
278
279			} else {
280				fingerprints = this.keys.getJSONArray("otr_fingerprints");
281			}
282			fingerprints.put(print);
283			this.keys.put("otr_fingerprints", fingerprints);
284			return true;
285		} catch (final JSONException ignored) {
286			return false;
287		}
288	}
289
290	public long getPgpKeyId() {
291		if (this.keys.has("pgp_keyid")) {
292			try {
293				return this.keys.getLong("pgp_keyid");
294			} catch (JSONException e) {
295				return 0;
296			}
297		} else {
298			return 0;
299		}
300	}
301
302	public void setPgpKeyId(long keyId) {
303		try {
304			this.keys.put("pgp_keyid", keyId);
305		} catch (final JSONException ignored) {
306
307		}
308	}
309
310	public void setOption(int option) {
311		this.subscription |= 1 << option;
312	}
313
314	public void resetOption(int option) {
315		this.subscription &= ~(1 << option);
316	}
317
318	public boolean getOption(int option) {
319		return ((this.subscription & (1 << option)) != 0);
320	}
321
322	public boolean showInRoster() {
323		return (this.getOption(Contact.Options.IN_ROSTER) && (!this
324				.getOption(Contact.Options.DIRTY_DELETE)))
325				|| (this.getOption(Contact.Options.DIRTY_PUSH));
326	}
327
328	public void parseSubscriptionFromElement(Element item) {
329		String ask = item.getAttribute("ask");
330		String subscription = item.getAttribute("subscription");
331
332		if (subscription != null) {
333			switch (subscription) {
334				case "to":
335					this.resetOption(Options.FROM);
336					this.setOption(Options.TO);
337					break;
338				case "from":
339					this.resetOption(Options.TO);
340					this.setOption(Options.FROM);
341					this.resetOption(Options.PREEMPTIVE_GRANT);
342					break;
343				case "both":
344					this.setOption(Options.TO);
345					this.setOption(Options.FROM);
346					this.resetOption(Options.PREEMPTIVE_GRANT);
347					break;
348				case "none":
349					this.resetOption(Options.FROM);
350					this.resetOption(Options.TO);
351					break;
352			}
353		}
354
355		// do NOT override asking if pending push request
356		if (!this.getOption(Contact.Options.DIRTY_PUSH)) {
357			if ((ask != null) && (ask.equals("subscribe"))) {
358				this.setOption(Contact.Options.ASKING);
359			} else {
360				this.resetOption(Contact.Options.ASKING);
361			}
362		}
363	}
364
365	public void parseGroupsFromElement(Element item) {
366		this.groups = new JSONArray();
367		for (Element element : item.getChildren()) {
368			if (element.getName().equals("group") && element.getContent() != null) {
369				this.groups.put(element.getContent());
370			}
371		}
372	}
373
374	public Element asElement() {
375		final Element item = new Element("item");
376		item.setAttribute("jid", this.jid.toString());
377		if (this.serverName != null) {
378			item.setAttribute("name", this.serverName);
379		}
380		for (String group : getGroups()) {
381			item.addChild("group").setContent(group);
382		}
383		return item;
384	}
385
386	@Override
387	public int compareTo(final ListItem another) {
388		return this.getDisplayName().compareToIgnoreCase(
389				another.getDisplayName());
390	}
391
392	public Jid getServer() {
393		return getJid().toDomainJid();
394	}
395
396	public boolean setAvatar(String filename) {
397		if (this.avatar != null && this.avatar.equals(filename)) {
398			return false;
399		} else {
400			this.avatar = filename;
401			return true;
402		}
403	}
404
405	public String getAvatar() {
406		return this.avatar;
407	}
408
409	public boolean deleteOtrFingerprint(String fingerprint) {
410		boolean success = false;
411		try {
412			if (this.keys.has("otr_fingerprints")) {
413				JSONArray newPrints = new JSONArray();
414				JSONArray oldPrints = this.keys
415						.getJSONArray("otr_fingerprints");
416				for (int i = 0; i < oldPrints.length(); ++i) {
417					if (!oldPrints.getString(i).equals(fingerprint)) {
418						newPrints.put(oldPrints.getString(i));
419					} else {
420						success = true;
421					}
422				}
423				this.keys.put("otr_fingerprints", newPrints);
424			}
425			return success;
426		} catch (JSONException e) {
427			return false;
428		}
429	}
430
431	public boolean trusted() {
432		return getOption(Options.FROM) && getOption(Options.TO);
433	}
434
435	public String getShareableUri() {
436		if (getOtrFingerprints().size() >= 1) {
437			String otr = getOtrFingerprints().get(0);
438			return "xmpp:" + getJid().toBareJid().toString() + "?otr-fingerprint=" + otr.replace(" ", "");
439		} else {
440			return "xmpp:" + getJid().toBareJid().toString();
441		}
442	}
443
444	public static class Lastseen {
445		public long time;
446		public String presence;
447
448		public Lastseen() {
449			time = 0;
450			presence = null;
451		}
452
453		public Lastseen(final String presence, final long time) {
454			this.time = time;
455			this.presence = presence;
456		}
457	}
458
459	public class Options {
460		public static final int TO = 0;
461		public static final int FROM = 1;
462		public static final int ASKING = 2;
463		public static final int PREEMPTIVE_GRANT = 3;
464		public static final int IN_ROSTER = 4;
465		public static final int PENDING_SUBSCRIPTION_REQUEST = 5;
466		public static final int DIRTY_PUSH = 6;
467		public static final int DIRTY_DELETE = 7;
468	}
469}