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}