EditAccountActivity.java

   1package eu.siacs.conversations.ui;
   2
   3import android.app.AlertDialog;
   4import android.app.AlertDialog.Builder;
   5import android.app.PendingIntent;
   6import android.content.ActivityNotFoundException;
   7import android.content.DialogInterface;
   8import android.content.Intent;
   9import android.content.SharedPreferences;
  10import android.graphics.Bitmap;
  11import android.net.Uri;
  12import android.os.Bundle;
  13import android.provider.Settings;
  14import android.security.KeyChain;
  15import android.security.KeyChainAliasCallback;
  16import android.support.v4.content.ContextCompat;
  17import android.support.v7.app.ActionBar;
  18import android.text.Editable;
  19import android.text.TextWatcher;
  20import android.view.Menu;
  21import android.view.MenuItem;
  22import android.view.View;
  23import android.view.View.OnClickListener;
  24import android.widget.AutoCompleteTextView;
  25import android.widget.Button;
  26import android.widget.CheckBox;
  27import android.widget.CompoundButton;
  28import android.widget.CompoundButton.OnCheckedChangeListener;
  29import android.widget.EditText;
  30import android.widget.ImageButton;
  31import android.widget.ImageView;
  32import android.widget.LinearLayout;
  33import android.widget.RelativeLayout;
  34import android.widget.TableLayout;
  35import android.widget.TableRow;
  36import android.widget.TextView;
  37import android.widget.Toast;
  38
  39import org.openintents.openpgp.util.OpenPgpUtils;
  40
  41import java.net.URL;
  42import java.util.Arrays;
  43import java.util.List;
  44import java.util.Set;
  45import java.util.concurrent.atomic.AtomicInteger;
  46
  47import eu.siacs.conversations.Config;
  48import eu.siacs.conversations.R;
  49import eu.siacs.conversations.crypto.axolotl.AxolotlService;
  50import eu.siacs.conversations.crypto.axolotl.XmppAxolotlSession;
  51import eu.siacs.conversations.entities.Account;
  52import eu.siacs.conversations.services.BarcodeProvider;
  53import eu.siacs.conversations.services.XmppConnectionService.OnCaptchaRequested;
  54import eu.siacs.conversations.services.XmppConnectionService;
  55import eu.siacs.conversations.services.XmppConnectionService.OnAccountUpdate;
  56import eu.siacs.conversations.ui.adapter.KnownHostsAdapter;
  57import eu.siacs.conversations.ui.widget.DisabledActionModeCallback;
  58import eu.siacs.conversations.utils.CryptoHelper;
  59import eu.siacs.conversations.utils.UIHelper;
  60import eu.siacs.conversations.utils.XmppUri;
  61import eu.siacs.conversations.xml.Element;
  62import eu.siacs.conversations.xmpp.OnKeyStatusUpdated;
  63import eu.siacs.conversations.xmpp.OnUpdateBlocklist;
  64import eu.siacs.conversations.xmpp.XmppConnection;
  65import eu.siacs.conversations.xmpp.XmppConnection.Features;
  66import eu.siacs.conversations.xmpp.forms.Data;
  67import eu.siacs.conversations.xmpp.jid.InvalidJidException;
  68import eu.siacs.conversations.xmpp.jid.Jid;
  69import eu.siacs.conversations.xmpp.pep.Avatar;
  70
  71public class EditAccountActivity extends OmemoActivity implements OnAccountUpdate, OnUpdateBlocklist,
  72		OnKeyStatusUpdated, OnCaptchaRequested, KeyChainAliasCallback, XmppConnectionService.OnShowErrorToast, XmppConnectionService.OnMamPreferencesFetched {
  73
  74	private static final int REQUEST_DATA_SAVER = 0x37af244;
  75	private AutoCompleteTextView mAccountJid;
  76	private EditText mPassword;
  77	private CheckBox mRegisterNew;
  78	private Button mCancelButton;
  79	private Button mSaveButton;
  80	private Button mDisableOsOptimizationsButton;
  81	private TextView mDisableOsOptimizationsHeadline;
  82	private TextView getmDisableOsOptimizationsBody;
  83	private TableLayout mMoreTable;
  84
  85	private LinearLayout mStats;
  86	private RelativeLayout mOsOptimizations;
  87	private TextView mServerInfoSm;
  88	private TextView mServerInfoRosterVersion;
  89	private TextView mServerInfoCarbons;
  90	private TextView mServerInfoMam;
  91	private TextView mServerInfoCSI;
  92	private TextView mServerInfoBlocking;
  93	private TextView mServerInfoPep;
  94	private TextView mServerInfoHttpUpload;
  95	private TextView mServerInfoPush;
  96	private TextView mSessionEst;
  97	private TextView mOtrFingerprint;
  98	private TextView mAxolotlFingerprint;
  99	private TextView mPgpFingerprint;
 100	private TextView mOwnFingerprintDesc;
 101	private TextView mOtrFingerprintDesc;
 102	private TextView getmPgpFingerprintDesc;
 103	private TextView mAccountJidLabel;
 104	private ImageView mAvatar;
 105	private RelativeLayout mOtrFingerprintBox;
 106	private RelativeLayout mAxolotlFingerprintBox;
 107	private RelativeLayout mPgpFingerprintBox;
 108	private ImageButton mOtrFingerprintToClipboardButton;
 109	private ImageButton mAxolotlFingerprintToClipboardButton;
 110	private ImageButton mPgpDeleteFingerprintButton;
 111	private LinearLayout keys;
 112	private LinearLayout keysCard;
 113	private LinearLayout mNamePort;
 114	private EditText mHostname;
 115	private EditText mPort;
 116	private AlertDialog mCaptchaDialog = null;
 117
 118	private Jid jidToEdit;
 119	private boolean mInitMode = false;
 120	private boolean mUsernameMode = Config.DOMAIN_LOCK != null;
 121	private boolean mShowOptions = false;
 122	private Account mAccount;
 123	private String messageFingerprint;
 124
 125	private boolean mFetchingAvatar = false;
 126
 127	private final OnClickListener mSaveButtonClickListener = new OnClickListener() {
 128
 129		@Override
 130		public void onClick(final View v) {
 131			final String password = mPassword.getText().toString();
 132			final boolean wasDisabled = mAccount != null && mAccount.getStatus() == Account.State.DISABLED;
 133
 134			if (!mInitMode && passwordChangedInMagicCreateMode()) {
 135				gotoChangePassword(password);
 136				return;
 137			}
 138			if (mInitMode && mAccount != null) {
 139				mAccount.setOption(Account.OPTION_DISABLED, false);
 140			}
 141			if (mAccount != null && mAccount.getStatus() == Account.State.DISABLED && !accountInfoEdited()) {
 142				mAccount.setOption(Account.OPTION_DISABLED, false);
 143				if (!xmppConnectionService.updateAccount(mAccount)) {
 144					Toast.makeText(EditAccountActivity.this,R.string.unable_to_update_account,Toast.LENGTH_SHORT).show();
 145				}
 146				return;
 147			}
 148			final boolean registerNewAccount = mRegisterNew.isChecked() && !Config.DISALLOW_REGISTRATION_IN_UI;
 149			if (mUsernameMode && mAccountJid.getText().toString().contains("@")) {
 150				mAccountJid.setError(getString(R.string.invalid_username));
 151				mAccountJid.requestFocus();
 152				return;
 153			}
 154
 155			XmppConnection connection = mAccount == null ? null : mAccount.getXmppConnection();
 156			boolean openRegistrationUrl = registerNewAccount && mAccount != null && mAccount.getStatus() == Account.State.REGISTRATION_WEB;
 157			boolean openPaymentUrl = mAccount != null && mAccount.getStatus() == Account.State.PAYMENT_REQUIRED;
 158			final boolean redirectionWorthyStatus = openPaymentUrl || openRegistrationUrl;
 159			URL url = connection != null && redirectionWorthyStatus ? connection.getRedirectionUrl() : null;
 160			if (url != null && redirectionWorthyStatus && !wasDisabled) {
 161				try {
 162					startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(url.toString())));
 163					return;
 164				} catch (ActivityNotFoundException e) {
 165					Toast.makeText(EditAccountActivity.this,R.string.application_found_to_open_website,Toast.LENGTH_SHORT);
 166					return;
 167				}
 168			}
 169
 170			final Jid jid;
 171			try {
 172				if (mUsernameMode) {
 173					jid = Jid.fromParts(mAccountJid.getText().toString(), getUserModeDomain(), null);
 174				} else {
 175					jid = Jid.fromString(mAccountJid.getText().toString());
 176				}
 177			} catch (final InvalidJidException e) {
 178				if (mUsernameMode) {
 179					mAccountJid.setError(getString(R.string.invalid_username));
 180				} else {
 181					mAccountJid.setError(getString(R.string.invalid_jid));
 182				}
 183				mAccountJid.requestFocus();
 184				return;
 185			}
 186			String hostname = null;
 187			int numericPort = 5222;
 188			if (mShowOptions) {
 189				hostname = mHostname.getText().toString().replaceAll("\\s","");
 190				final String port = mPort.getText().toString().replaceAll("\\s","");
 191				if (hostname.contains(" ")) {
 192					mHostname.setError(getString(R.string.not_valid_hostname));
 193					mHostname.requestFocus();
 194					return;
 195				}
 196				try {
 197					numericPort = Integer.parseInt(port);
 198					if (numericPort < 0 || numericPort > 65535) {
 199						mPort.setError(getString(R.string.not_a_valid_port));
 200						mPort.requestFocus();
 201						return;
 202					}
 203
 204				} catch (NumberFormatException e) {
 205					mPort.setError(getString(R.string.not_a_valid_port));
 206					mPort.requestFocus();
 207					return;
 208				}
 209			}
 210
 211			if (jid.isDomainJid()) {
 212				if (mUsernameMode) {
 213					mAccountJid.setError(getString(R.string.invalid_username));
 214				} else {
 215					mAccountJid.setError(getString(R.string.invalid_jid));
 216				}
 217				mAccountJid.requestFocus();
 218				return;
 219			}
 220			if (mAccount != null) {
 221				if (mInitMode && mAccount.isOptionSet(Account.OPTION_MAGIC_CREATE)) {
 222					mAccount.setOption(Account.OPTION_MAGIC_CREATE, mAccount.getPassword().contains(password));
 223				}
 224				mAccount.setJid(jid);
 225				mAccount.setPort(numericPort);
 226				mAccount.setHostname(hostname);
 227				mAccountJid.setError(null);
 228				mAccount.setPassword(password);
 229				mAccount.setOption(Account.OPTION_REGISTER, registerNewAccount);
 230				if (!xmppConnectionService.updateAccount(mAccount)) {
 231					Toast.makeText(EditAccountActivity.this,R.string.unable_to_update_account,Toast.LENGTH_SHORT).show();
 232					return;
 233				}
 234			} else {
 235				if (xmppConnectionService.findAccountByJid(jid) != null) {
 236					mAccountJid.setError(getString(R.string.account_already_exists));
 237					mAccountJid.requestFocus();
 238					return;
 239				}
 240				mAccount = new Account(jid.toBareJid(), password);
 241				mAccount.setPort(numericPort);
 242				mAccount.setHostname(hostname);
 243				mAccount.setOption(Account.OPTION_USETLS, true);
 244				mAccount.setOption(Account.OPTION_USECOMPRESSION, true);
 245				mAccount.setOption(Account.OPTION_REGISTER, registerNewAccount);
 246				xmppConnectionService.createAccount(mAccount);
 247			}
 248			mHostname.setError(null);
 249			mPort.setError(null);
 250			if (mAccount.isEnabled()
 251					&& !registerNewAccount
 252					&& !mInitMode) {
 253				finish();
 254			} else {
 255				updateSaveButton();
 256				updateAccountInformation(true);
 257			}
 258
 259		}
 260	};
 261	private final OnClickListener mCancelButtonClickListener = new OnClickListener() {
 262
 263		@Override
 264		public void onClick(final View v) {
 265			deleteAccountAndReturnIfNecessary();
 266			finish();
 267		}
 268	};
 269	private Toast mFetchingMamPrefsToast;
 270	private TableRow mPushRow;
 271	private String mSavedInstanceAccount;
 272	private boolean mSavedInstanceInit = false;
 273	private Button mClearDevicesButton;
 274	private XmppUri pendingUri = null;
 275
 276	public void refreshUiReal() {
 277		invalidateOptionsMenu();
 278		if (mAccount != null
 279				&& mAccount.getStatus() != Account.State.ONLINE
 280				&& mFetchingAvatar) {
 281			startActivity(new Intent(getApplicationContext(),
 282					ManageAccountActivity.class));
 283			finish();
 284		} else if (mInitMode && mAccount != null && mAccount.getStatus() == Account.State.ONLINE) {
 285			if (!mFetchingAvatar) {
 286				mFetchingAvatar = true;
 287				xmppConnectionService.checkForAvatar(mAccount, mAvatarFetchCallback);
 288			}
 289		}
 290		if (mAccount != null) {
 291			updateAccountInformation(false);
 292		}
 293		updateSaveButton();
 294	}
 295
 296	@Override
 297	public boolean onNavigateUp() {
 298		deleteAccountAndReturnIfNecessary();
 299		return super.onNavigateUp();
 300	}
 301
 302	@Override
 303	public void onBackPressed() {
 304		deleteAccountAndReturnIfNecessary();
 305		super.onBackPressed();
 306	}
 307
 308	private void deleteAccountAndReturnIfNecessary() {
 309		if (mInitMode && mAccount != null && !mAccount.isOptionSet(Account.OPTION_LOGGED_IN_SUCCESSFULLY)) {
 310			xmppConnectionService.deleteAccount(mAccount);
 311		}
 312
 313		if (xmppConnectionService.getAccounts().size() == 0) {
 314			Intent intent = new Intent(EditAccountActivity.this, WelcomeActivity.class);
 315			WelcomeActivity.addInviteUri(intent, getIntent());
 316			startActivity(intent);
 317		}
 318	}
 319
 320	@Override
 321	public void onAccountUpdate() {
 322		refreshUi();
 323	}
 324
 325	private final UiCallback<Avatar> mAvatarFetchCallback = new UiCallback<Avatar>() {
 326
 327		@Override
 328		public void userInputRequried(final PendingIntent pi, final Avatar avatar) {
 329			finishInitialSetup(avatar);
 330		}
 331
 332		@Override
 333		public void success(final Avatar avatar) {
 334			finishInitialSetup(avatar);
 335		}
 336
 337		@Override
 338		public void error(final int errorCode, final Avatar avatar) {
 339			finishInitialSetup(avatar);
 340		}
 341	};
 342	private final TextWatcher mTextWatcher = new TextWatcher() {
 343
 344		@Override
 345		public void onTextChanged(final CharSequence s, final int start, final int before, final int count) {
 346			updateSaveButton();
 347		}
 348
 349		@Override
 350		public void beforeTextChanged(final CharSequence s, final int start, final int count, final int after) {
 351		}
 352
 353		@Override
 354		public void afterTextChanged(final Editable s) {
 355
 356		}
 357	};
 358
 359	private final OnClickListener mAvatarClickListener = new OnClickListener() {
 360		@Override
 361		public void onClick(final View view) {
 362			if (mAccount != null) {
 363				final Intent intent = new Intent(getApplicationContext(), PublishProfilePictureActivity.class);
 364				intent.putExtra(EXTRA_ACCOUNT, mAccount.getJid().toBareJid().toString());
 365				startActivity(intent);
 366			}
 367		}
 368	};
 369
 370	protected void finishInitialSetup(final Avatar avatar) {
 371		runOnUiThread(() -> {
 372			hideKeyboard();
 373			final Intent intent;
 374			final XmppConnection connection = mAccount.getXmppConnection();
 375			final boolean wasFirstAccount = xmppConnectionService != null && xmppConnectionService.getAccounts().size() == 1;
 376			if (avatar != null || (connection != null && !connection.getFeatures().pep())) {
 377				intent = new Intent(getApplicationContext(), StartConversationActivity.class);
 378				if (wasFirstAccount) {
 379					intent.putExtra("init", true);
 380				}
 381			} else {
 382				intent = new Intent(getApplicationContext(), PublishProfilePictureActivity.class);
 383				intent.putExtra(EXTRA_ACCOUNT, mAccount.getJid().toBareJid().toString());
 384				intent.putExtra("setup", true);
 385			}
 386			if (wasFirstAccount) {
 387				intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
 388			}
 389			WelcomeActivity.addInviteUri(intent, getIntent());
 390			startActivity(intent);
 391			finish();
 392		});
 393	}
 394
 395	@Override
 396	public void onActivityResult(int requestCode, int resultCode, Intent data) {
 397		super.onActivityResult(requestCode, resultCode, data);
 398		if (requestCode == REQUEST_BATTERY_OP || requestCode == REQUEST_DATA_SAVER) {
 399			updateAccountInformation(mAccount == null);
 400		}
 401	}
 402
 403	@Override
 404	protected void processFingerprintVerification(XmppUri uri) {
 405		processFingerprintVerification(uri,true);
 406	}
 407
 408
 409	protected void processFingerprintVerification(XmppUri uri, boolean showWarningToast) {
 410		if (mAccount != null && mAccount.getJid().toBareJid().equals(uri.getJid()) && uri.hasFingerprints()) {
 411			if (xmppConnectionService.verifyFingerprints(mAccount,uri.getFingerprints())) {
 412				Toast.makeText(this,R.string.verified_fingerprints,Toast.LENGTH_SHORT).show();
 413			}
 414		} else if (showWarningToast) {
 415			Toast.makeText(this,R.string.invalid_barcode,Toast.LENGTH_SHORT).show();
 416		}
 417	}
 418
 419	protected void updateSaveButton() {
 420		boolean accountInfoEdited = accountInfoEdited();
 421
 422		if (!mInitMode && passwordChangedInMagicCreateMode()) {
 423			this.mSaveButton.setText(R.string.change_password);
 424			this.mSaveButton.setEnabled(true);
 425			this.mSaveButton.setTextColor(getPrimaryTextColor());
 426		} else if (accountInfoEdited && !mInitMode) {
 427			this.mSaveButton.setText(R.string.save);
 428			this.mSaveButton.setEnabled(true);
 429			this.mSaveButton.setTextColor(getPrimaryTextColor());
 430		} else if (mAccount != null
 431				&& (mAccount.getStatus() == Account.State.CONNECTING || mAccount.getStatus() == Account.State.REGISTRATION_SUCCESSFUL|| mFetchingAvatar)) {
 432			this.mSaveButton.setEnabled(false);
 433			this.mSaveButton.setTextColor(getSecondaryTextColor());
 434			this.mSaveButton.setText(R.string.account_status_connecting);
 435		} else if (mAccount != null && mAccount.getStatus() == Account.State.DISABLED && !mInitMode) {
 436			this.mSaveButton.setEnabled(true);
 437			this.mSaveButton.setTextColor(getPrimaryTextColor());
 438			this.mSaveButton.setText(R.string.enable);
 439		} else {
 440			this.mSaveButton.setEnabled(true);
 441			this.mSaveButton.setTextColor(getPrimaryTextColor());
 442			if (!mInitMode) {
 443				if (mAccount != null && mAccount.isOnlineAndConnected()) {
 444					this.mSaveButton.setText(R.string.save);
 445					if (!accountInfoEdited) {
 446						this.mSaveButton.setEnabled(false);
 447						this.mSaveButton.setTextColor(getSecondaryTextColor());
 448					}
 449				} else {
 450					XmppConnection connection = mAccount == null ? null : mAccount.getXmppConnection();
 451					URL url = connection != null &&  mAccount.getStatus() == Account.State.PAYMENT_REQUIRED ? connection.getRedirectionUrl() : null;
 452					if (url != null) {
 453						this.mSaveButton.setText(R.string.open_website);
 454					} else {
 455						this.mSaveButton.setText(R.string.connect);
 456					}
 457				}
 458			} else {
 459				XmppConnection connection = mAccount == null ? null : mAccount.getXmppConnection();
 460				URL url = connection != null &&  mAccount.getStatus() == Account.State.REGISTRATION_WEB ? connection.getRedirectionUrl() : null;
 461				if (url != null && mRegisterNew.isChecked()) {
 462					this.mSaveButton.setText(R.string.open_website);
 463				} else {
 464					this.mSaveButton.setText(R.string.next);
 465				}
 466			}
 467		}
 468	}
 469
 470	protected boolean accountInfoEdited() {
 471		if (this.mAccount == null) {
 472			return false;
 473		}
 474		return jidEdited() ||
 475				!this.mAccount.getPassword().equals(this.mPassword.getText().toString()) ||
 476				!this.mAccount.getHostname().equals(this.mHostname.getText().toString()) ||
 477				!String.valueOf(this.mAccount.getPort()).equals(this.mPort.getText().toString());
 478	}
 479
 480	protected boolean jidEdited() {
 481		final String unmodified;
 482		if (mUsernameMode) {
 483			unmodified = this.mAccount.getJid().getLocalpart();
 484		} else {
 485			unmodified = this.mAccount.getJid().toBareJid().toString();
 486		}
 487		return !unmodified.equals(this.mAccountJid.getText().toString());
 488	}
 489
 490	protected boolean passwordChangedInMagicCreateMode() {
 491		return mAccount != null
 492				&& mAccount.isOptionSet(Account.OPTION_MAGIC_CREATE)
 493				&& !this.mAccount.getPassword().equals(this.mPassword.getText().toString())
 494				&& !this.jidEdited()
 495				&& mAccount.isOnlineAndConnected();
 496	}
 497
 498	@Override
 499	protected String getShareableUri(boolean http) {
 500		if (mAccount != null) {
 501			return http ? mAccount.getShareableLink() : mAccount.getShareableUri();
 502		} else {
 503			return null;
 504		}
 505	}
 506
 507	@Override
 508	protected void onCreate(final Bundle savedInstanceState) {
 509		super.onCreate(savedInstanceState);
 510		if (savedInstanceState != null) {
 511			this.mSavedInstanceAccount = savedInstanceState.getString("account");
 512			this.mSavedInstanceInit = savedInstanceState.getBoolean("initMode", false);
 513		}
 514		setContentView(R.layout.activity_edit_account);
 515		this.mAccountJid = (AutoCompleteTextView) findViewById(R.id.account_jid);
 516		this.mAccountJid.addTextChangedListener(this.mTextWatcher);
 517		this.mAccountJidLabel = (TextView) findViewById(R.id.account_jid_label);
 518		this.mPassword = (EditText) findViewById(R.id.account_password);
 519		this.mPassword.addTextChangedListener(this.mTextWatcher);
 520		this.mAvatar = (ImageView) findViewById(R.id.avater);
 521		this.mAvatar.setOnClickListener(this.mAvatarClickListener);
 522		this.mRegisterNew = (CheckBox) findViewById(R.id.account_register_new);
 523		this.mStats = (LinearLayout) findViewById(R.id.stats);
 524		this.mOsOptimizations = (RelativeLayout) findViewById(R.id.os_optimization);
 525		this.mDisableOsOptimizationsButton = (Button) findViewById(R.id.os_optimization_disable);
 526		this.mDisableOsOptimizationsHeadline = (TextView) findViewById(R.id.os_optimization_headline);
 527		this.getmDisableOsOptimizationsBody = (TextView) findViewById(R.id.os_optimization_body);
 528		this.mSessionEst = (TextView) findViewById(R.id.session_est);
 529		this.mServerInfoRosterVersion = (TextView) findViewById(R.id.server_info_roster_version);
 530		this.mServerInfoCarbons = (TextView) findViewById(R.id.server_info_carbons);
 531		this.mServerInfoMam = (TextView) findViewById(R.id.server_info_mam);
 532		this.mServerInfoCSI = (TextView) findViewById(R.id.server_info_csi);
 533		this.mServerInfoBlocking = (TextView) findViewById(R.id.server_info_blocking);
 534		this.mServerInfoSm = (TextView) findViewById(R.id.server_info_sm);
 535		this.mServerInfoPep = (TextView) findViewById(R.id.server_info_pep);
 536		this.mServerInfoHttpUpload = (TextView) findViewById(R.id.server_info_http_upload);
 537		this.mPushRow = (TableRow) findViewById(R.id.push_row);
 538		this.mServerInfoPush = (TextView) findViewById(R.id.server_info_push);
 539		this.mPgpFingerprintBox = (RelativeLayout) findViewById(R.id.pgp_fingerprint_box);
 540		this.mPgpFingerprint = (TextView) findViewById(R.id.pgp_fingerprint);
 541		this.getmPgpFingerprintDesc = (TextView) findViewById(R.id.pgp_fingerprint_desc);
 542		this.mPgpDeleteFingerprintButton = (ImageButton) findViewById(R.id.action_delete_pgp);
 543		this.mOtrFingerprint = (TextView) findViewById(R.id.otr_fingerprint);
 544		this.mOtrFingerprintDesc = (TextView) findViewById(R.id.otr_fingerprint_desc);
 545		this.mOtrFingerprintBox = (RelativeLayout) findViewById(R.id.otr_fingerprint_box);
 546		this.mOtrFingerprintToClipboardButton = (ImageButton) findViewById(R.id.action_copy_to_clipboard);
 547		this.mAxolotlFingerprint = (TextView) findViewById(R.id.axolotl_fingerprint);
 548		this.mAxolotlFingerprintBox = (RelativeLayout) findViewById(R.id.axolotl_fingerprint_box);
 549		this.mAxolotlFingerprintToClipboardButton = (ImageButton) findViewById(R.id.action_copy_axolotl_to_clipboard);
 550		this.mOwnFingerprintDesc = (TextView) findViewById(R.id.own_fingerprint_desc);
 551		this.keysCard = (LinearLayout) findViewById(R.id.other_device_keys_card);
 552		this.keys = (LinearLayout) findViewById(R.id.other_device_keys);
 553		this.mNamePort = (LinearLayout) findViewById(R.id.name_port);
 554		this.mHostname = (EditText) findViewById(R.id.hostname);
 555		this.mHostname.addTextChangedListener(mTextWatcher);
 556		this.mClearDevicesButton = (Button) findViewById(R.id.clear_devices);
 557		this.mClearDevicesButton.setOnClickListener(new OnClickListener() {
 558			@Override
 559			public void onClick(View v) {
 560				showWipePepDialog();
 561			}
 562		});
 563		this.mPort = (EditText) findViewById(R.id.port);
 564		this.mPort.setText("5222");
 565		this.mPort.addTextChangedListener(mTextWatcher);
 566		this.mSaveButton = (Button) findViewById(R.id.save_button);
 567		this.mCancelButton = (Button) findViewById(R.id.cancel_button);
 568		this.mSaveButton.setOnClickListener(this.mSaveButtonClickListener);
 569		this.mCancelButton.setOnClickListener(this.mCancelButtonClickListener);
 570		this.mMoreTable = (TableLayout) findViewById(R.id.server_info_more);
 571		if (savedInstanceState != null && savedInstanceState.getBoolean("showMoreTable")) {
 572			changeMoreTableVisibility(true);
 573		}
 574		final OnCheckedChangeListener OnCheckedShowConfirmPassword = new OnCheckedChangeListener() {
 575			@Override
 576			public void onCheckedChanged(final CompoundButton buttonView, final boolean isChecked) {
 577				updateSaveButton();
 578			}
 579		};
 580		this.mRegisterNew.setOnCheckedChangeListener(OnCheckedShowConfirmPassword);
 581		if (Config.DISALLOW_REGISTRATION_IN_UI) {
 582			this.mRegisterNew.setVisibility(View.GONE);
 583		}
 584	}
 585
 586	@Override
 587	public boolean onCreateOptionsMenu(final Menu menu) {
 588		super.onCreateOptionsMenu(menu);
 589		getMenuInflater().inflate(R.menu.editaccount, menu);
 590		final MenuItem showBlocklist = menu.findItem(R.id.action_show_block_list);
 591		final MenuItem showMoreInfo = menu.findItem(R.id.action_server_info_show_more);
 592		final MenuItem changePassword = menu.findItem(R.id.action_change_password_on_server);
 593		final MenuItem renewCertificate = menu.findItem(R.id.action_renew_certificate);
 594		final MenuItem mamPrefs = menu.findItem(R.id.action_mam_prefs);
 595		final MenuItem changePresence = menu.findItem(R.id.action_change_presence);
 596		final MenuItem share = menu.findItem(R.id.action_share);
 597		renewCertificate.setVisible(mAccount != null && mAccount.getPrivateKeyAlias() != null);
 598
 599		share.setVisible(mAccount != null && !mInitMode);
 600
 601		if (mAccount != null && mAccount.isOnlineAndConnected()) {
 602			if (!mAccount.getXmppConnection().getFeatures().blocking()) {
 603				showBlocklist.setVisible(false);
 604			}
 605
 606			if (!mAccount.getXmppConnection().getFeatures().register()) {
 607				changePassword.setVisible(false);
 608			}
 609			mamPrefs.setVisible(mAccount.getXmppConnection().getFeatures().mam());
 610			changePresence.setVisible(manuallyChangePresence());
 611		} else {
 612			showBlocklist.setVisible(false);
 613			showMoreInfo.setVisible(false);
 614			changePassword.setVisible(false);
 615			mamPrefs.setVisible(false);
 616			changePresence.setVisible(false);
 617		}
 618		return super.onCreateOptionsMenu(menu);
 619	}
 620
 621	@Override
 622	public boolean onPrepareOptionsMenu(Menu menu) {
 623		final MenuItem showMoreInfo = menu.findItem(R.id.action_server_info_show_more);
 624		if (showMoreInfo.isVisible()) {
 625			showMoreInfo.setChecked(mMoreTable.getVisibility() == View.VISIBLE);
 626		}
 627		return super.onPrepareOptionsMenu(menu);
 628	}
 629
 630	@Override
 631	protected void onStart() {
 632		super.onStart();
 633		final int theme = findTheme();
 634		if (this.mTheme != theme) {
 635			recreate();
 636		} else if (getIntent() != null) {
 637			try {
 638				this.jidToEdit = Jid.fromString(getIntent().getStringExtra("jid"));
 639			} catch (final InvalidJidException | NullPointerException ignored) {
 640				this.jidToEdit = null;
 641			}
 642			if (jidToEdit != null && getIntent().getData() != null) {
 643				final XmppUri uri = new XmppUri(getIntent().getData());
 644				if (xmppConnectionServiceBound) {
 645					processFingerprintVerification(uri, false);
 646				} else {
 647					this.pendingUri = uri;
 648				}
 649			}
 650			boolean init = getIntent().getBooleanExtra("init", false);
 651			this.mInitMode = init || this.jidToEdit == null;
 652			this.messageFingerprint = getIntent().getStringExtra("fingerprint");
 653			if (!mInitMode) {
 654				this.mRegisterNew.setVisibility(View.GONE);
 655				if (getSupportActionBar() != null) {
 656					getSupportActionBar().setTitle(getString(R.string.account_details));
 657				}
 658			} else {
 659				this.mAvatar.setVisibility(View.GONE);
 660				ActionBar ab = getSupportActionBar();
 661				if (ab != null) {
 662					if (init && Config.MAGIC_CREATE_DOMAIN == null) {
 663						ab.setDisplayShowHomeEnabled(false);
 664						ab.setDisplayHomeAsUpEnabled(false);
 665					}
 666					ab.setTitle(R.string.action_add_account);
 667				}
 668			}
 669		}
 670		SharedPreferences preferences = getPreferences();
 671		boolean useTor = Config.FORCE_ORBOT || preferences.getBoolean("use_tor", false);
 672		this.mShowOptions = useTor || preferences.getBoolean("show_connection_options", false);
 673		mHostname.setHint(useTor ? R.string.hostname_or_onion : R.string.hostname_example);
 674		this.mNamePort.setVisibility(mShowOptions ? View.VISIBLE : View.GONE);
 675	}
 676
 677	@Override
 678	public void onNewIntent(Intent intent) {
 679		if (intent != null && intent.getData() != null) {
 680			final XmppUri uri = new XmppUri(intent.getData());
 681			if (xmppConnectionServiceBound) {
 682				processFingerprintVerification(uri, false);
 683			} else {
 684				this.pendingUri = uri;
 685			}
 686		}
 687	}
 688
 689	@Override
 690	public void onSaveInstanceState(final Bundle savedInstanceState) {
 691		if (mAccount != null) {
 692			savedInstanceState.putString("account", mAccount.getJid().toBareJid().toString());
 693			savedInstanceState.putBoolean("initMode", mInitMode);
 694			savedInstanceState.putBoolean("showMoreTable", mMoreTable.getVisibility() == View.VISIBLE);
 695		}
 696		super.onSaveInstanceState(savedInstanceState);
 697	}
 698
 699	protected void onBackendConnected() {
 700		boolean init = true;
 701		if (mSavedInstanceAccount != null) {
 702			try {
 703				this.mAccount = xmppConnectionService.findAccountByJid(Jid.fromString(mSavedInstanceAccount));
 704				this.mInitMode = mSavedInstanceInit;
 705				init = false;
 706			} catch (InvalidJidException e) {
 707				this.mAccount = null;
 708			}
 709
 710		} else if (this.jidToEdit != null) {
 711			this.mAccount = xmppConnectionService.findAccountByJid(jidToEdit);
 712		}
 713
 714		if (mAccount != null) {
 715			this.mInitMode |= this.mAccount.isOptionSet(Account.OPTION_REGISTER);
 716			this.mUsernameMode |= mAccount.isOptionSet(Account.OPTION_MAGIC_CREATE) && mAccount.isOptionSet(Account.OPTION_REGISTER);
 717			if (this.mAccount.getPrivateKeyAlias() != null) {
 718				this.mPassword.setHint(R.string.authenticate_with_certificate);
 719				if (this.mInitMode) {
 720					this.mPassword.requestFocus();
 721				}
 722			}
 723			if (mPendingFingerprintVerificationUri != null) {
 724				processFingerprintVerification(mPendingFingerprintVerificationUri, false);
 725				mPendingFingerprintVerificationUri = null;
 726			}
 727			updateAccountInformation(init);
 728		}
 729
 730
 731		if (Config.MAGIC_CREATE_DOMAIN == null && this.xmppConnectionService.getAccounts().size() == 0) {
 732			this.mCancelButton.setEnabled(false);
 733			this.mCancelButton.setTextColor(getSecondaryTextColor());
 734		}
 735		if (mUsernameMode) {
 736			this.mAccountJidLabel.setText(R.string.username);
 737			this.mAccountJid.setHint(R.string.username_hint);
 738		} else {
 739			final KnownHostsAdapter mKnownHostsAdapter = new KnownHostsAdapter(this,
 740					R.layout.simple_list_item,
 741					xmppConnectionService.getKnownHosts());
 742			this.mAccountJid.setAdapter(mKnownHostsAdapter);
 743		}
 744
 745		if (pendingUri != null) {
 746			processFingerprintVerification(pendingUri, false);
 747			pendingUri = null;
 748		}
 749
 750		updateSaveButton();
 751		invalidateOptionsMenu();
 752	}
 753
 754	private String getUserModeDomain() {
 755		if (mAccount != null) {
 756			return mAccount.getJid().getDomainpart();
 757		} else {
 758			return Config.DOMAIN_LOCK;
 759		}
 760	}
 761
 762	@Override
 763	public boolean onOptionsItemSelected(final MenuItem item) {
 764		switch (item.getItemId()) {
 765			case R.id.action_show_block_list:
 766				final Intent showBlocklistIntent = new Intent(this, BlocklistActivity.class);
 767				showBlocklistIntent.putExtra(EXTRA_ACCOUNT, mAccount.getJid().toString());
 768				startActivity(showBlocklistIntent);
 769				break;
 770			case R.id.action_server_info_show_more:
 771				changeMoreTableVisibility(!item.isChecked());
 772				break;
 773			case R.id.action_share_barcode:
 774				shareBarcode();
 775				break;
 776			case R.id.action_share_http:
 777				shareLink(true);
 778				break;
 779			case R.id.action_share_uri:
 780				shareLink(false);
 781				break;
 782			case R.id.action_change_password_on_server:
 783				gotoChangePassword(null);
 784				break;
 785			case R.id.action_mam_prefs:
 786				editMamPrefs();
 787				break;
 788			case R.id.action_renew_certificate:
 789				renewCertificate();
 790				break;
 791			case R.id.action_change_presence:
 792				changePresence();
 793				break;
 794		}
 795		return super.onOptionsItemSelected(item);
 796	}
 797
 798	private void shareBarcode() {
 799		Intent intent = new Intent(Intent.ACTION_SEND);
 800		intent.putExtra(Intent.EXTRA_STREAM,BarcodeProvider.getUriForAccount(this,mAccount));
 801		intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
 802		intent.setType("image/png");
 803		startActivity(Intent.createChooser(intent, getText(R.string.share_with)));
 804	}
 805
 806	private void changeMoreTableVisibility(boolean visible) {
 807		mMoreTable.setVisibility(visible ? View.VISIBLE : View.GONE);
 808	}
 809
 810	private void gotoChangePassword(String newPassword) {
 811		final Intent changePasswordIntent = new Intent(this, ChangePasswordActivity.class);
 812		changePasswordIntent.putExtra(EXTRA_ACCOUNT, mAccount.getJid().toString());
 813		if (newPassword != null) {
 814			changePasswordIntent.putExtra("password", newPassword);
 815		}
 816		startActivity(changePasswordIntent);
 817	}
 818
 819	private void renewCertificate() {
 820		KeyChain.choosePrivateKeyAlias(this, this, null, null, null, -1, null);
 821	}
 822
 823	private void changePresence() {
 824		Intent intent = new Intent(this, SetPresenceActivity.class);
 825		intent.putExtra(SetPresenceActivity.EXTRA_ACCOUNT,mAccount.getJid().toBareJid().toString());
 826		startActivity(intent);
 827	}
 828
 829	@Override
 830	public void alias(String alias) {
 831		if (alias != null) {
 832			xmppConnectionService.updateKeyInAccount(mAccount, alias);
 833		}
 834	}
 835
 836	private void updateAccountInformation(boolean init) {
 837		if (init) {
 838			this.mAccountJid.getEditableText().clear();
 839			if (mUsernameMode) {
 840				this.mAccountJid.getEditableText().append(this.mAccount.getJid().getLocalpart());
 841			} else {
 842				this.mAccountJid.getEditableText().append(this.mAccount.getJid().toBareJid().toString());
 843			}
 844			this.mPassword.getEditableText().clear();
 845			this.mPassword.getEditableText().append(this.mAccount.getPassword());
 846			this.mHostname.setText("");
 847			this.mHostname.getEditableText().append(this.mAccount.getHostname());
 848			this.mPort.setText("");
 849			this.mPort.getEditableText().append(String.valueOf(this.mAccount.getPort()));
 850			this.mNamePort.setVisibility(mShowOptions ? View.VISIBLE : View.GONE);
 851
 852		}
 853
 854		final boolean editable = !mAccount.isOptionSet(Account.OPTION_LOGGED_IN_SUCCESSFULLY);
 855		this.mAccountJid.setEnabled(editable);
 856		this.mAccountJid.setFocusable(editable);
 857		this.mAccountJid.setFocusableInTouchMode(editable);
 858		if (editable) {
 859			this.mPassword.setCustomSelectionActionModeCallback(null);
 860		} else {
 861			this.mPassword.setCustomSelectionActionModeCallback(new DisabledActionModeCallback());
 862		}
 863
 864		if (!mInitMode) {
 865			this.mAvatar.setVisibility(View.VISIBLE);
 866			this.mAvatar.setImageBitmap(avatarService().get(this.mAccount, getPixel(72)));
 867		} else {
 868			this.mAvatar.setVisibility(View.GONE);
 869		}
 870		if (this.mAccount.isOptionSet(Account.OPTION_REGISTER)) {
 871			this.mRegisterNew.setVisibility(View.VISIBLE);
 872			this.mRegisterNew.setChecked(true);
 873		} else {
 874			this.mRegisterNew.setVisibility(View.GONE);
 875			this.mRegisterNew.setChecked(false);
 876		}
 877		if (this.mAccount.isOnlineAndConnected() && !this.mFetchingAvatar) {
 878			Features features = this.mAccount.getXmppConnection().getFeatures();
 879			this.mStats.setVisibility(View.VISIBLE);
 880			boolean showBatteryWarning = !xmppConnectionService.getPushManagementService().available(mAccount) && isOptimizingBattery();
 881			boolean showDataSaverWarning = isAffectedByDataSaver();
 882			showOsOptimizationWarning(showBatteryWarning,showDataSaverWarning);
 883			this.mSessionEst.setText(UIHelper.readableTimeDifferenceFull(this, this.mAccount.getXmppConnection()
 884					.getLastSessionEstablished()));
 885			if (features.rosterVersioning()) {
 886				this.mServerInfoRosterVersion.setText(R.string.server_info_available);
 887			} else {
 888				this.mServerInfoRosterVersion.setText(R.string.server_info_unavailable);
 889			}
 890			if (features.carbons()) {
 891				this.mServerInfoCarbons.setText(R.string.server_info_available);
 892			} else {
 893				this.mServerInfoCarbons
 894						.setText(R.string.server_info_unavailable);
 895			}
 896			if (features.mam()) {
 897				this.mServerInfoMam.setText(R.string.server_info_available);
 898			} else {
 899				this.mServerInfoMam.setText(R.string.server_info_unavailable);
 900			}
 901			if (features.csi()) {
 902				this.mServerInfoCSI.setText(R.string.server_info_available);
 903			} else {
 904				this.mServerInfoCSI.setText(R.string.server_info_unavailable);
 905			}
 906			if (features.blocking()) {
 907				this.mServerInfoBlocking.setText(R.string.server_info_available);
 908			} else {
 909				this.mServerInfoBlocking.setText(R.string.server_info_unavailable);
 910			}
 911			if (features.sm()) {
 912				this.mServerInfoSm.setText(R.string.server_info_available);
 913			} else {
 914				this.mServerInfoSm.setText(R.string.server_info_unavailable);
 915			}
 916			if (features.pep()) {
 917				AxolotlService axolotlService = this.mAccount.getAxolotlService();
 918				if (axolotlService != null && axolotlService.isPepBroken()) {
 919					this.mServerInfoPep.setText(R.string.server_info_broken);
 920				} else if (features.pepPublishOptions() || features.pepOmemoWhitelisted()) {
 921					this.mServerInfoPep.setText(R.string.server_info_available);
 922				} else {
 923					this.mServerInfoPep.setText(R.string.server_info_partial);
 924				}
 925			} else {
 926				this.mServerInfoPep.setText(R.string.server_info_unavailable);
 927			}
 928			if (features.httpUpload(0)) {
 929				this.mServerInfoHttpUpload.setText(R.string.server_info_available);
 930			} else {
 931				this.mServerInfoHttpUpload.setText(R.string.server_info_unavailable);
 932			}
 933
 934			this.mPushRow.setVisibility(xmppConnectionService.getPushManagementService().isStub() ? View.GONE : View.VISIBLE);
 935
 936			if (xmppConnectionService.getPushManagementService().available(mAccount)) {
 937				this.mServerInfoPush.setText(R.string.server_info_available);
 938			} else {
 939				this.mServerInfoPush.setText(R.string.server_info_unavailable);
 940			}
 941			final long pgpKeyId = this.mAccount.getPgpId();
 942			if (pgpKeyId != 0 && Config.supportOpenPgp()) {
 943				OnClickListener openPgp = new OnClickListener() {
 944					@Override
 945					public void onClick(View view) {
 946						launchOpenKeyChain(pgpKeyId);
 947					}
 948				};
 949				OnClickListener delete = new OnClickListener() {
 950					@Override
 951					public void onClick(View view) {
 952						showDeletePgpDialog();
 953					}
 954				};
 955				this.mPgpFingerprintBox.setVisibility(View.VISIBLE);
 956				this.mPgpFingerprint.setText(OpenPgpUtils.convertKeyIdToHex(pgpKeyId));
 957				this.mPgpFingerprint.setOnClickListener(openPgp);
 958				if ("pgp".equals(messageFingerprint)) {
 959					this.getmPgpFingerprintDesc.setTextColor(ContextCompat.getColor(this, R.color.accent));
 960				}
 961				this.getmPgpFingerprintDesc.setOnClickListener(openPgp);
 962				this.mPgpDeleteFingerprintButton.setOnClickListener(delete);
 963			} else {
 964				this.mPgpFingerprintBox.setVisibility(View.GONE);
 965			}
 966			final String otrFingerprint = this.mAccount.getOtrFingerprint();
 967			if (otrFingerprint != null && Config.supportOtr()) {
 968				if ("otr".equals(messageFingerprint)) {
 969					this.mOtrFingerprintDesc.setTextColor(ContextCompat.getColor(this, R.color.accent));
 970				}
 971				this.mOtrFingerprintBox.setVisibility(View.VISIBLE);
 972				this.mOtrFingerprint.setText(CryptoHelper.prettifyFingerprint(otrFingerprint));
 973				this.mOtrFingerprintToClipboardButton
 974						.setVisibility(View.VISIBLE);
 975				this.mOtrFingerprintToClipboardButton
 976						.setOnClickListener(new View.OnClickListener() {
 977
 978							@Override
 979							public void onClick(final View v) {
 980
 981								if (copyTextToClipboard(CryptoHelper.prettifyFingerprint(otrFingerprint), R.string.otr_fingerprint)) {
 982									Toast.makeText(
 983											EditAccountActivity.this,
 984											R.string.toast_message_otr_fingerprint,
 985											Toast.LENGTH_SHORT).show();
 986								}
 987							}
 988						});
 989			} else {
 990				this.mOtrFingerprintBox.setVisibility(View.GONE);
 991			}
 992			final String ownAxolotlFingerprint = this.mAccount.getAxolotlService().getOwnFingerprint();
 993			if (ownAxolotlFingerprint != null && Config.supportOmemo()) {
 994				this.mAxolotlFingerprintBox.setVisibility(View.VISIBLE);
 995				if (ownAxolotlFingerprint.equals(messageFingerprint)) {
 996					this.mOwnFingerprintDesc.setTextColor(ContextCompat.getColor(this, R.color.accent));
 997					this.mOwnFingerprintDesc.setText(R.string.omemo_fingerprint_selected_message);
 998				} else {
 999					this.mOwnFingerprintDesc.setTextColor(getSecondaryTextColor());
1000					this.mOwnFingerprintDesc.setText(R.string.omemo_fingerprint);
1001				}
1002				this.mAxolotlFingerprint.setText(CryptoHelper.prettifyFingerprint(ownAxolotlFingerprint.substring(2)));
1003				this.mAxolotlFingerprintToClipboardButton
1004						.setVisibility(View.VISIBLE);
1005				this.mAxolotlFingerprintToClipboardButton
1006						.setOnClickListener(new View.OnClickListener() {
1007
1008							@Override
1009							public void onClick(final View v) {
1010								copyOmemoFingerprint(ownAxolotlFingerprint);
1011							}
1012						});
1013			} else {
1014				this.mAxolotlFingerprintBox.setVisibility(View.GONE);
1015			}
1016			boolean hasKeys = false;
1017			keys.removeAllViews();
1018			for(XmppAxolotlSession session : mAccount.getAxolotlService().findOwnSessions()) {
1019				if (!session.getTrust().isCompromised()) {
1020					boolean highlight = session.getFingerprint().equals(messageFingerprint);
1021					addFingerprintRow(keys,session,highlight);
1022					hasKeys = true;
1023				}
1024			}
1025			if (hasKeys && Config.supportOmemo()) {
1026				keysCard.setVisibility(View.VISIBLE);
1027				Set<Integer> otherDevices = mAccount.getAxolotlService().getOwnDeviceIds();
1028				if (otherDevices == null || otherDevices.isEmpty()) {
1029					mClearDevicesButton.setVisibility(View.GONE);
1030				} else {
1031					mClearDevicesButton.setVisibility(View.VISIBLE);
1032				}
1033			} else {
1034				keysCard.setVisibility(View.GONE);
1035			}
1036		} else {
1037			if (this.mAccount.errorStatus()) {
1038				final EditText errorTextField;
1039				if (this.mAccount.getStatus() == Account.State.UNAUTHORIZED) {
1040					errorTextField = this.mPassword;
1041				} else if (mShowOptions
1042						&& this.mAccount.getStatus() == Account.State.SERVER_NOT_FOUND
1043						&& this.mHostname.getText().length() > 0) {
1044					errorTextField = this.mHostname;
1045				} else {
1046					errorTextField = this.mAccountJid;
1047				}
1048				errorTextField.setError(getString(this.mAccount.getStatus().getReadableId()));
1049				if (init || !accountInfoEdited()) {
1050					errorTextField.requestFocus();
1051				}
1052			} else {
1053				this.mAccountJid.setError(null);
1054				this.mPassword.setError(null);
1055				this.mHostname.setError(null);
1056			}
1057			this.mStats.setVisibility(View.GONE);
1058		}
1059	}
1060
1061	private void showDeletePgpDialog() {
1062		AlertDialog.Builder builder = new AlertDialog.Builder(this);
1063		builder.setTitle(R.string.unpublish_pgp);
1064		builder.setMessage(R.string.unpublish_pgp_message);
1065		builder.setNegativeButton(R.string.cancel,null);
1066		builder.setPositiveButton(R.string.confirm, new DialogInterface.OnClickListener() {
1067			@Override
1068			public void onClick(DialogInterface dialogInterface, int i) {
1069				mAccount.setPgpSignId(0);
1070				mAccount.unsetPgpSignature();
1071				xmppConnectionService.databaseBackend.updateAccount(mAccount);
1072				xmppConnectionService.sendPresence(mAccount);
1073				refreshUiReal();
1074			}
1075		});
1076		builder.create().show();
1077	}
1078
1079	private void showOsOptimizationWarning(boolean showBatteryWarning, boolean showDataSaverWarning) {
1080		this.mOsOptimizations.setVisibility(showBatteryWarning || showDataSaverWarning ? View.VISIBLE : View.GONE);
1081		if (showDataSaverWarning) {
1082			this.mDisableOsOptimizationsHeadline.setText(R.string.data_saver_enabled);
1083			this.getmDisableOsOptimizationsBody.setText(R.string.data_saver_enabled_explained);
1084			this.mDisableOsOptimizationsButton.setText(R.string.allow);
1085			this.mDisableOsOptimizationsButton.setOnClickListener(new OnClickListener() {
1086				@Override
1087				public void onClick(View v) {
1088					Intent intent = new Intent(Settings.ACTION_IGNORE_BACKGROUND_DATA_RESTRICTIONS_SETTINGS);
1089					Uri uri = Uri.parse("package:"+getPackageName());
1090					intent.setData(uri);
1091					try {
1092						startActivityForResult(intent, REQUEST_DATA_SAVER);
1093					} catch (ActivityNotFoundException e) {
1094						Toast.makeText(EditAccountActivity.this, R.string.device_does_not_support_data_saver, Toast.LENGTH_SHORT).show();
1095					}
1096				}
1097			});
1098		} else if (showBatteryWarning) {
1099			this.mDisableOsOptimizationsButton.setText(R.string.disable);
1100			this.mDisableOsOptimizationsHeadline.setText(R.string.battery_optimizations_enabled);
1101			this.getmDisableOsOptimizationsBody.setText(R.string.battery_optimizations_enabled_explained);
1102			this.mDisableOsOptimizationsButton.setOnClickListener(new OnClickListener() {
1103				@Override
1104				public void onClick(View v) {
1105					Intent intent = new Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
1106					Uri uri = Uri.parse("package:"+getPackageName());
1107					intent.setData(uri);
1108					try {
1109						startActivityForResult(intent, REQUEST_BATTERY_OP);
1110					} catch (ActivityNotFoundException e) {
1111						Toast.makeText(EditAccountActivity.this, R.string.device_does_not_support_battery_op, Toast.LENGTH_SHORT).show();
1112					}
1113				}
1114			});
1115		}
1116	}
1117
1118	public void showWipePepDialog() {
1119		Builder builder = new Builder(this);
1120		builder.setTitle(getString(R.string.clear_other_devices));
1121		builder.setIconAttribute(android.R.attr.alertDialogIcon);
1122		builder.setMessage(getString(R.string.clear_other_devices_desc));
1123		builder.setNegativeButton(getString(R.string.cancel), null);
1124		builder.setPositiveButton(getString(R.string.accept),
1125				new DialogInterface.OnClickListener() {
1126					@Override
1127					public void onClick(DialogInterface dialog, int which) {
1128						mAccount.getAxolotlService().wipeOtherPepDevices();
1129					}
1130				});
1131		builder.create().show();
1132	}
1133
1134	private void editMamPrefs() {
1135		this.mFetchingMamPrefsToast = Toast.makeText(this, R.string.fetching_mam_prefs, Toast.LENGTH_LONG);
1136		this.mFetchingMamPrefsToast.show();
1137		xmppConnectionService.fetchMamPreferences(mAccount, this);
1138	}
1139
1140	@Override
1141	public void onKeyStatusUpdated(AxolotlService.FetchStatus report) {
1142		refreshUi();
1143	}
1144
1145	@Override
1146	public void onCaptchaRequested(final Account account, final String id, final Data data, final Bitmap captcha) {
1147		runOnUiThread(new Runnable() {
1148			@Override
1149			public void run() {
1150				if ((mCaptchaDialog != null) && mCaptchaDialog.isShowing()) {
1151					mCaptchaDialog.dismiss();
1152				}
1153				final AlertDialog.Builder builder = new AlertDialog.Builder(EditAccountActivity.this);
1154				final View view = getLayoutInflater().inflate(R.layout.captcha, null);
1155				final ImageView imageView = view.findViewById(R.id.captcha);
1156				final EditText input = view.findViewById(R.id.input);
1157				imageView.setImageBitmap(captcha);
1158
1159				builder.setTitle(getString(R.string.captcha_required));
1160				builder.setView(view);
1161
1162				builder.setPositiveButton(getString(R.string.ok),
1163						new DialogInterface.OnClickListener() {
1164							@Override
1165							public void onClick(DialogInterface dialog, int which) {
1166								String rc = input.getText().toString();
1167								data.put("username", account.getUsername());
1168								data.put("password", account.getPassword());
1169								data.put("ocr", rc);
1170								data.submit();
1171
1172								if (xmppConnectionServiceBound) {
1173									xmppConnectionService.sendCreateAccountWithCaptchaPacket(
1174											account, id, data);
1175								}
1176							}
1177						});
1178				builder.setNegativeButton(getString(R.string.cancel), new DialogInterface.OnClickListener() {
1179					@Override
1180					public void onClick(DialogInterface dialog, int which) {
1181						if (xmppConnectionService != null) {
1182							xmppConnectionService.sendCreateAccountWithCaptchaPacket(account, null, null);
1183						}
1184					}
1185				});
1186
1187				builder.setOnCancelListener(new DialogInterface.OnCancelListener() {
1188					@Override
1189					public void onCancel(DialogInterface dialog) {
1190						if (xmppConnectionService != null) {
1191							xmppConnectionService.sendCreateAccountWithCaptchaPacket(account, null, null);
1192						}
1193					}
1194				});
1195				mCaptchaDialog = builder.create();
1196				mCaptchaDialog.show();
1197				input.requestFocus();
1198			}
1199		});
1200	}
1201
1202	public void onShowErrorToast(final int resId) {
1203		runOnUiThread(new Runnable() {
1204			@Override
1205			public void run() {
1206				Toast.makeText(EditAccountActivity.this, resId, Toast.LENGTH_SHORT).show();
1207			}
1208		});
1209	}
1210
1211	@Override
1212	public void onPreferencesFetched(final Element prefs) {
1213		runOnUiThread(new Runnable() {
1214			@Override
1215			public void run() {
1216				if (mFetchingMamPrefsToast != null) {
1217					mFetchingMamPrefsToast.cancel();
1218				}
1219				AlertDialog.Builder builder = new Builder(EditAccountActivity.this);
1220				builder.setTitle(R.string.server_side_mam_prefs);
1221				String defaultAttr = prefs.getAttribute("default");
1222				final List<String> defaults = Arrays.asList("never", "roster", "always");
1223				final AtomicInteger choice = new AtomicInteger(Math.max(0,defaults.indexOf(defaultAttr)));
1224				builder.setSingleChoiceItems(R.array.mam_prefs, choice.get(), new DialogInterface.OnClickListener() {
1225					@Override
1226					public void onClick(DialogInterface dialog, int which) {
1227						choice.set(which);
1228					}
1229				});
1230				builder.setNegativeButton(R.string.cancel, null);
1231				builder.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
1232					@Override
1233					public void onClick(DialogInterface dialog, int which) {
1234						prefs.setAttribute("default",defaults.get(choice.get()));
1235						xmppConnectionService.pushMamPreferences(mAccount, prefs);
1236					}
1237				});
1238				builder.create().show();
1239			}
1240		});
1241	}
1242
1243	@Override
1244	public void onPreferencesFetchFailed() {
1245		runOnUiThread(new Runnable() {
1246			@Override
1247			public void run() {
1248				if (mFetchingMamPrefsToast != null) {
1249					mFetchingMamPrefsToast.cancel();
1250				}
1251				Toast.makeText(EditAccountActivity.this,R.string.unable_to_fetch_mam_prefs,Toast.LENGTH_LONG).show();
1252			}
1253		});
1254	}
1255
1256	@Override
1257	public void OnUpdateBlocklist(Status status) {
1258		refreshUi();
1259	}
1260}