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("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 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("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+" hours");
321 } else {
322 connection.setText(connectionAge+" mins");
323 }
324 if (xmpp.hasFeatureStreamManagment()) {
325 if (sessionAgeHours >= 2) {
326 session.setText(sessionAgeHours+" hours");
327 } else {
328 session.setText(sessionAge+" mins");
329 }
330 stream.setText("Yes");
331 } else {
332 stream.setText("No");
333 session.setText(connection.getText());
334 }
335 if (xmpp.hasFeaturesCarbon()) {
336 carbon.setText("Yes");
337 } else {
338 carbon.setText("No");
339 }
340 if (xmpp.hasFeatureRosterManagment()) {
341 roster.setText("Yes");
342 } else {
343 roster.setText("No");
344 }
345 builder.setView(view);
346 } else {
347 builder.setMessage("Account is offline");
348 }
349 builder.setPositiveButton("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 addAccount();
384 this.firstrun = false;
385 }
386 }
387
388 @Override
389 public boolean onCreateOptionsMenu(Menu menu) {
390 getMenuInflater().inflate(R.menu.manageaccounts, menu);
391 return true;
392 }
393
394 @Override
395 public boolean onOptionsItemSelected(MenuItem item) {
396 switch (item.getItemId()) {
397 case R.id.action_add_account:
398 addAccount();
399 break;
400 default:
401 break;
402 }
403 return super.onOptionsItemSelected(item);
404 }
405
406 private void editAccount(Account account) {
407 EditAccount dialog = new EditAccount();
408 dialog.setAccount(account);
409 dialog.setEditAccountListener(new EditAccountListener() {
410
411 @Override
412 public void onAccountEdited(Account account) {
413 xmppConnectionService.updateAccount(account);
414 if (actionMode != null) {
415 actionMode.finish();
416 }
417 }
418 });
419 dialog.show(getFragmentManager(), "edit_account");
420
421 }
422
423 protected void addAccount() {
424 final Activity activity = this;
425 EditAccount dialog = new EditAccount();
426 dialog.setEditAccountListener(new EditAccountListener() {
427
428 @Override
429 public void onAccountEdited(Account account) {
430 xmppConnectionService.createAccount(account);
431 activity.getActionBar().setDisplayHomeAsUpEnabled(true);
432 }
433 });
434 dialog.show(getFragmentManager(), "add_account");
435 }
436
437
438 @Override
439 public void onActionModeStarted(ActionMode mode) {
440 super.onActionModeStarted(mode);
441 this.isActionMode = true;
442 }
443
444 @Override
445 public void onActionModeFinished(ActionMode mode) {
446 super.onActionModeFinished(mode);
447 this.isActionMode = false;
448 accountListView.clearChoices();
449 accountListView.requestLayout();
450 accountListView.post(new Runnable() {
451 @Override
452 public void run() {
453 accountListView.setChoiceMode(ListView.CHOICE_MODE_NONE);
454 }
455 });
456 }
457
458 @Override
459 protected void onActivityResult(int requestCode, int resultCode, Intent data) {
460 super.onActivityResult(requestCode, resultCode, data);
461 if (resultCode == RESULT_OK) {
462 if (requestCode == REQUEST_ANNOUNCE_PGP) {
463 announcePgp(selectedAccountForActionMode,null);
464 }
465 }
466 }
467}