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