EditAccountActivity.java

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