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.supportOpenPgp());
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		} else {
158			addAccount.setVisible(!Config.LOCK_SETTINGS);
159		}
160		addAccountWithCertificate.setVisible(!Config.LOCK_SETTINGS);
161
162		if (!accountsLeftToEnable()) {
163			enableAll.setVisible(false);
164		}
165		MenuItem disableAll = menu.findItem(R.id.action_disable_all);
166		if (!accountsLeftToDisable()) {
167			disableAll.setVisible(false);
168		}
169		return true;
170	}
171
172	@Override
173	public boolean onContextItemSelected(MenuItem item) {
174		switch (item.getItemId()) {
175			case R.id.mgmt_account_publish_avatar:
176				publishAvatar(selectedAccount);
177				return true;
178			case R.id.mgmt_account_disable:
179				disableAccount(selectedAccount);
180				return true;
181			case R.id.mgmt_account_enable:
182				enableAccount(selectedAccount);
183				return true;
184			case R.id.mgmt_account_delete:
185				deleteAccount(selectedAccount);
186				return true;
187			case R.id.mgmt_account_announce_pgp:
188				publishOpenPGPPublicKey(selectedAccount);
189				return true;
190			default:
191				return super.onContextItemSelected(item);
192		}
193	}
194
195	@Override
196	public boolean onOptionsItemSelected(MenuItem item) {
197		switch (item.getItemId()) {
198			case R.id.action_add_account:
199				startActivity(new Intent(getApplicationContext(),
200						EditAccountActivity.class));
201				break;
202			case R.id.action_disable_all:
203				disableAllAccounts();
204				break;
205			case R.id.action_enable_all:
206				enableAllAccounts();
207				break;
208			case R.id.action_add_account_with_cert:
209				addAccountFromKey();
210				break;
211			default:
212				break;
213		}
214		return super.onOptionsItemSelected(item);
215	}
216
217	@Override
218	public boolean onNavigateUp() {
219		if (xmppConnectionService.getConversations().size() == 0) {
220			Intent contactsIntent = new Intent(this,
221					StartConversationActivity.class);
222			contactsIntent.setFlags(
223					// if activity exists in stack, pop the stack and go back to it
224					Intent.FLAG_ACTIVITY_CLEAR_TOP |
225							// otherwise, make a new task for it
226							Intent.FLAG_ACTIVITY_NEW_TASK |
227							// don't use the new activity animation; finish
228							// animation runs instead
229							Intent.FLAG_ACTIVITY_NO_ANIMATION);
230			startActivity(contactsIntent);
231			finish();
232			return true;
233		} else {
234			return super.onNavigateUp();
235		}
236	}
237
238	public void onClickTglAccountState(Account account, boolean enable) {
239		if (enable) {
240			enableAccount(account);
241		} else {
242			disableAccount(account);
243		}
244	}
245
246	private void addAccountFromKey() {
247		try {
248			KeyChain.choosePrivateKeyAlias(this, this, null, null, null, -1, null);
249		} catch (ActivityNotFoundException e) {
250			Toast.makeText(this, R.string.device_does_not_support_certificates, Toast.LENGTH_LONG).show();
251		}
252	}
253
254	private void publishAvatar(Account account) {
255		Intent intent = new Intent(getApplicationContext(),
256				PublishProfilePictureActivity.class);
257		intent.putExtra(EXTRA_ACCOUNT, account.getJid().toString());
258		startActivity(intent);
259	}
260
261	private void disableAllAccounts() {
262		List<Account> list = new ArrayList<>();
263		synchronized (this.accountList) {
264			for (Account account : this.accountList) {
265				if (!account.isOptionSet(Account.OPTION_DISABLED)) {
266					list.add(account);
267				}
268			}
269		}
270		for (Account account : list) {
271			disableAccount(account);
272		}
273	}
274
275	private boolean accountsLeftToDisable() {
276		synchronized (this.accountList) {
277			for (Account account : this.accountList) {
278				if (!account.isOptionSet(Account.OPTION_DISABLED)) {
279					return true;
280				}
281			}
282			return false;
283		}
284	}
285
286	private boolean accountsLeftToEnable() {
287		synchronized (this.accountList) {
288			for (Account account : this.accountList) {
289				if (account.isOptionSet(Account.OPTION_DISABLED)) {
290					return true;
291				}
292			}
293			return false;
294		}
295	}
296
297	private void enableAllAccounts() {
298		List<Account> list = new ArrayList<>();
299		synchronized (this.accountList) {
300			for (Account account : this.accountList) {
301				if (account.isOptionSet(Account.OPTION_DISABLED)) {
302					list.add(account);
303				}
304			}
305		}
306		for (Account account : list) {
307			enableAccount(account);
308		}
309	}
310
311	private void disableAccount(Account account) {
312		account.setOption(Account.OPTION_DISABLED, true);
313		xmppConnectionService.updateAccount(account);
314	}
315
316	private void enableAccount(Account account) {
317		account.setOption(Account.OPTION_DISABLED, false);
318		xmppConnectionService.updateAccount(account);
319	}
320
321	private void publishOpenPGPPublicKey(Account account) {
322		if (ManageAccountActivity.this.hasPgp()) {
323			choosePgpSignId(selectedAccount);
324		} else {
325			this.showInstallPgpDialog();
326		}
327	}
328
329	private void deleteAccount(final Account account) {
330		AlertDialog.Builder builder = new AlertDialog.Builder(
331				ManageAccountActivity.this);
332		builder.setTitle(getString(R.string.mgmt_account_are_you_sure));
333		builder.setIconAttribute(android.R.attr.alertDialogIcon);
334		builder.setMessage(getString(R.string.mgmt_account_delete_confirm_text));
335		builder.setPositiveButton(getString(R.string.delete),
336				new OnClickListener() {
337					@Override
338					public void onClick(DialogInterface dialog, int which) {
339						xmppConnectionService.deleteAccount(account);
340						selectedAccount = null;
341					}
342				});
343		builder.setNegativeButton(getString(R.string.cancel), null);
344		builder.create().show();
345	}
346
347	@Override
348	protected void onActivityResult(int requestCode, int resultCode, Intent data) {
349		super.onActivityResult(requestCode, resultCode, data);
350		if (resultCode == RESULT_OK) {
351			if (xmppConnectionServiceBound) {
352				if (requestCode == REQUEST_CHOOSE_PGP_ID) {
353					if (data.getExtras().containsKey(OpenPgpApi.EXTRA_SIGN_KEY_ID)) {
354						selectedAccount.setPgpSignId(data.getExtras().getLong(OpenPgpApi.EXTRA_SIGN_KEY_ID));
355						announcePgp(selectedAccount, null);
356					} else {
357						choosePgpSignId(selectedAccount);
358					}
359				} else if (requestCode == REQUEST_ANNOUNCE_PGP) {
360					announcePgp(selectedAccount, null);
361				}
362				this.mPostponedActivityResult = null;
363			} else {
364				this.mPostponedActivityResult = new Pair<>(requestCode, data);
365			}
366		}
367	}
368
369	@Override
370	public void alias(String alias) {
371		if (alias != null) {
372			xmppConnectionService.createAccountFromKey(alias, this);
373		}
374	}
375
376	@Override
377	public void onAccountCreated(Account account) {
378		switchToAccount(account, true);
379	}
380
381	@Override
382	public void informUser(final int r) {
383		runOnUiThread(new Runnable() {
384			@Override
385			public void run() {
386				Toast.makeText(ManageAccountActivity.this, r, Toast.LENGTH_LONG).show();
387			}
388		});
389	}
390}