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