ContactDetailsActivity.java

  1package eu.siacs.conversations.ui;
  2
  3import java.util.Iterator;
  4
  5import org.openintents.openpgp.util.OpenPgpUtils;
  6
  7import android.app.AlertDialog;
  8import android.app.PendingIntent;
  9import android.content.Context;
 10import android.content.DialogInterface;
 11import android.content.Intent;
 12import android.content.IntentSender.SendIntentException;
 13import android.net.Uri;
 14import android.os.Bundle;
 15import android.provider.ContactsContract.CommonDataKinds;
 16import android.provider.ContactsContract.Contacts;
 17import android.provider.ContactsContract.Intents;
 18import android.view.LayoutInflater;
 19import android.view.Menu;
 20import android.view.MenuItem;
 21import android.view.View;
 22import android.view.View.OnClickListener;
 23import android.widget.CheckBox;
 24import android.widget.CompoundButton.OnCheckedChangeListener;
 25import android.widget.CompoundButton;
 26import android.widget.ImageButton;
 27import android.widget.LinearLayout;
 28import android.widget.QuickContactBadge;
 29import android.widget.TextView;
 30import eu.siacs.conversations.R;
 31import eu.siacs.conversations.crypto.PgpEngine;
 32import eu.siacs.conversations.entities.Account;
 33import eu.siacs.conversations.entities.Contact;
 34import eu.siacs.conversations.entities.Presences;
 35import eu.siacs.conversations.services.XmppConnectionService.OnAccountUpdate;
 36import eu.siacs.conversations.services.XmppConnectionService.OnRosterUpdate;
 37import eu.siacs.conversations.utils.UIHelper;
 38import eu.siacs.conversations.xmpp.jid.InvalidJidException;
 39import eu.siacs.conversations.xmpp.jid.Jid;
 40
 41public class ContactDetailsActivity extends XmppActivity {
 42	public static final String ACTION_VIEW_CONTACT = "view_contact";
 43
 44	private Contact contact;
 45
 46	private Jid accountJid;
 47	private Jid contactJid;
 48
 49	private TextView contactJidTv;
 50	private TextView accountJidTv;
 51	private TextView status;
 52	private TextView lastseen;
 53	private CheckBox send;
 54	private CheckBox receive;
 55	private QuickContactBadge badge;
 56
 57	private DialogInterface.OnClickListener removeFromRoster = new DialogInterface.OnClickListener() {
 58
 59		@Override
 60		public void onClick(DialogInterface dialog, int which) {
 61			ContactDetailsActivity.this.xmppConnectionService
 62					.deleteContactOnServer(contact);
 63			ContactDetailsActivity.this.finish();
 64		}
 65	};
 66
 67	private DialogInterface.OnClickListener addToPhonebook = new DialogInterface.OnClickListener() {
 68
 69		@Override
 70		public void onClick(DialogInterface dialog, int which) {
 71			Intent intent = new Intent(Intent.ACTION_INSERT_OR_EDIT);
 72			intent.setType(Contacts.CONTENT_ITEM_TYPE);
 73			intent.putExtra(Intents.Insert.IM_HANDLE, contact.getJid().toString());
 74			intent.putExtra(Intents.Insert.IM_PROTOCOL,
 75					CommonDataKinds.Im.PROTOCOL_JABBER);
 76			intent.putExtra("finishActivityOnSaveCompleted", true);
 77			ContactDetailsActivity.this.startActivityForResult(intent, 0);
 78		}
 79	};
 80	private OnClickListener onBadgeClick = new OnClickListener() {
 81
 82		@Override
 83		public void onClick(View v) {
 84			AlertDialog.Builder builder = new AlertDialog.Builder(
 85					ContactDetailsActivity.this);
 86			builder.setTitle(getString(R.string.action_add_phone_book));
 87			builder.setMessage(getString(R.string.add_phone_book_text,
 88					contact.getJid()));
 89			builder.setNegativeButton(getString(R.string.cancel), null);
 90			builder.setPositiveButton(getString(R.string.add), addToPhonebook);
 91			builder.create().show();
 92		}
 93	};
 94
 95	private LinearLayout keys;
 96
 97	private OnRosterUpdate rosterUpdate = new OnRosterUpdate() {
 98
 99		@Override
100		public void onRosterUpdate() {
101			runOnUiThread(new Runnable() {
102
103				@Override
104				public void run() {
105					populateView();
106				}
107			});
108		}
109	};
110
111	private OnCheckedChangeListener mOnSendCheckedChange = new OnCheckedChangeListener() {
112
113		@Override
114		public void onCheckedChanged(CompoundButton buttonView,
115				boolean isChecked) {
116			if (isChecked) {
117				if (contact
118						.getOption(Contact.Options.PENDING_SUBSCRIPTION_REQUEST)) {
119					xmppConnectionService.sendPresencePacket(contact
120							.getAccount(),
121							xmppConnectionService.getPresenceGenerator()
122									.sendPresenceUpdatesTo(contact));
123				} else {
124					contact.setOption(Contact.Options.PREEMPTIVE_GRANT);
125				}
126			} else {
127				contact.resetOption(Contact.Options.PREEMPTIVE_GRANT);
128				xmppConnectionService.sendPresencePacket(contact.getAccount(),
129						xmppConnectionService.getPresenceGenerator()
130								.stopPresenceUpdatesTo(contact));
131			}
132		}
133	};
134
135	private OnCheckedChangeListener mOnReceiveCheckedChange = new OnCheckedChangeListener() {
136
137		@Override
138		public void onCheckedChanged(CompoundButton buttonView,
139				boolean isChecked) {
140			if (isChecked) {
141				xmppConnectionService.sendPresencePacket(contact.getAccount(),
142						xmppConnectionService.getPresenceGenerator()
143								.requestPresenceUpdatesFrom(contact));
144			} else {
145				xmppConnectionService.sendPresencePacket(contact.getAccount(),
146						xmppConnectionService.getPresenceGenerator()
147								.stopPresenceUpdatesFrom(contact));
148			}
149		}
150	};
151
152	private OnAccountUpdate accountUpdate = new OnAccountUpdate() {
153
154		@Override
155		public void onAccountUpdate() {
156			runOnUiThread(new Runnable() {
157
158				@Override
159				public void run() {
160					populateView();
161				}
162			});
163		}
164	};
165
166	@Override
167	protected String getShareableUri() {
168		if (contact!=null) {
169			return "xmpp:"+contact.getJid();
170		} else {
171			return "";
172		}
173	}
174
175	@Override
176	protected void onCreate(Bundle savedInstanceState) {
177		super.onCreate(savedInstanceState);
178		if (getIntent().getAction().equals(ACTION_VIEW_CONTACT)) {
179            try {
180                this.accountJid = Jid.fromString(getIntent().getExtras().getString("account"));
181            } catch (final InvalidJidException ignored) {
182            }
183            try {
184                this.contactJid = Jid.fromString(getIntent().getExtras().getString("contact"));
185            } catch (final InvalidJidException ignored) {
186            }
187        }
188		setContentView(R.layout.activity_contact_details);
189
190		contactJidTv = (TextView) findViewById(R.id.details_contactjid);
191		accountJidTv = (TextView) findViewById(R.id.details_account);
192		status = (TextView) findViewById(R.id.details_contactstatus);
193		lastseen = (TextView) findViewById(R.id.details_lastseen);
194		send = (CheckBox) findViewById(R.id.details_send_presence);
195		receive = (CheckBox) findViewById(R.id.details_receive_presence);
196		badge = (QuickContactBadge) findViewById(R.id.details_contact_badge);
197		keys = (LinearLayout) findViewById(R.id.details_contact_keys);
198		getActionBar().setHomeButtonEnabled(true);
199		getActionBar().setDisplayHomeAsUpEnabled(true);
200
201	}
202
203	@Override
204	public boolean onOptionsItemSelected(MenuItem menuItem) {
205		AlertDialog.Builder builder = new AlertDialog.Builder(this);
206		builder.setNegativeButton(getString(R.string.cancel), null);
207		switch (menuItem.getItemId()) {
208		case android.R.id.home:
209			finish();
210			break;
211		case R.id.action_delete_contact:
212			builder.setTitle(getString(R.string.action_delete_contact))
213					.setMessage(
214							getString(R.string.remove_contact_text,
215									contact.getJid()))
216					.setPositiveButton(getString(R.string.delete),
217							removeFromRoster).create().show();
218			break;
219		case R.id.action_edit_contact:
220			if (contact.getSystemAccount() == null) {
221				quickEdit(contact.getDisplayName(), new OnValueEdited() {
222
223					@Override
224					public void onValueEdited(String value) {
225						contact.setServerName(value);
226						ContactDetailsActivity.this.xmppConnectionService
227								.pushContactToServer(contact);
228						populateView();
229					}
230				});
231			} else {
232				Intent intent = new Intent(Intent.ACTION_EDIT);
233				String[] systemAccount = contact.getSystemAccount().split("#");
234				long id = Long.parseLong(systemAccount[0]);
235				Uri uri = Contacts.getLookupUri(id, systemAccount[1]);
236				intent.setDataAndType(uri, Contacts.CONTENT_ITEM_TYPE);
237				intent.putExtra("finishActivityOnSaveCompleted", true);
238				startActivity(intent);
239			}
240			break;
241		}
242		return super.onOptionsItemSelected(menuItem);
243	}
244
245	@Override
246	public boolean onCreateOptionsMenu(Menu menu) {
247		getMenuInflater().inflate(R.menu.contact_details, menu);
248		return true;
249	}
250
251	private void populateView() {
252		send.setOnCheckedChangeListener(null);
253		receive.setOnCheckedChangeListener(null);
254		setTitle(contact.getDisplayName());
255		if (contact.getOption(Contact.Options.FROM)) {
256			send.setText(R.string.send_presence_updates);
257			send.setChecked(true);
258		} else if (contact
259				.getOption(Contact.Options.PENDING_SUBSCRIPTION_REQUEST)) {
260			send.setChecked(false);
261			send.setText(R.string.send_presence_updates);
262		} else {
263			send.setText(R.string.preemptively_grant);
264			if (contact.getOption(Contact.Options.PREEMPTIVE_GRANT)) {
265				send.setChecked(true);
266			} else {
267				send.setChecked(false);
268			}
269		}
270		if (contact.getOption(Contact.Options.TO)) {
271			receive.setText(R.string.receive_presence_updates);
272			receive.setChecked(true);
273		} else {
274			receive.setText(R.string.ask_for_presence_updates);
275			if (contact.getOption(Contact.Options.ASKING)) {
276				receive.setChecked(true);
277			} else {
278				receive.setChecked(false);
279			}
280		}
281		if (contact.getAccount().getStatus() == Account.STATUS_ONLINE) {
282			receive.setEnabled(true);
283			send.setEnabled(true);
284		} else {
285			receive.setEnabled(false);
286			send.setEnabled(false);
287		}
288
289		send.setOnCheckedChangeListener(this.mOnSendCheckedChange);
290		receive.setOnCheckedChangeListener(this.mOnReceiveCheckedChange);
291
292		lastseen.setText(UIHelper.lastseen(getApplicationContext(),
293				contact.lastseen.time));
294
295		switch (contact.getMostAvailableStatus()) {
296		case Presences.CHAT:
297			status.setText(R.string.contact_status_free_to_chat);
298			status.setTextColor(mColorGreen);
299			break;
300		case Presences.ONLINE:
301			status.setText(R.string.contact_status_online);
302			status.setTextColor(mColorGreen);
303			break;
304		case Presences.AWAY:
305			status.setText(R.string.contact_status_away);
306			status.setTextColor(mColorOrange);
307			break;
308		case Presences.XA:
309			status.setText(R.string.contact_status_extended_away);
310			status.setTextColor(mColorOrange);
311			break;
312		case Presences.DND:
313			status.setText(R.string.contact_status_do_not_disturb);
314			status.setTextColor(mColorRed);
315			break;
316		case Presences.OFFLINE:
317			status.setText(R.string.contact_status_offline);
318			status.setTextColor(mSecondaryTextColor);
319			break;
320		default:
321			status.setText(R.string.contact_status_offline);
322			status.setTextColor(mSecondaryTextColor);
323			break;
324		}
325		if (contact.getPresences().size() > 1) {
326			contactJidTv.setText(contact.getJid() + " ("
327					+ contact.getPresences().size() + ")");
328		} else {
329			contactJidTv.setText(contact.getJid().toString());
330		}
331		accountJidTv.setText(getString(R.string.using_account, contact
332				.getAccount().getJid().toBareJid()));
333		prepareContactBadge(badge, contact);
334		if (contact.getSystemAccount() == null) {
335			badge.setOnClickListener(onBadgeClick);
336		}
337
338		keys.removeAllViews();
339		boolean hasKeys = false;
340		LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
341		for (Iterator<String> iterator = contact.getOtrFingerprints()
342				.iterator(); iterator.hasNext();) {
343			hasKeys = true;
344			final String otrFingerprint = iterator.next();
345			View view = inflater.inflate(R.layout.contact_key, keys, false);
346			TextView key = (TextView) view.findViewById(R.id.key);
347			TextView keyType = (TextView) view.findViewById(R.id.key_type);
348			ImageButton remove = (ImageButton) view
349					.findViewById(R.id.button_remove);
350			remove.setVisibility(View.VISIBLE);
351			keyType.setText("OTR Fingerprint");
352			key.setText(otrFingerprint);
353			keys.addView(view);
354			remove.setOnClickListener(new OnClickListener() {
355
356				@Override
357				public void onClick(View v) {
358					confirmToDeleteFingerprint(otrFingerprint);
359				}
360			});
361		}
362		if (contact.getPgpKeyId() != 0) {
363			hasKeys = true;
364			View view = inflater.inflate(R.layout.contact_key, keys, false);
365			TextView key = (TextView) view.findViewById(R.id.key);
366			TextView keyType = (TextView) view.findViewById(R.id.key_type);
367			keyType.setText("PGP Key ID");
368			key.setText(OpenPgpUtils.convertKeyIdToHex(contact.getPgpKeyId()));
369			view.setOnClickListener(new OnClickListener() {
370
371				@Override
372				public void onClick(View v) {
373					PgpEngine pgp = ContactDetailsActivity.this.xmppConnectionService
374							.getPgpEngine();
375					if (pgp != null) {
376						PendingIntent intent = pgp.getIntentForKey(contact);
377						if (intent != null) {
378							try {
379								startIntentSenderForResult(
380										intent.getIntentSender(), 0, null, 0,
381										0, 0);
382							} catch (SendIntentException e) {
383
384							}
385						}
386					}
387				}
388			});
389			keys.addView(view);
390		}
391		if (hasKeys) {
392			keys.setVisibility(View.VISIBLE);
393		} else {
394			keys.setVisibility(View.GONE);
395		}
396	}
397
398	private void prepareContactBadge(QuickContactBadge badge, Contact contact) {
399		if (contact.getSystemAccount() != null) {
400			String[] systemAccount = contact.getSystemAccount().split("#");
401			long id = Long.parseLong(systemAccount[0]);
402			badge.assignContactUri(Contacts.getLookupUri(id, systemAccount[1]));
403		}
404		badge.setImageBitmap(avatarService().get(contact, getPixel(72)));
405	}
406
407	protected void confirmToDeleteFingerprint(final String fingerprint) {
408		AlertDialog.Builder builder = new AlertDialog.Builder(this);
409		builder.setTitle(R.string.delete_fingerprint);
410		builder.setMessage(R.string.sure_delete_fingerprint);
411		builder.setNegativeButton(R.string.cancel, null);
412		builder.setPositiveButton(R.string.delete,
413				new android.content.DialogInterface.OnClickListener() {
414
415					@Override
416					public void onClick(DialogInterface dialog, int which) {
417						if (contact.deleteOtrFingerprint(fingerprint)) {
418							populateView();
419							xmppConnectionService.syncRosterToDisk(contact
420									.getAccount());
421						}
422					}
423
424				});
425		builder.create().show();
426	}
427
428	@Override
429	public void onBackendConnected() {
430		xmppConnectionService.setOnRosterUpdateListener(this.rosterUpdate);
431		xmppConnectionService
432				.setOnAccountListChangedListener(this.accountUpdate);
433		if ((accountJid != null) && (contactJid != null)) {
434			Account account = xmppConnectionService
435					.findAccountByJid(accountJid);
436			if (account == null) {
437				return;
438			}
439			this.contact = account.getRoster().getContact(contactJid);
440			populateView();
441		}
442	}
443
444	@Override
445	protected void onStop() {
446		super.onStop();
447		xmppConnectionService.removeOnRosterUpdateListener();
448		xmppConnectionService.removeOnAccountListChangedListener();
449	}
450
451}