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 void onCreate(Bundle savedInstanceState) {
166 super.onCreate(savedInstanceState);
167 if (getIntent().getAction().equals(ACTION_VIEW_CONTACT)) {
168 this.accountJid = getIntent().getExtras().getString("account");
169 this.contactJid = getIntent().getExtras().getString("contact");
170 }
171 setContentView(R.layout.activity_contact_details);
172
173 contactJidTv = (TextView) findViewById(R.id.details_contactjid);
174 accountJidTv = (TextView) findViewById(R.id.details_account);
175 status = (TextView) findViewById(R.id.details_contactstatus);
176 lastseen = (TextView) findViewById(R.id.details_lastseen);
177 send = (CheckBox) findViewById(R.id.details_send_presence);
178 receive = (CheckBox) findViewById(R.id.details_receive_presence);
179 badge = (QuickContactBadge) findViewById(R.id.details_contact_badge);
180 keys = (LinearLayout) findViewById(R.id.details_contact_keys);
181 getActionBar().setHomeButtonEnabled(true);
182 getActionBar().setDisplayHomeAsUpEnabled(true);
183
184 }
185
186 @Override
187 public boolean onOptionsItemSelected(MenuItem menuItem) {
188 AlertDialog.Builder builder = new AlertDialog.Builder(this);
189 builder.setNegativeButton(getString(R.string.cancel), null);
190 switch (menuItem.getItemId()) {
191 case android.R.id.home:
192 finish();
193 break;
194 case R.id.action_delete_contact:
195 builder.setTitle(getString(R.string.action_delete_contact))
196 .setMessage(
197 getString(R.string.remove_contact_text,
198 contact.getJid()))
199 .setPositiveButton(getString(R.string.delete),
200 removeFromRoster).create().show();
201 break;
202 case R.id.action_edit_contact:
203 if (contact.getSystemAccount() == null) {
204 quickEdit(contact.getDisplayName(), new OnValueEdited() {
205
206 @Override
207 public void onValueEdited(String value) {
208 contact.setServerName(value);
209 ContactDetailsActivity.this.xmppConnectionService
210 .pushContactToServer(contact);
211 populateView();
212 }
213 });
214 } else {
215 Intent intent = new Intent(Intent.ACTION_EDIT);
216 String[] systemAccount = contact.getSystemAccount().split("#");
217 long id = Long.parseLong(systemAccount[0]);
218 Uri uri = Contacts.getLookupUri(id, systemAccount[1]);
219 intent.setDataAndType(uri, Contacts.CONTENT_ITEM_TYPE);
220 intent.putExtra("finishActivityOnSaveCompleted", true);
221 startActivity(intent);
222 }
223 break;
224 }
225 return super.onOptionsItemSelected(menuItem);
226 }
227
228 @Override
229 public boolean onCreateOptionsMenu(Menu menu) {
230 getMenuInflater().inflate(R.menu.contact_details, menu);
231 return true;
232 }
233
234 private void populateView() {
235 send.setOnCheckedChangeListener(null);
236 receive.setOnCheckedChangeListener(null);
237 setTitle(contact.getDisplayName());
238 if (contact.getOption(Contact.Options.FROM)) {
239 send.setText(R.string.send_presence_updates);
240 send.setChecked(true);
241 } else if (contact
242 .getOption(Contact.Options.PENDING_SUBSCRIPTION_REQUEST)) {
243 send.setChecked(false);
244 send.setText(R.string.send_presence_updates);
245 } else {
246 send.setText(R.string.preemptively_grant);
247 if (contact.getOption(Contact.Options.PREEMPTIVE_GRANT)) {
248 send.setChecked(true);
249 } else {
250 send.setChecked(false);
251 }
252 }
253 if (contact.getOption(Contact.Options.TO)) {
254 receive.setText(R.string.receive_presence_updates);
255 receive.setChecked(true);
256 } else {
257 receive.setText(R.string.ask_for_presence_updates);
258 if (contact.getOption(Contact.Options.ASKING)) {
259 receive.setChecked(true);
260 } else {
261 receive.setChecked(false);
262 }
263 }
264 if (contact.getAccount().getStatus() == Account.STATUS_ONLINE) {
265 receive.setEnabled(true);
266 send.setEnabled(true);
267 } else {
268 receive.setEnabled(false);
269 send.setEnabled(false);
270 }
271
272 send.setOnCheckedChangeListener(this.mOnSendCheckedChange);
273 receive.setOnCheckedChangeListener(this.mOnReceiveCheckedChange);
274
275 lastseen.setText(UIHelper.lastseen(getApplicationContext(),
276 contact.lastseen.time));
277
278 switch (contact.getMostAvailableStatus()) {
279 case Presences.CHAT:
280 status.setText(R.string.contact_status_free_to_chat);
281 status.setTextColor(mColorGreen);
282 break;
283 case Presences.ONLINE:
284 status.setText(R.string.contact_status_online);
285 status.setTextColor(mColorGreen);
286 break;
287 case Presences.AWAY:
288 status.setText(R.string.contact_status_away);
289 status.setTextColor(mColorOrange);
290 break;
291 case Presences.XA:
292 status.setText(R.string.contact_status_extended_away);
293 status.setTextColor(mColorOrange);
294 break;
295 case Presences.DND:
296 status.setText(R.string.contact_status_do_not_disturb);
297 status.setTextColor(mColorRed);
298 break;
299 case Presences.OFFLINE:
300 status.setText(R.string.contact_status_offline);
301 status.setTextColor(mSecondaryTextColor);
302 break;
303 default:
304 status.setText(R.string.contact_status_offline);
305 status.setTextColor(mSecondaryTextColor);
306 break;
307 }
308 if (contact.getPresences().size() > 1) {
309 contactJidTv.setText(contact.getJid() + " ("
310 + contact.getPresences().size() + ")");
311 } else {
312 contactJidTv.setText(contact.getJid());
313 }
314 accountJidTv.setText(getString(R.string.using_account, contact
315 .getAccount().getJid()));
316 prepareContactBadge(badge, contact);
317 if (contact.getSystemAccount() == null) {
318 badge.setOnClickListener(onBadgeClick);
319 }
320
321 keys.removeAllViews();
322 boolean hasKeys = false;
323 LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
324 for (Iterator<String> iterator = contact.getOtrFingerprints()
325 .iterator(); iterator.hasNext();) {
326 hasKeys = true;
327 final String otrFingerprint = iterator.next();
328 View view = (View) inflater.inflate(R.layout.contact_key, keys,
329 false);
330 TextView key = (TextView) view.findViewById(R.id.key);
331 TextView keyType = (TextView) view.findViewById(R.id.key_type);
332 ImageButton remove = (ImageButton) view
333 .findViewById(R.id.button_remove);
334 remove.setVisibility(View.VISIBLE);
335 keyType.setText("OTR Fingerprint");
336 key.setText(otrFingerprint);
337 keys.addView(view);
338 remove.setOnClickListener(new OnClickListener() {
339
340 @Override
341 public void onClick(View v) {
342 confirmToDeleteFingerprint(otrFingerprint);
343 }
344 });
345 }
346 if (contact.getPgpKeyId() != 0) {
347 hasKeys = true;
348 View view = (View) inflater.inflate(R.layout.contact_key, keys,
349 false);
350 TextView key = (TextView) view.findViewById(R.id.key);
351 TextView keyType = (TextView) view.findViewById(R.id.key_type);
352 keyType.setText("PGP Key ID");
353 key.setText(OpenPgpUtils.convertKeyIdToHex(contact.getPgpKeyId()));
354 view.setOnClickListener(new OnClickListener() {
355
356 @Override
357 public void onClick(View v) {
358 PgpEngine pgp = ContactDetailsActivity.this.xmppConnectionService
359 .getPgpEngine();
360 if (pgp != null) {
361 PendingIntent intent = pgp.getIntentForKey(contact);
362 if (intent != null) {
363 try {
364 startIntentSenderForResult(
365 intent.getIntentSender(), 0, null, 0,
366 0, 0);
367 } catch (SendIntentException e) {
368
369 }
370 }
371 }
372 }
373 });
374 keys.addView(view);
375 }
376 if (hasKeys) {
377 keys.setVisibility(View.VISIBLE);
378 } else {
379 keys.setVisibility(View.GONE);
380 }
381 }
382
383 private void prepareContactBadge(QuickContactBadge badge, Contact contact) {
384 if (contact.getSystemAccount() != null) {
385 String[] systemAccount = contact.getSystemAccount().split("#");
386 long id = Long.parseLong(systemAccount[0]);
387 badge.assignContactUri(Contacts.getLookupUri(id, systemAccount[1]));
388 }
389 badge.setImageBitmap(xmppConnectionService.getAvatarService()
390 .getAvatar(contact, getPixel(72)));
391 }
392
393 protected void confirmToDeleteFingerprint(final String fingerprint) {
394 AlertDialog.Builder builder = new AlertDialog.Builder(this);
395 builder.setTitle(R.string.delete_fingerprint);
396 builder.setMessage(R.string.sure_delete_fingerprint);
397 builder.setNegativeButton(R.string.cancel, null);
398 builder.setPositiveButton(R.string.delete,
399 new android.content.DialogInterface.OnClickListener() {
400
401 @Override
402 public void onClick(DialogInterface dialog, int which) {
403 if (contact.deleteOtrFingerprint(fingerprint)) {
404 populateView();
405 xmppConnectionService.syncRosterToDisk(contact
406 .getAccount());
407 }
408 }
409
410 });
411 builder.create().show();
412 }
413
414 @Override
415 public void onBackendConnected() {
416 xmppConnectionService.setOnRosterUpdateListener(this.rosterUpdate);
417 xmppConnectionService
418 .setOnAccountListChangedListener(this.accountUpdate);
419 if ((accountJid != null) && (contactJid != null)) {
420 Account account = xmppConnectionService
421 .findAccountByJid(accountJid);
422 if (account == null) {
423 return;
424 }
425 this.contact = account.getRoster().getContact(contactJid);
426 populateView();
427 }
428 }
429
430 @Override
431 protected void onStop() {
432 super.onStop();
433 xmppConnectionService.removeOnRosterUpdateListener();
434 xmppConnectionService.removeOnAccountListChangedListener();
435 }
436
437}