ManageAccountActivity.java

  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	public static final int REQUEST_ANNOUNCE_PGP = 0x73731;
 43	
 44	protected boolean isActionMode = false;
 45	protected ActionMode actionMode;
 46	protected Account selectedAccountForActionMode = null;
 47	protected ManageAccountActivity activity = this;
 48	
 49	protected boolean firstrun = true;
 50	
 51	protected List<Account> accountList = new ArrayList<Account>();
 52	protected ListView accountListView;
 53	protected ArrayAdapter<Account> accountListViewAdapter;
 54	protected OnAccountListChangedListener accountChanged = new OnAccountListChangedListener() {
 55
 56		@Override
 57		public void onAccountListChangedListener() {
 58			accountList.clear();
 59			accountList.addAll(xmppConnectionService.getAccounts());
 60			runOnUiThread(new Runnable() {
 61
 62				@Override
 63				public void run() {
 64					accountListViewAdapter.notifyDataSetChanged();
 65				}
 66			});
 67		}
 68	};
 69	
 70	protected OnTLSExceptionReceived tlsExceptionReceived = new OnTLSExceptionReceived() {
 71		
 72		@Override
 73		public void onTLSExceptionReceived(final String fingerprint, final Account account) {
 74			activity.runOnUiThread(new Runnable() {
 75				
 76				@Override
 77				public void run() {
 78					AlertDialog.Builder builder = new AlertDialog.Builder(activity);
 79					builder.setTitle("Untrusted Certificate");
 80					builder.setIconAttribute(android.R.attr.alertDialogIcon);
 81					View view = (View) getLayoutInflater().inflate(R.layout.cert_warning, null);
 82					TextView sha = (TextView) view.findViewById(R.id.sha);
 83					TextView hint = (TextView) view.findViewById(R.id.hint);
 84					StringBuilder humanReadableSha = new StringBuilder();
 85					humanReadableSha.append(fingerprint);
 86					for(int i = 2; i < 59; i += 3) {
 87						if ((i==14)||(i==29)||(i==44)) {
 88							humanReadableSha.insert(i, "\n");
 89						} else {
 90							humanReadableSha.insert(i, ":");
 91						}
 92						
 93					}
 94					hint.setText(getString(R.string.untrusted_cert_hint,account.getServer()));
 95					sha.setText(humanReadableSha.toString());
 96					builder.setView(view);
 97					builder.setNegativeButton("Don't connect", null);
 98					builder.setPositiveButton("Trust certificate", new OnClickListener() {
 99						
100						@Override
101						public void onClick(DialogInterface dialog, int which) {
102							account.setSSLCertFingerprint(fingerprint);
103							activity.xmppConnectionService.updateAccount(account);
104						}
105					});
106					builder.create().show();
107				}
108			});
109			
110		}
111	};
112
113	@Override
114	protected void onCreate(Bundle savedInstanceState) {
115
116		super.onCreate(savedInstanceState);
117
118		setContentView(R.layout.manage_accounts);
119
120		accountListView = (ListView) findViewById(R.id.account_list);
121		accountListViewAdapter = new ArrayAdapter<Account>(
122				getApplicationContext(), R.layout.account_row, this.accountList) {
123			@Override
124			public View getView(int position, View view, ViewGroup parent) {
125				Account account = getItem(position);
126				if (view == null) {
127					LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
128					view = (View) inflater.inflate(R.layout.account_row, null);
129				}
130				((TextView) view.findViewById(R.id.account_jid))
131						.setText(account.getJid());
132				TextView statusView = (TextView) view
133						.findViewById(R.id.account_status);
134				switch (account.getStatus()) {
135				case Account.STATUS_DISABLED:
136					statusView.setText("temporarily disabled");
137					statusView.setTextColor(0xFF1da9da);
138					break;
139				case Account.STATUS_ONLINE:
140					statusView.setText("online");
141					statusView.setTextColor(0xFF83b600);
142					break;
143				case Account.STATUS_CONNECTING:
144					statusView.setText("connecting\u2026");
145					statusView.setTextColor(0xFF1da9da);
146					break;
147				case Account.STATUS_OFFLINE:
148					statusView.setText("offline");
149					statusView.setTextColor(0xFFe92727);
150					break;
151				case Account.STATUS_UNAUTHORIZED:
152					statusView.setText("unauthorized");
153					statusView.setTextColor(0xFFe92727);
154					break;
155				case Account.STATUS_SERVER_NOT_FOUND:
156					statusView.setText("server not found");
157					statusView.setTextColor(0xFFe92727);
158					break;
159				case Account.STATUS_NO_INTERNET:
160					statusView.setText("no internet");
161					statusView.setTextColor(0xFFe92727);
162					break;
163				case Account.STATUS_SERVER_REQUIRES_TLS:
164					statusView.setText("server requires TLS");
165					statusView.setTextColor(0xFFe92727);
166					break;
167				case Account.STATUS_TLS_ERROR:
168					statusView.setText("untrusted cerficate");
169					statusView.setTextColor(0xFFe92727);
170					break;
171				case Account.STATUS_REGISTRATION_FAILED:
172					statusView.setText("registration failed");
173					statusView.setTextColor(0xFFe92727);
174					break;
175				case Account.STATUS_REGISTRATION_CONFLICT:
176					statusView.setText("username already in use");
177					statusView.setTextColor(0xFFe92727);
178					break;
179				case  Account.STATUS_REGISTRATION_SUCCESSFULL:
180					statusView.setText("registration completed");
181					statusView.setTextColor(0xFF83b600);
182					break;
183				case Account.STATUS_REGISTRATION_NOT_SUPPORTED:
184					statusView.setText("server does not support registration");
185					statusView.setTextColor(0xFFe92727);
186					break;
187				default:
188					statusView.setText("");
189					break;
190				}
191
192				return view;
193			}
194		};
195		final XmppActivity activity = this;
196		accountListView.setAdapter(this.accountListViewAdapter);
197		accountListView.setOnItemClickListener(new OnItemClickListener() {
198
199			@Override
200			public void onItemClick(AdapterView<?> arg0, View view,
201					int position, long arg3) {
202				if (!isActionMode) {
203					Account account = accountList.get(position);
204					if ((account.getStatus() == Account.STATUS_OFFLINE)||(account.getStatus() == Account.STATUS_TLS_ERROR)) {
205						activity.xmppConnectionService.reconnectAccount(accountList.get(position),true);
206					} else if (account.getStatus() == Account.STATUS_ONLINE) {
207						activity.startActivity(new Intent(activity.getApplicationContext(),ContactsActivity.class));
208					} else if (account.getStatus() != Account.STATUS_DISABLED) {
209						editAccount(account);
210					}
211				} else {
212					selectedAccountForActionMode = accountList.get(position);
213					actionMode.invalidate();
214				}
215			}
216		});
217		accountListView.setOnItemLongClickListener(new OnItemLongClickListener() {
218
219			@Override
220			public boolean onItemLongClick(AdapterView<?> arg0, View view,
221					int position, long arg3) {
222				if (!isActionMode) {
223					accountListView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
224					accountListView.setItemChecked(position,true);
225					selectedAccountForActionMode = accountList.get(position);
226					actionMode = activity.startActionMode((new ActionMode.Callback() {
227						
228						@Override
229						public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
230							if (selectedAccountForActionMode.isOptionSet(Account.OPTION_DISABLED)) {
231					        	menu.findItem(R.id.mgmt_account_enable).setVisible(true);
232					        	menu.findItem(R.id.mgmt_account_disable).setVisible(false);
233					        } else {
234					        	menu.findItem(R.id.mgmt_account_disable).setVisible(true);
235					        	menu.findItem(R.id.mgmt_account_enable).setVisible(false);
236					        }
237							return true;
238						}
239						
240						@Override
241						public void onDestroyActionMode(ActionMode mode) {
242							// TODO Auto-generated method stub
243							
244						}
245						
246						@Override
247						public boolean onCreateActionMode(ActionMode mode, Menu menu) {
248							MenuInflater inflater = mode.getMenuInflater();
249					        inflater.inflate(R.menu.manageaccounts_context, menu);
250							return true;
251						}
252						
253						@Override
254						public boolean onActionItemClicked(final ActionMode mode, MenuItem item) {
255							if (item.getItemId()==R.id.mgmt_account_edit) {
256								editAccount(selectedAccountForActionMode);
257							} else if (item.getItemId()==R.id.mgmt_account_disable) {
258								selectedAccountForActionMode.setOption(Account.OPTION_DISABLED, true);
259								xmppConnectionService.updateAccount(selectedAccountForActionMode);
260								mode.finish();
261							} else if (item.getItemId()==R.id.mgmt_account_enable) {
262								selectedAccountForActionMode.setOption(Account.OPTION_DISABLED, false);
263								xmppConnectionService.updateAccount(selectedAccountForActionMode);
264								mode.finish();
265							} else if (item.getItemId()==R.id.mgmt_account_delete) {
266								AlertDialog.Builder builder = new AlertDialog.Builder(activity);
267								builder.setTitle("Are you sure?");
268								builder.setIconAttribute(android.R.attr.alertDialogIcon);
269								builder.setMessage("If you delete your account your entire conversation history will be lost");
270								builder.setPositiveButton("Delete", new OnClickListener() {
271									
272									@Override
273									public void onClick(DialogInterface dialog, int which) {
274										xmppConnectionService.deleteAccount(selectedAccountForActionMode);
275										selectedAccountForActionMode = null;
276										mode.finish();
277									}
278								});
279								builder.setNegativeButton("Cancel",null);
280								builder.create().show();
281							} else if (item.getItemId()==R.id.mgmt_account_announce_pgp) {
282								if (activity.hasPgp()) {
283									mode.finish();
284									announcePgp();
285								}
286							} else if (item.getItemId() == R.id.mgmt_otr_key) {
287								AlertDialog.Builder builder = new AlertDialog.Builder(activity);
288								builder.setTitle("OTR Fingerprint");
289								String fingerprintTxt = selectedAccountForActionMode.getOtrFingerprint(getApplicationContext());
290								View view = (View) getLayoutInflater().inflate(R.layout.otr_fingerprint, null);
291								if (fingerprintTxt!=null) {
292									TextView fingerprint = (TextView) view.findViewById(R.id.otr_fingerprint);
293									fingerprint.setText(fingerprintTxt);
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	private void announcePgp() {
366		final Account account = selectedAccountForActionMode;
367		xmppConnectionService.getPgpEngine().generateSignature(account, "online", new OnPgpEngineResult() {
368			
369			@Override
370			public void userInputRequried(PendingIntent pi) {
371				try {
372					startIntentSenderForResult(pi.getIntentSender(), REQUEST_ANNOUNCE_PGP, null, 0, 0, 0);
373				} catch (SendIntentException e) {
374					Log.d("xmppService","coulnd start intent for pgp anncouncment");
375				}
376			}
377			
378			@Override
379			public void success() {
380				xmppConnectionService.databaseBackend.updateAccount(account);
381				xmppConnectionService.sendPgpPresence(account, account.getPgpSignature());
382			}
383			
384			@Override
385			public void error(OpenPgpError openPgpError) {
386				// TODO Auto-generated method stub
387				
388			}
389		});
390	}
391	
392	@Override
393	protected void onStop() {
394		if (xmppConnectionServiceBound) {
395			xmppConnectionService.removeOnAccountListChangedListener();
396			xmppConnectionService.removeOnTLSExceptionReceivedListener();
397		}
398		super.onStop();
399	}
400
401	@Override
402	void onBackendConnected() {
403		xmppConnectionService.setOnAccountListChangedListener(accountChanged);
404		xmppConnectionService.setOnTLSExceptionReceivedListener(tlsExceptionReceived);
405		this.accountList.clear();
406		this.accountList.addAll(xmppConnectionService.getAccounts());
407		accountListViewAdapter.notifyDataSetChanged();
408		if ((this.accountList.size() == 0)&&(this.firstrun)) {
409			getActionBar().setDisplayHomeAsUpEnabled(false);
410			addAccount();
411			this.firstrun = false;
412		}
413	}
414
415	@Override
416	public boolean onCreateOptionsMenu(Menu menu) {
417		getMenuInflater().inflate(R.menu.manageaccounts, menu);
418		return true;
419	}
420
421	@Override
422	public boolean onOptionsItemSelected(MenuItem item) {
423		switch (item.getItemId()) {
424		case R.id.action_add_account:
425			addAccount();
426			break;
427		default:
428			break;
429		}
430		return super.onOptionsItemSelected(item);
431	}
432
433	private void editAccount(Account account) {
434			EditAccount dialog = new EditAccount();
435			dialog.setAccount(account);
436			dialog.setEditAccountListener(new EditAccountListener() {
437
438				@Override
439				public void onAccountEdited(Account account) {
440					xmppConnectionService.updateAccount(account);
441					if (actionMode != null) { 
442						actionMode.finish();
443					}
444				}
445			});
446			dialog.show(getFragmentManager(), "edit_account");
447		
448	}
449	
450	protected void addAccount() {
451		final Activity activity = this;
452		EditAccount dialog = new EditAccount();
453		dialog.setEditAccountListener(new EditAccountListener() {
454
455			@Override
456			public void onAccountEdited(Account account) {
457				xmppConnectionService.createAccount(account);
458				activity.getActionBar().setDisplayHomeAsUpEnabled(true);
459			}
460		});
461		dialog.show(getFragmentManager(), "add_account");
462	}
463
464	
465	@Override
466	public void onActionModeStarted(ActionMode mode) {
467		super.onActionModeStarted(mode);
468		this.isActionMode = true;
469	}
470	
471	@Override
472	public void onActionModeFinished(ActionMode mode) {
473		super.onActionModeFinished(mode);
474		this.isActionMode = false;
475		accountListView.clearChoices();
476		accountListView.requestLayout();
477		accountListView.post(new Runnable() {
478            @Override
479            public void run() {
480                accountListView.setChoiceMode(ListView.CHOICE_MODE_NONE);
481            }
482        });
483	}
484	
485	 @Override
486	 protected void onActivityResult(int requestCode, int resultCode, Intent data) {
487		 super.onActivityResult(requestCode, resultCode, data);
488		 if (resultCode == RESULT_OK) {
489			if (requestCode == REQUEST_ANNOUNCE_PGP) {
490				announcePgp();
491			 }
492		 }
493	 }
494}