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		setSupportActionBar(findViewById(R.id.toolbar));
 78		configureActionBar(getSupportActionBar());
 79		if (savedInstanceState != null) {
 80			String jid = savedInstanceState.getString(STATE_SELECTED_ACCOUNT);
 81			if (jid != null) {
 82				try {
 83					this.selectedAccountJid = Jid.of(jid);
 84				} catch (IllegalArgumentException e) {
 85					this.selectedAccountJid = null;
 86				}
 87			}
 88		}
 89
 90		accountListView = (ListView) findViewById(R.id.account_list);
 91		this.mAccountAdapter = new AccountAdapter(this, accountList);
 92		accountListView.setAdapter(this.mAccountAdapter);
 93		accountListView.setOnItemClickListener(new OnItemClickListener() {
 94
 95			@Override
 96			public void onItemClick(AdapterView<?> arg0, View view,
 97									int position, long arg3) {
 98				switchToAccount(accountList.get(position));
 99			}
100		});
101		registerForContextMenu(accountListView);
102	}
103
104	@Override
105	protected void onStart() {
106		super.onStart();
107		final int theme = findTheme();
108		if (this.mTheme != theme) {
109			recreate();
110		}
111	}
112
113	@Override
114	public void onSaveInstanceState(final Bundle savedInstanceState) {
115		if (selectedAccount != null) {
116			savedInstanceState.putString(STATE_SELECTED_ACCOUNT, selectedAccount.getJid().asBareJid().toString());
117		}
118		super.onSaveInstanceState(savedInstanceState);
119	}
120
121	@Override
122	public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
123		super.onCreateContextMenu(menu, v, menuInfo);
124		ManageAccountActivity.this.getMenuInflater().inflate(
125				R.menu.manageaccounts_context, menu);
126		AdapterContextMenuInfo acmi = (AdapterContextMenuInfo) menuInfo;
127		this.selectedAccount = accountList.get(acmi.position);
128		if (this.selectedAccount.isEnabled()) {
129			menu.findItem(R.id.mgmt_account_enable).setVisible(false);
130			menu.findItem(R.id.mgmt_account_announce_pgp).setVisible(Config.supportOpenPgp());
131		} else {
132			menu.findItem(R.id.mgmt_account_disable).setVisible(false);
133			menu.findItem(R.id.mgmt_account_announce_pgp).setVisible(false);
134			menu.findItem(R.id.mgmt_account_publish_avatar).setVisible(false);
135		}
136		menu.setHeaderTitle(this.selectedAccount.getJid().asBareJid().toString());
137	}
138
139	@Override
140	void onBackendConnected() {
141		if (selectedAccountJid != null) {
142			this.selectedAccount = xmppConnectionService.findAccountByJid(selectedAccountJid);
143		}
144		refreshUiReal();
145		if (this.mPostponedActivityResult != null) {
146			this.onActivityResult(mPostponedActivityResult.first, RESULT_OK, mPostponedActivityResult.second);
147		}
148		if (Config.X509_VERIFICATION && this.accountList.size() == 0) {
149			if (mInvokedAddAccount.compareAndSet(false, true)) {
150				addAccountFromKey();
151			}
152		}
153	}
154
155	@Override
156	public boolean onCreateOptionsMenu(Menu menu) {
157		getMenuInflater().inflate(R.menu.manageaccounts, menu);
158		MenuItem enableAll = menu.findItem(R.id.action_enable_all);
159		MenuItem addAccount = menu.findItem(R.id.action_add_account);
160		MenuItem addAccountWithCertificate = menu.findItem(R.id.action_add_account_with_cert);
161
162		if (Config.X509_VERIFICATION) {
163			addAccount.setVisible(false);
164			addAccountWithCertificate.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
165		}
166
167		if (!accountsLeftToEnable()) {
168			enableAll.setVisible(false);
169		}
170		MenuItem disableAll = menu.findItem(R.id.action_disable_all);
171		if (!accountsLeftToDisable()) {
172			disableAll.setVisible(false);
173		}
174		return true;
175	}
176
177	@Override
178	public boolean onContextItemSelected(MenuItem item) {
179		switch (item.getItemId()) {
180			case R.id.mgmt_account_publish_avatar:
181				publishAvatar(selectedAccount);
182				return true;
183			case R.id.mgmt_account_disable:
184				disableAccount(selectedAccount);
185				return true;
186			case R.id.mgmt_account_enable:
187				enableAccount(selectedAccount);
188				return true;
189			case R.id.mgmt_account_delete:
190				deleteAccount(selectedAccount);
191				return true;
192			case R.id.mgmt_account_announce_pgp:
193				publishOpenPGPPublicKey(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	public void onClickTglAccountState(Account account, boolean enable) {
244		if (enable) {
245			enableAccount(account);
246		} else {
247			disableAccount(account);
248		}
249	}
250
251	private void addAccountFromKey() {
252		try {
253			KeyChain.choosePrivateKeyAlias(this, this, null, null, null, -1, null);
254		} catch (ActivityNotFoundException e) {
255			Toast.makeText(this, R.string.device_does_not_support_certificates, Toast.LENGTH_LONG).show();
256		}
257	}
258
259	private void publishAvatar(Account account) {
260		Intent intent = new Intent(getApplicationContext(),
261				PublishProfilePictureActivity.class);
262		intent.putExtra(EXTRA_ACCOUNT, account.getJid().toString());
263		startActivity(intent);
264	}
265
266	private void disableAllAccounts() {
267		List<Account> list = new ArrayList<>();
268		synchronized (this.accountList) {
269			for (Account account : this.accountList) {
270				if (account.isEnabled()) {
271					list.add(account);
272				}
273			}
274		}
275		for (Account account : list) {
276			disableAccount(account);
277		}
278	}
279
280	private boolean accountsLeftToDisable() {
281		synchronized (this.accountList) {
282			for (Account account : this.accountList) {
283				if (account.isEnabled()) {
284					return true;
285				}
286			}
287			return false;
288		}
289	}
290
291	private boolean accountsLeftToEnable() {
292		synchronized (this.accountList) {
293			for (Account account : this.accountList) {
294				if (!account.isEnabled()) {
295					return true;
296				}
297			}
298			return false;
299		}
300	}
301
302	private void enableAllAccounts() {
303		List<Account> list = new ArrayList<>();
304		synchronized (this.accountList) {
305			for (Account account : this.accountList) {
306				if (!account.isEnabled()) {
307					list.add(account);
308				}
309			}
310		}
311		for (Account account : list) {
312			enableAccount(account);
313		}
314	}
315
316	private void disableAccount(Account account) {
317		account.setOption(Account.OPTION_DISABLED, true);
318		if (!xmppConnectionService.updateAccount(account)) {
319			Toast.makeText(this,R.string.unable_to_update_account,Toast.LENGTH_SHORT).show();
320		}
321	}
322
323	private void enableAccount(Account account) {
324		account.setOption(Account.OPTION_DISABLED, false);
325		final XmppConnection connection = account.getXmppConnection();
326		if (connection != null) {
327			connection.resetEverything();
328		}
329		if (!xmppConnectionService.updateAccount(account)) {
330			Toast.makeText(this,R.string.unable_to_update_account,Toast.LENGTH_SHORT).show();
331		}
332	}
333
334	private void publishOpenPGPPublicKey(Account account) {
335		if (ManageAccountActivity.this.hasPgp()) {
336			announcePgp(selectedAccount, null,null, onOpenPGPKeyPublished);
337		} else {
338			this.showInstallPgpDialog();
339		}
340	}
341
342	private void deleteAccount(final Account account) {
343		AlertDialog.Builder builder = new AlertDialog.Builder(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				(dialog, which) -> {
349					xmppConnectionService.deleteAccount(account);
350					selectedAccount = null;
351					if (xmppConnectionService.getAccounts().size() == 0 && Config.MAGIC_CREATE_DOMAIN != null) {
352						WelcomeActivity.launch(this);
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, null, onOpenPGPKeyPublished);
368					} else {
369						choosePgpSignId(selectedAccount);
370					}
371				} else if (requestCode == REQUEST_ANNOUNCE_PGP) {
372					announcePgp(selectedAccount, null, data, 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		Intent intent = new Intent(this, EditAccountActivity.class);
391		intent.putExtra("jid", account.getJid().asBareJid().toString());
392		intent.putExtra("init", true);
393		startActivity(intent);
394	}
395
396	@Override
397	public void informUser(final int r) {
398		runOnUiThread(() -> Toast.makeText(ManageAccountActivity.this, r, Toast.LENGTH_LONG).show());
399	}
400}