EditAccountActivity.java

  1package eu.siacs.conversations.ui;
  2
  3import android.app.AlertDialog;
  4import android.app.AlertDialog.Builder;
  5import android.app.PendingIntent;
  6import android.content.DialogInterface;
  7import android.content.Intent;
  8import android.graphics.Bitmap;
  9import android.os.Bundle;
 10import android.security.KeyChain;
 11import android.security.KeyChainAliasCallback;
 12import android.text.Editable;
 13import android.text.TextWatcher;
 14import android.view.Menu;
 15import android.view.MenuItem;
 16import android.view.View;
 17import android.view.View.OnClickListener;
 18import android.widget.AutoCompleteTextView;
 19import android.widget.Button;
 20import android.widget.CheckBox;
 21import android.widget.CompoundButton;
 22import android.widget.CompoundButton.OnCheckedChangeListener;
 23import android.widget.EditText;
 24import android.widget.ImageButton;
 25import android.widget.ImageView;
 26import android.widget.LinearLayout;
 27import android.widget.RelativeLayout;
 28import android.widget.TableLayout;
 29import android.widget.TextView;
 30import android.widget.Toast;
 31
 32import java.util.Set;
 33
 34import eu.siacs.conversations.Config;
 35import eu.siacs.conversations.R;
 36import eu.siacs.conversations.crypto.axolotl.AxolotlService;
 37import eu.siacs.conversations.entities.Account;
 38import eu.siacs.conversations.services.XmppConnectionService.OnCaptchaRequested;
 39import eu.siacs.conversations.services.XmppConnectionService;
 40import eu.siacs.conversations.services.XmppConnectionService.OnAccountUpdate;
 41import eu.siacs.conversations.ui.adapter.KnownHostsAdapter;
 42import eu.siacs.conversations.utils.CryptoHelper;
 43import eu.siacs.conversations.utils.UIHelper;
 44import eu.siacs.conversations.xmpp.OnKeyStatusUpdated;
 45import eu.siacs.conversations.xmpp.XmppConnection.Features;
 46import eu.siacs.conversations.xmpp.forms.Data;
 47import eu.siacs.conversations.xmpp.jid.InvalidJidException;
 48import eu.siacs.conversations.xmpp.jid.Jid;
 49import eu.siacs.conversations.xmpp.pep.Avatar;
 50
 51public class EditAccountActivity extends XmppActivity implements OnAccountUpdate,
 52		OnKeyStatusUpdated, OnCaptchaRequested, KeyChainAliasCallback, XmppConnectionService.OnShowErrorToast {
 53
 54	private AutoCompleteTextView mAccountJid;
 55	private EditText mPassword;
 56	private EditText mPasswordConfirm;
 57	private CheckBox mRegisterNew;
 58	private Button mCancelButton;
 59	private Button mSaveButton;
 60	private TableLayout mMoreTable;
 61
 62	private LinearLayout mStats;
 63	private TextView mServerInfoSm;
 64	private TextView mServerInfoRosterVersion;
 65	private TextView mServerInfoCarbons;
 66	private TextView mServerInfoMam;
 67	private TextView mServerInfoCSI;
 68	private TextView mServerInfoBlocking;
 69	private TextView mServerInfoPep;
 70	private TextView mServerInfoHttpUpload;
 71	private TextView mSessionEst;
 72	private TextView mOtrFingerprint;
 73	private TextView mAxolotlFingerprint;
 74	private TextView mAccountJidLabel;
 75	private ImageView mAvatar;
 76	private RelativeLayout mOtrFingerprintBox;
 77	private RelativeLayout mAxolotlFingerprintBox;
 78	private ImageButton mOtrFingerprintToClipboardButton;
 79	private ImageButton mAxolotlFingerprintToClipboardButton;
 80	private ImageButton mRegenerateAxolotlKeyButton;
 81	private LinearLayout keys;
 82	private LinearLayout keysCard;
 83	private AlertDialog mCaptchaDialog = null;
 84
 85	private Jid jidToEdit;
 86	private boolean mInitMode = false;
 87	private Account mAccount;
 88	private String messageFingerprint;
 89
 90	private boolean mFetchingAvatar = false;
 91
 92	private final OnClickListener mSaveButtonClickListener = new OnClickListener() {
 93
 94		@Override
 95		public void onClick(final View v) {
 96			if (mInitMode && mAccount != null) {
 97				mAccount.setOption(Account.OPTION_DISABLED, false);
 98			}
 99			if (mAccount != null && mAccount.getStatus() == Account.State.DISABLED && !accountInfoEdited()) {
100				mAccount.setOption(Account.OPTION_DISABLED, false);
101				xmppConnectionService.updateAccount(mAccount);
102				return;
103			}
104			final boolean registerNewAccount = mRegisterNew.isChecked() && !Config.DISALLOW_REGISTRATION_IN_UI;
105			if (Config.DOMAIN_LOCK != null && mAccountJid.getText().toString().contains("@")) {
106				mAccountJid.setError(getString(R.string.invalid_username));
107				mAccountJid.requestFocus();
108				return;
109			}
110			final Jid jid;
111			try {
112				if (Config.DOMAIN_LOCK != null) {
113					jid = Jid.fromParts(mAccountJid.getText().toString(), Config.DOMAIN_LOCK, null);
114				} else {
115					jid = Jid.fromString(mAccountJid.getText().toString());
116				}
117			} catch (final InvalidJidException e) {
118				if (Config.DOMAIN_LOCK != null) {
119					mAccountJid.setError(getString(R.string.invalid_username));
120				} else {
121					mAccountJid.setError(getString(R.string.invalid_jid));
122				}
123				mAccountJid.requestFocus();
124				return;
125			}
126			if (jid.isDomainJid()) {
127				if (Config.DOMAIN_LOCK != null) {
128					mAccountJid.setError(getString(R.string.invalid_username));
129				} else {
130					mAccountJid.setError(getString(R.string.invalid_jid));
131				}
132				mAccountJid.requestFocus();
133				return;
134			}
135			final String password = mPassword.getText().toString();
136			final String passwordConfirm = mPasswordConfirm.getText().toString();
137			if (registerNewAccount) {
138				if (!password.equals(passwordConfirm)) {
139					mPasswordConfirm.setError(getString(R.string.passwords_do_not_match));
140					mPasswordConfirm.requestFocus();
141					return;
142				}
143			}
144			if (mAccount != null) {
145				mAccount.setJid(jid);
146				mAccountJid.setError(null);
147				mPasswordConfirm.setError(null);
148				mAccount.setPassword(password);
149				mAccount.setOption(Account.OPTION_REGISTER, registerNewAccount);
150				xmppConnectionService.updateAccount(mAccount);
151			} else {
152				if (xmppConnectionService.findAccountByJid(jid) != null) {
153					mAccountJid.setError(getString(R.string.account_already_exists));
154					mAccountJid.requestFocus();
155					return;
156				}
157				mAccount = new Account(jid.toBareJid(), password);
158				mAccount.setOption(Account.OPTION_USETLS, true);
159				mAccount.setOption(Account.OPTION_USECOMPRESSION, true);
160				mAccount.setOption(Account.OPTION_REGISTER, registerNewAccount);
161				xmppConnectionService.createAccount(mAccount);
162			}
163			if (!mAccount.isOptionSet(Account.OPTION_DISABLED)
164					&& !registerNewAccount
165					&& !mInitMode) {
166				finish();
167			} else {
168				updateSaveButton();
169				updateAccountInformation(true);
170			}
171
172		}
173	};
174	private final OnClickListener mCancelButtonClickListener = new OnClickListener() {
175
176		@Override
177		public void onClick(final View v) {
178			finish();
179		}
180	};
181
182	public void refreshUiReal() {
183		invalidateOptionsMenu();
184		if (mAccount != null
185				&& mAccount.getStatus() != Account.State.ONLINE
186				&& mFetchingAvatar) {
187			startActivity(new Intent(getApplicationContext(),
188					ManageAccountActivity.class));
189			finish();
190		} else if (mInitMode && mAccount != null && mAccount.getStatus() == Account.State.ONLINE) {
191			if (!mFetchingAvatar) {
192				mFetchingAvatar = true;
193				xmppConnectionService.checkForAvatar(mAccount, mAvatarFetchCallback);
194			}
195		} else {
196			updateSaveButton();
197		}
198		if (mAccount != null) {
199			updateAccountInformation(false);
200		}
201	}
202
203	@Override
204	public void onAccountUpdate() {
205		refreshUi();
206	}
207
208	private final UiCallback<Avatar> mAvatarFetchCallback = new UiCallback<Avatar>() {
209
210		@Override
211		public void userInputRequried(final PendingIntent pi, final Avatar avatar) {
212			finishInitialSetup(avatar);
213		}
214
215		@Override
216		public void success(final Avatar avatar) {
217			finishInitialSetup(avatar);
218		}
219
220		@Override
221		public void error(final int errorCode, final Avatar avatar) {
222			finishInitialSetup(avatar);
223		}
224	};
225	private final TextWatcher mTextWatcher = new TextWatcher() {
226
227		@Override
228		public void onTextChanged(final CharSequence s, final int start, final int before, final int count) {
229			updateSaveButton();
230		}
231
232		@Override
233		public void beforeTextChanged(final CharSequence s, final int start, final int count, final int after) {
234		}
235
236		@Override
237		public void afterTextChanged(final Editable s) {
238
239		}
240	};
241
242	private final OnClickListener mAvatarClickListener = new OnClickListener() {
243		@Override
244		public void onClick(final View view) {
245			if (mAccount != null) {
246				final Intent intent = new Intent(getApplicationContext(), PublishProfilePictureActivity.class);
247				intent.putExtra("account", mAccount.getJid().toBareJid().toString());
248				startActivity(intent);
249			}
250		}
251	};
252
253	protected void finishInitialSetup(final Avatar avatar) {
254		runOnUiThread(new Runnable() {
255
256			@Override
257			public void run() {
258				final Intent intent;
259				if (avatar != null) {
260					intent = new Intent(getApplicationContext(),
261							StartConversationActivity.class);
262					if (xmppConnectionService != null && xmppConnectionService.getAccounts().size() == 1) {
263						intent.putExtra("init", true);
264					}
265				} else {
266					intent = new Intent(getApplicationContext(),
267							PublishProfilePictureActivity.class);
268					intent.putExtra("account", mAccount.getJid().toBareJid().toString());
269					intent.putExtra("setup", true);
270				}
271				startActivity(intent);
272				finish();
273			}
274		});
275	}
276
277	protected void updateSaveButton() {
278		if (accountInfoEdited() && !mInitMode) {
279			this.mSaveButton.setText(R.string.save);
280			this.mSaveButton.setEnabled(true);
281			this.mSaveButton.setTextColor(getPrimaryTextColor());
282		} else if (mAccount != null && (mAccount.getStatus() == Account.State.CONNECTING || mFetchingAvatar)) {
283			this.mSaveButton.setEnabled(false);
284			this.mSaveButton.setTextColor(getSecondaryTextColor());
285			this.mSaveButton.setText(R.string.account_status_connecting);
286		} else if (mAccount != null && mAccount.getStatus() == Account.State.DISABLED && !mInitMode) {
287			this.mSaveButton.setEnabled(true);
288			this.mSaveButton.setTextColor(getPrimaryTextColor());
289			this.mSaveButton.setText(R.string.enable);
290		} else {
291			this.mSaveButton.setEnabled(true);
292			this.mSaveButton.setTextColor(getPrimaryTextColor());
293			if (!mInitMode) {
294				if (mAccount != null && mAccount.isOnlineAndConnected()) {
295					this.mSaveButton.setText(R.string.save);
296					if (!accountInfoEdited()) {
297						this.mSaveButton.setEnabled(false);
298						this.mSaveButton.setTextColor(getSecondaryTextColor());
299					}
300				} else {
301					this.mSaveButton.setText(R.string.connect);
302				}
303			} else {
304				this.mSaveButton.setText(R.string.next);
305			}
306		}
307	}
308
309	protected boolean accountInfoEdited() {
310		if (this.mAccount == null) {
311			return false;
312		}
313		final String unmodified;
314		if (Config.DOMAIN_LOCK != null) {
315			unmodified = this.mAccount.getJid().getLocalpart();
316		} else {
317			unmodified = this.mAccount.getJid().toBareJid().toString();
318		}
319		return !unmodified.equals(this.mAccountJid.getText().toString()) ||
320				!this.mAccount.getPassword().equals(this.mPassword.getText().toString());
321	}
322
323	@Override
324	protected String getShareableUri() {
325		if (mAccount != null) {
326			return mAccount.getShareableUri();
327		} else {
328			return "";
329		}
330	}
331
332	@Override
333	protected void onCreate(final Bundle savedInstanceState) {
334		super.onCreate(savedInstanceState);
335		setContentView(R.layout.activity_edit_account);
336		this.mAccountJid = (AutoCompleteTextView) findViewById(R.id.account_jid);
337		this.mAccountJid.addTextChangedListener(this.mTextWatcher);
338		this.mAccountJidLabel = (TextView) findViewById(R.id.account_jid_label);
339		if (Config.DOMAIN_LOCK != null) {
340			this.mAccountJidLabel.setText(R.string.username);
341			this.mAccountJid.setHint(R.string.username_hint);
342		}
343		this.mPassword = (EditText) findViewById(R.id.account_password);
344		this.mPassword.addTextChangedListener(this.mTextWatcher);
345		this.mPasswordConfirm = (EditText) findViewById(R.id.account_password_confirm);
346		this.mAvatar = (ImageView) findViewById(R.id.avater);
347		this.mAvatar.setOnClickListener(this.mAvatarClickListener);
348		this.mRegisterNew = (CheckBox) findViewById(R.id.account_register_new);
349		this.mStats = (LinearLayout) findViewById(R.id.stats);
350		this.mSessionEst = (TextView) findViewById(R.id.session_est);
351		this.mServerInfoRosterVersion = (TextView) findViewById(R.id.server_info_roster_version);
352		this.mServerInfoCarbons = (TextView) findViewById(R.id.server_info_carbons);
353		this.mServerInfoMam = (TextView) findViewById(R.id.server_info_mam);
354		this.mServerInfoCSI = (TextView) findViewById(R.id.server_info_csi);
355		this.mServerInfoBlocking = (TextView) findViewById(R.id.server_info_blocking);
356		this.mServerInfoSm = (TextView) findViewById(R.id.server_info_sm);
357		this.mServerInfoPep = (TextView) findViewById(R.id.server_info_pep);
358		this.mServerInfoHttpUpload = (TextView) findViewById(R.id.server_info_http_upload);
359		this.mOtrFingerprint = (TextView) findViewById(R.id.otr_fingerprint);
360		this.mOtrFingerprintBox = (RelativeLayout) findViewById(R.id.otr_fingerprint_box);
361		this.mOtrFingerprintToClipboardButton = (ImageButton) findViewById(R.id.action_copy_to_clipboard);
362		this.mAxolotlFingerprint = (TextView) findViewById(R.id.axolotl_fingerprint);
363		this.mAxolotlFingerprintBox = (RelativeLayout) findViewById(R.id.axolotl_fingerprint_box);
364		this.mAxolotlFingerprintToClipboardButton = (ImageButton) findViewById(R.id.action_copy_axolotl_to_clipboard);
365		this.mRegenerateAxolotlKeyButton = (ImageButton) findViewById(R.id.action_regenerate_axolotl_key);
366		this.keysCard = (LinearLayout) findViewById(R.id.other_device_keys_card);
367		this.keys = (LinearLayout) findViewById(R.id.other_device_keys);
368		this.mSaveButton = (Button) findViewById(R.id.save_button);
369		this.mCancelButton = (Button) findViewById(R.id.cancel_button);
370		this.mSaveButton.setOnClickListener(this.mSaveButtonClickListener);
371		this.mCancelButton.setOnClickListener(this.mCancelButtonClickListener);
372		this.mMoreTable = (TableLayout) findViewById(R.id.server_info_more);
373		final OnCheckedChangeListener OnCheckedShowConfirmPassword = new OnCheckedChangeListener() {
374			@Override
375			public void onCheckedChanged(final CompoundButton buttonView,
376										 final boolean isChecked) {
377				if (isChecked) {
378					mPasswordConfirm.setVisibility(View.VISIBLE);
379				} else {
380					mPasswordConfirm.setVisibility(View.GONE);
381				}
382				updateSaveButton();
383			}
384		};
385		this.mRegisterNew.setOnCheckedChangeListener(OnCheckedShowConfirmPassword);
386		if (Config.DISALLOW_REGISTRATION_IN_UI) {
387			this.mRegisterNew.setVisibility(View.GONE);
388		}
389	}
390
391	@Override
392	public boolean onCreateOptionsMenu(final Menu menu) {
393		super.onCreateOptionsMenu(menu);
394		getMenuInflater().inflate(R.menu.editaccount, menu);
395		final MenuItem showQrCode = menu.findItem(R.id.action_show_qr_code);
396		final MenuItem showBlocklist = menu.findItem(R.id.action_show_block_list);
397		final MenuItem showMoreInfo = menu.findItem(R.id.action_server_info_show_more);
398		final MenuItem changePassword = menu.findItem(R.id.action_change_password_on_server);
399		final MenuItem clearDevices = menu.findItem(R.id.action_clear_devices);
400		final MenuItem renewCertificate = menu.findItem(R.id.action_renew_certificate);
401
402		renewCertificate.setVisible(mAccount != null && mAccount.getPrivateKeyAlias() != null);
403
404		if (mAccount != null && mAccount.isOnlineAndConnected()) {
405			if (!mAccount.getXmppConnection().getFeatures().blocking()) {
406				showBlocklist.setVisible(false);
407			}
408			if (!mAccount.getXmppConnection().getFeatures().register()) {
409				changePassword.setVisible(false);
410			}
411			Set<Integer> otherDevices = mAccount.getAxolotlService().getOwnDeviceIds();
412			if (otherDevices == null || otherDevices.isEmpty()) {
413				clearDevices.setVisible(false);
414			}
415		} else {
416			showQrCode.setVisible(false);
417			showBlocklist.setVisible(false);
418			showMoreInfo.setVisible(false);
419			changePassword.setVisible(false);
420			clearDevices.setVisible(false);
421		}
422		return true;
423	}
424
425	@Override
426	protected void onStart() {
427		super.onStart();
428		if (getIntent() != null) {
429			try {
430				this.jidToEdit = Jid.fromString(getIntent().getStringExtra("jid"));
431			} catch (final InvalidJidException | NullPointerException ignored) {
432				this.jidToEdit = null;
433			}
434			this.mInitMode = getIntent().getBooleanExtra("init", false) || this.jidToEdit == null;
435			this.messageFingerprint = getIntent().getStringExtra("fingerprint");
436			if (!mInitMode) {
437				this.mRegisterNew.setVisibility(View.GONE);
438				if (getActionBar() != null) {
439					getActionBar().setTitle(getString(R.string.account_details));
440				}
441			} else {
442				this.mAvatar.setVisibility(View.GONE);
443				if (getActionBar() != null) {
444					getActionBar().setTitle(R.string.action_add_account);
445				}
446			}
447		}
448	}
449
450	@Override
451	protected void onBackendConnected() {
452		if (this.jidToEdit != null) {
453			this.mAccount = xmppConnectionService.findAccountByJid(jidToEdit);
454			if (this.mAccount != null) {
455				if (this.mAccount.getPrivateKeyAlias() != null) {
456					this.mPassword.setHint(R.string.authenticate_with_certificate);
457					if (this.mInitMode) {
458						this.mPassword.requestFocus();
459					}
460				}
461				updateAccountInformation(true);
462			}
463		} else if (this.xmppConnectionService.getAccounts().size() == 0) {
464			if (getActionBar() != null) {
465				getActionBar().setDisplayHomeAsUpEnabled(false);
466				getActionBar().setDisplayShowHomeEnabled(false);
467				getActionBar().setHomeButtonEnabled(false);
468			}
469			this.mCancelButton.setEnabled(false);
470			this.mCancelButton.setTextColor(getSecondaryTextColor());
471		}
472		if (Config.DOMAIN_LOCK == null) {
473			final KnownHostsAdapter mKnownHostsAdapter = new KnownHostsAdapter(this,
474					android.R.layout.simple_list_item_1,
475					xmppConnectionService.getKnownHosts());
476			this.mAccountJid.setAdapter(mKnownHostsAdapter);
477		}
478		updateSaveButton();
479	}
480
481	@Override
482	public boolean onOptionsItemSelected(final MenuItem item) {
483		switch (item.getItemId()) {
484			case R.id.action_show_block_list:
485				final Intent showBlocklistIntent = new Intent(this, BlocklistActivity.class);
486				showBlocklistIntent.putExtra("account", mAccount.getJid().toString());
487				startActivity(showBlocklistIntent);
488				break;
489			case R.id.action_server_info_show_more:
490				mMoreTable.setVisibility(item.isChecked() ? View.GONE : View.VISIBLE);
491				item.setChecked(!item.isChecked());
492				break;
493			case R.id.action_change_password_on_server:
494				final Intent changePasswordIntent = new Intent(this, ChangePasswordActivity.class);
495				changePasswordIntent.putExtra("account", mAccount.getJid().toString());
496				startActivity(changePasswordIntent);
497				break;
498			case R.id.action_clear_devices:
499				showWipePepDialog();
500				break;
501			case R.id.action_renew_certificate:
502				renewCertificate();
503				break;
504		}
505		return super.onOptionsItemSelected(item);
506	}
507
508	private void renewCertificate() {
509		KeyChain.choosePrivateKeyAlias(this, this, null, null, null, -1, null);
510	}
511
512	@Override
513	public void alias(String alias) {
514		if (alias != null) {
515			xmppConnectionService.updateKeyInAccount(mAccount, alias);
516		}
517	}
518
519	private void updateAccountInformation(boolean init) {
520		if (init) {
521			if (Config.DOMAIN_LOCK != null) {
522				this.mAccountJid.setText(this.mAccount.getJid().getLocalpart());
523			} else {
524				this.mAccountJid.setText(this.mAccount.getJid().toBareJid().toString());
525			}
526			this.mPassword.setText(this.mAccount.getPassword());
527		}
528		if (!mInitMode) {
529			this.mAvatar.setVisibility(View.VISIBLE);
530			this.mAvatar.setImageBitmap(avatarService().get(this.mAccount, getPixel(72)));
531		}
532		if (this.mAccount.isOptionSet(Account.OPTION_REGISTER)) {
533			this.mRegisterNew.setVisibility(View.VISIBLE);
534			this.mRegisterNew.setChecked(true);
535			this.mPasswordConfirm.setText(this.mAccount.getPassword());
536		} else {
537			this.mRegisterNew.setVisibility(View.GONE);
538			this.mRegisterNew.setChecked(false);
539		}
540		if (this.mAccount.isOnlineAndConnected() && !this.mFetchingAvatar) {
541			this.mStats.setVisibility(View.VISIBLE);
542			this.mSessionEst.setText(UIHelper.readableTimeDifferenceFull(this, this.mAccount.getXmppConnection()
543					.getLastSessionEstablished()));
544			Features features = this.mAccount.getXmppConnection().getFeatures();
545			if (features.rosterVersioning()) {
546				this.mServerInfoRosterVersion.setText(R.string.server_info_available);
547			} else {
548				this.mServerInfoRosterVersion.setText(R.string.server_info_unavailable);
549			}
550			if (features.carbons()) {
551				this.mServerInfoCarbons.setText(R.string.server_info_available);
552			} else {
553				this.mServerInfoCarbons
554						.setText(R.string.server_info_unavailable);
555			}
556			if (features.mam()) {
557				this.mServerInfoMam.setText(R.string.server_info_available);
558			} else {
559				this.mServerInfoMam.setText(R.string.server_info_unavailable);
560			}
561			if (features.csi()) {
562				this.mServerInfoCSI.setText(R.string.server_info_available);
563			} else {
564				this.mServerInfoCSI.setText(R.string.server_info_unavailable);
565			}
566			if (features.blocking()) {
567				this.mServerInfoBlocking.setText(R.string.server_info_available);
568			} else {
569				this.mServerInfoBlocking.setText(R.string.server_info_unavailable);
570			}
571			if (features.sm()) {
572				this.mServerInfoSm.setText(R.string.server_info_available);
573			} else {
574				this.mServerInfoSm.setText(R.string.server_info_unavailable);
575			}
576			if (features.pep()) {
577				AxolotlService axolotlService = this.mAccount.getAxolotlService();
578				if (axolotlService != null && axolotlService.isPepBroken()) {
579					this.mServerInfoPep.setText(R.string.server_info_broken);
580				} else {
581					this.mServerInfoPep.setText(R.string.server_info_available);
582				}
583			} else {
584				this.mServerInfoPep.setText(R.string.server_info_unavailable);
585			}
586			if (features.httpUpload()) {
587				this.mServerInfoHttpUpload.setText(R.string.server_info_available);
588			} else {
589				this.mServerInfoHttpUpload.setText(R.string.server_info_unavailable);
590			}
591			final String otrFingerprint = this.mAccount.getOtrFingerprint();
592			if (otrFingerprint != null) {
593				this.mOtrFingerprintBox.setVisibility(View.VISIBLE);
594				this.mOtrFingerprint.setText(CryptoHelper.prettifyFingerprint(otrFingerprint));
595				this.mOtrFingerprintToClipboardButton
596						.setVisibility(View.VISIBLE);
597				this.mOtrFingerprintToClipboardButton
598						.setOnClickListener(new View.OnClickListener() {
599
600							@Override
601							public void onClick(final View v) {
602
603								if (copyTextToClipboard(otrFingerprint, R.string.otr_fingerprint)) {
604									Toast.makeText(
605											EditAccountActivity.this,
606											R.string.toast_message_otr_fingerprint,
607											Toast.LENGTH_SHORT).show();
608								}
609							}
610						});
611			} else {
612				this.mOtrFingerprintBox.setVisibility(View.GONE);
613			}
614			final String axolotlFingerprint = this.mAccount.getAxolotlService().getOwnFingerprint();
615			if (axolotlFingerprint != null) {
616				this.mAxolotlFingerprintBox.setVisibility(View.VISIBLE);
617				this.mAxolotlFingerprint.setText(CryptoHelper.prettifyFingerprint(axolotlFingerprint));
618				this.mAxolotlFingerprintToClipboardButton
619						.setVisibility(View.VISIBLE);
620				this.mAxolotlFingerprintToClipboardButton
621						.setOnClickListener(new View.OnClickListener() {
622
623							@Override
624							public void onClick(final View v) {
625
626								if (copyTextToClipboard(axolotlFingerprint, R.string.omemo_fingerprint)) {
627									Toast.makeText(
628											EditAccountActivity.this,
629											R.string.toast_message_omemo_fingerprint,
630											Toast.LENGTH_SHORT).show();
631								}
632							}
633						});
634				if (Config.SHOW_REGENERATE_AXOLOTL_KEYS_BUTTON) {
635					this.mRegenerateAxolotlKeyButton
636							.setVisibility(View.VISIBLE);
637					this.mRegenerateAxolotlKeyButton
638							.setOnClickListener(new View.OnClickListener() {
639
640								@Override
641								public void onClick(final View v) {
642									showRegenerateAxolotlKeyDialog();
643								}
644							});
645				}
646			} else {
647				this.mAxolotlFingerprintBox.setVisibility(View.GONE);
648			}
649			final String ownFingerprint = mAccount.getAxolotlService().getOwnFingerprint();
650			boolean hasKeys = false;
651			keys.removeAllViews();
652			for (final String fingerprint : mAccount.getAxolotlService().getFingerprintsForOwnSessions()) {
653				if (ownFingerprint.equals(fingerprint)) {
654					continue;
655				}
656				boolean highlight = fingerprint.equals(messageFingerprint);
657				hasKeys |= addFingerprintRow(keys, mAccount, fingerprint, highlight);
658			}
659			if (hasKeys) {
660				keysCard.setVisibility(View.VISIBLE);
661			} else {
662				keysCard.setVisibility(View.GONE);
663			}
664		} else {
665			if (this.mAccount.errorStatus()) {
666				this.mAccountJid.setError(getString(this.mAccount.getStatus().getReadableId()));
667				if (init || !accountInfoEdited()) {
668					this.mAccountJid.requestFocus();
669				}
670			} else {
671				this.mAccountJid.setError(null);
672			}
673			this.mStats.setVisibility(View.GONE);
674		}
675	}
676
677	public void showRegenerateAxolotlKeyDialog() {
678		Builder builder = new Builder(this);
679		builder.setTitle("Regenerate Key");
680		builder.setIconAttribute(android.R.attr.alertDialogIcon);
681		builder.setMessage("Are you sure you want to regenerate your Identity Key? (This will also wipe all established sessions and contact Identity Keys)");
682		builder.setNegativeButton(getString(R.string.cancel), null);
683		builder.setPositiveButton("Yes",
684				new DialogInterface.OnClickListener() {
685					@Override
686					public void onClick(DialogInterface dialog, int which) {
687						mAccount.getAxolotlService().regenerateKeys(false);
688					}
689				});
690		builder.create().show();
691	}
692
693	public void showWipePepDialog() {
694		Builder builder = new Builder(this);
695		builder.setTitle(getString(R.string.clear_other_devices));
696		builder.setIconAttribute(android.R.attr.alertDialogIcon);
697		builder.setMessage(getString(R.string.clear_other_devices_desc));
698		builder.setNegativeButton(getString(R.string.cancel), null);
699		builder.setPositiveButton(getString(R.string.accept),
700				new DialogInterface.OnClickListener() {
701					@Override
702					public void onClick(DialogInterface dialog, int which) {
703						mAccount.getAxolotlService().wipeOtherPepDevices();
704					}
705				});
706		builder.create().show();
707	}
708
709	@Override
710	public void onKeyStatusUpdated() {
711		refreshUi();
712	}
713
714	@Override
715	public void onCaptchaRequested(final Account account, final String id, final Data data,
716								   final Bitmap captcha) {
717		final AlertDialog.Builder builder = new AlertDialog.Builder(this);
718		final ImageView view = new ImageView(this);
719		final LinearLayout layout = new LinearLayout(this);
720		final EditText input = new EditText(this);
721
722		view.setImageBitmap(captcha);
723		view.setScaleType(ImageView.ScaleType.FIT_CENTER);
724
725		input.setHint(getString(R.string.captcha_hint));
726
727		layout.setOrientation(LinearLayout.VERTICAL);
728		layout.addView(view);
729		layout.addView(input);
730
731		builder.setTitle(getString(R.string.captcha_required));
732		builder.setView(layout);
733
734		builder.setPositiveButton(getString(R.string.ok),
735				new DialogInterface.OnClickListener() {
736					@Override
737					public void onClick(DialogInterface dialog, int which) {
738						String rc = input.getText().toString();
739						data.put("username", account.getUsername());
740						data.put("password", account.getPassword());
741						data.put("ocr", rc);
742						data.submit();
743
744						if (xmppConnectionServiceBound) {
745							xmppConnectionService.sendCreateAccountWithCaptchaPacket(
746									account, id, data);
747						}
748					}
749				});
750		builder.setNegativeButton(getString(R.string.cancel), new DialogInterface.OnClickListener() {
751			@Override
752			public void onClick(DialogInterface dialog, int which) {
753				if (xmppConnectionService != null) {
754					xmppConnectionService.sendCreateAccountWithCaptchaPacket(account, null, null);
755				}
756			}
757		});
758
759		builder.setOnCancelListener(new DialogInterface.OnCancelListener() {
760			@Override
761			public void onCancel(DialogInterface dialog) {
762				if (xmppConnectionService != null) {
763					xmppConnectionService.sendCreateAccountWithCaptchaPacket(account, null, null);
764				}
765			}
766		});
767
768		runOnUiThread(new Runnable() {
769			@Override
770			public void run() {
771				if ((mCaptchaDialog != null) && mCaptchaDialog.isShowing()) {
772					mCaptchaDialog.dismiss();
773				}
774				mCaptchaDialog = builder.create();
775				mCaptchaDialog.show();
776			}
777		});
778	}
779
780	public void onShowErrorToast(final int resId) {
781		runOnUiThread(new Runnable() {
782			@Override
783			public void run() {
784				Toast.makeText(EditAccountActivity.this, resId, Toast.LENGTH_SHORT).show();
785			}
786		});
787	}
788}