1package eu.siacs.conversations.ui;
2
3import java.util.ArrayList;
4import java.util.List;
5
6import eu.siacs.conversations.R;
7import eu.siacs.conversations.entities.Account;
8import eu.siacs.conversations.services.XmppConnectionService.OnAccountUpdate;
9import eu.siacs.conversations.ui.EditAccountDialog.EditAccountListener;
10import eu.siacs.conversations.xmpp.OnTLSExceptionReceived;
11import eu.siacs.conversations.xmpp.XmppConnection;
12import android.app.Activity;
13import android.app.AlertDialog;
14import android.content.Context;
15import android.content.DialogInterface;
16import android.content.DialogInterface.OnClickListener;
17import android.content.Intent;
18import android.os.Bundle;
19import android.os.SystemClock;
20import android.view.ActionMode;
21import android.view.LayoutInflater;
22import android.view.Menu;
23import android.view.MenuInflater;
24import android.view.MenuItem;
25import android.view.View;
26import android.view.ViewGroup;
27import android.widget.AdapterView;
28import android.widget.AdapterView.OnItemClickListener;
29import android.widget.AdapterView.OnItemLongClickListener;
30import android.widget.ArrayAdapter;
31import android.widget.ListView;
32import android.widget.TextView;
33
34public class ManageAccountActivity extends XmppActivity {
35
36 protected boolean isActionMode = false;
37 protected ActionMode actionMode;
38 protected Account selectedAccountForActionMode = null;
39 protected ManageAccountActivity activity = this;
40
41 protected boolean firstrun = true;
42
43 protected List<Account> accountList = new ArrayList<Account>();
44 protected ListView accountListView;
45 protected ArrayAdapter<Account> accountListViewAdapter;
46 protected OnAccountUpdate accountChanged = new OnAccountUpdate() {
47
48 @Override
49 public void onAccountUpdate() {
50 accountList.clear();
51 accountList.addAll(xmppConnectionService.getAccounts());
52 runOnUiThread(new Runnable() {
53
54 @Override
55 public void run() {
56 accountListViewAdapter.notifyDataSetChanged();
57 }
58 });
59 }
60 };
61
62 protected OnTLSExceptionReceived tlsExceptionReceived = new OnTLSExceptionReceived() {
63
64 @Override
65 public void onTLSExceptionReceived(final String fingerprint,
66 final Account account) {
67 activity.runOnUiThread(new Runnable() {
68
69 @Override
70 public void run() {
71 AlertDialog.Builder builder = new AlertDialog.Builder(
72 activity);
73 builder.setTitle(getString(R.string.account_status_error));
74 builder.setIconAttribute(android.R.attr.alertDialogIcon);
75 View view = (View) getLayoutInflater().inflate(
76 R.layout.cert_warning, null);
77 TextView sha = (TextView) view.findViewById(R.id.sha);
78 TextView hint = (TextView) view.findViewById(R.id.hint);
79 StringBuilder humanReadableSha = new StringBuilder();
80 humanReadableSha.append(fingerprint);
81 for (int i = 2; i < 59; i += 3) {
82 if ((i == 14) || (i == 29) || (i == 44)) {
83 humanReadableSha.insert(i, "\n");
84 } else {
85 humanReadableSha.insert(i, ":");
86 }
87
88 }
89 hint.setText(getString(R.string.untrusted_cert_hint,
90 account.getServer()));
91 sha.setText(humanReadableSha.toString());
92 builder.setView(view);
93 builder.setNegativeButton(
94 getString(R.string.certif_no_trust), null);
95 builder.setPositiveButton(getString(R.string.certif_trust),
96 new OnClickListener() {
97
98 @Override
99 public void onClick(DialogInterface dialog,
100 int which) {
101 account.setSSLCertFingerprint(fingerprint);
102 activity.xmppConnectionService
103 .updateAccount(account);
104 }
105 });
106 builder.create().show();
107 }
108 });
109
110 }
111 };
112
113 @Override
114 protected void onCreate(Bundle savedInstanceState) {
115
116 super.onCreate(savedInstanceState);
117
118 setContentView(R.layout.manage_accounts);
119
120 accountListView = (ListView) findViewById(R.id.account_list);
121 accountListViewAdapter = new ArrayAdapter<Account>(
122 getApplicationContext(), R.layout.account_row, this.accountList) {
123 @Override
124 public View getView(int position, View view, ViewGroup parent) {
125 Account account = getItem(position);
126 if (view == null) {
127 LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
128 view = (View) inflater.inflate(R.layout.account_row, null);
129 }
130 ((TextView) view.findViewById(R.id.account_jid))
131 .setText(account.getJid());
132 TextView statusView = (TextView) view
133 .findViewById(R.id.account_status);
134 switch (account.getStatus()) {
135 case Account.STATUS_DISABLED:
136 statusView
137 .setText(getString(R.string.account_status_disabled));
138 statusView.setTextColor(0xFF1da9da);
139 break;
140 case Account.STATUS_ONLINE:
141 statusView
142 .setText(getString(R.string.account_status_online));
143 statusView.setTextColor(0xFF83b600);
144 break;
145 case Account.STATUS_CONNECTING:
146 statusView
147 .setText(getString(R.string.account_status_connecting));
148 statusView.setTextColor(0xFF1da9da);
149 break;
150 case Account.STATUS_OFFLINE:
151 statusView
152 .setText(getString(R.string.account_status_offline));
153 statusView.setTextColor(0xFFe92727);
154 break;
155 case Account.STATUS_UNAUTHORIZED:
156 statusView
157 .setText(getString(R.string.account_status_unauthorized));
158 statusView.setTextColor(0xFFe92727);
159 break;
160 case Account.STATUS_SERVER_NOT_FOUND:
161 statusView
162 .setText(getString(R.string.account_status_not_found));
163 statusView.setTextColor(0xFFe92727);
164 break;
165 case Account.STATUS_NO_INTERNET:
166 statusView
167 .setText(getString(R.string.account_status_no_internet));
168 statusView.setTextColor(0xFFe92727);
169 break;
170 case Account.STATUS_SERVER_REQUIRES_TLS:
171 statusView
172 .setText(getString(R.string.account_status_requires_tls));
173 statusView.setTextColor(0xFFe92727);
174 break;
175 case Account.STATUS_TLS_ERROR:
176 statusView
177 .setText(getString(R.string.account_status_error));
178 statusView.setTextColor(0xFFe92727);
179 break;
180 case Account.STATUS_REGISTRATION_FAILED:
181 statusView
182 .setText(getString(R.string.account_status_regis_fail));
183 statusView.setTextColor(0xFFe92727);
184 break;
185 case Account.STATUS_REGISTRATION_CONFLICT:
186 statusView
187 .setText(getString(R.string.account_status_regis_conflict));
188 statusView.setTextColor(0xFFe92727);
189 break;
190 case Account.STATUS_REGISTRATION_SUCCESSFULL:
191 statusView
192 .setText(getString(R.string.account_status_regis_success));
193 statusView.setTextColor(0xFF83b600);
194 break;
195 case Account.STATUS_REGISTRATION_NOT_SUPPORTED:
196 statusView
197 .setText(getString(R.string.account_status_regis_not_sup));
198 statusView.setTextColor(0xFFe92727);
199 break;
200 default:
201 statusView.setText("");
202 break;
203 }
204
205 return view;
206 }
207 };
208 final XmppActivity activity = this;
209 accountListView.setAdapter(this.accountListViewAdapter);
210 accountListView.setOnItemClickListener(new OnItemClickListener() {
211
212 @Override
213 public void onItemClick(AdapterView<?> arg0, View view,
214 int position, long arg3) {
215 if (!isActionMode) {
216 Account account = accountList.get(position);
217 if ((account.getStatus() == Account.STATUS_OFFLINE)
218 || (account.getStatus() == Account.STATUS_TLS_ERROR)) {
219 activity.xmppConnectionService.reconnectAccount(
220 accountList.get(position), true);
221 } else if (account.getStatus() == Account.STATUS_ONLINE) {
222 activity.startActivity(new Intent(activity
223 .getApplicationContext(),
224 StartConversationActivity.class));
225 } else if (account.getStatus() != Account.STATUS_DISABLED) {
226 editAccount(account);
227 }
228 } else {
229 selectedAccountForActionMode = accountList.get(position);
230 actionMode.invalidate();
231 }
232 }
233 });
234 accountListView
235 .setOnItemLongClickListener(new OnItemLongClickListener() {
236
237 @Override
238 public boolean onItemLongClick(AdapterView<?> arg0,
239 View view, int position, long arg3) {
240 if (!isActionMode) {
241 accountListView
242 .setChoiceMode(ListView.CHOICE_MODE_SINGLE);
243 accountListView.setItemChecked(position, true);
244 selectedAccountForActionMode = accountList
245 .get(position);
246 actionMode = activity
247 .startActionMode((new ActionMode.Callback() {
248
249 @Override
250 public boolean onPrepareActionMode(
251 ActionMode mode, Menu menu) {
252 if (selectedAccountForActionMode
253 .isOptionSet(Account.OPTION_DISABLED)) {
254 menu.findItem(
255 R.id.mgmt_account_enable)
256 .setVisible(true);
257 menu.findItem(
258 R.id.mgmt_account_disable)
259 .setVisible(false);
260 } else {
261 menu.findItem(
262 R.id.mgmt_account_disable)
263 .setVisible(true);
264 menu.findItem(
265 R.id.mgmt_account_enable)
266 .setVisible(false);
267 }
268 return true;
269 }
270
271 @Override
272 public void onDestroyActionMode(
273 ActionMode mode) {
274 // TODO Auto-generated method stub
275
276 }
277
278 @Override
279 public boolean onCreateActionMode(
280 ActionMode mode, Menu menu) {
281 MenuInflater inflater = mode
282 .getMenuInflater();
283 inflater.inflate(
284 R.menu.manageaccounts_context,
285 menu);
286 return true;
287 }
288
289 @Override
290 public boolean onActionItemClicked(
291 final ActionMode mode,
292 MenuItem item) {
293 if (item.getItemId() == R.id.mgmt_account_edit) {
294 editAccount(selectedAccountForActionMode);
295 } else if (item.getItemId() == R.id.mgmt_account_disable) {
296 selectedAccountForActionMode
297 .setOption(
298 Account.OPTION_DISABLED,
299 true);
300 xmppConnectionService
301 .updateAccount(selectedAccountForActionMode);
302 mode.finish();
303 } else if (item.getItemId() == R.id.mgmt_account_enable) {
304 selectedAccountForActionMode
305 .setOption(
306 Account.OPTION_DISABLED,
307 false);
308 xmppConnectionService
309 .updateAccount(selectedAccountForActionMode);
310 mode.finish();
311 } else if (item.getItemId() == R.id.mgmt_account_delete) {
312 AlertDialog.Builder builder = new AlertDialog.Builder(
313 activity);
314 builder.setTitle(getString(R.string.mgmt_account_are_you_sure));
315 builder.setIconAttribute(android.R.attr.alertDialogIcon);
316 builder.setMessage(getString(R.string.mgmt_account_delete_confirm_text));
317 builder.setPositiveButton(
318 getString(R.string.delete),
319 new OnClickListener() {
320
321 @Override
322 public void onClick(
323 DialogInterface dialog,
324 int which) {
325 xmppConnectionService
326 .deleteAccount(selectedAccountForActionMode);
327 selectedAccountForActionMode = null;
328 mode.finish();
329 }
330 });
331 builder.setNegativeButton(
332 getString(R.string.cancel),
333 null);
334 builder.create().show();
335 } else if (item.getItemId() == R.id.mgmt_account_announce_pgp) {
336 if (activity.hasPgp()) {
337 mode.finish();
338 announcePgp(
339 selectedAccountForActionMode,
340 null);
341 } else {
342 activity.showInstallPgpDialog();
343 }
344 } else if (item.getItemId() == R.id.mgmt_otr_key) {
345 AlertDialog.Builder builder = new AlertDialog.Builder(
346 activity);
347 builder.setTitle("OTR Fingerprint");
348 String fingerprintTxt = selectedAccountForActionMode
349 .getOtrFingerprint(getApplicationContext());
350 View view = (View) getLayoutInflater()
351 .inflate(
352 R.layout.otr_fingerprint,
353 null);
354 if (fingerprintTxt != null) {
355 TextView fingerprint = (TextView) view
356 .findViewById(R.id.otr_fingerprint);
357 TextView noFingerprintView = (TextView) view
358 .findViewById(R.id.otr_no_fingerprint);
359 fingerprint
360 .setText(fingerprintTxt);
361 fingerprint
362 .setVisibility(View.VISIBLE);
363 noFingerprintView
364 .setVisibility(View.GONE);
365 }
366 builder.setView(view);
367 builder.setPositiveButton(
368 getString(R.string.done),
369 null);
370 builder.create().show();
371 } else if (item.getItemId() == R.id.mgmt_account_info) {
372 AlertDialog.Builder builder = new AlertDialog.Builder(
373 activity);
374 builder.setTitle(getString(R.string.account_info));
375 if (selectedAccountForActionMode
376 .getStatus() == Account.STATUS_ONLINE) {
377 XmppConnection xmpp = selectedAccountForActionMode
378 .getXmppConnection();
379 long connectionAge = (SystemClock
380 .elapsedRealtime() - xmpp.lastConnect) / 60000;
381 long sessionAge = (SystemClock
382 .elapsedRealtime() - xmpp.lastSessionStarted) / 60000;
383 long connectionAgeHours = connectionAge / 60;
384 long sessionAgeHours = sessionAge / 60;
385 View view = (View) getLayoutInflater()
386 .inflate(
387 R.layout.server_info,
388 null);
389 TextView connection = (TextView) view
390 .findViewById(R.id.connection);
391 TextView session = (TextView) view
392 .findViewById(R.id.session);
393 TextView pcks_sent = (TextView) view
394 .findViewById(R.id.pcks_sent);
395 TextView pcks_received = (TextView) view
396 .findViewById(R.id.pcks_received);
397 TextView carbon = (TextView) view
398 .findViewById(R.id.carbon);
399 TextView stream = (TextView) view
400 .findViewById(R.id.stream);
401 TextView roster = (TextView) view
402 .findViewById(R.id.roster);
403 TextView presences = (TextView) view
404 .findViewById(R.id.number_presences);
405 presences.setText(selectedAccountForActionMode
406 .countPresences()
407 + "");
408 pcks_received.setText(""
409 + xmpp.getReceivedStanzas());
410 pcks_sent.setText(""
411 + xmpp.getSentStanzas());
412 if (connectionAgeHours >= 2) {
413 connection
414 .setText(connectionAgeHours
415 + " "
416 + getString(R.string.hours));
417 } else {
418 connection
419 .setText(connectionAge
420 + " "
421 + getString(R.string.mins));
422 }
423 if (xmpp.hasFeatureStreamManagment()) {
424 if (sessionAgeHours >= 2) {
425 session.setText(sessionAgeHours
426 + " "
427 + getString(R.string.hours));
428 } else {
429 session.setText(sessionAge
430 + " "
431 + getString(R.string.mins));
432 }
433 stream.setText(getString(R.string.yes));
434 } else {
435 stream.setText(getString(R.string.no));
436 session.setText(connection
437 .getText());
438 }
439 if (xmpp.hasFeaturesCarbon()) {
440 carbon.setText(getString(R.string.yes));
441 } else {
442 carbon.setText(getString(R.string.no));
443 }
444 if (xmpp.hasFeatureRosterManagment()) {
445 roster.setText(getString(R.string.yes));
446 } else {
447 roster.setText(getString(R.string.no));
448 }
449 builder.setView(view);
450 } else {
451 builder.setMessage(getString(R.string.mgmt_account_account_offline));
452 }
453 builder.setPositiveButton(
454 getString(R.string.hide),
455 null);
456 builder.create().show();
457 }
458 return true;
459 }
460
461 }));
462 return true;
463 } else {
464 return false;
465 }
466 }
467 });
468 }
469
470 @Override
471 protected void onStop() {
472 if (xmppConnectionServiceBound) {
473 xmppConnectionService.removeOnAccountListChangedListener();
474 xmppConnectionService.removeOnTLSExceptionReceivedListener();
475 }
476 super.onStop();
477 }
478
479 @Override
480 void onBackendConnected() {
481 xmppConnectionService.setOnAccountListChangedListener(accountChanged);
482 xmppConnectionService
483 .setOnTLSExceptionReceivedListener(tlsExceptionReceived);
484 this.accountList.clear();
485 this.accountList.addAll(xmppConnectionService.getAccounts());
486 accountListViewAdapter.notifyDataSetChanged();
487 if ((this.accountList.size() == 0) && (this.firstrun)) {
488 getActionBar().setDisplayHomeAsUpEnabled(false);
489 getActionBar().setHomeButtonEnabled(false);
490 addAccount();
491 this.firstrun = false;
492 }
493 }
494
495 @Override
496 public boolean onCreateOptionsMenu(Menu menu) {
497 getMenuInflater().inflate(R.menu.manageaccounts, menu);
498 return true;
499 }
500
501 @Override
502 public boolean onOptionsItemSelected(MenuItem item) {
503 switch (item.getItemId()) {
504 case R.id.action_add_account:
505 addAccount();
506 break;
507 default:
508 break;
509 }
510 return super.onOptionsItemSelected(item);
511 }
512
513 @Override
514 public boolean onNavigateUp() {
515 if (xmppConnectionService.getConversations().size() == 0) {
516 Intent contactsIntent = new Intent(this, StartConversationActivity.class);
517 contactsIntent.setFlags(
518 // if activity exists in stack, pop the stack and go back to it
519 Intent.FLAG_ACTIVITY_CLEAR_TOP |
520 // otherwise, make a new task for it
521 Intent.FLAG_ACTIVITY_NEW_TASK |
522 // don't use the new activity animation; finish
523 // animation runs instead
524 Intent.FLAG_ACTIVITY_NO_ANIMATION);
525 startActivity(contactsIntent);
526 finish();
527 return true;
528 } else {
529 return super.onNavigateUp();
530 }
531 }
532
533 private void editAccount(Account account) {
534 EditAccountDialog dialog = new EditAccountDialog();
535 dialog.setAccount(account);
536 dialog.setEditAccountListener(new EditAccountListener() {
537
538 @Override
539 public void onAccountEdited(Account account) {
540 xmppConnectionService.updateAccount(account);
541 if (actionMode != null) {
542 actionMode.finish();
543 }
544 }
545 });
546 dialog.show(getFragmentManager(), "edit_account");
547 dialog.setKnownHosts(xmppConnectionService.getKnownHosts(), this);
548
549 }
550
551 protected void addAccount() {
552 final Activity activity = this;
553 EditAccountDialog dialog = new EditAccountDialog();
554 dialog.setEditAccountListener(new EditAccountListener() {
555
556 @Override
557 public void onAccountEdited(Account account) {
558 xmppConnectionService.createAccount(account);
559 activity.getActionBar().setDisplayHomeAsUpEnabled(true);
560 activity.getActionBar().setHomeButtonEnabled(true);
561 }
562 });
563 dialog.show(getFragmentManager(), "add_account");
564 dialog.setKnownHosts(xmppConnectionService.getKnownHosts(), this);
565 }
566
567 @Override
568 public void onActionModeStarted(ActionMode mode) {
569 super.onActionModeStarted(mode);
570 this.isActionMode = true;
571 }
572
573 @Override
574 public void onActionModeFinished(ActionMode mode) {
575 super.onActionModeFinished(mode);
576 this.isActionMode = false;
577 accountListView.clearChoices();
578 accountListView.requestLayout();
579 accountListView.post(new Runnable() {
580 @Override
581 public void run() {
582 accountListView.setChoiceMode(ListView.CHOICE_MODE_NONE);
583 }
584 });
585 }
586
587 @Override
588 protected void onActivityResult(int requestCode, int resultCode, Intent data) {
589 super.onActivityResult(requestCode, resultCode, data);
590 if (resultCode == RESULT_OK) {
591 if (requestCode == REQUEST_ANNOUNCE_PGP) {
592 announcePgp(selectedAccountForActionMode, null);
593 }
594 }
595 }
596}