ManageAccountActivity.java

  1package eu.siacs.conversations.ui;
  2
  3import android.content.ActivityNotFoundException;
  4import android.content.Intent;
  5import android.os.Bundle;
  6import android.security.KeyChain;
  7import android.security.KeyChainAliasCallback;
  8import android.support.v7.app.ActionBar;
  9import android.support.v7.app.AlertDialog;
 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 org.openintents.openpgp.util.OpenPgpApi;
 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.ui.util.MenuDoubleTabUtil;
 35import eu.siacs.conversations.xmpp.XmppConnection;
 36import rocks.xmpp.addr.Jid;
 37
 38public class ManageAccountActivity extends XmppActivity implements OnAccountUpdate, KeyChainAliasCallback, XmppConnectionService.OnAccountCreated {
 39
 40	private final String STATE_SELECTED_ACCOUNT = "selected_account";
 41
 42	protected Account selectedAccount = null;
 43	protected Jid selectedAccountJid = null;
 44
 45	protected final List<Account> accountList = new ArrayList<>();
 46	protected ListView accountListView;
 47	protected AccountAdapter mAccountAdapter;
 48	protected AtomicBoolean mInvokedAddAccount = new AtomicBoolean(false);
 49
 50	protected Pair<Integer, Intent> mPostponedActivityResult = null;
 51
 52	@Override
 53	public void onAccountUpdate() {
 54		refreshUi();
 55	}
 56
 57	@Override
 58	protected void refreshUiReal() {
 59		synchronized (this.accountList) {
 60			accountList.clear();
 61			accountList.addAll(xmppConnectionService.getAccounts());
 62		}
 63		ActionBar actionBar = getSupportActionBar();
 64		if (actionBar != null) {
 65			actionBar.setHomeButtonEnabled(this.accountList.size() > 0);
 66			actionBar.setDisplayHomeAsUpEnabled(this.accountList.size() > 0);
 67		}
 68		invalidateOptionsMenu();
 69		mAccountAdapter.notifyDataSetChanged();
 70	}
 71
 72	@Override
 73	protected void onCreate(Bundle savedInstanceState) {
 74
 75		super.onCreate(savedInstanceState);
 76
 77		setContentView(R.layout.activity_manage_accounts);
 78		setSupportActionBar(findViewById(R.id.toolbar));
 79		configureActionBar(getSupportActionBar());
 80		if (savedInstanceState != null) {
 81			String jid = savedInstanceState.getString(STATE_SELECTED_ACCOUNT);
 82			if (jid != null) {
 83				try {
 84					this.selectedAccountJid = Jid.of(jid);
 85				} catch (IllegalArgumentException 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().asBareJid().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.isEnabled()) {
130			menu.findItem(R.id.mgmt_account_enable).setVisible(false);
131			menu.findItem(R.id.mgmt_account_announce_pgp).setVisible(Config.supportOpenPgp());
132		} else {
133			menu.findItem(R.id.mgmt_account_disable).setVisible(false);
134			menu.findItem(R.id.mgmt_account_announce_pgp).setVisible(false);
135			menu.findItem(R.id.mgmt_account_publish_avatar).setVisible(false);
136		}
137		menu.setHeaderTitle(this.selectedAccount.getJid().asBareJid().toString());
138	}
139
140	@Override
141	void onBackendConnected() {
142		if (selectedAccountJid != null) {
143			this.selectedAccount = xmppConnectionService.findAccountByJid(selectedAccountJid);
144		}
145		refreshUiReal();
146		if (this.mPostponedActivityResult != null) {
147			this.onActivityResult(mPostponedActivityResult.first, RESULT_OK, mPostponedActivityResult.second);
148		}
149		if (Config.X509_VERIFICATION && this.accountList.size() == 0) {
150			if (mInvokedAddAccount.compareAndSet(false, true)) {
151				addAccountFromKey();
152			}
153		}
154	}
155
156	@Override
157	public boolean onCreateOptionsMenu(Menu menu) {
158		getMenuInflater().inflate(R.menu.manageaccounts, menu);
159		MenuItem enableAll = menu.findItem(R.id.action_enable_all);
160		MenuItem addAccount = menu.findItem(R.id.action_add_account);
161		MenuItem addAccountWithCertificate = menu.findItem(R.id.action_add_account_with_cert);
162
163		if (Config.X509_VERIFICATION) {
164			addAccount.setVisible(false);
165			addAccountWithCertificate.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
166		}
167
168		if (!accountsLeftToEnable()) {
169			enableAll.setVisible(false);
170		}
171		MenuItem disableAll = menu.findItem(R.id.action_disable_all);
172		if (!accountsLeftToDisable()) {
173			disableAll.setVisible(false);
174		}
175		return true;
176	}
177
178	@Override
179	public boolean onContextItemSelected(MenuItem item) {
180		switch (item.getItemId()) {
181			case R.id.mgmt_account_publish_avatar:
182				publishAvatar(selectedAccount);
183				return true;
184			case R.id.mgmt_account_disable:
185				disableAccount(selectedAccount);
186				return true;
187			case R.id.mgmt_account_enable:
188				enableAccount(selectedAccount);
189				return true;
190			case R.id.mgmt_account_delete:
191				deleteAccount(selectedAccount);
192				return true;
193			case R.id.mgmt_account_announce_pgp:
194				publishOpenPGPPublicKey(selectedAccount);
195				return true;
196			default:
197				return super.onContextItemSelected(item);
198		}
199	}
200
201	@Override
202	public boolean onOptionsItemSelected(MenuItem item) {
203		if (MenuDoubleTabUtil.shouldIgnoreTap()) {
204			return false;
205		}
206		switch (item.getItemId()) {
207			case R.id.action_add_account:
208				startActivity(new Intent(getApplicationContext(),
209						EditAccountActivity.class));
210				break;
211			case R.id.action_disable_all:
212				disableAllAccounts();
213				break;
214			case R.id.action_enable_all:
215				enableAllAccounts();
216				break;
217			case R.id.action_add_account_with_cert:
218				addAccountFromKey();
219				break;
220			default:
221				break;
222		}
223		return super.onOptionsItemSelected(item);
224	}
225
226	@Override
227	public boolean onNavigateUp() {
228		if (xmppConnectionService.getConversations().size() == 0) {
229			Intent contactsIntent = new Intent(this,
230					StartConversationActivity.class);
231			contactsIntent.setFlags(
232					// if activity exists in stack, pop the stack and go back to it
233					Intent.FLAG_ACTIVITY_CLEAR_TOP |
234							// otherwise, make a new task for it
235							Intent.FLAG_ACTIVITY_NEW_TASK |
236							// don't use the new activity animation; finish
237							// animation runs instead
238							Intent.FLAG_ACTIVITY_NO_ANIMATION);
239			startActivity(contactsIntent);
240			finish();
241			return true;
242		} else {
243			return super.onNavigateUp();
244		}
245	}
246
247	public void onClickTglAccountState(Account account, boolean enable) {
248		if (enable) {
249			enableAccount(account);
250		} else {
251			disableAccount(account);
252		}
253	}
254
255	private void addAccountFromKey() {
256		try {
257			KeyChain.choosePrivateKeyAlias(this, this, null, null, null, -1, null);
258		} catch (ActivityNotFoundException e) {
259			Toast.makeText(this, R.string.device_does_not_support_certificates, Toast.LENGTH_LONG).show();
260		}
261	}
262
263	private void publishAvatar(Account account) {
264		Intent intent = new Intent(getApplicationContext(),
265				PublishProfilePictureActivity.class);
266		intent.putExtra(EXTRA_ACCOUNT, account.getJid().toString());
267		startActivity(intent);
268	}
269
270	private void disableAllAccounts() {
271		List<Account> list = new ArrayList<>();
272		synchronized (this.accountList) {
273			for (Account account : this.accountList) {
274				if (account.isEnabled()) {
275					list.add(account);
276				}
277			}
278		}
279		for (Account account : list) {
280			disableAccount(account);
281		}
282	}
283
284	private boolean accountsLeftToDisable() {
285		synchronized (this.accountList) {
286			for (Account account : this.accountList) {
287				if (account.isEnabled()) {
288					return true;
289				}
290			}
291			return false;
292		}
293	}
294
295	private boolean accountsLeftToEnable() {
296		synchronized (this.accountList) {
297			for (Account account : this.accountList) {
298				if (!account.isEnabled()) {
299					return true;
300				}
301			}
302			return false;
303		}
304	}
305
306	private void enableAllAccounts() {
307		List<Account> list = new ArrayList<>();
308		synchronized (this.accountList) {
309			for (Account account : this.accountList) {
310				if (!account.isEnabled()) {
311					list.add(account);
312				}
313			}
314		}
315		for (Account account : list) {
316			enableAccount(account);
317		}
318	}
319
320	private void disableAccount(Account account) {
321		account.setOption(Account.OPTION_DISABLED, true);
322		if (!xmppConnectionService.updateAccount(account)) {
323			Toast.makeText(this,R.string.unable_to_update_account,Toast.LENGTH_SHORT).show();
324		}
325	}
326
327	private void enableAccount(Account account) {
328		account.setOption(Account.OPTION_DISABLED, false);
329		final XmppConnection connection = account.getXmppConnection();
330		if (connection != null) {
331			connection.resetEverything();
332		}
333		if (!xmppConnectionService.updateAccount(account)) {
334			Toast.makeText(this,R.string.unable_to_update_account,Toast.LENGTH_SHORT).show();
335		}
336	}
337
338	private void publishOpenPGPPublicKey(Account account) {
339		if (ManageAccountActivity.this.hasPgp()) {
340			announcePgp(selectedAccount, null,null, onOpenPGPKeyPublished);
341		} else {
342			this.showInstallPgpDialog();
343		}
344	}
345
346	private void deleteAccount(final Account account) {
347		AlertDialog.Builder builder = new AlertDialog.Builder(this);
348		builder.setTitle(getString(R.string.mgmt_account_are_you_sure));
349		builder.setIconAttribute(android.R.attr.alertDialogIcon);
350		builder.setMessage(getString(R.string.mgmt_account_delete_confirm_text));
351		builder.setPositiveButton(getString(R.string.delete),
352				(dialog, which) -> {
353					xmppConnectionService.deleteAccount(account);
354					selectedAccount = null;
355					if (xmppConnectionService.getAccounts().size() == 0 && Config.MAGIC_CREATE_DOMAIN != null) {
356						WelcomeActivity.launch(this);
357					}
358				});
359		builder.setNegativeButton(getString(R.string.cancel), null);
360		builder.create().show();
361	}
362
363	@Override
364	protected void onActivityResult(int requestCode, int resultCode, Intent data) {
365		super.onActivityResult(requestCode, resultCode, data);
366		if (resultCode == RESULT_OK) {
367			if (xmppConnectionServiceBound) {
368				if (requestCode == REQUEST_CHOOSE_PGP_ID) {
369					if (data.getExtras().containsKey(OpenPgpApi.EXTRA_SIGN_KEY_ID)) {
370						selectedAccount.setPgpSignId(data.getExtras().getLong(OpenPgpApi.EXTRA_SIGN_KEY_ID));
371						announcePgp(selectedAccount, null, null, onOpenPGPKeyPublished);
372					} else {
373						choosePgpSignId(selectedAccount);
374					}
375				} else if (requestCode == REQUEST_ANNOUNCE_PGP) {
376					announcePgp(selectedAccount, null, data, onOpenPGPKeyPublished);
377				}
378				this.mPostponedActivityResult = null;
379			} else {
380				this.mPostponedActivityResult = new Pair<>(requestCode, data);
381			}
382		}
383	}
384
385	@Override
386	public void alias(String alias) {
387		if (alias != null) {
388			xmppConnectionService.createAccountFromKey(alias, this);
389		}
390	}
391
392	@Override
393	public void onAccountCreated(Account account) {
394		Intent intent = new Intent(this, EditAccountActivity.class);
395		intent.putExtra("jid", account.getJid().asBareJid().toString());
396		intent.putExtra("init", true);
397		startActivity(intent);
398	}
399
400	@Override
401	public void informUser(final int r) {
402		runOnUiThread(() -> Toast.makeText(ManageAccountActivity.this, r, Toast.LENGTH_LONG).show());
403	}
404}