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 } else {
283 activity.showInstallPgpDialog();
284 }
285 } else if (item.getItemId() == R.id.mgmt_otr_key) {
286 AlertDialog.Builder builder = new AlertDialog.Builder(activity);
287 builder.setTitle("OTR Fingerprint");
288 String fingerprintTxt = selectedAccountForActionMode.getOtrFingerprint(getApplicationContext());
289 View view = (View) getLayoutInflater().inflate(R.layout.otr_fingerprint, null);
290 if (fingerprintTxt!=null) {
291 TextView fingerprint = (TextView) view.findViewById(R.id.otr_fingerprint);
292 TextView noFingerprintView = (TextView) view.findViewById(R.id.otr_no_fingerprint);
293 fingerprint.setText(fingerprintTxt);
294 fingerprint.setVisibility(View.VISIBLE);
295 noFingerprintView.setVisibility(View.GONE);
296 }
297 builder.setView(view);
298 builder.setPositiveButton(getString(R.string.done), null);
299 builder.create().show();
300 } else if (item.getItemId() == R.id.mgmt_account_info) {
301 AlertDialog.Builder builder = new AlertDialog.Builder(activity);
302 builder.setTitle(getString(R.string.account_info));
303 if (selectedAccountForActionMode.getStatus() == Account.STATUS_ONLINE) {
304 XmppConnection xmpp = selectedAccountForActionMode.getXmppConnection();
305 long connectionAge = (SystemClock.elapsedRealtime() - xmpp.lastConnect) / 60000;
306 long sessionAge = (SystemClock.elapsedRealtime() - xmpp.lastSessionStarted) / 60000;
307 long connectionAgeHours = connectionAge / 60;
308 long sessionAgeHours = sessionAge / 60;
309 View view = (View) getLayoutInflater().inflate(R.layout.server_info, null);
310 TextView connection = (TextView) view.findViewById(R.id.connection);
311 TextView session = (TextView) view.findViewById(R.id.session);
312 TextView pcks_sent = (TextView) view.findViewById(R.id.pcks_sent);
313 TextView pcks_received = (TextView) view.findViewById(R.id.pcks_received);
314 TextView carbon = (TextView) view.findViewById(R.id.carbon);
315 TextView stream = (TextView) view.findViewById(R.id.stream);
316 TextView roster = (TextView) view.findViewById(R.id.roster);
317 TextView presences = (TextView) view.findViewById(R.id.number_presences);
318 presences.setText(selectedAccountForActionMode.countPresences()+"");
319 pcks_received.setText(""+xmpp.getReceivedStanzas());
320 pcks_sent.setText(""+xmpp.getSentStanzas());
321 if (connectionAgeHours >= 2) {
322 connection.setText(connectionAgeHours+" " + getString(R.string.hours));
323 } else {
324 connection.setText(connectionAge+" " + getString(R.string.mins));
325 }
326 if (xmpp.hasFeatureStreamManagment()) {
327 if (sessionAgeHours >= 2) {
328 session.setText(sessionAgeHours+" " + getString(R.string.hours));
329 } else {
330 session.setText(sessionAge+" " + getString(R.string.mins));
331 }
332 stream.setText(getString(R.string.yes));
333 } else {
334 stream.setText(getString(R.string.no));
335 session.setText(connection.getText());
336 }
337 if (xmpp.hasFeaturesCarbon()) {
338 carbon.setText(getString(R.string.yes));
339 } else {
340 carbon.setText(getString(R.string.no));
341 }
342 if (xmpp.hasFeatureRosterManagment()) {
343 roster.setText(getString(R.string.yes));
344 } else {
345 roster.setText(getString(R.string.no));
346 }
347 builder.setView(view);
348 } else {
349 builder.setMessage(getString(R.string.mgmt_account_account_offline));
350 }
351 builder.setPositiveButton(getString(R.string.hide), null);
352 builder.create().show();
353 }
354 return true;
355 }
356
357
358 }));
359 return true;
360 } else {
361 return false;
362 }
363 }
364 });
365 }
366
367 @Override
368 protected void onStop() {
369 if (xmppConnectionServiceBound) {
370 xmppConnectionService.removeOnAccountListChangedListener();
371 xmppConnectionService.removeOnTLSExceptionReceivedListener();
372 }
373 super.onStop();
374 }
375
376 @Override
377 void onBackendConnected() {
378 xmppConnectionService.setOnAccountListChangedListener(accountChanged);
379 xmppConnectionService.setOnTLSExceptionReceivedListener(tlsExceptionReceived);
380 this.accountList.clear();
381 this.accountList.addAll(xmppConnectionService.getAccounts());
382 accountListViewAdapter.notifyDataSetChanged();
383 if ((this.accountList.size() == 0)&&(this.firstrun)) {
384 getActionBar().setDisplayHomeAsUpEnabled(false);
385 getActionBar().setHomeButtonEnabled(false);
386 addAccount();
387 this.firstrun = false;
388 }
389 }
390
391 @Override
392 public boolean onCreateOptionsMenu(Menu menu) {
393 getMenuInflater().inflate(R.menu.manageaccounts, menu);
394 return true;
395 }
396
397 @Override
398 public boolean onOptionsItemSelected(MenuItem item) {
399 switch (item.getItemId()) {
400 case R.id.action_add_account:
401 addAccount();
402 break;
403 default:
404 break;
405 }
406 return super.onOptionsItemSelected(item);
407 }
408
409 @Override
410 public boolean onNavigateUp() {
411 if (xmppConnectionService.getConversations().size() == 0) {
412 Intent contactsIntent = new Intent(this, ContactsActivity.class);
413 contactsIntent.setFlags(
414 // if activity exists in stack, pop the stack and go back to it
415 Intent.FLAG_ACTIVITY_CLEAR_TOP |
416 // otherwise, make a new task for it
417 Intent.FLAG_ACTIVITY_NEW_TASK |
418 // don't use the new activity animation; finish animation runs instead
419 Intent.FLAG_ACTIVITY_NO_ANIMATION);
420 startActivity(contactsIntent);
421 finish();
422 return true;
423 } else {
424 return super.onNavigateUp();
425 }
426 }
427
428 private void editAccount(Account account) {
429 EditAccount dialog = new EditAccount();
430 dialog.setAccount(account);
431 dialog.setEditAccountListener(new EditAccountListener() {
432
433 @Override
434 public void onAccountEdited(Account account) {
435 xmppConnectionService.updateAccount(account);
436 if (actionMode != null) {
437 actionMode.finish();
438 }
439 }
440 });
441 dialog.show(getFragmentManager(), "edit_account");
442
443 }
444
445 protected void addAccount() {
446 final Activity activity = this;
447 EditAccount dialog = new EditAccount();
448 dialog.setEditAccountListener(new EditAccountListener() {
449
450 @Override
451 public void onAccountEdited(Account account) {
452 xmppConnectionService.createAccount(account);
453 activity.getActionBar().setDisplayHomeAsUpEnabled(true);
454 activity.getActionBar().setHomeButtonEnabled(true);
455 }
456 });
457 dialog.show(getFragmentManager(), "add_account");
458 }
459
460
461 @Override
462 public void onActionModeStarted(ActionMode mode) {
463 super.onActionModeStarted(mode);
464 this.isActionMode = true;
465 }
466
467 @Override
468 public void onActionModeFinished(ActionMode mode) {
469 super.onActionModeFinished(mode);
470 this.isActionMode = false;
471 accountListView.clearChoices();
472 accountListView.requestLayout();
473 accountListView.post(new Runnable() {
474 @Override
475 public void run() {
476 accountListView.setChoiceMode(ListView.CHOICE_MODE_NONE);
477 }
478 });
479 }
480
481 @Override
482 protected void onActivityResult(int requestCode, int resultCode, Intent data) {
483 super.onActivityResult(requestCode, resultCode, data);
484 if (resultCode == RESULT_OK) {
485 if (requestCode == REQUEST_ANNOUNCE_PGP) {
486 announcePgp(selectedAccountForActionMode,null);
487 }
488 }
489 }
490}