1package eu.siacs.conversations.ui;
2
3import java.util.ArrayList;
4import java.util.List;
5
6import eu.siacs.conversations.R;
7import eu.siacs.conversations.crypto.PgpEngine;
8import eu.siacs.conversations.crypto.PgpEngine.UserInputRequiredException;
9import eu.siacs.conversations.entities.Account;
10import eu.siacs.conversations.ui.EditAccount.EditAccountListener;
11import eu.siacs.conversations.xmpp.OnTLSExceptionReceived;
12import eu.siacs.conversations.xmpp.XmppConnection;
13import android.app.Activity;
14import android.app.AlertDialog;
15import android.content.Context;
16import android.content.DialogInterface;
17import android.content.DialogInterface.OnClickListener;
18import android.content.Intent;
19import android.content.IntentSender.SendIntentException;
20import android.os.Bundle;
21import android.os.SystemClock;
22import android.util.Log;
23import android.view.ActionMode;
24import android.view.LayoutInflater;
25import android.view.Menu;
26import android.view.MenuInflater;
27import android.view.MenuItem;
28import android.view.View;
29import android.view.ViewGroup;
30import android.widget.AdapterView;
31import android.widget.AdapterView.OnItemClickListener;
32import android.widget.AdapterView.OnItemLongClickListener;
33import android.widget.ArrayAdapter;
34import android.widget.ListView;
35import android.widget.TextView;
36
37public class ManageAccountActivity extends XmppActivity {
38
39 public static final int REQUEST_ANNOUNCE_PGP = 0x73731;
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("Untrusted Certificate");
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("Don't connect", null);
95 builder.setPositiveButton("Trust certificate", 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("temporarily disabled");
134 statusView.setTextColor(0xFF1da9da);
135 break;
136 case Account.STATUS_ONLINE:
137 statusView.setText("online");
138 statusView.setTextColor(0xFF83b600);
139 break;
140 case Account.STATUS_CONNECTING:
141 statusView.setText("connecting\u2026");
142 statusView.setTextColor(0xFF1da9da);
143 break;
144 case Account.STATUS_OFFLINE:
145 statusView.setText("offline");
146 statusView.setTextColor(0xFFe92727);
147 break;
148 case Account.STATUS_UNAUTHORIZED:
149 statusView.setText("unauthorized");
150 statusView.setTextColor(0xFFe92727);
151 break;
152 case Account.STATUS_SERVER_NOT_FOUND:
153 statusView.setText("server not found");
154 statusView.setTextColor(0xFFe92727);
155 break;
156 case Account.STATUS_NO_INTERNET:
157 statusView.setText("no internet");
158 statusView.setTextColor(0xFFe92727);
159 break;
160 case Account.STATUS_SERVER_REQUIRES_TLS:
161 statusView.setText("server requires TLS");
162 statusView.setTextColor(0xFFe92727);
163 break;
164 case Account.STATUS_TLS_ERROR:
165 statusView.setText("untrusted cerficate");
166 statusView.setTextColor(0xFFe92727);
167 break;
168 case Account.STATUS_REGISTRATION_FAILED:
169 statusView.setText("registration failed");
170 statusView.setTextColor(0xFFe92727);
171 break;
172 case Account.STATUS_REGISTRATION_CONFLICT:
173 statusView.setText("username already in use");
174 statusView.setTextColor(0xFFe92727);
175 break;
176 case Account.STATUS_REGISTRATION_SUCCESSFULL:
177 statusView.setText("registration completed");
178 statusView.setTextColor(0xFF83b600);
179 break;
180 case Account.STATUS_REGISTRATION_NOT_SUPPORTED:
181 statusView.setText("server does not support registration");
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("Are you sure?");
265 builder.setIconAttribute(android.R.attr.alertDialogIcon);
266 builder.setMessage("If you delete your account your entire conversation history will be lost");
267 builder.setPositiveButton("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("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 try {
282 xmppConnectionService.generatePgpAnnouncement(selectedAccountForActionMode);
283 } catch (PgpEngine.UserInputRequiredException e) {
284 try {
285 startIntentSenderForResult(e.getPendingIntent().getIntentSender(), REQUEST_ANNOUNCE_PGP, null, 0, 0, 0);
286 } catch (SendIntentException e1) {
287 Log.d("gultsch","sending intent failed");
288 }
289 }
290 }
291 } else if (item.getItemId() == R.id.mgmt_otr_key) {
292 AlertDialog.Builder builder = new AlertDialog.Builder(activity);
293 builder.setTitle("OTR Fingerprint");
294 String fingerprintTxt = selectedAccountForActionMode.getOtrFingerprint(getApplicationContext());
295 View view = (View) getLayoutInflater().inflate(R.layout.otr_fingerprint, null);
296 if (fingerprintTxt!=null) {
297 TextView fingerprint = (TextView) view.findViewById(R.id.otr_fingerprint);
298 fingerprint.setText(fingerprintTxt);
299 }
300 builder.setView(view);
301 builder.setPositiveButton("Done", null);
302 builder.create().show();
303 } else if (item.getItemId() == R.id.mgmt_account_info) {
304 AlertDialog.Builder builder = new AlertDialog.Builder(activity);
305 builder.setTitle(getString(R.string.account_info));
306 if (selectedAccountForActionMode.getStatus() == Account.STATUS_ONLINE) {
307 XmppConnection xmpp = selectedAccountForActionMode.getXmppConnection();
308 long connectionAge = (SystemClock.elapsedRealtime() - xmpp.lastConnect) / 60000;
309 long sessionAge = (SystemClock.elapsedRealtime() - xmpp.lastSessionStarted) / 60000;
310 long connectionAgeHours = connectionAge / 60;
311 long sessionAgeHours = sessionAge / 60;
312 View view = (View) getLayoutInflater().inflate(R.layout.server_info, null);
313 TextView connection = (TextView) view.findViewById(R.id.connection);
314 TextView session = (TextView) view.findViewById(R.id.session);
315 TextView pcks_sent = (TextView) view.findViewById(R.id.pcks_sent);
316 TextView pcks_received = (TextView) view.findViewById(R.id.pcks_received);
317 TextView carbon = (TextView) view.findViewById(R.id.carbon);
318 TextView stream = (TextView) view.findViewById(R.id.stream);
319 TextView roster = (TextView) view.findViewById(R.id.roster);
320 TextView presences = (TextView) view.findViewById(R.id.number_presences);
321 presences.setText(selectedAccountForActionMode.countPresences()+"");
322 pcks_received.setText(""+xmpp.getReceivedStanzas());
323 pcks_sent.setText(""+xmpp.getSentStanzas());
324 if (connectionAgeHours >= 2) {
325 connection.setText(connectionAgeHours+" hours");
326 } else {
327 connection.setText(connectionAge+" mins");
328 }
329 if (xmpp.hasFeatureStreamManagment()) {
330 if (sessionAgeHours >= 2) {
331 session.setText(sessionAgeHours+" hours");
332 } else {
333 session.setText(sessionAge+" mins");
334 }
335 stream.setText("Yes");
336 } else {
337 stream.setText("No");
338 session.setText(connection.getText());
339 }
340 if (xmpp.hasFeaturesCarbon()) {
341 carbon.setText("Yes");
342 } else {
343 carbon.setText("No");
344 }
345 if (xmpp.hasFeatureRosterManagment()) {
346 roster.setText("Yes");
347 } else {
348 roster.setText("No");
349 }
350 builder.setView(view);
351 } else {
352 builder.setMessage("Account is offline");
353 }
354 builder.setPositiveButton("Hide", null);
355 builder.create().show();
356 }
357 return true;
358 }
359
360
361 }));
362 return true;
363 } else {
364 return false;
365 }
366 }
367 });
368 }
369
370 @Override
371 protected void onStop() {
372 if (xmppConnectionServiceBound) {
373 xmppConnectionService.removeOnAccountListChangedListener();
374 xmppConnectionService.removeOnTLSExceptionReceivedListener();
375 }
376 super.onStop();
377 }
378
379 @Override
380 void onBackendConnected() {
381 xmppConnectionService.setOnAccountListChangedListener(accountChanged);
382 xmppConnectionService.setOnTLSExceptionReceivedListener(tlsExceptionReceived);
383 this.accountList.clear();
384 this.accountList.addAll(xmppConnectionService.getAccounts());
385 accountListViewAdapter.notifyDataSetChanged();
386 if ((this.accountList.size() == 0)&&(this.firstrun)) {
387 getActionBar().setDisplayHomeAsUpEnabled(false);
388 addAccount();
389 this.firstrun = false;
390 }
391 }
392
393 @Override
394 public boolean onCreateOptionsMenu(Menu menu) {
395 getMenuInflater().inflate(R.menu.manageaccounts, menu);
396 return true;
397 }
398
399 @Override
400 public boolean onOptionsItemSelected(MenuItem item) {
401 switch (item.getItemId()) {
402 case R.id.action_add_account:
403 addAccount();
404 break;
405 default:
406 break;
407 }
408 return super.onOptionsItemSelected(item);
409 }
410
411 private void editAccount(Account account) {
412 EditAccount dialog = new EditAccount();
413 dialog.setAccount(account);
414 dialog.setEditAccountListener(new EditAccountListener() {
415
416 @Override
417 public void onAccountEdited(Account account) {
418 xmppConnectionService.updateAccount(account);
419 if (actionMode != null) {
420 actionMode.finish();
421 }
422 }
423 });
424 dialog.show(getFragmentManager(), "edit_account");
425
426 }
427
428 protected void addAccount() {
429 final Activity activity = this;
430 EditAccount dialog = new EditAccount();
431 dialog.setEditAccountListener(new EditAccountListener() {
432
433 @Override
434 public void onAccountEdited(Account account) {
435 xmppConnectionService.createAccount(account);
436 activity.getActionBar().setDisplayHomeAsUpEnabled(true);
437 }
438 });
439 dialog.show(getFragmentManager(), "add_account");
440 }
441
442
443 @Override
444 public void onActionModeStarted(ActionMode mode) {
445 super.onActionModeStarted(mode);
446 this.isActionMode = true;
447 }
448
449 @Override
450 public void onActionModeFinished(ActionMode mode) {
451 super.onActionModeFinished(mode);
452 this.isActionMode = false;
453 accountListView.clearChoices();
454 accountListView.requestLayout();
455 accountListView.post(new Runnable() {
456 @Override
457 public void run() {
458 accountListView.setChoiceMode(ListView.CHOICE_MODE_NONE);
459 }
460 });
461 }
462
463 @Override
464 protected void onActivityResult(int requestCode, int resultCode, Intent data) {
465 super.onActivityResult(requestCode, resultCode, data);
466 if (resultCode == RESULT_OK) {
467 if (requestCode == REQUEST_ANNOUNCE_PGP) {
468 try {
469 xmppConnectionService.generatePgpAnnouncement(selectedAccountForActionMode);
470 } catch (UserInputRequiredException e) {
471 try {
472 startIntentSenderForResult(e.getPendingIntent().getIntentSender(), REQUEST_ANNOUNCE_PGP, null, 0, 0, 0);
473 } catch (SendIntentException e1) {
474 Log.d(LOGTAG,"sending intent failed");
475 }
476 }
477 }
478 }
479 }
480}