ManageAccountActivity.java

  1package eu.siacs.conversations.ui;
  2
  3import android.app.ActionBar;
  4import android.app.AlertDialog;
  5import android.content.ActivityNotFoundException;
  6import android.content.DialogInterface;
  7import android.content.DialogInterface.OnClickListener;
  8import android.content.Intent;
  9import android.os.Bundle;
 10import android.security.KeyChain;
 11import android.security.KeyChainAliasCallback;
 12import android.util.Pair;
 13import android.view.ContextMenu;
 14import android.view.ContextMenu.ContextMenuInfo;
 15import android.view.Menu;
 16import android.view.MenuItem;
 17import android.view.View;
 18import android.widget.AdapterView;
 19import android.widget.AdapterView.AdapterContextMenuInfo;
 20import android.widget.AdapterView.OnItemClickListener;
 21import android.widget.ListView;
 22import android.widget.Toast;
 23
 24import java.util.ArrayList;
 25import java.util.List;
 26import java.util.concurrent.atomic.AtomicBoolean;
 27
 28import eu.siacs.conversations.Config;
 29import eu.siacs.conversations.R;
 30import eu.siacs.conversations.entities.Account;
 31import eu.siacs.conversations.services.XmppConnectionService;
 32import eu.siacs.conversations.services.XmppConnectionService.OnAccountUpdate;
 33import eu.siacs.conversations.ui.adapter.AccountAdapter;
 34import eu.siacs.conversations.xmpp.jid.InvalidJidException;
 35import eu.siacs.conversations.xmpp.jid.Jid;
 36
 37import org.openintents.openpgp.util.OpenPgpApi;
 38
 39public class ManageAccountActivity extends XmppActivity implements OnAccountUpdate, KeyChainAliasCallback, XmppConnectionService.OnAccountCreated {
 40
 41	private final String STATE_SELECTED_ACCOUNT = "selected_account";
 42
 43	protected Account selectedAccount = null;
 44	protected Jid selectedAccountJid = null;
 45
 46	protected final List<Account> accountList = new ArrayList<>();
 47	protected ListView accountListView;
 48	protected AccountAdapter mAccountAdapter;
 49	protected AtomicBoolean mInvokedAddAccount = new AtomicBoolean(false);
 50
 51	protected Pair<Integer, Intent> mPostponedActivityResult = null;
 52
 53	@Override
 54	public void onAccountUpdate() {
 55		refreshUi();
 56	}
 57
 58	@Override
 59	protected void refreshUiReal() {
 60		synchronized (this.accountList) {
 61			accountList.clear();
 62			accountList.addAll(xmppConnectionService.getAccounts());
 63		}
 64		ActionBar actionBar = getActionBar();
 65		if (actionBar != null) {
 66			actionBar.setHomeButtonEnabled(this.accountList.size() > 0);
 67			actionBar.setDisplayHomeAsUpEnabled(this.accountList.size() > 0);
 68		}
 69		invalidateOptionsMenu();
 70		mAccountAdapter.notifyDataSetChanged();
 71	}
 72
 73	@Override
 74	protected void onCreate(Bundle savedInstanceState) {
 75
 76		super.onCreate(savedInstanceState);
 77
 78		setContentView(R.layout.manage_accounts);
 79
 80		if (savedInstanceState != null) {
 81			String jid = savedInstanceState.getString(STATE_SELECTED_ACCOUNT);
 82			if (jid != null) {
 83				try {
 84					this.selectedAccountJid = Jid.fromString(jid);
 85				} catch (InvalidJidException e) {
 86					this.selectedAccountJid = null;
 87				}
 88			}
 89		}
 90
 91		accountListView = (ListView) findViewById(R.id.account_list);
 92		this.mAccountAdapter = new AccountAdapter(this, accountList);
 93		accountListView.setAdapter(this.mAccountAdapter);
 94		accountListView.setOnItemClickListener(new OnItemClickListener() {
 95
 96			@Override
 97			public void onItemClick(AdapterView<?> arg0, View view,
 98									int position, long arg3) {
 99				switchToAccount(accountList.get(position));
100			}
101		});
102		registerForContextMenu(accountListView);
103	}
104
105	@Override
106	public void onSaveInstanceState(final Bundle savedInstanceState) {
107		if (selectedAccount != null) {
108			savedInstanceState.putString(STATE_SELECTED_ACCOUNT, selectedAccount.getJid().toBareJid().toString());
109		}
110		super.onSaveInstanceState(savedInstanceState);
111	}
112
113	@Override
114	public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
115		super.onCreateContextMenu(menu, v, menuInfo);
116		ManageAccountActivity.this.getMenuInflater().inflate(
117				R.menu.manageaccounts_context, menu);
118		AdapterContextMenuInfo acmi = (AdapterContextMenuInfo) menuInfo;
119		this.selectedAccount = accountList.get(acmi.position);
120		if (this.selectedAccount.isOptionSet(Account.OPTION_DISABLED)) {
121			menu.findItem(R.id.mgmt_account_disable).setVisible(false);
122			menu.findItem(R.id.mgmt_account_announce_pgp).setVisible(false);
123			menu.findItem(R.id.mgmt_account_publish_avatar).setVisible(false);
124		} else {
125			menu.findItem(R.id.mgmt_account_enable).setVisible(false);
126			menu.findItem(R.id.mgmt_account_announce_pgp).setVisible(!Config.HIDE_PGP_IN_UI);
127		}
128		menu.setHeaderTitle(this.selectedAccount.getJid().toBareJid().toString());
129	}
130
131	@Override
132	void onBackendConnected() {
133		if (selectedAccountJid != null) {
134			this.selectedAccount = xmppConnectionService.findAccountByJid(selectedAccountJid);
135		}
136		refreshUiReal();
137		if (this.mPostponedActivityResult != null) {
138			this.onActivityResult(mPostponedActivityResult.first, RESULT_OK, mPostponedActivityResult.second);
139		}
140		if (Config.X509_VERIFICATION && this.accountList.size() == 0) {
141			if (mInvokedAddAccount.compareAndSet(false, true)) {
142				addAccountFromKey();
143			}
144		}
145	}
146
147	@Override
148	public boolean onCreateOptionsMenu(Menu menu) {
149		getMenuInflater().inflate(R.menu.manageaccounts, menu);
150		MenuItem enableAll = menu.findItem(R.id.action_enable_all);
151		MenuItem addAccount = menu.findItem(R.id.action_add_account);
152		MenuItem addAccountWithCertificate = menu.findItem(R.id.action_add_account_with_cert);
153
154		if (Config.X509_VERIFICATION) {
155			addAccount.setVisible(false);
156			addAccountWithCertificate.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
157		}
158
159		if (!accountsLeftToEnable()) {
160			enableAll.setVisible(false);
161		}
162		MenuItem disableAll = menu.findItem(R.id.action_disable_all);
163		if (!accountsLeftToDisable()) {
164			disableAll.setVisible(false);
165		}
166		return true;
167	}
168
169	@Override
170	public boolean onContextItemSelected(MenuItem item) {
171		switch (item.getItemId()) {
172			case R.id.mgmt_account_publish_avatar:
173				publishAvatar(selectedAccount);
174				return true;
175			case R.id.mgmt_account_disable:
176				disableAccount(selectedAccount);
177				return true;
178			case R.id.mgmt_account_enable:
179				enableAccount(selectedAccount);
180				return true;
181			case R.id.mgmt_account_delete:
182				deleteAccount(selectedAccount);
183				return true;
184			case R.id.mgmt_account_announce_pgp:
185				publishOpenPGPPublicKey(selectedAccount);
186				return true;
187			default:
188				return super.onContextItemSelected(item);
189		}
190	}
191
192	@Override
193	public boolean onOptionsItemSelected(MenuItem item) {
194		switch (item.getItemId()) {
195			case R.id.action_add_account:
196				startActivity(new Intent(getApplicationContext(),
197						EditAccountActivity.class));
198				break;
199			case R.id.action_disable_all:
200				disableAllAccounts();
201				break;
202			case R.id.action_enable_all:
203				enableAllAccounts();
204				break;
205			case R.id.action_add_account_with_cert:
206				addAccountFromKey();
207				break;
208			default:
209				break;
210		}
211		return super.onOptionsItemSelected(item);
212	}
213
214	@Override
215	public boolean onNavigateUp() {
216		if (xmppConnectionService.getConversations().size() == 0) {
217			Intent contactsIntent = new Intent(this,
218					StartConversationActivity.class);
219			contactsIntent.setFlags(
220					// if activity exists in stack, pop the stack and go back to it
221					Intent.FLAG_ACTIVITY_CLEAR_TOP |
222							// otherwise, make a new task for it
223							Intent.FLAG_ACTIVITY_NEW_TASK |
224							// don't use the new activity animation; finish
225							// animation runs instead
226							Intent.FLAG_ACTIVITY_NO_ANIMATION);
227			startActivity(contactsIntent);
228			finish();
229			return true;
230		} else {
231			return super.onNavigateUp();
232		}
233	}
234
235	public void onClickTglAccountState(Account account, boolean enable) {
236		if (enable) {
237			enableAccount(account);
238		} else {
239			disableAccount(account);
240		}
241	}
242
243	private void addAccountFromKey() {
244		try {
245			KeyChain.choosePrivateKeyAlias(this, this, null, null, null, -1, null);
246		} catch (ActivityNotFoundException e) {
247			Toast.makeText(this, R.string.device_does_not_support_certificates, Toast.LENGTH_LONG).show();
248		}
249	}
250
251	private void publishAvatar(Account account) {
252		Intent intent = new Intent(getApplicationContext(),
253				PublishProfilePictureActivity.class);
254		intent.putExtra("account", account.getJid().toString());
255		startActivity(intent);
256	}
257
258	private void disableAllAccounts() {
259		List<Account> list = new ArrayList<>();
260		synchronized (this.accountList) {
261			for (Account account : this.accountList) {
262				if (!account.isOptionSet(Account.OPTION_DISABLED)) {
263					list.add(account);
264				}
265			}
266		}
267		for (Account account : list) {
268			disableAccount(account);
269		}
270	}
271
272	private boolean accountsLeftToDisable() {
273		synchronized (this.accountList) {
274			for (Account account : this.accountList) {
275				if (!account.isOptionSet(Account.OPTION_DISABLED)) {
276					return true;
277				}
278			}
279			return false;
280		}
281	}
282
283	private boolean accountsLeftToEnable() {
284		synchronized (this.accountList) {
285			for (Account account : this.accountList) {
286				if (account.isOptionSet(Account.OPTION_DISABLED)) {
287					return true;
288				}
289			}
290			return false;
291		}
292	}
293
294	private void enableAllAccounts() {
295		List<Account> list = new ArrayList<>();
296		synchronized (this.accountList) {
297			for (Account account : this.accountList) {
298				if (account.isOptionSet(Account.OPTION_DISABLED)) {
299					list.add(account);
300				}
301			}
302		}
303		for (Account account : list) {
304			enableAccount(account);
305		}
306	}
307
308	private void disableAccount(Account account) {
309		account.setOption(Account.OPTION_DISABLED, true);
310		xmppConnectionService.updateAccount(account);
311	}
312
313	private void enableAccount(Account account) {
314		account.setOption(Account.OPTION_DISABLED, false);
315		xmppConnectionService.updateAccount(account);
316	}
317
318	private void publishOpenPGPPublicKey(Account account) {
319		if (ManageAccountActivity.this.hasPgp()) {
320			choosePgpSignId(selectedAccount);
321		} else {
322			this.showInstallPgpDialog();
323		}
324	}
325
326	private void deleteAccount(final Account account) {
327		AlertDialog.Builder builder = new AlertDialog.Builder(
328				ManageAccountActivity.this);
329		builder.setTitle(getString(R.string.mgmt_account_are_you_sure));
330		builder.setIconAttribute(android.R.attr.alertDialogIcon);
331		builder.setMessage(getString(R.string.mgmt_account_delete_confirm_text));
332		builder.setPositiveButton(getString(R.string.delete),
333				new OnClickListener() {
334					@Override
335					public void onClick(DialogInterface dialog, int which) {
336						xmppConnectionService.deleteAccount(account);
337						selectedAccount = null;
338					}
339				});
340		builder.setNegativeButton(getString(R.string.cancel), null);
341		builder.create().show();
342	}
343
344	@Override
345	protected void onActivityResult(int requestCode, int resultCode, Intent data) {
346		super.onActivityResult(requestCode, resultCode, data);
347		if (resultCode == RESULT_OK) {
348			if (xmppConnectionServiceBound) {
349				if (requestCode == REQUEST_CHOOSE_PGP_ID) {
350					if (data.getExtras().containsKey(OpenPgpApi.EXTRA_SIGN_KEY_ID)) {
351						selectedAccount.setPgpSignId(data.getExtras().getLong(OpenPgpApi.EXTRA_SIGN_KEY_ID));
352						announcePgp(selectedAccount, null);
353					} else {
354						choosePgpSignId(selectedAccount);
355					}
356				} else if (requestCode == REQUEST_ANNOUNCE_PGP) {
357					announcePgp(selectedAccount, null);
358				}
359				this.mPostponedActivityResult = null;
360			} else {
361				this.mPostponedActivityResult = new Pair<>(requestCode, data);
362			}
363		}
364	}
365
366	@Override
367	public void alias(String alias) {
368		if (alias != null) {
369			xmppConnectionService.createAccountFromKey(alias, this);
370		}
371	}
372
373	@Override
374	public void onAccountCreated(Account account) {
375		switchToAccount(account, true);
376	}
377
378	@Override
379	public void informUser(final int r) {
380		runOnUiThread(new Runnable() {
381			@Override
382			public void run() {
383				Toast.makeText(ManageAccountActivity.this, r, Toast.LENGTH_LONG).show();
384			}
385		});
386	}
387}