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