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);
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 fingerprint.setText(fingerprintTxt);
292 }
293 builder.setView(view);
294 builder.setPositiveButton("Done", null);
295 builder.create().show();
296 } else if (item.getItemId() == R.id.mgmt_account_info) {
297 AlertDialog.Builder builder = new AlertDialog.Builder(activity);
298 builder.setTitle(getString(R.string.account_info));
299 if (selectedAccountForActionMode.getStatus() == Account.STATUS_ONLINE) {
300 XmppConnection xmpp = selectedAccountForActionMode.getXmppConnection();
301 long connectionAge = (SystemClock.elapsedRealtime() - xmpp.lastConnect) / 60000;
302 long sessionAge = (SystemClock.elapsedRealtime() - xmpp.lastSessionStarted) / 60000;
303 long connectionAgeHours = connectionAge / 60;
304 long sessionAgeHours = sessionAge / 60;
305 View view = (View) getLayoutInflater().inflate(R.layout.server_info, null);
306 TextView connection = (TextView) view.findViewById(R.id.connection);
307 TextView session = (TextView) view.findViewById(R.id.session);
308 TextView pcks_sent = (TextView) view.findViewById(R.id.pcks_sent);
309 TextView pcks_received = (TextView) view.findViewById(R.id.pcks_received);
310 TextView carbon = (TextView) view.findViewById(R.id.carbon);
311 TextView stream = (TextView) view.findViewById(R.id.stream);
312 TextView roster = (TextView) view.findViewById(R.id.roster);
313 TextView presences = (TextView) view.findViewById(R.id.number_presences);
314 presences.setText(selectedAccountForActionMode.countPresences()+"");
315 pcks_received.setText(""+xmpp.getReceivedStanzas());
316 pcks_sent.setText(""+xmpp.getSentStanzas());
317 if (connectionAgeHours >= 2) {
318 connection.setText(connectionAgeHours+" hours");
319 } else {
320 connection.setText(connectionAge+" mins");
321 }
322 if (xmpp.hasFeatureStreamManagment()) {
323 if (sessionAgeHours >= 2) {
324 session.setText(sessionAgeHours+" hours");
325 } else {
326 session.setText(sessionAge+" mins");
327 }
328 stream.setText("Yes");
329 } else {
330 stream.setText("No");
331 session.setText(connection.getText());
332 }
333 if (xmpp.hasFeaturesCarbon()) {
334 carbon.setText("Yes");
335 } else {
336 carbon.setText("No");
337 }
338 if (xmpp.hasFeatureRosterManagment()) {
339 roster.setText("Yes");
340 } else {
341 roster.setText("No");
342 }
343 builder.setView(view);
344 } else {
345 builder.setMessage("Account is offline");
346 }
347 builder.setPositiveButton("Hide", null);
348 builder.create().show();
349 }
350 return true;
351 }
352
353
354 }));
355 return true;
356 } else {
357 return false;
358 }
359 }
360 });
361 }
362
363 @Override
364 protected void onStop() {
365 if (xmppConnectionServiceBound) {
366 xmppConnectionService.removeOnAccountListChangedListener();
367 xmppConnectionService.removeOnTLSExceptionReceivedListener();
368 }
369 super.onStop();
370 }
371
372 @Override
373 void onBackendConnected() {
374 xmppConnectionService.setOnAccountListChangedListener(accountChanged);
375 xmppConnectionService.setOnTLSExceptionReceivedListener(tlsExceptionReceived);
376 this.accountList.clear();
377 this.accountList.addAll(xmppConnectionService.getAccounts());
378 accountListViewAdapter.notifyDataSetChanged();
379 if ((this.accountList.size() == 0)&&(this.firstrun)) {
380 getActionBar().setDisplayHomeAsUpEnabled(false);
381 addAccount();
382 this.firstrun = false;
383 }
384 }
385
386 @Override
387 public boolean onCreateOptionsMenu(Menu menu) {
388 getMenuInflater().inflate(R.menu.manageaccounts, menu);
389 return true;
390 }
391
392 @Override
393 public boolean onOptionsItemSelected(MenuItem item) {
394 switch (item.getItemId()) {
395 case R.id.action_add_account:
396 addAccount();
397 break;
398 default:
399 break;
400 }
401 return super.onOptionsItemSelected(item);
402 }
403
404 private void editAccount(Account account) {
405 EditAccount dialog = new EditAccount();
406 dialog.setAccount(account);
407 dialog.setEditAccountListener(new EditAccountListener() {
408
409 @Override
410 public void onAccountEdited(Account account) {
411 xmppConnectionService.updateAccount(account);
412 if (actionMode != null) {
413 actionMode.finish();
414 }
415 }
416 });
417 dialog.show(getFragmentManager(), "edit_account");
418
419 }
420
421 protected void addAccount() {
422 final Activity activity = this;
423 EditAccount dialog = new EditAccount();
424 dialog.setEditAccountListener(new EditAccountListener() {
425
426 @Override
427 public void onAccountEdited(Account account) {
428 xmppConnectionService.createAccount(account);
429 activity.getActionBar().setDisplayHomeAsUpEnabled(true);
430 }
431 });
432 dialog.show(getFragmentManager(), "add_account");
433 }
434
435
436 @Override
437 public void onActionModeStarted(ActionMode mode) {
438 super.onActionModeStarted(mode);
439 this.isActionMode = true;
440 }
441
442 @Override
443 public void onActionModeFinished(ActionMode mode) {
444 super.onActionModeFinished(mode);
445 this.isActionMode = false;
446 accountListView.clearChoices();
447 accountListView.requestLayout();
448 accountListView.post(new Runnable() {
449 @Override
450 public void run() {
451 accountListView.setChoiceMode(ListView.CHOICE_MODE_NONE);
452 }
453 });
454 }
455
456 @Override
457 protected void onActivityResult(int requestCode, int resultCode, Intent data) {
458 super.onActivityResult(requestCode, resultCode, data);
459 if (resultCode == RESULT_OK) {
460 if (requestCode == REQUEST_ANNOUNCE_PGP) {
461 announcePgp(selectedAccountForActionMode);
462 }
463 }
464 }
465}