ManageAccountActivity.java

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