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