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}