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