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			this.mAccountJid.getEditableText().clear();
522			if (Config.DOMAIN_LOCK != null) {
523				this.mAccountJid.getEditableText().append(this.mAccount.getJid().getLocalpart());
524			} else {
525				this.mAccountJid.getEditableText().append(this.mAccount.getJid().toBareJid().toString());
526			}
527			this.mPassword.setText(this.mAccount.getPassword());
528		}
529		if (!mInitMode) {
530			this.mAvatar.setVisibility(View.VISIBLE);
531			this.mAvatar.setImageBitmap(avatarService().get(this.mAccount, getPixel(72)));
532		}
533		if (this.mAccount.isOptionSet(Account.OPTION_REGISTER)) {
534			this.mRegisterNew.setVisibility(View.VISIBLE);
535			this.mRegisterNew.setChecked(true);
536			this.mPasswordConfirm.setText(this.mAccount.getPassword());
537		} else {
538			this.mRegisterNew.setVisibility(View.GONE);
539			this.mRegisterNew.setChecked(false);
540		}
541		if (this.mAccount.isOnlineAndConnected() && !this.mFetchingAvatar) {
542			this.mStats.setVisibility(View.VISIBLE);
543			this.mSessionEst.setText(UIHelper.readableTimeDifferenceFull(this, this.mAccount.getXmppConnection()
544					.getLastSessionEstablished()));
545			Features features = this.mAccount.getXmppConnection().getFeatures();
546			if (features.rosterVersioning()) {
547				this.mServerInfoRosterVersion.setText(R.string.server_info_available);
548			} else {
549				this.mServerInfoRosterVersion.setText(R.string.server_info_unavailable);
550			}
551			if (features.carbons()) {
552				this.mServerInfoCarbons.setText(R.string.server_info_available);
553			} else {
554				this.mServerInfoCarbons
555						.setText(R.string.server_info_unavailable);
556			}
557			if (features.mam()) {
558				this.mServerInfoMam.setText(R.string.server_info_available);
559			} else {
560				this.mServerInfoMam.setText(R.string.server_info_unavailable);
561			}
562			if (features.csi()) {
563				this.mServerInfoCSI.setText(R.string.server_info_available);
564			} else {
565				this.mServerInfoCSI.setText(R.string.server_info_unavailable);
566			}
567			if (features.blocking()) {
568				this.mServerInfoBlocking.setText(R.string.server_info_available);
569			} else {
570				this.mServerInfoBlocking.setText(R.string.server_info_unavailable);
571			}
572			if (features.sm()) {
573				this.mServerInfoSm.setText(R.string.server_info_available);
574			} else {
575				this.mServerInfoSm.setText(R.string.server_info_unavailable);
576			}
577			if (features.pep()) {
578				AxolotlService axolotlService = this.mAccount.getAxolotlService();
579				if (axolotlService != null && axolotlService.isPepBroken()) {
580					this.mServerInfoPep.setText(R.string.server_info_broken);
581				} else {
582					this.mServerInfoPep.setText(R.string.server_info_available);
583				}
584			} else {
585				this.mServerInfoPep.setText(R.string.server_info_unavailable);
586			}
587			if (features.httpUpload()) {
588				this.mServerInfoHttpUpload.setText(R.string.server_info_available);
589			} else {
590				this.mServerInfoHttpUpload.setText(R.string.server_info_unavailable);
591			}
592			final String otrFingerprint = this.mAccount.getOtrFingerprint();
593			if (otrFingerprint != null) {
594				this.mOtrFingerprintBox.setVisibility(View.VISIBLE);
595				this.mOtrFingerprint.setText(CryptoHelper.prettifyFingerprint(otrFingerprint));
596				this.mOtrFingerprintToClipboardButton
597						.setVisibility(View.VISIBLE);
598				this.mOtrFingerprintToClipboardButton
599						.setOnClickListener(new View.OnClickListener() {
600
601							@Override
602							public void onClick(final View v) {
603
604								if (copyTextToClipboard(otrFingerprint, R.string.otr_fingerprint)) {
605									Toast.makeText(
606											EditAccountActivity.this,
607											R.string.toast_message_otr_fingerprint,
608											Toast.LENGTH_SHORT).show();
609								}
610							}
611						});
612			} else {
613				this.mOtrFingerprintBox.setVisibility(View.GONE);
614			}
615			final String axolotlFingerprint = this.mAccount.getAxolotlService().getOwnFingerprint();
616			if (axolotlFingerprint != null) {
617				this.mAxolotlFingerprintBox.setVisibility(View.VISIBLE);
618				this.mAxolotlFingerprint.setText(CryptoHelper.prettifyFingerprint(axolotlFingerprint));
619				this.mAxolotlFingerprintToClipboardButton
620						.setVisibility(View.VISIBLE);
621				this.mAxolotlFingerprintToClipboardButton
622						.setOnClickListener(new View.OnClickListener() {
623
624							@Override
625							public void onClick(final View v) {
626
627								if (copyTextToClipboard(axolotlFingerprint, R.string.omemo_fingerprint)) {
628									Toast.makeText(
629											EditAccountActivity.this,
630											R.string.toast_message_omemo_fingerprint,
631											Toast.LENGTH_SHORT).show();
632								}
633							}
634						});
635				if (Config.SHOW_REGENERATE_AXOLOTL_KEYS_BUTTON) {
636					this.mRegenerateAxolotlKeyButton
637							.setVisibility(View.VISIBLE);
638					this.mRegenerateAxolotlKeyButton
639							.setOnClickListener(new View.OnClickListener() {
640
641								@Override
642								public void onClick(final View v) {
643									showRegenerateAxolotlKeyDialog();
644								}
645							});
646				}
647			} else {
648				this.mAxolotlFingerprintBox.setVisibility(View.GONE);
649			}
650			final String ownFingerprint = mAccount.getAxolotlService().getOwnFingerprint();
651			boolean hasKeys = false;
652			keys.removeAllViews();
653			for (final String fingerprint : mAccount.getAxolotlService().getFingerprintsForOwnSessions()) {
654				if (ownFingerprint.equals(fingerprint)) {
655					continue;
656				}
657				boolean highlight = fingerprint.equals(messageFingerprint);
658				hasKeys |= addFingerprintRow(keys, mAccount, fingerprint, highlight);
659			}
660			if (hasKeys) {
661				keysCard.setVisibility(View.VISIBLE);
662			} else {
663				keysCard.setVisibility(View.GONE);
664			}
665		} else {
666			if (this.mAccount.errorStatus()) {
667				this.mAccountJid.setError(getString(this.mAccount.getStatus().getReadableId()));
668				if (init || !accountInfoEdited()) {
669					this.mAccountJid.requestFocus();
670				}
671			} else {
672				this.mAccountJid.setError(null);
673			}
674			this.mStats.setVisibility(View.GONE);
675		}
676	}
677
678	public void showRegenerateAxolotlKeyDialog() {
679		Builder builder = new Builder(this);
680		builder.setTitle("Regenerate Key");
681		builder.setIconAttribute(android.R.attr.alertDialogIcon);
682		builder.setMessage("Are you sure you want to regenerate your Identity Key? (This will also wipe all established sessions and contact Identity Keys)");
683		builder.setNegativeButton(getString(R.string.cancel), null);
684		builder.setPositiveButton("Yes",
685				new DialogInterface.OnClickListener() {
686					@Override
687					public void onClick(DialogInterface dialog, int which) {
688						mAccount.getAxolotlService().regenerateKeys(false);
689					}
690				});
691		builder.create().show();
692	}
693
694	public void showWipePepDialog() {
695		Builder builder = new Builder(this);
696		builder.setTitle(getString(R.string.clear_other_devices));
697		builder.setIconAttribute(android.R.attr.alertDialogIcon);
698		builder.setMessage(getString(R.string.clear_other_devices_desc));
699		builder.setNegativeButton(getString(R.string.cancel), null);
700		builder.setPositiveButton(getString(R.string.accept),
701				new DialogInterface.OnClickListener() {
702					@Override
703					public void onClick(DialogInterface dialog, int which) {
704						mAccount.getAxolotlService().wipeOtherPepDevices();
705					}
706				});
707		builder.create().show();
708	}
709
710	@Override
711	public void onKeyStatusUpdated(AxolotlService.FetchStatus report) {
712		refreshUi();
713	}
714
715	@Override
716	public void onCaptchaRequested(final Account account, final String id, final Data data,
717								   final Bitmap captcha) {
718		final AlertDialog.Builder builder = new AlertDialog.Builder(this);
719		final ImageView view = new ImageView(this);
720		final LinearLayout layout = new LinearLayout(this);
721		final EditText input = new EditText(this);
722
723		view.setImageBitmap(captcha);
724		view.setScaleType(ImageView.ScaleType.FIT_CENTER);
725
726		input.setHint(getString(R.string.captcha_hint));
727
728		layout.setOrientation(LinearLayout.VERTICAL);
729		layout.addView(view);
730		layout.addView(input);
731
732		builder.setTitle(getString(R.string.captcha_required));
733		builder.setView(layout);
734
735		builder.setPositiveButton(getString(R.string.ok),
736				new DialogInterface.OnClickListener() {
737					@Override
738					public void onClick(DialogInterface dialog, int which) {
739						String rc = input.getText().toString();
740						data.put("username", account.getUsername());
741						data.put("password", account.getPassword());
742						data.put("ocr", rc);
743						data.submit();
744
745						if (xmppConnectionServiceBound) {
746							xmppConnectionService.sendCreateAccountWithCaptchaPacket(
747									account, id, data);
748						}
749					}
750				});
751		builder.setNegativeButton(getString(R.string.cancel), new DialogInterface.OnClickListener() {
752			@Override
753			public void onClick(DialogInterface dialog, int which) {
754				if (xmppConnectionService != null) {
755					xmppConnectionService.sendCreateAccountWithCaptchaPacket(account, null, null);
756				}
757			}
758		});
759
760		builder.setOnCancelListener(new DialogInterface.OnCancelListener() {
761			@Override
762			public void onCancel(DialogInterface dialog) {
763				if (xmppConnectionService != null) {
764					xmppConnectionService.sendCreateAccountWithCaptchaPacket(account, null, null);
765				}
766			}
767		});
768
769		runOnUiThread(new Runnable() {
770			@Override
771			public void run() {
772				if ((mCaptchaDialog != null) && mCaptchaDialog.isShowing()) {
773					mCaptchaDialog.dismiss();
774				}
775				mCaptchaDialog = builder.create();
776				mCaptchaDialog.show();
777			}
778		});
779	}
780
781	public void onShowErrorToast(final int resId) {
782		runOnUiThread(new Runnable() {
783			@Override
784			public void run() {
785				Toast.makeText(EditAccountActivity.this, resId, Toast.LENGTH_SHORT).show();
786			}
787		});
788	}
789}