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