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