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