1package eu.siacs.conversations.ui;
2
3import android.app.AlertDialog;
4import android.app.AlertDialog.Builder;
5import android.app.PendingIntent;
6import android.content.DialogInterface;
7import android.content.Intent;
8import android.content.res.Configuration;
9import android.graphics.Bitmap;
10import android.os.Bundle;
11import android.security.KeyChain;
12import android.security.KeyChainAliasCallback;
13import android.text.Editable;
14import android.text.TextWatcher;
15import android.view.Menu;
16import android.view.MenuItem;
17import android.view.View;
18import android.view.View.OnClickListener;
19import android.widget.AutoCompleteTextView;
20import android.widget.Button;
21import android.widget.CheckBox;
22import android.widget.CompoundButton;
23import android.widget.CompoundButton.OnCheckedChangeListener;
24import android.widget.EditText;
25import android.widget.ImageButton;
26import android.widget.ImageView;
27import android.widget.LinearLayout;
28import android.widget.RelativeLayout;
29import android.widget.TableLayout;
30import android.widget.TextView;
31import android.widget.Toast;
32
33import java.util.Set;
34
35import eu.siacs.conversations.Config;
36import eu.siacs.conversations.R;
37import eu.siacs.conversations.crypto.axolotl.AxolotlService;
38import eu.siacs.conversations.entities.Account;
39import eu.siacs.conversations.services.XmppConnectionService.OnCaptchaRequested;
40import eu.siacs.conversations.services.XmppConnectionService;
41import eu.siacs.conversations.services.XmppConnectionService.OnAccountUpdate;
42import eu.siacs.conversations.ui.adapter.KnownHostsAdapter;
43import eu.siacs.conversations.utils.CryptoHelper;
44import eu.siacs.conversations.utils.UIHelper;
45import eu.siacs.conversations.xmpp.OnKeyStatusUpdated;
46import eu.siacs.conversations.xmpp.XmppConnection.Features;
47import eu.siacs.conversations.xmpp.forms.Data;
48import eu.siacs.conversations.xmpp.jid.InvalidJidException;
49import eu.siacs.conversations.xmpp.jid.Jid;
50import eu.siacs.conversations.xmpp.pep.Avatar;
51
52public class EditAccountActivity extends XmppActivity implements OnAccountUpdate,
53 OnKeyStatusUpdated, OnCaptchaRequested, KeyChainAliasCallback, XmppConnectionService.OnShowErrorToast {
54
55 private LinearLayout mMainLayout;
56 private AutoCompleteTextView mAccountJid;
57 private EditText mPassword;
58 private EditText mPasswordConfirm;
59 private CheckBox mRegisterNew;
60 private Button mCancelButton;
61 private Button mSaveButton;
62 private TableLayout mMoreTable;
63
64 private LinearLayout mStats;
65 private TextView mServerInfoSm;
66 private TextView mServerInfoRosterVersion;
67 private TextView mServerInfoCarbons;
68 private TextView mServerInfoMam;
69 private TextView mServerInfoCSI;
70 private TextView mServerInfoBlocking;
71 private TextView mServerInfoPep;
72 private TextView mServerInfoHttpUpload;
73 private TextView mSessionEst;
74 private TextView mOtrFingerprint;
75 private TextView mAxolotlFingerprint;
76 private TextView mAccountJidLabel;
77 private ImageView mAvatar;
78 private RelativeLayout mOtrFingerprintBox;
79 private RelativeLayout mAxolotlFingerprintBox;
80 private ImageButton mOtrFingerprintToClipboardButton;
81 private ImageButton mAxolotlFingerprintToClipboardButton;
82 private ImageButton mRegenerateAxolotlKeyButton;
83 private LinearLayout keys;
84 private LinearLayout keysCard;
85 private LinearLayout mNamePort;
86 private EditText mHostname;
87 private EditText mPort;
88 private AlertDialog mCaptchaDialog = null;
89
90 private Jid jidToEdit;
91 private boolean mInitMode = false;
92 private boolean mUseTor = false;
93 private Account mAccount;
94 private String messageFingerprint;
95
96 private boolean mFetchingAvatar = false;
97
98 private final OnClickListener mSaveButtonClickListener = new OnClickListener() {
99
100 @Override
101 public void onClick(final View v) {
102 if (mInitMode && mAccount != null) {
103 mAccount.setOption(Account.OPTION_DISABLED, false);
104 }
105 if (mAccount != null && mAccount.getStatus() == Account.State.DISABLED && !accountInfoEdited()) {
106 mAccount.setOption(Account.OPTION_DISABLED, false);
107 xmppConnectionService.updateAccount(mAccount);
108 return;
109 }
110 final boolean registerNewAccount = mRegisterNew.isChecked() && !Config.DISALLOW_REGISTRATION_IN_UI;
111 if (Config.DOMAIN_LOCK != null && mAccountJid.getText().toString().contains("@")) {
112 mAccountJid.setError(getString(R.string.invalid_username));
113 mAccountJid.requestFocus();
114 return;
115 }
116 final Jid jid;
117 try {
118 if (Config.DOMAIN_LOCK != null) {
119 jid = Jid.fromParts(mAccountJid.getText().toString(), Config.DOMAIN_LOCK, null);
120 } else {
121 jid = Jid.fromString(mAccountJid.getText().toString());
122 }
123 } catch (final InvalidJidException e) {
124 if (Config.DOMAIN_LOCK != null) {
125 mAccountJid.setError(getString(R.string.invalid_username));
126 } else {
127 mAccountJid.setError(getString(R.string.invalid_jid));
128 }
129 mAccountJid.requestFocus();
130 return;
131 }
132 if (jid.isDomainJid()) {
133 if (Config.DOMAIN_LOCK != null) {
134 mAccountJid.setError(getString(R.string.invalid_username));
135 } else {
136 mAccountJid.setError(getString(R.string.invalid_jid));
137 }
138 mAccountJid.requestFocus();
139 return;
140 }
141 final String password = mPassword.getText().toString();
142 final String passwordConfirm = mPasswordConfirm.getText().toString();
143 final String hostname = mHostname.getText().toString();
144 final String port = mPort.getText().toString();
145 if (registerNewAccount) {
146 if (!password.equals(passwordConfirm)) {
147 mPasswordConfirm.setError(getString(R.string.passwords_do_not_match));
148 mPasswordConfirm.requestFocus();
149 return;
150 }
151 }
152 if (mAccount != null) {
153 mAccount.setJid(jid);
154 mAccountJid.setError(null);
155 mPasswordConfirm.setError(null);
156 mAccount.setPassword(password);
157 mAccount.setOption(Account.OPTION_REGISTER, registerNewAccount);
158 if (hostname.contains(" ")) {
159 mHostname.setError(getString(R.string.not_valid_hostname));
160 mHostname.requestFocus();
161 return;
162 }
163 mAccount.setHostname(hostname);
164 try {
165 int numericPort = Integer.parseInt(port);
166 if (numericPort < 0 || numericPort > 65535) {
167 mPort.setError(getString(R.string.not_a_valid_port));
168 mPort.requestFocus();
169 return;
170 }
171 mAccount.setPort(numericPort);
172 } catch (NumberFormatException e) {
173 mPort.setError(getString(R.string.not_a_valid_port));
174 mPort.requestFocus();
175 return;
176 }
177 xmppConnectionService.updateAccount(mAccount);
178 } else {
179 if (xmppConnectionService.findAccountByJid(jid) != null) {
180 mAccountJid.setError(getString(R.string.account_already_exists));
181 mAccountJid.requestFocus();
182 return;
183 }
184 mAccount = new Account(jid.toBareJid(), password);
185 mAccount.setOption(Account.OPTION_USETLS, true);
186 mAccount.setOption(Account.OPTION_USECOMPRESSION, true);
187 mAccount.setOption(Account.OPTION_REGISTER, registerNewAccount);
188 xmppConnectionService.createAccount(mAccount);
189 }
190 if (!mAccount.isOptionSet(Account.OPTION_DISABLED)
191 && !registerNewAccount
192 && !mInitMode) {
193 finish();
194 } else {
195 updateSaveButton();
196 updateAccountInformation(true);
197 }
198
199 }
200 };
201 private final OnClickListener mCancelButtonClickListener = new OnClickListener() {
202
203 @Override
204 public void onClick(final View v) {
205 finish();
206 }
207 };
208
209 public void refreshUiReal() {
210 invalidateOptionsMenu();
211 if (mAccount != null
212 && mAccount.getStatus() != Account.State.ONLINE
213 && mFetchingAvatar) {
214 startActivity(new Intent(getApplicationContext(),
215 ManageAccountActivity.class));
216 finish();
217 } else if (mInitMode && mAccount != null && mAccount.getStatus() == Account.State.ONLINE) {
218 if (!mFetchingAvatar) {
219 mFetchingAvatar = true;
220 xmppConnectionService.checkForAvatar(mAccount, mAvatarFetchCallback);
221 }
222 } else {
223 updateSaveButton();
224 }
225 if (mAccount != null) {
226 updateAccountInformation(false);
227 }
228 }
229
230 @Override
231 public void onAccountUpdate() {
232 refreshUi();
233 }
234
235 private final UiCallback<Avatar> mAvatarFetchCallback = new UiCallback<Avatar>() {
236
237 @Override
238 public void userInputRequried(final PendingIntent pi, final Avatar avatar) {
239 finishInitialSetup(avatar);
240 }
241
242 @Override
243 public void success(final Avatar avatar) {
244 finishInitialSetup(avatar);
245 }
246
247 @Override
248 public void error(final int errorCode, final Avatar avatar) {
249 finishInitialSetup(avatar);
250 }
251 };
252 private final TextWatcher mTextWatcher = new TextWatcher() {
253
254 @Override
255 public void onTextChanged(final CharSequence s, final int start, final int before, final int count) {
256 updateSaveButton();
257 }
258
259 @Override
260 public void beforeTextChanged(final CharSequence s, final int start, final int count, final int after) {
261 }
262
263 @Override
264 public void afterTextChanged(final Editable s) {
265
266 }
267 };
268
269 private final OnClickListener mAvatarClickListener = new OnClickListener() {
270 @Override
271 public void onClick(final View view) {
272 if (mAccount != null) {
273 final Intent intent = new Intent(getApplicationContext(), PublishProfilePictureActivity.class);
274 intent.putExtra("account", mAccount.getJid().toBareJid().toString());
275 startActivity(intent);
276 }
277 }
278 };
279
280 protected void finishInitialSetup(final Avatar avatar) {
281 runOnUiThread(new Runnable() {
282
283 @Override
284 public void run() {
285 final Intent intent;
286 if (avatar != null) {
287 intent = new Intent(getApplicationContext(),
288 StartConversationActivity.class);
289 if (xmppConnectionService != null && xmppConnectionService.getAccounts().size() == 1) {
290 intent.putExtra("init", true);
291 }
292 } else {
293 intent = new Intent(getApplicationContext(),
294 PublishProfilePictureActivity.class);
295 intent.putExtra("account", mAccount.getJid().toBareJid().toString());
296 intent.putExtra("setup", true);
297 }
298 startActivity(intent);
299 finish();
300 }
301 });
302 }
303
304 protected void updateSaveButton() {
305 if (accountInfoEdited() && !mInitMode) {
306 this.mSaveButton.setText(R.string.save);
307 this.mSaveButton.setEnabled(true);
308 this.mSaveButton.setTextColor(getPrimaryTextColor());
309 } else if (mAccount != null && (mAccount.getStatus() == Account.State.CONNECTING || mFetchingAvatar)) {
310 this.mSaveButton.setEnabled(false);
311 this.mSaveButton.setTextColor(getSecondaryTextColor());
312 this.mSaveButton.setText(R.string.account_status_connecting);
313 } else if (mAccount != null && mAccount.getStatus() == Account.State.DISABLED && !mInitMode) {
314 this.mSaveButton.setEnabled(true);
315 this.mSaveButton.setTextColor(getPrimaryTextColor());
316 this.mSaveButton.setText(R.string.enable);
317 } else {
318 this.mSaveButton.setEnabled(true);
319 this.mSaveButton.setTextColor(getPrimaryTextColor());
320 if (!mInitMode) {
321 if (mAccount != null && mAccount.isOnlineAndConnected()) {
322 this.mSaveButton.setText(R.string.save);
323 if (!accountInfoEdited()) {
324 this.mSaveButton.setEnabled(false);
325 this.mSaveButton.setTextColor(getSecondaryTextColor());
326 }
327 } else {
328 this.mSaveButton.setText(R.string.connect);
329 }
330 } else {
331 this.mSaveButton.setText(R.string.next);
332 }
333 }
334 }
335
336 protected boolean accountInfoEdited() {
337 if (this.mAccount == null) {
338 return false;
339 }
340 final String unmodified;
341 if (Config.DOMAIN_LOCK != null) {
342 unmodified = this.mAccount.getJid().getLocalpart();
343 } else {
344 unmodified = this.mAccount.getJid().toBareJid().toString();
345 }
346 return !unmodified.equals(this.mAccountJid.getText().toString()) ||
347 !this.mAccount.getPassword().equals(this.mPassword.getText().toString()) ||
348 !this.mAccount.getHostname().equals(this.mHostname.getText().toString()) ||
349 !String.valueOf(this.mAccount.getPort()).equals(this.mPort.getText().toString());
350 }
351
352 @Override
353 protected String getShareableUri() {
354 if (mAccount != null) {
355 return mAccount.getShareableUri();
356 } else {
357 return "";
358 }
359 }
360
361 @Override
362 protected void onCreate(final Bundle savedInstanceState) {
363 super.onCreate(savedInstanceState);
364 setContentView(R.layout.activity_edit_account);
365 this.mMainLayout = (LinearLayout) findViewById(R.id.account_main_layout);
366 this.mAccountJid = (AutoCompleteTextView) findViewById(R.id.account_jid);
367 this.mAccountJid.addTextChangedListener(this.mTextWatcher);
368 this.mAccountJidLabel = (TextView) findViewById(R.id.account_jid_label);
369 if (Config.DOMAIN_LOCK != null) {
370 this.mAccountJidLabel.setText(R.string.username);
371 this.mAccountJid.setHint(R.string.username_hint);
372 }
373 this.mPassword = (EditText) findViewById(R.id.account_password);
374 this.mPassword.addTextChangedListener(this.mTextWatcher);
375 this.mPasswordConfirm = (EditText) findViewById(R.id.account_password_confirm);
376 this.mAvatar = (ImageView) findViewById(R.id.avater);
377 this.mAvatar.setOnClickListener(this.mAvatarClickListener);
378 this.mRegisterNew = (CheckBox) findViewById(R.id.account_register_new);
379 this.mStats = (LinearLayout) findViewById(R.id.stats);
380 this.mSessionEst = (TextView) findViewById(R.id.session_est);
381 this.mServerInfoRosterVersion = (TextView) findViewById(R.id.server_info_roster_version);
382 this.mServerInfoCarbons = (TextView) findViewById(R.id.server_info_carbons);
383 this.mServerInfoMam = (TextView) findViewById(R.id.server_info_mam);
384 this.mServerInfoCSI = (TextView) findViewById(R.id.server_info_csi);
385 this.mServerInfoBlocking = (TextView) findViewById(R.id.server_info_blocking);
386 this.mServerInfoSm = (TextView) findViewById(R.id.server_info_sm);
387 this.mServerInfoPep = (TextView) findViewById(R.id.server_info_pep);
388 this.mServerInfoHttpUpload = (TextView) findViewById(R.id.server_info_http_upload);
389 this.mOtrFingerprint = (TextView) findViewById(R.id.otr_fingerprint);
390 this.mOtrFingerprintBox = (RelativeLayout) findViewById(R.id.otr_fingerprint_box);
391 this.mOtrFingerprintToClipboardButton = (ImageButton) findViewById(R.id.action_copy_to_clipboard);
392 this.mAxolotlFingerprint = (TextView) findViewById(R.id.axolotl_fingerprint);
393 this.mAxolotlFingerprintBox = (RelativeLayout) findViewById(R.id.axolotl_fingerprint_box);
394 this.mAxolotlFingerprintToClipboardButton = (ImageButton) findViewById(R.id.action_copy_axolotl_to_clipboard);
395 this.mRegenerateAxolotlKeyButton = (ImageButton) findViewById(R.id.action_regenerate_axolotl_key);
396 this.keysCard = (LinearLayout) findViewById(R.id.other_device_keys_card);
397 this.keys = (LinearLayout) findViewById(R.id.other_device_keys);
398 this.mNamePort = (LinearLayout) findViewById(R.id.name_port);
399 this.mHostname = (EditText) findViewById(R.id.hostname);
400 this.mHostname.addTextChangedListener(mTextWatcher);
401 this.mPort = (EditText) findViewById(R.id.port);
402 this.mPort.setText("5222");
403 this.mPort.addTextChangedListener(mTextWatcher);
404 this.mSaveButton = (Button) findViewById(R.id.save_button);
405 this.mCancelButton = (Button) findViewById(R.id.cancel_button);
406 this.mSaveButton.setOnClickListener(this.mSaveButtonClickListener);
407 this.mCancelButton.setOnClickListener(this.mCancelButtonClickListener);
408 this.mMoreTable = (TableLayout) findViewById(R.id.server_info_more);
409 final OnCheckedChangeListener OnCheckedShowConfirmPassword = new OnCheckedChangeListener() {
410 @Override
411 public void onCheckedChanged(final CompoundButton buttonView,
412 final boolean isChecked) {
413 if (isChecked) {
414 mPasswordConfirm.setVisibility(View.VISIBLE);
415 } else {
416 mPasswordConfirm.setVisibility(View.GONE);
417 }
418 updateSaveButton();
419 }
420 };
421 this.mRegisterNew.setOnCheckedChangeListener(OnCheckedShowConfirmPassword);
422 if (Config.DISALLOW_REGISTRATION_IN_UI) {
423 this.mRegisterNew.setVisibility(View.GONE);
424 }
425 }
426
427 @Override
428 public boolean onCreateOptionsMenu(final Menu menu) {
429 super.onCreateOptionsMenu(menu);
430 getMenuInflater().inflate(R.menu.editaccount, menu);
431 final MenuItem showQrCode = menu.findItem(R.id.action_show_qr_code);
432 final MenuItem showBlocklist = menu.findItem(R.id.action_show_block_list);
433 final MenuItem showMoreInfo = menu.findItem(R.id.action_server_info_show_more);
434 final MenuItem changePassword = menu.findItem(R.id.action_change_password_on_server);
435 final MenuItem clearDevices = menu.findItem(R.id.action_clear_devices);
436 final MenuItem renewCertificate = menu.findItem(R.id.action_renew_certificate);
437
438 renewCertificate.setVisible(mAccount != null && mAccount.getPrivateKeyAlias() != null);
439
440 if (mAccount != null && mAccount.isOnlineAndConnected()) {
441 if (!mAccount.getXmppConnection().getFeatures().blocking()) {
442 showBlocklist.setVisible(false);
443 }
444 if (!mAccount.getXmppConnection().getFeatures().register()) {
445 changePassword.setVisible(false);
446 }
447 Set<Integer> otherDevices = mAccount.getAxolotlService().getOwnDeviceIds();
448 if (otherDevices == null || otherDevices.isEmpty()) {
449 clearDevices.setVisible(false);
450 }
451 } else {
452 showQrCode.setVisible(false);
453 showBlocklist.setVisible(false);
454 showMoreInfo.setVisible(false);
455 changePassword.setVisible(false);
456 clearDevices.setVisible(false);
457 }
458 return true;
459 }
460
461 @Override
462 protected void onStart() {
463 super.onStart();
464 if (getIntent() != null) {
465 try {
466 this.jidToEdit = Jid.fromString(getIntent().getStringExtra("jid"));
467 } catch (final InvalidJidException | NullPointerException ignored) {
468 this.jidToEdit = null;
469 }
470 this.mInitMode = getIntent().getBooleanExtra("init", false) || this.jidToEdit == null;
471 this.messageFingerprint = getIntent().getStringExtra("fingerprint");
472 if (!mInitMode) {
473 this.mRegisterNew.setVisibility(View.GONE);
474 if (getActionBar() != null) {
475 getActionBar().setTitle(getString(R.string.account_details));
476 }
477 } else {
478 this.mAvatar.setVisibility(View.GONE);
479 if (getActionBar() != null) {
480 getActionBar().setTitle(R.string.action_add_account);
481 }
482 }
483 }
484 this.mUseTor = getPreferences().getBoolean("use_tor", false);
485 this.mNamePort.setVisibility(mUseTor ? View.VISIBLE : View.GONE);
486 }
487
488 @Override
489 protected void onBackendConnected() {
490 if (this.jidToEdit != null) {
491 this.mAccount = xmppConnectionService.findAccountByJid(jidToEdit);
492 if (this.mAccount != null) {
493 if (this.mAccount.getPrivateKeyAlias() != null) {
494 this.mPassword.setHint(R.string.authenticate_with_certificate);
495 if (this.mInitMode) {
496 this.mPassword.requestFocus();
497 }
498 }
499 updateAccountInformation(true);
500 }
501 } else if (this.xmppConnectionService.getAccounts().size() == 0) {
502 if (getActionBar() != null) {
503 getActionBar().setDisplayHomeAsUpEnabled(false);
504 getActionBar().setDisplayShowHomeEnabled(false);
505 getActionBar().setHomeButtonEnabled(false);
506 }
507 this.mCancelButton.setEnabled(false);
508 this.mCancelButton.setTextColor(getSecondaryTextColor());
509 }
510 if (Config.DOMAIN_LOCK == null) {
511 final KnownHostsAdapter mKnownHostsAdapter = new KnownHostsAdapter(this,
512 android.R.layout.simple_list_item_1,
513 xmppConnectionService.getKnownHosts());
514 this.mAccountJid.setAdapter(mKnownHostsAdapter);
515 }
516 updateSaveButton();
517 invalidateOptionsMenu();
518 }
519
520 @Override
521 public boolean onOptionsItemSelected(final MenuItem item) {
522 switch (item.getItemId()) {
523 case R.id.action_show_block_list:
524 final Intent showBlocklistIntent = new Intent(this, BlocklistActivity.class);
525 showBlocklistIntent.putExtra("account", mAccount.getJid().toString());
526 startActivity(showBlocklistIntent);
527 break;
528 case R.id.action_server_info_show_more:
529 mMoreTable.setVisibility(item.isChecked() ? View.GONE : View.VISIBLE);
530 item.setChecked(!item.isChecked());
531 break;
532 case R.id.action_change_password_on_server:
533 final Intent changePasswordIntent = new Intent(this, ChangePasswordActivity.class);
534 changePasswordIntent.putExtra("account", mAccount.getJid().toString());
535 startActivity(changePasswordIntent);
536 break;
537 case R.id.action_clear_devices:
538 showWipePepDialog();
539 break;
540 case R.id.action_renew_certificate:
541 renewCertificate();
542 break;
543 }
544 return super.onOptionsItemSelected(item);
545 }
546
547 private void renewCertificate() {
548 KeyChain.choosePrivateKeyAlias(this, this, null, null, null, -1, null);
549 }
550
551 @Override
552 public void alias(String alias) {
553 if (alias != null) {
554 xmppConnectionService.updateKeyInAccount(mAccount, alias);
555 }
556 }
557
558 private void updateAccountInformation(boolean init) {
559 if (init) {
560 this.mAccountJid.getEditableText().clear();
561 if (Config.DOMAIN_LOCK != null) {
562 this.mAccountJid.getEditableText().append(this.mAccount.getJid().getLocalpart());
563 } else {
564 this.mAccountJid.getEditableText().append(this.mAccount.getJid().toBareJid().toString());
565 }
566 this.mPassword.setText(this.mAccount.getPassword());
567 this.mHostname.setText("");
568 this.mHostname.getEditableText().append(this.mAccount.getHostname());
569 this.mPort.setText("");
570 this.mPort.getEditableText().append(String.valueOf(this.mAccount.getPort()));
571 this.mNamePort.setVisibility(mUseTor ? View.VISIBLE : View.GONE);
572
573 }
574 if (!mInitMode) {
575 this.mAvatar.setVisibility(View.VISIBLE);
576 this.mAvatar.setImageBitmap(avatarService().get(this.mAccount, getPixel(72)));
577 }
578 if (this.mAccount.isOptionSet(Account.OPTION_REGISTER)) {
579 this.mRegisterNew.setVisibility(View.VISIBLE);
580 this.mRegisterNew.setChecked(true);
581 this.mPasswordConfirm.setText(this.mAccount.getPassword());
582 } else {
583 this.mRegisterNew.setVisibility(View.GONE);
584 this.mRegisterNew.setChecked(false);
585 }
586 if (this.mAccount.isOnlineAndConnected() && !this.mFetchingAvatar) {
587 this.mStats.setVisibility(View.VISIBLE);
588 this.mSessionEst.setText(UIHelper.readableTimeDifferenceFull(this, this.mAccount.getXmppConnection()
589 .getLastSessionEstablished()));
590 Features features = this.mAccount.getXmppConnection().getFeatures();
591 if (features.rosterVersioning()) {
592 this.mServerInfoRosterVersion.setText(R.string.server_info_available);
593 } else {
594 this.mServerInfoRosterVersion.setText(R.string.server_info_unavailable);
595 }
596 if (features.carbons()) {
597 this.mServerInfoCarbons.setText(R.string.server_info_available);
598 } else {
599 this.mServerInfoCarbons
600 .setText(R.string.server_info_unavailable);
601 }
602 if (features.mam()) {
603 this.mServerInfoMam.setText(R.string.server_info_available);
604 } else {
605 this.mServerInfoMam.setText(R.string.server_info_unavailable);
606 }
607 if (features.csi()) {
608 this.mServerInfoCSI.setText(R.string.server_info_available);
609 } else {
610 this.mServerInfoCSI.setText(R.string.server_info_unavailable);
611 }
612 if (features.blocking()) {
613 this.mServerInfoBlocking.setText(R.string.server_info_available);
614 } else {
615 this.mServerInfoBlocking.setText(R.string.server_info_unavailable);
616 }
617 if (features.sm()) {
618 this.mServerInfoSm.setText(R.string.server_info_available);
619 } else {
620 this.mServerInfoSm.setText(R.string.server_info_unavailable);
621 }
622 if (features.pep()) {
623 AxolotlService axolotlService = this.mAccount.getAxolotlService();
624 if (axolotlService != null && axolotlService.isPepBroken()) {
625 this.mServerInfoPep.setText(R.string.server_info_broken);
626 } else {
627 this.mServerInfoPep.setText(R.string.server_info_available);
628 }
629 } else {
630 this.mServerInfoPep.setText(R.string.server_info_unavailable);
631 }
632 if (features.httpUpload()) {
633 this.mServerInfoHttpUpload.setText(R.string.server_info_available);
634 } else {
635 this.mServerInfoHttpUpload.setText(R.string.server_info_unavailable);
636 }
637 final String otrFingerprint = this.mAccount.getOtrFingerprint();
638 if (otrFingerprint != null) {
639 this.mOtrFingerprintBox.setVisibility(View.VISIBLE);
640 this.mOtrFingerprint.setText(CryptoHelper.prettifyFingerprint(otrFingerprint));
641 this.mOtrFingerprintToClipboardButton
642 .setVisibility(View.VISIBLE);
643 this.mOtrFingerprintToClipboardButton
644 .setOnClickListener(new View.OnClickListener() {
645
646 @Override
647 public void onClick(final View v) {
648
649 if (copyTextToClipboard(otrFingerprint, R.string.otr_fingerprint)) {
650 Toast.makeText(
651 EditAccountActivity.this,
652 R.string.toast_message_otr_fingerprint,
653 Toast.LENGTH_SHORT).show();
654 }
655 }
656 });
657 } else {
658 this.mOtrFingerprintBox.setVisibility(View.GONE);
659 }
660 final String axolotlFingerprint = this.mAccount.getAxolotlService().getOwnFingerprint();
661 if (axolotlFingerprint != null) {
662 this.mAxolotlFingerprintBox.setVisibility(View.VISIBLE);
663 this.mAxolotlFingerprint.setText(CryptoHelper.prettifyFingerprint(axolotlFingerprint));
664 this.mAxolotlFingerprintToClipboardButton
665 .setVisibility(View.VISIBLE);
666 this.mAxolotlFingerprintToClipboardButton
667 .setOnClickListener(new View.OnClickListener() {
668
669 @Override
670 public void onClick(final View v) {
671
672 if (copyTextToClipboard(axolotlFingerprint, R.string.omemo_fingerprint)) {
673 Toast.makeText(
674 EditAccountActivity.this,
675 R.string.toast_message_omemo_fingerprint,
676 Toast.LENGTH_SHORT).show();
677 }
678 }
679 });
680 if (Config.SHOW_REGENERATE_AXOLOTL_KEYS_BUTTON) {
681 this.mRegenerateAxolotlKeyButton
682 .setVisibility(View.VISIBLE);
683 this.mRegenerateAxolotlKeyButton
684 .setOnClickListener(new View.OnClickListener() {
685
686 @Override
687 public void onClick(final View v) {
688 showRegenerateAxolotlKeyDialog();
689 }
690 });
691 }
692 } else {
693 this.mAxolotlFingerprintBox.setVisibility(View.GONE);
694 }
695 final String ownFingerprint = mAccount.getAxolotlService().getOwnFingerprint();
696 boolean hasKeys = false;
697 keys.removeAllViews();
698 for (final String fingerprint : mAccount.getAxolotlService().getFingerprintsForOwnSessions()) {
699 if (ownFingerprint.equals(fingerprint)) {
700 continue;
701 }
702 boolean highlight = fingerprint.equals(messageFingerprint);
703 hasKeys |= addFingerprintRow(keys, mAccount, fingerprint, highlight);
704 }
705 if (hasKeys) {
706 keysCard.setVisibility(View.VISIBLE);
707 } else {
708 keysCard.setVisibility(View.GONE);
709 }
710 } else {
711 if (this.mAccount.errorStatus()) {
712 this.mAccountJid.setError(getString(this.mAccount.getStatus().getReadableId()));
713 if (init || !accountInfoEdited()) {
714 this.mAccountJid.requestFocus();
715 }
716 } else {
717 this.mAccountJid.setError(null);
718 }
719 this.mStats.setVisibility(View.GONE);
720 }
721 }
722
723 public void showRegenerateAxolotlKeyDialog() {
724 Builder builder = new Builder(this);
725 builder.setTitle("Regenerate Key");
726 builder.setIconAttribute(android.R.attr.alertDialogIcon);
727 builder.setMessage("Are you sure you want to regenerate your Identity Key? (This will also wipe all established sessions and contact Identity Keys)");
728 builder.setNegativeButton(getString(R.string.cancel), null);
729 builder.setPositiveButton("Yes",
730 new DialogInterface.OnClickListener() {
731 @Override
732 public void onClick(DialogInterface dialog, int which) {
733 mAccount.getAxolotlService().regenerateKeys(false);
734 }
735 });
736 builder.create().show();
737 }
738
739 public void showWipePepDialog() {
740 Builder builder = new Builder(this);
741 builder.setTitle(getString(R.string.clear_other_devices));
742 builder.setIconAttribute(android.R.attr.alertDialogIcon);
743 builder.setMessage(getString(R.string.clear_other_devices_desc));
744 builder.setNegativeButton(getString(R.string.cancel), null);
745 builder.setPositiveButton(getString(R.string.accept),
746 new DialogInterface.OnClickListener() {
747 @Override
748 public void onClick(DialogInterface dialog, int which) {
749 mAccount.getAxolotlService().wipeOtherPepDevices();
750 }
751 });
752 builder.create().show();
753 }
754
755 @Override
756 public void onKeyStatusUpdated(AxolotlService.FetchStatus report) {
757 refreshUi();
758 }
759
760 @Override
761 public void onCaptchaRequested(final Account account, final String id, final Data data,
762 final Bitmap captcha) {
763 final AlertDialog.Builder builder = new AlertDialog.Builder(this);
764 final ImageView view = new ImageView(this);
765 final LinearLayout layout = new LinearLayout(this);
766 final EditText input = new EditText(this);
767
768 view.setImageBitmap(captcha);
769 view.setScaleType(ImageView.ScaleType.FIT_CENTER);
770
771 input.setHint(getString(R.string.captcha_hint));
772
773 layout.setOrientation(LinearLayout.VERTICAL);
774 layout.addView(view);
775 layout.addView(input);
776
777 builder.setTitle(getString(R.string.captcha_required));
778 builder.setView(layout);
779
780 builder.setPositiveButton(getString(R.string.ok),
781 new DialogInterface.OnClickListener() {
782 @Override
783 public void onClick(DialogInterface dialog, int which) {
784 String rc = input.getText().toString();
785 data.put("username", account.getUsername());
786 data.put("password", account.getPassword());
787 data.put("ocr", rc);
788 data.submit();
789
790 if (xmppConnectionServiceBound) {
791 xmppConnectionService.sendCreateAccountWithCaptchaPacket(
792 account, id, data);
793 }
794 }
795 });
796 builder.setNegativeButton(getString(R.string.cancel), new DialogInterface.OnClickListener() {
797 @Override
798 public void onClick(DialogInterface dialog, int which) {
799 if (xmppConnectionService != null) {
800 xmppConnectionService.sendCreateAccountWithCaptchaPacket(account, null, null);
801 }
802 }
803 });
804
805 builder.setOnCancelListener(new DialogInterface.OnCancelListener() {
806 @Override
807 public void onCancel(DialogInterface dialog) {
808 if (xmppConnectionService != null) {
809 xmppConnectionService.sendCreateAccountWithCaptchaPacket(account, null, null);
810 }
811 }
812 });
813
814 runOnUiThread(new Runnable() {
815 @Override
816 public void run() {
817 if ((mCaptchaDialog != null) && mCaptchaDialog.isShowing()) {
818 mCaptchaDialog.dismiss();
819 }
820 mCaptchaDialog = builder.create();
821 mCaptchaDialog.show();
822 }
823 });
824 }
825
826 public void onShowErrorToast(final int resId) {
827 runOnUiThread(new Runnable() {
828 @Override
829 public void run() {
830 Toast.makeText(EditAccountActivity.this, resId, Toast.LENGTH_SHORT).show();
831 }
832 });
833 }
834}