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