ManageAccountActivity.java

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