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