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