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