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